← 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');