TorbatYar/frontend/modules/commercial/adapters/http.ts
2026-07-28 20:39:10 +03:30

177 lines
5.4 KiB
TypeScript

/**
* Commercial registry HTTP — Core Commercial APIs only.
* Supports: ready | empty | unavailable | permission_denied | error
* Never invents catalog rows.
*/
import { getValidAccessToken } from "@/lib/auth";
import type { AdapterResult, RegistryAvailability } from "../types";
const API_BASE =
process.env.NEXT_PUBLIC_API_BASE_URL ||
process.env.NEXT_PUBLIC_BACKEND_URL ||
"http://localhost:8000";
async function authHeaders(): Promise<Headers> {
const headers = new Headers({ "Content-Type": "application/json" });
const token = await getValidAccessToken();
if (token) headers.set("Authorization", `Bearer ${token}`);
return headers;
}
function availabilityFromStatus(status: number): RegistryAvailability {
if (status === 401 || status === 403) return "permission_denied";
if (status === 404 || status === 501 || status === 502 || status === 503) {
return "unavailable";
}
return "error";
}
export async function commercialGet<T>(
path: string
): Promise<{ ok: true; data: T } | { ok: false; availability: RegistryAvailability; message: string; status: number }> {
try {
const res = await fetch(`${API_BASE}${path}`, { headers: await authHeaders() });
if (!res.ok) {
const body = await res.json().catch(() => ({}));
return {
ok: false,
status: res.status,
availability: availabilityFromStatus(res.status),
message:
(body as { error?: { message?: string } })?.error?.message ||
res.statusText ||
"درخواست ناموفق",
};
}
return { ok: true, data: (await res.json()) as T };
} catch (err) {
return {
ok: false,
status: 0,
availability: "unavailable",
message: err instanceof Error ? err.message : "خطای شبکه",
};
}
}
export async function commercialPost<T>(
path: string,
body: unknown
): Promise<{ ok: true; data: T } | { ok: false; availability: RegistryAvailability; message: string; status: number }> {
return commercialMutate<T>("POST", path, body);
}
export async function commercialPut<T>(
path: string,
body: unknown
): Promise<{ ok: true; data: T } | { ok: false; availability: RegistryAvailability; message: string; status: number }> {
return commercialMutate<T>("PUT", path, body);
}
export async function commercialPatch<T>(
path: string,
body: unknown
): Promise<{ ok: true; data: T } | { ok: false; availability: RegistryAvailability; message: string; status: number }> {
return commercialMutate<T>("PATCH", path, body);
}
export async function commercialDelete(
path: string
): Promise<{ ok: true; data: null } | { ok: false; availability: RegistryAvailability; message: string; status: number }> {
try {
const res = await fetch(`${API_BASE}${path}`, {
method: "DELETE",
headers: await authHeaders(),
});
if (!res.ok) {
const payload = await res.json().catch(() => ({}));
return {
ok: false,
status: res.status,
availability: availabilityFromStatus(res.status),
message:
(payload as { error?: { message?: string } })?.error?.message ||
res.statusText ||
"حذف ناموفق",
};
}
return { ok: true, data: null };
} catch (err) {
return {
ok: false,
status: 0,
availability: "unavailable",
message: err instanceof Error ? err.message : "خطای شبکه",
};
}
}
async function commercialMutate<T>(
method: "POST" | "PUT" | "PATCH",
path: string,
body: unknown
): Promise<{ ok: true; data: T } | { ok: false; availability: RegistryAvailability; message: string; status: number }> {
try {
const res = await fetch(`${API_BASE}${path}`, {
method,
headers: await authHeaders(),
body: JSON.stringify(body),
});
if (!res.ok) {
const payload = await res.json().catch(() => ({}));
return {
ok: false,
status: res.status,
availability: availabilityFromStatus(res.status),
message:
(payload as { error?: { message?: string } })?.error?.message ||
res.statusText ||
"درخواست ناموفق",
};
}
if (res.status === 204) return { ok: true, data: {} as T };
return { ok: true, data: (await res.json()) as T };
} catch (err) {
return {
ok: false,
status: 0,
availability: "unavailable",
message: err instanceof Error ? err.message : "خطای شبکه",
};
}
}
/** Extract items array from common commercial list envelopes. */
export function extractItems<T>(payload: unknown, keys: string[]): T[] {
if (!payload || typeof payload !== "object") return [];
const obj = payload as Record<string, unknown>;
for (const key of keys) {
const v = obj[key];
if (Array.isArray(v)) return v as T[];
}
if (Array.isArray(payload)) return payload as T[];
return [];
}
export function listResult<T>(
items: T[],
source: string,
emptyMessage?: string
): AdapterResult<T[]> {
return {
availability: items.length ? "ready" : "empty",
data: items,
source,
message: items.length ? undefined : emptyMessage,
};
}
export function failResult<T>(
availability: RegistryAvailability,
message: string,
empty: T
): AdapterResult<T> {
return { availability, data: empty, message, source: "none" };
}