fix: 第一轮修复 - 记忆管理/IoT操控/历史消息持久化/动作消息/链路优化/安全配置

- 修复记忆管理数据库连接不可用 (ai-core重编译+Unicode修复)
- 修复IoT子会话工具调用链路日志缺失
- 新增最终审查子会话(review_provider) 支持消息格式解析拆分
- 实现历史消息持久化(后端存储+前端分页加载)
- 前端新增动作消息(ActionMessage)类型和渲染
- 优化对话链路速度(非阻塞子会话+快速问候通道)
- JWT密钥环境变量化(无默认值启动panic)
- Token自动刷新机制(401拦截器+refresh接口)
- WebSocket指数退避重连(jitter+最大10次)
- localStorage清理一致性(cyrene_前缀+版本检查)
- IoT环境变量统一为IOT_SERVICE_URL
This commit is contained in:
2026-05-21 23:10:07 +08:00
parent 8b7d4ec19a
commit a058b0ab8e
53 changed files with 5535 additions and 241 deletions
+72 -2
View File
@@ -3,24 +3,58 @@
* 用于跨组件共享登录/退出状态
*/
import { create } from 'zustand';
import { login as apiLogin, register as apiRegister, clearToken, isAuthenticated, getToken } from '@/api/client';
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 keyslogout 时全部清除 */
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 {
@@ -33,6 +67,7 @@ export const useAuthStore = create<AuthStore>((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 };
@@ -58,6 +93,7 @@ export const useAuthStore = create<AuthStore>((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 };
@@ -69,16 +105,50 @@ export const useAuthStore = create<AuthStore>((set) => ({
logout: () => {
clearToken();
localStorage.removeItem('user_nickname');
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();