metis-desk/modules/resources/src/web/catalog-api.ts

43 lines
1.6 KiB
TypeScript
Raw Normal View History

2026-10-05 11:13:51 +00:00
import { z } from "zod/v4";
import type { ResourceDefinitionSpec } from "../common";
import { CatalogResponseSchema, SaveDefinitionResponseSchema } from "../common";
export const catalogKeys = { scope: (scope: string) => ["resource-catalog", scope] };
export class CatalogApiError extends Error {
constructor(public readonly code: string) {
super(code);
}
}
async function request(path: string, body?: unknown, csrfToken?: string) {
const response = await fetch(`/api/resource-catalog/${path}`, {
method: body === undefined ? "GET" : "POST",
credentials: "same-origin",
headers:
body === undefined
? {}
: { "content-type": "application/json", "x-csrf-token": csrfToken ?? "" },
...(body === undefined ? {} : { body: JSON.stringify(body) }),
});
const json: unknown = await response.json();
if (!response.ok) {
const error = z.object({ code: z.string() }).safeParse(json);
throw new CatalogApiError(error.success ? error.data.code : "INTERNAL_ERROR");
}
return json;
}
export const catalogApi = {
async get(scope: string) {
return CatalogResponseSchema.parse(await request(scope));
},
async save(scope: string, values: ResourceDefinitionSpec, csrfToken: string, id?: string) {
return SaveDefinitionResponseSchema.parse(
await request(`${scope}${id ? `/${id}/update` : ""}`, { values }, csrfToken)
);
},
async status(scope: string, id: string, active: boolean | null, csrfToken: string) {
return CatalogResponseSchema.parse(
await request(`${scope}/${id}/status`, { active }, csrfToken)
);
},
};