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