refactor: serve static frontend with Go APIs

This commit is contained in:
brother7 committed 2026-08-16 20:47:45 +08:00
1 parent 763d2f0648
commit b14b4fced7
101 files changed
+963 -3928

No files matched your search

+30 -13
View File
@@ -5,37 +5,47 @@ import type { FormEvent } from "react";
import Image from "next/image";
import { Loader2, LogIn } from "lucide-react";
import { pulseFeedback, revealChildren, runScopedMotion } from "@/lib/ui/motion";
import { useBrowserAuth } from "@/components/browser-auth";
import { safeBrowserNext } from "@/lib/client/browser-auth";
export function AuthLoginPanel({
next,
configured,
message,
missing,
title = "账户登录",
submitLabel = "登录"
}: {
next: string;
configured: boolean;
message?: string | null;
missing?: string[];
title?: string;
submitLabel?: string;
}) {
const auth = useBrowserAuth();
const [phone, setPhone] = useState("");
const [password, setPassword] = useState("");
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
const [next, setNext] = useState("/create");
const [message, setMessage] = useState<string | null>(null);
const screenRef = useRef<HTMLDivElement | null>(null);
const feedbackRef = useRef<HTMLDivElement | null>(null);
const hasMissingConfig = !configured && Boolean(missing?.length);
const configured = auth.status === "ready" && auth.authConfigured;
const hasMissingConfig = auth.status === "ready" && auth.authRequired && !auth.authConfigured;
useEffect(() => {
return runScopedMotion(screenRef, (scope) => revealChildren(scope));
}, []);
useEffect(() => {
const params = new URLSearchParams(window.location.search);
setNext(safeBrowserNext(params.get("next")));
const errorCode = params.get("error");
if (errorCode) setMessage(errorMessages[errorCode] || "登录失败,请重新登录。");
else if (params.get("loggedOut") === "1") setMessage("已退出登录。");
}, []);
useEffect(() => {
if (auth.status === "ready" && auth.user) window.location.replace(next);
}, [auth.status, auth.user, next]);
useEffect(() => {
pulseFeedback(feedbackRef.current);
}, [error, message, missing?.join(",")]);
}, [error, message, hasMissingConfig]);
async function submit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
@@ -58,7 +68,7 @@ export function AuthLoginPanel({
});
const result = await response.json().catch(() => ({})) as { error?: string; redirectTo?: string };
if (!response.ok) throw new Error(result.error || "登录失败");
window.location.assign(result.redirectTo || next || "/create");
window.location.assign(safeBrowserNext(result.redirectTo, next));
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
} finally {
@@ -76,14 +86,15 @@ export function AuthLoginPanel({
<section className="panel auth-panel" data-animate>
<h2>{title}</h2>
{message || hasMissingConfig || error ? (
{message || hasMissingConfig || auth.status === "error" || error ? (
<div ref={feedbackRef}>
{message ? <div className="callout" role="alert">{message}</div> : null}
{hasMissingConfig ? (
<div className="callout" role="alert">
缺少配置:{missing?.join("、")}
账号认证配置不完整,请联系管理员。
</div>
) : null}
{auth.status === "error" ? <div className="callout" role="alert">{auth.error || "无法读取认证状态。"}</div> : null}
{error ? <div className="callout" role="alert">{error}</div> : null}
</div>
) : null}
@@ -128,3 +139,9 @@ export function AuthLoginPanel({
function formStringValue(value: FormDataEntryValue | null): string {
return typeof value === "string" ? value.trim() : "";
}
const errorMessages: Record<string, string> = {
auth_not_configured: "账号认证配置不完整,请联系管理员。",
callback_failed: "登录失败,请重新登录。",
state_invalid: "登录状态已失效,请重新登录。"
};