Files
wm-projekt/README.md
2026-06-22 15:44:54 -05:00

152 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WM 2026 Board
Live-Gruppentabellen, Drittplatzierten-Wertung und vollständiger K.o.-Baum der
FIFA WM 2026 — mit korrekter **Annex-C-Zuordnung** der acht besten Gruppendritten
und optionalen **Polymarket-Wahrscheinlichkeiten** pro Spiel.
Gebaut als Next.js 15 (App Router) mit serverseitigem Feed-Proxy und Caching,
Docker-Standalone-Image, ausgelegt für Caddy als Reverse Proxy.
## Warum ein Backend-Proxy?
Das Frontend kann die Feeds **nicht direkt** aufrufen:
- **football-data.org** erlaubt im Free-Tier nur 10 Anfragen/Minute und verlangt
einen `X-Auth-Token` (im Browser nicht sicher unterzubringen).
- **Polymarket Gamma** liefert `outcomePrices` als JSON-String und hat Rate Limits.
- Beide setzen kein CORS für beliebige Browser-Origins.
Deshalb holt die Route `app/api/matches` beide Feeds **serverseitig**, cached sie
in-memory (Spiele 60 s, Odds 120 s) und liefert ein fertiges JSON. Das Frontend
pollt nur diese eine eigene Route (alle 30 s).
## Architektur
```
Browser ──poll 30s──> /api/matches (Next.js, gecacht)
├── football-data.org/v4/competitions/WC/matches
└── gamma-api.polymarket.com/events?slug=...
├── computeGroupTables() (FIFA-Tiebreaker)
├── computeThirdPlaceTable() (beste 8 Dritte)
└── resolveAnnexC() (495 Szenarien, lib/annexc-data.ts)
```
Die Annex-C-Tabelle (`lib/annexc-data.ts`) enthält alle 495 möglichen
Kombinationen aus den FIFA-Wettbewerbsregeln. Sobald genau 8 Gruppendritte
feststehen, ordnet `resolveAnnexC()` jedem der acht betroffenen Gruppensieger
(A, B, D, E, G, I, K, L) seinen Drittplatzierten zu, und der Bracket „verbindet"
sich automatisch.
## Schnellstart (lokal)
```bash
cp .env.example .env # FOOTBALL_DATA_TOKEN eintragen
npm install
npm run dev # http://localhost:3000
```
Token kostenlos registrieren: https://www.football-data.org/client/register
## Deployment auf dem VPS (Docker + Caddy)
Das Projekt geht davon aus, dass Caddy bereits als Reverse Proxy in einem
externen Docker-Netz läuft. Heißt dein Netz anders, in `docker-compose.yml`
und im Caddy-Snippet anpassen.
```bash
# 1. Netz prüfen/anlegen (falls noch nicht vorhanden)
docker network ls | grep caddy_net || docker network create caddy_net
# 2. .env anlegen
cp .env.example .env && nano .env
# 3. Bauen und starten
docker compose up -d --build
# 4. Caddy-Route einbinden
# Inhalt von Caddyfile.snippet in deinen Caddyfile übernehmen,
# Domain anpassen, dann Caddy neu laden:
docker exec -w /etc/caddy caddy caddy reload
```
Danach ist die Seite unter `https://wm.deine-domain.de` erreichbar. Caddy
besorgt TLS automatisch.
## Konfiguration (.env)
| Variable | Pflicht | Beschreibung |
|-----------------------|---------|--------------|
| `FOOTBALL_DATA_TOKEN` | ja | Token von football-data.org. Ohne ihn liefert die API nur die Wettbewerbsliste. |
| `POLYMARKET_WC_SLUG` | nein | Slug des WM-Events auf Polymarket (Standard `world-cup-2026`). Den exakten Slug aus der URL nach `/event/` ablesen. |
## Feeds verifizieren
Beide Upstream-Strukturen vor dem Turnierstart kurz gegenchecken (Felder können
sich ändern):
```bash
# football-data: Gruppenfeld "GROUP_X", score.fullTime, stage
curl -H "X-Auth-Token: $FOOTBALL_DATA_TOKEN" \
"https://api.football-data.org/v4/competitions/WC/matches" | jq '.matches[0]'
# Polymarket: markets[].outcomes / outcomePrices (JSON-Strings!)
curl "https://gamma-api.polymarket.com/events?slug=world-cup-2026" | jq '.[0].markets[0]'
```
Stimmen Feldnamen nicht, nur die Adapter in `lib/feeds.ts` anpassen — der Rest
(Tabellen, Annex C, Bracket) ist davon entkoppelt.
## Was anpassbar ist
- **Anderer Ergebnis-Feed** (API-Football, TheStatsAPI): nur `fetchMatchesAndTeams`
in `lib/feeds.ts` neu schreiben, sodass es `Match[]` + `Team[]` liefert.
- **Odds-Matching**: `attachOdds` verknüpft per Teamname im Markttitel. Bei
abweichenden Namen (z. B. „USA" vs. „United States") dort eine Alias-Tabelle ergänzen.
- **Poll-Intervall**: in `app/page.tsx` (`setInterval`, Standard 30 s) und den
Cache-TTLs in `lib/feeds.ts`.
## Korrektheit der Annex-C-Logik
`lib/annexc-data.ts` wurde gegen die veröffentlichte FIFA-/Wikipedia-Tabelle
geprüft. Beispiel Szenario 415 (Dritte aus A,B,C,E,F,H,I,J) ergibt die Zuordnung
`H J B C A F E I` für die Gruppensieger A,B,D,E,G,I,K,L — exakt wie in den
offiziellen Regeln.
## Lizenz / Hinweise
Kein offizielles FIFA-Produkt. football-data.org ist für nicht-kommerzielle
Nutzung kostenlos; bei kommerzieller Nutzung dort anfragen. Polymarket-Zugang
und -Daten unterliegen den jeweiligen lokalen Bestimmungen.
## Lokal testen (ohne VPS/Caddy)
Für den lokalen Docker-Test gibt es eine separate Compose-Datei ohne externes
Caddy-Netz und mit direktem Port-Mapping:
```bash
cp .env.example .env # FOOTBALL_DATA_TOKEN eintragen
docker compose -f docker-compose.local.yml up --build
# -> http://localhost:3000
```
Die VPS-Variante (`docker compose up --build`) bleibt davon unberührt.
## Tabs & Bedienung
- **Gruppen** — alle 12 Tabellen mit Flaggen. Klick auf einen Gruppen-Header
öffnet die **Spiele** dieser Gruppe.
- **Spiele** — Anstoßzeit/Ergebnis/Live-Minute, Flaggen, Austragungsort und
(falls im Feed) Zuschauerzahl. Der Tab merkt sich die zuletzt gewählte Gruppe
und zeigt sie im Label an („Spiele Gruppe H"). Oben ein Chip-Wechsler AL.
- **Drittplatzierte** — gruppenübergreifende Wertung, Schnittlinie bei Platz 8/9.
- **K.o.-Baum** — fixe Teams stehen voll ausgeschrieben mit grünem Marker;
vorläufige (nur nach aktueller Tabelle, Gruppe noch nicht durchgespielt oder
Annex C noch offen) erscheinen kursiv mit „≈"-Zeichen. Flaggen inklusive.
## Flaggen
Die Flaggen kommen aus dem `crest`-Feld von football-data.org (SVG-URLs auf
`crests.football-data.org`). Fehlt eine URL, zeigt die App einen neutralen Kreis
mit Ländercode als Fallback.