Files
memorium/lib/marka/index.ts
2025-11-05 00:42:53 +01:00

115 lines
3.1 KiB
TypeScript

import { createCache } from "../cache.ts";
import { MARKA_API_KEY, MARKA_API_URL } from "../env.ts";
import { getImage } from "../image.ts";
import { GenericResource } from "./schema.ts";
async function addImageToResource<T extends GenericResource>(
resource: GenericResource,
): Promise<T> {
const imageUrl = resource?.content?.image;
if (imageUrl) {
try {
const absoluteImageUrl = (imageUrl.startsWith("https://") ||
imageUrl.startsWith("http://"))
? imageUrl
: `${MARKA_API_URL}/${imageUrl}`;
const image = await getImage(absoluteImageUrl);
return { ...resource, image } as T;
} catch (e) {
console.log(`Failed to fetch image: ${imageUrl}`, e);
}
}
return resource as T;
}
type Resource = GenericResource & {
content: GenericResource["content"] & Array<GenericResource>;
};
const fetchCache = createCache<Resource>("marka");
const cacheLock = new Map<string, Promise<Resource>>();
async function fetchAndStoreUrl(url: string) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch resource: ${response.status}`);
}
const res = await response.json();
fetchCache.set(url, res);
return res;
}
async function cachedFetch(
url: string,
): Promise<Resource | undefined> {
if (fetchCache.has(url)) {
fetchAndStoreUrl(url); // Fetch the url in the background
return fetchCache.get(url);
}
if (cacheLock.has(url)) return cacheLock.get(url);
const response = fetchAndStoreUrl(url);
cacheLock.set(url, response);
const res = await response;
cacheLock.delete(url);
return res;
}
export async function fetchResource<T extends GenericResource>(
resource: string,
): Promise<T | undefined> {
try {
const d = `${MARKA_API_URL}/resources/${resource}`;
const res = await cachedFetch(d);
if (!res) return;
return addImageToResource<T>(res);
} catch (_e) {
return;
}
}
export async function listResources<T extends GenericResource>(
resource: string,
): Promise<T[]> {
try {
const d = `${MARKA_API_URL}/resources/${resource}`;
const list = await cachedFetch(d);
if (!list) return [];
return Promise.all(
list?.content
.filter((a) => a?.content?._type)
.map((res) => addImageToResource(res) as Promise<T>),
);
} catch (_e) {
console.log(`Failed to fetch resource: ${resource}`, _e);
return [];
}
}
export async function createResource(
path: string,
content: string | object | ArrayBuffer,
) {
const isJson = typeof content === "object" &&
!(content instanceof ArrayBuffer);
const fetchUrl = `${MARKA_API_URL}/resources/${path}`;
const headers = new Headers();
headers.append("Content-Type", isJson ? "application/json" : "");
if (MARKA_API_KEY) {
headers.append("Authentication", MARKA_API_KEY);
}
const response = await fetch(fetchUrl, {
method: "POST",
headers,
body: isJson ? JSON.stringify(content) : content,
});
if (!response.ok) {
throw new Error(
`Failed to create resource (resources/${path}) : ${response.status}`,
);
}
return response.json();
}