Files
Mutual_Fund/frontend/lib/api.ts
T

61 lines
2.2 KiB
TypeScript
Raw Normal View History

2026-09-14 11:54:20 +08:00
const API_BASE_URL =
process.env.NEXT_PUBLIC_API_BASE_URL ?? "/backend";
export class ApiError extends Error {
status: number;
code: number | string | undefined;
constructor(message: string, status: number, code?: number | string) {
super(message);
this.name = "ApiError";
this.status = status;
this.code = code;
}
}
export async function apiFetch<T>(path: string, init: RequestInit = {}): Promise<T> {
const token = typeof window === "undefined" ? null : localStorage.getItem("huaxia_token");
const headers = new Headers(init.headers);
headers.set("Content-Type", "application/json");
if (token) headers.set("Authorization", `Bearer ${token}`);
const response = await fetch(`${API_BASE_URL}${path}`, {
...init,
headers,
cache: "no-store",
});
const payload = await response.json().catch(() => null);
if (response.status === 401 && typeof window !== "undefined") {
localStorage.removeItem("huaxia_token");
if (!window.location.pathname.startsWith("/login")) window.location.href = "/login";
}
if (!response.ok || (payload && payload.code !== 200)) {
throw new ApiError(
payload?.message ?? "请求失败,请稍后重试",
response.status,
payload?.code
);
}
return (payload?.data ?? payload) as T;
}
export async function apiStream(path: string, body: unknown): Promise<string> {
const token = typeof window === "undefined" ? null : localStorage.getItem("huaxia_token");
const headers = new Headers({ "Content-Type": "application/json", Accept: "text/event-stream" });
if (token) headers.set("Authorization", `Bearer ${token}`);
const response = await fetch(`${API_BASE_URL}${path}`, {
method: "POST",
headers,
body: JSON.stringify(body),
cache: "no-store",
});
if (!response.ok) throw new ApiError("请求失败,请稍后重试", response.status);
const text = await response.text();
const event = text.split("\n").find((line) => line.startsWith("data:"));
if (!event) return text;
const payload = JSON.parse(event.slice(5).trim()) as { content?: string; answer?: string; message?: string };
return payload.content ?? payload.answer ?? payload.message ?? text;
}