TorbatYar/frontend/lib/api-client.ts

63 lines
1.9 KiB
TypeScript

/**
* API Client — تنها راه ارتباط frontend با backend.
* توکن SSO از sessionStorage به هدر Authorization اضافه می‌شود.
*/
import { getValidAccessToken } from "./auth";
const SERVER_API_BASE =
process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000";
const API_BASE = typeof window === "undefined" ? SERVER_API_BASE : "/api/core";
export class ApiError extends Error {
constructor(
public status: number,
public code: string,
message: string
) {
super(message);
this.name = "ApiError";
}
}
interface RequestOptions extends RequestInit {
tenantId?: string;
tenantSlug?: string;
}
async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
const { tenantId, tenantSlug, headers: customHeaders, ...init } = options;
const headers = new Headers(customHeaders);
headers.set("Content-Type", "application/json");
const token = await getValidAccessToken();
if (token) headers.set("Authorization", `Bearer ${token}`);
if (tenantId) headers.set("X-Tenant-ID", tenantId);
if (tenantSlug) headers.set("X-Tenant-Slug", tenantSlug);
const res = await fetch(`${API_BASE}${path}`, { ...init, headers });
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new ApiError(
res.status,
body?.error?.code || "unknown_error",
body?.error?.message || res.statusText
);
}
return res.json() as Promise<T>;
}
export const api = {
health: () => request<{ status: string; service: string }>("/health"),
tenants: {
list: (page = 1, pageSize = 20) =>
request(`/api/v1/tenants?page=${page}&page_size=${pageSize}`),
get: (id: string) => request(`/api/v1/tenants/${id}`),
create: (data: { name: string; slug: string }) =>
request("/api/v1/tenants", { method: "POST", body: JSON.stringify(data) }),
},
};