fixes; locked down api (allegedly, idk, i let claude vibe it) and vandilised pedophiles profiles

This commit is contained in:
Clippii
2026-06-28 11:44:52 +01:00
parent 931dd0141c
commit e9567e367b
2 changed files with 68 additions and 7 deletions
+47 -4
View File
@@ -63,6 +63,9 @@ const siteGateEnabled = String(import.meta.env.VITE_SITE_GATE || 'false').toLowe
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 || 'false').toLowerCase() === 'true' const staticDataEnabled = String(import.meta.env.VITE_STATIC_DATA || 'false').toLowerCase() === 'true'
const missingStaticDataPaths = new Set() const missingStaticDataPaths = new Set()
const BLOCKED_PLAYER_UIDS = new Set(['165569402', '86157459', '33536334', '41808996', '3651161'])
const BLOCKED_TEAM_NAMES = new Set(['TPC'])
const blogPostFiles = import.meta.glob('./blog/posts/*.md', { const blogPostFiles = import.meta.glob('./blog/posts/*.md', {
eager: true, eager: true,
import: 'default', import: 'default',
@@ -1393,11 +1396,11 @@ function AppContent() {
games: { status: 'idle', data: null, error: null }, games: { status: 'idle', data: null, error: null },
}) })
const teams = useMemo( const teams = useMemo(
() => leaderboard.data?.teams || leaderboard.data?.squadrons || [], () => (leaderboard.data?.teams || leaderboard.data?.squadrons || []).filter((t) => !BLOCKED_TEAM_NAMES.has(bestTeamName(t))),
[leaderboard.data], [leaderboard.data],
) )
const players = useMemo( const players = useMemo(
() => playerLeaderboard.data?.players || [], () => (playerLeaderboard.data?.players || []).filter((p) => !BLOCKED_PLAYER_UIDS.has(String(p?.uid || ''))),
[playerLeaderboard.data], [playerLeaderboard.data],
) )
const teamsToWatch = useMemo( const teamsToWatch = useMemo(
@@ -1645,10 +1648,14 @@ function AppContent() {
if (controller.signal.aborted) return if (controller.signal.aborted) return
const teamResults = teamResult.status === 'fulfilled' const teamResults = teamResult.status === 'fulfilled'
? (teamResult.value.teams || teamResult.value.results || []).map(teamSearchResult) ? (teamResult.value.teams || teamResult.value.results || [])
.filter((team) => !BLOCKED_TEAM_NAMES.has(bestTeamName(team)))
.map(teamSearchResult)
: [] : []
const playerResults = playerResult.status === 'fulfilled' const playerResults = playerResult.status === 'fulfilled'
? (playerResult.value.players || []).map(playerSearchResult) ? (playerResult.value.players || [])
.filter((player) => !BLOCKED_PLAYER_UIDS.has(String(player?.uid || '')))
.map(playerSearchResult)
: [] : []
const results = dedupeSearchResults([...teamResults, ...playerResults]).slice(0, 10) const results = dedupeSearchResults([...teamResults, ...playerResults]).slice(0, 10)
@@ -2663,6 +2670,26 @@ function PlayerStatCard({ label, value }) {
) )
} }
function BlockedPage({ onBack }) {
return (
<section className="mx-auto max-w-4xl pb-12 pt-24 sm:pt-28">
<div className="mt-8 rounded-xl border border-border bg-fury-white p-10 text-center">
<p className="text-7xl">🤡🤡🤡🤡</p>
<p className="mt-4 text-text-soft">user affiliated with pedophiles, blocked~</p>
{onBack ? (
<button
className="mt-6 text-sm font-semibold text-fury-cyan transition hover:text-text"
onClick={onBack}
type="button"
>
Go back
</button>
) : null}
</div>
</section>
)
}
function PlayerPage({ uid, navigate }) { function PlayerPage({ uid, navigate }) {
const [state, setState] = useState({ status: 'loading', data: null, error: '' }) const [state, setState] = useState({ status: 'loading', data: null, error: '' })
@@ -2671,6 +2698,10 @@ function PlayerPage({ uid, navigate }) {
setState({ status: 'error', data: null, error: 'No player specified.' }) setState({ status: 'error', data: null, error: 'No player specified.' })
return return
} }
if (BLOCKED_PLAYER_UIDS.has(String(uid))) {
setState({ status: 'blocked', data: null, error: '' })
return
}
let cancelled = false let cancelled = false
setState({ status: 'loading', data: null, error: '' }) setState({ status: 'loading', data: null, error: '' })
fetchPublicJson(publicDataSources.player(uid)) fetchPublicJson(publicDataSources.player(uid))
@@ -2687,6 +2718,12 @@ function PlayerPage({ uid, navigate }) {
const { status, data, error } = state const { status, data, error } = state
if (status === 'blocked') {
return (
<BlockedPage onBack={() => navigate('/players')} />
)
}
return ( return (
<section className="mx-auto max-w-4xl pb-12 pt-24 sm:pt-28"> <section className="mx-auto max-w-4xl pb-12 pt-24 sm:pt-28">
<div className="border-b border-border pb-6"> <div className="border-b border-border pb-6">
@@ -4241,6 +4278,12 @@ function TeamProfilePage({ navigate, profile, requestedTeam, teams }) {
const leaderboardTeam = teams.find((team) => bestTeamName(team) === requestedTeam) const leaderboardTeam = teams.find((team) => bestTeamName(team) === requestedTeam)
const displayName = detail?.name || bestTeamName(leaderboardTeam) || requestedTeam const displayName = detail?.name || bestTeamName(leaderboardTeam) || requestedTeam
if (BLOCKED_TEAM_NAMES.has(requestedTeam)) {
return (
<BlockedPage onBack={() => navigate('/teams')} />
)
}
return ( return (
<section className="space-y-6 pt-24 sm:pt-28"> <section className="space-y-6 pt-24 sm:pt-28">
<button <button
+21 -3
View File
@@ -1944,6 +1944,8 @@ function isRateLimited(req) {
const current = rateLimits.get(ip) const current = rateLimits.get(ip)
if (!current || current.resetAt <= now) { if (!current || current.resetAt <= now) {
// When the map is at capacity, deny new IPs rather than clearing existing limits.
if (!current && rateLimits.size >= MAX_RATE_LIMIT_KEYS) return true
rateLimits.set(ip, { count: 1, resetAt: now + API_RATE_LIMIT_WINDOW_MS }) rateLimits.set(ip, { count: 1, resetAt: now + API_RATE_LIMIT_WINDOW_MS })
return false return false
} }
@@ -1956,11 +1958,17 @@ function pruneMaps() {
const now = Date.now() const now = Date.now()
for (const [key, value] of apiCache) { for (const [key, value] of apiCache) {
if (value.expiresAt <= now || apiCache.size > MAX_CACHE_ENTRIES) apiCache.delete(key) if (value.expiresAt <= now) apiCache.delete(key)
}
if (apiCache.size > MAX_CACHE_ENTRIES) {
const sorted = [...apiCache.entries()].sort((a, b) => a[1].expiresAt - b[1].expiresAt)
for (const [key] of sorted.slice(0, apiCache.size - MAX_CACHE_ENTRIES)) {
apiCache.delete(key)
}
} }
for (const [key, value] of rateLimits) { for (const [key, value] of rateLimits) {
if (value.resetAt <= now || rateLimits.size > MAX_RATE_LIMIT_KEYS) rateLimits.delete(key) if (value.resetAt <= now) rateLimits.delete(key)
} }
} }
@@ -2156,6 +2164,8 @@ function proxyRequest(req, res) {
delete headers['access-control-allow-methods'] delete headers['access-control-allow-methods']
delete headers['access-control-allow-headers'] delete headers['access-control-allow-headers']
delete headers['access-control-expose-headers'] delete headers['access-control-expose-headers']
delete headers['server']
delete headers['x-powered-by']
res.writeHead(statusCode, headers) res.writeHead(statusCode, headers)
@@ -2759,7 +2769,11 @@ const server = http.createServer((req, res) => {
} }
if (req.url === '/health') { if (req.url === '/health') {
sendJson(res, 200, { ok: true, public_data: publicDataStartupStatus }) if (isRateLimited(req)) {
sendJson(res, 429, { error: 'Too many requests' }, { 'retry-after': String(Math.ceil(API_RATE_LIMIT_WINDOW_MS / 1000)) })
return
}
sendJson(res, 200, { ok: true })
return return
} }
@@ -2790,6 +2804,10 @@ const server = http.createServer((req, res) => {
} }
if (req.method === 'GET' && req.url === '/api/viewers') { if (req.method === 'GET' && req.url === '/api/viewers') {
if (!isSameOriginRequest(req)) {
sendJson(res, 403, { error: 'Viewer analytics are restricted to this site' })
return
}
if (isRateLimited(req)) { if (isRateLimited(req)) {
sendJson(res, 429, { error: 'Too many requests' }, { 'retry-after': String(Math.ceil(API_RATE_LIMIT_WINDOW_MS / 1000)) }) sendJson(res, 429, { error: 'Too many requests' }, { 'retry-after': String(Math.ceil(API_RATE_LIMIT_WINDOW_MS / 1000)) })
return return