"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 (
);
}
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 (
);
}