"use client"; import Link from "next/link"; import { FormEvent, useEffect, useState } from "react"; import { PreviewNotice } from "@/components/ui"; const apiBase = process.env.NEXT_PUBLIC_API_BASE_URL ?? "/api/v1"; export function AuthForm({ mode }: { mode: "login" | "register" }) { const isLogin = mode === "login"; const [csrf, setCsrf] = useState(""); const [email, setEmail] = useState(""); const [username, setUsername] = useState(""); const [phone, setPhone] = useState(""); const [channel, setChannel] = useState<"email" | "phone">("email"); const [password, setPassword] = useState(""); const [confirmation, setConfirmation] = useState(""); const [verificationId, setVerificationId] = useState(""); const [verificationCode, setVerificationCode] = useState(""); const [captchaId, setCaptchaId] = useState(""); const [captchaImage, setCaptchaImage] = useState(""); const [captchaCode, setCaptchaCode] = useState(""); const [message, setMessage] = useState(""); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); useEffect(() => { fetch(`${apiBase}/auth/csrf`, { credentials: "include" }) .then((response) => response.json()) .then((body) => setCsrf(body.csrf_token ?? "")) .catch(() => setError("暂时无法连接认证服务")); }, []); async function refreshCaptcha() { setCaptchaCode(""); const response = await fetch(`${apiBase}/auth/captcha`, { credentials: "include" }); const body = await response.json(); setCaptchaId(body.captcha_id ?? ""); setCaptchaImage(body.image ?? ""); } useEffect(() => { refreshCaptcha().catch(() => setError("暂时无法加载图形验证码")); }, []); async function submit(event: FormEvent) { event.preventDefault(); setError(""); setMessage(""); if (!isLogin && password !== confirmation) { setError("两次输入的密码不一致"); return; } setBusy(true); try { const response = await fetch(`${apiBase}/auth/${isLogin ? "login" : "register"}`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", ...(csrf ? { "X-CSRF-Token": csrf } : {}) }, body: JSON.stringify(isLogin ? { identifier: email, password, captcha_id: captchaId, captcha_code: captchaCode } : { username, email: channel === "email" ? email : email || null, phone: phone || null, verification_channel: channel, password, captcha_id: captchaId, captcha_code: captchaCode }), }); const body = await response.json().catch(() => ({})); if (!response.ok) throw new Error(body.error?.message ?? "请求失败,请稍后重试"); if (isLogin) { window.location.assign("/account"); } else { setVerificationId(body.challenge_id ?? ""); setMessage(`注册成功,验证码已发送到你的${channel === "email" ? "邮箱" : "手机"}`); } } catch (submitError) { setError(submitError instanceof Error ? submitError.message : "请求失败,请稍后重试"); refreshCaptcha().catch(() => undefined); } finally { setBusy(false); } } async function confirmVerification(event: FormEvent) { event.preventDefault(); setError(""); setBusy(true); try { const response = await fetch(`${apiBase}/auth/verification/confirm-registration`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", ...(csrf ? { "X-CSRF-Token": csrf } : {}) }, body: JSON.stringify({ challenge_id: verificationId, code: verificationCode }), }); const body = await response.json().catch(() => ({})); if (!response.ok) throw new Error(body.error?.message ?? "验证码错误,请重试"); setMessage("验证成功,请登录"); window.setTimeout(() => window.location.assign("/login"), 500); } catch (confirmError) { setError(confirmError instanceof Error ? confirmError.message : "验证失败,请重试"); } finally { setBusy(false); } } async function resendVerification() { setError(""); setBusy(true); try { const response = await fetch(`${apiBase}/auth/verification/resend`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ challenge_id: verificationId }), }); const body = await response.json().catch(() => ({})); if (!response.ok) throw new Error(body.error?.message ?? "验证码发送失败,请稍后重试"); setMessage("新的验证码已发送"); } catch (resendError) { setError(resendError instanceof Error ? resendError.message : "验证码发送失败,请稍后重试"); } finally { setBusy(false); } } return (
支持邮箱或手机二选一验证。验证码只由服务端发送和校验。 {verificationId ?
setVerificationCode(event.target.value)} placeholder="输入 6 位验证码" required value={verificationCode} />
{error ?

{error}

: null}
:
{isLogin ?
setEmail(event.target.value)} placeholder="name@example.com / 13800138000" required value={email} />
: <>
setUsername(event.target.value)} placeholder="3-32 位字母、数字、下划线或短横线" required value={username} />
setEmail(event.target.value)} placeholder="name@example.com" required={channel === "email"} type="email" value={email} />
setPhone(event.target.value)} placeholder="中国大陆手机号" required={channel === "phone"} type="tel" value={phone} />
}
图形验证码
setCaptchaCode(event.target.value)} placeholder="输入图形验证码" required value={captchaCode} />
setPassword(event.target.value)} placeholder="至少 8 位字符" required type="password" value={password} />
{!isLogin ?
setConfirmation(event.target.value)} placeholder="再次输入密码" required type="password" value={confirmation} />
: null} {error ?

{error}

: null} {message ?

{message}

: null}
}

{isLogin ? "还没有账户?" : "已有账户?"}{" "}{isLogin ? "去注册" : "去登录"}

); }