Reconcile offer_id after every import
CI / check (push) Successful in 40s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m12s

After each single or batch import, walk all catalog items under the
Imported Furniture tree and set offer_id to the furnidata sprite id,
repairing any stale values (e.g. legacy -1 rows). Reports how many
rows were fixed in the API response.
This commit is contained in:
openhands committed 2026-08-18 19:40:17 +02:00
1 parent 305a0c42ff
commit c95ad4a37f
2 files changed
+35 -3

No files matched your search

+16 -3
View File
@@ -19,6 +19,7 @@ import {
ensureDirectories,
IMPORTED_PAGE_CAPTION_SAVE,
importSingleFurni,
reconcileImportedOfferIds,
resetCatalogPageCache,
} from "@/lib/services/furni-import";
import { getOfficialHabboFurnidata } from "@/lib/services/habbo-furnidata-cache";
@@ -403,10 +404,19 @@ export const POST = withAdmin(
);
}
// Reconcile offer_id with the furnidata sprite id across the tree
const reconciled = await reconcileImportedOfferIds();
const succeeded = results.filter((r) => r.ok).length;
const failed = results.filter((r) => !r.ok).length;
return apiOk({ batch: true, succeeded, failed, results });
return apiOk({
batch: true,
succeeded,
failed,
results,
offerIdsFixed: reconciled.fixed,
});
}
// ── Single import ────────────────────────────────────────────────
@@ -455,6 +465,9 @@ export const POST = withAdmin(
result.warnings.push("RCON cache refresh failed");
}
// Reconcile offer_id with the furnidata sprite id across the tree
const reconciled = await reconcileImportedOfferIds();
logAudit({
userId: ctx.session.user.id,
action: "furni_import",
@@ -475,9 +488,9 @@ export const POST = withAdmin(
return apiOk({
id: result.itemId,
catalogItemId: result.catalogItemId,
dimensions: result.dimensions,
offerIdsFixed: reconciled.fixed,
spriteCount: result.spriteCount,
warnings: result.warnings.length > 0 ? result.warnings : undefined,
warnings: result.warnings,
});
},
);
+19
View File
@@ -284,6 +284,25 @@ export function resetCatalogPageCache(): void {
categoryCacheTimestamp = 0;
}
/**
* Walk every catalog item under the "Imported Furniture" tree and make its
* offer_id match the furnidata sprite id (item_ids). Fixes any stale values
* (e.g. legacy -1 rows) after an import.
*/
export async function reconcileImportedOfferIds(): Promise<{
fixed: number;
}> {
const parentId = await getOrCreateImportedParentPage();
const [result] = (await db.execute(sql`
UPDATE catalog_items ci
JOIN catalog_pages cp ON cp.id = ci.page_id
SET ci.offer_id = CAST(ci.item_ids AS UNSIGNED)
WHERE cp.parent_id = ${parentId}
AND ci.offer_id <> CAST(ci.item_ids AS UNSIGNED)
`)) as unknown as [Record<string, unknown>, unknown];
return { fixed: Number(result.affectedRows ?? 0) };
}
/**
* Allocate the next `catalog_items.id` and run the INSERT through a
* process-level async mutex so concurrent imports in the same Node process