revert fb8e77bb68
Local Build and Deploy / deploy (push) Successful in 1m11s

revert style: clean up code with prettier and eslint
This commit is contained in:
remco committed 2026-07-12 21:02:03 +02:00
1 parent 27de078f54
commit e85e4d74ea
378 files changed
+20710 -22428

No files matched your search

+87 -87
View File
@@ -1,20 +1,20 @@
import { withAdmin } from "@/lib/api-handler";
import { apiError } from "@/lib/api-response";
import { PERMS } from "@/lib/permissions";
import { logAudit } from "@/lib/services/audit";
import { appendFurniEntriesBatch } from "@/lib/services/furni-data";
import { ensureDirectories, importSingleFurni } from "@/lib/services/furni-import";
import { rcon } from "@/lib/services/rcon";
import type { ImportSingleResult } from "@/types/furni";
import { withAdmin } from '@/lib/api-handler'
import { apiError } from '@/lib/api-response'
import { PERMS } from '@/lib/permissions'
import { logAudit } from '@/lib/services/audit'
import { appendFurniEntriesBatch } from '@/lib/services/furni-data'
import { ensureDirectories, importSingleFurni } from '@/lib/services/furni-import'
import { rcon } from '@/lib/services/rcon'
import type { ImportSingleResult } from '@/types/furni'
interface BatchItem {
id: number;
classname: string;
name: string;
description: string;
type: string;
revision: number;
category: string;
id: number
classname: string
name: string
description: string
type: string
revision: number
category: string
}
/**
@@ -23,135 +23,135 @@ interface BatchItem {
* streaming real-time progress events to the client.
*/
export const POST = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async (request, ctx) => {
const body = await request.json();
const rawItems: BatchItem[] = body.items || [];
const concurrency = Math.min(Math.max(body.concurrency || 3, 1), 5);
const body = await request.json()
const rawItems: BatchItem[] = body.items || []
const concurrency = Math.min(Math.max(body.concurrency || 3, 1), 5)
if (rawItems.length === 0) return apiError("No items to import", 400);
if (rawItems.length === 0) return apiError('No items to import', 400)
// Deduplicate by (spriteId, classname) to avoid concurrent INSERT races on
// items_base.id when the same item is listed twice in the request body.
const seen = new Set<string>();
const items: BatchItem[] = [];
const seen = new Set<string>()
const items: BatchItem[] = []
for (const it of rawItems) {
const key = `${it.id}:${it.classname}`;
if (seen.has(key)) continue;
seen.add(key);
items.push(it);
const key = `${it.id}:${it.classname}`
if (seen.has(key)) continue
seen.add(key)
items.push(it)
}
await ensureDirectories();
const encoder = new TextEncoder();
await ensureDirectories()
const encoder = new TextEncoder()
const stream = new ReadableStream({
async start(controller) {
const send = (data: unknown) => {
try {
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`));
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
} catch {
/* stream closed by client */
}
};
}
const startTime = Date.now();
send({ type: "batch_start", total: items.length, concurrency });
const startTime = Date.now()
send({ type: 'batch_start', total: items.length, concurrency })
let succeeded = 0;
let failed = 0;
let withWarnings = 0;
const furniDataEntries: Array<{ entry: Record<string, unknown>; itemType: string }> = [];
let succeeded = 0
let failed = 0
let withWarnings = 0
const furniDataEntries: Array<{ entry: Record<string, unknown>; itemType: string }> = []
// Process in chunks of `concurrency`
for (let i = 0; i < items.length; i += concurrency) {
const chunk = items.slice(i, i + concurrency);
const chunk = items.slice(i, i + concurrency)
const promises = chunk.map(async (item, chunkIdx) => {
const index = i + chunkIdx;
send({ type: "item_progress", classname: item.classname, status: "started", index });
const index = i + chunkIdx
send({ type: 'item_progress', classname: item.classname, status: 'started', index })
try {
const result: ImportSingleResult = await importSingleFurni({
id: item.id ?? 0,
classname: item.classname,
name: item.name,
description: item.description ?? "",
type: item.type ?? "flooritem",
description: item.description ?? '',
type: item.type ?? 'flooritem',
revision: item.revision ?? 0,
category: item.category ?? "unknown",
category: item.category ?? 'unknown',
skipFurniDataWrite: true,
onProgress: (status: string) => {
send({ type: "item_progress", classname: item.classname, status, index });
send({ type: 'item_progress', classname: item.classname, status, index })
},
});
})
if (result.ok) {
succeeded++;
if (result.warnings.length > 0) withWarnings++;
if (result.furniDataEntry) furniDataEntries.push(result.furniDataEntry);
succeeded++
if (result.warnings.length > 0) withWarnings++
if (result.furniDataEntry) furniDataEntries.push(result.furniDataEntry)
send({
type: "item_progress",
type: 'item_progress',
classname: item.classname,
status: "done",
status: 'done',
index,
itemId: result.itemId,
warnings: result.warnings.length > 0 ? result.warnings : undefined,
});
})
logAudit({
userId: ctx.session.user.id,
action: "furni_import",
target: "ItemsBase",
action: 'furni_import',
target: 'ItemsBase',
targetId: result.itemId!,
after: {
classname: item.classname,
name: item.name,
type: item.type === "wallitem" ? "i" : "s",
type: item.type === 'wallitem' ? 'i' : 's',
catalogItemId: result.catalogItemId,
dimensions: result.dimensions,
batch: true,
},
});
})
} else {
failed++;
failed++
send({
type: "item_progress",
type: 'item_progress',
classname: item.classname,
status: "failed",
status: 'failed',
index,
message: result.error,
});
})
}
} catch (err) {
failed++;
failed++
send({
type: "item_progress",
type: 'item_progress',
classname: item.classname,
status: "failed",
status: 'failed',
index,
message: (err as Error).message,
});
})
}
});
})
await Promise.allSettled(promises);
await Promise.allSettled(promises)
}
// Batch write FurnitureData.json once at the end — BEFORE RCON so the
// emulator never sees items that the Nitro client can't render.
let furniDataWriteOk = true;
let furniDataWriteOk = true
if (furniDataEntries.length > 0) {
try {
await appendFurniEntriesBatch(furniDataEntries);
await appendFurniEntriesBatch(furniDataEntries)
} catch (err) {
furniDataWriteOk = false;
furniDataWriteOk = false
send({
type: "item_progress",
classname: "_batch_",
status: "failed",
type: 'item_progress',
classname: '_batch_',
status: 'failed',
index: -1,
message: `FurnitureData.json batch write failed: ${(err as Error).message}`,
});
})
}
}
@@ -159,39 +159,39 @@ export const POST = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async (reques
// to avoid an inconsistent state (emulator sees items, client doesn't).
if (furniDataWriteOk) {
try {
const okCat = await rcon.updateCatalog();
const okItems = await rcon.updateItems();
const okCat = await rcon.updateCatalog()
const okItems = await rcon.updateItems()
if (!okCat || !okItems) {
send({
type: "item_progress",
classname: "_rcon_",
status: "failed",
type: 'item_progress',
classname: '_rcon_',
status: 'failed',
index: -1,
message: "RCON refresh failed — emulator cache may be stale",
});
message: 'RCON refresh failed — emulator cache may be stale',
})
}
} catch (err) {
console.warn("[import-furni] RCON update failed after batch:", (err as Error).message);
console.warn('[import-furni] RCON update failed after batch:', (err as Error).message)
}
}
send({
type: "batch_complete",
type: 'batch_complete',
succeeded,
failed,
warnings: withWarnings,
duration: Date.now() - startTime,
});
})
controller.close();
controller.close()
},
});
})
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
},
});
});
})
})