← back to Dw Domain Fleet

scripts/apply-standalone-heroes.js

52 lines

#!/usr/bin/env node
/**
 * apply-standalone-heroes.js  (runs ON PROD)
 *
 * Reads standalone-hero-allocation.json + a newline list of domains to update
 * (the duplicate-cluster sites). For each: backs up the existing hero-bg.jpg to
 * hero-bg.jpg.pre-uniquehero (once), downloads the assigned catalog image via
 * curl, validates it's a real JPEG > 30KB, and only then replaces the file.
 * On any failure the original is restored.
 *
 * Usage: node apply-standalone-heroes.js <alloc.json> <dup-domains.txt> [--dry]
 */
const fs = require('fs');
const { execSync } = require('child_process');

const [allocPath, domsPath, flag] = process.argv.slice(2);
const DRY = flag === '--dry';
const alloc = JSON.parse(fs.readFileSync(allocPath, 'utf8'));
const doms = fs.readFileSync(domsPath, 'utf8').trim().split('\n').filter(Boolean);

let ok = 0, skip = 0, fail = 0;
for (const d of doms) {
  const a = alloc[d];
  if (!a) { console.log(`SKIP ${d} (no allocation)`); skip++; continue; }
  const { path: p, url } = a;
  if (!fs.existsSync(p)) { console.log(`SKIP ${d} (missing ${p})`); skip++; continue; }
  const bak = p + '.pre-uniquehero';
  const tmp = p + '.tmp-newhero';
  try {
    if (DRY) { console.log(`DRY  ${d}  <- ${a.title}`); ok++; continue; }
    if (!fs.existsSync(bak)) fs.copyFileSync(p, bak); // backup once
    execSync(`curl -sL --max-time 40 -o ${JSON.stringify(tmp)} ${JSON.stringify(url)}`, { stdio: 'ignore' });
    const sz = fs.existsSync(tmp) ? fs.statSync(tmp).size : 0;
    const buf = sz ? fs.readFileSync(tmp, { encoding: null }).subarray(0, 3) : Buffer.alloc(0);
    const isJpeg = buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff;
    const isPng = sz && fs.readFileSync(tmp).subarray(0, 4).toString('hex') === '89504e47';
    if (sz < 30000 || (!isJpeg && !isPng)) {
      fs.rmSync(tmp, { force: true });
      console.log(`FAIL ${d} (bad download sz=${sz} jpeg=${isJpeg} png=${isPng}) — kept original`);
      fail++; continue;
    }
    fs.renameSync(tmp, p);
    console.log(`OK   ${d}  ${sz}b  <- ${a.title}`);
    ok++;
  } catch (e) {
    fs.rmSync(tmp, { force: true });
    console.log(`FAIL ${d} (${e.message.slice(0, 60)}) — kept original`);
    fail++;
  }
}
console.log(`\n[apply] ok=${ok} skip=${skip} fail=${fail} of ${doms.length}`);