← 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}`);