/** * 认证状态管理 (Zustand Store) * 用于跨组件共享登录/退出状态 */ import { create } from 'zustand'; import { login as apiLogin, register as apiRegister, clearToken, isAuthenticated, getToken, getRefreshToken, setTokens } from '@/api/client'; /** localStorage key 前缀 */ const LS_VERSION_KEY = 'cyrene_store_version'; const CURRENT_VERSION = 1; /** 所有 cyrene_ 前缀的 localStorage keys,logout 时全部清除 */ const CYRENE_KEYS = [ 'token', 'refresh_token', 'user_id', 'user_nickname', 'cyrene_store_version', ]; function clearAllCyreneData(): void { for (const key of CYRENE_KEYS) { try { localStorage.removeItem(key); } catch { /* ignore */ } } } interface AuthStore { isLoggedIn: boolean; userId: string | null; token: string | null; refreshToken: string | null; loading: boolean; login: (username: string, password: string) => Promise<{ success: boolean; error?: string }>; register: (username: string, password: string, email: string, nickname: string, verifyCode: string) => Promise<{ success: boolean; error?: string }>; logout: () => void; setTokens: (accessToken: string, refreshToken?: string) => void; clearAuth: () => void; } export const useAuthStore = create((set) => ({ isLoggedIn: isAuthenticated(), userId: localStorage.getItem('user_id'), token: getToken(), refreshToken: getRefreshToken(), loading: false, setTokens: (accessToken: string, refreshTokenValue?: string) => { setTokens(accessToken, refreshTokenValue); set({ isLoggedIn: true, token: accessToken, refreshToken: refreshTokenValue || null, }); }, login: async (username: string, password: string) => { set({ loading: true }); try { const resp = await apiLogin(username, password); if (resp.error) { set({ loading: false }); return { success: false, error: resp.error }; } set({ isLoggedIn: true, userId: resp.data?.user_id || null, token: resp.data?.token || null, refreshToken: resp.data?.refresh_token || null, loading: false, }); return { success: true }; } catch (err) { set({ loading: false }); return { success: false, error: err instanceof Error ? err.message : '登录失败' }; } }, register: async (username: string, password: string, email: string, nickname: string, verifyCode: string) => { set({ loading: true }); try { const resp = await apiRegister(username, password, email, nickname, verifyCode); if (resp.error) { set({ loading: false }); return { success: false, error: resp.error }; } // 保存昵称到 localStorage if (resp.data?.nickname) { localStorage.setItem('user_nickname', resp.data.nickname); } set({ isLoggedIn: true, userId: resp.data?.user_id || null, token: resp.data?.token || null, refreshToken: resp.data?.refresh_token || null, loading: false, }); return { success: true }; } catch (err) { set({ loading: false }); return { success: false, error: err instanceof Error ? err.message : '注册失败' }; } }, logout: () => { clearToken(); clearAllCyreneData(); set({ isLoggedIn: false, userId: null, token: null, refreshToken: null, loading: false, }); }, clearAuth: () => { clearToken(); clearAllCyreneData(); set({ isLoggedIn: false, userId: null, token: null, refreshToken: null, loading: false, }); }, })); /** * 在应用启动时检查 localStorage 数据版本 * 如果版本不兼容,清除所有数据 */ export function checkAndMigrateStore(): void { try { const storedVersion = localStorage.getItem(LS_VERSION_KEY); if (storedVersion) { const version = parseInt(storedVersion, 10); if (version !== CURRENT_VERSION) { console.log(`[store] localStorage 版本不兼容 (${version} → ${CURRENT_VERSION}),清除旧数据`); clearAllCyreneData(); } } // 写入当前版本 localStorage.setItem(LS_VERSION_KEY, String(CURRENT_VERSION)); } catch { // 忽略存储错误 } } /** 兼容旧代码的 Hook 导出 */ export function useAuth() { return useAuthStore(); }