import { getAccessToken, getRefreshToken, setTokens, clearTokens, } from './token-store' interface ApiResponse { success: boolean data: T message?: string statusCode?: number } export class ApiError extends Error { httpStatus: number code: number constructor(message: string, httpStatus: number, code?: number) { super(message) this.name = 'ApiError' this.httpStatus = httpStatus this.code = code ?? httpStatus } } let refreshPromise: Promise | null = null async function tryRefresh(): Promise { const token = getRefreshToken() if (!token) return false if (!refreshPromise) { refreshPromise = (async () => { try { const res = await fetch('/admin-api/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken: token }), }) if (!res.ok) return false const json: ApiResponse<{ accessToken: string; refreshToken: string }> = await res.json() if (json.success) { setTokens(json.data.accessToken, json.data.refreshToken) return true } return false } catch { return false } finally { refreshPromise = null } })() } return refreshPromise } async function request( path: string, options: RequestInit = {}, retried = false, ): Promise { const token = getAccessToken() const headers: Record = { 'Content-Type': 'application/json', ...(options.headers as Record), } if (token) { headers['Authorization'] = `Bearer ${token}` } const res = await fetch(path, { ...options, headers }) if (res.status === 401 && !retried) { const refreshed = await tryRefresh() if (refreshed) { return request(path, options, true) } clearTokens() window.location.href = '/login' throw new ApiError('登录已过期', 401) } const json: ApiResponse = await res.json() if (!json.success) { throw new ApiError(json.message || '请求失败', res.status, json.statusCode) } return json.data } export const api = { get(path: string) { return request(path) }, post(path: string, body?: unknown) { return request(path, { method: 'POST', body: body != null ? JSON.stringify(body) : undefined, }) }, put(path: string, body?: unknown) { return request(path, { method: 'PUT', body: body != null ? JSON.stringify(body) : undefined, }) }, delete(path: string, body?: unknown) { return request(path, { method: 'DELETE', body: body != null ? JSON.stringify(body) : undefined, }) }, }