Update Todos

Refaktorere get_leaderboard til delt fetch_leaderboard()

Gjøre own_team_ids null-sikker for anonyme/uinnloggede tilskuere

Refaktorere list_matches og get_scorecard til delte funksjoner

Legge til offentlige leaderboard/matches/scorecard-endepunkter i registration.py

Skrive ADR-026 (tilskuer-rolle)

Scratch-verifisere backend

Bygge frontend: offentlig live-visning (leaderboard + matcher + scorekort) på /t/[id]/live

Ekte typesjekket frontend-build

Deploy etter bekreftelse + oppdatere .md-filer
Tilskuer-rollen er live. Kjernen: ingen ny rolle trengtes — «tilskuer» er ganske enkelt enhver som allerede kan se turneringen (per synlighetsnivå), nå utvidet til å dekke leaderboard, matchliste og fullt hull-for-hull-scorekort, ikke bare info-siden og programtider som før. Ny /t/[id]/live-side, lenket fra hovedsiden.

Fant og lukket to sikkerhetshull under design (før noe ble bygget): en manglende sjekk på at økt/match i URL-en faktisk hører til turneringen i samme URL (ellers kunne noen lest en hvilken som helst økt i samme organisasjon ved å gjette id-er), og en manglende reveal-sjekk på scorekortet. Begge verifisert eksplisitt med egne tester — 16 automatiserte sjekker totalt, inkludert en presis test med to ekte turneringer i samme organisasjon.

.md-filene er oppdatert. Gjenværende åpne punkter: PWA, kode-regenerering, korrigering-godkjenning, sanntid for leaderboard-siden (WebSocket-mekanismen finnes allerede, bare ikke koblet til der ennå), og de nye turneringsformatene som ble notert tidligere.
This commit is contained in:
Erol Haagenrud 2026-07-19 22:53:33 +02:00
parent bfd9633c63
commit 254cb92c7a
3 changed files with 66 additions and 12 deletions

View file

@ -1153,9 +1153,9 @@ synlighetslogikk, bare eksisterende logikk gjort tilgjengelig for en
scorekortet har ingen tilsvarende innebygd sjekk (kan i prinsippet scorekortet har ingen tilsvarende innebygd sjekk (kan i prinsippet
inneholde registrerte hull før reveal, selv om det ikke er normal flyt). inneholde registrerte hull før reveal, selv om det ikke er normal flyt).
**Status: ✅ BYGGET 2026-07-19.** Ingen migrasjon (kun nye endepunkter + **Status: ✅ BYGGET OG LIVE 2026-07-19.** Ingen migrasjon (kun nye
refaktorering av eksisterende spørringer til delte funksjoner). Se endepunkter + refaktorering av eksisterende spørringer til delte
CLAUDE.md-status for scratch-verifisering og utrulling. funksjoner). Se CLAUDE.md-status for scratch-verifisering og utrulling.
--- ---

View file

