import { and, eq } from "drizzle-orm"; import * as z from "zod"; import { getDb } from "@/db"; import { commissionMessages, commissionTickets } from "@/db/schema"; import { notifyCommission } from "@/lib/commission/server"; import { authorizeTicket } from "@/lib/commission/tickets"; import { getMediaStorage } from "@/lib/media/storage"; import { errorResponse, HttpError, requireSameOrigin } from "@/lib/security/http"; import { limitRequest } from "@/lib/security/rate-limit"; export async function DELETE(request: Request, context: RouteContext<"/api/commission/tickets/[id]/messages/[messageId]">) { try { requireSameOrigin(request); const { id, messageId } = await context.params; if (!z.uuid().safeParse(messageId).success) throw new HttpError(404, "message-not-found"); const { ticket, user } = await authorizeTicket(id); if (ticket.status !== "open") throw new HttpError(409, "ticket-closed"); await limitRequest("commission-message-delete", user.id, 30); const imageObjectKeys = await getDb().transaction(async (tx) => { const [message] = await tx.delete(commissionMessages) .where(and(eq(commissionMessages.id, messageId), eq(commissionMessages.ticketId, id), eq(commissionMessages.authorId, user.id))) .returning({ imageObjectKey: commissionMessages.imageObjectKey, imageObjectKeys: commissionMessages.imageObjectKeys }); if (!message) throw new HttpError(404, "message-not-found"); await tx.update(commissionTickets).set({ updatedAt: new Date() }).where(eq(commissionTickets.id, id)); return [message.imageObjectKey, ...message.imageObjectKeys].filter((key): key is string => Boolean(key)); }); if (imageObjectKeys.length) { const storage = await getMediaStorage(); await Promise.all(imageObjectKeys.map((key) => storage.delete(key).catch(() => undefined))); } await notifyCommission(id, ticket.userId); return new Response(null, { status: 204 }); } catch (cause) { return errorResponse(cause); } }