135 lines
3.2 KiB
TypeScript
135 lines
3.2 KiB
TypeScript
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<T = any>(
|
|
path: string,
|
|
options: {
|
|
method?: string;
|
|
json?: unknown;
|
|
body?: BodyInit;
|
|
headers?: Record<string, string>;
|
|
} = {}
|
|
): Promise<T> {
|
|
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<string, string>;
|
|
} = {}
|
|
) {
|
|
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);
|
|
}
|