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:
@@ -68,6 +68,16 @@ export class TournamentState {
|
||||
return this.fixtures;
|
||||
}
|
||||
|
||||
/** Which fixture number does a (home, away) pairing map to? Used by the
|
||||
* ingestion layer to key provider event ids against our fixtures. */
|
||||
matchFixtureNum(home: string, away: string, stage: Fixture['stage'] = 'group', kickoff: string | null = null): number | null {
|
||||
const f = this.findFixture({
|
||||
home, away, group: null, stage, kickoff,
|
||||
homeScore: null, awayScore: null, status: 'scheduled', minute: null,
|
||||
});
|
||||
return f?.num ?? null;
|
||||
}
|
||||
|
||||
/** True if any match is live or kicks off within `windowMs` of now — used to
|
||||
* poll the APIs often during match windows and rarely when nothing is on. */
|
||||
hasLiveActivity(windowMs: number): boolean {
|
||||
|
||||
Reference in New Issue
Block a user