"""Application configuration. All values come from environment variables (populated by docker-compose or a local .env). No secrets are committed to the repo. """ from __future__ import annotations import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent def _bool(name: str, default: bool = False) -> bool: raw = os.getenv(name) if raw is None: return default return raw.strip().lower() in {"1", "true", "yes", "on"} class Settings: # --- General --- APP_NAME: str = os.getenv("APP_NAME", "Family Home Lab") DEBUG: bool = _bool("DEBUG", False) SECRET_KEY: str = os.getenv( "SESSION_SECRET", "change-me-in-prod-use-openssl-rand-hex" ) SESSION_COOKIE: str = os.getenv("SESSION_COOKIE", "fhl_session") # How long a session cookie stays valid (seconds). 7 days. SESSION_TTL: int = int(os.getenv("SESSION_TTL", str(7 * 24 * 3600))) # --- First-run admin bootstrap (breed: env only, never committed) --- ADMIN_USERNAME: str = os.getenv("ADMIN_USERNAME", "sam") ADMIN_PASSWORD: str = os.getenv("ADMIN_PASSWORD", "") ADMIN_FULLNAME: str = os.getenv("ADMIN_FULLNAME", "Sam") # --- Database (pgvector pg16) --- DATABASE_URL: str = os.getenv( "DATABASE_URL", "postgresql+asyncpg://fhl:fhl@postgres:5432/fhl" ) # --- Task queue (Celery + RabbitMQ broker, Redis backend) --- # Final decision (plan.md ยง8 #1): own RabbitMQ container in our stack. REDIS_URL: str = os.getenv("REDIS_URL", "redis://redis:6379/0") CELERY_BROKER_URL: str = os.getenv( "CELERY_BROKER_URL", "amqp://guest:guest@rabbitmq:5672//" ) # --- Object storage (Garage S3) --- S3_ENDPOINT_URL: str = os.getenv("S3_ENDPOINT_URL", "http://garage:3900") S3_REGION: str = os.getenv("S3_REGION", "garage") S3_ACCESS_KEY: str = os.getenv("S3_ACCESS_KEY", "") S3_SECRET_KEY: str = os.getenv("S3_SECRET_KEY", "") # --- Tool catalogue endpoint for opening tools --- SECTION_COLORS: dict[str, str] = { "chat": "#62aef0", # accent-sky "image": "#d6b6f6", # accent-purple "video": "#ff64c8", # accent-pink "audio": "#dd5b00", # accent-orange "docs": "#2a9d99", # accent-teal "ai": "#62aef0", # accent-sky (fallback for ai stack) "media": "#5fbf7a", # media & entertainment "data": "#c07af0", # data & links "storage": "#e0a63c", # storage & backups "home": "#41b6c6", # home & iot "home-mgmt": "#3fbfa8", # home management "dev": "#e07b39", # development "portfolio": "#b089d6", # portfolio / resume "infra": "#9aa4ad", # infrastructure "network": "#7db2f0", # home network } @property def is_secure(self) -> bool: return not self.DEBUG settings = Settings()