Files
EpicNext-Cms/src/actions/admin-bans.ts
T
Simo 5861d9f1f5 Add admin Articles (CRUD) + Bans resources
- Articles: list / new / edit / delete (websiteArticles), unique-slug
  generation via slugify() (pure, unit-tested); staff-gated server actions.
- Bans: list active bans (ban_expire > now), create ban (writes the bans row +
  RCON disconnect) with type/duration/reason, lift ban; staff-gated actions.
- Admin nav extended (Articles, Bans).

Verified: tsc exit 0, vitest 48/48, next build exit 0 (7 /admin routes).
2026-06-28 12:52:38 +02:00

52 lines
1.6 KiB
TypeScript

"use server";
import { revalidatePath } from "next/cache";
import { requireStaff } from "@/lib/admin/guard";
import { prisma } from "@/lib/prisma";
import { rcon } from "@/lib/services/rcon";
type BanType = "account" | "ip" | "machine" | "super";
const BAN_TYPES: ReadonlySet<string> = new Set(["account", "ip", "machine", "super"]);
const PERMANENT_SECONDS = 100 * 365 * 24 * 3600;
export async function createBan(formData: FormData): Promise<void> {
const staff = await requireStaff();
const userId = Number(formData.get("userId"));
const reason = String(formData.get("reason") ?? "").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);
revalidatePath("/admin/bans");
}
export async function liftBan(formData: FormData): Promise<void> {
await requireStaff();
const id = Number(formData.get("id"));
if (id > 0) await prisma.ban.delete({ where: { id } });
revalidatePath("/admin/bans");
}