feat(studio): add nitro cleanup tool for fake and broken bundles
This commit is contained in:
1 parent
ce93b92a81
commit
694f87d98c
6 files changed
+869
No files matched your search
@@ -0,0 +1,22 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import { NitroCleanupPanel } from "@/components/admin/studio/nitro-cleanup-panel";
|
||||
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
|
||||
|
||||
export default async function StudioNitroCleanupPage() {
|
||||
const { session, permissions } = await getAdminContext();
|
||||
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
|
||||
redirect("/admin");
|
||||
}
|
||||
return (
|
||||
<div className="space-y-6 p-4">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">Nitro cleanup</h1>
|
||||
<p className="mt-1 text-muted-foreground">
|
||||
Scan the nitro directories for fake bundles (no matching furniture)
|
||||
and broken bundles (unparseable) and remove them.
|
||||
</p>
|
||||
</div>
|
||||
<NitroCleanupPanel />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
ClipboardCheck,
|
||||
Copy,
|
||||
Dumbbell,
|
||||
FileX2,
|
||||
Image as ImageIcon,
|
||||
type LucideIcon,
|
||||
Puzzle,
|
||||
@@ -52,6 +53,11 @@ const maintenance: StudioLink[] = [
|
||||
label: "Catalog maintenance",
|
||||
icon: Wrench,
|
||||
},
|
||||
{
|
||||
href: "/admin/studio/nitro-cleanup",
|
||||
label: "Nitro cleanup",
|
||||
icon: FileX2,
|
||||
},
|
||||
];
|
||||
const activePath = (path: string, href: string) =>
|
||||
path === href || path.startsWith(`${href}/`);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { apiError, apiOk } from "@/lib/api";
|
||||
import { withAdmin } from "@/lib/api-handler";
|
||||
import { PERMS } from "@/lib/permissions";
|
||||
import { logAudit } from "@/lib/services/audit";
|
||||
import { clearFurniImportCache } from "@/lib/services/furni-import-cache";
|
||||
import {
|
||||
deleteNitroCleanupFiles,
|
||||
scanFakeBrokenNitros,
|
||||
} from "@/lib/services/nitro-cleanup";
|
||||
|
||||
/** Scan all nitro directories for fake (orphaned) and broken (unparseable) .nitro files. */
|
||||
export const GET = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async () => {
|
||||
try {
|
||||
const scan = await scanFakeBrokenNitros();
|
||||
return apiOk({
|
||||
fake: scan.fake,
|
||||
broken: scan.broken,
|
||||
total: scan.total,
|
||||
});
|
||||
} catch (err) {
|
||||
return apiError(`Nitro scan failed: ${(err as Error).message}`, 500);
|
||||
}
|
||||
});
|
||||
|
||||
/** Delete the given .nitro files from every nitro directory (primary + mirrors). */
|
||||
export const DELETE = withAdmin(
|
||||
{ permission: PERMS.ASSETS_IMPORT },
|
||||
async (request, ctx) => {
|
||||
let body: { fileNames?: unknown };
|
||||
try {
|
||||
body = await request.json();
|
||||
} catch {
|
||||
return apiError("Invalid JSON body", 400);
|
||||
}
|
||||
const fileNames = Array.isArray(body?.fileNames)
|
||||
? body.fileNames.filter(
|
||||
(name): name is string => typeof name === "string",
|
||||
)
|
||||
: [];
|
||||
if (fileNames.length === 0) {
|
||||
return apiError("fileNames[] is required", 400);
|
||||
}
|
||||
try {
|
||||
const result = await deleteNitroCleanupFiles(fileNames);
|
||||
// The deleted .nitro presence is cached by the Studio — drop it so
|
||||
// the furniture list reflects the removal immediately.
|
||||
clearFurniImportCache();
|
||||
if (result.deleted > 0) {
|
||||
logAudit({
|
||||
userId: ctx.session.user.id,
|
||||
action: "nitro_cleanup_delete",
|
||||
target: "NitroFiles",
|
||||
after: {
|
||||
deleted: result.deleted,
|
||||
copiesRemoved: result.copiesRemoved,
|
||||
fileNames,
|
||||
errors: result.errors.length > 0 ? result.errors : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
return apiOk({
|
||||
deleted: result.deleted,
|
||||
copiesRemoved: result.copiesRemoved,
|
||||
errors: result.errors.length > 0 ? result.errors : undefined,
|
||||
});
|
||||
} catch (err) {
|
||||
return apiError(`Nitro cleanup failed: ${(err as Error).message}`, 500);
|
||||
}
|
||||
},
|
||||
);
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user