← back to Secrets Manager
coordonne-collab-audit/cotswolds-activate-wallcoverings.js
51 lines
#!/usr/bin/env node
// Activate the 29 Cotswolds WALLCOVERINGS (Steve-approved 2026-07-22, DTD verdict B).
// Per SKU: add "New Arrival" tag -> status ACTIVE -> publish to Online Store ONLY.
// EXPLICITLY does NOT publish to Google & YouTube (avoids the $4.25 sample GMC leak).
// The 8 murals are NOT touched (held for the Coco Dávez mural batch).
const fs = require('fs'), path = require('path');
const ENV = fs.readFileSync(path.join(__dirname, '..', '.env'), 'utf8');
const TOKEN = (ENV.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m) || [])[1];
const DOMAIN = 'designer-laboratory-sandbox.myshopify.com', VER = '2024-10';
const ONLINE_STORE = 'gid://shopify/Publication/22208643184';
const GOOGLE_YOUTUBE = 'gid://shopify/Publication/29646651457'; // must NEVER appear below
if (!TOKEN) { console.error('no token'); process.exit(1); }
const DRY = process.env.DRY === '1';
async function gql(query, variables) {
const r = await fetch(`https://${DOMAIN}/admin/api/${VER}/graphql.json`, {
method: 'POST', headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables }),
});
return r.json();
}
const M_TAGS = `mutation($id: ID!, $tags: [String!]!) { tagsAdd(id: $id, tags: $tags) { userErrors { message } } }`;
const M_UPDATE = `mutation($input: ProductInput!) { productUpdate(input: $input) { product { id status } userErrors { field message } } }`;
const M_PUBLISH = `mutation($id: ID!, $input: [PublicationInput!]!) { publishablePublish(id: $id, input: $input) { userErrors { field message } } }`;
(async () => {
const created = JSON.parse(fs.readFileSync(path.join(__dirname, 'cotswolds-create-result.json'), 'utf8')).results;
const wall = created.filter(r => r.productType === 'Wallcovering' && r.shopify_id);
const results = []; let done = 0, err = 0;
for (const p of wall) {
const id = p.shopify_id;
if (DRY) { process.stderr.write(`[DRY] activate ${p.dwSku} ${p.title}\n`); results.push({ dwSku: p.dwSku, status: 'DRY' }); continue; }
const errs = [];
// 1) New Arrival tag
const t = await gql(M_TAGS, { id, tags: ['New Arrival'] });
(t?.data?.tagsAdd?.userErrors || []).forEach(e => errs.push('tag:' + e.message));
// 2) status ACTIVE
const u = await gql(M_UPDATE, { input: { id, status: 'ACTIVE' } });
(u?.data?.productUpdate?.userErrors || []).forEach(e => errs.push('update:' + e.message));
const newStatus = u?.data?.productUpdate?.product?.status;
// 3) publish to Online Store ONLY
const pub = await gql(M_PUBLISH, { id, input: [{ publicationId: ONLINE_STORE }] });
(pub?.data?.publishablePublish?.userErrors || []).forEach(e => errs.push('publish:' + e.message));
if (errs.length) { err++; results.push({ dwSku: p.dwSku, status: 'error', errors: errs }); process.stderr.write(`ERR ${p.dwSku}: ${errs.join('; ')}\n`); }
else { done++; results.push({ dwSku: p.dwSku, shopify_id: id, status: newStatus, published: 'Online Store' }); process.stderr.write(`OK ${p.dwSku} -> ACTIVE + Online Store + New Arrival\n`); }
await new Promise(r => setTimeout(r, 500));
}
fs.writeFileSync(path.join(__dirname, 'cotswolds-activate-result.json'), JSON.stringify({ activated: done, err, total: wall.length, google_publication_avoided: GOOGLE_YOUTUBE, results }, null, 2));
console.log(JSON.stringify({ activated: done, err, total: wall.length }, null, 2));
})();