← back to CelebritySignatures

scripts/fetch-printful-catalog.mjs

58 lines

#!/usr/bin/env node
// READ-ONLY Printful catalog helper for /wear go-live wiring (TK-10286).
//
// Once PRINTFUL_API_KEY is set, this fetches a Printful catalog product's variants
// and prints a suggested `variants` map ({ "color/size": variant_id }) to paste into
// data/wear-templates.json for each garment. It ONLY issues GET requests to the
// Printful catalog — it never creates, confirms, or touches an order. Safe to run
// any time the key is present; a no-op (prints guidance) when it is not.
//
// Usage:
//   node scripts/fetch-printful-catalog.mjs                 # list candidate products
//   node scripts/fetch-printful-catalog.mjs <productId>     # dump variants for a product
import { fileURLToPath } from 'node:url';
import { join } from 'node:path';
import { readFileSync } from 'node:fs';

const ROOT = fileURLToPath(new URL('..', import.meta.url));
function envVal(name) {
  if (process.env[name]) return process.env[name];
  for (const p of [join(ROOT, '.env'), join(ROOT, '..', 'secrets-manager', '.env')]) {
    try { const m = readFileSync(p, 'utf8').match(new RegExp('^' + name + '=(.+)$', 'm')); if (m) return m[1].trim(); } catch {}
  }
  return null;
}

async function pf(key, path) {
  const r = await fetch('https://api.printful.com' + path, { headers: { Authorization: `Bearer ${key}` } });
  const j = await r.json().catch(() => ({}));
  if (!r.ok) throw new Error(`Printful ${r.status}: ${j.error?.message || 'error'} on ${path}`);
  return j.result;
}

async function main() {
  const key = envVal('PRINTFUL_API_KEY');
  if (!key) {
    console.log('PRINTFUL_API_KEY not set — paste it via the secrets skill first.');
    console.log('This script is read-only (GET /products only) and will list candidate garments once the key is present.');
    return;
  }
  const productId = process.argv[2];
  if (!productId) {
    const products = await pf(key, '/products');
    const tees = products.filter(p => /t-?shirt|tee|polo/i.test(p.type_name || p.title || ''));
    console.log(`Catalog products matching tee/polo (${tees.length}):`);
    for (const p of tees.slice(0, 40)) console.log(`  id=${p.id}  ${p.title}  (${p.type_name})`);
    console.log(`\nNext: node scripts/fetch-printful-catalog.mjs <id>  to dump variants for the chosen product.`);
    return;
  }
  const prod = await pf(key, `/products/${productId}`);
  console.log(`Product ${productId}: ${prod.product?.title}`);
  console.log(`Map our color/size -> Printful variant_id. Suggested variants block (edit color labels to match our ids):`);
  const map = {};
  for (const v of prod.variants || []) map[`${v.color}/${v.size}`] = v.id;
  console.log(JSON.stringify({ productId: Number(productId), placementType: 'front', variants: map }, null, 2));
  console.log(`\nOur color ids are white/heather/sand/sky and sizes S/M/L/XL/2XL — align the keys, then paste into the garment's "printful" block in data/wear-templates.json.`);
}
main().catch(e => { console.error(e.message); process.exit(1); });