'use client'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react'; import { Logo } from '@/components/chrome/Logo'; import { TimeToggle } from '@/components/chrome/TimeToggle'; import { AdminError, adminFetch, getAdminToken, setAdminToken } from '@/lib/admin-fetch'; const NAV: [string, string][] = [ ['/admin', 'Overview'], ['/admin/targets', 'Targets'], ['/admin/probes', 'Probes'], ['/admin/config', 'Scoring config'], ['/admin/baselines', 'Baselines'], ['/admin/raw', 'Raw explorer'], ['/admin/incidents', 'Incidents review'], ['/admin/annotations', 'Annotations'], ['/admin/replay', 'Replay'], ['/admin/boost', 'Boost'], ]; const AuthCtx = createContext<{ authed: boolean; logout: () => void }>({ authed: false, logout: () => {} }); export const useAdminAuth = () => useContext(AuthCtx); /** Token gate: the token lives in sessionStorage only and is validated against /api/admin/overview. */ export function AdminShell({ children }: { children: ReactNode }) { const [state, setState] = useState<'checking' | 'locked' | 'ok'>('checking'); const [input, setInput] = useState(''); const [err, setErr] = useState(null); const path = usePathname(); const verify = useCallback(async (token: string) => { setAdminToken(token); try { await adminFetch('/overview'); setState('ok'); setErr(null); } catch (e) { setAdminToken(''); setState('locked'); setErr(e instanceof AdminError && e.status === 401 ? 'Invalid token.' : 'Admin API unreachable.'); } }, []); useEffect(() => { const t = getAdminToken(); // Defer to a microtask so no state is set synchronously inside the effect body. void Promise.resolve().then(() => (t ? verify(t) : setState('locked'))); }, [verify]); const logout = useCallback(() => { setAdminToken(''); setState('locked'); }, []); return (
InternetPressure admin
public site → {state === 'ok' && ( )}
{state === 'ok' && ( )}
{state === 'checking' &&

Checking token…

} {state === 'locked' && (
{ e.preventDefault(); if (input.trim()) void verify(input.trim()); }} >

Restricted

Admin console

Enter the admin token. It is kept in this tab's session storage only and sent as X-IP-Admin-Token.

setInput(e.target.value)} autoFocus autoComplete="off" placeholder="token" className="num mt-4 h-10 w-full rounded-[4px] border border-line bg-panel px-3 text-[14px] text-ink placeholder:text-ink-3" aria-label="Admin token" /> {err &&

{err}

}
)} {state === 'ok' && children}
); }