import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { createBan, liftBan } from "@/actions/admin-bans"; import { StatusCard } from "@/components/admin/dashboard"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; function fromUnix(ts: number): string { return new Date(ts * 1000).toISOString().slice(0, 16).replace("T", " "); } export default async function AdminBans() { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.BANS_VIEW, session.user.rank)) { redirect("/admin"); } const t = await getTranslations("pages.admin.bans"); const now = Math.floor(Date.now() / 1000); let bans: Awaited> = []; try { bans = await prisma.ban.findMany({ where: { banExpire: { gt: now } }, orderBy: { timestamp: "desc" }, take: 100, }); } catch { bans = []; } // banExpire of 0 means permanent in the emulator schema. const permanent = bans.filter((b) => b.banExpire === 0).length; const accountBans = bans.filter((b) => b.type === "account").length; return (
0 ? "warn" : "ok"} icon="🔨" /> 0 ? "danger" : "neutral"} icon="⛔" />

{t("newBan")}

{t("activeBans")} ({bans.length})

{bans.length === 0 ? (
{t("noBans")}
) : (
{bans.map((b) => ( ))}
{t("colUserId")} {t("colType")} {t("colReason")} {t("colExpires")}
{b.userId} {b.type} {b.banReason || "—"} {b.banExpire === 0 ? ( {t("permanent")} ) : ( {fromUnix(b.banExpire)} )}
)}
); }