← back to Sister Parish Onboarding
scripts/append_title.js
62 lines
#!/usr/bin/env node
/**
* Append " | Sister Parish" to every Sister Parish product title.
* Idempotent — skips products whose title already ends with the suffix.
*/
require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') });
const SHOP = process.env.SHOPIFY_STORE;
const TOKEN = process.env.SHOPIFY_ADMIN_TOKEN;
if (!SHOP || !TOKEN) { console.error('Need SHOPIFY_STORE + SHOPIFY_ADMIN_TOKEN'); process.exit(1); }
const BASE = `https://${SHOP}/admin/api/2026-01`;
const SUFFIX = ' | Sister Parish';
const RATE_DELAY_MS = 350;
const DRY = process.argv.includes('--dry-run');
const sleep = ms => new Promise(r => setTimeout(r, ms));
async function shopify(method, url, body) {
const res = await fetch(url.startsWith('http') ? url : `${BASE}${url}`, {
method,
headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' },
body: body ? JSON.stringify(body) : undefined,
});
const text = await res.text();
let json; try { json = JSON.parse(text); } catch { json = { raw: text }; }
if (!res.ok) { const e = new Error(`${method} ${url} → ${res.status}: ${text.slice(0,300)}`); e.status = res.status; throw e; }
return { json, link: res.headers.get('link') || '' };
}
const nextPage = link => {
const m = link.split(',').find(s => s.includes('rel="next"'));
return m ? m.match(/<([^>]+)>/)[1] : null;
};
(async () => {
const products = [];
let url = `${BASE}/products.json?vendor=${encodeURIComponent('Sister Parish')}&limit=250&fields=id,title`;
while (url) {
const { json, link } = await shopify('GET', url);
products.push(...json.products);
url = nextPage(link);
if (url) await sleep(RATE_DELAY_MS);
}
const todo = products.filter(p => !p.title.endsWith(SUFFIX));
console.log(`${DRY ? '[DRY-RUN] ' : ''}${products.length} SP products, ${todo.length} need the suffix.`);
let ok = 0, fail = 0;
for (let i = 0; i < todo.length; i++) {
const p = todo[i];
const stamp = `[${String(i+1).padStart(3,'0')}/${todo.length}]`;
const newTitle = p.title + SUFFIX;
if (DRY) { console.log(`${stamp} "${p.title}" → "${newTitle}"`); continue; }
try {
await shopify('PUT', `/products/${p.id}.json`, { product: { id: p.id, title: newTitle } });
ok++; console.log(`${stamp} ✓ ${newTitle}`);
} catch (e) {
fail++; console.log(`${stamp} ✗ ${p.title}: ${e.message.slice(0,150)}`);
}
await sleep(RATE_DELAY_MS);
}
console.log(`\nUpdated: ${ok} Failed: ${fail}`);
})().catch(e => { console.error('FATAL', e); process.exit(1); });