Upload via GUI (16 Dateien)
This commit is contained in:
+103
@@ -0,0 +1,103 @@
|
||||
// backup.js — simple daily rotating backup of the shared data folder (fellakte.db + files/).
|
||||
// No external dependencies: plain folder copies under <dataFolder>/backups/<YYYY-MM-DD>/,
|
||||
// pruned to the most recent KEEP_BACKUPS. Runs once automatically per day (on app start)
|
||||
// and can also be triggered manually from Settings.
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const KEEP_BACKUPS = 30;
|
||||
|
||||
function pad(n) {
|
||||
return String(n).padStart(2, "0");
|
||||
}
|
||||
function todayStamp() {
|
||||
const d = new Date();
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
||||
}
|
||||
|
||||
function backupsDir(dataFolder) {
|
||||
return path.join(dataFolder, "backups");
|
||||
}
|
||||
|
||||
function listBackups(dataFolder) {
|
||||
const dir = backupsDir(dataFolder);
|
||||
if (!fs.existsSync(dir)) return [];
|
||||
return fs
|
||||
.readdirSync(dir, { withFileTypes: true })
|
||||
.filter((e) => e.isDirectory())
|
||||
.map((e) => e.name)
|
||||
.sort();
|
||||
}
|
||||
|
||||
function pruneOldBackups(dataFolder) {
|
||||
const names = listBackups(dataFolder);
|
||||
if (names.length <= KEEP_BACKUPS) return;
|
||||
for (const name of names.slice(0, names.length - KEEP_BACKUPS)) {
|
||||
fs.rmSync(path.join(backupsDir(dataFolder), name), { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
function copySnapshot(dataFolder, dest) {
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
const dbFile = path.join(dataFolder, "fellakte.db");
|
||||
if (fs.existsSync(dbFile)) fs.copyFileSync(dbFile, path.join(dest, "fellakte.db"));
|
||||
const filesDir = path.join(dataFolder, "files");
|
||||
if (fs.existsSync(filesDir)) fs.cpSync(filesDir, path.join(dest, "files"), { recursive: true });
|
||||
}
|
||||
|
||||
// Manual/"Jetzt sichern" backup. If today's daily backup already exists, this makes an
|
||||
// additional time-stamped one instead of silently doing nothing.
|
||||
function runBackupNow(dataFolder) {
|
||||
const stamp = todayStamp();
|
||||
let dest = path.join(backupsDir(dataFolder), stamp);
|
||||
if (fs.existsSync(dest)) {
|
||||
const d = new Date();
|
||||
dest = `${dest}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
||||
}
|
||||
copySnapshot(dataFolder, dest);
|
||||
pruneOldBackups(dataFolder);
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Safe to call on every app start — no-ops if today's backup already exists.
|
||||
function runDailyBackupIfNeeded(dataFolder) {
|
||||
try {
|
||||
const dest = path.join(backupsDir(dataFolder), todayStamp());
|
||||
if (fs.existsSync(dest)) return { ran: false };
|
||||
copySnapshot(dataFolder, dest);
|
||||
pruneOldBackups(dataFolder);
|
||||
return { ran: true, dest };
|
||||
} catch (e) {
|
||||
console.error("Automatisches Backup fehlgeschlagen:", e);
|
||||
return { ran: false, error: String((e && e.message) || e) };
|
||||
}
|
||||
}
|
||||
|
||||
function backupInfo(dataFolder) {
|
||||
const names = listBackups(dataFolder);
|
||||
return { count: names.length, last: names.length ? names[names.length - 1] : null };
|
||||
}
|
||||
|
||||
// Restores fellakte.db + files/ from a named backup, overwriting the live data folder.
|
||||
// Takes a fresh safety-net backup of the current (about-to-be-overwritten) state first,
|
||||
// so a restore is itself always undoable. Caller is responsible for restarting the app
|
||||
// afterwards — the in-memory database must be reloaded from the restored file.
|
||||
function restoreBackup(dataFolder, backupName) {
|
||||
const src = path.join(backupsDir(dataFolder), backupName);
|
||||
if (!fs.existsSync(src)) throw new Error("Sicherung nicht gefunden: " + backupName);
|
||||
runBackupNow(dataFolder);
|
||||
|
||||
const dbFile = path.join(dataFolder, "fellakte.db");
|
||||
const srcDb = path.join(src, "fellakte.db");
|
||||
if (fs.existsSync(srcDb)) fs.copyFileSync(srcDb, dbFile);
|
||||
|
||||
const filesDir = path.join(dataFolder, "files");
|
||||
fs.rmSync(filesDir, { recursive: true, force: true });
|
||||
const srcFiles = path.join(src, "files");
|
||||
if (fs.existsSync(srcFiles)) fs.cpSync(srcFiles, filesDir, { recursive: true });
|
||||
else fs.mkdirSync(filesDir, { recursive: true });
|
||||
}
|
||||
|
||||
module.exports = { runBackupNow, runDailyBackupIfNeeded, restoreBackup, listBackups, backupInfo, backupsDir };
|
||||
Reference in New Issue
Block a user