223 lines
6.5 KiB
TypeScript
223 lines
6.5 KiB
TypeScript
/**
|
|
* RustyCMS API client. Base URL from NEXT_PUBLIC_RUSTYCMS_API_URL.
|
|
* Optional RUSTYCMS_API_KEY for write operations (sent as X-API-Key).
|
|
*/
|
|
|
|
const getBaseUrl = () =>
|
|
process.env.NEXT_PUBLIC_RUSTYCMS_API_URL || "http://127.0.0.1:3000";
|
|
|
|
const getHeaders = (): HeadersInit => {
|
|
const headers: HeadersInit = {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
};
|
|
const key =
|
|
typeof window !== "undefined"
|
|
? process.env.NEXT_PUBLIC_RUSTYCMS_API_KEY ?? null
|
|
: process.env.RUSTYCMS_API_KEY ?? process.env.NEXT_PUBLIC_RUSTYCMS_API_KEY ?? null;
|
|
if (key) headers["X-API-Key"] = key;
|
|
return headers;
|
|
};
|
|
|
|
export type CollectionMeta = {
|
|
name: string;
|
|
description?: string;
|
|
tags?: string[];
|
|
category?: string;
|
|
field_count?: number;
|
|
extends?: string | string[];
|
|
strict?: boolean;
|
|
};
|
|
|
|
export type CollectionsResponse = {
|
|
collections: CollectionMeta[];
|
|
};
|
|
|
|
export type FieldDefinition = {
|
|
type: string;
|
|
required?: boolean;
|
|
description?: string;
|
|
collection?: string;
|
|
collections?: string[];
|
|
enum?: unknown[];
|
|
default?: unknown;
|
|
items?: FieldDefinition;
|
|
[key: string]: unknown;
|
|
};
|
|
|
|
export type SchemaDefinition = {
|
|
name: string;
|
|
description?: string;
|
|
tags?: string[];
|
|
category?: string;
|
|
fields: Record<string, FieldDefinition>;
|
|
extends?: string | string[];
|
|
reusable?: boolean;
|
|
[key: string]: unknown;
|
|
};
|
|
|
|
export type ListResponse<T = Record<string, unknown>> = {
|
|
items: T[];
|
|
total: number;
|
|
page: number;
|
|
per_page: number;
|
|
total_pages: number;
|
|
};
|
|
|
|
export async function fetchCollections(): Promise<CollectionsResponse> {
|
|
const res = await fetch(`${getBaseUrl()}/api/collections`, {
|
|
headers: getHeaders(),
|
|
});
|
|
if (!res.ok) throw new Error(`Collections: ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
export async function fetchSchema(
|
|
collection: string
|
|
): Promise<SchemaDefinition> {
|
|
const res = await fetch(
|
|
`${getBaseUrl()}/api/collections/${encodeURIComponent(collection)}`,
|
|
{ headers: getHeaders() }
|
|
);
|
|
if (!res.ok) throw new Error(`Schema ${collection}: ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
export async function createSchema(
|
|
schema: SchemaDefinition
|
|
): Promise<SchemaDefinition> {
|
|
const res = await fetch(`${getBaseUrl()}/api/schemas`, {
|
|
method: "POST",
|
|
headers: getHeaders(),
|
|
body: JSON.stringify(schema),
|
|
});
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({}));
|
|
const msg = (err as { error?: string }).error ?? (err as { errors?: string[] }).errors?.join(", ") ?? `Schema: ${res.status}`;
|
|
throw new Error(msg);
|
|
}
|
|
return res.json();
|
|
}
|
|
|
|
export type SlugCheckResponse = {
|
|
valid: boolean;
|
|
normalized: string;
|
|
available: boolean;
|
|
error?: string;
|
|
};
|
|
|
|
export async function checkSlug(
|
|
collection: string,
|
|
slug: string,
|
|
params: { exclude?: string; _locale?: string } = {}
|
|
): Promise<SlugCheckResponse> {
|
|
const sp = new URLSearchParams({ slug: slug.trim() });
|
|
if (params.exclude) sp.set("exclude", params.exclude);
|
|
if (params._locale) sp.set("_locale", params._locale);
|
|
const res = await fetch(
|
|
`${getBaseUrl()}/api/collections/${encodeURIComponent(collection)}/slug-check?${sp}`,
|
|
{ headers: getHeaders() }
|
|
);
|
|
if (!res.ok) throw new Error(`Slug-Check: ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
export type ContentListParams = {
|
|
_page?: number;
|
|
_per_page?: number;
|
|
_sort?: string;
|
|
_order?: "asc" | "desc";
|
|
_resolve?: string;
|
|
_locale?: string;
|
|
[key: string]: string | number | undefined;
|
|
};
|
|
|
|
export async function fetchContentList<T = Record<string, unknown>>(
|
|
collection: string,
|
|
params: ContentListParams = {}
|
|
): Promise<ListResponse<T>> {
|
|
const sp = new URLSearchParams();
|
|
Object.entries(params).forEach(([k, v]) => {
|
|
if (v !== undefined && v !== "") sp.set(k, String(v));
|
|
});
|
|
const qs = sp.toString();
|
|
const url = `${getBaseUrl()}/api/content/${encodeURIComponent(collection)}${qs ? `?${qs}` : ""}`;
|
|
const res = await fetch(url, { headers: getHeaders() });
|
|
if (!res.ok) throw new Error(`List ${collection}: ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
export async function fetchEntry<T = Record<string, unknown>>(
|
|
collection: string,
|
|
slug: string,
|
|
params: { _resolve?: string; _locale?: string } = {}
|
|
): Promise<T> {
|
|
const sp = new URLSearchParams();
|
|
if (params._resolve) sp.set("_resolve", params._resolve);
|
|
if (params._locale) sp.set("_locale", params._locale);
|
|
const qs = sp.toString();
|
|
const url = `${getBaseUrl()}/api/content/${encodeURIComponent(collection)}/${encodeURIComponent(slug)}${qs ? `?${qs}` : ""}`;
|
|
const res = await fetch(url, { headers: getHeaders() });
|
|
if (!res.ok) throw new Error(`Entry ${collection}/${slug}: ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
export async function createEntry(
|
|
collection: string,
|
|
data: Record<string, unknown>,
|
|
params: { _locale?: string } = {}
|
|
): Promise<Record<string, unknown>> {
|
|
const sp = new URLSearchParams();
|
|
if (params._locale) sp.set("_locale", params._locale);
|
|
const qs = sp.toString();
|
|
const url = `${getBaseUrl()}/api/content/${encodeURIComponent(collection)}${qs ? `?${qs}` : ""}`;
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: getHeaders(),
|
|
body: JSON.stringify(data),
|
|
});
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({}));
|
|
throw new Error((err as { message?: string }).message || `Create: ${res.status}`);
|
|
}
|
|
return res.json();
|
|
}
|
|
|
|
export async function updateEntry(
|
|
collection: string,
|
|
slug: string,
|
|
data: Record<string, unknown>,
|
|
params: { _locale?: string } = {}
|
|
): Promise<Record<string, unknown>> {
|
|
const sp = new URLSearchParams();
|
|
if (params._locale) sp.set("_locale", params._locale);
|
|
const qs = sp.toString();
|
|
const url = `${getBaseUrl()}/api/content/${encodeURIComponent(collection)}/${encodeURIComponent(slug)}${qs ? `?${qs}` : ""}`;
|
|
const res = await fetch(url, {
|
|
method: "PUT",
|
|
headers: getHeaders(),
|
|
body: JSON.stringify(data),
|
|
});
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({}));
|
|
throw new Error((err as { message?: string }).message || `Update: ${res.status}`);
|
|
}
|
|
return res.json();
|
|
}
|
|
|
|
export async function deleteEntry(
|
|
collection: string,
|
|
slug: string,
|
|
params: { _locale?: string } = {}
|
|
): Promise<void> {
|
|
const sp = new URLSearchParams();
|
|
if (params._locale) sp.set("_locale", params._locale);
|
|
const qs = sp.toString();
|
|
const url = `${getBaseUrl()}/api/content/${encodeURIComponent(collection)}/${encodeURIComponent(slug)}${qs ? `?${qs}` : ""}`;
|
|
const res = await fetch(url, {
|
|
method: "DELETE",
|
|
headers: getHeaders(),
|
|
});
|
|
if (!res.ok) throw new Error(`Delete: ${res.status}`);
|
|
}
|