"use client"; import * as React from "react"; import { KeyRound, Lock, ShieldAlert, ShieldCheck } from "lucide-react"; import { Button, Input, Spinner } from "@/components/ui"; import { SpinzaMark } from "@/components/brand/logo"; import { api, ApiClientError } from "@/lib/api"; import { useAdmin } from "./store"; import type { AdminIdentity } from "./types"; import { ErrorState } from "./primitives"; /** Gate: GET /api/admin/me → login form (401), restricted notice (403), or the console. */ export function AdminGate({ children }: { children: React.ReactNode }) { const status = useAdmin((s) => s.status); const error = useAdmin((s) => s.error); const check = useAdmin((s) => s.check); React.useEffect(() => { void check(); }, [check]); if (status === "loading") { return (
Checking admin session…
); } if (status === "forbidden") return ; if (status === "error") { return ( void check()} /> ); } if (status === "unauthed") return ; return <>{children}; } function Frame({ children }: { children: React.ReactNode }) { return (
SPINZA Admin
{children}
); } function Restricted({ message }: { message: string | null }) { return (

Admin access is restricted

{message ?? "This console is only reachable from allow-listed addresses."}

); } function LoginScreen() { const signedIn = useAdmin((s) => s.signedIn); const [username, setUsername] = React.useState(""); const [password, setPassword] = React.useState(""); const [totp, setTotp] = React.useState(""); const [busy, setBusy] = React.useState(false); const [error, setError] = React.useState(null); const [forbidden, setForbidden] = React.useState(null); const totpOk = /^\d{6}$/.test(totp); const canSubmit = username.trim().length > 0 && password.length > 0 && totpOk && !busy; async function submit(e: React.FormEvent) { e.preventDefault(); if (!canSubmit) return; setBusy(true); setError(null); try { const r = await api<{ admin: AdminIdentity }>("/api/admin/auth/login", { json: { username: username.trim(), password, totp } }); signedIn(r.admin); } catch (err) { if (err instanceof ApiClientError) { if (err.status === 403) setForbidden(err.message); else if (err.status === 429) setError("Too many attempts. Wait a few minutes and try again."); else if (err.status === 401) setError("Invalid credentials."); else setError(err.message); } else setError("Unexpected error."); setPassword(""); setTotp(""); } finally { setBusy(false); } } if (forbidden) return ; return (

Admin sign-in

Username, password and a 6-digit authenticator code.

setUsername(e.target.value)} autoFocus /> setPassword(e.target.value)} /> setTotp(e.target.value.replace(/\D/g, "").slice(0, 6))} error={totp.length > 0 && !totpOk ? "Enter the 6 digits from your authenticator app." : null} />
{error ? (
{error}
) : null}

Sessions expire after 12 hours. All actions are logged.

); }