← back to All Designerwallcoverings
Crawler: derive membership handle + title from mfr_sku/pattern_name when feed omits them (fixes gracie/zuber 0-count)
26c1985284bc3a440e5ed08dcd9ef03cba20ff79 · 2026-07-14 09:48:55 -0700 · Steve Abrams
Files touched
M config/known-subdomains.jsonM scripts/crawl-microsites.js
Diff
commit 26c1985284bc3a440e5ed08dcd9ef03cba20ff79
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jul 14 09:48:55 2026 -0700
Crawler: derive membership handle + title from mfr_sku/pattern_name when feed omits them (fixes gracie/zuber 0-count)
---
config/known-subdomains.json | 8 ++++----
scripts/crawl-microsites.js | 14 ++++++++++----
2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/config/known-subdomains.json b/config/known-subdomains.json
index 9bff674..2ca5b3f 100644
--- a/config/known-subdomains.json
+++ b/config/known-subdomains.json
@@ -6,7 +6,7 @@
"vendor": "Artmura",
"note": "LIVE reference build for the dw-vendor-landing template",
"hasMicrosite": true,
- "internalViewer": "http://127.0.0.1:9948 (~/Projects/artmura-internal, pm2 artmura-internal)"
+ "internalViewer": "http://127.0.0.1:9931 (~/Projects/artmura-site, pm2 artmura-site)"
},
{
"slug": "astek",
@@ -22,7 +22,7 @@
"knownProject": "~/Projects/reidwitlin-landing",
"note": "Local project built; deploy status tracked by probe",
"hasMicrosite": true,
- "internalViewer": "http://127.0.0.1:9945 (pm2 reidwitlin-landing)"
+ "internalViewer": "http://127.0.0.1:9952 (~/Projects/reidwitlin-landing, pm2 reidwitlin-landing)"
},
{
"slug": "quadrille",
@@ -62,7 +62,7 @@
"knownProject": "~/Projects/dw-vendor-microsites/vendors/gracie",
"note": "ONBOARDING/STAGING 2026-07-09 — ultra-luxury hand-painted scenic & chinoiserie wallpaper panels (Gracie Studio, est. 1898). 150 patterns in gracie_catalog (feed-first Next.js/Sanity page-data, $0). QUOTE-ONLY bespoke. Shopify DRAFT import gated (DWGA-, MANDATORY settlement-gate — chinoiserie is bird/butterfly/botanical heavy). Microsite deploy + DNS gated.",
"hasMicrosite": true,
- "internalViewer": "http://127.0.0.1:10105 (~/Projects/gracie-internal, pm2 gracie-internal)"
+ "internalViewer": "http://127.0.0.1:10104 (~/Projects/gracie-internal, pm2 gracie-internal)"
},
{
"slug": "zuber",
@@ -70,7 +70,7 @@
"knownProject": "~/Projects/dw-vendor-microsites/vendors/zuber",
"note": "ONBOARDING/STAGING 2026-07-09 — historic French house of hand-block-printed antique scenic/panoramic wallpaper (Zuber & Cie). 192 SKUs in zuber_catalog (feed-first sitemap.xml → plain-fetch pages, $0). QUOTE-ONLY heritage made-to-order. Shopify DRAFT import gated (DWZB-, MANDATORY settlement-gate — scenics are botanical/floral/bird heavy). Microsite deploy + DNS gated.",
"hasMicrosite": true,
- "internalViewer": "http://127.0.0.1:10106 (~/Projects/zuber-internal, pm2 zuber-internal)"
+ "internalViewer": "http://127.0.0.1:10105 (~/Projects/zuber-internal, pm2 zuber-internal)"
},
{
"slug": "crezana",
diff --git a/scripts/crawl-microsites.js b/scripts/crawl-microsites.js
index cb39aea..6fc30f1 100644
--- a/scripts/crawl-microsites.js
+++ b/scripts/crawl-microsites.js
@@ -207,17 +207,23 @@ const abs = (u, host) => {
return `https://${host}${u.startsWith('/') ? '' : '/'}${u}`;
};
+// Derive an internal membership handle even when a feed omits one — internal line
+// viewers (gracie/zuber/crezana family) emit mfr_sku/pattern_name but no handle.
+// Never displayed; membership key only.
+const rowHandle = (p) => p.handle || (p.dw_sku || p.mfr_sku || p.sku ? slugify(p.dw_sku || p.mfr_sku || p.sku) : null);
+
function normalizeRow(p, host) {
const shopify = Array.isArray(p.variants);
+ const handle = rowHandle(p);
return {
- title: clean(p.title || p.display_name || ''),
- handle: p.handle || null,
+ title: clean(p.title || p.display_name || p.pattern_name || ''),
+ handle: handle,
vendor: clean(p.vendor || null),
type: p.product_type || p.series || null,
image: abs(firstImage(p.images) || firstImage(p.image) || p.swatch || null, host),
price: shopify ? (p.variants[0] ? num(p.variants[0].price) : null) : num(p.price),
// Prefer the row's own store link; else the landing's product page.
- url: abs(p.store_url || (p.handle ? `/products/${p.handle}` : null), host),
+ url: abs(p.store_url || (handle ? `/products/${handle}` : null), host),
};
}
@@ -225,7 +231,7 @@ function normalizeRow(p, host) {
// (the membership source the all- server resolves "This Item" against — internal keys,
// never displayed) PLUS a scrubbed ≤24-product sample for the directory UI.
function buildFeedResult(arr, host, reportedTotal, truncated) {
- const handles = [...new Set(arr.map((p) => p.handle).filter(Boolean).map(String))];
+ const handles = [...new Set(arr.map((p) => rowHandle(p)).filter(Boolean).map(String))];
const products = arr
.filter((p) => !BANNED.test(p.title || '') && !BANNED.test(p.vendor || '') && !BANNED.test((p.tags || []).join(' ')))
.slice(0, SAMPLE_PER_SITE)
← 22ee077 Fix crezana internalViewer port 10104->10106; vendor-name fa
·
back to All Designerwallcoverings
·
Contrarian fixes: scope crawler cred to internal hosts only; 7e64421 →