Add updateExisting option to furniture import

- Added updateExisting parameter to importSingleFurni and API routes
- When updateExisting=true, existing items are updated (catalog price/page) instead of failing
- Added catalogUpdated flag to ImportSingleResult
- Updates existing catalog entries (price, page) instead of skipping duplicates
This commit is contained in:
openhands committed 2026-08-14 18:35:00 +02:00
1 parent f89b8a6adf
commit 7ef5038a9e
3 files changed
+48 -15

No files matched your search

+3
View File
@@ -297,6 +297,7 @@ export const POST = withAdmin(
type: item.type ?? "flooritem",
revision: item.revision ?? 0,
category: item.category ?? "unknown",
updateExisting: body.updateExisting === true,
sourceSwfBaseUrl: source?.sourceSwfBaseUrl,
nitroBaseUrl: source?.nitroBaseUrl || undefined,
iconBaseUrl: source?.iconBaseUrl || undefined,
@@ -359,6 +360,7 @@ export const POST = withAdmin(
revision,
category,
sourceId,
updateExisting,
} = body;
if (!classname || !name) return apiError("Missing classname or name", 400);
@@ -374,6 +376,7 @@ export const POST = withAdmin(
type: type ?? "flooritem",
revision: revision ?? 0,
category: category ?? "unknown",
updateExisting: updateExisting === true,
sourceSwfBaseUrl: source?.sourceSwfBaseUrl,
nitroBaseUrl: source?.nitroBaseUrl || undefined,
iconBaseUrl: source?.iconBaseUrl || undefined,
+44 -15
View File
@@ -424,6 +424,7 @@ export async function importSingleFurni(params: {
revision: number;
category: string;
skipFurniDataWrite?: boolean;
updateExisting?: boolean;
onProgress?: (status: string) => void;
/** Per-source SWF download base URL (e.g. "https://virtualc.nl/dcr"). */
sourceSwfBaseUrl?: string;
@@ -440,6 +441,7 @@ export async function importSingleFurni(params: {
revision,
category,
skipFurniDataWrite,
updateExisting,
onProgress,
sourceSwfBaseUrl,
nitroBaseUrl: sourceNitroBaseUrl,
@@ -454,28 +456,34 @@ export async function importSingleFurni(params: {
.where(eq(ItemsBase.itemName, classname))
.limit(1);
if (existsByName) {
return {
ok: false,
warnings,
error: `Item "${classname}" already exists (id: ${existsByName.id})`,
};
if (!updateExisting) {
return {
ok: false,
warnings,
error: `Item "${classname}" already exists (id: ${existsByName.id})`,
};
}
// Update existing item - fall through to update logic
}
const [existsById] = await db
.select({ itemName: ItemsBase.itemName })
.from(ItemsBase)
.where(eq(ItemsBase.id, originalId))
.limit(1);
if (existsById) {
return {
ok: false,
warnings,
error: `spriteId ${originalId} already used by "${existsById.itemName}"`,
};
if (existsById && (!existsByName || existsById.itemName !== classname)) {
if (!updateExisting) {
return {
ok: false,
warnings,
error: `spriteId ${originalId} already used by "${existsById.itemName}"`,
};
}
// Update existing item - fall through to update logic
}
// ── Check if spriteId already used in FurnitureData.json ──
const takenBy = await findFurniDataIdConflict(originalId, classname);
if (takenBy) {
if (takenBy && !updateExisting) {
return {
ok: false,
warnings,
@@ -824,6 +832,7 @@ export async function importSingleFurni(params: {
// ── Create catalog entry (with duplicate check + category sub-page) ──
let catalogItemId: number | null = null;
let catalogUpdated = false;
try {
const itemIdStr = String(spriteId);
@@ -834,9 +843,28 @@ export async function importSingleFurni(params: {
if (existingCatalog.length > 0) {
catalogItemId = existingCatalog[0].id;
warnings.push(
`Item already in catalog (catalog_item #${catalogItemId}, page ${existingCatalog[0].page_id}) — skipped duplicate`,
);
if (updateExisting) {
// Update existing catalog entry: price, page, etc.
const categoryPageId = await getOrCreateCategoryPage(
classname,
itemType,
);
const pageIdStr = String(categoryPageId);
const price = autoPriceFurni(classname);
await db.execute(sql`
UPDATE catalog_items
SET page_id = ${pageIdStr}, cost_credits = ${price.credits}, cost_points = ${price.points}, points_type = ${price.pointsType}
WHERE id = ${catalogItemId}
`);
catalogUpdated = true;
warnings.push(
`Item updated in catalog (catalog_item #${catalogItemId}, page ${categoryPageId})`,
);
} else {
warnings.push(
`Item already in catalog (catalog_item #${catalogItemId}, page ${existingCatalog[0].page_id}) — skipped duplicate`,
);
}
} else {
// Get or create the right category sub-page based on classname
const categoryPageId = await getOrCreateCategoryPage(classname, itemType);
@@ -861,6 +889,7 @@ export async function importSingleFurni(params: {
ok: true,
itemId: spriteId,
catalogItemId,
catalogUpdated,
dimensions: dims,
spriteCount: conversionResult?.spriteCount,
warnings,
+1
View File
@@ -93,6 +93,7 @@ export interface ImportSingleResult {
ok: boolean;
itemId?: number;
catalogItemId?: number | null;
catalogUpdated?: boolean;
dimensions?: { x: number; y: number; z: number };
spriteCount?: number;
warnings: string[];