import { NextRequest, NextResponse } from "next/server"; import { db, recipes, recipeNotes, eq, and, or, inArray } from "@epicure/db"; import { z } from "zod"; import { requireSessionOrApiKey } from "@/lib/api-auth"; type Params = { params: Promise<{ id: string }> }; const PutSchema = z.object({ content: z.string().max(5000), }); async function assertRecipeAccessible(recipeId: string, userId: string) { const recipe = await db.query.recipes.findFirst({ where: and( eq(recipes.id, recipeId), or(eq(recipes.authorId, userId), inArray(recipes.visibility, ["public", "unlisted"])) ), columns: { id: true }, }); return recipe; } export async function GET(req: NextRequest, { params }: Params) { const { session, response } = await requireSessionOrApiKey(req, { rateLimit: { limit: 60, windowSeconds: 60 } }); if (response) return response; const { id } = await params; const recipe = await assertRecipeAccessible(id, session!.user.id); if (!recipe) return NextResponse.json({ error: "Not found" }, { status: 404 }); const note = await db.query.recipeNotes.findFirst({ where: and(eq(recipeNotes.recipeId, id), eq(recipeNotes.userId, session!.user.id)), columns: { content: true, updatedAt: true }, }); return NextResponse.json({ note: note ?? null }); } export async function PUT(req: NextRequest, { params }: Params) { const { session, response } = await requireSessionOrApiKey(req, { rateLimit: { limit: 60, windowSeconds: 60 } }); if (response) return response; const { id } = await params; const recipe = await assertRecipeAccessible(id, session!.user.id); if (!recipe) return NextResponse.json({ error: "Not found" }, { status: 404 }); const parsed = PutSchema.safeParse(await req.json()); if (!parsed.success) { return NextResponse.json({ error: "Validation error", issues: parsed.error.issues }, { status: 400 }); } const content = parsed.data.content.trim(); const userId = session!.user.id; if (content.length === 0) { await db .delete(recipeNotes) .where(and(eq(recipeNotes.recipeId, id), eq(recipeNotes.userId, userId))); return NextResponse.json({ note: null }); } await db .insert(recipeNotes) .values({ id: crypto.randomUUID(), recipeId: id, userId, content, updatedAt: new Date() }) .onConflictDoUpdate({ target: [recipeNotes.recipeId, recipeNotes.userId], set: { content, updatedAt: new Date() }, }); const note = await db.query.recipeNotes.findFirst({ where: and(eq(recipeNotes.recipeId, id), eq(recipeNotes.userId, userId)), columns: { content: true, updatedAt: true }, }); return NextResponse.json({ note }); }