/** * Toutes les requêtes passent par la même origine (le frontend est servi * par le même Caddy qui route /api/* vers tripoli-edge -> tripoli-api) : * pas besoin de configurer une URL de base, ni de CORS. */ export class ApiError extends Error { status: number; constructor(status: number, message: string) { super(message); this.status = status; } } export async function apiFetch(path: string, init?: RequestInit): Promise { const res = await fetch(path, { ...init, credentials: 'include', headers: { ...(init?.body && !(init.body instanceof FormData) ? { 'Content-Type': 'application/json' } : {}), ...init?.headers, }, }); if (!res.ok) { let message = res.statusText; try { const body = (await res.json()) as { error?: string }; if (body.error) message = body.error; } catch { // pas de corps JSON exploitable - on garde le statusText } throw new ApiError(res.status, message); } if (res.status === 204 || res.headers.get('content-length') === '0') { return undefined as T; } return (await res.json()) as T; }