Files
mek-tech-consulting/routes/backup.js
T

127 lines
4.3 KiB
JavaScript

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;