// Kommunikation mit der API. Einziger Ort, an dem fetch() aufgerufen wird. "use strict"; export class ApiError extends Error { constructor(status, detail) { super(detail); this.name = "ApiError"; this.status = status; } } export class OfflineError extends Error { constructor() { super("Keine Verbindung zum Server."); this.name = "OfflineError"; } } /** Das CSRF-Token steht in einem lesbaren Cookie. Das Session-Cookie ist * HttpOnly und für JavaScript unsichtbar - genau so soll es sein. */ function csrfToken() { const hit = document.cookie .split(";") .map((c) => c.trim()) .find((c) => c.startsWith("ea_csrf=")); return hit ? decodeURIComponent(hit.slice("ea_csrf=".length)) : ""; } /** Übersetzt die Fehlerform von FastAPI in einen Satz für Menschen. * Bei Validierungsfehlern ist "detail" eine Liste, keine Zeichenkette. */ function describe(status, data) { const detail = data && data.detail; if (typeof detail === "string") return detail; if (Array.isArray(detail) && detail.length) { const first = detail[0]; const field = Array.isArray(first.loc) ? first.loc[first.loc.length - 1] : ""; return field ? `Ungültige Eingabe bei "${field}": ${first.msg}` : first.msg; } if (status === 401) return "Nicht angemeldet."; if (status === 429) return "Zu viele Versuche. Bitte einen Moment warten."; if (status >= 500) return "Der Server hat einen Fehler gemeldet."; return `Unerwarteter Fehler (HTTP ${status}).`; } export async function api(path, { method = "GET", body } = {}) { const headers = {}; if (body !== undefined) headers["Content-Type"] = "application/json"; if (method !== "GET" && method !== "HEAD") headers["X-CSRF-Token"] = csrfToken(); let response; try { response = await fetch(path, { method, headers, credentials: "same-origin", body: body === undefined ? undefined : JSON.stringify(body), }); } catch { // Netzwerkfehler von HTTP-Fehlern trennen: In Phase 4 entscheidet // genau das, ob eine Änderung in die Outbox wandert. throw new OfflineError(); } const text = await response.text(); let data = null; if (text) { try { data = JSON.parse(text); } catch { throw new ApiError( response.status, `Unerwartete Antwort vom Server (HTTP ${response.status}).` ); } } if (!response.ok) throw new ApiError(response.status, describe(response.status, data)); return data; } export const get = (path) => api(path); export const post = (path, body) => api(path, { method: "POST", body }); export const patch = (path, body) => api(path, { method: "PATCH", body }); export const put = (path, body) => api(path, { method: "PUT", body }); export const del = (path) => api(path, { method: "DELETE" });