init
This commit is contained in:
120
README.md
Normal file
120
README.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user