← back to Tk10661 Product Seo

rollback-all.mjs

19 lines

// TK-10661 ROLLBACK: restore ORIGINAL variant positions from targets.jsonl for products in
// the done-log. DRY-RUN default; --apply writes. Undoes reorder-all.
import {readFileSync, existsSync} from 'node:fs';
const txt=readFileSync(`${process.env.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, TOKEN=env.SHOPIFY_FULL_ACCESS_TOKEN, API='2024-10';
const APPLY=process.argv.includes('--apply');
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
async function gql(q,v={}){for(let a=0;a<10;a++){try{const r=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 j=await r.json();if(j.errors){if(/THROTTLED/i.test(JSON.stringify(j.errors))){await sleep(2500*(a+1));continue;}throw new Error(JSON.stringify(j.errors));}return j;}catch(e){if(a<9){await sleep(1500*(a+1));continue;}throw e;}}}
const TFILE=process.argv.find(a=>a.endsWith('.jsonl'))||'data/targets.jsonl';
const targets=new Map(readFileSync(TFILE,'utf8').trim().split('\n').filter(Boolean).map(l=>{const t=JSON.parse(l);return [t.id,t];}));
const done=existsSync('data/done-reorder.jsonl')?readFileSync('data/done-reorder.jsonl','utf8').trim().split('\n').filter(Boolean).map(JSON.parse).filter(x=>x.ok):[];
const MUT=`mutation($pid:ID!,$pos:[ProductVariantPositionInput!]!){ productVariantsBulkReorder(productId:$pid,positions:$pos){ userErrors{message} } }`;
let restored=0,err=0;
for(const d of done){ const t=targets.get(d.id); if(!t) continue;
  const positions=t.variants.map(v=>({id:v.id,position:v.position}));
  if(APPLY){ const res=await gql(MUT,{pid:d.id,pos:positions}); if(res.data?.productVariantsBulkReorder?.userErrors?.length) err++; else restored++; await sleep(220);} }
console.log(JSON.stringify({mode:APPLY?'APPLY':'DRY-RUN',in_done_log:done.length,restored,errors:err},null,2));