← back to Philipperomano

scripts/sample-add-rollback.mjs

121 lines

/**
 * PhilRomano Sample-Add ROLLBACK Script
 * TK-10869 — reverses sample-add.mjs via restore-map
 *
 * Usage:
 *   node sample-add-rollback.mjs --restore <restore-map.json>
 *   DRY_RUN=true node sample-add-rollback.mjs
 */

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const SHOP = 'designer-laboratory-sandbox.myshopify.com';
const TOKEN = process.env.SHOPIFY_ADMIN_TOKEN || (() => {
  const envPath = '/Users/macstudio3/Projects/secrets-manager/.env';
  if (fs.existsSync(envPath)) {
    const lines = fs.readFileSync(envPath, 'utf8').split('\n');
    for (const l of lines) {
      const m = l.match(/^SHOPIFY_ADMIN_TOKEN=(.+)/);
      if (m) return m[1].trim();
    }
  }
  throw new Error('SHOPIFY_ADMIN_TOKEN not found');
})();

const DRY_RUN = process.env.DRY_RUN === 'true';

// Find restore map path from args
const args = process.argv.slice(2);
const restoreIdx = args.indexOf('--restore');
const RESTORE_MAP_PATH = restoreIdx >= 0
  ? args[restoreIdx + 1]
  : path.join(__dirname, '../data/sample-add-restore-map.json');

async function shopifyPut(path, body) {
  const url = `https://${SHOP}/admin/api/2024-10/${path}`;
  const resp = await fetch(url, {
    method: 'PUT',
    headers: {
      'X-Shopify-Access-Token': TOKEN,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  });
  if (!resp.ok) {
    const text = await resp.text();
    throw new Error(`PUT ${path} → ${resp.status}: ${text.slice(0, 200)}`);
  }
  return resp.json();
}

function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }

async function main() {
  console.log('=== PhilRomano Sample-Add ROLLBACK TK-10869 ===');
  console.log(`DRY_RUN: ${DRY_RUN}`);
  console.log(`Restore map: ${RESTORE_MAP_PATH}`);

  if (!fs.existsSync(RESTORE_MAP_PATH)) {
    console.error('ERROR: Restore map not found:', RESTORE_MAP_PATH);
    process.exit(1);
  }

  const restoreMap = JSON.parse(fs.readFileSync(RESTORE_MAP_PATH, 'utf8'));
  const entries = Object.values(restoreMap);
  console.log(`\nRestoring ${entries.length} products...\n`);

  let ok = 0, err = 0;

  for (const entry of entries) {
    const numId = entry.productId;

    if (DRY_RUN) {
      console.log(`DRY  ${numId} — ${entry.productTitle?.slice(0, 50)}`);
      ok++;
      continue;
    }

    try {
      // Restore original options and variants
      const restoreBody = {
        product: {
          id: numId,
          options: entry.originalOptions.map(o => ({
            name: o.name,
            values: o.values
          })),
          variants: entry.originalVariants.map(v => ({
            id: v.id,
            option1: v.option1,
            option2: v.option2,
            price: v.price,
            sku: v.sku,
            inventory_management: v.inventory_management,
            inventory_policy: v.inventory_policy
          }))
        }
      };

      await shopifyPut(`products/${numId}.json`, restoreBody);
      console.log(`OK   ${numId} — ${entry.productTitle?.slice(0, 50)}`);
      ok++;
      await sleep(400);
    } catch (e) {
      console.error(`ERR  ${numId}: ${e.message}`);
      err++;
      await sleep(1000);
    }
  }

  console.log(`\nRollback complete: OK=${ok} ERR=${err}`);
}

main().catch(e => {
  console.error('FATAL:', e);
  process.exit(1);
});