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

+136 -99
View File
@@ -1,3 +1,4 @@
import { and, asc, desc, eq, inArray, or } from "drizzle-orm";
import type { Metadata } from "next";
import Link from "next/link";
import { notFound } from "next/navigation";
@@ -12,9 +13,19 @@ import {
StatBlock,
} from "@/components/public/ui";
import { auth } from "@/lib/auth";
import {
CameraWeb,
db,
MessengerFriendrequests,
MessengerFriendships,
Rooms,
User,
UsersBadges,
UsersCurrency,
WebsiteUserGuestbooks,
} from "@/lib/db";
import { avatarImageUrl } from "@/lib/format";
import { formatDate } from "@/lib/format-date";
import { prisma } from "@/lib/prisma";
import { siteSettings } from "@/lib/services/site-settings";
export const dynamic = "force-dynamic";
@@ -47,12 +58,12 @@ export async function generateMetadata({
params: Promise<{ username: string }>;
}): Promise<Metadata> {
const { username } = await params;
const user = await prisma.user
.findUnique({
where: { username },
select: { username: true, motto: true },
})
.catch(() => null);
const [user] = await db
.select({ username: User.username, motto: User.motto })
.from(User)
.where(eq(User.username, username))
.limit(1)
.catch(() => []);
if (!user) return { title: "Profile" };
return {
title: user.username,
@@ -78,26 +89,28 @@ export default async function ProfilePage({
const { username } = await params;
const { friend, guestbook: guestbookStatus, error } = await searchParams;
const [user, imagerBase, session] = await Promise.all([
prisma.user.findUnique({
where: { username },
select: {
id: true,
username: true,
motto: true,
look: true,
rank: true,
credits: true,
online: true,
accountCreated: true,
},
}),
const [userRows, imagerBase, session] = await Promise.all([
db
.select({
id: User.id,
username: User.username,
motto: User.motto,
look: User.look,
rank: User.rank,
credits: User.credits,
online: User.online,
accountCreated: User.accountCreated,
})
.from(User)
.where(eq(User.username, username))
.limit(1),
siteSettings.get(
"habbo_imaging_url",
"https://www.habbo.com/habbo-imaging/avatarimage",
),
auth(),
]);
const user = userRows[0] ?? null;
if (!user) notFound();
@@ -112,34 +125,50 @@ export default async function ProfilePage({
let outgoingPending = false;
let incomingPending = false;
if (isLoggedIn && !isSelf) {
const [friendship, outgoing, incoming] = await Promise.all([
prisma.messengerFriendships
.findFirst({
where: {
OR: [
{ userOneId: meId, userTwoId: user.id },
{ userOneId: user.id, userTwoId: meId },
],
},
select: { id: true },
})
.catch(() => null),
prisma.messengerFriendrequests
.findFirst({
where: { userFromId: meId, userToId: user.id },
select: { id: true },
})
.catch(() => null),
prisma.messengerFriendrequests
.findFirst({
where: { userFromId: user.id, userToId: meId },
select: { id: true },
})
.catch(() => null),
const [friendshipRows, outgoingRows, incomingRows] = await Promise.all([
db
.select({ id: MessengerFriendships.id })
.from(MessengerFriendships)
.where(
or(
and(
eq(MessengerFriendships.userOneId, meId),
eq(MessengerFriendships.userTwoId, user.id),
),
and(
eq(MessengerFriendships.userOneId, user.id),
eq(MessengerFriendships.userTwoId, meId),
),
),
)
.limit(1)
.catch(() => []),
db
.select({ id: MessengerFriendrequests.id })
.from(MessengerFriendrequests)
.where(
and(
eq(MessengerFriendrequests.userFromId, meId),
eq(MessengerFriendrequests.userToId, user.id),
),
)
.limit(1)
.catch(() => []),
db
.select({ id: MessengerFriendrequests.id })
.from(MessengerFriendrequests)
.where(
and(
eq(MessengerFriendrequests.userFromId, user.id),
eq(MessengerFriendrequests.userToId, meId),
),
)
.limit(1)
.catch(() => []),
]);
isFriend = Boolean(friendship);
outgoingPending = Boolean(outgoing);
incomingPending = Boolean(incoming);
isFriend = Boolean(friendshipRows[0]);
outgoingPending = Boolean(outgoingRows[0]);
incomingPending = Boolean(incomingRows[0]);
}
const feedbackErrorMessage =
@@ -168,14 +197,18 @@ export default async function ProfilePage({
// Currency ledger rows (duckets/diamonds). Credits live on the users row
// itself; the alt-currencies sit in the users_currency ledger keyed by type.
const currencyRows = await prisma.usersCurrency
.findMany({
where: {
userId: user.id,
type: { in: [CURRENCY_DUCKETS, CURRENCY_DIAMONDS] },
},
select: { type: true, amount: true },
const currencyRows = await db
.select({
type: UsersCurrency.type,
amount: UsersCurrency.amount,
})
.from(UsersCurrency)
.where(
and(
eq(UsersCurrency.userId, user.id),
inArray(UsersCurrency.type, [CURRENCY_DUCKETS, CURRENCY_DIAMONDS]),
),
)
.catch(() => []);
const ducketsAmount =
currencyRows.find((c) => c.type === CURRENCY_DUCKETS)?.amount ?? 0;
@@ -207,21 +240,23 @@ export default async function ProfilePage({
// Friends. messenger_friendships stores one directed row per pair keyed on
// user_one_id; resolve the user_two_id side to usernames/looks for the grid.
const friendships = await prisma.messengerFriendships
.findMany({
where: { userOneId: user.id },
orderBy: { friendsSince: "desc" },
take: 18,
select: { userTwoId: true },
})
const friendships = await db
.select({ userTwoId: MessengerFriendships.userTwoId })
.from(MessengerFriendships)
.where(eq(MessengerFriendships.userOneId, user.id))
.orderBy(desc(MessengerFriendships.friendsSince))
.limit(18)
.catch(() => []);
const friendIds = Array.from(new Set(friendships.map((f) => f.userTwoId)));
const friendUsers = friendIds.length
? await prisma.user
.findMany({
where: { id: { in: friendIds } },
select: { id: true, username: true, look: true },
? await db
.select({
id: User.id,
username: User.username,
look: User.look,
})
.from(User)
.where(inArray(User.id, friendIds))
.catch(() => [])
: [];
const friendById = new Map(friendUsers.map((f) => [f.id, f]));
@@ -230,52 +265,54 @@ export default async function ProfilePage({
.filter((f): f is NonNullable<typeof f> => Boolean(f));
// Rooms owned by this user.
const rooms = await prisma.rooms
.findMany({
where: { ownerId: user.id },
orderBy: { users: "desc" },
take: 8,
select: {
id: true,
name: true,
users: true,
usersMax: true,
state: true,
},
const rooms = await db
.select({
id: Rooms.id,
name: Rooms.name,
users: Rooms.users,
usersMax: Rooms.usersMax,
state: Rooms.state,
})
.from(Rooms)
.where(eq(Rooms.ownerId, user.id))
.orderBy(desc(Rooms.users))
.limit(8)
.catch(() => []);
const badges = await prisma.usersBadges
.findMany({
where: { userId: user.id },
orderBy: { slotId: "asc" },
})
const badges = await db
.select()
.from(UsersBadges)
.where(eq(UsersBadges.userId, user.id))
.orderBy(asc(UsersBadges.slotId))
.catch(() => []);
const photos = await prisma.cameraWeb
.findMany({
where: { userId: user.id },
orderBy: { timestamp: "desc" },
take: 6,
})
const photos = await db
.select()
.from(CameraWeb)
.where(eq(CameraWeb.userId, user.id))
.orderBy(desc(CameraWeb.timestamp))
.limit(6)
.catch(() => []);
const guestbook = await prisma.websiteUserGuestbooks
.findMany({
where: { profileId: user.id },
orderBy: { createdAt: "desc" },
take: 30,
})
const guestbook = await db
.select()
.from(WebsiteUserGuestbooks)
.where(eq(WebsiteUserGuestbooks.profileId, user.id))
.orderBy(desc(WebsiteUserGuestbooks.createdAt))
.limit(30)
.catch(() => []);
// Resolve author usernames/looks for the guestbook entries in one query.
const authorIds = Array.from(new Set(guestbook.map((g) => g.userId)));
const authors = authorIds.length
? await prisma.user
.findMany({
where: { id: { in: authorIds } },
select: { id: true, username: true, look: true },
? await db
.select({
id: User.id,
username: User.username,
look: User.look,
})
.from(User)
.where(inArray(User.id, authorIds))
.catch(() => [])
: [];
const authorById = new Map(authors.map((a) => [a.id, a]));