[object Object]

← back to Interiordesignershowroom

Scaled CJ catalog ingest (48 categories, image-required) -> 2697 real products/27 brands incl. appliances; idempotent, swaps in tracked links as advertisers activate

365ba46f74dbaf1947fef00d776c534d980f6e85 · 2026-08-01 20:14:50 -0700 · Steve Abrams

Files touched

Diff

commit 365ba46f74dbaf1947fef00d776c534d980f6e85
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 1 20:14:50 2026 -0700

    Scaled CJ catalog ingest (48 categories, image-required) -> 2697 real products/27 brands incl. appliances; idempotent, swaps in tracked links as advertisers activate
---
 scripts/ingest-cj-catalog.js | 56 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/scripts/ingest-cj-catalog.js b/scripts/ingest-cj-catalog.js
new file mode 100644
index 0000000..1517a11
--- /dev/null
+++ b/scripts/ingest-cj-catalog.js
@@ -0,0 +1,56 @@
+// Scaled CJ catalog ingest for the Room Builder. Pulls a broad, image-required
+// set across furniture / lighting / rugs / decor / appliances, normalizes, and
+// UPSERTs into products. Idempotent (network+external_id key) so re-runs refresh
+// prices AND swap in tracked links as advertisers get joined in CJ.
+//
+// Usage: node scripts/ingest-cj-catalog.js [--per=60] [--only=sofa,desk]
+try { require('dotenv').config(); } catch (_) {}
+const db = require('../lib/db');
+const cj = require('../lib/adapters/cj');
+const { normalizeProduct, UPSERT_SQL, upsertParams } = require('../lib/normalize');
+
+const arg = (n, d) => { const h = process.argv.find(a => a.startsWith(`--${n}=`)); return h ? h.split('=')[1] : d; };
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+// Category -> search keyword. Broad home-furnishing coverage + appliances.
+const CATEGORIES = [
+  'sofa', 'sectional sofa', 'loveseat', 'accent chair', 'recliner', 'ottoman',
+  'coffee table', 'console table', 'side table', 'media console', 'tv stand',
+  'office chair', 'ergonomic office chair', 'standing desk', 'writing desk', 'bookcase',
+  'bed frame', 'upholstered headboard', 'nightstand', 'dresser', 'wardrobe',
+  'dining table', 'dining chair', 'bar stool', 'counter stool', 'sideboard buffet', 'bar cart',
+  'area rug', 'runner rug', 'floor lamp', 'table lamp', 'pendant light', 'chandelier', 'wall sconce',
+  'wall mirror', 'framed wall art', 'decorative vase', 'throw pillow', 'throw blanket', 'wall clock',
+  'planter', 'bookshelf', 'bench', 'vitamix blender', 'stand mixer', 'espresso machine', 'air purifier',
+];
+
+async function main() {
+  await db.query('SELECT 1');
+  if (!cj.enabled(process.env)) { console.error('CJ not configured (need CJ_TOKEN/CJ_COMPANY_ID/CJ_WEBSITE_ID)'); process.exit(1); }
+  const per = parseInt(arg('per', '60'), 10);
+  const only = (arg('only', '') || '').split(',').filter(Boolean);
+  const cats = only.length ? CATEGORIES.filter(c => only.some(o => c.includes(o))) : CATEGORIES;
+
+  let total = 0, withImg = 0, tracked = 0;
+  for (const cat of cats) {
+    let raws = [];
+    try { raws = await cj.fetch(process.env, { limit: per, keywords: cat }); }
+    catch (e) { console.log(`  ${cat}: ERROR ${e.message}`); continue; }
+    let ok = 0;
+    for (const raw of raws) {
+      if (!raw.image_url) continue;           // Room Builder needs images
+      withImg++;
+      if (/cj\.com|dpbolvw|anrdoezrs|kqzyfj|tkqlhce|jdoqocy/.test(raw.affiliate_url || '')) tracked++;
+      const norm = normalizeProduct(raw, 'cj');
+      if (!norm) continue;
+      try { await db.query(UPSERT_SQL, upsertParams(norm)); ok++; total++; } catch (_) {}
+    }
+    console.log(`  ${cat.padEnd(24)} -> ${ok} upserted (of ${raws.length})`);
+    await sleep(400); // pace the API
+  }
+  const { rows } = await db.query(`SELECT count(*) n, count(image_url) img FROM products WHERE network='cj'`);
+  console.log(`\nDONE. this run: ${total} upserted, ${tracked} with tracked links.`);
+  console.log(`catalog now: ${rows[0].n} CJ products (${rows[0].img} with images).`);
+  await db.pool.end();
+}
+main().catch(e => { console.error(e); process.exit(1); });

← 145b8c5 auto-save: 2026-08-01T20:09:07 (1 files) — lib/adapters/cj.j  ·  back to Interiordesignershowroom  ·  Room Builder: tray+GO, collapsed left tabs (trending/style/h 1e3d7b1 →