You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

77 lines
2.0 KiB

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
import {
AUTH_TOKEN_KEY,
AUTH_USER_KEY,
} from "@/lib/frontend/constants";
import type { AdminUser } from "@/lib/frontend/types";
interface AuthContextValue {
user: AdminUser | null;
token: string | null;
ready: boolean;
login: (token: string, user: AdminUser) => void;
logout: () => void;
}
const AuthContext = createContext<AuthContextValue | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<AdminUser | null>(null);
const [token, setToken] = useState<string | null>(null);
const [ready, setReady] = useState(false);
useEffect(() => {
try {
const storedToken = localStorage.getItem(AUTH_TOKEN_KEY);
const storedUser = localStorage.getItem(AUTH_USER_KEY);
if (storedToken && storedUser) {
setToken(storedToken);
setUser(JSON.parse(storedUser) as AdminUser);
}
} catch {
localStorage.removeItem(AUTH_TOKEN_KEY);
localStorage.removeItem(AUTH_USER_KEY);
}
setReady(true);
}, []);
const login = useCallback((nextToken: string, nextUser: AdminUser) => {
localStorage.setItem(AUTH_TOKEN_KEY, nextToken);
localStorage.setItem(AUTH_USER_KEY, JSON.stringify(nextUser));
setToken(nextToken);
setUser(nextUser);
}, []);
const logout = useCallback(() => {
localStorage.removeItem(AUTH_TOKEN_KEY);
localStorage.removeItem(AUTH_USER_KEY);
setToken(null);
setUser(null);
}, []);
const value = useMemo(
() => ({ user, token, ready, login, logout }),
[user, token, ready, login, logout],
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext);
if (!ctx) {
throw new Error("useAuth 必须在 AuthProvider 内使用");
}
return ctx;
}