← back to CelebritySignatures
scripts/fetch-printify-catalog.mjs
64 lines
#!/usr/bin/env node
// READ-ONLY Printify account/catalog helper for TK-10286.
// Lists the account's shops, blank-product blueprints, print providers, and
// variants. It never creates products, orders, or production jobs.
import { fileURLToPath } from 'node:url';
import { join } from 'node:path';
import { readFileSync } from 'node:fs';
const ROOT = fileURLToPath(new URL('..', import.meta.url));
const API = 'https://api.printify.com/v1';
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 get(token, path) {
const r = await fetch(API + path, { headers: { Authorization: `Bearer ${token}`, 'User-Agent': 'CelebritySignatures/1.0' } });
const j = await r.json().catch(() => ({}));
if (!r.ok) throw new Error(`Printify ${r.status}: ${j.message || j.error || 'error'} on ${path}`);
return j;
}
async function main() {
const token = envVal('PRINTIFY_API_TOKEN');
if (!token) {
console.log('PRINTIFY_API_TOKEN not set — generate a scoped personal access token in Printify Profile > Connections, then paste it via the secrets skill.');
console.log('This helper is GET-only and will discover shops/products after the token is routed.');
return;
}
const shops = await get(token, '/shops.json');
const blueprintArg = process.argv[2];
if (!blueprintArg) {
console.log(`Printify shops (${shops.length}):`);
for (const shop of shops) console.log(` id=${shop.id} ${shop.title} (${shop.sales_channel || 'unknown channel'})`);
const blueprints = await get(token, '/catalog/blueprints.json');
const candidates = blueprints.filter(b => /t-?shirt|tee|polo/i.test(b.title || ''));
console.log(`\nBlank garment blueprints matching tee/polo (${candidates.length}):`);
for (const b of candidates.slice(0, 50)) console.log(` id=${b.id} ${b.title}`);
console.log('\nNext: node scripts/fetch-printify-catalog.mjs <blueprintId> [printProviderId]');
return;
}
const providerArg = process.argv[3];
if (!providerArg) {
const providers = await get(token, `/catalog/blueprints/${blueprintArg}/print_providers.json`);
console.log(`Print providers for blueprint ${blueprintArg} (${providers.length}):`);
for (const p of providers) console.log(` id=${p.id} ${p.title}`);
console.log(`\nNext: node scripts/fetch-printify-catalog.mjs ${blueprintArg} <printProviderId>`);
return;
}
const catalog = await get(token, `/catalog/blueprints/${blueprintArg}/print_providers/${providerArg}/variants.json`);
const variants = {};
for (const v of catalog.variants || []) {
const title = String(v.title || '');
const size = title.match(/(?:^|\s\/\s)(S|M|L|XL|2XL)(?:$|\s\/\s)/i)?.[1]?.toUpperCase();
const color = title.split(/\s\/\s/).find(x => !/^(S|M|L|XL|2XL)$/i.test(x));
if (color && size) variants[`${color}/${size}`] = v.id;
}
console.log(JSON.stringify({ blueprintId: Number(blueprintArg), printProviderId: Number(providerArg),
printPosition: { placeholder: 'front', scale: 0.25, x: 0.32, y: 0.3 }, variants }, null, 2));
console.log('\nNormalize Printify color labels to our white/heather/sand/sky ids before saving the mapping.');
}
main().catch(e => { console.error(e.message); process.exit(1); });