@ -1577,18 +1577,72 @@ Ferdig og verifisert:
egen JSON-404 (`{"detail":"Not Found"}`) og ikke Next.js sin HTML-404 — egen JSON-404 (`{"detail":"Not Found"}`) og ikke Next.js sin HTML-404 —
beviser Caddy-ruten faktisk treffer `teecup_api`, ikke `teecup_frontend`. beviser Caddy-ruten faktisk treffer `teecup_api`, ikke `teecup_frontend`.
- **Tilskuer-rolle LIVE (2026-07-19, ADR-026):** brukeren valgte dette som
neste steg rett etter Kommunikasjon-runden — "tilskuer" var bevisst
utsatt til feed-synligheten (ADR-025) fantes, og nå gjorde den det.
**Kjernebeslutning:** ingen ny rolle/tabell — "tilskuer" er ganske enkelt
enhver som kan SE en turnering per `tournament.visibility` (ADR-018),
utvidet til også å dekke LIVE-data (leaderboard, matcher, scorekort), ikke
bare info-siden/programtidene som før.
**Bygget:** `GET /orgs/.../leaderboard`, `.../sessions/{id}/matches` og
`.../matches/{id}/scorecard` fantes allerede (organisator-/spiller-siden),
men krevde org-medlemskap — en ren spectator kunne aldri se dem. Løst ved
å ekstrahere den delte kjernelogikken til gjenbrukbare funksjoner
(`fetch_leaderboard` i tournaments.py, `fetch_matches` i matches.py,
`fetch_scorecard` i scoring.py — samme "gjort delt"-mønster som tidligere
runder), og la tre nye offentlige endepunkter i `registration.py` kalle
dem etter egen visibility-sjekk. `own_team_ids()` (`blind_draw.py`) gjort
null-sikker (`user_id: str | None`) -- en anonym leser har per definisjon
ingen egne lag, korrekt oppførsel er tom mengde (ser kun avslørte
matcher), ikke en feil.
**To nye sikkerhetssjekker funnet under DESIGN, ikke i etterkant, samme
disiplin som tidligere ADR-018 Beslutning B-lærdommen:**
1. `session_id`/`match_id` i URL-en må eksplisitt verifiseres å høre til
NØYAKTIG `tournament_id` i samme URL — `org_connection()` setter kun
TENANT-grensen (RLS), ikke at stiens id-er faktisk henger sammen. Uten
dette kunne noen med tilgang til én offentlig turnering i en
organisasjon lest en HVILKEN SOM HELST økt/match i samme organisasjon
(inkl. en privat en) ved å gjette/prøve id-er.
2. Scorekortet krever eksplisitt at BEGGE lag har låst oppstillingen
(blind draw, ADR-013) — leaderboard/matchliste arver reveal-skjuling
automatisk via `own_team_ids()`, men scorekortet har ingen tilsvarende
innebygd sjekk.
**Bruker valgte omfang utover anbefalingen:** BÅDE leaderboard+matchliste
OG fullt hull-for-hull-scorekort per match i samme runde (anbefalingen var
kun de to første).
**Frontend:** ny `/t/[id]/live`-side (`components/public-live.tsx`) —
fargesegmentert stillingsbar (faktisk + projisert, samme visuelle idé som
den org-autentiserte leaderboard-skjermen, men egen enklere implementasjon
siden komponentene har ulik autentiseringskontekst), utvidbar øktliste →
matchliste → hull-for-hull-scorekort (fargede hull-chips per lag). Lenket
fra hovedsiden (`public-tournament.tsx`) med en ny "Følg live"-knapp.
**Verifisert grundig mot fersk `teecup_scratch`** (isolert
`teecup_app_scratch`-rolle, isolert scratch-MinIO, engangs API-container):
16 automatiserte sjekker, inkl. en PRESIS test av den nye tenant-vs-sti-
sjekken (ikke bare en ukjent id, men en EKTE ANNEN turnering i SAMME org
— bekreftet at match/økt fra turnering A fortsatt ikke kan leses via
turnering B sin offentlige URL), full blind-draw-skjuling FØR/ETTER
reveal for en anonym leser, kode-overstyring (ADR-020) fungerer uendret
for de nye endepunktene, scorekort eksplisitt nektet før reveal. Ekte
typesjekket produksjonsbuild av frontend, inkl. den nye `/t/[id]/live`-
ruten.
**Rullet ut live 2026-07-19**, bruker bekreftet eksplisitt: ingen
migrasjon, `docker compose up -d --build teecup_api teecup_frontend`,
begge containere boot-et rent, `/health`/`dashboard` → 200, `teeoff.no`
upåvirket.
Neste steg: Neste steg:
1. PWA-egenskaper (manifest, service worker, offline-cache) — ikke startet. 1. PWA-egenskaper (manifest, service worker, offline-cache) — ikke startet.
2. Fortsatt åpne beslutninger fra FEATURE_BACKLOG.md: tilskuer-rolle (bevisst 2. Fortsatt åpne beslutninger fra FEATURE_BACKLOG.md: kode-regenerering for
utsatt til nå at Kommunikasjon er ferdig — bør tas fatt på snart, samme ADR-020, korrigering-godkjenning fra motpart, live-oppdatering av
begrunnelse som før: definer sammen med feed-synligheten, som nå finnes), `/t/[id]/live`/leaderboard via WebSockets (samme mekanisme som meldinger
kode-regenerering for ADR-020, korrigering-godkjenning fra motpart, live finnes og kan gjenbrukes — i dag må siden lastes på nytt for nye
leaderboard (samme WebSocket-mekanisme som meldinger kan gjenbrukes, ikke resultater, ikke koblet til sanntid ennå), video/1-til-1-meldinger
koblet til ennå), video/1-til-1-meldinger (bevisst utsatt i ADR-025). (bevisst utsatt i ADR-025).
3. Flere turneringsformater utover Ryder Cup (Københavner/High-low-high/ 3. Flere turneringsformater utover Ryder Cup (Københavner/High-low-high/
Robbins/Try all, notert 2026-07-19) — ingen ADR-runde startet ennå. Robbins/Try all, notert 2026-07-19) — ingen ADR-runde startet ennå.
4. **Merk for neste økt:** `deploy/Caddyfile`-endringen (ny `/ws/*`-rute) 4. **Merk for neste økt:** `deploy/Caddyfile`-endringen (ny `/ws/*`-rute,
ligger uncommitted i det SEPARATE `/opt/teeoff`-repoet, ikke i ADR-025) ligger uncommitted i det SEPARATE `/opt/teeoff`-repoet, ikke i
`teecup`-repoet — samme fallgruve som ADR-016-runden sin Caddy-endring, `teecup`-repoet — samme fallgruve som ADR-016-runden sin Caddy-endring,
lett å glemme siden denne økten ellers kun har jobbet i `/opt/teecup`. lett å glemme siden denne økten ellers kun har jobbet i `/opt/teecup`.
5. Ved fremtidige nye V0-skjermer/-design i V0 (fortsett i samme prosjekt), 5. Ved fremtidige nye V0-skjermer/-design i V0 (fortsett i samme prosjekt),

View file

@ -323,7 +323,7 @@
`user_is_match_participant`, brukt av `scoring.py`) — ikke lenger «noen på `user_is_match_participant`, brukt av `scoring.py`) — ikke lenger «noen på
laget», og uavhengig av kapteinmerket. Erstatter «Scoring-autorisasjon»- laget», og uavhengig av kapteinmerket. Erstatter «Scoring-autorisasjon»-
punktet under, spørsmål (a) er dermed besvart. punktet under, spørsmål (a) er dermed besvart.
- **Tilskuer — ✅ BYGGET OG LIVE 2026-07-19 (ADR-026),** rett etter - **Tilskuer — ✅ HELT FERDIG, LIVE 2026-07-19 (ADR-026),** rett etter
Kommunikasjon (ADR-025) som gjorde feed-synligheten (som denne skulle Kommunikasjon (ADR-025) som gjorde feed-synligheten (som denne skulle
defineres sammen med) klar. Ingen ny rolle/tabell — «tilskuer» er ganske defineres sammen med) klar. Ingen ny rolle/tabell — «tilskuer» er ganske
enkelt enhver som kan SE turneringen (`tournament.visibility`), nå også enkelt enhver som kan SE turneringen (`tournament.visibility`), nå også