75 lines
2.8 KiB
JavaScript
75 lines
2.8 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* Patch W7: health-check.js 时间回拨防护
|
|||
|
|
* 注入 safeAge(t) 统一处理 Date.now() - mtime,max(0, diff) 防负值
|
|||
|
|
* 幂等: sentinel W7_SAFE_AGE_v1
|
|||
|
|
*/
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
const TARGET = path.join(__dirname, '..', 'health-check.js');
|
|||
|
|
const SENTINEL = 'W7_SAFE_AGE_v1';
|
|||
|
|
|
|||
|
|
function main() {
|
|||
|
|
if (!fs.existsSync(TARGET)) { console.error('[patch-w7] missing target'); process.exit(2); }
|
|||
|
|
const src = fs.readFileSync(TARGET, 'utf8');
|
|||
|
|
if (src.includes(SENTINEL)) { console.error('[patch-w7] already applied, skip'); process.exit(0); }
|
|||
|
|
|
|||
|
|
// 1) 注入 safeAge helper (在 JSON_MODE 声明后)
|
|||
|
|
const anchor1 = "const JSON_MODE = process.argv.includes('--json');";
|
|||
|
|
if (!src.includes(anchor1)) { console.error('[patch-w7] anchor1 missing'); process.exit(3); }
|
|||
|
|
const helperBlock =
|
|||
|
|
anchor1 + "\n\n" +
|
|||
|
|
"// " + SENTINEL + ": 时间回拨防护 (NTP 回跳/跨时区/休眠恢复)\n" +
|
|||
|
|
"function safeAge(t) {\n" +
|
|||
|
|
" const ts = typeof t === 'number' ? t : (t && t.getTime ? t.getTime() : 0);\n" +
|
|||
|
|
" const d = Date.now() - ts;\n" +
|
|||
|
|
" return d < 0 ? 0 : d;\n" +
|
|||
|
|
"}\n";
|
|||
|
|
|
|||
|
|
let patched = src.replace(anchor1, helperBlock);
|
|||
|
|
|
|||
|
|
// 2) 替换 4 处 Date.now() - X 调用点
|
|||
|
|
const replacements = [
|
|||
|
|
{
|
|||
|
|
from: "const lockAge = (Date.now() - fs.statSync(lockFile).mtimeMs) / 3600000;",
|
|||
|
|
to: "const lockAge = safeAge(fs.statSync(lockFile).mtimeMs) / 3600000;"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
from: "const daysSince = genDate ? Math.floor((Date.now() - genDate.getTime()) / 86400000) : -1;",
|
|||
|
|
to: "const daysSince = genDate ? Math.floor(safeAge(genDate) / 86400000) : -1;",
|
|||
|
|
all: true
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
from: "return (Date.now() - created) > 30 * 60 * 1000;",
|
|||
|
|
to: "return safeAge(created) > 30 * 60 * 1000;"
|
|||
|
|
}
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
let hitCount = 0;
|
|||
|
|
for (const r of replacements) {
|
|||
|
|
if (r.all) {
|
|||
|
|
while (patched.includes(r.from)) {
|
|||
|
|
patched = patched.replace(r.from, r.to);
|
|||
|
|
hitCount++;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
if (!patched.includes(r.from)) { console.error('[patch-w7] miss:', r.from.slice(0,60)); process.exit(4); }
|
|||
|
|
patched = patched.replace(r.from, r.to);
|
|||
|
|
hitCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (hitCount < 4) { console.error('[patch-w7] expected 4 hits, got', hitCount); process.exit(5); }
|
|||
|
|
if (patched === src) { console.error('[patch-w7] nothing changed'); process.exit(6); }
|
|||
|
|
|
|||
|
|
const bakDir = path.join(path.dirname(TARGET), 'patches', 'bak');
|
|||
|
|
if (!fs.existsSync(bakDir)) fs.mkdirSync(bakDir, { recursive: true });
|
|||
|
|
const bak = path.join(bakDir, 'health-check.js.bak.w7.' + Date.now());
|
|||
|
|
fs.writeFileSync(bak, src);
|
|||
|
|
const tmp = TARGET + '.tmp.' + process.pid;
|
|||
|
|
fs.writeFileSync(tmp, patched);
|
|||
|
|
fs.renameSync(tmp, TARGET);
|
|||
|
|
console.error('[patch-w7] applied, hits=' + hitCount + ', bak=' + bak);
|
|||
|
|
}
|
|||
|
|
main();
|