| import { getCurrentLocale, translateInstant } from "../i18n"; |
|
|
| const BASE = "/api"; |
|
|
| export class ApiError extends Error { |
| status: number; |
| body: unknown; |
|
|
| constructor(message: string, status: number, body: unknown) { |
| super(message); |
| this.name = "ApiError"; |
| this.status = status; |
| this.body = body; |
| } |
| } |
|
|
| async function request<T>(path: string, init?: RequestInit): Promise<T> { |
| const headers = new Headers(init?.headers ?? undefined); |
| const body = init?.body; |
| if (!(body instanceof FormData) && !headers.has("Content-Type")) { |
| headers.set("Content-Type", "application/json"); |
| } |
| if (!headers.has("Accept-Language")) { |
| headers.set("Accept-Language", getCurrentLocale()); |
| } |
|
|
| const res = await fetch(`${BASE}${path}`, { |
| headers, |
| credentials: "include", |
| ...init, |
| }); |
| if (!res.ok) { |
| const errorBody = await res.json().catch(() => null); |
| const requestFailedFallback = translateInstant("api.requestFailedWithStatus", { |
| status: res.status, |
| defaultValue: |
| getCurrentLocale() === "zh-CN" |
| ? `请求失败:${res.status}` |
| : `Request failed: ${res.status}`, |
| }); |
| throw new ApiError( |
| (errorBody as { error?: string } | null)?.error ?? requestFailedFallback, |
| res.status, |
| errorBody, |
| ); |
| } |
| if (res.status === 204) return undefined as T; |
| return res.json(); |
| } |
|
|
| export const api = { |
| get: <T>(path: string) => request<T>(path), |
| post: <T>(path: string, body: unknown) => |
| request<T>(path, { method: "POST", body: JSON.stringify(body) }), |
| postForm: <T>(path: string, body: FormData) => |
| request<T>(path, { method: "POST", body }), |
| put: <T>(path: string, body: unknown) => |
| request<T>(path, { method: "PUT", body: JSON.stringify(body) }), |
| patch: <T>(path: string, body: unknown) => |
| request<T>(path, { method: "PATCH", body: JSON.stringify(body) }), |
| delete: <T>(path: string) => request<T>(path, { method: "DELETE" }), |
| }; |
|
|