import { message } from "@/utils/notify"; import type { ApiEnvelope, ApiErrorBody } from "./types"; export class ApiError extends Error { readonly code: string; readonly httpStatus: number; readonly data?: unknown; constructor(code: string, msg: string, httpStatus = 400, data?: unknown) { super(msg); this.name = "ApiError"; this.code = code; this.httpStatus = httpStatus; this.data = data; } } export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; export interface RequestOptions { method?: HttpMethod; body?: unknown; headers?: Record; /** 为 true 时不弹出错误提示(由调用方处理) */ silent?: boolean; rawBody?: BodyInit | null; contentType?: string | null; } /** * 管理接口请求封装:自动带 credentials 与 X-Nixmsg-Request: 1,错误直接 message 显示。 * W4 接真实后端时页面不必改,只需让 admin 模块走本函数。 */ export async function requestAdmin(path: string, opts: RequestOptions = {}): Promise { const method = opts.method ?? (opts.body != null || opts.rawBody != null ? "POST" : "GET"); const headers: Record = { "X-Nixmsg-Request": "1", ...opts.headers, }; let body: BodyInit | undefined | null = opts.rawBody; if (opts.rawBody === undefined && opts.body !== undefined) { headers["Content-Type"] = opts.contentType ?? "application/json"; body = JSON.stringify(opts.body); } else if (opts.contentType) { headers["Content-Type"] = opts.contentType; } const res = await fetch(path, { method, credentials: "include", headers, body: body ?? undefined, }); let envelope: ApiEnvelope; try { envelope = (await res.json()) as ApiEnvelope; } catch { const err = new ApiError("internal", `响应不是 JSON(HTTP ${res.status})`, res.status); if (!opts.silent) { message.error(err.message); } throw err; } if (typeof envelope !== "object" || envelope === null) { const err = new ApiError("internal", "响应格式无效", res.status); if (!opts.silent) { message.error(err.message); } throw err; } if (envelope.ok) { return envelope.data; } const errBody: ApiErrorBody = envelope.error ?? { code: "internal", message: "未知错误" }; const err = new ApiError(errBody.code, errBody.message, res.status, "data" in envelope ? envelope.data : undefined); if (!opts.silent) { message.error(err.message); } throw err; }