refactor: replace Prisma ORM runtime with Drizzle ORM facade
- Replace Prisma client runtime with Drizzle ORM (zero Prisma engine/query engine in production) - Add Prisma-compatible facade (@/lib/prisma-facade.ts) backed by Drizzle for backwards compatibility - Runtime queries route through Drizzle ORM; @prisma/client is now devDependency (types only) - Remove @prisma/adapter-mariadb dependency; delete prisma-pool.ts and types/prisma.ts - New Drizzle schema layer: src/db/schema.ts (176 tables) and src/lib/db.ts (connection) - Update README documenting the dual-layer ORM architecture - Restore src/generated/ gitignore (build artifact for local type generation) - 0 TypeScript errors, 583 tests passing The facade intentionally uses `any` types to match the Prisma Client API surface, allowing existing code to run unmodified while routing queries through Drizzle at runtime.
This commit is contained in:
1 parent
9d1c71d926
commit
56061e41d4
52 files changed
+5825
-748
No files matched your search
@@ -1,7 +1,8 @@
|
||||
import { asc, count, eq } from "drizzle-orm";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { AdminBreadcrumb } from "@/components/admin/admin-breadcrumb";
|
||||
import { AclModelPermission, AclPermission, AclRole, db, User } from "@/lib/db";
|
||||
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { fetchEmulatorRankForEdit } from "@/lib/services/permission-ranks";
|
||||
import { RankEditClient } from "./rank-edit-client";
|
||||
|
||||
@@ -19,23 +20,32 @@ export default async function RankEditPage({
|
||||
const rankId = parseInt(rawId, 10);
|
||||
if (Number.isNaN(rankId)) notFound();
|
||||
|
||||
const rank = await fetchEmulatorRankForEdit(prisma, rankId);
|
||||
const rank = await fetchEmulatorRankForEdit(db, rankId);
|
||||
if (!rank) notFound();
|
||||
|
||||
// Fetch users with this rank (limit 200)
|
||||
const users = await prisma.user.findMany({
|
||||
where: { rank: rankId },
|
||||
select: { id: true, username: true, look: true },
|
||||
orderBy: { username: "asc" },
|
||||
take: 200,
|
||||
});
|
||||
const users = await db
|
||||
.select({ id: User.id, username: User.username, look: User.look })
|
||||
.from(User)
|
||||
.where(eq(User.rank, rankId))
|
||||
.orderBy(asc(User.username))
|
||||
.limit(200);
|
||||
|
||||
const totalUsers = await prisma.user.count({ where: { rank: rankId } });
|
||||
const [totalUsersRow] = await db
|
||||
.select({ total: count() })
|
||||
.from(User)
|
||||
.where(eq(User.rank, rankId));
|
||||
const totalUsers = totalUsersRow?.total ?? 0;
|
||||
|
||||
// Fetch all CMS permissions
|
||||
const allCmsPermissions = await prisma.aclPermission.findMany({
|
||||
orderBy: { slug: "asc" },
|
||||
});
|
||||
const allCmsPermissions = await db
|
||||
.select({
|
||||
id: AclPermission.id,
|
||||
slug: AclPermission.slug,
|
||||
title: AclPermission.title,
|
||||
})
|
||||
.from(AclPermission)
|
||||
.orderBy(asc(AclPermission.slug));
|
||||
|
||||
// Auto-create CMS role for rank >= 4
|
||||
let cmsRole: {
|
||||
@@ -46,22 +56,37 @@ export default async function RankEditPage({
|
||||
} | null = null;
|
||||
|
||||
if (rankId >= 4) {
|
||||
const role = await prisma.aclRole.upsert({
|
||||
where: { slug: `rank_${rankId}` },
|
||||
create: {
|
||||
slug: `rank_${rankId}`,
|
||||
title: rank.rank_name || `Rank ${rankId}`,
|
||||
},
|
||||
update: {},
|
||||
include: {
|
||||
rolePermissions: { include: { permission: true } },
|
||||
},
|
||||
});
|
||||
const slug = `rank_${rankId}`;
|
||||
const title = rank.rank_name || `Rank ${rankId}`;
|
||||
const [existing] = await db
|
||||
.select({ id: AclRole.id })
|
||||
.from(AclRole)
|
||||
.where(eq(AclRole.slug, slug))
|
||||
.limit(1);
|
||||
|
||||
let roleId: number;
|
||||
if (existing) {
|
||||
roleId = existing.id;
|
||||
await db.update(AclRole).set({ title }).where(eq(AclRole.id, roleId));
|
||||
} else {
|
||||
const inserted = await db.insert(AclRole).values({ slug, title });
|
||||
roleId = inserted[0].insertId;
|
||||
}
|
||||
|
||||
const permissionRows = await db
|
||||
.select({ slug: AclPermission.slug })
|
||||
.from(AclModelPermission)
|
||||
.innerJoin(
|
||||
AclPermission,
|
||||
eq(AclModelPermission.permissionId, AclPermission.id),
|
||||
)
|
||||
.where(eq(AclModelPermission.modelId, roleId));
|
||||
|
||||
cmsRole = {
|
||||
id: role.id,
|
||||
slug: role.slug,
|
||||
title: role.title,
|
||||
permissionSlugs: role.rolePermissions.map((rp) => rp.permission.slug),
|
||||
id: roleId,
|
||||
slug,
|
||||
title,
|
||||
permissionSlugs: permissionRows.map((rp) => rp.slug),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { sql } from "drizzle-orm";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Prisma } from "@/generated/prisma/client";
|
||||
import { db } from "@/lib/db";
|
||||
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { fetchEmulatorRankSummaries } from "@/lib/services/permission-ranks";
|
||||
import { PermissionsList } from "./permissions-list";
|
||||
|
||||
@@ -19,12 +19,12 @@ export default async function PermissionsPage({
|
||||
const sp = (await searchParams) ?? {};
|
||||
const fromHousekeeping = sp.from === "housekeeping";
|
||||
|
||||
const ranks = await fetchEmulatorRankSummaries(prisma);
|
||||
const ranks = await fetchEmulatorRankSummaries(db);
|
||||
|
||||
// User counts per rank
|
||||
const rawCounts: { rank: number; cnt: number }[] = await prisma.$queryRaw(
|
||||
Prisma.sql`SELECT \`rank\`, COUNT(*) as cnt FROM users GROUP BY \`rank\``,
|
||||
);
|
||||
const [rawCounts] = (await db.execute(
|
||||
sql`SELECT \`rank\`, COUNT(*) as cnt FROM users GROUP BY \`rank\``,
|
||||
)) as unknown as [{ rank: number; cnt: number }[], unknown[]];
|
||||
const countMap: Record<number, number> = {};
|
||||
for (const row of rawCounts) {
|
||||
countMap[Number(row.rank)] = Number(row.cnt);
|
||||
|
||||
Reference in new issue
Block a user