"use client"; // Chamadas feitas pelo navegador. Vão para /api/v1 na mesma origem; o Next // repassa para a API Go. Toda mutação envia o token CSRF (double submit). import type { Problem } from "./types"; export class ApiError extends Error { status: number; problem: Problem | null; constructor(status: number, problem: Problem | null) { super(problem?.title ?? `Erro ${status}`); this.status = status; this.problem = problem; } /** Mensagem com os erros de campo, se houver. */ details(): string { const fields = this.problem?.errors ?? []; if (fields.length === 0) return this.message; return `${this.message}: ${fields.map((e) => `${e.field} — ${e.message}`).join("; ")}`; } } function readCookie(name: string): string { const match = document.cookie.split("; ").find((c) => c.startsWith(`${name}=`)); return match ? decodeURIComponent(match.slice(name.length + 1)) : ""; } export async function apiFetch( path: string, options: { method?: string; body?: unknown } = {}, ): Promise { const method = options.method ?? "GET"; const headers: Record = { accept: "application/json" }; if (method !== "GET") { headers["X-CSRF-Token"] = readCookie("bp_csrf"); } if (options.body !== undefined) { headers["content-type"] = "application/json"; } const res = await fetch(`/api/v1${path}`, { method, headers, body: options.body === undefined ? undefined : JSON.stringify(options.body), credentials: "same-origin", }); if (res.status === 401 && path !== "/auth/login") { window.location.href = "/login"; throw new ApiError(401, null); } if (!res.ok) { const problem = (await res.json().catch(() => null)) as Problem | null; throw new ApiError(res.status, problem); } if (res.status === 204) { return undefined as T; } return (await res.json()) as T; } export function errorMessage(err: unknown): string { if (err instanceof ApiError) return err.details(); if (err instanceof Error) return err.message; return "Erro inesperado"; }