diff --git a/src/lib/services/furni-import.ts b/src/lib/services/furni-import.ts index 6e667e34..392f75db 100644 --- a/src/lib/services/furni-import.ts +++ b/src/lib/services/furni-import.ts @@ -126,6 +126,29 @@ export function buildItemsBaseEnrichmentUpdate(params: { `; } +/** Keep the lowest-id row for a catalog page and delete any duplicate created + * concurrently by a racing import. Returns the surviving page id. */ +async function dedupeCatalogPage( + captionSave: string, + parentId: number, + justCreatedId: number, +): Promise { + const [rows] = (await db.execute(sql` + SELECT MIN(id) AS id FROM catalog_pages + WHERE caption_save = ${captionSave} AND parent_id = ${parentId} + `)) as unknown as [Array<{ id: number }>, unknown]; + const winner = rows[0]?.id ?? justCreatedId; + if (winner !== justCreatedId) { + // Never delete a page that already received catalog items. + await db.execute(sql` + DELETE FROM catalog_pages + WHERE id = ${justCreatedId} + AND NOT EXISTS (SELECT 1 FROM catalog_items WHERE page_id = ${justCreatedId}) + `); + } + return winner; +} + /** Get or create the root "Imported Furniture" parent page (no items directly on this page). * Caches the page ID in-memory for the process lifetime to avoid redundant DB reads * during batch imports */ @@ -158,8 +181,12 @@ export async function getOrCreateImportedParentPage(): Promise { 'Failed to create "Imported Furniture" parent catalog page', ); } - parentIdCache = created.id; - return created.id; + parentIdCache = await dedupeCatalogPage( + IMPORTED_PAGE_CAPTION_SAVE, + -1, + created.id, + ); + return parentIdCache; } /** Determine the catalog category for a furniture item based on its classname and type. */ @@ -246,8 +273,9 @@ export async function getOrCreateCategoryPage( ); } if (!categoryPageCache) categoryPageCache = new Map(); - categoryPageCache.set(captionSave, created.id); - return created.id; + const winner = await dedupeCatalogPage(captionSave, parentId, created.id); + categoryPageCache.set(captionSave, winner); + return winner; } export function resetCatalogPageCache(): void {