import { and, eq } from "drizzle-orm"; import { getDb } from "@/db"; import { commissionMessages, commissionReactions } from "@/db/schema"; import { notifyCommission } from "@/lib/commission/server"; import { authorizeTicket } from "@/lib/commission/tickets"; import { errorResponse, HttpError, readJson, requireSameOrigin } from "@/lib/security/http"; import { limitRequest } from "@/lib/security/rate-limit"; import * as z from "zod"; const inputSchema = z.object({ messageId: z.uuid(), emoji: z.enum(["👍", "❤️", "😂", "😮", "😢", "🎉"]) }); export async function POST(request: Request, context: RouteContext<"/api/commission/tickets/[id]/reactions">) { try { requireSameOrigin(request); const { id } = await context.params; const { ticket, user } = await authorizeTicket(id); if (ticket.status !== "open") throw new HttpError(409, "ticket-closed"); await limitRequest("commission-reaction", user.id, 60); const parsed = inputSchema.safeParse(await readJson(request)); if (!parsed.success) throw new HttpError(400, "invalid-reaction"); const [message] = await getDb().select({ id: commissionMessages.id }).from(commissionMessages) .where(and(eq(commissionMessages.id, parsed.data.messageId), eq(commissionMessages.ticketId, id))).limit(1); if (!message) throw new HttpError(404, "message-not-found"); const where = and(eq(commissionReactions.messageId, message.id), eq(commissionReactions.userId, user.id), eq(commissionReactions.emoji, parsed.data.emoji)); const [existing] = await getDb().select().from(commissionReactions).where(where).limit(1); if (existing) await getDb().delete(commissionReactions).where(where); else await getDb().insert(commissionReactions).values({ messageId: message.id, userId: user.id, emoji: parsed.data.emoji, }).onConflictDoNothing(); await notifyCommission(id, ticket.userId); return Response.json({ active: !existing }); } catch (cause) { return errorResponse(cause); } }