style: clean up code with prettier and eslint
This commit is contained in:
1 parent
60278b9e71
commit
fb8e77bb68
378 files changed
+22407
-20689
No files matched your search
@@ -1,19 +1,19 @@
|
||||
import { existsSync, promises as fs } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { withAdmin } from '@/lib/api-handler'
|
||||
import { apiError } from '@/lib/api-response'
|
||||
import { PERMS } from '@/lib/permissions'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { getFurniAssetDirs } from '@/lib/services/furni-asset-dirs'
|
||||
import { appendFurniEntriesBatch, buildFurniEntry, readFurniData } from '@/lib/services/furni-data'
|
||||
import { downloadFile, ensureDirectories } from '@/lib/services/furni-import'
|
||||
import { getHabboItFurnidata } from '@/lib/services/habbo-furnidata-cache'
|
||||
import { rcon } from '@/lib/services/rcon'
|
||||
import { convertSwfToNitro } from '@/lib/services/swf-to-nitro'
|
||||
import { existsSync, promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { withAdmin } from "@/lib/api-handler";
|
||||
import { apiError } from "@/lib/api-response";
|
||||
import { PERMS } from "@/lib/permissions";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { getFurniAssetDirs } from "@/lib/services/furni-asset-dirs";
|
||||
import { appendFurniEntriesBatch, buildFurniEntry, readFurniData } from "@/lib/services/furni-data";
|
||||
import { downloadFile, ensureDirectories } from "@/lib/services/furni-import";
|
||||
import { getHabboItFurnidata } from "@/lib/services/habbo-furnidata-cache";
|
||||
import { rcon } from "@/lib/services/rcon";
|
||||
import { convertSwfToNitro } from "@/lib/services/swf-to-nitro";
|
||||
|
||||
interface RegenItem {
|
||||
classname: string
|
||||
revision: number
|
||||
classname: string;
|
||||
revision: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,210 +21,209 @@ interface RegenItem {
|
||||
* Streams progress events for each item.
|
||||
*/
|
||||
export const POST = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async (request) => {
|
||||
const body = await request.json()
|
||||
const items: RegenItem[] = body.items || []
|
||||
const concurrency = Math.min(Math.max(body.concurrency || 3, 1), 5)
|
||||
const body = await request.json();
|
||||
const items: RegenItem[] = body.items || [];
|
||||
const concurrency = Math.min(Math.max(body.concurrency || 3, 1), 5);
|
||||
|
||||
if (items.length === 0) return apiError('No items to regenerate', 400)
|
||||
if (items.length === 0) return apiError("No items to regenerate", 400);
|
||||
|
||||
await ensureDirectories()
|
||||
const { swfDir, nitroDir } = await getFurniAssetDirs()
|
||||
const encoder = new TextEncoder()
|
||||
await ensureDirectories();
|
||||
const { swfDir, nitroDir } = await getFurniAssetDirs();
|
||||
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 */
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const startTime = Date.now()
|
||||
send({ type: 'regen_start', total: items.length })
|
||||
const startTime = Date.now();
|
||||
send({ type: "regen_start", total: items.length });
|
||||
|
||||
let succeeded = 0
|
||||
let failed = 0
|
||||
let skipped = 0
|
||||
const furniDataEntries: Array<{ entry: Record<string, unknown>; itemType: string }> = []
|
||||
let succeeded = 0;
|
||||
let failed = 0;
|
||||
let skipped = 0;
|
||||
const furniDataEntries: Array<{ entry: Record<string, unknown>; itemType: string }> = [];
|
||||
|
||||
// Load furnidata once for all items
|
||||
const furnidata = await getHabboItFurnidata()
|
||||
const furnidata = await getHabboItFurnidata();
|
||||
|
||||
// Load existing FurnitureData.json classnames for dedup
|
||||
const existingFD = (await readFurniData()) as {
|
||||
roomitemtypes?: { furnitype?: Array<{ classname?: string }> }
|
||||
wallitemtypes?: { furnitype?: Array<{ classname?: string }> }
|
||||
}
|
||||
roomitemtypes?: { furnitype?: Array<{ classname?: string }> };
|
||||
wallitemtypes?: { furnitype?: Array<{ classname?: string }> };
|
||||
};
|
||||
const fdClassnames = new Set([
|
||||
...(existingFD.roomitemtypes?.furnitype || []).map((e) => e.classname),
|
||||
...(existingFD.wallitemtypes?.furnitype || []).map((e) => e.classname),
|
||||
])
|
||||
]);
|
||||
|
||||
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) => {
|
||||
const starIdx = item.classname.indexOf('*')
|
||||
const baseClassname =
|
||||
starIdx !== -1 ? item.classname.substring(0, starIdx) : item.classname
|
||||
const nitroPath = path.join(/*turbopackIgnore: true*/ nitroDir, `${baseClassname}.nitro`)
|
||||
const starIdx = item.classname.indexOf("*");
|
||||
const baseClassname = starIdx !== -1 ? item.classname.substring(0, starIdx) : item.classname;
|
||||
const nitroPath = path.join(/*turbopackIgnore: true*/ nitroDir, `${baseClassname}.nitro`);
|
||||
|
||||
if (existsSync(/*turbopackIgnore: true*/ nitroPath)) {
|
||||
skipped++
|
||||
send({ type: 'regen_progress', classname: item.classname, status: 'skipped' })
|
||||
return
|
||||
skipped++;
|
||||
send({ type: "regen_progress", classname: item.classname, status: "skipped" });
|
||||
return;
|
||||
}
|
||||
|
||||
send({ type: 'regen_progress', classname: item.classname, status: 'downloading' })
|
||||
send({ type: "regen_progress", classname: item.classname, status: "downloading" });
|
||||
|
||||
// Resolve revision
|
||||
let rev = item.revision || 0
|
||||
let rev = item.revision || 0;
|
||||
if (rev === 0) {
|
||||
const fd =
|
||||
furnidata.get(item.classname) ||
|
||||
furnidata.get(baseClassname) ||
|
||||
furnidata.get(`${baseClassname}*0`)
|
||||
if (fd) rev = fd.revision
|
||||
furnidata.get(`${baseClassname}*0`);
|
||||
if (fd) rev = fd.revision;
|
||||
}
|
||||
|
||||
// Try spriteId as fallback revision
|
||||
const revsToTry: number[] = []
|
||||
if (rev > 0) revsToTry.push(rev)
|
||||
const revsToTry: number[] = [];
|
||||
if (rev > 0) revsToTry.push(rev);
|
||||
const dbItem = await prisma.itemsBase.findFirst({
|
||||
where: { itemName: item.classname },
|
||||
select: { id: true, spriteId: true, publicName: true, type: true },
|
||||
})
|
||||
});
|
||||
if (dbItem?.spriteId && !revsToTry.includes(dbItem.spriteId)) {
|
||||
revsToTry.push(dbItem.spriteId)
|
||||
revsToTry.push(dbItem.spriteId);
|
||||
}
|
||||
|
||||
if (revsToTry.length === 0) {
|
||||
failed++
|
||||
failed++;
|
||||
send({
|
||||
type: 'regen_progress',
|
||||
type: "regen_progress",
|
||||
classname: item.classname,
|
||||
status: 'failed',
|
||||
message: 'No revision found',
|
||||
})
|
||||
return
|
||||
status: "failed",
|
||||
message: "No revision found",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const swfPath = path.join(swfDir, `${baseClassname}.swf`)
|
||||
const swfPath = path.join(swfDir, `${baseClassname}.swf`);
|
||||
|
||||
// Download SWF if needed
|
||||
if (!existsSync(swfPath)) {
|
||||
let downloaded = false
|
||||
let downloaded = false;
|
||||
for (const tryRev of revsToTry) {
|
||||
const dl = await downloadFile(
|
||||
`https://images.habbo.com/dcr/hof_furni/${tryRev}/${encodeURIComponent(baseClassname)}.swf`,
|
||||
swfPath,
|
||||
{ validate: 'swf' },
|
||||
)
|
||||
{ validate: "swf" },
|
||||
);
|
||||
if (dl.ok) {
|
||||
rev = tryRev
|
||||
downloaded = true
|
||||
break
|
||||
rev = tryRev;
|
||||
downloaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!downloaded) {
|
||||
failed++
|
||||
failed++;
|
||||
send({
|
||||
type: 'regen_progress',
|
||||
type: "regen_progress",
|
||||
classname: item.classname,
|
||||
status: 'failed',
|
||||
message: `SWF not found (revs: ${revsToTry.join(', ')})`,
|
||||
})
|
||||
return
|
||||
status: "failed",
|
||||
message: `SWF not found (revs: ${revsToTry.join(", ")})`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert
|
||||
send({ type: 'regen_progress', classname: item.classname, status: 'converting' })
|
||||
send({ type: "regen_progress", classname: item.classname, status: "converting" });
|
||||
try {
|
||||
const swfBuffer = await fs.readFile(swfPath)
|
||||
const conversion = convertSwfToNitro(swfBuffer, baseClassname)
|
||||
await fs.writeFile(nitroPath, conversion.bundle)
|
||||
const swfBuffer = await fs.readFile(swfPath);
|
||||
const conversion = convertSwfToNitro(swfBuffer, baseClassname);
|
||||
await fs.writeFile(nitroPath, conversion.bundle);
|
||||
|
||||
// Build FurnitureData entry if missing
|
||||
if (!fdClassnames.has(baseClassname) && !fdClassnames.has(item.classname)) {
|
||||
const fd = furnidata.get(item.classname) || furnidata.get(baseClassname)
|
||||
const itemType = dbItem?.type === 'i' ? 'i' : 's'
|
||||
const fd = furnidata.get(item.classname) || furnidata.get(baseClassname);
|
||||
const itemType = dbItem?.type === "i" ? "i" : "s";
|
||||
const entry = buildFurniEntry({
|
||||
id: dbItem?.spriteId || 0,
|
||||
classname: baseClassname,
|
||||
revision: rev,
|
||||
category: fd?.category || '',
|
||||
category: fd?.category || "",
|
||||
name: dbItem?.publicName || baseClassname,
|
||||
description: fd?.description || '',
|
||||
description: fd?.description || "",
|
||||
spriteId: dbItem?.spriteId || 0,
|
||||
dims: conversion.dimensions || { x: 1, y: 1, z: 1 },
|
||||
metadata: conversion.metadata || null,
|
||||
habboIt: fd || null,
|
||||
})
|
||||
furniDataEntries.push({ entry, itemType })
|
||||
fdClassnames.add(baseClassname)
|
||||
});
|
||||
furniDataEntries.push({ entry, itemType });
|
||||
fdClassnames.add(baseClassname);
|
||||
}
|
||||
|
||||
succeeded++
|
||||
send({ type: 'regen_progress', classname: item.classname, status: 'done' })
|
||||
succeeded++;
|
||||
send({ type: "regen_progress", classname: item.classname, status: "done" });
|
||||
} catch (err) {
|
||||
failed++
|
||||
failed++;
|
||||
send({
|
||||
type: 'regen_progress',
|
||||
type: "regen_progress",
|
||||
classname: item.classname,
|
||||
status: 'failed',
|
||||
status: "failed",
|
||||
message: (err as Error).message,
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
await Promise.allSettled(promises)
|
||||
await Promise.allSettled(promises);
|
||||
}
|
||||
|
||||
// Batch write FurnitureData.json
|
||||
if (furniDataEntries.length > 0) {
|
||||
try {
|
||||
await appendFurniEntriesBatch(furniDataEntries)
|
||||
await appendFurniEntriesBatch(furniDataEntries);
|
||||
send({
|
||||
type: 'regen_info',
|
||||
type: "regen_info",
|
||||
message: `Added ${furniDataEntries.length} entries to FurnitureData.json`,
|
||||
})
|
||||
});
|
||||
} catch (err) {
|
||||
send({
|
||||
type: 'regen_info',
|
||||
type: "regen_info",
|
||||
message: `FurnitureData.json write failed: ${(err as Error).message}`,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// RCON refresh
|
||||
try {
|
||||
await rcon.updateItems()
|
||||
await rcon.updateCatalog()
|
||||
await rcon.updateItems();
|
||||
await rcon.updateCatalog();
|
||||
} catch {
|
||||
/* non-fatal */
|
||||
}
|
||||
|
||||
send({
|
||||
type: 'regen_complete',
|
||||
type: "regen_complete",
|
||||
succeeded,
|
||||
failed,
|
||||
skipped,
|
||||
furniDataAdded: furniDataEntries.length,
|
||||
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",
|
||||
},
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user