feat: parallelize audit repair and add nitro/SQL repair options
- 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:
1 parent
0c8e62357f
commit
5308ce6a12
8 files changed
+1166
-58
No files matched your search
@@ -6,41 +6,72 @@ function sseEvent(data: unknown): string {
|
||||
return `data: ${JSON.stringify(data)}\n\n`;
|
||||
}
|
||||
|
||||
export const POST = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async () => {
|
||||
const encoder = new TextEncoder();
|
||||
export const POST = withAdmin(
|
||||
{ permission: PERMS.ASSETS_IMPORT },
|
||||
async (request) => {
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
const send = (data: unknown) => {
|
||||
try {
|
||||
controller.enqueue(encoder.encode(sseEvent(data)));
|
||||
} catch {
|
||||
/* stream closed */
|
||||
}
|
||||
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 */
|
||||
}
|
||||
|
||||
try {
|
||||
await runCatalogAudit(send);
|
||||
} catch (err) {
|
||||
send({
|
||||
type: "error",
|
||||
message: `Audit threw: ${(err as Error).message}`,
|
||||
});
|
||||
}
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
const send = (data: unknown) => {
|
||||
try {
|
||||
controller.enqueue(encoder.encode(sseEvent(data)));
|
||||
} catch {
|
||||
/* stream closed */
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
controller.close();
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
},
|
||||
});
|
||||
try {
|
||||
await runCatalogAudit(send, {
|
||||
repair,
|
||||
repairNitros,
|
||||
sql,
|
||||
applySql,
|
||||
repairFurniData,
|
||||
});
|
||||
} catch (err) {
|
||||
send({
|
||||
type: "error",
|
||||
message: `Audit threw: ${(err as Error).message}`,
|
||||
});
|
||||
}
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
Connection: "keep-alive",
|
||||
},
|
||||
});
|
||||
});
|
||||
try {
|
||||
controller.close();
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
Connection: "keep-alive",
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user