"use server"; import { z } from "zod"; import { actionOk, adminAction } from "@/lib/foundation/action"; import { NotFoundError } from "@/lib/foundation/errors"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { logAudit } from "@/lib/services/audit"; import { rcon } from "@/lib/services/rcon"; // ── CFH Ticket Actions ────────────────────────────────────────────── const cfhIdSchema = z.object({ ticketId: z.coerce.number().int().positive() }); const CFH_PERM = [PERMS.MODERATION_EDIT, PERMS.MOD_CFH_EDIT] as const; const MOD_ACTION_PERM = [PERMS.MODERATION_EDIT, PERMS.MOD_ACTIONS] as const; export const assignCfhTicket = adminAction( { permission: CFH_PERM, schema: cfhIdSchema }, async (ctx) => { const ticket = await prisma.supportTickets.findUnique({ where: { id: ctx.data.ticketId }, }); if (!ticket) throw new NotFoundError("SupportTicket", ctx.data.ticketId); await prisma.supportTickets.update({ where: { id: ctx.data.ticketId }, data: { modId: ctx.session.user.id, state: 1 }, }); logAudit({ userId: ctx.session.user.id, action: "cfh_assign", target: "support_tickets", targetId: ctx.data.ticketId, }); return actionOk(); }, ); const cfhStateSchema = z.object({ ticketId: z.coerce.number().int().positive(), state: z.coerce.number().int().min(0).max(3), }); export const updateCfhState = adminAction( { permission: CFH_PERM, schema: cfhStateSchema }, async (ctx) => { const ticket = await prisma.supportTickets.findUnique({ where: { id: ctx.data.ticketId }, }); if (!ticket) throw new NotFoundError("SupportTicket", ctx.data.ticketId); await prisma.supportTickets.update({ where: { id: ctx.data.ticketId }, data: { state: ctx.data.state, modId: ctx.session.user.id }, }); logAudit({ userId: ctx.session.user.id, action: "cfh_state_change", target: "support_tickets", targetId: ctx.data.ticketId, before: { state: ticket.state }, after: { state: ctx.data.state }, }); return actionOk(); }, ); export const closeCfhTicket = adminAction( { permission: CFH_PERM, schema: cfhIdSchema }, async (ctx) => { await prisma.supportTickets.update({ where: { id: ctx.data.ticketId }, data: { state: 2, modId: ctx.session.user.id }, }); logAudit({ userId: ctx.session.user.id, action: "cfh_close", target: "support_tickets", targetId: ctx.data.ticketId, }); return actionOk(); }, ); // ── Quick Mod Actions ──────────────────────────────────────────────── const userIdSchema = z.object({ userId: z.coerce.number().int().positive() }); export const quickKick = adminAction( { permission: MOD_ACTION_PERM, schema: userIdSchema }, async (ctx) => { await rcon.disconnectUser(ctx.data.userId); logAudit({ userId: ctx.session.user.id, action: "mod_kick", target: "User", targetId: ctx.data.userId, }); return actionOk(); }, ); const muteSchema = z.object({ userId: z.coerce.number().int().positive(), duration: z.coerce.number().int().min(0).max(525600), }); export const quickMute = adminAction( { permission: MOD_ACTION_PERM, schema: muteSchema }, async (ctx) => { await rcon.muteUser(ctx.data.userId, ctx.data.duration); logAudit({ userId: ctx.session.user.id, action: "mod_mute", target: "User", targetId: ctx.data.userId, after: { duration: ctx.data.duration }, }); return actionOk(); }, ); export const quickUnmute = adminAction( { permission: MOD_ACTION_PERM, schema: userIdSchema }, async (ctx) => { await rcon.unmuteUser(ctx.data.userId); logAudit({ userId: ctx.session.user.id, action: "mod_unmute", target: "User", targetId: ctx.data.userId, }); return actionOk(); }, ); const alertSchema = z.object({ userId: z.coerce.number().int().positive(), message: z.string().min(1).max(500), }); export const quickAlert = adminAction( { permission: MOD_ACTION_PERM, schema: alertSchema }, async (ctx) => { await rcon.alertUser(ctx.data.userId, ctx.data.message); logAudit({ userId: ctx.session.user.id, action: "mod_alert", target: "User", targetId: ctx.data.userId, after: { message: ctx.data.message }, }); return actionOk(); }, ); const roomIdSchema = z.object({ roomId: z.coerce.number().int().positive() }); export const quickRoomKick = adminAction( { permission: MOD_ACTION_PERM, schema: roomIdSchema }, async (ctx) => { await rcon.kickAll(ctx.data.roomId); logAudit({ userId: ctx.session.user.id, action: "mod_room_kick", target: "Room", targetId: ctx.data.roomId, }); return actionOk(); }, ); const broadcastSchema = z.object({ message: z.string().min(1).max(500), type: z.enum(["hotel", "staff"]), }); export const broadcastAlert = adminAction( { permission: MOD_ACTION_PERM, schema: broadcastSchema }, async (ctx) => { if (ctx.data.type === "hotel") { await rcon.hotelAlert(ctx.data.message); } else { await rcon.staffAlert(ctx.data.message); } logAudit({ userId: ctx.session.user.id, action: `mod_broadcast_${ctx.data.type}`, target: "broadcast", after: { message: ctx.data.message }, }); return actionOk(); }, );