UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

60 lines (59 loc) 2.39 kB
import path from "node:path"; //#region src/config/backup-rotation.ts const CONFIG_BACKUP_COUNT = 5; /** * Advances the config `.bak` ring before a new primary backup is copied in. * * Missing slots are ignored so interrupted writes or first-run configs do not * block the next config write. */ async function rotateConfigBackups(configPath, ioFs) { const backupBase = `${configPath}.bak`; await ioFs.unlink(`${backupBase}.4`).catch(() => {}); for (let index = 3; index >= 1; index -= 1) await ioFs.rename(`${backupBase}.${index}`, `${backupBase}.${index + 1}`).catch(() => {}); await ioFs.rename(backupBase, `${backupBase}.1`).catch(() => {}); } /** * Sets owner-only permissions on every backup slot when chmod exists. * * Backups are copied on mixed filesystems, so copy mode preservation is not a * portable security guarantee. */ async function hardenBackupPermissions(configPath, ioFs) { if (!ioFs.chmod) return; const backupBase = `${configPath}.bak`; await ioFs.chmod(backupBase, 384).catch(() => {}); for (let i = 1; i < CONFIG_BACKUP_COUNT; i++) await ioFs.chmod(`${backupBase}.${i}`, 384).catch(() => {}); } const preUpdateConfigSnapshotsWritten = /* @__PURE__ */ new Set(); /** * Captures the first on-disk config state for an update attempt. * * The snapshot is outside the rotating `.bak` ring so repeated writes during * one process keep an operator-visible rollback point for the original file. */ async function createPreUpdateConfigSnapshot(params) { if (!params.fs.existsSync(params.configPath)) return; const snapshotKey = path.resolve(params.configPath); if (preUpdateConfigSnapshotsWritten.has(snapshotKey)) return; preUpdateConfigSnapshotsWritten.add(snapshotKey); const snapshotPath = `${params.configPath}.pre-update`; try { const content = await params.fs.readFile(params.configPath, "utf-8"); await params.fs.writeFile(snapshotPath, content, { encoding: "utf-8", mode: 384, flag: "w" }); } catch { preUpdateConfigSnapshotsWritten.delete(snapshotKey); } } /** Runs rotation, primary copy, and permission hardening. */ async function maintainConfigBackups(configPath, ioFs) { await rotateConfigBackups(configPath, ioFs); await ioFs.copyFile(configPath, `${configPath}.bak`).catch(() => {}); await hardenBackupPermissions(configPath, ioFs); } //#endregion export { maintainConfigBackups as n, createPreUpdateConfigSnapshot as t };