"use server"; import { revalidatePath } from "next/cache"; import { z } from "zod"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { adminAction } from "@/lib/safe-action"; import { ActionError, actionOk } from "@/lib/safe-action-shared"; import { logAudit } from "@/lib/services/audit"; const ticketIdField = z .union([z.string(), z.number(), z.bigint()]) .transform((v) => BigInt(String(v))); const replyHelpCenterTicketSchema = z.object({ ticketId: ticketIdField, content: z.string().min(1).max(5000), }); const helpCenterTicketIdSchema = z.object({ ticketId: ticketIdField, }); function revalidateHelpCenterTicketPaths(ticketId: bigint) { const id = String(ticketId); revalidatePath("/admin/help-tickets"); revalidatePath(`/admin/help-tickets/${id}`); revalidatePath("/mod/help-tickets"); revalidatePath(`/mod/help-tickets/${id}`); revalidatePath("/help/tickets"); revalidatePath(`/help/tickets/${id}`); } export const liftBanFromHelpTicket = adminAction( { permission: PERMS.USERS_BAN, schema: helpCenterTicketIdSchema, }, async (ctx) => { const ticketId = ctx.data.ticketId; const ticket = await prisma.websiteHelpCenterTickets.findUnique({ where: { id: ticketId }, select: { id: true, userId: true, open: true, title: true }, }); if (!ticket) throw new ActionError("Ticket not found"); if (ticket.userId == null) { throw new ActionError("Ticket has no requester to unban"); } const removed = await prisma.ban.deleteMany({ where: { userId: ticket.userId }, }); const now = new Date(); if (ticket.open) { await prisma.websiteHelpCenterTickets.update({ where: { id: ticketId }, data: { open: false, updatedAt: now }, }); } logAudit({ userId: ctx.session.user.id, action: "unban_via_help_ticket", target: "User", targetId: ticket.userId, after: { ticketId: String(ticketId), removedBans: removed.count, title: ticket.title, }, }); revalidateHelpCenterTicketPaths(ticketId); revalidatePath("/admin/bans"); revalidatePath(`/admin/users/show/${ticket.userId}`); return actionOk({ removed: removed.count, userId: ticket.userId }); }, ); const HELP_TICKET_EDIT = [PERMS.TICKETS_EDIT, PERMS.MOD_TICKETS_EDIT] as const; export const replyHelpCenterTicket = adminAction( { permission: HELP_TICKET_EDIT, schema: replyHelpCenterTicketSchema }, async (ctx) => { const ticketId = ctx.data.ticketId; const ticket = await prisma.websiteHelpCenterTickets.findUnique({ where: { id: ticketId }, select: { id: true, open: true }, }); if (!ticket) throw new ActionError("Ticket not found"); const now = new Date(); const staffId = Number(ctx.session.user.id); await prisma.$transaction([ prisma.websiteHelpCenterTicketReplies.create({ data: { ticketId, userId: staffId, content: ctx.data.content.trim(), createdAt: now, updatedAt: now, }, }), prisma.websiteHelpCenterTickets.update({ where: { id: ticketId }, data: { updatedAt: now }, }), ]); logAudit({ userId: staffId, action: "help_center_ticket_reply", target: "WebsiteHelpCenterTickets", targetId: Number(ticketId), }); revalidateHelpCenterTicketPaths(ticketId); return actionOk(); }, ); export const closeHelpCenterTicket = adminAction( { permission: HELP_TICKET_EDIT, schema: helpCenterTicketIdSchema }, async (ctx) => { const ticketId = ctx.data.ticketId; const ticket = await prisma.websiteHelpCenterTickets.findUnique({ where: { id: ticketId }, select: { id: true, open: true }, }); if (!ticket) throw new ActionError("Ticket not found"); if (!ticket.open) throw new ActionError("Ticket is already closed"); const now = new Date(); await prisma.websiteHelpCenterTickets.update({ where: { id: ticketId }, data: { open: false, updatedAt: now }, }); logAudit({ userId: Number(ctx.session.user.id), action: "help_center_ticket_close", target: "WebsiteHelpCenterTickets", targetId: Number(ticketId), before: { open: true }, after: { open: false }, }); revalidateHelpCenterTicketPaths(ticketId); return actionOk(); }, ); export const reopenHelpCenterTicket = adminAction( { permission: HELP_TICKET_EDIT, schema: helpCenterTicketIdSchema }, async (ctx) => { const ticketId = ctx.data.ticketId; const ticket = await prisma.websiteHelpCenterTickets.findUnique({ where: { id: ticketId }, select: { id: true, open: true }, }); if (!ticket) throw new ActionError("Ticket not found"); if (ticket.open) throw new ActionError("Ticket is already open"); const now = new Date(); await prisma.websiteHelpCenterTickets.update({ where: { id: ticketId }, data: { open: true, updatedAt: now }, }); logAudit({ userId: Number(ctx.session.user.id), action: "help_center_ticket_reopen", target: "WebsiteHelpCenterTickets", targetId: Number(ticketId), before: { open: false }, after: { open: true }, }); revalidateHelpCenterTicketPaths(ticketId); return actionOk(); }, );