Files
moltbot/src/secrets/migrate/apply.ts

77 lines
2.0 KiB
TypeScript

import fs from "node:fs";
import { ensureDirForFile, writeJsonFileSecure } from "../shared.js";
import {
createBackupManifest,
pruneOldBackups,
resolveUniqueBackupId,
restoreFromManifest,
} from "./backup.js";
import { createSecretsMigrationConfigIO } from "./config-io.js";
import type { MigrationPlan, SecretsMigrationRunResult } from "./types.js";
export async function applyMigrationPlan(params: {
plan: MigrationPlan;
env: NodeJS.ProcessEnv;
now: Date;
}): Promise<SecretsMigrationRunResult> {
const { plan } = params;
if (!plan.changed) {
return {
mode: "write",
changed: false,
secretsFilePath: plan.secretsFilePath,
counters: plan.counters,
changedFiles: [],
};
}
const backupId = resolveUniqueBackupId(plan.stateDir, params.now);
const backup = createBackupManifest({
stateDir: plan.stateDir,
targets: plan.backupTargets,
backupId,
now: params.now,
});
try {
if (plan.payloadChanged) {
writeJsonFileSecure(plan.secretsFilePath, plan.nextPayload);
}
if (plan.configChanged) {
const io = createSecretsMigrationConfigIO({ env: params.env });
await io.writeConfigFile(plan.nextConfig, plan.configWriteOptions);
}
for (const change of plan.authStoreChanges) {
writeJsonFileSecure(change.path, change.nextStore);
}
if (plan.envChange) {
ensureDirForFile(plan.envChange.path);
fs.writeFileSync(plan.envChange.path, plan.envChange.nextRaw, "utf8");
fs.chmodSync(plan.envChange.path, 0o600);
}
} catch (err) {
restoreFromManifest(backup.manifest);
throw new Error(
`Secrets migration failed and was rolled back from backup ${backupId}: ${String(err)}`,
{
cause: err,
},
);
}
pruneOldBackups(plan.stateDir);
return {
mode: "write",
changed: true,
backupId,
backupDir: backup.backupDir,
secretsFilePath: plan.secretsFilePath,
counters: plan.counters,
changedFiles: plan.backupTargets,
};
}