export const prerender = false; import type { APIRoute } from "astro"; import { eq } from "drizzle-orm"; import { createDb } from "@/db"; import { comments, events } from "@/db/schema"; import { authenticate } from "@/lib/auth"; import { createCommentNotifications } from "@/lib/notifications"; import { isUuid, sanitizeBody } from "@/lib/sanitize"; export const POST: APIRoute = async ({ request, locals }) => { const runtime = locals.runtime as { env: { DATABASE_URL: string } }; const db = createDb(runtime.env.DATABASE_URL); const profile = await authenticate(request, db); if (!profile) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } const ct = request.headers.get("Content-Type")?.split(";")[0].trim(); if (ct !== "application/json") { return Response.json({ error: "Content-Type must be application/json" }, { status: 415 }); } let body: { eventId?: string; parentId?: string; body?: string }; try { body = await request.json(); } catch { return Response.json({ error: "Invalid JSON body" }, { status: 400 }); } const { eventId, parentId, body: commentBody } = body; if (!eventId || typeof commentBody !== "string" || !commentBody.trim()) { return Response.json({ error: "Missing required fields" }, { status: 400 }); } if (!isUuid(eventId)) { return Response.json({ error: "Invalid eventId format" }, { status: 400 }); } const sanitized = sanitizeBody(commentBody.trim()); if (sanitized.length > 500) { return Response.json({ error: "Comment too long (max 500 characters)" }, { status: 400 }); } const [event] = await db.select({ id: events.id, active: events.active }).from(events).where(eq(events.id, eventId)); if (!event || !event.active) { return Response.json({ error: "Event not found or inactive" }, { status: 404 }); } let depth = 0; if (parentId) { if (!isUuid(parentId)) { return Response.json({ error: "Invalid parentId format" }, { status: 400 }); } const [parent] = await db .select({ depth: comments.depth, eventId: comments.eventId }) .from(comments) .where(eq(comments.id, parentId)); if (!parent || parent.eventId !== eventId) { return Response.json({ error: "Parent comment not found in this event" }, { status: 404 }); } depth = Math.min(parent.depth + 1, 2); } const newComment = await db.transaction(async (tx) => { const [created] = await tx .insert(comments) .values({ eventId, profileId: profile.id, parentId: parentId || null, body: sanitized, depth, }) .returning(); await createCommentNotifications(tx, { commentId: created.id, eventId, actorId: profile.id, parentId: parentId || null, body: sanitized, }); return created; }); return Response.json({ id: newComment.id, success: true }); };