""" Konfigurasjon for TeeCup-API-et. ADR (åpent spørsmål 1): TeeCup bruker EGNE, uavhengige secrets — den skal ikke falle tilbake på teeoff sine. Manglende variabel skal feile høyt ved oppstart, ikke stille bruke en usikker standard. """ import os def _required(name: str) -> str: value = os.environ.get(name, "").strip() if not value: raise RuntimeError( f"Miljøvariabelen {name} mangler. TeeCup starter ikke uten den " f"(ingen fallback til teeoff sine verdier — se ADR)." ) return value class Settings: # Peker på teecup_db, og bruker RUNTIME-rollen teecup_app (ikke teeoff_admin). # Fem separate felt (ikke én sammensatt DSN-streng) -- en URL som bærer # passordet innebygd unngår ikke hemmeligheten, den gjemmer den bare et # sted secret-filtre som leter etter "pass|secret|key" i variabelNAVNET # ikke ser etter (se CLAUDE.md-status for hendelsen dette rettet). DB_HOST: str = _required("TEECUP_DB_HOST") DB_PORT: int = int(_required("TEECUP_DB_PORT")) DB_NAME: str = _required("TEECUP_DB_NAME") DB_USER: str = _required("TEECUP_DB_USER") DB_PASS: str = _required("TEECUP_DB_PASS") # Egen sesjons-secret, uavhengig av teeoff. SESSION_SECRET: str = _required("TEECUP_SESSION_SECRET") DB_POOL_MIN: int = int(os.getenv("TEECUP_DB_POOL_MIN", "2")) DB_POOL_MAX: int = int(os.getenv("TEECUP_DB_POOL_MAX", "10")) # Ekte autentisering (magic-link + JWT-sesjon). Ikke hemmeligheter -- # sane defaults, ikke _required. SESSION_MAX_AGE_SECONDS: int = int( os.getenv("TEECUP_SESSION_MAX_AGE_SECONDS", str(30 * 24 * 3600)) ) MAGIC_LINK_MAX_AGE_MINUTES: int = int(os.getenv("TEECUP_MAGIC_LINK_MAX_AGE_MINUTES", "20")) MAGIC_LINK_COOLDOWN_SECONDS: int = int(os.getenv("TEECUP_MAGIC_LINK_COOLDOWN_SECONDS", "60")) # Logger rå magic-link-token server-side i stedet for å sende e-post. # ALDRI på som standard -- må settes eksplisitt, kun for dev/scratch- # testing (samme klasse feil som denne runden finnes for å lukke for # X-Debug-User-Id-headeren). DEV_LOG_MAGIC_LINKS: bool = os.getenv("TEECUP_DEV_LOG_MAGIC_LINKS", "").strip().lower() in ( "1", "true", "yes", ) # Ekte e-postutsending for magic-link (ADR-009: egne credentials, ikke # delt med teeoff). Valgfritt (ikke _required) -- scratch-/dev-testing # skal fortsatt fungere uten SMTP satt, via DEV_LOG_MAGIC_LINKS over. SMTP_SERVER: str | None = os.environ.get("TEECUP_SMTP_SERVER", "").strip() or None SMTP_PORT: int | None = ( int(os.environ["TEECUP_SMTP_PORT"]) if os.environ.get("TEECUP_SMTP_PORT", "").strip() else None ) SMTP_USER: str | None = os.environ.get("TEECUP_SMTP_USER", "").strip() or None SMTP_PASS: str | None = os.environ.get("TEECUP_SMTP_PASS", "").strip() or None FROM_EMAIL: str | None = os.environ.get("TEECUP_FROM_EMAIL", "").strip() or None SMTP_CONFIGURED: bool = bool(SMTP_SERVER and SMTP_PORT and SMTP_USER and SMTP_PASS and FROM_EMAIL) # Offentlig URL-base for lenker i utsendt e-post (frontend, ikke API-et # direkte). Ikke hemmelig -- valgfri med prod-riktig standard slik at # scratch-/dev-testing kan overstyre uten kodeendring. PUBLIC_BASE_URL: str = os.getenv("TEECUP_PUBLIC_BASE_URL", "https://teecup.teeoff.no").rstrip("/") # MinIO (ADR-018 MinIO-runde). Root-credentials -- ekte hemmeligheter, # _required. INTERNAL_ENDPOINT (container-til-container, ingen Caddy) # brukes KUN til engangs bucket-/policy-oppsett ved oppstart. # PUBLIC_BASE_URL gjenbrukes til å bygge presignerte URL-er nettleseren # faktisk skal treffe -- se app/storage.py for hvorfor de to endepunktene # bevisst er atskilt. MINIO_ROOT_USER: str = _required("TEECUP_MINIO_ROOT_USER") MINIO_ROOT_PASSWORD: str = _required("TEECUP_MINIO_ROOT_PASSWORD") MINIO_BUCKET: str = os.getenv("TEECUP_MINIO_BUCKET", "teecup-media") MINIO_INTERNAL_ENDPOINT: str = os.getenv("TEECUP_MINIO_INTERNAL_ENDPOINT", "teecup-minio:9000") settings = Settings()