a058b0ab8e
- 修复记忆管理数据库连接不可用 (ai-core重编译+Unicode修复) - 修复IoT子会话工具调用链路日志缺失 - 新增最终审查子会话(review_provider) 支持消息格式解析拆分 - 实现历史消息持久化(后端存储+前端分页加载) - 前端新增动作消息(ActionMessage)类型和渲染 - 优化对话链路速度(非阻塞子会话+快速问候通道) - JWT密钥环境变量化(无默认值启动panic) - Token自动刷新机制(401拦截器+refresh接口) - WebSocket指数退避重连(jitter+最大10次) - localStorage清理一致性(cyrene_前缀+版本检查) - IoT环境变量统一为IOT_SERVICE_URL
156 lines
4.3 KiB
TypeScript
156 lines
4.3 KiB
TypeScript
/**
|
||
* 认证状态管理 (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<AuthStore>((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();
|
||
}
|