Files
ERP/src/lib/api.ts
T
2026-10-02 23:48:11 -07:00

60 lines
1.5 KiB
TypeScript

import { getDeviceId } from "@/lib/deviceId";
export class ApiError extends Error {
status: number;
body: any;
constructor(message: string, status: number, body?: any) {
super(message);
this.status = status;
this.body = body;
}
}
let refreshInFlight: Promise<boolean> | null = null;
async function tryRefresh(): Promise<boolean> {
if (!refreshInFlight) {
refreshInFlight = fetch("/api/auth/refresh", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ deviceId: getDeviceId() }),
})
.then((r) => r.ok)
.finally(() => {
refreshInFlight = null;
});
}
return refreshInFlight;
}
export async function apiFetch<T = any>(
path: string,
options: RequestInit = {},
_retried = false
): Promise<T> {
const res = await fetch(path, {
...options,
credentials: "include",
headers: {
"Content-Type": "application/json",
...(options.headers ?? {}),
},
});
if (res.status === 401 && !_retried && !path.startsWith("/api/auth/")) {
const refreshed = await tryRefresh();
if (refreshed) {
return apiFetch<T>(path, options, true);
}
}
const isJson = res.headers.get("content-type")?.includes("application/json");
const body = isJson ? await res.json().catch(() => ({})) : null;
if (!res.ok) {
throw new ApiError(body?.error ?? "Request failed", res.status, body);
}
return body as T;
}