← back to Dw Image Shrink
TK-10048 COMPLETE: ~33GB reclaimed (6893 imgs + 61 vids), 409-sweep + verification scripts, 0 blanked
2305bbf70a4c7f2dd59fa3afed683f1cf6ad0625 · 2026-07-30 19:45:12 -0700 · Steve Abrams
Files touched
A finalize-cleanup.jsA verify-blanked.js
Diff
commit 2305bbf70a4c7f2dd59fa3afed683f1cf6ad0625
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 30 19:45:12 2026 -0700
TK-10048 COMPLETE: ~33GB reclaimed (6893 imgs + 61 vids), 409-sweep + verification scripts, 0 blanked
---
finalize-cleanup.js | 34 ++++++++++++++++++++++++++++++++++
verify-blanked.js | 24 ++++++++++++++++++++++++
2 files changed, 58 insertions(+)
diff --git a/finalize-cleanup.js b/finalize-cleanup.js
new file mode 100644
index 0000000..8f5e54b
--- /dev/null
+++ b/finalize-cleanup.js
@@ -0,0 +1,34 @@
+// Clean up the 409-race stragglers correctly.
+// Two classes among the reset-to-pending rows:
+// (A) delete_409 dups: new_image_id IS NOT NULL → new copy already live, original leftover.
+// Verify new is live, delete the leftover ORIGINAL, mark done. (Do NOT re-encode.)
+// (B) upload_409: new_image_id IS NULL → original intact, no new copy.
+// Leave pending; the worker re-run handles these.
+import fs from 'fs';
+import pg from 'pg';
+const SHOP='designer-laboratory-sandbox.myshopify.com';
+const ATOK=fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env','utf8').split('\n').find(l=>l.startsWith('SHOPIFY_ADMIN_TOKEN=')).split('=').slice(1).join('=').replace(/["' ]/g,'');
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+async function api(m,p){const r=await fetch(`https://${SHOP}/admin/api/2024-10/${p}`,{method:m,headers:{'X-Shopify-Access-Token':ATOK}});await sleep(300);return {status:r.status,json:await r.json().catch(()=>({}))};}
+const db=new pg.Client({host:'/tmp',database:'dw_unified'}); await db.connect();
+const {rows}=await db.query("SELECT id,product_id,orig_image_id,new_image_id FROM img_shrink_ledger WHERE state='pending' AND new_image_id IS NOT NULL");
+console.log(`delete_409 dup rows to resolve: ${rows.length}`);
+let cleaned=0, retry=0, kept=0;
+for(const r of rows){
+ const pr=await api('GET',`products/${r.product_id}.json?fields=images`);
+ const ids=(pr.json.product?.images||[]).map(x=>String(x.id));
+ const newLive=ids.includes(String(r.new_image_id));
+ const origLive=ids.includes(String(r.orig_image_id));
+ if(newLive && origLive){
+ const d=await api('DELETE',`products/${r.product_id}/images/${r.orig_image_id}.json`);
+ if(d.status===200){ await db.query("UPDATE img_shrink_ledger SET state='done',fail_reason=NULL WHERE id=$1",[r.id]); cleaned++; console.log(` ✓ ${r.product_id} deleted leftover original → done`); }
+ else { console.log(` ⚠ ${r.product_id} delete HTTP ${d.status} — leaving pending`); }
+ } else if(newLive && !origLive){
+ await db.query("UPDATE img_shrink_ledger SET state='done',fail_reason=NULL WHERE id=$1",[r.id]); kept++; console.log(` ✓ ${r.product_id} already clean (only new live) → done`);
+ } else { // new not live → true retry
+ await db.query("UPDATE img_shrink_ledger SET new_image_id=NULL WHERE id=$1",[r.id]); retry++; console.log(` ↻ ${r.product_id} new not live → reset for re-encode`);
+ }
+}
+const {rows:[c]}=await db.query("SELECT count(*) FILTER(WHERE state='pending') pending, count(*) FILTER(WHERE state='done') done FROM img_shrink_ledger");
+console.log(`\ndup cleaned=${cleaned} already-clean=${kept} reset-for-retry=${retry} | ledger now: ${c.pending} pending, ${c.done} done`);
+await db.end();
diff --git a/verify-blanked.js b/verify-blanked.js
new file mode 100644
index 0000000..7b9025f
--- /dev/null
+++ b/verify-blanked.js
@@ -0,0 +1,24 @@
+// Robust "0 blanked" check: sample N done-products spread across the whole run,
+// confirm each still has >=1 live image on Shopify. Read-only.
+import fs from 'fs';
+import pg from 'pg';
+const SHOP='designer-laboratory-sandbox.myshopify.com';
+const ATOK=fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env','utf8').split('\n').find(l=>l.startsWith('SHOPIFY_ADMIN_TOKEN=')).split('=').slice(1).join('=').replace(/["' ]/g,'');
+const N=+(process.argv[2]||100);
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+const db=new pg.Client({host:'/tmp',database:'dw_unified'}); await db.connect();
+// spread the sample across the id range (start, middle, end of the run)
+const {rows}=await db.query("SELECT product_id FROM img_shrink_ledger WHERE state='done' ORDER BY (id % 97), id LIMIT $1",[N]);
+let blanked=0, ok=0, err=0;
+for(const r of rows){
+ try{
+ const res=await fetch(`https://${SHOP}/admin/api/2024-10/products/${r.product_id}.json?fields=id,status,images`,{headers:{'X-Shopify-Access-Token':ATOK}});
+ await sleep(120);
+ const p=(await res.json()).product;
+ const n=(p?.images||[]).length;
+ if(n===0){ blanked++; console.log(` BLANKED product ${r.product_id} (status ${p?.status})`); } else ok++;
+ }catch(e){ err++; }
+}
+console.log(`\nchecked ${rows.length} done-products spread across the run | with-image=${ok} | BLANKED=${blanked} | err=${err}`);
+console.log(blanked===0 ? 'VERDICT: 0 blanked across the broad sample — upload-first integrity holds.' : `VERDICT: ${blanked} BLANKED — investigate.`);
+await db.end();
← 0bbfc4d auto-save: 2026-07-30T15:49:17 (1 files) — verify-urlgap.js
·
back to Dw Image Shrink
·
chore: lint ✓ (0 fixes needed), v1.1.0 (video-shrink pipelin bdffd8b →