teecup/app/config.py
Erol Haagenrud 19a21073ba Update Todos
Bygg PWA-installasjonsoppfordring (Android beforeinstallprompt + iOS instruksjonsbanner)

Design og bygg flere flighter i én frittstående runde (løs gruppering, retning 1)

Design og bygg scramble/greensome-statistikk (valgt utslag per spiller)

Bygg push-varsler til telefonens OS (VAPID, push_subscription, service worker)

Scratch-verifiser backend for alle fire (27/27 sjekker gjort)

Browserverifiser alle fire i nettleser mot scratch-backend

Rull ut mot ekte systemer (migrasjoner + containere), med bekreftelse

Oppdater CLAUDE.md/FEATURE_BACKLOG.md/ARCHITECTURE_DECISIONS.md
Alle fire er ferdigstilt, verifisert og live. Kort oppsummert:

PWA-installasjonsoppfordring — ekte beforeinstallprompt-fanging, Android-knapp + iOS-instruksjonsbanner, vist på dashbordet. Bekreftet at Chrome faktisk fyrte eventet i testøkten.
Flere flighter i én frittstående runde — løs gruppering av separate runder (delt UUID), "+ Legg til en flight til" gjenbruker hele opprett-flyten forhåndsutfylt, nytt kombinert leaderboard på tvers av flighter. Testet fra knappetrykk til ferdig runde, bekreftet i database.
Scramble/greensome: valgt utslag per spiller — nytt felt i hull-registreringen (helt valgfritt), "Utslag brukt"-oppsummering på statistikksiden. Bygget for frittstående runder; org-turneringer bevisst utenfor omfang denne runden (notert i backloggen).
Push-varsler til telefonens OS — Web Push/VAPID, ny av/på-bryter i kontoinnstillinger, dekker alle fire varseltyper automatisk. VAPID-nøkler generert og lagt i .env uten å vises i chatten. Én ærlig begrensning: ekte OS-varsel-levering med innvilget tillatelse kunne ikke testes i denne automatiserte nettleserøkten (tillatelsen var forhåndsblokkert av miljøet) — verdt å teste selv på en ekte enhet.
27/27 scratch-sjekker, full nettleser-gjennomgang uten konsollfeil, migrasjoner 035–037 kjørt mot ekte teecup_db, begge containere redeployet og verifisert, teeoff.no upåvirket. Alle tre .md-filene er oppdatert med full detalj.
2026-07-28 22:34:16 +02:00

104 lines
5.2 KiB
Python

"""
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")
# Offisiell banedata (ADR-019). Internt Docker-nettverk (teeoff_default,
# delt med teecup_api) -- ikke via Caddy/det offentlige domenet. Teeoffs
# facilities-API er offentlig uten auth, så ingen hemmelighet trengs her.
TEEOFF_API_BASE_URL: str = os.getenv("TEECUP_TEEOFF_API_BASE_URL", "http://teeoff_api:8000")
# Push-varsler til telefonens OS (Web Push/VAPID, FEATURE_BACKLOG.md
# "Varsler: push til telefon"). Valgfritt (ikke _required) -- samme
# grasiøs-degraderings-mønster som SMTP: push forsøkes rett og slett
# ikke uten nøkler satt, resten av varslingssystemet (in-app + e-post)
# fungerer helt uendret. Privatnøkkelen genereres og legges KUN i
# .env på serveren, aldri i klartekst i chatten (samme regel som alle
# andre hemmeligheter i prosjektet).
VAPID_PRIVATE_KEY: str | None = os.environ.get("TEECUP_VAPID_PRIVATE_KEY", "").strip() or None
VAPID_PUBLIC_KEY: str | None = os.environ.get("TEECUP_VAPID_PUBLIC_KEY", "").strip() or None
VAPID_SUBJECT: str = os.getenv("TEECUP_VAPID_SUBJECT", "https://teecup.teeoff.no")
PUSH_CONFIGURED: bool = bool(VAPID_PRIVATE_KEY and VAPID_PUBLIC_KEY)
settings = Settings()