Add database backup/restore feature with admin UI

- Backup service using VACUUM INTO for WAL-safe atomic snapshots
- Server-side storage in DATA_DIR/backups with download/upload API routes
- Restore with integrity validation and automatic pre-restore safety backup
- Scheduled daily backups via cron with configurable retention
- Admin-only settings UI consolidated under single Server section
- Clean up account section sign-out button, fix switch sub-pixel rendering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 20:01:10 -05:00
co-authored by Claude Opus 4.6
parent 724893196e
commit e59364e126
13 changed files with 826 additions and 50 deletions
+22
View File
@@ -9,6 +9,11 @@ import {
} from "@/lib/db/schema";
import { createLogger } from "@/lib/logger";
import { refreshAvailability } from "@/lib/services/availability";
import {
createBackup,
ensureBackupDir,
pruneBackups,
} from "@/lib/services/backup";
import {
cacheEpisodeStills,
cacheImagesForTitle,
@@ -20,6 +25,7 @@ import {
refreshTitle,
refreshTvChildren,
} from "@/lib/services/metadata";
import { getSetting } from "@/lib/services/settings";
import { getTvDetails } from "@/lib/tmdb/client";
const log = createLogger("cron");
@@ -207,9 +213,25 @@ async function cacheImagesJob() {
}
}
async function scheduledBackupJob() {
const enabled = getSetting("scheduledBackups");
if (enabled !== "true") {
log.debug("Scheduled backups disabled, skipping");
return;
}
ensureBackupDir();
createBackup();
const maxStr = getSetting("maxBackupRetention");
const max = maxStr ? Number.parseInt(maxStr, 10) : 7;
pruneBackups(max);
}
export function startJobs() {
if (jobs.size > 0) return;
schedule("scheduledBackup", "0 2 * * *", scheduledBackupJob);
schedule("nightlyRefreshLibrary", "0 3 * * *", nightlyRefreshLibrary);
schedule("refreshAvailability", "0 */6 * * *", refreshAvailabilityJob);
schedule("refreshRecommendations", "0 */12 * * *", refreshRecommendationsJob);