refactor(db): migrate app pages and APIs from Prisma facade to Drizzle (3)

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-08-01 14:15:32 +02:00
1 parent 2cd0863cb8
commit 580972c0a0
40 files changed
+1089 -808

No files matched your search

+54 -33
View File
@@ -1,11 +1,12 @@
import { and, count, desc, eq, gte, inArray, like } from "drizzle-orm";
import {
AlertTriangle,
Ban,
Ban as BanIcon,
Clock,
Gavel,
Radio,
Shield,
User,
User as UserIcon,
} from "lucide-react";
import Link from "next/link";
import { redirect } from "next/navigation";
@@ -13,8 +14,8 @@ import { StatsCard } from "@/components/admin/stats-card";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { getMinStaffRank } from "@/lib/admin/min-staff-rank";
import { AdminAuditLog, Ban, db, SupportTickets, User } from "@/lib/db";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
const CFH_STATES: Record<number, { label: string; class: string }> = {
0: {
@@ -38,33 +39,53 @@ export default async function ModerationDashboard() {
const minStaffRank = await getMinStaffRank();
const [
openCfh,
pickedCfh,
todayBans,
modsOnline,
openCfhRows,
pickedCfhRows,
todayBansRows,
modsOnlineRows,
recentCfh,
recentModActions,
] = await Promise.all([
prisma.supportTickets.count({ where: { state: 0 } }),
prisma.supportTickets.count({ where: { state: 1 } }),
prisma.ban.count({ where: { timestamp: { gte: todayStart } } }),
prisma.user.count({
where: { online: "1", rank: { gte: minStaffRank } },
}),
prisma.supportTickets.findMany({
orderBy: { id: "desc" },
take: 10,
}),
prisma.adminAuditLog.findMany({
where: {
action: { startsWith: "mod_" },
},
orderBy: { id: "desc" },
take: 10,
include: { user: { select: { username: true } } },
}),
db
.select({ total: count() })
.from(SupportTickets)
.where(eq(SupportTickets.state, 0)),
db
.select({ total: count() })
.from(SupportTickets)
.where(eq(SupportTickets.state, 1)),
db
.select({ total: count() })
.from(Ban)
.where(gte(Ban.timestamp, todayStart)),
db
.select({ total: count() })
.from(User)
.where(and(eq(User.online, "1"), gte(User.rank, minStaffRank))),
db.select().from(SupportTickets).orderBy(desc(SupportTickets.id)).limit(10),
db
.select({
id: AdminAuditLog.id,
action: AdminAuditLog.action,
details: AdminAuditLog.details,
createdAt: AdminAuditLog.createdAt,
userId: AdminAuditLog.userId,
target: AdminAuditLog.target,
targetId: AdminAuditLog.targetId,
username: User.username,
})
.from(AdminAuditLog)
.leftJoin(User, eq(AdminAuditLog.userId, User.id))
.where(like(AdminAuditLog.action, "mod_%"))
.orderBy(desc(AdminAuditLog.id))
.limit(10),
]);
const openCfh = Number(openCfhRows[0]?.total ?? 0);
const pickedCfh = Number(pickedCfhRows[0]?.total ?? 0);
const todayBans = Number(todayBansRows[0]?.total ?? 0);
const modsOnline = Number(modsOnlineRows[0]?.total ?? 0);
// Fetch usernames for CFH tickets
const userIds = [
...new Set([
@@ -72,14 +93,14 @@ export default async function ModerationDashboard() {
...recentCfh.map((t) => t.reportedId),
...recentCfh.map((t) => t.modId),
]),
].filter(Boolean);
].filter(Boolean) as number[];
const users =
userIds.length > 0
? await prisma.user.findMany({
where: { id: { in: userIds } },
select: { id: true, username: true },
})
? await db
.select({ id: User.id, username: User.username })
.from(User)
.where(inArray(User.id, userIds))
: [];
const userMap = new Map(users.map((u) => [u.id, u.username]));
@@ -97,7 +118,7 @@ export default async function ModerationDashboard() {
<StatsCard
title="Bans Today"
value={todayBans}
icon={<Ban className="h-4 w-4" />}
icon={<BanIcon className="h-4 w-4" />}
href="/admin/bans"
/>
<StatsCard
@@ -190,13 +211,13 @@ export default async function ModerationDashboard() {
key={log.id}
className="flex items-center gap-3 py-2.5 first:pt-0 last:pb-0"
>
<User className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
<UserIcon className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
<div className="flex-1 min-w-0">
<p className="text-sm font-medium truncate">
{log.action.replace("mod_", "").replace(/_/g, " ")}
</p>
<p className="text-xs text-muted-foreground">
by {log.user?.username ?? "?"} · {log.target}{" "}
by {log.username ?? "?"} · {log.target}{" "}
{log.targetId ? `#${log.targetId}` : ""}
</p>
</div>