SPB Git forge
1commits 1branches 0releases
189.0 KBsize
maindefault branch
5 h agolast push
Python 54.7% TypeScript 42.5% SQL 1.9%
1.8 KB · 50 lines typescript
Raw Blame History
1export class ApiError extends Error {2  status: number;3  detail: unknown;4  constructor(status: number, detail: unknown) {5    super(typeof detail === "string" ? detail : `HTTP ${status}`);6    this.status = status;7    this.detail = detail;8  }9}1011function redirectToLogin() {12  if (typeof window !== "undefined" && !window.location.pathname.startsWith("/login")) {13    // Full navigation on purpose: clears all client state/query cache after the session expired.14    // eslint-disable-next-line @next/next/no-location-assign-relative-destination15    window.location.href = `/login?next=${encodeURIComponent(window.location.pathname)}`;16  }17}1819export async function api<T>(path: string, init?: RequestInit & { json?: unknown }): Promise<T> {20  const headers: Record<string, string> = { Accept: "application/json", ...(init?.headers as Record<string, string>) };21  let body = init?.body;22  if (init?.json !== undefined) {23    headers["Content-Type"] = "application/json";24    body = JSON.stringify(init.json);25  }26  const res = await fetch(path, { ...init, body, headers, credentials: "include" });27  if (res.status === 401) {28    redirectToLogin();29    throw new ApiError(401, "authentication required");30  }31  const text = await res.text();32  let data: unknown = text;33  try {34    data = text ? JSON.parse(text) : null;35  } catch {36    /* non-JSON */37  }38  if (!res.ok) {39    const detail = (data as { detail?: unknown })?.detail ?? data;40    throw new ApiError(res.status, typeof detail === "string" ? detail : JSON.stringify(detail));41  }42  return data as T;43}4445export const qs = (params: Record<string, string | number | boolean | undefined | null>) =>46  Object.entries(params)47    .filter(([, v]) => v !== undefined && v !== null && v !== "")48    .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)49    .join("&");50