[object Object]

← 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

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 →