let csrfToken: string | null = null export class ApiError extends Error { constructor(public readonly status: number, message: string) { super(message) } } export function clearSessionToken() { csrfToken = null } export async function apiRequest(path: string, init: RequestInit = {}, protect = false): Promise { if (protect && !csrfToken) await apiRequest('/api/auth/me') const headers = new Headers(init.headers) headers.set('Accept', 'application/json') if (init.body) headers.set('Content-Type', 'application/json') if (protect && csrfToken) headers.set('X-CSRF-Token', csrfToken) const response = await fetch(path, { ...init, credentials: 'include', headers }) if (!response.ok) { if (response.status === 401) clearSessionToken() const body: unknown = await response.json().catch(() => null) const detail = body && typeof body === 'object' && 'detail' in body ? body.detail : null throw new ApiError(response.status, typeof detail === 'string' ? detail : `请求失败(${response.status}),请检查登录状态或填写内容。`) } if (response.status === 204) return undefined as T const data = await response.json() if (path === '/api/auth/login' || path === '/api/auth/me') { csrfToken = typeof data.csrf_token === 'string' ? data.csrf_token : null } return data as T }