const express = require('express'); const router = express.Router(); const path = require('path'); const fs = require('fs'); const multer = require('multer'); const { getDb, logAudit } = require('../db'); const { collectStats, formatBytes, createBackup, listBackups, getBackupPath, deleteBackup, restoreBackup, getPaths } = require('../lib/backup'); function requireAdmin(req, res, next) { const db = getDb(); const user = db.prepare('SELECT * FROM users WHERE id = ?').get(req.session.userId); if (!user || user.role !== 'admin') return res.status(403).render('404', { path: req.path }); next(); } const uploadDir = path.join(getPaths().dataDir, 'backups', 'uploads-tmp'); if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true }); const upload = multer({ dest: uploadDir, limits: { fileSize: 500 * 1024 * 1024 }, fileFilter: (req, file, cb) => { if (file.originalname.endsWith('.tar.gz') || file.originalname.endsWith('.tgz') || file.mimetype === 'application/gzip') { cb(null, true); } else { cb(new Error('Alleen .tar.gz backupbestanden')); } } }); router.get('/', requireAdmin, (req, res) => { const stats = collectStats(); const backups = listBackups(); const disk = getPaths(); let diskFree = null; try { const { execSync } = require('child_process'); const out = execSync(`df -B1 "${disk.dataDir}" 2>/dev/null | tail -1`, { encoding: 'utf8' }); const parts = out.trim().split(/\s+/); if (parts.length >= 4) diskFree = formatBytes(parseInt(parts[3], 10)); } catch (e) { /* ignore */ } res.render('backup', { stats, backups, diskFree, formatBytes, restored: req.query.restored === '1', created: req.query.created === '1', deleted: req.query.deleted === '1', error: req.query.error || null, user: { username: req.session.username } }); }); router.post('/create', requireAdmin, (req, res) => { try { const backup = createBackup(); logAudit(req.session.userId, req.session.username, 'backup_create', 'system', 0, backup.filename); if (req.query.download === '1') { return res.download(backup.path, backup.filename); } res.redirect('/backup?created=1'); } catch (e) { console.error('Backup create error:', e); res.redirect('/backup?error=' + encodeURIComponent('Backup mislukt: ' + e.message)); } }); router.get('/download/:filename', requireAdmin, (req, res) => { const p = getBackupPath(req.params.filename); if (!p) return res.redirect('/backup?error=' + encodeURIComponent('Backup niet gevonden')); logAudit(req.session.userId, req.session.username, 'backup_download', 'system', 0, req.params.filename); res.download(p, req.params.filename); }); router.post('/delete/:filename', requireAdmin, (req, res) => { try { if (!deleteBackup(req.params.filename)) { return res.redirect('/backup?error=' + encodeURIComponent('Backup niet gevonden')); } logAudit(req.session.userId, req.session.username, 'backup_delete', 'system', 0, req.params.filename); res.redirect('/backup?deleted=1'); } catch (e) { res.redirect('/backup?error=' + encodeURIComponent(e.message)); } }); router.post('/restore', requireAdmin, upload.single('backup_file'), (req, res) => { if (!req.file) { return res.redirect('/backup?error=' + encodeURIComponent('Geen bestand geüpload')); } try { const result = restoreBackup(req.file.path); fs.unlinkSync(req.file.path); logAudit(req.session.userId, req.session.username, 'backup_restore', 'system', 0, JSON.stringify(result)); res.redirect('/backup?restored=1'); } catch (e) { console.error('Restore error:', e); try { fs.unlinkSync(req.file.path); } catch (err) { /* ignore */ } res.redirect('/backup?error=' + encodeURIComponent('Herstel mislukt: ' + e.message)); } }); router.post('/restore/:filename', requireAdmin, (req, res) => { const p = getBackupPath(req.params.filename); if (!p) return res.redirect('/backup?error=' + encodeURIComponent('Backup niet gevonden')); try { const result = restoreBackup(p); logAudit(req.session.userId, req.session.username, 'backup_restore', 'system', 0, req.params.filename); res.redirect('/backup?restored=1'); } catch (e) { res.redirect('/backup?error=' + encodeURIComponent('Herstel mislukt: ' + e.message)); } }); module.exports = router;