178 lines
5.5 KiB
TypeScript
178 lines
5.5 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 SERVER_API_BASE =
|
|
process.env.NEXT_PUBLIC_API_BASE_URL ||
|
|
process.env.NEXT_PUBLIC_BACKEND_URL ||
|
|
"http://localhost:8000";
|
|
const API_BASE = typeof window === "undefined" ? SERVER_API_BASE : "/api/core";
|
|
|
|
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" };
|
|
}
|