"use server"; import { revalidatePath } from "next/cache"; import { requireStaffRateLimited as requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; import { rcon } from "@/lib/services/rcon"; import { logStaffActivity } from "@/lib/services/staff-activity"; import type { $Enums } from "@/generated/prisma/client"; type BanType = $Enums.bans_type; const BAN_TYPES: ReadonlySet = new Set(["account", "ip", "machine", "super"]); const PERMANENT_SECONDS = 100 * 365 * 24 * 3600; export async function createBan(formData: FormData): Promise { const staff = await requireStaff(); const userId = Number(formData.get("userId")); const reason = String(formData.get("reason") ?? "").normalize("NFC") .trim() .slice(0, 200) || "Banned"; const hours = Number(formData.get("hours")); const type = String(formData.get("type")); if (!(userId > 0) || !BAN_TYPES.has(type)) return; const now = Math.floor(Date.now() / 1000); const banExpire = hours > 0 ? now + Math.floor(hours) * 3600 : now + PERMANENT_SECONDS; const user = await prisma.user.findUnique({ where: { id: userId }, select: { username: true }, }); await prisma.ban.create({ data: { userId, ip: "", machineId: "", userStaffId: staff.id, timestamp: now, banExpire, banReason: reason, type: type as BanType, cfhTopic: -1, }, }); if (user) await rcon.disconnectUser(userId, user.username); await logStaffActivity({ staffId: staff.id, action: "user_ban", description: `Banned user #${userId} (${type}, ${hours > 0 ? `${hours}h` : "permanent"}): ${reason}`, targetType: "user", targetId: userId, }); revalidatePath("/admin/bans"); } export async function liftBan(formData: FormData): Promise { const staff = await requireStaff(); const id = Number(formData.get("id")); if (id > 0) { await prisma.ban.delete({ where: { id } }); await logStaffActivity({ staffId: staff.id, action: "ban_lift", description: `Lifted ban #${id}`, }); } revalidatePath("/admin/bans"); }