mirror of
https://github.com/temetro/temetro.git
synced 2026-07-26 11:58:14 +00:00
403e0e38e9
Resolve the backend URL from the current host at runtime (lib/backend-url.ts) so one build works on localhost and any clinic LAN IP without a rebuild; used by the API client, auth client, and socket. Wire the AI-chat mic to the Web Speech API with graceful fallback. Add a Settings "About & updates" panel (current/latest version, update command, shareable network address) and an optional dismissible update banner. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
58 lines
1.4 KiB
TypeScript
58 lines
1.4 KiB
TypeScript
// Thin fetch wrapper for the temetro backend's non-auth API (patients, …).
|
|
// Auth itself goes through the Better Auth client (lib/auth-client.ts).
|
|
|
|
import { resolveBackendUrl } from "@/lib/backend-url";
|
|
|
|
// Derived from the current host in the browser (see lib/backend-url.ts), so the
|
|
// app works over localhost and the clinic LAN without a per-deployment rebuild.
|
|
export const API_BASE_URL = resolveBackendUrl();
|
|
|
|
export class ApiError extends Error {
|
|
constructor(
|
|
public status: number,
|
|
message: string,
|
|
) {
|
|
super(message);
|
|
this.name = "ApiError";
|
|
}
|
|
}
|
|
|
|
export async function apiFetch<T>(
|
|
path: string,
|
|
init?: RequestInit,
|
|
): Promise<T> {
|
|
const res = await fetch(`${API_BASE_URL}${path}`, {
|
|
...init,
|
|
credentials: "include",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...init?.headers,
|
|
},
|
|
});
|
|
|
|
// Session missing/expired — bounce to login (browser only).
|
|
if (res.status === 401) {
|
|
if (typeof window !== "undefined") {
|
|
window.location.href = "/login";
|
|
}
|
|
throw new ApiError(401, "Not authenticated.");
|
|
}
|
|
|
|
if (res.status === 204) {
|
|
return undefined as T;
|
|
}
|
|
|
|
const body = (await res.json().catch(() => null)) as
|
|
| (T & { error?: string })
|
|
| null;
|
|
|
|
if (!res.ok) {
|
|
throw new ApiError(
|
|
res.status,
|
|
body?.error ?? `Request failed with status ${res.status}.`,
|
|
);
|
|
}
|
|
|
|
return body as T;
|
|
}
|