refactor(catalog): unify commands and embed guarded catalog workspace
This commit is contained in:
1 parent
16f35568f5
commit
193b6686a9
57 files changed
+3268
-728
No files matched your search
@@ -1,4 +1,8 @@
|
||||
import { asc, eq, inArray, sql } from "drizzle-orm";
|
||||
import { asc, eq, sql } from "drizzle-orm";
|
||||
import {
|
||||
deletePageCommand,
|
||||
updatePageCommand,
|
||||
} from "@/features/catalog/server/page-commands";
|
||||
import { CatalogPages, db } from "@/lib/db";
|
||||
|
||||
export type { NestedTreeNode, TreeNode } from "@/types/catalog";
|
||||
@@ -72,7 +76,7 @@ export async function getTreeFlat(): Promise<TreeNode[]> {
|
||||
enabled: CatalogPages.enabled,
|
||||
})
|
||||
.from(CatalogPages)
|
||||
.orderBy(asc(CatalogPages.orderNum));
|
||||
.orderBy(asc(CatalogPages.orderNum), asc(CatalogPages.id));
|
||||
const itemCountMap = await getCatalogItemCounts();
|
||||
|
||||
const childCountMap = new Map<number, number>();
|
||||
@@ -204,94 +208,13 @@ export async function movePage(
|
||||
pageId: number,
|
||||
newParentId: number,
|
||||
): Promise<void> {
|
||||
if (newParentId > 0) {
|
||||
let currentId = newParentId;
|
||||
for (let i = 0; i < 50; i++) {
|
||||
if (currentId === pageId) {
|
||||
throw new Error("Cannot move page: would create a circular hierarchy");
|
||||
}
|
||||
const [parent] = await db
|
||||
.select({ parentId: CatalogPages.parentId })
|
||||
.from(CatalogPages)
|
||||
.where(eq(CatalogPages.id, currentId))
|
||||
.limit(1);
|
||||
if (!parent || parent.parentId <= 0) break;
|
||||
currentId = parent.parentId;
|
||||
}
|
||||
}
|
||||
|
||||
await db
|
||||
.update(CatalogPages)
|
||||
.set({ parentId: newParentId })
|
||||
.where(eq(CatalogPages.id, pageId));
|
||||
await updatePageCommand("normal", pageId, { parentId: newParentId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete catalog_items for the given page ids.
|
||||
* Habbo DBs often store page_id as VARCHAR; text delete on INT or VARCHAR
|
||||
* columns keeps the page_id index usable (CAST would force a full scan).
|
||||
*/
|
||||
async function deleteCatalogItemsByPageIds(pageIds: number[]): Promise<void> {
|
||||
if (pageIds.length === 0) return;
|
||||
const idStrs = pageIds.map(String);
|
||||
await db.execute(sql`
|
||||
DELETE FROM catalog_items
|
||||
WHERE page_id IN (${sql.join(idStrs, sql`, `)})
|
||||
`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a page with cascade or reparent mode.
|
||||
*/
|
||||
export async function deletePage(
|
||||
pageId: number,
|
||||
mode: "cascade" | "reparent" = "reparent",
|
||||
): Promise<{ deletedPages: number; movedChildren: number }> {
|
||||
const [page] = await db
|
||||
.select({ parentId: CatalogPages.parentId })
|
||||
.from(CatalogPages)
|
||||
.where(eq(CatalogPages.id, pageId))
|
||||
.limit(1);
|
||||
if (!page) return { deletedPages: 0, movedChildren: 0 };
|
||||
|
||||
if (mode === "reparent") {
|
||||
const [result] = await db
|
||||
.update(CatalogPages)
|
||||
.set({ parentId: page.parentId })
|
||||
.where(eq(CatalogPages.parentId, pageId));
|
||||
|
||||
await deleteCatalogItemsByPageIds([pageId]);
|
||||
await db.delete(CatalogPages).where(eq(CatalogPages.id, pageId));
|
||||
|
||||
return { deletedPages: 1, movedChildren: result.affectedRows };
|
||||
}
|
||||
|
||||
const deleted = await cascadeDelete(pageId);
|
||||
return { deletedPages: deleted, movedChildren: 0 };
|
||||
}
|
||||
|
||||
async function cascadeDelete(pageId: number): Promise<number> {
|
||||
const toDelete: number[] = [pageId];
|
||||
const queue: number[] = [pageId];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const children = await db
|
||||
.select({ id: CatalogPages.id })
|
||||
.from(CatalogPages)
|
||||
.where(inArray(CatalogPages.parentId, queue));
|
||||
queue.length = 0;
|
||||
for (const child of children) {
|
||||
toDelete.push(child.id);
|
||||
queue.push(child.id);
|
||||
}
|
||||
}
|
||||
|
||||
await deleteCatalogItemsByPageIds(toDelete);
|
||||
for (let i = toDelete.length - 1; i >= 0; i--) {
|
||||
await db.delete(CatalogPages).where(eq(CatalogPages.id, toDelete[i]));
|
||||
}
|
||||
|
||||
return toDelete.length;
|
||||
) {
|
||||
return deletePageCommand("normal", pageId, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user