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:
@@ -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 };
|
||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user