import { getBaseUrl, type BaseUrlKey } from "./baseUrls"; export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; export interface EndpointConfig { base: BaseUrlKey; // e.g. "events" path: string; // e.g. "/api/event/all" method: HttpMethod; } export const createEndpoint = ( base: BaseUrlKey, path: string, method: HttpMethod = "GET" ): EndpointConfig => ({ base, path, method }); interface CallEndpointOptions { body?: TBody; query?: Record; headers?: Record; } export async function callEndpoint( endpoint: EndpointConfig, options: CallEndpointOptions = {} ): Promise { const { body, query, headers } = options; const baseUrl = getBaseUrl(endpoint.base); const url = buildUrl(baseUrl, endpoint.path, query); const init: RequestInit = { method: endpoint.method, headers: { "Content-Type": "application/json", ...(headers || {}), }, }; if (body && endpoint.method !== "GET") { // @ts-ignore body is allowed on RequestInit for fetch init.body = JSON.stringify(body); } // Debug log of what we're calling (without dumping full token) const authHeader = headers?.Authorization || ""; const shortToken = authHeader.startsWith("Bearer ") ? authHeader.slice(7, 27) + "..." : undefined; // eslint-disable-next-line no-console console.log("[api] request", { url, method: endpoint.method, hasAuth: !!authHeader, tokenPrefix: shortToken, }); const res = await fetch(url, init as any); if (!res.ok) { const text = await res.text().catch(() => ""); // eslint-disable-next-line no-console console.log("[api] error response", { url, status: res.status, body: text, }); throw new Error(text || `Request failed with status ${res.status}`); } const contentType = res.headers.get("content-type") || ""; if (contentType.includes("application/json")) { return (await res.json()) as TResponse; } return (await res.text()) as unknown as TResponse; } function buildUrl( baseUrl: string, path: string, query?: Record ): string { const trimmedBase = baseUrl.replace(/\/$/, ""); const trimmedPath = path.startsWith("/") ? path : `/${path}`; const url = new URL(trimmedBase + trimmedPath); if (query) { Object.entries(query).forEach(([key, value]) => { if (value === undefined) return; url.searchParams.set(key, String(value)); }); } return url.toString(); }