ai generated solutions to our ai generated problems

This commit is contained in:
2026-06-20 00:43:44 +01:00
parent fb773489f5
commit 22bff51147
7 changed files with 218 additions and 35 deletions
+7 -7
View File
@@ -86,12 +86,12 @@ Vehicle icon PNGs are served statically at `/vehicle-icons` from `VEHICLE_ICONS_
The proxy blocks cross-origin/API-navigation requests, strips CORS headers from The proxy blocks cross-origin/API-navigation requests, strips CORS headers from
the upstream response, rate limits callers, and caches successful GET responses. the upstream response, rate limits callers, and caches successful GET responses.
Public TSS reads are written to a bounded JSON snapshot cache and served at both Public TSS reads are written to a bounded JSON snapshot cache and served through
their `/api/tss/*` route and matching `/data/*` path. The frontend uses `/data/*` their normal `/api/tss/*` route. Fresh snapshots return without touching the
by default for public pages. Fresh snapshots return without touching the backend; backend; stale snapshots are served immediately while the server refreshes them
stale snapshots are served immediately while the server refreshes them in the in the background. Matching `/data/*` paths are also available for diagnostics or
background. Missing `/data/*` snapshots are filled from the matching upstream API static-first experiments, but the frontend uses `/api/tss/*` by default so the
with a short timeout, then written atomically for future requests. All responses site stays dynamic. All responses
ship `X-Content-Type-Options`, `X-Frame-Options: DENY`, `Referrer-Policy`, ship `X-Content-Type-Options`, `X-Frame-Options: DENY`, `Referrer-Policy`,
`Permissions-Policy`, `Cross-Origin-Opener-Policy`, `Cross-Origin-Resource-Policy`, `Permissions-Policy`, `Cross-Origin-Opener-Policy`, `Cross-Origin-Resource-Policy`,
HSTS (over HTTPS), and HTML responses include a Content Security Policy that HSTS (over HTTPS), and HTML responses include a Content Security Policy that
@@ -119,7 +119,7 @@ PUBLIC_DATA_CACHE_FRESH_MS=300000
PUBLIC_DATA_CACHE_STALE_MS=86400000 PUBLIC_DATA_CACHE_STALE_MS=86400000
PUBLIC_DATA_PREWARM_INTERVAL_MS=300000 PUBLIC_DATA_PREWARM_INTERVAL_MS=300000
PUBLIC_DATA_COLD_TIMEOUT_MS=8000 PUBLIC_DATA_COLD_TIMEOUT_MS=8000
VITE_STATIC_DATA=true VITE_STATIC_DATA=false
VITE_SITE_GATE=false VITE_SITE_GATE=false
API_RATE_LIMIT_WINDOW_MS=60000 API_RATE_LIMIT_WINDOW_MS=60000
API_RATE_LIMIT_MAX=120 API_RATE_LIMIT_MAX=120
+1 -1
View File
@@ -63,4 +63,4 @@ DISCORD_INCLUDE_PATCH=true
VITE_TURNSTILE_SITE_KEY= VITE_TURNSTILE_SITE_KEY=
TURNSTILE_SECRET_KEY= TURNSTILE_SECRET_KEY=
VITE_SITE_GATE=false VITE_SITE_GATE=false
VITE_STATIC_DATA=true VITE_STATIC_DATA=false
-1
View File
@@ -38,7 +38,6 @@
} catch {} } catch {}
window.__TSS_BOOT_PREFERENCES__ = { analyticsPreferences, theme } window.__TSS_BOOT_PREFERENCES__ = { analyticsPreferences, theme }
window.__TSS_BOOT_DATA__ = __TSS_BOOT_DATA__
document.documentElement.dataset.theme = theme document.documentElement.dataset.theme = theme
document.documentElement.style.colorScheme = theme document.documentElement.style.colorScheme = theme
document.querySelector('meta[name="theme-color"]')?.setAttribute( document.querySelector('meta[name="theme-color"]')?.setAttribute(
+4 -3
View File
@@ -1,8 +1,9 @@
# Static Public Data # Static Public Data
The frontend tries these JSON snapshots before falling back to the live API by The web server serves `/data/*` from the same public data cache used by
default. The web server serves `/data/*` from the public data cache and fills `/api/tss/*`, and fills missing snapshots from the matching API route with a
missing snapshots from the matching `/api/tss/*` route with a short timeout. short timeout. The frontend uses `/api/tss/*` by default; set
`VITE_STATIC_DATA=true` only for static-first experiments.
- `/data/leaderboard-teams.json` - `/data/leaderboard-teams.json`
- `/data/leaderboard-players.json` - `/data/leaderboard-players.json`
+1 -1
View File
@@ -52,7 +52,7 @@ const siteVersion = '1.0.1'
const turnstileSiteKey = import.meta.env.VITE_TURNSTILE_SITE_KEY || '' const turnstileSiteKey = import.meta.env.VITE_TURNSTILE_SITE_KEY || ''
const siteGateEnabled = String(import.meta.env.VITE_SITE_GATE || 'false').toLowerCase() === 'true' const siteGateEnabled = String(import.meta.env.VITE_SITE_GATE || 'false').toLowerCase() === 'true'
const staticDataBase = (import.meta.env.VITE_STATIC_DATA_BASE || '/data').replace(/\/+$/, '') const staticDataBase = (import.meta.env.VITE_STATIC_DATA_BASE || '/data').replace(/\/+$/, '')
const staticDataEnabled = String(import.meta.env.VITE_STATIC_DATA || 'true').toLowerCase() !== 'false' const staticDataEnabled = String(import.meta.env.VITE_STATIC_DATA || 'false').toLowerCase() === 'true'
const missingStaticDataPaths = new Set() const missingStaticDataPaths = new Set()
const defaultAnalyticsPreferences = { const defaultAnalyticsPreferences = {
+50 -1
View File
@@ -2,4 +2,53 @@ import { createRoot } from 'react-dom/client'
import './styles.css' import './styles.css'
import App from './App.jsx' import App from './App.jsx'
createRoot(document.getElementById('root')).render(<App />) 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(<App />)
})
+155 -21
View File
@@ -2294,28 +2294,156 @@ function readPublicDataSnapshot(relativePath) {
} }
} }
function routeBootData(pathname) { function fallbackNumber(value) {
const boot = {} return new Intl.NumberFormat('en-GB').format(Number(value || 0))
}
if (pathname === '/') { function fallbackDate(timestamp) {
const homeTeams = readPublicDataSnapshot('home-teams.json') if (!timestamp) return 'Unknown time'
const live = readPublicDataSnapshot('recent-games.json') return new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(Number(timestamp) * 1000))
if (homeTeams) boot.homeTeams = homeTeams }
if (live) boot.live = live
} else if (pathname === '/teams') {
const leaderboard = readPublicDataSnapshot('leaderboard-teams.json')
if (leaderboard) boot.leaderboard = leaderboard
} else if (pathname === '/players') {
const playerLeaderboard = readPublicDataSnapshot('leaderboard-players.json')
if (playerLeaderboard) boot.playerLeaderboard = playerLeaderboard
} else if (pathname === '/battle-logs' || pathname === '/live') {
const live = readPublicDataSnapshot('recent-games.json')
const leaderboard = readPublicDataSnapshot('leaderboard-teams.json')
if (live) boot.live = live
if (leaderboard) boot.leaderboard = leaderboard
}
return Object.keys(boot).length ? boot : null function fallbackShell(title, meta, body) {
return `
<main class="min-h-screen bg-bg px-5 py-24 text-text sm:px-8 sm:pt-28">
<section class="mx-auto w-full max-w-7xl space-y-6">
<div>
<h1 class="text-3xl font-bold">${escapeHtml(title)}</h1>
<p class="mt-2 text-sm text-text-soft">${escapeHtml(meta)}</p>
</div>
${body}
</section>
</main>
`
}
function playersFallbackHtml() {
const players = readPublicDataSnapshot('leaderboard-players.json')?.players || []
if (!players.length) return ''
const rows = players.slice(0, 100).map((player, index) => `
<a class="grid w-full grid-cols-[4rem_minmax(220px,1fr)_repeat(7,92px)] gap-3 border-b border-surface px-5 py-4 text-left text-sm transition hover:bg-surface" href="/players/${encodeURIComponent(player.uid)}">
<p class="font-semibold text-fury-cyan">#${index + 1}</p>
<div class="min-w-0">
<p class="truncate text-base font-semibold">${escapeHtml(player.nick || player.uid)}</p>
<p class="truncate text-xs text-text-soft">${escapeHtml(player.uid)} · last seen ${escapeHtml(fallbackDate(player.last_seen))}</p>
</div>
<p class="text-right font-semibold">${fallbackNumber(player.score)}</p>
<p class="text-right">${fallbackNumber(player.total_battles)}</p>
<p class="text-right">${fallbackNumber(player.total_kills)}</p>
<p class="text-right">${fallbackNumber(player.assists)}</p>
<p class="text-right">${Number(player.win_rate || 0).toFixed(1)}%</p>
<p class="text-right">${Number(player.kdr || 0).toFixed(2)}</p>
<p class="text-right">${fallbackNumber(player.teams_seen)}</p>
</a>
`).join('')
return fallbackShell(
'Player Leaderboard',
`${players.length} players returned`,
`<div class="overflow-hidden rounded-lg border border-border bg-fury-white shadow-sm">
<div class="overflow-x-auto">
<div class="min-w-[900px]">
<div class="grid grid-cols-[4rem_minmax(220px,1fr)_repeat(7,92px)] gap-3 border-b border-surface px-5 py-3 text-xs font-semibold uppercase tracking-wide text-text-soft">
<p>Rank</p><p>Player</p><p class="text-right">Score</p><p class="text-right">Battles</p><p class="text-right">Kills</p><p class="text-right">Assists</p><p class="text-right">WR</p><p class="text-right">KDR</p><p class="text-right">Teams</p>
</div>
${rows}
</div>
</div>
</div>`,
)
}
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 `
<a class="grid w-full gap-4 border-b border-surface px-5 py-4 text-left transition hover:bg-surface md:grid-cols-[4rem_1fr_repeat(4,auto)] md:items-center" href="/teams/${encodeURIComponent(name)}">
<span class="text-sm font-semibold text-fury-cyan">#${index + 1}</span>
<span class="min-w-0"><span class="block truncate text-lg font-semibold">${escapeHtml(name)}</span></span>
<span class="text-sm">${fallbackNumber(team.player_count)} players</span>
<span class="text-sm">${fallbackNumber(team.total_battles)} battles</span>
<span class="text-sm">${Number(team.win_rate || 0).toFixed(1)}% WR</span>
<span class="text-sm font-semibold">${fallbackNumber(team.points?.total_points || team.total_kills)}</span>
</a>
`
}).join('')
return fallbackShell(
'Team Leaderboard',
`${teams.length} teams returned`,
`<div class="overflow-hidden rounded-lg border border-border bg-fury-white shadow-sm">${rows}</div>`,
)
}
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 `
<a class="grid w-full gap-x-6 gap-y-2 border-b border-surface px-5 py-4 text-left transition hover:bg-surface md:grid-cols-[minmax(0,1fr)_minmax(18rem,0.9fr)_auto] md:items-center" href="/games/${encodeURIComponent(match.session_id)}">
<div class="min-w-0">
<p class="truncate font-semibold">${escapeHtml(match.map_name || 'Unknown map')}</p>
<p class="text-xs text-text-soft">${escapeHtml(fallbackDate(match.timestamp))} · ${escapeHtml(match.session_id)}</p>
</div>
<div class="mx-auto grid w-full max-w-xl min-w-0 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-x-4">
<span class="min-w-0 truncate text-right text-sm font-semibold text-win">${escapeHtml(winner)}</span>
<span class="shrink-0 text-xs font-semibold uppercase tracking-wide text-text-muted">vs</span>
<span class="min-w-0 truncate text-left text-sm font-semibold text-loss">${escapeHtml(loser)}</span>
</div>
<p class="text-sm">${fallbackNumber(match.player_count)}v${fallbackNumber(match.player_count)}</p>
</a>
`
}).join('')
return fallbackShell(
'Battle Logs',
`${matches.length} battles returned`,
`<div class="overflow-hidden rounded-lg border border-border bg-fury-white shadow-sm">${rows}</div>`,
)
}
function homeFallbackHtml() {
const teams = readPublicDataSnapshot('home-teams.json')?.teams || []
const matches = readPublicDataSnapshot('recent-games.json')?.matches || []
return `
<main class="min-h-screen bg-bg px-5 py-24 text-text sm:px-8 sm:pt-28">
<section class="mx-auto grid w-full max-w-7xl gap-8 lg:grid-cols-[1.05fr_0.95fr] lg:items-center">
<div class="max-w-3xl">
<p class="text-base font-semibold uppercase tracking-wide text-fury-cyan">BorisBot got nothin on THIS</p>
<h1 class="mt-3 text-6xl font-bold tracking-normal sm:text-7xl lg:text-8xl">Toothless&apos; TSS Bot</h1>
<p class="mt-6 max-w-2xl text-xl leading-9 text-text-soft">Powered by Spectra. TSS analytics.</p>
<div class="mt-8 grid gap-4 sm:grid-cols-3">
<a class="apricot-button-text min-h-15 rounded-lg bg-text px-5 py-4 text-center text-base font-semibold text-bg" href="/teams">Team Leaderboard</a>
<a class="min-h-15 rounded-lg border-2 border-ring px-5 py-4 text-center text-base font-semibold text-fury-cyan" href="/battle-logs">Battle Logs</a>
<a class="min-h-15 rounded-lg border-2 border-text bg-fury-white px-5 py-4 text-center text-base font-semibold text-text" href="/players">Player Leaderboard</a>
</div>
</div>
<div class="rounded-lg border border-border bg-fury-white p-5 shadow-sm">
<p class="text-sm font-semibold uppercase tracking-wide text-fury-cyan">Cached now</p>
<div class="mt-4 grid gap-3">
<p class="text-sm text-text-soft">${fallbackNumber(teams.length)} featured teams · ${fallbackNumber(matches.length)} recent games</p>
${teams.slice(0, 4).map((team) => `<a class="rounded-md border border-border bg-bg px-3 py-2 text-sm font-semibold" href="/teams/${encodeURIComponent(team.name || '')}">${escapeHtml(team.name || '')}</a>`).join('')}
</div>
</div>
</section>
</main>
`
}
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) { function htmlWithSeo(req, data) {
@@ -2330,7 +2458,14 @@ function htmlWithSeo(req, data) {
const seo = routeSeo(pathname) const seo = routeSeo(pathname)
const canonicalUrl = `${origin}${seo.path}` const canonicalUrl = `${origin}${seo.path}`
const fallback = routeFallbackHtml(pathname)
const rootHtml = fallback
? `<div id="root" data-tss-fallback="1">${fallback}</div>`
: '<div id="root"></div>'
return data.toString('utf8') return data.toString('utf8')
.replace('<div id="root"></div>', rootHtml)
.replace(/\s+integrity=(["'])sha(?:256|384|512)-[^"']+\1/g, '') .replace(/\s+integrity=(["'])sha(?:256|384|512)-[^"']+\1/g, '')
.replaceAll('__PUBLIC_ORIGIN__', origin) .replaceAll('__PUBLIC_ORIGIN__', origin)
.replaceAll('__SEO_TITLE__', escapeHtml(seo.title)) .replaceAll('__SEO_TITLE__', escapeHtml(seo.title))
@@ -2338,7 +2473,6 @@ function htmlWithSeo(req, data) {
.replaceAll('__SEO_ROBOTS__', escapeHtml(seo.robots)) .replaceAll('__SEO_ROBOTS__', escapeHtml(seo.robots))
.replaceAll('__SEO_CANONICAL__', escapeHtml(canonicalUrl)) .replaceAll('__SEO_CANONICAL__', escapeHtml(canonicalUrl))
.replaceAll('__SEO_JSON_LD__', routeStructuredData(origin, seo, canonicalUrl).replace(/</g, '\\u003c')) .replaceAll('__SEO_JSON_LD__', routeStructuredData(origin, seo, canonicalUrl).replace(/</g, '\\u003c'))
.replaceAll('__TSS_BOOT_DATA__', JSON.stringify(routeBootData(pathname)).replace(/</g, '\\u003c'))
.replaceAll('__TURNSTILE_SESSION__', isTurnstileSessionVerified(req) ? 'verified' : 'required') .replaceAll('__TURNSTILE_SESSION__', isTurnstileSessionVerified(req) ? 'verified' : 'required')
} }