feat: parallelize audit repair and add nitro/SQL repair options
CI / check (push) Successful in 48s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m55s

- Parallelize icon and nitro downloads in the audit repair with a
  sliding-window worker pool (6 concurrent) to speed up large catalogs
- Add repairMissingNitros: fetch missing .nitro bundles from configured
  nitro sources (Wibbo default), validating each bundle before writing
- Add catalog-repair service: generate/apply catalog_items SQL for furni
  missing a catalog entry and repair FurnitureData.json (add missing +
  dedupe classnames)
- Wire all options through the audit API and client UI with live progress
  and result stats (icons, nitros, SQL, furnidata)
- Add Wibbo as default nitro source alongside existing icon sources
- Ignore runtime furni assets downloaded into public/ during repair
This commit is contained in:
openhands committed 2026-08-02 19:12:28 +02:00
1 parent 0c8e62357f
commit 5308ce6a12
8 files changed
+1129 -21

No files matched your search

+5
View File
@@ -24,5 +24,10 @@ gitea
# Runtime uploaded media (persistent, outside public/) # Runtime uploaded media (persistent, outside public/)
storage/ storage/
# Runtime downloaded furni assets (mirrored from gamedata / audit repair)
public/nitro-assets/bundled/furniture/
public/swf/dcr/
public/tmp/
# Test coverage reports # Test coverage reports
coverage/ coverage/
File diff suppressed because it is too large. Load diff
+34 -3
View File
@@ -6,9 +6,33 @@ function sseEvent(data: unknown): string {
return `data: ${JSON.stringify(data)}\n\n`; return `data: ${JSON.stringify(data)}\n\n`;
} }
export const POST = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async () => { export const POST = withAdmin(
{ permission: PERMS.ASSETS_IMPORT },
async (request) => {
const encoder = new TextEncoder(); const encoder = new TextEncoder();
let repair = false;
let repairNitros = false;
let sql = false;
let applySql = false;
let repairFurniData = false;
try {
const body = (await request.json().catch(() => ({}))) as {
repair?: unknown;
repairNitros?: unknown;
sql?: unknown;
applySql?: unknown;
repairFurniData?: unknown;
};
repair = body.repair === true;
repairNitros = body.repairNitros === true;
sql = body.sql === true;
applySql = body.applySql === true;
repairFurniData = body.repairFurniData === true;
} catch {
/* no body */
}
const stream = new ReadableStream({ const stream = new ReadableStream({
async start(controller) { async start(controller) {
const send = (data: unknown) => { const send = (data: unknown) => {
@@ -20,7 +44,13 @@ export const POST = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async () => {
}; };
try { try {
await runCatalogAudit(send); await runCatalogAudit(send, {
repair,
repairNitros,
sql,
applySql,
repairFurniData,
});
} catch (err) { } catch (err) {
send({ send({
type: "error", type: "error",
@@ -43,4 +73,5 @@ export const POST = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async () => {
Connection: "keep-alive", Connection: "keep-alive",
}, },
}); });
}); },
);
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+10
View File
@@ -35,6 +35,16 @@ export const DEFAULT_ICON_SOURCES: CloneSource[] = [
}, },
]; ];
export const DEFAULT_NITRO_SOURCES: CloneSource[] = [
{
id: "default-wibbo",
name: "Wibbo",
furnidataUrl: "",
nitroBaseUrl: "https://assets.wibbo.org/bundled/furniture",
iconBaseUrl: "",
},
];
export async function listSources(): Promise<CloneSource[]> { export async function listSources(): Promise<CloneSource[]> {
const raw = (await siteSettings.get(KEY, "[]")) ?? "[]"; const raw = (await siteSettings.get(KEY, "[]")) ?? "[]";
try { try {
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff