feat: add catalog audit page
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 1m56s

Checks for:
- items_base entries without catalog_items (not purchasable)
- catalog_items referencing non-existent items_base
- Items without .nitro or icon files on disk
- Duplicate classnames
- Items missing from all configured clone sources
This commit is contained in:
openhands committed 2026-07-21 15:24:58 +02:00
1 parent 90107c83d5
commit 19e4e10b1d
6 files changed
+607 -1

No files matched your search

File diff suppressed because it is too large. Load diff
+23
View File
@@ -0,0 +1,23 @@
import { redirect } from "next/navigation";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { AuditClient } from "./audit-client";
export default async function AuditPage() {
const { session, permissions } = await getAdminContext();
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
redirect("/admin");
}
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-bold tracking-tight">Catalog Audit</h1>
<p className="text-muted-foreground mt-1">
Scan your entire furniture catalog for missing entries, broken
references, missing files, and items available from clone sources.
</p>
</div>
<AuditClient />
</div>
);
}
+7
View File
@@ -1,6 +1,7 @@
import { import {
BadgeCheck, BadgeCheck,
Cat, Cat,
ClipboardCheck,
Copy, Copy,
Dumbbell, Dumbbell,
ImageIcon, ImageIcon,
@@ -76,6 +77,12 @@ export default async function ImportPage() {
icon: ImageIcon, icon: ImageIcon,
description: t("repairIconsDescription"), description: t("repairIconsDescription"),
}, },
{
href: "/admin/import/audit",
label: t("audit"),
icon: ClipboardCheck,
description: t("auditDescription"),
},
] as const; ] as const;
return ( return (
+13
View File
@@ -0,0 +1,13 @@
import { withAdmin } from "@/lib/api-handler";
import { apiError, apiOk } from "@/lib/api-response";
import { PERMS } from "@/lib/permissions";
import { runCatalogAudit } from "@/lib/services/catalog-audit";
export const POST = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async () => {
try {
const result = await runCatalogAudit();
return apiOk(result as unknown as Record<string, unknown>);
} catch (err) {
return apiError(`Audit failed: ${(err as Error).message}`, 500);
}
});
File diff suppressed because it is too large. Load diff
+3 -1
View File
@@ -2494,7 +2494,9 @@
"sync": "Auto Sync", "sync": "Auto Sync",
"syncDescription": "Automatically import missing furniture from all configured sources.", "syncDescription": "Automatically import missing furniture from all configured sources.",
"repairIcons": "Repair Icons", "repairIcons": "Repair Icons",
"repairIconsDescription": "Download any missing icon files for furniture already in the database." "repairIconsDescription": "Download any missing icon files for furniture already in the database.",
"audit": "Catalog Audit",
"auditDescription": "Scan your catalog for missing entries, broken references, and items missing from clone sources."
}, },
"translations": { "translations": {
"title": "Translations", "title": "Translations",