import { useEffect, useId, useState, type FormEvent } from "react"; import "./admin-auth.css"; interface ErrorEnvelopeBody { error?: { details?: { field_errors?: Array<{ message_key?: string }> }; }; } function adminErrorMessage(body: ErrorEnvelopeBody) { const key = body.error?.details?.field_errors?.[0]?.message_key; if (key === "auth.challenge.invalid") return "验证码不正确,请检查后重试。"; if (key === "auth.challenge.expired") return "验证码已过期,请重新获取。"; if (key === "auth.challenge.resend_too_soon") return "请等待倒计时结束后重新获取验证码。"; if (key === "auth.account.suspended" || key === "admin.auth.not_allowed") return "无法使用管理员入口,请联系部署维护人员。"; return "管理员登录暂时无法完成,请稍后重试。"; } export function AdminAuthPage() { const emailId = useId(); const codeId = useId(); const [email, setEmail] = useState(""); const [code, setCode] = useState(""); const [registrationId, setRegistrationId] = useState(); const [countdown, setCountdown] = useState(0); const [sending, setSending] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(); const emailValid = /^[^@\s]+@[^@\s]+$/.test(email); useEffect(() => { if (countdown <= 0) return; const timer = window.setInterval(() => setCountdown((value) => Math.max(0, value - 1)), 1_000); return () => window.clearInterval(timer); }, [countdown]); async function sendCode() { if (!emailValid || sending || countdown > 0) return; setSending(true); setError(undefined); try { const response = await fetch("/api/v1/admin-auth/login/send", { body: JSON.stringify({ email }), credentials: "same-origin", headers: { "Content-Type": "application/json" }, method: "POST", }); const body = await response.json() as ErrorEnvelopeBody & { registration_id?: string }; if (!response.ok || !body.registration_id) { setError(adminErrorMessage(body)); return; } setRegistrationId(body.registration_id); setCountdown(60); window.requestAnimationFrame(() => document.getElementById(codeId)?.focus()); } catch { setError("管理员登录暂时无法完成,请稍后重试。"); } finally { setSending(false); } } async function completeLogin(event: FormEvent) { event.preventDefault(); if (!registrationId || !/^[0-9]{6}$/.test(code) || submitting) return; setSubmitting(true); setError(undefined); try { const response = await fetch("/api/v1/admin-auth/login/complete", { body: JSON.stringify({ registration_id: registrationId, verification_code: code }), credentials: "same-origin", headers: { "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID().replaceAll("-", "") + crypto.randomUUID().replaceAll("-", ""), }, method: "POST", }); const body = await response.json() as ErrorEnvelopeBody; if (!response.ok) { setError(adminErrorMessage(body)); return; } window.location.assign("/admin"); } catch { setError("管理员登录暂时无法完成,请稍后重试。"); } finally { setSubmitting(false); } } return (
); }