Files
zhimingge/components/auth/auth-provider.tsx
T
2026-06-13 09:39:38 +08:00

81 lines
2.0 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
interface AuthState {
loading: boolean;
authEnabled: boolean;
loggedIn: boolean;
username?: string;
refresh: () => Promise<void>;
logout: () => Promise<void>;
}
const AuthContext = createContext<AuthState | null>(null);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [loading, setLoading] = useState(true);
const [authEnabled, setAuthEnabled] = useState(false);
const [loggedIn, setLoggedIn] = useState(true);
const [username, setUsername] = useState<string>();
const refresh = useCallback(async () => {
try {
const res = await fetch("/api/auth/me", { cache: "no-store" });
const data = await res.json();
setAuthEnabled(!!data.authEnabled);
setLoggedIn(!!data.loggedIn);
setUsername(data.username);
} catch {
setAuthEnabled(false);
setLoggedIn(true);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
refresh();
}, [refresh]);
const logout = useCallback(async () => {
await fetch("/api/auth/logout", { method: "POST" });
await refresh();
window.location.href = "/";
}, [refresh]);
const value = useMemo(
() => ({ loading, authEnabled, loggedIn, username, refresh, logout }),
[loading, authEnabled, loggedIn, username, refresh, logout],
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) {
throw new Error("useAuth must be used within AuthProvider");
}
return ctx;
}
export function useRequireAuthForPath(path: string): boolean {
const { authEnabled, loggedIn } = useAuth();
if (!authEnabled) {
return true;
}
const protectedPaths = ["/liuyao", "/bazi", "/combined", "/history"];
if (!protectedPaths.some((p) => path === p || path.startsWith(`${p}/`))) {
return true;
}
return loggedIn;
}