import { getToken } from "@/utils/auth"; export class AdminApiError extends Error { status: number; body: unknown; constructor(message: string, status: number, body: unknown) { super(message); this.name = "AdminApiError"; this.status = status; this.body = body; } } export function adminToken() { return getToken()?.accessToken || localStorage.getItem("admin_token") || ""; } export function friendlyError(error: unknown) { const anyError = error as any; if (anyError?.body?.detail) { return typeof anyError.body.detail === "string" ? anyError.body.detail : JSON.stringify(anyError.body.detail); } if (typeof anyError?.body === "string" && anyError.body) return anyError.body; return anyError?.message || String(error || "未知错误"); } export async function adminRequest( path: string, options: { method?: string; json?: unknown; body?: BodyInit; headers?: Record; } = {} ): Promise { const headers = new Headers(options.headers || {}); const token = adminToken(); if (token) headers.set("X-Admin-Token", token); let body = options.body; if (options.json !== undefined) { headers.set("Content-Type", "application/json"); body = JSON.stringify(options.json); } const response = await fetch(path, { method: options.method || "GET", headers, body, cache: "no-store" }); const text = await response.text(); let parsed: unknown = null; if (text) { try { parsed = JSON.parse(text); } catch { parsed = text; } } if (!response.ok) { throw new AdminApiError( `HTTP ${response.status}: ${text || response.statusText}`, response.status, parsed ); } return parsed as T; } export async function copyText(text: string) { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); return; } const textarea = document.createElement("textarea"); textarea.value = text; textarea.style.position = "fixed"; textarea.style.opacity = "0"; document.body.appendChild(textarea); textarea.select(); document.execCommand("copy"); textarea.remove(); } export async function downloadAdminFile( path: string, filename: string, options: { method?: string; json?: unknown; body?: BodyInit; headers?: Record; } = {} ) { const headers = new Headers(options.headers || {}); headers.set("X-Admin-Token", adminToken()); let body = options.body; if (options.json !== undefined) { headers.set("Content-Type", "application/json"); body = JSON.stringify(options.json); } const response = await fetch(path, { method: options.method || "GET", cache: "no-store", headers, body }); if (!response.ok) { const text = await response.text(); throw new AdminApiError( `HTTP ${response.status}: ${text || response.statusText}`, response.status, text ); } const blob = await response.blob(); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); link.remove(); URL.revokeObjectURL(url); }