36 lines
2.0 KiB
TypeScript
36 lines
2.0 KiB
TypeScript
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 { isSingleEmoji } from "@/lib/commission/reaction";
|
|
import * as z from "zod";
|
|
|
|
const inputSchema = z.object({ messageId: z.uuid(), emoji: z.string().refine(isSingleEmoji) });
|
|
|
|
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); }
|
|
}
|