Fix catalog items missing due to page_id VARCHAR mismatch.
Local Build and Deploy / deploy (push) Successful in 56s

Use raw SQL for counts/loads/creates/moves so Habbo DBs with VARCHAR page_id and no AUTO_INCREMENT still show and persist furni.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-17 22:21:49 +02:00
1 parent 33a8a19820
commit 7bc0845932
6 files changed
+139 -79

No files matched your search

+4 -1
View File
@@ -135,8 +135,11 @@ export async function loadCatalogItemsData(
): Promise<CatalogItemsData> {
// Load items via raw query to work around pageId Int vs VARCHAR mismatch
const pageIdStr = String(pageId);
// CAST: live Habbo DBs often store page_id as VARCHAR while Prisma maps Int.
const rawItems = await prisma.$queryRaw<Array<Record<string, unknown>>>`
SELECT * FROM catalog_items WHERE page_id = ${pageIdStr} ORDER BY id ASC
SELECT * FROM catalog_items
WHERE CAST(page_id AS CHAR) = ${pageIdStr}
ORDER BY order_number ASC, id ASC
`;
const items: RawItem[] = rawItems.map((r: Record<string, unknown>) => ({
id: Number(r.id),
+33 -17
View File
@@ -1,3 +1,4 @@
import { Prisma } from "@/generated/prisma/client";
import { prisma } from "@/lib/prisma";
export type { NestedTreeNode, TreeNode } from "@/types/catalog";
@@ -9,27 +10,43 @@ function toInt(value: unknown, fallback = 0): number {
return Number.isFinite(n) ? n : fallback;
}
async function safeItemCounts(
/**
* Count catalog_items per page via raw SQL.
* Real Habbo DBs often store page_id as VARCHAR; Prisma Int groupBy fails or returns 0.
*/
export async function getCatalogItemCounts(
pageIds?: number[],
): Promise<Map<number, number>> {
const map = new Map<number, number>();
try {
if (pageIds && pageIds.length === 0) return map;
const itemCounts = pageIds
? await prisma.catalogItems.groupBy({
by: ["pageId"],
where: { pageId: { in: pageIds } },
_count: true,
})
: await prisma.catalogItems.groupBy({
by: ["pageId"],
_count: true,
});
for (const c of itemCounts) {
map.set(toInt(c.pageId), c._count);
if (pageIds) {
const idStrs = pageIds.map(String);
const rows = await prisma.$queryRaw<
Array<{ page_id: string | number; cnt: number | bigint }>
>`
SELECT page_id, COUNT(*) as cnt
FROM catalog_items
WHERE CAST(page_id AS CHAR) IN (${Prisma.join(idStrs)})
GROUP BY page_id
`;
for (const r of rows) {
map.set(toInt(r.page_id), toInt(r.cnt));
}
return map;
}
const rows = await prisma.$queryRaw<
Array<{ page_id: string | number; cnt: number | bigint }>
>`
SELECT page_id, COUNT(*) as cnt FROM catalog_items GROUP BY page_id
`;
for (const r of rows) {
map.set(toInt(r.page_id), toInt(r.cnt));
}
} catch {
// Some DBs store page_id as VARCHAR — ignore counts rather than failing the tree.
// Best-effort — never break the tree for count failures.
}
return map;
}
@@ -41,7 +58,7 @@ export async function getTreeFlat(): Promise<TreeNode[]> {
const allPages = await prisma.catalogPages.findMany({
orderBy: { orderNum: "asc" },
});
const itemCountMap = await safeItemCounts();
const itemCountMap = await getCatalogItemCounts();
const childCountMap = new Map<number, number>();
for (const p of allPages) {
@@ -261,5 +278,4 @@ export async function getAncestors(
return ancestors;
}
/** Safe item-count helper exported for the tree API route. */
export { safeItemCounts, toInt };
export { toInt };