moon cricket

This commit is contained in:
Heidi
2026-06-28 22:50:47 +01:00
parent 0c764a773d
commit 88d6bbe68b
+31 -8
View File
@@ -66,6 +66,7 @@ const siteGateEnabled = siteGateSetting == null
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 turnstileRequiredEvent = 'tssbot:turnstile-required'
const BLOCKED_PLAYER_UIDS = new Set(['165569402', '86157459', '33536334', '41808996', '3651161']) const BLOCKED_PLAYER_UIDS = new Set(['165569402', '86157459', '33536334', '41808996', '3651161'])
const BLOCKED_TEAM_NAMES = new Set(['TPC']) const BLOCKED_TEAM_NAMES = new Set(['TPC'])
@@ -135,6 +136,15 @@ function dataSource(apiPath, staticPath = null) {
return { apiPath, staticPath } return { apiPath, staticPath }
} }
function turnstileSessionRequiredMessage(error) {
return error === 'Turnstile session required' || error === 'Site session required'
}
function promptTurnstileSession(detail) {
if (typeof window === 'undefined' || !turnstileSiteKey) return
window.dispatchEvent(new CustomEvent(turnstileRequiredEvent, { detail }))
}
const publicDataSources = { const publicDataSources = {
teams: dataSource(apiEndpoints.teams, staticDataPath('leaderboard-teams.json')), teams: dataSource(apiEndpoints.teams, staticDataPath('leaderboard-teams.json')),
players: dataSource(apiEndpoints.players, staticDataPath('leaderboard-players.json')), players: dataSource(apiEndpoints.players, staticDataPath('leaderboard-players.json')),
@@ -161,6 +171,10 @@ async function fetchJson(path, signal) {
const error = new Error(body?.error || `Request failed with ${response.status}`) const error = new Error(body?.error || `Request failed with ${response.status}`)
error.status = response.status error.status = response.status
error.path = path error.path = path
error.detail = body?.detail
if (response.status === 403 && turnstileSessionRequiredMessage(body?.error)) {
promptTurnstileSession({ path, error: body?.error, detail: body?.detail })
}
throw error throw error
} }
@@ -1307,10 +1321,10 @@ function SiteGate({ onVerified }) {
> >
<div className="flex w-full max-w-sm flex-col items-center gap-4 rounded-md border border-border bg-fury-white p-6 text-center text-text shadow-[0_24px_70px_rgba(0,0,0,0.24)]"> <div className="flex w-full max-w-sm flex-col items-center gap-4 rounded-md border border-border bg-fury-white p-6 text-center text-text shadow-[0_24px_70px_rgba(0,0,0,0.24)]">
<h1 id="site-gate-title" className="text-lg font-semibold"> <h1 id="site-gate-title" className="text-lg font-semibold">
Verifying you are human Start a protected session
</h1> </h1>
<p className="text-sm text-text-soft"> <p className="text-sm text-text-soft">
This quick check protects the site from automated abuse. It usually clears itself. Complete this Turnstile check to continue using protected site data.
</p> </p>
<TurnstileWidget <TurnstileWidget
siteKey={turnstileSiteKey} siteKey={turnstileSiteKey}
@@ -1330,15 +1344,15 @@ function SiteGate({ onVerified }) {
} }
function App() { function App() {
if (!siteGateEnabled) return <AppContent /> if (!turnstileSiteKey) return <AppContent />
return <GatedAppContent /> return <GatedAppContent enforceInitialGate={siteGateEnabled} />
} }
function GatedAppContent() { function GatedAppContent({ enforceInitialGate }) {
const embeddedGateState = document const embeddedGateState = document
.querySelector('meta[name="tss-turnstile-session"]') .querySelector('meta[name="tss-turnstile-session"]')
?.getAttribute('content') ?.getAttribute('content')
const initialGateState = turnstileSiteKey const initialGateState = enforceInitialGate
? ['verified', 'required'].includes(embeddedGateState) ? ['verified', 'required'].includes(embeddedGateState)
? embeddedGateState ? embeddedGateState
: 'checking' : 'checking'
@@ -1346,7 +1360,7 @@ function GatedAppContent() {
const [gateState, setGateState] = useState(initialGateState) const [gateState, setGateState] = useState(initialGateState)
useEffect(() => { useEffect(() => {
if (!turnstileSiteKey || gateState !== 'checking') return undefined if (!enforceInitialGate || gateState !== 'checking') return undefined
let cancelled = false let cancelled = false
fetch('/api/turnstile/session', { headers: { Accept: 'application/json' } }) fetch('/api/turnstile/session', { headers: { Accept: 'application/json' } })
.then((response) => (response.ok ? response.json() : { verified: false })) .then((response) => (response.ok ? response.json() : { verified: false }))
@@ -1360,7 +1374,16 @@ function GatedAppContent() {
return () => { return () => {
cancelled = true cancelled = true
} }
}, [gateState]) }, [enforceInitialGate, gateState])
useEffect(() => {
function handleTurnstileRequired() {
setGateState('required')
}
window.addEventListener(turnstileRequiredEvent, handleTurnstileRequired)
return () => window.removeEventListener(turnstileRequiredEvent, handleTurnstileRequired)
}, [])
if (gateState === 'checking') { if (gateState === 'checking') {
return <div className="fixed inset-0 bg-bg" aria-hidden="true" /> return <div className="fixed inset-0 bg-bg" aria-hidden="true" />