memorium/lib/crud.ts

116 lines
3.0 KiB
TypeScript
Raw Normal View History

2023-08-01 17:50:00 +02:00
import {
createDocument,
getDocument,
getDocuments,
transformDocument,
} from "@lib/documents.ts";
import { Root } from "https://esm.sh/remark-frontmatter@4.0.1";
import { getThumbhash } from "@lib/cache/image.ts";
2023-08-29 13:48:52 +02:00
import { GenericResource } from "@lib/types.ts";
import { parseRating } from "@lib/helpers.ts";
2023-08-29 13:48:52 +02:00
export async function addThumbnailToResource<T = GenericResource>(
res: T,
): Promise<T> {
const imageUrl = res?.meta?.image;
if (!imageUrl) return res;
2023-08-12 18:32:56 +02:00
const [thumbhash, average] = await getThumbhash({ url: imageUrl });
if (!thumbhash) return res;
return {
...res,
meta: {
...res?.meta,
2023-08-12 18:32:56 +02:00
average: average,
2023-08-11 17:33:06 +02:00
thumbnail: thumbhash,
},
};
}
2023-08-01 17:50:00 +02:00
2023-08-29 13:48:52 +02:00
type SortType = "rating" | "date" | "name" | "author";
function sortFunction<T extends GenericResource>(sortType: SortType) {
return (a: T, b: T) => {
switch (sortType) {
case "rating":
return parseRating(a.meta?.rating || 0) >
parseRating(b.meta?.rating || 0)
? -1
: 1;
case "date":
return (a.meta?.date || 0) > (b.meta?.date || 0) ? -1 : 1;
case "name":
return a.name.localeCompare(b.name);
case "author":
return a.meta?.author?.localeCompare(b.meta?.author || "");
}
};
}
export function createCrud<T extends GenericResource>(
{ prefix, parse, render, hasThumbnails = false }: {
2023-08-01 17:50:00 +02:00
prefix: string;
hasThumbnails?: boolean;
render?: (doc: T) => string;
2023-08-01 17:50:00 +02:00
parse: (doc: string, id: string) => T;
},
) {
function pathFromId(id: string) {
2023-08-04 22:35:25 +02:00
return `${prefix}${id.replaceAll(":", "")}.md`;
2023-08-01 17:50:00 +02:00
}
async function read(id: string) {
const path = pathFromId(id);
const content = await getDocument(path);
2023-08-19 23:29:39 +02:00
const res = parse(content, id);
if (hasThumbnails) {
return addThumbnailToResource(res);
}
return res;
2023-08-01 17:50:00 +02:00
}
function create(id: string, content: string | ArrayBuffer | T) {
2023-08-01 17:50:00 +02:00
const path = pathFromId(id);
if (
typeof content === "string" || content instanceof ArrayBuffer
) {
return createDocument(path, content);
}
if (render) {
return createDocument(path, render(content));
}
throw new Error("No renderer defined for " + prefix + " CRUD");
2023-08-01 17:50:00 +02:00
}
async function update(id: string, updater: (r: Root) => Root) {
const path = pathFromId(id);
const content = await getDocument(path);
const newDoc = transformDocument(content, updater);
await createDocument(path, newDoc);
}
2023-08-29 13:48:52 +02:00
async function readAll({ sort = "rating" }: { sort?: SortType } = {}) {
2023-08-01 17:50:00 +02:00
const allDocuments = await getDocuments();
2023-08-29 13:48:52 +02:00
return (await Promise.all(
2023-08-01 17:50:00 +02:00
allDocuments.filter((d) => {
return d.name.startsWith(prefix) &&
d.contentType === "text/markdown" &&
!d.name.endsWith("index.md");
}).map((doc) => {
const id = doc.name.replace(prefix, "").replace(/\.md$/, "");
return read(id);
}),
2023-08-29 13:48:52 +02:00
)).sort(sortFunction<T>(sort));
2023-08-01 17:50:00 +02:00
}
return {
read,
readAll,
create,
update,
};
}