'use strict'; /** * GET /api/live/:sport — LIVE TRACKING read (A1 board, Session 11). * * Public, rate-limited 60/min. Cache-aside via liveTrackingService: * reads `live:{sport}:{date}` (TTL 90s); on a miss it consults the day's * schedule and fetches boxscores ONLY when games are actually live — so the * total upstream cost is 1 schedule call + N-live-games boxscore calls per * 90s window across ALL users, and zero boxscore calls when nothing is live * (specs/LIVE-TRACKING.md POLLING RULE). * * This is TRACKING, not re-grading — grades are locked pre-game and this * endpoint never touches them. */ const express = require('express'); const { createRateLimit } = require('../middleware/rateLimit'); const { getLiveTracking } = require('../services/liveTrackingService'); const router = express.Router(); router.use(createRateLimit({ windowMs: 60_000, max: 60 })); const LIVE_SPORTS = new Set(['mlb', 'wnba']); router.get('/:sport', async (req, res) => { const sport = String(req.params.sport || '').toLowerCase(); if (!LIVE_SPORTS.has(sport)) { // Honest empty for sports without a free live box feed — never an error. return res.json({ sport, hasLive: false, games: [] }); } try { const data = await getLiveTracking(sport); // Short CDN/browser cache — matches the 90s shared window without // holding a live slate stale for long. res.set('Cache-Control', 'public, max-age=30'); return res.json(data); } catch (err) { console.error('[live]', err.message); return res.status(200).json({ sport, hasLive: false, games: [] }); } }); module.exports = router;