feat: automate verified SQLite backups and safe recovery

This commit is contained in:
syntaxbullet
2026-09-04 18:28:28 +02:00
parent 32002151e0
commit 661bc6c346
10 changed files with 227 additions and 2 deletions

5
scripts/backup.ts Normal file
View File

@@ -0,0 +1,5 @@
import { Database } from "bun:sqlite";
import { databasePath } from "../src/db/config";
import { backupConfig, createBackup } from "../src/ops/backups";
const db = new Database(databasePath, { readonly: true, strict: true });
try { console.log(createBackup(db, backupConfig(databasePath))); } finally { db.close(); }

View File

@@ -1,7 +1,13 @@
import { backupConfig, createBackup } from "../src/ops/backups";
import { databasePath } from "../src/db/config";
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
import { db, sqlite } from "../src/db";
try {
if (process.env.NODE_ENV === "production" && sqlite.query("SELECT name FROM sqlite_master WHERE name = 'habits'").get()) {
// Fail before migrations if the safety snapshot cannot be created.
createBackup(sqlite, backupConfig(databasePath));
}
migrate(db, { migrationsFolder: "./drizzle" });
console.log("Database migrations applied.");
} finally {

5
scripts/restore.ts Normal file
View File

@@ -0,0 +1,5 @@
import { resolve } from "node:path";
import { restoreBackup } from "../src/ops/backups";
const [source, destination] = process.argv.slice(2);
if (!source || !destination || process.argv.length !== 4) throw new Error("Usage: bun scripts/restore.ts BACKUP NEW_DATABASE_PATH");
console.log(`Restored and verified: ${restoreBackup(resolve(source), resolve(destination))}. All sessions revoked.`);

View File

@@ -1,9 +1,13 @@
import { backupConfig } from "../src/ops/backups";
import { databasePath } from "../src/db/config";
import { fileURLToPath } from "node:url";
// Bun's built HTML assets resolve from dist; keep the database path absolute.
process.env.DATABASE_PATH = databasePath;
process.env.NODE_ENV = "production";
const backups = backupConfig(databasePath);
process.env.BACKUP_DIR = backups.directory;
if (backups.replica) process.env.BACKUP_REPLICA_DIR = backups.replica;
process.chdir(fileURLToPath(new URL("../dist", import.meta.url)));
const entrypoint = new URL("../dist/index.js", import.meta.url).href;
await import(entrypoint);