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

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-08-01 14:15:28 +02:00
1 parent 7c39aef5d9
commit 2cd0863cb8
40 files changed
+1069 -726

No files matched your search

+31 -42
View File
@@ -1,11 +1,12 @@
import { asc, eq } from "drizzle-orm";
import Link from "next/link";
import { getTranslations } from "next-intl/server";
import type { CSSProperties } from "react";
import { buyShopArticle } from "@/actions/shop";
import { ContentCard, EmptyState } from "@/components/public/ui";
import { auth } from "@/lib/auth";
import { db, WebsiteShopArticles, WebsiteShopCategories } from "@/lib/db";
import { excerpt } from "@/lib/format";
import { prisma } from "@/lib/prisma";
import { creditsPerUnit } from "@/lib/services/paypal";
export const revalidate = 300;
@@ -62,18 +63,18 @@ type ShopCategory = {
icon: string | null;
};
const ARTICLE_SELECT = {
id: true,
name: true,
info: true,
iconUrl: true,
color: true,
costs: true,
credits: true,
duckets: true,
diamonds: true,
badges: true,
position: true,
const articleSelect = {
id: WebsiteShopArticles.id,
name: WebsiteShopArticles.name,
info: WebsiteShopArticles.info,
iconUrl: WebsiteShopArticles.iconUrl,
color: WebsiteShopArticles.color,
costs: WebsiteShopArticles.costs,
credits: WebsiteShopArticles.credits,
duckets: WebsiteShopArticles.duckets,
diamonds: WebsiteShopArticles.diamonds,
badges: WebsiteShopArticles.badges,
position: WebsiteShopArticles.position,
} as const;
export default async function ShopPage({
@@ -115,43 +116,31 @@ export default async function ShopPage({
// Categories for the filter bar.
let categories: ShopCategory[] = [];
try {
categories = await prisma.websiteShopCategories.findMany({
select: { id: true, name: true, icon: true },
orderBy: { name: "asc" },
});
categories = await db
.select({
id: WebsiteShopCategories.id,
name: WebsiteShopCategories.name,
icon: WebsiteShopCategories.icon,
})
.from(WebsiteShopCategories)
.orderBy(asc(WebsiteShopCategories.name));
} catch {
categories = [];
}
// Articles. The category foreign key (website_shop_category_id) is not modelled
// in the typed Prisma client, so when filtering we resolve the matching ids via
// a guarded raw query, then load the rows through the typed client.
let articles: ShopArticle[] = [];
try {
if (categoryId) {
let ids: bigint[] = [];
try {
const rows = await prisma.$queryRawUnsafe<{ id: bigint }[]>(
"SELECT id FROM website_shop_articles WHERE website_shop_category_id = ?",
BigInt(categoryId),
);
ids = rows.map((r) => r.id);
} catch {
ids = [];
}
articles = ids.length
? await prisma.websiteShopArticles.findMany({
where: { id: { in: ids } },
select: ARTICLE_SELECT,
orderBy: { position: "asc" },
})
: [];
articles = await db
.select(articleSelect)
.from(WebsiteShopArticles)
.where(eq(WebsiteShopArticles.categoryId, BigInt(categoryId)))
.orderBy(asc(WebsiteShopArticles.position));
} else {
articles = await prisma.websiteShopArticles.findMany({
select: ARTICLE_SELECT,
orderBy: { position: "asc" },
});
articles = await db
.select(articleSelect)
.from(WebsiteShopArticles)
.orderBy(asc(WebsiteShopArticles.position));
}
} catch {
articles = [];
+8 -5
View File
@@ -1,8 +1,9 @@
import { eq } from "drizzle-orm";
import { redirect } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { ContentCard, StatBlock } from "@/components/public/ui";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { db, User } from "@/lib/db";
import {
creditsPerUnit,
isPayPalConfigured,
@@ -27,10 +28,12 @@ export default async function TopUpPage({
// Current credits balance for context (fail soft).
let user: { username: string; credits: number } | null = null;
try {
user = await prisma.user.findUnique({
where: { id },
select: { username: true, credits: true },
});
const [row] = await db
.select({ username: User.username, credits: User.credits })
.from(User)
.where(eq(User.id, id))
.limit(1);
user = row ?? null;
} catch {
user = null;
}