← back to Dw Yolo Loop
scripts/no-image-scan/no-image-scan.mjs
20 lines
import fs from 'fs';
const T=(fs.readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1];
const API='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
async function gql(q,v){for(let a=0;a<6;a++){const r=await fetch(API,{method:'POST',headers:{'X-Shopify-Access-Token':T,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v||{}})});if(r.status===429){await sleep(2500);continue;}const j=await r.json();if(j.errors&&JSON.stringify(j.errors).match(/THROTTLED/)){await sleep(2500);continue;}return j;}throw new Error('throttled');}
let cur=null,total=0,withImg=0,noImg=0,pages=0; const byVendor={}, samples=[];
const t0=Date.now();
do{
const j=await gql(`query($c:String){products(first:100,after:$c,query:"status:active"){pageInfo{hasNextPage endCursor} nodes{handle vendor images(first:1){nodes{id}}}}}`,{c:cur});
const pg=j.data.products;
for(const n of pg.nodes){ total++; if(n.images.nodes.length===0){ noImg++; byVendor[n.vendor]=(byVendor[n.vendor]||0)+1; if(samples.length<12)samples.push(n.handle+' ['+n.vendor+']'); } else withImg++; }
pages++; if(pages%150===0)process.stderr.write(` ...${pages}p ${total} prod, noImg=${noImg}\n`);
cur=pg.pageInfo.hasNextPage?pg.pageInfo.endCursor:null;
}while(cur);
const out={total, withImg, noImg, pct:total?+(100*noImg/total).toFixed(2):0, byVendor, samples, elapsed_s:((Date.now()-t0)/1000)|0};
fs.writeFileSync(process.env.HOME+'/.claude/yolo-queue/no-image-scan-2026-06-16.json',JSON.stringify(out,null,2));
console.log(`scanned ${total} ACTIVE in ${out.elapsed_s}s | NO IMAGE: ${noImg} (${out.pct}%) | with image: ${withImg}`);
console.log('top vendors w/ no-image:'); Object.entries(byVendor).sort((a,b)=>b[1]-a[1]).slice(0,12).forEach(([k,v])=>console.log(' '+v+' '+k));
console.log('samples:'); samples.forEach(s=>console.log(' '+s));