/** * 认证API模块 */ import type { ApiResponse, LoginResponse, AuthUser, TokenInfo, PasswordLoginRequest, CodeLoginRequest, ChangePasswordRequest, } from './types'; // API基础URL const API_BASE = '/api/v1/auth'; /** * 存储Token到localStorage */ export function saveTokens(tokens: TokenInfo): void { localStorage.setItem('accessToken', tokens.accessToken); localStorage.setItem('refreshToken', tokens.refreshToken); localStorage.setItem('tokenExpiresAt', String(Date.now() + tokens.expiresIn * 1000)); } /** * 从localStorage获取Token */ export function getAccessToken(): string | null { return localStorage.getItem('accessToken'); } export function getRefreshToken(): string | null { return localStorage.getItem('refreshToken'); } /** * 清除Token */ export function clearTokens(): void { localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); localStorage.removeItem('tokenExpiresAt'); localStorage.removeItem('user'); } /** * 存储用户信息 */ export function saveUser(user: AuthUser): void { localStorage.setItem('user', JSON.stringify(user)); } /** * 获取存储的用户信息 */ export function getSavedUser(): AuthUser | null { const userStr = localStorage.getItem('user'); if (!userStr) return null; try { return JSON.parse(userStr); } catch { return null; } } /** * 检查Token是否过期 */ export function isTokenExpired(): boolean { const expiresAt = localStorage.getItem('tokenExpiresAt'); if (!expiresAt) return true; return Date.now() > Number(expiresAt) - 60000; // 提前1分钟判断为过期 } /** * 创建带认证的fetch */ async function authFetch( url: string, options: RequestInit = {} ): Promise> { const token = getAccessToken(); const headers: HeadersInit = { 'Content-Type': 'application/json', ...(options.headers || {}), }; if (token) { (headers as Record)['Authorization'] = `Bearer ${token}`; } const response = await fetch(url, { ...options, headers, }); const data = await response.json(); if (!response.ok) { throw new Error(data.message || '请求失败'); } return data; } /** * 密码登录 */ export async function loginWithPassword(request: PasswordLoginRequest): Promise { const response = await authFetch(`${API_BASE}/login/password`, { method: 'POST', body: JSON.stringify(request), }); if (!response.success || !response.data) { throw new Error(response.message || '登录失败'); } // 保存Token和用户信息 saveTokens(response.data.tokens); saveUser(response.data.user); return response.data; } /** * 验证码登录 */ export async function loginWithCode(request: CodeLoginRequest): Promise { const response = await authFetch(`${API_BASE}/login/code`, { method: 'POST', body: JSON.stringify(request), }); if (!response.success || !response.data) { throw new Error(response.message || '登录失败'); } // 保存Token和用户信息 saveTokens(response.data.tokens); saveUser(response.data.user); return response.data; } /** * 发送验证码 */ export async function sendVerificationCode( phone: string, type: 'LOGIN' | 'RESET_PASSWORD' = 'LOGIN' ): Promise<{ expiresIn: number }> { const response = await authFetch<{ message: string; expiresIn: number }>( `${API_BASE}/verification-code`, { method: 'POST', body: JSON.stringify({ phone, type }), } ); if (!response.success || !response.data) { throw new Error(response.message || '发送失败'); } return { expiresIn: response.data.expiresIn }; } /** * 获取当前用户信息 */ export async function getCurrentUser(): Promise { const response = await authFetch(`${API_BASE}/me`); if (!response.success || !response.data) { throw new Error(response.message || '获取用户信息失败'); } // 更新本地存储 saveUser(response.data); return response.data; } /** * 修改密码 */ export async function changePassword(request: ChangePasswordRequest): Promise { const response = await authFetch<{ message: string }>(`${API_BASE}/change-password`, { method: 'POST', body: JSON.stringify(request), }); if (!response.success) { throw new Error(response.message || '修改密码失败'); } } /** * 刷新Token */ export async function refreshAccessToken(): Promise { const refreshToken = getRefreshToken(); if (!refreshToken) { throw new Error('无RefreshToken'); } const response = await fetch(`${API_BASE}/refresh`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken }), }); const data = await response.json(); if (!response.ok || !data.success) { clearTokens(); throw new Error(data.message || '刷新Token失败'); } // 保存新Token saveTokens(data.data); return data.data; } /** * 登出 */ export async function logout(): Promise { try { await authFetch(`${API_BASE}/logout`, { method: 'POST' }); } catch { // 忽略登出API错误 } finally { clearTokens(); } }