From 50439720dd112dc3eed1406d9c27218058a2148d Mon Sep 17 00:00:00 2001 From: Heidi Date: Sat, 20 Jun 2026 00:50:28 +0100 Subject: [PATCH] ai generated solutions to our ai generated problems --- README.md | 6 + frontend/src/App.jsx | 25 +--- frontend/src/main.jsx | 51 +-------- server.cjs | 259 ++++++++++++------------------------------ 4 files changed, 86 insertions(+), 255 deletions(-) diff --git a/README.md b/README.md index 2afa7f2..3497eee 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,12 @@ API_RATE_LIMIT_WINDOW_MS=60000 API_RATE_LIMIT_MAX=120 ``` +On startup, the web server preloads the critical public snapshots before +signalling PM2 `ready`: team leaderboard, player leaderboard, home teams, and +recent games. `/health` includes a `public_data` block with the latest preload +status. A same-origin `POST /api/cache/prewarm` refreshes those snapshots on +demand. + ## Reverse proxy / Cloudflare The server only honours `CF-Connecting-IP`, `X-Forwarded-For`, `X-Forwarded-Proto`, diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 8801076..5c6f1ce 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1074,28 +1074,11 @@ function GatedAppContent() { } function AppContent() { - const bootData = window.__TSS_BOOT_DATA__ || {} const [route, setRoute] = useState(() => parseRoute()) - const [leaderboard, setLeaderboard] = useState(() => ( - bootData.leaderboard - ? { status: 'ready', data: bootData.leaderboard, error: null } - : { status: 'idle', data: null, error: null } - )) - const [playerLeaderboard, setPlayerLeaderboard] = useState(() => ( - bootData.playerLeaderboard - ? { status: 'ready', data: bootData.playerLeaderboard, error: null } - : { status: 'idle', data: null, error: null } - )) - const [homeTeams, setHomeTeams] = useState(() => ( - bootData.homeTeams - ? { status: 'ready', data: bootData.homeTeams, error: null } - : { status: 'idle', data: null, error: null } - )) - const [live, setLive] = useState(() => ( - bootData.live - ? { status: 'ready', data: bootData.live, error: null, updatedAt: Date.now() } - : { status: 'idle', data: null, error: null, updatedAt: 0 } - )) + const [leaderboard, setLeaderboard] = useState({ status: 'idle', data: null, error: null }) + const [playerLeaderboard, setPlayerLeaderboard] = useState({ status: 'idle', data: null, error: null }) + const [homeTeams, setHomeTeams] = useState({ status: 'idle', data: null, error: null }) + const [live, setLive] = useState({ status: 'idle', data: null, error: null, updatedAt: 0 }) const [uptime, setUptime] = useState({ status: 'idle', checks: [], history: [], updatedAt: null }) const [viewers, setViewers] = useState({ status: 'idle', data: null, error: null, updatedAt: null }) const [analyticsPreferences, setAnalyticsPreferences] = useState(() => storedAnalyticsPreferences()) diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index f4199eb..5671800 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -2,53 +2,4 @@ import { createRoot } from 'react-dom/client' import './styles.css' import App from './App.jsx' -const root = document.getElementById('root') - -async function fetchBootJson(path) { - const response = await fetch(path, { headers: { Accept: 'application/json' } }) - if (!response.ok) throw new Error(`Boot request failed with ${response.status}`) - return response.json() -} - -async function preloadBootData() { - if (!root?.dataset.tssFallback) return - - const pathname = window.location.pathname - const timeout = new Promise((_, reject) => { - window.setTimeout(() => reject(new Error('Boot preload timed out')), 1500) - }) - - const load = async () => { - if (pathname === '/') { - const [homeTeams, live] = await Promise.all([ - fetchBootJson('/data/home-teams.json'), - fetchBootJson('/data/recent-games.json'), - ]) - return { homeTeams, live } - } - - if (pathname === '/teams') { - return { leaderboard: await fetchBootJson('/data/leaderboard-teams.json') } - } - - if (pathname === '/players') { - return { playerLeaderboard: await fetchBootJson('/data/leaderboard-players.json') } - } - - if (pathname === '/battle-logs' || pathname === '/live') { - const [live, leaderboard] = await Promise.all([ - fetchBootJson('/data/recent-games.json'), - fetchBootJson('/data/leaderboard-teams.json'), - ]) - return { live, leaderboard } - } - - return null - } - - window.__TSS_BOOT_DATA__ = await Promise.race([load(), timeout]).catch(() => null) -} - -preloadBootData().finally(() => { - createRoot(root).render() -}) +createRoot(document.getElementById('root')).render() diff --git a/server.cjs b/server.cjs index 133f308..abee758 100644 --- a/server.cjs +++ b/server.cjs @@ -212,6 +212,7 @@ let uptimeDb = null let analyticsDb = null let latestUptimeSnapshot = null let publicDataPrewarmTimer = null +let publicDataStartupStatus = { ready: false, checked_at: null, results: [] } function sendJson(res, status, body, headers = {}) { send(res, status, JSON.stringify(body), { ...jsonHeaders, ...headers }) @@ -464,13 +465,14 @@ function refreshPublicData(filePath, target) { function publicDataPrewarmTargets() { return [ - '/api/tss/leaderboard/teams?limit=100', - '/api/tss/leaderboard/players?limit=100', - '/api/tss/leaderboard/teams?limit=4', - '/api/tss/games/recent?limit=50', - ].map((requestPath) => { + { name: 'team leaderboard', path: '/api/tss/leaderboard/teams?limit=100' }, + { name: 'player leaderboard', path: '/api/tss/leaderboard/players?limit=100' }, + { name: 'home teams', path: '/api/tss/leaderboard/teams?limit=4' }, + { name: 'recent games', path: '/api/tss/games/recent?limit=50' }, + ].map(({ name, path: requestPath }) => { const requestUrl = new URL(requestPath, 'http://localhost') return { + name, requestUrl, target: new URL(requestPath, API_UPSTREAM), filePath: publicDataCachePathForUrl(requestUrl), @@ -478,14 +480,60 @@ function publicDataPrewarmTargets() { }).filter((entry) => entry.filePath) } -function prewarmPublicDataCache() { +async function prewarmPublicDataCache({ force = false, log = false } = {}) { const jobs = [] - for (const { filePath, target } of publicDataPrewarmTargets()) { + for (const { name, filePath, target } of publicDataPrewarmTargets()) { const current = cachedPublicData(filePath) - if (current?.fresh) continue - jobs.push(fillPublicData(filePath, target, PUBLIC_DATA_COLD_TIMEOUT_MS)) + if (current?.fresh && !force) { + jobs.push(Promise.resolve({ name, filePath, ok: true, cached: true, bytes: fs.statSync(filePath).size })) + continue + } + + const startedAt = Date.now() + jobs.push( + fillPublicData(filePath, target, PUBLIC_DATA_COLD_TIMEOUT_MS) + .then((ok) => ({ + name, + filePath, + ok, + cached: false, + ms: Date.now() - startedAt, + bytes: fs.existsSync(filePath) ? fs.statSync(filePath).size : 0, + })) + .catch((error) => ({ + name, + filePath, + ok: false, + cached: false, + ms: Date.now() - startedAt, + error: error.message, + bytes: fs.existsSync(filePath) ? fs.statSync(filePath).size : 0, + })), + ) } - return Promise.allSettled(jobs) + const results = await Promise.all(jobs) + publicDataStartupStatus = { + ready: results.every((result) => result.ok || result.bytes > 0), + checked_at: new Date().toISOString(), + results: results.map((result) => ({ + name: result.name, + ok: Boolean(result.ok || result.bytes > 0), + cached: Boolean(result.cached), + bytes: result.bytes || 0, + ms: result.ms || 0, + error: result.error || '', + })), + } + + if (log) { + for (const result of publicDataStartupStatus.results) { + const state = result.ok ? (result.cached ? 'cached' : 'warmed') : 'failed' + const detail = result.error ? ` (${result.error})` : '' + console.log(`public data ${state}: ${result.name} ${result.bytes} bytes in ${result.ms}ms${detail}`) + } + } + + return publicDataStartupStatus } function startPublicDataPrewarmer() { @@ -2281,171 +2329,6 @@ function routeStructuredData(origin, seo, canonicalUrl) { ]) } -function readPublicDataSnapshot(relativePath) { - try { - const filePath = path.resolve(PUBLIC_DATA_CACHE_DIR, relativePath) - const relativeToCache = path.relative(PUBLIC_DATA_CACHE_DIR, filePath) - if (relativeToCache.startsWith('..') || path.isAbsolute(relativeToCache)) return null - const stat = fs.statSync(filePath) - if (!stat.isFile() || Date.now() - stat.mtimeMs > PUBLIC_DATA_CACHE_STALE_MS) return null - return JSON.parse(fs.readFileSync(filePath, 'utf8')) - } catch { - return null - } -} - -function fallbackNumber(value) { - return new Intl.NumberFormat('en-GB').format(Number(value || 0)) -} - -function fallbackDate(timestamp) { - if (!timestamp) return 'Unknown time' - return new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(Number(timestamp) * 1000)) -} - -function fallbackShell(title, meta, body) { - return ` -
-
-
-

