'use client'; import { ReactNode, useCallback, useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; type Status = 'unknown' | 'authed' | 'denied'; /** * Client-side admin gate. * * 1. If `x-admin-key` is stored in sessionStorage → validate via * /api/admin/verify. * 2. Otherwise show a key entry form (basic-auth fallback for users who * prefer the `ADMIN_USER:ADMIN_KEY` header). * * The key is never written to localStorage and is cleared on a hard tab * close. For hardening, front the admin area with real HTTP Basic auth * at the reverse-proxy layer too. */ export default function AdminGate({ children }: { children: ReactNode }) { const [status, setStatus] = useState('unknown'); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const router = useRouter(); const [keyInput, setKeyInput] = useState(''); const validate = useCallback( async (key: string) => { setBusy(true); setError(null); try { const res = await fetch('/api/admin/verify', { headers: { 'x-admin-key': key }, }); if (res.ok) { sessionStorage.setItem('mb_admin_key', key); setStatus('authed'); } else { setStatus('denied'); setError('Invalid admin key.'); } } catch { setError('Network error'); } finally { setBusy(false); } }, [], ); useEffect(() => { const stored = sessionStorage.getItem('mb_admin_key'); if (stored) validate(stored); else setStatus('denied'); }, [validate]); async function submit(e: React.FormEvent) { e.preventDefault(); if (keyInput.trim()) await validate(keyInput.trim()); } function logout() { sessionStorage.removeItem('mb_admin_key'); setStatus('denied'); setKeyInput(''); router.refresh(); } if (status === 'authed') { return (

Admin · Settings

{children}
); } return (

Admin

Enter the admin key from ADMIN_API_KEY (or your{' '} USER:ADMIN_API_KEY basic-auth string) to continue.

setKeyInput(e.target.value)} placeholder="Admin key" autoFocus className="w-full rounded-lg border border-ink-300 px-3 py-2.5 text-sm focus:border-maple-600 focus:outline-none" /> {error &&

{error}

}

Tip: keep the key in a password manager; it is only held in your browser session while you have this tab open.

); }