feat: 实现前端认证系统,WebSocket 连接携带 JWT token
- 新增 AuthContext/AuthProvider:登录/注册状态管理,JWT 自动刷新 - 新增 AuthPage 组件:登录/注册表单,支持模式切换和前端校验 - 新增 api.ts:封装 auth REST API 客户端(register/login/refresh/logout) - WebSocket 连接时拼接 ?token=<jwt>,重连自动携带 - useVisionSession 接受 accessToken 参数并透传 - App.tsx 包裹 AuthProvider,未登录时显示登录页 - storage.ts 新增 token/user 的 localStorage 存储 - i18n 新增中/英/日三语 auth 翻译 - App.css 新增 auth 页面和用户 badge 样式
This commit is contained in:
@@ -7,6 +7,9 @@ import type { ChatMessage, SessionConfig, SessionSummary, Theme } from "../types
|
||||
|
||||
const CONFIG_KEY = "camtalk:config";
|
||||
const THEME_KEY = "camtalk:theme";
|
||||
const ACCESS_TOKEN_KEY = "camtalk:access_token";
|
||||
const REFRESH_TOKEN_KEY = "camtalk:refresh_token";
|
||||
const USER_KEY = "camtalk:user";
|
||||
|
||||
const DEFAULT_CONFIG: SessionConfig = {
|
||||
ttsEnabled: true,
|
||||
@@ -100,3 +103,70 @@ export function deleteSessionMessages(sessionId: string): void {
|
||||
localStorage.removeItem(SESSION_MSG_PREFIX + sessionId);
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
// ---- Auth Token 存储 ----
|
||||
|
||||
export interface StoredUser {
|
||||
id: string;
|
||||
username: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/** 保存 access token */
|
||||
export function saveAccessToken(token: string): void {
|
||||
try {
|
||||
localStorage.setItem(ACCESS_TOKEN_KEY, token);
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
/** 获取 access token */
|
||||
export function loadAccessToken(): string | null {
|
||||
try {
|
||||
return localStorage.getItem(ACCESS_TOKEN_KEY);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 保存 refresh token */
|
||||
export function saveRefreshToken(token: string): void {
|
||||
try {
|
||||
localStorage.setItem(REFRESH_TOKEN_KEY, token);
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
/** 获取 refresh token */
|
||||
export function loadRefreshToken(): string | null {
|
||||
try {
|
||||
return localStorage.getItem(REFRESH_TOKEN_KEY);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 保存用户信息 */
|
||||
export function saveUser(user: StoredUser): void {
|
||||
try {
|
||||
localStorage.setItem(USER_KEY, JSON.stringify(user));
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
/** 获取用户信息 */
|
||||
export function loadUser(): StoredUser | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(USER_KEY);
|
||||
if (!raw) return null;
|
||||
return JSON.parse(raw) as StoredUser;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 清除所有 auth 数据 */
|
||||
export function clearAuth(): void {
|
||||
try {
|
||||
localStorage.removeItem(ACCESS_TOKEN_KEY);
|
||||
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
||||
localStorage.removeItem(USER_KEY);
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user