← back to Sister Parish Onboarding
sp: activate_sp.js — flip all 50 draft Sister Parish products to active
a28adcaaaaab4050214be430dd2f05b97b6f569b · 2026-05-18 09:23:01 -0700 · SteveStudio2
Files touched
Diff
commit a28adcaaaaab4050214be430dd2f05b97b6f569b
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Mon May 18 09:23:01 2026 -0700
sp: activate_sp.js — flip all 50 draft Sister Parish products to active
---
scripts/activate_sp.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/scripts/activate_sp.js b/scripts/activate_sp.js
new file mode 100644
index 0000000..e01de0d
--- /dev/null
+++ b/scripts/activate_sp.js
@@ -0,0 +1,59 @@
+#!/usr/bin/env node
+/**
+ * Flip every draft Sister Parish product to status=active on the DW sandbox.
+ * Paginates the full vendor list, PUTs status=active on each draft.
+ */
+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 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') || '' };
+}
+
+function nextPage(link) {
+ const m = link.split(',').find(s => s.includes('rel="next"'));
+ return m ? m.match(/<([^>]+)>/)[1] : null;
+}
+
+(async () => {
+ const drafts = [];
+ let url = `${BASE}/products.json?vendor=${encodeURIComponent('Sister Parish')}&status=draft&limit=250&fields=id,title,status`;
+ while (url) {
+ const { json, link } = await shopify('GET', url);
+ drafts.push(...json.products);
+ url = nextPage(link);
+ if (url) await sleep(RATE_DELAY_MS);
+ }
+ console.log(`${DRY ? '[DRY-RUN] ' : ''}Found ${drafts.length} draft Sister Parish products to activate.`);
+
+ let ok = 0, fail = 0;
+ for (let i = 0; i < drafts.length; i++) {
+ const p = drafts[i];
+ const stamp = `[${String(i+1).padStart(3,'0')}/${drafts.length}]`;
+ if (DRY) { console.log(`${stamp} would activate: ${p.title} (#${p.id})`); continue; }
+ try {
+ await shopify('PUT', `/products/${p.id}.json`, { product: { id: p.id, status: 'active' } });
+ ok++; console.log(`${stamp} ✓ ${p.title} → active`);
+ } catch (e) {
+ fail++; console.log(`${stamp} ✗ ${p.title}: ${e.message.slice(0,160)}`);
+ }
+ await sleep(RATE_DELAY_MS);
+ }
+ console.log(`\nActivated: ${ok} Failed: ${fail}`);
+})().catch(e => { console.error('FATAL', e); process.exit(1); });
← 8b0c300 fix: preflight count by 3-call status sum (status=any return
·
back to Sister Parish Onboarding
·
sp: check_sp_inventory.js — audit + set all 250 SP variants aac311d →