2026-07-16 07:26:04 +02:00
|
|
|
"""
|
|
|
|
|
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).
|
2026-07-17 21:40:42 +02:00
|
|
|
# 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")
|
2026-07-16 07:26:04 +02:00
|
|
|
|
|
|
|
|
# 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 er bygget og verifisert. X-Debug-User-Id-stubben er helt fjernet, ingen fallback beholdt.
Ny flyt: magic-link (POST /auth/request-link → POST /auth/verify-link) + JWT-sesjon i HttpOnly/SameSite=Lax/dynamisk-Secure-cookie (30 dager), pluss /auth/logout og /auth/me. Ny migrasjon 004_auth.sql (unik e-post-indeks + magic_link_token-tabell).
Sikkerhetsdesignet fra Plan-agent-gjennomgangen holdt gjennom testing:
Token: secrets.token_urlsafe(32), kun SHA-256-hash lagres
Atomisk forbruk (UPDATE...RETURNING, ikke les-sjekk-skriv) — hindrer replay
Generisk respons uansett om e-posten finnes — hindrer enumerering
app_user opprettes først ved vellykket verifisering, ikke ved forespørsel — hindrer massopprettelse
Gamle uforbrukte lenker ugyldiggjøres når en ny utstedes
PyJWT (byttet fra python-jose pga. bredere sårbarhetsflate) med eksplisitt algorithms=["HS256"]
Ekte eksistens-sjekk mot app_user på hvert kall — en slettet bruker mister tilgang umiddelbart, ikke etter 30 dager
Alle 12 planlagte tester bestått, inkludert cooldown, token-ugyldiggjøring, utløp, tuklet JWT, slettet bruker, og at debug-headeren nå er helt uten effekt.
To ting funnet og fikset/dokumentert underveis:
ON CONFLICT (email) matchet ikke den nye partielle unike indeksen uten eksplisitt WHERE-klausul — fikset.
En reell, dypere RLS-bug (dokumentert i FEATURE_BACKLOG.md, ikke fikset her): organization-tabellens RLS-policy kaster en 500 i stedet for "se ingenting" når app.current_org leses tilbake som tomstreng (ikke NULL) på en gjenbrukt pool-tilkobling. Berører trolig alle 15 RLS-policyer i skjemaet — for stort og sensitivt (ADR-003-grunnmuren) til å hastefikse her, så jeg mitigerte det lokalt i /auth/me og satte det som punkt 1 i neste-steg-listen.
2026-07-16 15:16:53 +02:00
|
|
|
# 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",
|
|
|
|
|
)
|
|
|
|
|
|
2026-07-16 20:59:59 +02:00
|
|
|
# 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)
|
|
|
|
|
|
Alt rent, ingen byggeartefakter liggende igjen, dokumentasjonen oppdatert. Oppsummering:
Live og verifisert ende-til-ende:
/verify-siden bygget og live, e-postmalen sender nå en ekte klikkbar lenke (app/email.py/app/config.py)
Frontend containerisert (frontend/Dockerfile, standalone Next.js) og rullet ut som egen teecup_frontend-tjeneste
Caddy peker nå teecup.teeoff.no på frontend-en, som selv proxyer API-kall server-side — same-origin, ingen CORS, cookie uendret (dokumentert som ny ADR-016)
Fant og fikset en reell fallgruve: Next.js sin rewrites() bakes inn ved build-tid for standalone-output, ikke lest ved kjøretid — løst med en Docker build-time ARG
Ekte e-post sendt, ekte lenke klikket, sesjon opprettet — bekreftet av deg
Én ting du må huske: Caddy-endringen ligger i det separate /opt/teeoff-repoet (deploy/Caddyfile), ikke i teecup-repoet — den er ikke committet ennå. Lett å glemme siden resten av denne økten kun har jobbet i /opt/teecup. Vil du at jeg minner deg, eller committer du den nå selv i teeoff-repoet?
CLAUDE.md, FEATURE_BACKLOG.md og ARCHITECTURE_DECISIONS.md (ny ADR-016) er oppdatert. Klar for commit i teecup-repoet når du vil.
2026-07-17 22:28:43 +02:00
|
|
|
# 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.
|
2026-08-05 13:35:19 +02:00
|
|
|
PUBLIC_BASE_URL: str = os.getenv("TEECUP_PUBLIC_BASE_URL", "https://teecup.golf").rstrip("/")
|
Alt rent, ingen byggeartefakter liggende igjen, dokumentasjonen oppdatert. Oppsummering:
Live og verifisert ende-til-ende:
/verify-siden bygget og live, e-postmalen sender nå en ekte klikkbar lenke (app/email.py/app/config.py)
Frontend containerisert (frontend/Dockerfile, standalone Next.js) og rullet ut som egen teecup_frontend-tjeneste
Caddy peker nå teecup.teeoff.no på frontend-en, som selv proxyer API-kall server-side — same-origin, ingen CORS, cookie uendret (dokumentert som ny ADR-016)
Fant og fikset en reell fallgruve: Next.js sin rewrites() bakes inn ved build-tid for standalone-output, ikke lest ved kjøretid — løst med en Docker build-time ARG
Ekte e-post sendt, ekte lenke klikket, sesjon opprettet — bekreftet av deg
Én ting du må huske: Caddy-endringen ligger i det separate /opt/teeoff-repoet (deploy/Caddyfile), ikke i teecup-repoet — den er ikke committet ennå. Lett å glemme siden resten av denne økten kun har jobbet i /opt/teecup. Vil du at jeg minner deg, eller committer du den nå selv i teeoff-repoet?
CLAUDE.md, FEATURE_BACKLOG.md og ARCHITECTURE_DECISIONS.md (ny ADR-016) er oppdatert. Klar for commit i teecup-repoet når du vil.
2026-07-17 22:28:43 +02:00
|
|
|
|
Update Todos
Chapman: format-valg i new-round.tsx + tournament-program.tsx (session)
Nassau: nytt vindu-resultatvisning i round-detail.tsx + session-scorecard.tsx
Københavner: format-valg + poengtabell-visning i individual-tournament-detail.tsx
Bingo Bango Bongo: format-valg + per-hull picker + poengtabell
Flaggturnering: format-valg + nedtelling/resultatvisning
Shamble: format-valg + best_n + lagvisning (frittstående + org-lag)
Money Ball: format-valg + lineup_order + lagvisning (frittstående + org-lag)
High-low-high: format-valg + løpende poeng-resultatvisning (alle 3 flater)
Ekte produksjonsbuild -- FERDIG, kompilerte rent (27 ruter)
Browserverifisert alle åtte formatene -- fant og fikset ekte backend-bug (side-handicap-recompute)
Rullet ut mot ekte teecup_api/teecup_frontend -- FERDIG, health checks grønne, teeoff.no upåvirket
Rullet ut live 2026-07-30. Frontend for alle åtte nye turneringsformatene (Chapman, Nassau, Københavner, Bingo Bango Bongo, Flaggturnering, Shamble, Money Ball, High-low-high) er nå bygget, browserverifisert og live — dekker alle tre flatene (frittstående runder, org-lagturneringer, org-individuelle turneringer). Fant og fikset én reell backend-bug underveis (side-handicap ble ikke regnet på nytt når en deltakers HCP ble satt/endret etter at de allerede var tildelt en side). Ingen migrasjon i denne runden, teeoff.no upåvirket. ADR-039/åtte-formater-arbeidet er dermed helt ferdig, backend og frontend.
2026-08-01 06:51:53 +02:00
|
|
|
# Driftsvarsel til appens eier hver gang en HELT NY TeeCup-konto
|
|
|
|
|
# opprettes (2026-07-30, reist av bruker) -- ikke en hemmelighet, bare
|
|
|
|
|
# en fast mottakeradresse. Egen setting (ikke hardkodet i selve
|
|
|
|
|
# koden) slik at adressen kan endres uten en ny utrulling.
|
|
|
|
|
NEW_ACCOUNT_ALERT_EMAIL: str = os.getenv("TEECUP_NEW_ACCOUNT_ALERT_EMAIL", "hei@erol.no")
|
|
|
|
|
|
MinIO-runden er ferdig, verifisert og live — ADR-018 er nå helt komplett, ingenting utsatt igjen bortsett fra selve opplastings-skjermen i frontend.
Presiseringen din midt i byggingen (AVIF) endret arkitekturen til det bedre: droppet den opprinnelige planen med presignerte nettleser-URL-er til fordel for ekte multipart-opplasting gjennom API-et, som konverterer til AVIF (Pillow) før lagring. Dette forenklet ting betydelig — kun én MinIO-klient trengs nå, og Caddy-ruten slapp den skjøre signatur-Host-header-følsomheten presignering ville krevd.
To reelle feil funnet under scratch-verifisering, aldri i produksjon:
pillow-avif-plugin testet frittstående først — fungerer uten ekstra systempakker.
MinIO avviser understrek i vertsnavn (teecup_minio feilet umiddelbart) — rettet til teecup-minio (bindestrek) før noe ble forsøkt mot ekte infrastruktur.
Caddy-ruten unngikk en skjult DNS-felle: en ny subdomene ville pekt til en IPv6-adresse denne serveren ikke har — løst med en sti-prefiks (/teecup-media/*) på det eksisterende domenet i stedet.
Verifisert i flere lag: ekte bilde lastet opp i scratch, bekreftet konvertert til gyldig AVIF, bekreftet lesbart anonymt direkte mot MinIO, alle tre valideringsveier (ugyldig format, korrupt fil, for stor fil) testet. Etter Caddy-omstart: et ekte anonymt kall mot produksjonsdomenet ga en ekte MinIO-feilrespons — beviser ruten treffer MinIO, ikke frontend sin 404-side. teeoff.no upåvirket gjennom hele runden.
Bevisst ikke gjort: et ekte bilde lastet opp til en ekte, live turnering (ville skrevet i din ekte konto uten at du ble spurt) — tilbys, ikke utført.
Vil du at jeg tester en ekte opplasting mot en av dine eksisterende turneringer, eller går vi videre til neste V0-skjerm (økt/program, blind draw, scorekort eller leaderboard)?
2026-07-18 11:25:52 +02:00
|
|
|
# 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")
|
|
|
|
|
|
Bygget, og verifisert grundig mot ekte infrastruktur — inkludert et ekte kall mot teeoff_api (søkte opp «Borregaard», importerte Borregaard Golfklubb sin 18-hulls hovedbane med alle hull, 4 tee-farger × kjønn, ratinger, og opprettet faktisk en økt med den importerte banen). Duplikat-import ble korrekt avvist (409), kryss-org-isolasjon holder, test_isolation.sql 12/12.
To ting gjenstår, begge mot ekte infrastruktur — vil du bekrefte at jeg går videre?
Migrasjon 010_official_course_unique_ref.sql mot ekte teecup_db — kun én ny partiell unik-indeks (organization_id, external_course_ref), rører ingen eksisterende rader (alle er source='custom' med external_course_ref IS NULL i dag)
docker compose up -d --build teecup_api teecup_frontend — ny backend-kode (courses.py, teeoff_client.py, httpx-avhengighet) + ny frontend-kode (bane-søk mot teeoff i program-skjemaet)
2026-07-18 12:44:05 +02:00
|
|
|
# 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")
|
|
|
|
|
|
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
|
|
|
# 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
|
2026-08-05 13:35:19 +02:00
|
|
|
VAPID_SUBJECT: str = os.getenv("TEECUP_VAPID_SUBJECT", "https://teecup.golf")
|
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
|
|
|
PUSH_CONFIGURED: bool = bool(VAPID_PRIVATE_KEY and VAPID_PUBLIC_KEY)
|
|
|
|
|
|
2026-08-07 22:02:56 +02:00
|
|
|
# Mapbox Static Images API (ADR-048) -- serverside genererte satellitt-
|
|
|
|
|
# utsnitt for delte slag-målinger. Egen, HEMMELIG token -- IKKE samme
|
|
|
|
|
# som frontendens offentlige NEXT_PUBLIC_MAPBOX_TOKEN (URL-restriktert,
|
|
|
|
|
# trygt eksponert i nettleseren). Denne skal ALDRI nå klienten. Valgfritt,
|
|
|
|
|
# samme grasiøs-degraderings-mønster som SMTP/push: deling uten satellitt-
|
|
|
|
|
# bilde faller tilbake til ren tekst hvis ikke satt.
|
|
|
|
|
MAPBOX_SECRET_TOKEN: str | None = os.environ.get("TEECUP_MAPBOX_SECRET_TOKEN", "").strip() or None
|
|
|
|
|
|
2026-07-16 07:26:04 +02:00
|
|
|
|
|
|
|
|
settings = Settings()
|