diff --git a/server/backup.js b/server/backup.js new file mode 100644 index 0000000..67aed7c --- /dev/null +++ b/server/backup.js @@ -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 }; diff --git a/server/index.js b/server/index.js index f7190cb..976317c 100644 --- a/server/index.js +++ b/server/index.js @@ -1,6 +1,7 @@ const express = require('express'); const cors = require('cors'); const db = require('./db'); +const { startBackupScheduler, runBackup } = require('./backup'); const app = express(); app.use(cors()); @@ -364,6 +365,17 @@ app.get('/api/utilization', (req, res) => { 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 const path = require('path'); const fs = require('fs'); @@ -377,4 +389,5 @@ if (fs.existsSync(clientDist)) { app.listen(PORT, () => { console.log(`Ressourcenplanung-API läuft auf http://localhost:${PORT}`); + startBackupScheduler(); });