import { createContext, useContext, useState, useCallback, useMemo, useEffect, type ReactNode, } from "react"; import { login as loginApi, getMe } from "../services/authApi"; // ============================================================ // 类型 // ============================================================ export interface UserInfo { id: string; username: string; display_name: string; role: string; } interface AuthState { user: UserInfo | null; token: string | null; loading: boolean; // 初始化时检查 token } interface AuthContextValue extends AuthState { login: (username: string, password: string) => Promise; logout: () => void; isAuthenticated: boolean; } // ============================================================ // Token 存储 key // ============================================================ const TOKEN_KEY = "track_admin_token"; const USER_KEY = "track_admin_user"; // ============================================================ // Context // ============================================================ const AuthContext = createContext(null); export function useAuth() { const ctx = useContext(AuthContext); if (!ctx) throw new Error("useAuth 必须在 AuthProvider 内使用"); return ctx; } // ============================================================ // Provider // ============================================================ export function AuthProvider({ children }: { children: ReactNode }) { const [state, setState] = useState({ user: null, token: null, loading: true, }); // 初始化:从 localStorage 恢复 token useEffect(() => { const savedToken = localStorage.getItem(TOKEN_KEY); const savedUser = localStorage.getItem(USER_KEY); if (savedToken && savedUser) { try { const user = JSON.parse(savedUser) as UserInfo; setState({ user, token: savedToken, loading: false }); // 可选:后端验证 token 是否仍有效 getMe(savedToken) .then((fresh) => { setState((prev) => ({ ...prev, user: fresh })); localStorage.setItem(USER_KEY, JSON.stringify(fresh)); }) .catch(() => { // token 过期,清除 logoutInternal(); }); } catch { logoutInternal(); } } else { setState((prev) => ({ ...prev, loading: false })); } }, []); function logoutInternal() { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); setState({ user: null, token: null, loading: false }); } const login = useCallback(async (username: string, password: string) => { const result = await loginApi(username, password); const token = result.access_token; const user: UserInfo = result.user; localStorage.setItem(TOKEN_KEY, token); localStorage.setItem(USER_KEY, JSON.stringify(user)); setState({ user, token, loading: false }); }, []); const logout = useCallback(() => { logoutInternal(); }, []); // 🚀 稳定 Context value 引用,避免消费者不必要的 re-render const ctxValue = useMemo( () => ({ ...state, login, logout, isAuthenticated: !!state.token && !!state.user, }), [state, login, logout], ); return ( {children} ); }