Files
EpicNext-Cms/src/actions/admin-help-tickets.ts
T
SimoandCursor 3ad3f9512c
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 1m17s
CI / check (push) Successful in 25s
CI / deploy (push) Failing after 11s
feat(admin): ban appeals, photos polish, economy adjust, staff smoke
Co-authored-by: Cursor <[email protected]>
2026-07-30 20:23:03 +02:00

188 lines
5.0 KiB
TypeScript

"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();
},
);