← back to Tk10640 Redirect Prune
verify-applied-targets.mjs
29 lines
// TK-10653 — verify every APPLIED repoint's target is STILL a live ACTIVE+published product.
// Catalog churn between scan and apply can leave a repoint pointing at a now-dead product
// (301 fires -> target 404). For each done repoint, re-check productByHandle liveness.
// Emits data/full-scan/applied-dead-targets.json (rows to re-prune). READ-ONLY.
import { readFileSync, writeFileSync } from 'node:fs';
const HOME=process.env.HOME;
const txt=readFileSync(`${HOME}/Projects/secrets-manager/.env`,'utf8');
const env={}; for(const l of txt.split('\n')){const m=l.match(/^([A-Z0-9_]+)=(.*)$/); if(m) env[m[1]]=m[2].replace(/^["']|["']$/g,'');}
const STORE=env.SHOPIFY_STORE_DOMAIN||'designer-laboratory-sandbox.myshopify.com', TOKEN=env.SHOPIFY_FULL_ACCESS_TOKEN, API='2024-10';
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
async function gql(q,v={}){for(let a=0;a<12;a++){try{
const res=await fetch(`https://${STORE}/admin/api/${API}/graphql.json`,{method:'POST',headers:{'Content-Type':'application/json','X-Shopify-Access-Token':TOKEN},body:JSON.stringify({query:q,variables:v})});
const tx=await res.text();let j;try{j=JSON.parse(tx);}catch{await sleep(1200*(a+1));continue;}
if(j.errors){if(/THROTTLED/i.test(JSON.stringify(j.errors))){await sleep(1200*(a+1));continue;}throw new Error(JSON.stringify(j.errors));}
return j.data;
}catch(e){if(a<11){await sleep(1200*(a+1));continue;}throw e;}}}
const applied=readFileSync(`${HOME}/Projects/tk10640-redirect-prune/data/full-scan/done-remediation.jsonl`,'utf8').trim().split('\n').filter(Boolean).map(JSON.parse).filter(x=>x.repointed);
const dead=[]; let live=0,i=0;
for(const a of applied){
const h=a.repointed.replace('/products/','').split('?')[0].split('#')[0];
const d=await gql(`query($h:String!){productByHandle(handle:$h){status onlineStoreUrl}}`,{h});
const p=d.productByHandle;
if(p&&p.status==='ACTIVE'&&p.onlineStoreUrl) live++;
else dead.push({id:a.id,path:a.path,repointed:a.repointed});
if(++i%200===0) process.stderr.write(` ${i}/${applied.length} live=${live} dead=${dead.length}\n`);
}
writeFileSync(`${HOME}/Projects/tk10640-redirect-prune/data/full-scan/applied-dead-targets.json`, JSON.stringify(dead,null,1));
console.log(JSON.stringify({applied_repoints:applied.length, target_still_live:live, target_now_dead_reprune:dead.length},null,2));