Yaltopia-Hotels/src/lib/api.ts
2026-04-17 11:37:28 +03:00

117 lines
3.7 KiB
TypeScript

import { parseApiError } from '@/lib/auth-api';
function getBaseUrl(): string {
return import.meta.env.VITE_API_BASE_URL ?? '';
}
let getToken: () => string | null = () => null;
let getPropertyId: () => string | null = () => null;
/** Register token + property scope for hotel API paths (call once from the auth store module). */
export function registerHotelApiContext(ctx: {
getToken: () => string | null;
getPropertyId: () => string | null;
}) {
getToken = ctx.getToken;
getPropertyId = ctx.getPropertyId;
}
/** Paths that stay unscoped (mock-only or non-hotel). */
function shouldRewriteForHotel(path: string): boolean {
const first = path.split('?')[0];
if (first.startsWith('/auth')) return false;
if (first.startsWith('/properties')) return false;
if (first.startsWith('/admin')) return false;
return true;
}
function rewriteHotelPath(path: string): string {
const pid = getPropertyId();
if (!pid || !shouldRewriteForHotel(path)) return path;
const [pathname, query] = path.split('?');
const q = query ? `?${query}` : '';
if (pathname === '/dashboard') {
return `/properties/${pid}/hotel/dashboard/summary${q}`;
}
return `/properties/${pid}/hotel${pathname}${q}`;
}
async function request(
method: string,
path: string,
body?: unknown,
init?: RequestInit,
): Promise<Response> {
const token = getToken();
const resolved = rewriteHotelPath(path);
const headers: Record<string, string> = {
...(init?.headers as Record<string, string>),
};
const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
if (body !== undefined && !isFormData) headers["Content-Type"] = "application/json";
if (token) headers.Authorization = `Bearer ${token}`;
const url = `${getBaseUrl()}${resolved}`;
return fetch(url, {
...init,
method,
headers,
body:
body === undefined
? init?.body
: isFormData
? (body as FormData)
: JSON.stringify(body),
});
}
export async function apiGet<T>(path: string, init?: RequestInit): Promise<T> {
const res = await request('GET', path, undefined, init);
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<T>;
}
export async function apiPost<T>(path: string, body: unknown, init?: RequestInit): Promise<T> {
const res = await request('POST', path, body, init);
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<T>;
}
export async function apiPatch<T>(path: string, body: unknown, init?: RequestInit): Promise<T> {
const res = await request('PATCH', path, body, init);
if (!res.ok) throw new Error(await parseApiError(res));
return res.json() as Promise<T>;
}
export async function apiDelete(path: string, init?: RequestInit): Promise<void> {
const res = await request('DELETE', path, undefined, init);
if (!res.ok) throw new Error(await parseApiError(res));
}
/** Authenticated binary download (e.g. CSV export). */
export async function apiDownloadBlob(
path: string,
init?: RequestInit,
): Promise<{ blob: Blob; filename: string | undefined }> {
const token = getToken();
const resolved = rewriteHotelPath(path);
const headers: Record<string, string> = { ...(init?.headers as Record<string, string>) };
if (token) headers.Authorization = `Bearer ${token}`;
const url = `${getBaseUrl()}${resolved}`;
const res = await fetch(url, { ...init, method: 'GET', headers });
if (!res.ok) throw new Error(await parseApiError(res));
const cd = res.headers.get('Content-Disposition');
let filename: string | undefined;
const m = cd?.match(/filename="?([^";]+)"?/);
if (m) filename = m[1];
const blob = await res.blob();
return { blob, filename };
}