add TSS API endpoints + web proxy (#1234)

Adds /api/tss/teams/* and /api/tss/leaderboard/teams to SREBOT/server.js,
reading tss_battles.db and tss_teams.db with queries adapted to the
team_id / team_name / teams_data schema. Mirrors the existing
/api/squadrons/* endpoints. SREBOT/web/server.js gains matching proxy
routes so the frontend can reach them via the website host.

TSSBOT/BOT/storage.py picks up the columns and table the endpoints need
to return meaningful data: teams_data.clanrating, team_members.points,
and a new teams_points history table. All idempotent under the existing
init_tss_dbs() call.

Co-authored-by: Heidi <clippii@protonmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
NotSoToothless
2026-05-14 14:21:58 -07:00
committed by GitHub
parent ec08655a59
commit 6e66d23313
2 changed files with 770 additions and 0 deletions
+92
View File
@@ -2181,6 +2181,98 @@ app.get('/api/squadrons/:name/games', cors(apiCorsOptions), async (req, res) =>
}
});
// ── TSS proxy routes ───────────────────────────────────────────────────────
// Mirrors the /api/squadrons/* proxy block above but targets the upstream
// /api/tss/* endpoints (defined in SREBOT/server.js) which read tss_battles.db
// and tss_teams.db. Both /api/tss/teams/* and /api/tss/squadrons/* are
// supported upstream — we proxy the canonical /teams/* path here.
const TSS_BACKEND = () => process.env.EXTERNAL_API_URL || 'http://127.0.0.1:6000';
function _forwardDateParams(req) {
const qp = new URLSearchParams();
if (req.query.start_date) qp.append('start_date', req.query.start_date);
if (req.query.end_date) qp.append('end_date', req.query.end_date);
if (req.query.season) qp.append('season', req.query.season);
if (req.query.week) qp.append('week', req.query.week);
return qp;
}
app.get('/api/tss/teams/resolve', cors(apiCorsOptions), async (req, res) => {
try {
const name = req.query.name || req.query.q || req.query.team || '';
const apiUrl = `${TSS_BACKEND()}/api/tss/teams/resolve?name=${encodeURIComponent(name)}`;
const response = await fetch(apiUrl);
if (!response.ok) return res.status(response.status).json({ error: 'Failed to resolve TSS team' });
res.json(await response.json());
} catch (err) {
log.error('Error resolving TSS team', err);
res.status(500).json({ error: 'Failed to resolve TSS team' });
}
});
app.get('/api/tss/leaderboard/teams', cors(apiCorsOptions), async (req, res) => {
try {
const qp = _forwardDateParams(req);
if (req.query.limit) qp.append('limit', req.query.limit);
const qs = qp.toString();
const apiUrl = `${TSS_BACKEND()}/api/tss/leaderboard/teams${qs ? '?' + qs : ''}`;
const response = await fetch(apiUrl);
if (!response.ok) return res.status(response.status).json({ error: 'Failed to fetch TSS leaderboard', teams: [], total_teams: 0 });
res.json(await response.json());
} catch (err) {
log.error('Error in TSS leaderboard proxy', err);
res.status(500).json({ error: 'Failed to fetch TSS leaderboard', teams: [], total_teams: 0 });
}
});
app.get('/api/tss/teams/:name', cors(apiCorsOptions), async (req, res) => {
try {
const qs = _forwardDateParams(req).toString();
const apiUrl = `${TSS_BACKEND()}/api/tss/teams/${encodeURIComponent(req.params.name)}${qs ? '?' + qs : ''}`;
const response = await fetch(apiUrl);
if (!response.ok) {
if (response.status === 404) {
return res.status(404).json({ error: 'TSS team not found', players: [] });
}
return res.status(response.status).json({ error: 'Failed to fetch TSS team details' });
}
res.json(await response.json());
} catch (err) {
log.error('Error fetching TSS team details', err);
res.status(500).json({ error: 'Failed to fetch TSS team details' });
}
});
app.get('/api/tss/teams/:name/history', cors(apiCorsOptions), async (req, res) => {
try {
const apiUrl = `${TSS_BACKEND()}/api/tss/teams/${encodeURIComponent(req.params.name)}/history`;
const response = await fetch(apiUrl);
if (!response.ok) return res.status(response.status).json({ error: 'Failed to fetch TSS team history' });
res.json(await response.json());
} catch (err) {
log.error('Error fetching TSS team history', err);
res.status(500).json({ error: 'Failed to fetch TSS team history' });
}
});
app.get('/api/tss/teams/:name/games', cors(apiCorsOptions), async (req, res) => {
try {
const qs = _forwardDateParams(req).toString();
const apiUrl = `${TSS_BACKEND()}/api/tss/teams/${encodeURIComponent(req.params.name)}/games${qs ? '?' + qs : ''}`;
const response = await fetch(apiUrl);
if (!response.ok) {
if (response.status === 404) {
return res.status(404).json({ games: [], total_games_returned: 0, error: 'TSS team not found' });
}
return res.status(response.status).json({ error: 'Failed to fetch TSS team games' });
}
res.json(await response.json());
} catch (err) {
log.error('Error fetching TSS team games', err);
res.status(500).json({ error: 'Failed to fetch TSS team games', games: [], total_games_returned: 0 });
}
});
// API Routes with better error handling
let statsCache = null;
let statsCacheTime = 0;