export const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8000' export const ADMIN_UNAUTHORIZED_EVENT = 'admin:unauthorized' export class AdminUnauthorizedError extends Error { constructor(message = '未登录或登录已过期') { super(message) this.name = 'AdminUnauthorizedError' } } export function getAdminToken(): string { if (typeof window === 'undefined') return '' return localStorage.getItem('admin_token') || '' } export function clearAdminToken() { if (typeof window === 'undefined') return localStorage.removeItem('admin_token') } function notifyUnauthorized(message?: string) { if (typeof window === 'undefined') return window.dispatchEvent(new CustomEvent(ADMIN_UNAUTHORIZED_EVENT, { detail: { message } })) } async function safeReadErrorMessage(res: Response): Promise { try { const data = await res.json() const detail = typeof data?.detail === 'string' ? data.detail : null if (detail) return detail } catch { // ignore } return null } export async function adminFetch(path: string, init: RequestInit = {}): Promise { const token = getAdminToken() const headers = new Headers(init.headers) if (!token) { notifyUnauthorized() throw new AdminUnauthorizedError() } if (token) headers.set('Authorization', `Bearer ${token}`) if (!headers.has('Content-Type') && init.body) headers.set('Content-Type', 'application/json') const res = await fetch(`${API_BASE}${path}`, { ...init, headers }) if (res.status === 401) { const message = (await safeReadErrorMessage(res)) || undefined clearAdminToken() notifyUnauthorized(message) throw new AdminUnauthorizedError(message) } return res } export async function adminFetchJson(path: string, init: RequestInit = {}): Promise { const res = await adminFetch(path, init) if (!res.ok) { const message = (await safeReadErrorMessage(res)) || `请求失败 (${res.status})` throw new Error(message) } return res.json() as Promise }