import { userApi, User, RegisterRequest as ApiRegisterRequest, LoginResponse as ApiLoginResponse, TokenVerifyResponse } from './userApi'; // 用户信息接口(兼容现有代码) export interface UserInfo { id: string; nickname: string; phone?: string; email?: string; avatar?: string; registrationDate?: string; apikey?: string; // 添加API密钥字段 realName?: string; // 真实姓名 isVerified?: string; // 认证状态 verifiedAt?: string; // 认证时间 } // 登录响应接口(兼容现有代码) export interface LoginResponse { code: number; message: string; data: { token: string; refreshToken?: string; expiresIn?: number; user: UserInfo; }; } // 注册请求接口 export interface RegisterRequest { username: string; password: string; email?: string; phone?: string; nickname?: string; schoolEmail?: string; // 学校邮箱(可选) sms_code?: string; email_code?: string; } // 注册响应接口 export interface RegisterResponse { code: number; message: string; data: { userId: string; user?: UserInfo; }; } // 转换后端用户数据为前端格式 function convertUserToUserInfo(user: User): UserInfo { return { id: user.id, nickname: user.nickname, phone: user.phone || undefined, email: user.email || undefined, avatar: user.avatar || undefined, registrationDate: user.registration_date, apikey: user.apikey || undefined, realName: user.real_name || undefined, isVerified: user.is_verified || 'unverified', verifiedAt: user.verified_at || undefined, }; } // Token 存储键名 const TOKEN_KEY = 'aigc_space_token'; const REFRESH_TOKEN_KEY = 'aigc_space_refresh_token'; const USER_INFO_KEY = 'aigc_space_user_info'; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:8010'; // 认证状态变化事件名 const AUTH_CHANGE_EVENT = 'auth_state_change'; /** * 鉴权服务类 * 负责管理用户认证、token 存储和 API 请求鉴权 */ class AuthService { private token: string | null = null; private refreshToken: string | null = null; private userInfo: UserInfo | null = null; constructor() { // 从 localStorage 恢复 token 和用户信息 this.loadFromStorage(); } /** * 订阅认证状态变化 * @param callback 状态变化时的回调函数 * @returns 取消订阅的函数 */ subscribe(callback: () => void): () => void { const handler = (e: Event) => { // console.log('[AuthService] event handler triggered'); callback(); }; window.addEventListener(AUTH_CHANGE_EVENT, handler); console.log('[AuthService] subscribed to', AUTH_CHANGE_EVENT); return () => { window.removeEventListener(AUTH_CHANGE_EVENT, handler); // console.log('[AuthService] unsubscribed from', AUTH_CHANGE_EVENT); }; } /** * 通知所有订阅者状态已变化 */ private notifyListeners(): void { console.log('[AuthService] notifyListeners called'); // 触发自定义事件,用于跨组件通信 if (typeof window !== 'undefined') { const event = new CustomEvent(AUTH_CHANGE_EVENT); console.log('[AuthService] dispatching event:', AUTH_CHANGE_EVENT); window.dispatchEvent(event); } } /** * 从 localStorage 加载 token 和用户信息 */ private loadFromStorage(): void { if (typeof window !== 'undefined') { const token = localStorage.getItem(TOKEN_KEY); const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY); // 确保 token 不是空字符串 this.token = token && token.trim() !== '' ? token : null; this.refreshToken = refreshToken && refreshToken.trim() !== '' ? refreshToken : null; const userInfoStr = localStorage.getItem(USER_INFO_KEY); if (userInfoStr) { try { this.userInfo = JSON.parse(userInfoStr); } catch (e) { console.error('Failed to parse user info from storage:', e); this.userInfo = null; } } } } /** * 保存 token 和用户信息到 localStorage */ private saveToStorage(token: string, userInfo: UserInfo, refreshToken?: string): void { if (typeof window !== 'undefined') { localStorage.setItem(TOKEN_KEY, token); if (refreshToken) { localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken); } localStorage.setItem(USER_INFO_KEY, JSON.stringify(userInfo)); } this.token = token; this.refreshToken = refreshToken || null; this.userInfo = userInfo; // 通知订阅者状态已变化 this.notifyListeners(); } /** * 清除存储的 token 和用户信息 */ private clearStorage(): void { console.log('[AuthService] clearStorage called'); if (typeof window !== 'undefined') { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(REFRESH_TOKEN_KEY); localStorage.removeItem(USER_INFO_KEY); console.log('[AuthService] localStorage cleared'); } this.token = null; this.refreshToken = null; this.userInfo = null; console.log('[AuthService] instance state cleared, notifying listeners'); // 通知订阅者状态已变化 this.notifyListeners(); } /** * SSO一键登录(通过学校网站token) * @param ssoToken 学校网站提供的SSO token * @returns Promise */ async ssoLogin(ssoToken: string): Promise { try { const response = await fetch(`${API_BASE_URL}/api/auth/verify`, { method: 'GET', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${ssoToken}`, }, }); if (!response.ok) { return { code: response.status, message: 'SSO登录失败,票据已失效或无效', data: { token: '', user: { id: '', nickname: '', }, }, }; } const verifyResult = (await response.json()) as TokenVerifyResponse; const userInfo = convertUserToUserInfo(verifyResult.user); this.saveToStorage(ssoToken, userInfo); return { code: 200, message: 'SSO登录成功', data: { token: ssoToken, user: userInfo, }, }; } catch (error) { return { code: 500, message: error instanceof Error ? error.message : 'SSO登录失败', data: { token: '', user: { id: '', nickname: '', }, }, }; } } /** * 查询后端 SSO 配置(含是否启用、CAS 登录地址等) */ async getSsoConfig(): Promise<{ sso_enabled: boolean; cas_login_url: string } | null> { try { const res = await fetch(`${API_BASE_URL}/api/sso/config`); if (!res.ok) return null; return await res.json(); } catch { return null; } } /** * 查询后端 SSO 是否启用 */ async isSsoEnabled(): Promise { const cfg = await this.getSsoConfig(); if (cfg === null) return false; // 请求失败时默认不启用SSO,跳普通登录页 return cfg.sso_enabled === true; } /** * 登录 * @param username 用户名或手机号 * @param password 密码 * @param keyId 会话密钥ID * @returns Promise */ async login(username: string, password: string, keyId: string | null = null): Promise { try { const apiResponse = await userApi.login(username, password, keyId); const userInfo = convertUserToUserInfo(apiResponse.user); // 保存 token 和用户信息 this.saveToStorage(apiResponse.access_token, userInfo); return { code: 200, message: '登录成功', data: { token: apiResponse.access_token, user: userInfo, } }; } catch (error) { return { code: 401, message: error instanceof Error ? error.message : '登录失败', data: { token: '', user: { id: '', nickname: '', } } }; } } /** * 注册 * @param registerData 注册信息 * @param keyId 会话密钥ID * @returns Promise */ async register(registerData: RegisterRequest, keyId: string | null = null): Promise { try { const apiRegisterData: ApiRegisterRequest = { username: registerData.username, password: registerData.password, nickname: registerData.nickname || registerData.username, email: registerData.email || registerData.schoolEmail, phone: registerData.phone, sms_code: registerData.sms_code, email_code: registerData.email_code, }; const user = await userApi.register(apiRegisterData, keyId); const userInfo = convertUserToUserInfo(user); return { code: 200, message: '注册成功', data: { userId: user.id, user: userInfo, } }; } catch (error) { return { code: 400, message: error instanceof Error ? error.message : '注册失败', data: { userId: '', } }; } } /** * 登出 * @param callApi 是否调用后端API(默认true) */ async logout(callApi: boolean = true): Promise { console.log('[AuthService] logout called'); // 尝试调用后端登出API if (callApi && this.token) { try { await userApi.logout(); console.log('[AuthService] backend logout successful'); } catch (error) { console.warn('[AuthService] backend logout failed:', error); // 即使后端调用失败,也继续清理本地状态 } } this.clearStorage(); } /** * 本地登出后跳转CAS统一认证注销 */ async logoutAndRedirectToCas(): Promise { await this.logout(true); if (typeof window !== 'undefined') { const cfg = await this.getSsoConfig(); if (cfg?.sso_enabled) { const casLogoutUrl = cfg.cas_login_url.replace(/\/login$/, '/logout'); window.location.href = casLogoutUrl; } else { window.location.href = '/login'; } } } /** * 验证Token是否有效 * @returns Promise<{valid: boolean, user?: UserInfo}> */ async verifyToken(): Promise<{ valid: boolean; user?: UserInfo }> { if (!this.token) { return { valid: false }; } try { const response = await userApi.verifyToken(); if (response.valid && response.user) { const userInfo = convertUserToUserInfo(response.user); // 更新本地存储的用户信息 this.userInfo = userInfo; if (typeof window !== 'undefined') { localStorage.setItem(USER_INFO_KEY, JSON.stringify(userInfo)); } return { valid: true, user: userInfo }; } return { valid: false }; } catch (error) { console.error('[AuthService] Token verification failed:', error); return { valid: false }; } } /** * 刷新Token并延长有效期 * @returns Promise 是否刷新成功 */ async refreshTokenAndExtend(): Promise { if (!this.token) { return false; } try { const response = await userApi.refreshToken(); const userInfo = convertUserToUserInfo(response.user); // 保存新的Token和用户信息 this.saveToStorage(response.access_token, userInfo); console.log('[AuthService] Token refreshed successfully'); return true; } catch (error) { console.error('[AuthService] Token refresh failed:', error); return false; } } /** * 检查并刷新Token * 如果Token有效则刷新延长有效期,如果无效则清除登录状态 * @returns Promise Token是否有效 */ async checkAndRefreshToken(): Promise { const verifyResult = await this.verifyToken(); if (!verifyResult.valid) { // Token无效,清除登录状态(不调用后端API,因为Token已经无效) await this.logout(false); return false; } // Token有效,刷新延长有效期 const refreshed = await this.refreshTokenAndExtend(); if (!refreshed) { // 刷新失败但验证成功,保持当前状态 console.warn('[AuthService] Token valid but refresh failed'); } return true; } /** * 获取当前 token * @returns token 字符串或 null */ getToken(): string | null { return this.token; } /** * 获取刷新 token * @returns refreshToken 字符串或 null */ getRefreshToken(): string | null { return this.refreshToken; } /** * 获取当前用户信息 * @returns 用户信息对象或 null */ getUserInfo(): UserInfo | null { return this.userInfo; } /** * 检查是否已登录 * @returns 是否已登录 */ isAuthenticated(): boolean { return this.token !== null && this.userInfo !== null; } /** * 刷新 token * @returns Promise 新的 token */ async refreshAccessToken(): Promise { if (!this.refreshToken) { throw new Error('No refresh token available'); } // 模拟网络延迟 await new Promise(resolve => setTimeout(resolve, 300)); // 模拟刷新 token API 调用 const newToken = `mock_token_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; if (typeof window !== 'undefined') { localStorage.setItem(TOKEN_KEY, newToken); } this.token = newToken; return newToken; } /** * 设置 token(用于外部设置,如从其他来源获取 token) * @param token token 字符串 * @param userInfo 用户信息 */ setToken(token: string, userInfo: UserInfo): void { this.saveToStorage(token, userInfo); } /** * 获取 Authorization header 值 * @returns Authorization header 字符串,格式:'Bearer {token}' */ getAuthHeader(): string | null { if (!this.token || this.token.trim() === '') { return null; } return `Bearer ${this.token}`; } } // 导出单例 export const authService = new AuthService(); // 导出类型 export type { UserInfo, LoginResponse, RegisterRequest, RegisterResponse };