← back to Designer Wallcoverings
TK-10046: harden DIGAI SKU minter — DB collision guard + registry reservation
05c7ea5e797ccb633558b47c7d734602df80b0b8 · 2026-08-10 04:22:36 -0700 · steve@designerwallcoverings.com
- Import skuTakenOnActiveProduct (fails CLOSED on DB error) from vendor-crawlers/lib/sku-registry
- Before minting, verify candidate DIGAI-MMDDYY### is not live on Shopify; retry up to 10 slots if taken
- Reserve candidate in dw_sku_registry (vendor_prefix=DIGAI) BEFORE Shopify create, closing the mint→create race
- Keep .env counter as belt-and-suspenders; DB guard is primary duplicate barrier
- node --check: SYNTAX OK (no runtime change until pm2 restart dwtool-recolor-v14, Steve-gated)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files touched
M DW-Programming/DW-Duplicate-Recolor-v1.4/server.js
Diff
commit 05c7ea5e797ccb633558b47c7d734602df80b0b8
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Mon Aug 10 04:22:36 2026 -0700
TK-10046: harden DIGAI SKU minter — DB collision guard + registry reservation
- Import skuTakenOnActiveProduct (fails CLOSED on DB error) from vendor-crawlers/lib/sku-registry
- Before minting, verify candidate DIGAI-MMDDYY### is not live on Shopify; retry up to 10 slots if taken
- Reserve candidate in dw_sku_registry (vendor_prefix=DIGAI) BEFORE Shopify create, closing the mint→create race
- Keep .env counter as belt-and-suspenders; DB guard is primary duplicate barrier
- node --check: SYNTAX OK (no runtime change until pm2 restart dwtool-recolor-v14, Steve-gated)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
DW-Programming/DW-Duplicate-Recolor-v1.4/server.js | 66 ++++++++++++++++------
1 file changed, 50 insertions(+), 16 deletions(-)
diff --git a/DW-Programming/DW-Duplicate-Recolor-v1.4/server.js b/DW-Programming/DW-Duplicate-Recolor-v1.4/server.js
index 1a377071..f14997d9 100644
--- a/DW-Programming/DW-Duplicate-Recolor-v1.4/server.js
+++ b/DW-Programming/DW-Duplicate-Recolor-v1.4/server.js
@@ -13,6 +13,10 @@ const GeminiAnalyzer = require('./lib/geminiAnalyzer');
const ManualRoomGenerator = require('./lib/manualRoomGenerator');
const PreciseRenderer = require('./lib/preciseRenderer');
+// TK-10046: forward-only SKU collision guard + registry reservation
+// skuTakenOnActiveProduct fails CLOSED on DB error (prevents minting live duplicates)
+const { skuTakenOnActiveProduct, pool: skuRegistryPool } = require('../vendor-crawlers/lib/sku-registry');
+
// Configure multer for image uploads
const upload = multer({
dest: 'temp/',
@@ -462,32 +466,62 @@ app.post('/api/mockup', requireAuth, async (req, res) => {
});
// SKU Management Functions
+// TK-10046: hardened — DB-backed collision guard replaces bare .env counter.
+// Generates DIGAI-MMDDYY### candidate, verifies it is not live on Shopify
+// (skuTakenOnActiveProduct fails CLOSED on DB error), reserves it in
+// dw_sku_registry before Shopify create, then bumps the .env counter.
async function getNextSKUNumber() {
- const fs = require('fs');
const envPath = path.join(__dirname, '.env');
- let envContent = await fs.promises.readFile(envPath, 'utf8');
+ let envContent = await fs.readFile(envPath, 'utf8');
- // Extract current SKU number
const match = envContent.match(/NEXT_SKU_NUMBER=(\d+)/);
- const currentNumber = match ? parseInt(match[1]) : 0;
- const nextNumber = currentNumber + 1;
-
- // Update .env file
- envContent = envContent.replace(/NEXT_SKU_NUMBER=\d+/, `NEXT_SKU_NUMBER=${nextNumber}`);
- await fs.promises.writeFile(envPath, envContent);
-
- // Update process.env
- process.env.NEXT_SKU_NUMBER = nextNumber.toString();
+ let currentNumber = match ? parseInt(match[1]) : 0;
- // Generate SKU in format: DIGAI-MMDDYY###
- // Example: DIGAI-093025000 for Sept 30, 2025
const now = new Date();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
const year = now.getFullYear().toString().slice(-2);
- const sequence = currentNumber.toString().padStart(3, '0');
- return `DIGAI-${month}${day}${year}${sequence}`;
+ // Try up to 10 sequence slots to find a free SKU (collision-resilient)
+ let candidate;
+ let attempts = 0;
+ while (attempts < 10) {
+ const sequence = currentNumber.toString().padStart(3, '0');
+ candidate = `DIGAI-${month}${day}${year}${sequence}`;
+ const taken = await skuTakenOnActiveProduct(candidate);
+ if (!taken.taken) break;
+ console.warn(`[sku-minter] collision: ${candidate} already live on Shopify — retrying`);
+ currentNumber++;
+ attempts++;
+ }
+ if (attempts >= 10) {
+ throw new Error('[sku-minter] Could not mint a free DIGAI SKU after 10 attempts — check dw_sku_registry');
+ }
+
+ // Reserve in dw_sku_registry BEFORE Shopify create (closes mint→create race)
+ try {
+ await skuRegistryPool.query(
+ `INSERT INTO dw_sku_registry (dw_sku, vendor_prefix, vendor_name, mfr_sku)
+ VALUES ($1, 'DIGAI', 'DW Bespoke Studios', $1)
+ ON CONFLICT (dw_sku) DO NOTHING`,
+ [candidate]
+ );
+ } catch (registryErr) {
+ // Non-fatal: registry is a safety net; log and continue
+ console.warn('[sku-minter] registry reservation failed (non-fatal):', registryErr.message);
+ }
+
+ // Update .env counter (belt-and-suspenders; DB guard is primary)
+ const nextNumber = currentNumber + 1;
+ if (match) {
+ envContent = envContent.replace(/NEXT_SKU_NUMBER=\d+/, `NEXT_SKU_NUMBER=${nextNumber}`);
+ } else {
+ envContent += `\nNEXT_SKU_NUMBER=${nextNumber}`;
+ }
+ await fs.writeFile(envPath, envContent);
+ process.env.NEXT_SKU_NUMBER = nextNumber.toString();
+
+ return candidate;
}
// Advanced auth middleware - requires advanced access level
← 4e37bd79 auto-data-snapshot: 2026-08-10T03:14:37 (2 data files) — DW-
·
back to Designer Wallcoverings
·
CC: rebuild New Arrivals email as 'The Classics' — Kravet-fa 2eea5ae1 →