← back to Designer Wallcoverings
no-image backfill: attach 18 verified vendor images (src + base64-retry for octet-stream/oversized CDNs) + Nantucket description
770e4cf511c648d50ac962df512d8a6d567e04c2 · 2026-07-22 06:51:58 -0700 · Steve
Files touched
A scripts/wallquest-refresh/attach-14-retry-b64.mjs
Diff
commit 770e4cf511c648d50ac962df512d8a6d567e04c2
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jul 22 06:51:58 2026 -0700
no-image backfill: attach 18 verified vendor images (src + base64-retry for octet-stream/oversized CDNs) + Nantucket description
---
scripts/wallquest-refresh/attach-14-retry-b64.mjs | 55 +++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/scripts/wallquest-refresh/attach-14-retry-b64.mjs b/scripts/wallquest-refresh/attach-14-retry-b64.mjs
new file mode 100644
index 00000000..922abbd4
--- /dev/null
+++ b/scripts/wallquest-refresh/attach-14-retry-b64.mjs
@@ -0,0 +1,55 @@
+#!/usr/bin/env node
+// Retry the 14 attaches that failed the src-URL path: Brewster/azureedge serve
+// image/jpeg under content-type application/octet-stream (Shopify's URL fetcher
+// rejects it), and Kravet Brandfolder originals exceed Shopify's 20MB limit.
+// Fix: download locally (Kravet via ?width=2000), upload as a base64 attachment
+// (bypasses content-type sniffing + we control the size). Verified-colorway set
+// only (from /tmp/noimg-verified.json), aura-light-gray still held.
+import { execSync } from 'node:child_process';
+import { readFileSync } from 'node:fs';
+import https from 'node:https';
+
+const TOK = process.env.SHOPIFY_ADMIN_TOKEN;
+if (!TOK) { console.error('SHOPIFY_ADMIN_TOKEN missing'); process.exit(1); }
+const SKIP = new Set(['7876263313459']); // aura: colorway mismatch, held
+const DONE = new Set(['7513505431603', '7878219530291', '7878240665651']); // already attached in pass 1
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+function get(url) {
+ return new Promise((res, rej) => {
+ https.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }, r => {
+ if (r.statusCode >= 300 && r.statusCode < 400 && r.headers.location) return res(get(r.headers.location));
+ if (r.statusCode !== 200) return rej(new Error('HTTP ' + r.statusCode));
+ const chunks = []; r.on('data', c => chunks.push(c)); r.on('end', () => res(Buffer.concat(chunks)));
+ }).on('error', rej);
+ });
+}
+function rest(method, path, body) {
+ return new Promise((res, rej) => {
+ const data = body ? JSON.stringify(body) : null;
+ const r = https.request({ hostname: 'designer-laboratory-sandbox.myshopify.com', path: `/admin/api/2024-10/${path}`, method,
+ headers: { 'X-Shopify-Access-Token': TOK, 'Content-Type': 'application/json', ...(data ? { 'Content-Length': Buffer.byteLength(data) } : {}) } },
+ rs => { let d = ''; rs.on('data', c => d += c); rs.on('end', () => { try { res({ status: rs.statusCode, body: d ? JSON.parse(d) : {} }); } catch (e) { res({ status: rs.statusCode, body: d }); } }); });
+ r.on('error', rej); if (data) r.write(data); r.end();
+ });
+}
+const sizedUrl = u => u.includes('cdn.brandfolder.io') ? (u.includes('?') ? u + '&width=2000' : u + '?width=2000') : u;
+
+const verified = JSON.parse(readFileSync('/tmp/noimg-verified.json', 'utf8'));
+const todo = verified.filter(p => !SKIP.has(String(p.pid)) && !DONE.has(String(p.pid)));
+let ok = 0, fail = 0;
+for (const p of todo) {
+ try {
+ const buf = await get(sizedUrl(p.src.url));
+ if (buf.length > 20 * 1024 * 1024) { fail++; console.log(`SKIP ${p.pid} still >20MB (${(buf.length/1e6).toFixed(1)}MB)`); continue; }
+ const filename = (p.src.match || String(p.pid)).replace(/[^A-Za-z0-9._-]/g, '_') + '.jpg';
+ const r = await rest('POST', `products/${p.pid}/images.json`, { image: { attachment: buf.toString('base64'), filename, alt: p.title } });
+ if (r.status < 300 && r.body.image) {
+ const cdn = r.body.image.src.replace(/'/g, "''");
+ execSync(`psql "host=/tmp dbname=dw_unified" -X -q -c "UPDATE shopify_products SET image_url='${cdn}' WHERE shopify_id='gid://shopify/Product/${p.pid}';"`);
+ ok++; console.log(`OK ${p.pid} ${p.vendor} ${p.src.match} (${(buf.length/1e6).toFixed(1)}MB) image_id=${r.body.image.id}`);
+ } else { fail++; console.log(`FAIL ${p.pid} status=${r.status} ${JSON.stringify(r.body).slice(0, 140)}`); }
+ } catch (e) { fail++; console.log(`FAIL ${p.pid} ${String(e).slice(0, 100)}`); }
+ await sleep(800);
+}
+console.log(`\nretry: attached=${ok} fail=${fail}`);
← 871d8d35 auto-save: 2026-07-22T06:41:50 (1 files) — scripts/wallquest
·
back to Designer Wallcoverings
·
Anthology Phase-2 scrapers: SDG Trade Hub feed-first pull → 35503bb0 →