v2 Phase 1: ingestion + SQLite foundation

- Persistence via Node's built-in node:sqlite (zero native deps) on a Docker
  volume: response cache, source health, fixture↔provider id map, per-fixture
  enrichment, ingest log. Runtime bumped to node:24-slim + --experimental-sqlite.
- Resilient fetcher: DB cache + per-source rate-limit + jittered backoff +
  circuit-breaker (blocked sources trip + skip; UI reads DB, never breaks).
- ESPN hidden API as the PRIMARY rich source (works from the VPS where SofaScore
  403s): scoreboard (live scores w/ clock) + summary (venue, H2H, recent form,
  lineups, team stats). football-data / SofaScore are fallbacks.
- Scheduler: maps all 72 group fixtures to ESPN event ids, polls live on a
  dynamic cadence, enriches imminent fixtures into match_ext. /api/sources/health.
- Verified: DB populates (real H2H + form for opening matches), 22 tests pass,
  Docker image runs node:sqlite on the volume and persists across restart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 15:21:01 +02:00
parent 9c7605391c
commit b6f62679c9
13 changed files with 697 additions and 88 deletions
+8 -1
View File
@@ -14,10 +14,14 @@ services:
- STATIC_DIR=/app/dist
- NODE_ENV=production
- ALLOWED_ORIGINS=https://cup26.briggen.dev
- DATA_DIR=/data
# football-data.org free API token (https://www.football-data.org/client/register)
- FOOTBALL_DATA_TOKEN=${FOOTBALL_DATA_TOKEN:-}
# opt-in: layer the unofficial real-time source on top of the free API
# opt-in unofficial sources (usually blocked from a datacenter IP; ESPN is primary)
- ENABLE_SOFASCORE=${ENABLE_SOFASCORE:-false}
- ENABLE_FOTMOB=${ENABLE_FOTMOB:-false}
volumes:
- cup26-data:/data
security_opt:
- no-new-privileges:true
mem_limit: 384m
@@ -33,6 +37,9 @@ services:
networks:
- proxy
volumes:
cup26-data:
networks:
proxy:
external: true