Automatische Datenbank-Backups

Sichert die SQLite-Datenbank periodisch (Standard: alle 24h) nach
server/data/backups und räumt alte Sicherungen auf (Standard: 14
behalten). Zusätzlich per POST /api/backup manuell auslösbar.
This commit is contained in:
2026-07-04 11:56:38 +02:00
parent 4abbb50f8c
commit 60a0928d2f
2 changed files with 55 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
const path = require('path');
const fs = require('fs');
const db = require('./db');
const dataDir = path.join(__dirname, 'data');
const backupDir = path.join(dataDir, 'backups');
const INTERVAL_HOURS = Number(process.env.BACKUP_INTERVAL_HOURS) || 24;
const RETENTION_COUNT = Number(process.env.BACKUP_RETENTION_COUNT) || 14;
function timestamp() {
return new Date().toISOString().replace(/[:.]/g, '-');
}
async function runBackup() {
if (!fs.existsSync(backupDir)) fs.mkdirSync(backupDir, { recursive: true });
const target = path.join(backupDir, `data-${timestamp()}.db`);
await db.backup(target);
cleanupOldBackups();
console.log(`Backup geschrieben: ${target}`);
return target;
}
function cleanupOldBackups() {
const files = fs
.readdirSync(backupDir)
.filter((f) => f.startsWith('data-') && f.endsWith('.db'))
.sort();
const excess = files.length - RETENTION_COUNT;
for (let i = 0; i < excess; i++) {
fs.unlinkSync(path.join(backupDir, files[i]));
}
}
function startBackupScheduler() {
runBackup().catch((err) => console.error('Backup fehlgeschlagen:', err));
setInterval(() => {
runBackup().catch((err) => console.error('Backup fehlgeschlagen:', err));
}, INTERVAL_HOURS * 60 * 60 * 1000);
}
module.exports = { startBackupScheduler, runBackup };
+13
View File
@@ -1,6 +1,7 @@
const express = require('express'); const express = require('express');
const cors = require('cors'); const cors = require('cors');
const db = require('./db'); const db = require('./db');
const { startBackupScheduler, runBackup } = require('./backup');
const app = express(); const app = express();
app.use(cors()); app.use(cors());
@@ -364,6 +365,17 @@ app.get('/api/utilization', (req, res) => {
app.get('/health', (req, res) => res.json({ ok: true })); app.get('/health', (req, res) => res.json({ ok: true }));
// ---------- backup ----------
app.post('/api/backup', async (req, res) => {
try {
const file = await runBackup();
res.status(201).json({ file });
} catch (err) {
res.status(500).json({ error: 'Backup fehlgeschlagen' });
}
});
// serve built frontend in production // serve built frontend in production
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
@@ -377,4 +389,5 @@ if (fs.existsSync(clientDist)) {
app.listen(PORT, () => { app.listen(PORT, () => {
console.log(`Ressourcenplanung-API läuft auf http://localhost:${PORT}`); console.log(`Ressourcenplanung-API läuft auf http://localhost:${PORT}`);
startBackupScheduler();
}); });