103 lines
4.0 KiB
JavaScript
103 lines
4.0 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* /api/content (Session 29)
|
|
*
|
|
* Structured content objects generated from live VYNDR data, degrading
|
|
* gracefully by data level. Read-only, zero-credit (reads cached data).
|
|
* `?format=text` additionally returns post-ready plain text.
|
|
*
|
|
* GET /api/content/slate/:sport → daily slate thread
|
|
* GET /api/content/potd/:sport → prop (or game) of the day
|
|
* GET /api/content/recap/:sport → results recap (needs resolved grades)
|
|
* GET /api/content/preview/:sport/:gameId → matchup preview for one game
|
|
*/
|
|
|
|
const express = require('express');
|
|
const template = require('../services/contentTemplateService');
|
|
const formatter = require('../services/contentFormatter');
|
|
const { cacheGet } = require('../utils/redis');
|
|
|
|
const router = express.Router();
|
|
|
|
const MISSION_HEADER = { 'X-VYNDR-Mission': 'Every post is a free ad' };
|
|
const SUPPORTED = new Set(['nba', 'wnba', 'mlb', 'soccer', 'nfl', 'nhl']);
|
|
|
|
function guard(req, res) {
|
|
const sport = String(req.params.sport || '').toLowerCase();
|
|
if (!SUPPORTED.has(sport)) {
|
|
res.status(404).set(MISSION_HEADER).json({ error: `No content for sport: ${sport}` });
|
|
return null;
|
|
}
|
|
return sport;
|
|
}
|
|
|
|
router.get('/slate/:sport', async (req, res) => {
|
|
const sport = guard(req, res);
|
|
if (!sport) return undefined;
|
|
try {
|
|
const data = await template.collectSlateData(sport);
|
|
const thread = template.generateSlateThread(sport, data);
|
|
const body = { ...thread };
|
|
if (req.query.format === 'text') body.text = formatter.formatSlateThread(thread);
|
|
return res.set(MISSION_HEADER).json(body);
|
|
} catch (err) {
|
|
console.error(`[content/slate/${sport}]`, err.message);
|
|
return res.status(500).set(MISSION_HEADER).json({ error: 'Content generation failed' });
|
|
}
|
|
});
|
|
|
|
router.get('/potd/:sport', async (req, res) => {
|
|
const sport = guard(req, res);
|
|
if (!sport) return undefined;
|
|
try {
|
|
const data = await template.collectSlateData(sport);
|
|
const potd = template.generatePOTD(sport, data);
|
|
const body = { ...potd };
|
|
if (req.query.format === 'text') body.text = formatter.formatPOTD(potd);
|
|
return res.set(MISSION_HEADER).json(body);
|
|
} catch (err) {
|
|
console.error(`[content/potd/${sport}]`, err.message);
|
|
return res.status(500).set(MISSION_HEADER).json({ error: 'Content generation failed' });
|
|
}
|
|
});
|
|
|
|
router.get('/recap/:sport', async (req, res) => {
|
|
const sport = guard(req, res);
|
|
if (!sport) return undefined;
|
|
try {
|
|
// Resolved grades land in a daily cache when the grading flow settles.
|
|
const utc = new Date().toISOString().split('T')[0];
|
|
const cache = (await cacheGet(`results:${sport}:${utc}`)) ?? (await cacheGet(`results:${sport}`));
|
|
const resolved = Array.isArray(cache) ? cache : Array.isArray(cache?.grades) ? cache.grades : [];
|
|
const recap = template.generateResultsRecap(sport, resolved);
|
|
const body = { ...recap };
|
|
if (req.query.format === 'text') body.text = formatter.formatRecap(recap);
|
|
return res.set(MISSION_HEADER).json(body);
|
|
} catch (err) {
|
|
console.error(`[content/recap/${sport}]`, err.message);
|
|
return res.status(500).set(MISSION_HEADER).json({ error: 'Content generation failed' });
|
|
}
|
|
});
|
|
|
|
router.get('/preview/:sport/:gameId', async (req, res) => {
|
|
const sport = guard(req, res);
|
|
if (!sport) return undefined;
|
|
const { gameId } = req.params;
|
|
try {
|
|
const data = await template.collectSlateData(sport);
|
|
const game = (data.schedule || []).find((g) => String(g.id) === String(gameId));
|
|
if (!game) {
|
|
return res.status(404).set(MISSION_HEADER).json({ error: 'Game not on today\'s schedule.' });
|
|
}
|
|
const lines = template.__internals.findGameLinesFor(game, data.gameLines);
|
|
const preview = template.generateMatchupPreview(game, lines, data.streaks);
|
|
return res.set(MISSION_HEADER).json(preview);
|
|
} catch (err) {
|
|
console.error(`[content/preview/${sport}]`, err.message);
|
|
return res.status(500).set(MISSION_HEADER).json({ error: 'Content generation failed' });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|