"use client"; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react"; import { usePathname, useRouter } from "next/navigation"; import type { AuthMode, AuthUser } from "@/lib/auth/session"; import { fetchBrowserAuthState, safeBrowserLocationNext, type BrowserCurrentSession } from "@/lib/client/browser-auth"; type BrowserAuthStatus = "loading" | "ready" | "error"; type BrowserAuthState = { status: BrowserAuthStatus; user: AuthUser | null; authMode: AuthMode | null; authRequired: boolean; authConfigured: boolean; isAdmin: boolean; isSuperAdmin: boolean; error: string | null; refresh: () => Promise; }; const BrowserAuthContext = createContext(null); export function BrowserAuthProvider({ children }: { children: React.ReactNode }) { const [session, setSession] = useState(null); const [status, setStatus] = useState("loading"); const [error, setError] = useState(null); const loadSession = useCallback(async (signal?: AbortSignal) => { try { const current = await fetchBrowserAuthState(signal); setSession(current); setStatus("ready"); setError(null); } catch (cause) { if (cause instanceof DOMException && cause.name === "AbortError") return; setStatus("error"); setError(cause instanceof Error ? cause.message : "无法读取认证状态"); } }, []); useEffect(() => { const controller = new AbortController(); void loadSession(controller.signal); return () => controller.abort(); }, [loadSession]); const value = useMemo(() => { const user = session?.user || null; const authRequired = session?.authRequired ?? true; const isSuperAdmin = !authRequired || user?.role === "super_admin"; const isAdmin = isSuperAdmin || user?.role === "organization_admin"; return { status, user, authMode: session?.authMode || null, authRequired, authConfigured: session?.authConfigured ?? false, isAdmin, isSuperAdmin, error, refresh: () => loadSession() }; }, [error, loadSession, session, status]); return {children}; } export function useBrowserAuth(): BrowserAuthState { const state = useContext(BrowserAuthContext); if (!state) throw new Error("useBrowserAuth must be used inside BrowserAuthProvider"); return state; } export function BrowserAuthGuard({ children, role = "user" }: { children: React.ReactNode; role?: "user" | "admin" | "super"; }) { const auth = useBrowserAuth(); const pathname = usePathname(); const router = useRouter(); const needsLogin = auth.status === "ready" && auth.authRequired && !auth.user; useEffect(() => { if (needsLogin) { const next = safeBrowserLocationNext(window.location, pathname || "/create"); router.replace(`/auth/login?next=${encodeURIComponent(next)}`); } }, [needsLogin, pathname, router]); if (auth.status === "loading" || needsLogin) { return ; } if (auth.status === "error") { return void auth.refresh()} />; } if ((role === "admin" && !auth.isAdmin) || (role === "super" && !auth.isSuperAdmin)) { return ; } return <>{children}; } export function ClientRedirect({ to, forwardNext = false }: { to: string; forwardNext?: boolean }) { useEffect(() => { const next = forwardNext ? new URLSearchParams(window.location.search).get("next") : null; const destination = next ? `${to}?next=${encodeURIComponent(next)}` : to; window.location.replace(destination); }, [forwardNext, to]); return ; } function AuthStatePanel({ message, actionLabel, onAction }: { message: string; actionLabel?: string; onAction?: () => void; }) { return (

{message}

{actionLabel && onAction ? : null}
); }