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
outcomePricesals 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)
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.
# 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):
# 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
fetchMatchesAndTeamsinlib/feeds.tsneu schreiben, sodass esMatch[]+Team[]liefert. - Odds-Matching:
attachOddsverknü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 inlib/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:
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 A–L.
- 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.