Files
HamHam/src/lib/repository.ts
T

60 lines
1.9 KiB
TypeScript

import { eq, desc, and } from 'drizzle-orm';
import { db } from '@/db';
import { recipes } from '@/db/schema';
import type { Recipe, SavedRecipe } from './recipe';
import { ApiError } from './server';
export function serialize(row: typeof recipes.$inferSelect): SavedRecipe {
return {
...row.data,
steps: row.data.steps.map((step) => ({ ...step, images: step.images ?? [] })),
id: row.id,
title: row.title,
published: row.published,
createdAt: row.createdAt.toISOString(),
updatedAt: row.updatedAt.toISOString(),
};
}
export async function listRecipes(isAdmin = false) {
return (
await db
.select()
.from(recipes)
.where(isAdmin ? undefined : eq(recipes.published, true))
.orderBy(desc(recipes.createdAt))
).map(serialize);
}
export async function getRecipe(id: string, isAdmin = false) {
if (!/^[0-9a-f-]{36}$/i.test(id)) return null;
const [row] = await db
.select()
.from(recipes)
.where(and(eq(recipes.id, id), isAdmin ? undefined : eq(recipes.published, true)));
return row ? serialize(row) : null;
}
export const titleKey = (title: string) => title.normalize('NFKC').trim().toLocaleLowerCase('de');
export async function saveRecipe(data: Recipe, id?: string, expected?: string) {
const values = {
title: data.title,
titleKey: titleKey(data.title),
published: data.published,
data,
updatedAt: new Date(),
};
if (id) {
if (!expected) throw new ApiError(428, 'Versionsstand zum Speichern fehlt.');
const [row] = await db
.update(recipes)
.set(values)
.where(and(eq(recipes.id, id), eq(recipes.updatedAt, new Date(expected))))
.returning();
if (!row)
throw new ApiError(
409,
'Das Rezept wurde zwischenzeitlich verändert. Entwurf sichern und Seite neu laden.',
);
return serialize(row);
}
const [row] = await db.insert(recipes).values(values).returning();
return serialize(row);
}