63 lines
1.9 KiB
TypeScript
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) }),
|
|
},
|
|
};
|