${escapeHtml(title)}

-

${escapeHtml(meta)}

-
- ${body} -
-
- ` -} - -function playersFallbackHtml() { - const players = readPublicDataSnapshot('leaderboard-players.json')?.players || [] - if (!players.length) return '' - - const rows = players.slice(0, 100).map((player, index) => ` - -

#${index + 1}

-
-

${escapeHtml(player.nick || player.uid)}

-

${escapeHtml(player.uid)} · last seen ${escapeHtml(fallbackDate(player.last_seen))}

-
-

${fallbackNumber(player.score)}

-

${fallbackNumber(player.total_battles)}

-

${fallbackNumber(player.total_kills)}

-

${fallbackNumber(player.assists)}

-

${Number(player.win_rate || 0).toFixed(1)}%

-

${Number(player.kdr || 0).toFixed(2)}

-

${fallbackNumber(player.teams_seen)}

-
- `).join('') - - return fallbackShell( - 'Player Leaderboard', - `${players.length} players returned`, - `
-
-
-
-

Rank

Player

Score

Battles

Kills

Assists

WR

KDR

Teams

-
- ${rows} -
-
-
`, - ) -} - -function teamsFallbackHtml() { - const teams = readPublicDataSnapshot('leaderboard-teams.json')?.teams || [] - if (!teams.length) return '' - - const rows = teams.slice(0, 100).map((team, index) => { - const name = team.name || '' - return ` - - #${index + 1} - ${escapeHtml(name)} - ${fallbackNumber(team.player_count)} players - ${fallbackNumber(team.total_battles)} battles - ${Number(team.win_rate || 0).toFixed(1)}% WR - ${fallbackNumber(team.points?.total_points || team.total_kills)} - - ` - }).join('') - - return fallbackShell( - 'Team Leaderboard', - `${teams.length} teams returned`, - `
${rows}
`, - ) -} - -function battleLogsFallbackHtml() { - const matches = readPublicDataSnapshot('recent-games.json')?.matches || [] - if (!matches.length) return '' - - const rows = matches.slice(0, 50).map((match) => { - const winner = match.winning_team || match.team_name || '' - const loser = match.losing_team || '' - return ` - -
-

