// 对齐 web.archived dataProvider 的请求语义(fetch → Basic Auth → 401 全局登出)。 export const AUTH_STORAGE_KEY = 'creatorhub.auth'; export class RequestError extends Error { status: number; body?: unknown; constructor(message: string, status: number, body?: unknown) { super(message); this.status = status; this.body = body; } } export function unauthorized(): never { localStorage.removeItem(AUTH_STORAGE_KEY); if (window.location.pathname !== '/login') { window.history.replaceState({}, '', '/login'); window.dispatchEvent(new PopStateEvent('popstate')); } throw new RequestError('认证已过期,请重新登录', 401); } export async function request(path: string, options: RequestInit = {}): Promise { const auth = localStorage.getItem(AUTH_STORAGE_KEY); const headers: Record = { ...(options.headers as Record) }; if (auth) headers.Authorization = `Basic ${btoa(auth)}`; let response: Response; try { response = await fetch(`/api${path}`, { ...options, headers }); } catch (networkError: any) { throw new RequestError(`网络请求失败:${networkError?.message ?? networkError}`, 0); } if (response.status === 401) unauthorized(); if (!response.ok) { const body = await response.json().catch(() => ({})); throw new RequestError(body.error || `请求失败 (${response.status})`, response.status, body); } if (response.status === 204) return null; const contentType = response.headers?.get?.('content-type') || ''; if (contentType.toLowerCase().includes('text/html')) { throw new RequestError('API 返回了非 JSON 响应,请检查控制面路由', response.status); } return response.json(); } export function jsonOptions(method: string, data: unknown): RequestInit { return { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }; }