← back to Dw Vendor Mailer

upload_logos.mjs

59 lines

import fs from 'node:fs';
import path from 'node:path';

const env = fs.readFileSync(path.join(process.env.HOME,'Projects/secrets-manager/.env'),'utf8');
const g = k => (env.match(new RegExp('^'+k+'=(.*)$','m'))||[])[1]?.trim().replace(/^["']|["']$/g,'');
const TOKEN = g('SHOPIFY_FULL_ACCESS_TOKEN') || g('SHOPIFY_ADMIN_TOKEN');
const SHOP = 'designer-laboratory-sandbox.myshopify.com';
const API = '2024-10';
const GQL = `https://${SHOP}/admin/api/${API}/graphql.json`;
const H = { 'X-Shopify-Access-Token': TOKEN, 'Content-Type':'application/json' };

const files = ['thibaut.png','cole-son.png','brunschwig-fils.png','osborne-little-shopify.png','designers-guild.png']
  .map(f => path.join('/tmp/dwlogos', f));

async function gql(query, variables){
  const r = await fetch(GQL,{method:'POST',headers:H,body:JSON.stringify({query,variables})});
  const j = await r.json();
  if(j.errors) throw new Error(JSON.stringify(j.errors));
  return j.data;
}

async function uploadOne(fp){
  const filename = path.basename(fp);
  // 1) staged target
  const d1 = await gql(`mutation($input:[StagedUploadInput!]!){stagedUploadsCreate(input:$input){stagedTargets{url resourceUrl parameters{name value}} userErrors{field message}}}`,
    {input:[{filename, mimeType:'image/png', resource:'FILE', httpMethod:'POST'}]});
  const t = d1.stagedUploadsCreate.stagedTargets[0];
  if(!t) throw new Error('no staged target: '+JSON.stringify(d1));
  // 2) upload to staged url (multipart)
  const form = new FormData();
  for(const p of t.parameters) form.append(p.name, p.value);
  form.append('file', new Blob([fs.readFileSync(fp)],{type:'image/png'}), filename);
  const up = await fetch(t.url,{method:'POST',body:form});
  if(!(up.status>=200 && up.status<300)) throw new Error('staged upload failed '+up.status+' '+await up.text());
  // 3) fileCreate
  const d2 = await gql(`mutation($files:[FileCreateInput!]!){fileCreate(files:$files){files{id fileStatus alt} userErrors{field message}}}`,
    {files:[{originalSource:t.resourceUrl, contentType:'IMAGE', alt:filename.replace('.png','')}]});
  const errs = d2.fileCreate.userErrors;
  if(errs && errs.length) throw new Error('fileCreate: '+JSON.stringify(errs));
  const id = d2.fileCreate.files[0].id;
  // 4) poll for READY url
  for(let i=0;i<20;i++){
    await new Promise(r=>setTimeout(r,1500));
    const d3 = await gql(`query($id:ID!){node(id:$id){... on MediaImage{fileStatus image{url}}}}`,{id});
    const n = d3.node;
    if(n && n.fileStatus==='READY' && n.image && n.image.url) return {filename, url:n.image.url};
    if(n && n.fileStatus==='FAILED') throw new Error('file processing FAILED');
  }
  throw new Error('timeout waiting for READY: '+filename);
}

const out={};
for(const fp of files){
  try{ const r=await uploadOne(fp); out[r.filename]=r.url; console.log('✓',r.filename,'->',r.url); }
  catch(e){ console.error('✗',path.basename(fp),e.message); }
}
fs.writeFileSync('logo-urls.json', JSON.stringify(out,null,2));
console.log('\nsaved logo-urls.json');