${escapeHtml(match.map_name || 'Unknown map')}

-

${escapeHtml(fallbackDate(match.timestamp))} · ${escapeHtml(match.session_id)}

-
-
- ${escapeHtml(winner)} - vs - ${escapeHtml(loser)} -
-

${fallbackNumber(match.player_count)}v${fallbackNumber(match.player_count)}

-
- ` - }).join('') - - return fallbackShell( - 'Battle Logs', - `${matches.length} battles returned`, - `
${rows}
`, - ) -} - -function homeFallbackHtml() { - const teams = readPublicDataSnapshot('home-teams.json')?.teams || [] - const matches = readPublicDataSnapshot('recent-games.json')?.matches || [] - - return ` -
-
-
-

BorisBot got nothin on THIS

-

Toothless' TSS Bot

-

Powered by Spectra. TSS analytics.

- -
-
-

Cached now

-
-

${fallbackNumber(teams.length)} featured teams · ${fallbackNumber(matches.length)} recent games

- ${teams.slice(0, 4).map((team) => `${escapeHtml(team.name || '')}`).join('')} -
-
-
-
- ` -} - -function routeFallbackHtml(pathname) { - if (pathname === '/players') return playersFallbackHtml() - if (pathname === '/teams') return teamsFallbackHtml() - if (pathname === '/battle-logs' || pathname === '/live') return battleLogsFallbackHtml() - if (pathname === '/') return homeFallbackHtml() - return '' -} - function htmlWithSeo(req, data) { const origin = pagePublicOrigin(req) let pathname = '/' @@ -2458,14 +2341,7 @@ function htmlWithSeo(req, data) { const seo = routeSeo(pathname) const canonicalUrl = `${origin}${seo.path}` - const fallback = routeFallbackHtml(pathname) - - const rootHtml = fallback - ? `
${fallback}
` - : '
' - return data.toString('utf8') - .replace('
', rootHtml) .replace(/\s+integrity=(["'])sha(?:256|384|512)-[^"']+\1/g, '') .replaceAll('__PUBLIC_ORIGIN__', origin) .replaceAll('__SEO_TITLE__', escapeHtml(seo.title)) @@ -2854,7 +2730,7 @@ const server = http.createServer((req, res) => { } if (req.url === '/health') { - sendJson(res, 200, { ok: true }) + sendJson(res, 200, { ok: true, public_data: publicDataStartupStatus }) return } @@ -2869,6 +2745,21 @@ const server = http.createServer((req, res) => { return } + if (req.method === 'POST' && req.url === '/api/cache/prewarm') { + if (!isSameOriginRequest(req)) { + sendJson(res, 403, { error: 'Cache prewarm is restricted to this site' }) + return + } + if (isRateLimited(req)) { + sendJson(res, 429, { error: 'Too many requests' }, { 'retry-after': String(Math.ceil(API_RATE_LIMIT_WINDOW_MS / 1000)) }) + return + } + prewarmPublicDataCache({ force: true, log: true }) + .then((status) => sendJson(res, status.ready ? 200 : 207, status)) + .catch((error) => sendJson(res, 500, { error: 'Cache prewarm failed', detail: error.message })) + return + } + if (req.method === 'GET' && req.url === '/api/viewers') { if (isRateLimited(req)) { sendJson(res, 429, { error: 'Too many requests' }, { 'retry-after': String(Math.ceil(API_RATE_LIMIT_WINDOW_MS / 1000)) }) @@ -3030,7 +2921,7 @@ server.listen(PORT, '0.0.0.0', async () => { if (RUN_BACKGROUND_JOBS) { startUptimeSampler() fs.mkdirSync(PUBLIC_DATA_CACHE_DIR, { recursive: true }) - await prewarmPublicDataCache() + await prewarmPublicDataCache({ log: true }) startPublicDataPrewarmer() } process.send?.('ready')