mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 22:16:26 +00:00
fix(backup): snapshot the database instead of archiving it live (#1675)
The scheduled backup archived travel.db directly. The archiver reads its entries during finalize(), so a WAL checkpoint writing pages back mid-stream tore the copy — and the -wal that would make it recoverable isn't in the zip. The manual path was moved to VACUUM INTO for exactly this reason; the scheduler never followed. It calls the same createBackup() now, which also gets it the encryption key and plugin data it was missing, so a scheduled backup is finally restorable onto another install. The pre-rotation copy in migrate-encryption.ts had the same problem: a plain file copy of a live WAL database, and it is the operator's only rollback if a key rotation goes wrong. Auto-backups now exclude the re-derivable photo caches, matching manual ones.
This commit is contained in:
@@ -143,14 +143,25 @@ export function listBackups(): BackupInfo[] {
|
||||
// Create backup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function createBackup(): Promise<BackupInfo> {
|
||||
/**
|
||||
* Writes a full backup zip and returns its BackupInfo.
|
||||
*
|
||||
* `prefix` picks the filename scheme. The scheduler passes 'auto-backup' because
|
||||
* everything downstream tells the two apart by name: cleanupOldBackups() prunes
|
||||
* only auto-backup-*.zip, and the admin panel badges them as automatic. Manual
|
||||
* backups keep the default.
|
||||
*/
|
||||
export async function createBackup(prefix: 'backup' | 'auto-backup' = 'backup'): Promise<BackupInfo> {
|
||||
ensureBackupsDir();
|
||||
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
||||
const filename = `backup-${timestamp}.zip`;
|
||||
const filename = `${prefix}-${timestamp}.zip`;
|
||||
const outputPath = path.join(backupsDir, filename);
|
||||
const pdataSnap = path.join(backupsDir, `.plugins-snap-${timestamp}`);
|
||||
const dbSnap = path.join(backupsDir, `.travel-snap-${timestamp}.db`);
|
||||
// The scratch names carry the prefix too: a scheduled run and a manual one that
|
||||
// start in the same second would otherwise share a snapshot path, and the first
|
||||
// to finish would delete the other's staging copy mid-archive.
|
||||
const pdataSnap = path.join(backupsDir, `.plugins-snap-${prefix}-${timestamp}`);
|
||||
const dbSnap = path.join(backupsDir, `.travel-snap-${prefix}-${timestamp}.db`);
|
||||
|
||||
try {
|
||||
try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch (e) {}
|
||||
|
||||
Reference in New Issue
Block a user