- 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).
52 lines
1.6 KiB
TypeScript
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");
|
|
}
|