mirror of
http://88.130.71.182:3000/BlitTech/badoHair_fe.git
synced 2026-06-13 10:41:11 +00:00
97 lines
3.4 KiB
TypeScript
97 lines
3.4 KiB
TypeScript
const BASE = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000";
|
|
const PREFIX = "/api/v1";
|
|
|
|
if (typeof window !== "undefined") {
|
|
console.log("[API] base URL:", `${BASE}${PREFIX}`);
|
|
}
|
|
|
|
// ── Token management ──────────────────────────────────────────────────────────
|
|
|
|
export function getToken(): string | null {
|
|
if (typeof window === "undefined") return null;
|
|
return localStorage.getItem("bado_token");
|
|
}
|
|
|
|
export function setTokens(access: string, refresh: string) {
|
|
localStorage.setItem("bado_token", access);
|
|
localStorage.setItem("bado_refresh", refresh);
|
|
}
|
|
|
|
export function clearTokens() {
|
|
localStorage.removeItem("bado_token");
|
|
localStorage.removeItem("bado_refresh");
|
|
}
|
|
|
|
// ── Error ─────────────────────────────────────────────────────────────────────
|
|
|
|
export class ApiError extends Error {
|
|
code: string;
|
|
status: number;
|
|
details?: unknown;
|
|
constructor(code: string, message: string, status: number, details?: unknown) {
|
|
super(message);
|
|
this.code = code;
|
|
this.status = status;
|
|
this.details = details;
|
|
}
|
|
}
|
|
|
|
// ── Paginated result shape ────────────────────────────────────────────────────
|
|
|
|
export interface PaginatedResult<T> {
|
|
data: T[];
|
|
meta: { total: number; page: number; per_page: number; pages: number };
|
|
}
|
|
|
|
// ── Core request ──────────────────────────────────────────────────────────────
|
|
|
|
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
|
const token = getToken();
|
|
const method = (init.method ?? "GET").toUpperCase();
|
|
const headers: Record<string, string> = {
|
|
...(init.headers as Record<string, string>),
|
|
};
|
|
if (method !== "GET" && method !== "DELETE" && !(init.body instanceof FormData)) {
|
|
headers["Content-Type"] = "application/json";
|
|
}
|
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
|
|
const res = await fetch(`${BASE}${PREFIX}${path}`, { ...init, headers });
|
|
|
|
if (res.status === 204) return undefined as unknown as T;
|
|
|
|
const body = await res.json();
|
|
|
|
if (body.success === false) {
|
|
const err = body.error ?? {};
|
|
throw new ApiError(
|
|
err.code ?? "UNKNOWN",
|
|
err.message ?? "Une erreur est survenue",
|
|
res.status,
|
|
err.details
|
|
);
|
|
}
|
|
|
|
if ("meta" in body) {
|
|
return { data: body.data, meta: body.meta } as T;
|
|
}
|
|
|
|
return body.data as T;
|
|
}
|
|
|
|
export const api = {
|
|
get: <T>(path: string) => request<T>(path),
|
|
post: <T>(path: string, data?: unknown) =>
|
|
request<T>(path, {
|
|
method: "POST",
|
|
body: data !== undefined ? JSON.stringify(data) : undefined,
|
|
}),
|
|
put: <T>(path: string, data?: unknown) =>
|
|
request<T>(path, { method: "PUT", body: JSON.stringify(data) }),
|
|
patch: <T>(path: string, data?: unknown) =>
|
|
request<T>(path, { method: "PATCH", body: JSON.stringify(data) }),
|
|
del: <T = void>(path: string) => request<T>(path, { method: "DELETE" }),
|
|
upload: <T>(path: string, formData: FormData) =>
|
|
request<T>(path, { method: "POST", body: formData }),
|
|
};
|