文件
csp/frontend/src/lib/api.ts

104 行
3.0 KiB
TypeScript
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
export const API_BASE =
process.env.NEXT_PUBLIC_API_BASE ??
(process.env.NODE_ENV === "development" ? "http://localhost:8080" : "/admin139");
function uiText(zhText: string, enText: string): string {
if (typeof window === "undefined") return zhText;
const lang = window.localStorage.getItem("csp.ui.language");
return lang === "en" ? enText : zhText;
}
type ApiEnvelope<T> =
| { ok: true; data?: T;[k: string]: unknown }
| { ok: false; error?: string;[k: string]: unknown };
export async function apiFetch<T>(
path: string,
init?: RequestInit,
token?: string
): Promise<T> {
const headers = new Headers(init?.headers);
if (token) headers.set("Authorization", `Bearer ${token}`);
if (init?.body && !headers.has("Content-Type")) {
headers.set("Content-Type", "application/json");
}
const method = (init?.method ?? "GET").toUpperCase();
const retryable = method === "GET" || method === "HEAD";
let resp: Response;
try {
resp = await fetch(`${API_BASE}${path}`, {
...init,
headers,
cache: "no-store",
});
} catch (err) {
if (!retryable) {
throw new Error(
uiText(
`网络请求失败,请检查后端服务或代理连接(${err instanceof Error ? err.message : String(err)}`,
`Network request failed. Please check backend/proxy connectivity (${err instanceof Error ? err.message : String(err)}).`
)
);
}
await new Promise((resolve) => setTimeout(resolve, 400));
try {
resp = await fetch(`${API_BASE}${path}`, {
...init,
headers,
cache: "no-store",
});
} catch (retryErr) {
throw new Error(
uiText(
`网络请求失败,请检查后端服务或代理连接(${retryErr instanceof Error ? retryErr.message : String(retryErr)
}`,
`Network request failed. Please check backend/proxy connectivity (${retryErr instanceof Error ? retryErr.message : String(retryErr)
}).`
)
);
}
}
const text = await resp.text();
let payload: unknown = null;
if (text) {
try {
payload = JSON.parse(text) as unknown;
} catch {
payload = text;
}
}
if (!resp.ok) {
const msg =
typeof payload === "object" && payload !== null && "error" in payload
? String((payload as { error?: unknown }).error ?? `HTTP ${resp.status}`)
: `HTTP ${resp.status}`;
throw new Error(msg);
}
if (typeof payload === "object" && payload !== null && "ok" in payload) {
const env = payload as ApiEnvelope<T>;
if (!env.ok) {
throw new Error(env.error ?? "request failed");
}
if ("data" in env) return (env.data as T) ?? ({} as T);
return payload as T;
}
return payload as T;
}
export interface RatingHistoryItem {
type: string;
created_at: number;
change: number;
note: string;
}
export async function listRatingHistory(limit: number = 100): Promise<RatingHistoryItem[]> {
return apiFetch<RatingHistoryItem[]>(`/api/v1/me/rating-history?limit=${limit}`);
}