74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
/**
|
|
* Server-side typed fetch for legacy page loaders that still render
|
|
* office pages. Mirrors the client-side `fetchJson` helper but
|
|
* resolves the backend base URL from env on the server (no
|
|
* `window`-based fallback) and defaults to no-store so each navigation
|
|
* produces fresh data, matching the live-data nature of the office surface.
|
|
*
|
|
* Use this in `page.tsx` route loaders only. Client Components stick
|
|
* with `apps/web/lib/api/domains/*` helpers.
|
|
*/
|
|
|
|
import { getBackendConfig } from "@/lib/config";
|
|
|
|
export type ServerFetchOptions = {
|
|
/** HTTP method. Defaults to GET. */
|
|
method?: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
/** JSON body — serialized automatically. */
|
|
body?: unknown;
|
|
/** Extra request headers (Content-Type is set automatically). */
|
|
headers?: Record<string, string>;
|
|
/**
|
|
* Pass `true` to opt into the platform fetch cache for this request.
|
|
* Default is `false` because office data is live; pages should re-fetch
|
|
* on every route render.
|
|
*/
|
|
cache?: boolean;
|
|
};
|
|
|
|
export class ServerFetchError extends Error {
|
|
constructor(
|
|
public readonly status: number,
|
|
public readonly body: string,
|
|
public readonly path: string,
|
|
) {
|
|
super(`server fetch ${path} failed (${status}): ${body}`);
|
|
this.name = "ServerFetchError";
|
|
}
|
|
}
|
|
|
|
const OFFICE_BASE = "/api/v1/office";
|
|
|
|
/**
|
|
* Awaits a JSON response from the backend's office API. Path is
|
|
* relative to `/api/v1/office` — pass e.g. `"/agents/123/summary"`.
|
|
* Returns the parsed JSON body, typed by the caller's generic.
|
|
*
|
|
* Throws `ServerFetchError` on non-2xx responses; callers should let it
|
|
* bubble to the route error surface rather than swallowing it.
|
|
*/
|
|
export async function serverFetchOfficeJson<T>(
|
|
path: string,
|
|
options: ServerFetchOptions = {},
|
|
): Promise<T> {
|
|
const base = getBackendConfig().apiBaseUrl.replace(/\/$/, "");
|
|
const url = `${base}${OFFICE_BASE}${path.startsWith("/") ? path : `/${path}`}`;
|
|
const init: RequestInit = {
|
|
method: options.method ?? "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...options.headers,
|
|
},
|
|
cache: options.cache ? "force-cache" : "no-store",
|
|
};
|
|
if (options.body !== undefined) {
|
|
init.body = JSON.stringify(options.body);
|
|
}
|
|
const res = await fetch(url, init);
|
|
if (!res.ok) {
|
|
const body = await res.text().catch(() => "");
|
|
throw new ServerFetchError(res.status, body, path);
|
|
}
|
|
return (await res.json()) as T;
|
|
}
|