Session 23: All-day intelligence layer — schedule, game lines, streaks, hot lists, stat filtering, ParlayAPI dead (1567 tests)
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* /api/schedule/:sport (Session 23)
|
||||
*
|
||||
* Returns today's game schedule from cached/free ESPN data. NO odds-api
|
||||
* credits burned. The PM2 pollers warm this cache every 60s; on a miss
|
||||
* the schedule service self-heals by fetching the free ESPN scoreboard.
|
||||
*
|
||||
* Each game carries two boolean flags read from OTHER caches (no fetch):
|
||||
* hasOdds — odds-api player props exist for this slate
|
||||
* hasGameLines — Tank01 game-level lines exist for this slate
|
||||
*
|
||||
* Response shape:
|
||||
* {
|
||||
* sport: 'nba',
|
||||
* date: '2026-06-12',
|
||||
* games: [ { id, homeTeam, awayTeam, gameTime, status, score,
|
||||
* venue, broadcast, hasOdds, hasGameLines } ],
|
||||
* source: 'espn',
|
||||
* }
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const scheduleService = require('../services/scheduleService');
|
||||
const { SPORT_CONFIG } = require('../config/sports');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const MISSION_HEADER = { 'X-VYNDR-Mission': 'The slate is never empty' };
|
||||
|
||||
router.get('/:sport', async (req, res) => {
|
||||
const sport = String(req.params.sport || '').toLowerCase();
|
||||
const date = req.query.date || scheduleService.todayET();
|
||||
|
||||
if (!SPORT_CONFIG[sport]) {
|
||||
return res.status(404).set(MISSION_HEADER).json({ error: `Unknown sport: ${sport}` });
|
||||
}
|
||||
|
||||
try {
|
||||
const raw = await scheduleService.getSchedule(sport, date);
|
||||
// null → unsupported sport (already guarded above); treat defensively.
|
||||
const games = await scheduleService.enrichFlags(sport, date, raw || []);
|
||||
return res.set(MISSION_HEADER).json({
|
||||
sport,
|
||||
date,
|
||||
games,
|
||||
source: 'espn',
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`[schedule/${sport}]`, err.message);
|
||||
// Even on error we return an empty slate, not a 5xx — the platform
|
||||
// is NEVER down. Other layers (game lines, props) keep it alive.
|
||||
return res.set(MISSION_HEADER).json({ sport, date, games: [], source: 'espn' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user