- VERSION file as authoritative version source - export.mjs reads VERSION with package.json fallback - bw-ota.ps1 DryRun mode for safe testing - auto-setup.ps1 bumped to v3.2.0 (Phase 8 OTA)
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();
|