import "server-only"; import { getValidSession } from "@/lib/auth/session"; import { ApiError, type WinofyErrorDetails } from "./errors"; import { logWinofyRequest, logWinofyResponse } from "./dev-logger"; const BASE_URL = process.env.WINOFY_API_BASE_URL!; interface WinofyFetchOptions extends Omit { /** Attach the caller's bearer token. Defaults to true — set false for public endpoints. */ auth?: boolean; params?: Record; body?: unknown; } export interface PaginatedResponse { count: number; next: string | null; previous: string | null; results: T[]; } export async function winofyFetch(path: string, options: WinofyFetchOptions = {}): Promise { const { auth = true, params, headers, body, ...rest } = options; const method = rest.method ?? "GET"; const url = new URL(BASE_URL.replace(/\/$/, "") + path); if (params) { for (const [key, value] of Object.entries(params)) { if (value !== undefined) url.searchParams.set(key, String(value)); } } const finalHeaders = new Headers(headers); const hasBody = body !== undefined; if (hasBody && !finalHeaders.has("Content-Type")) { finalHeaders.set("Content-Type", "application/json"); } if (auth) { const session = await getValidSession(); if (session) finalHeaders.set("Authorization", `Bearer ${session.accessToken}`); } logWinofyRequest(method, url.toString(), body); const res = await fetch(url.toString(), { ...rest, headers: finalHeaders, body: hasBody ? JSON.stringify(body) : undefined, cache: "no-store", }); if (res.status === 204) { logWinofyResponse(method, url.toString(), res.status, null); return undefined as T; } const data = await res.json().catch(() => null); logWinofyResponse(method, url.toString(), res.status, data); if (!res.ok) { const envelope = data as { status_message?: string; details?: WinofyErrorDetails } | null; throw new ApiError(res.status, envelope?.status_message ?? res.statusText, envelope?.details ?? null); } return data as T; } export { ApiError };