123
This commit is contained in:
79
server.js
79
server.js
@@ -4,6 +4,7 @@ const { Pool } = require('pg');
|
||||
const session = require('express-session');
|
||||
const cors = require('cors');
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const {
|
||||
ALLOWED_SQL_TYPES,
|
||||
canAccessFolder,
|
||||
@@ -34,10 +35,17 @@ const {
|
||||
createBackup,
|
||||
getBackupPath,
|
||||
listBackups,
|
||||
pruneBackups,
|
||||
} = require('./src/services/backups');
|
||||
const {
|
||||
notifyError,
|
||||
notifyInfo,
|
||||
} = require('./src/services/notifications');
|
||||
const {
|
||||
getSettings,
|
||||
saveSettings,
|
||||
validateSettings,
|
||||
} = require('./src/services/runtime-config');
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -103,6 +111,45 @@ function applyRecordMetadata(structure, payload, currentUser, { isCreate = false
|
||||
return data;
|
||||
}
|
||||
|
||||
let lastAutoBackupSlot = '';
|
||||
|
||||
async function runScheduledBackupIfNeeded() {
|
||||
const settings = getSettings();
|
||||
if (!settings.backups.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
if (now.getHours() !== settings.backups.hour || now.getMinutes() !== settings.backups.minute) {
|
||||
return;
|
||||
}
|
||||
|
||||
const slot = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}-${String(settings.backups.hour).padStart(2, '0')}-${String(settings.backups.minute).padStart(2, '0')}`;
|
||||
if (lastAutoBackupSlot === slot) {
|
||||
return;
|
||||
}
|
||||
|
||||
const backup = await createBackup(pool, 'system', {
|
||||
includeAppSnapshot: settings.backups.includeAppSnapshot,
|
||||
keepLast: settings.backups.keepLast,
|
||||
});
|
||||
|
||||
lastAutoBackupSlot = slot;
|
||||
appendAudit('backup.auto_created', 'system', {
|
||||
files: backup.files,
|
||||
source: 'WEB',
|
||||
});
|
||||
notifyInfo('Scheduled backup completed', backup.files.map((file) => `${file.kind}: ${file.filename}`)).catch(() => {});
|
||||
}
|
||||
|
||||
function startBackupScheduler() {
|
||||
setInterval(() => {
|
||||
runScheduledBackupIfNeeded().catch((error) => {
|
||||
notifyError('Scheduled backup failed', error).catch(() => {});
|
||||
});
|
||||
}, 60000);
|
||||
}
|
||||
|
||||
// Helper: get primary key column for a table (returns null if none)
|
||||
async function getPrimaryKeyColumn(tableName) {
|
||||
const result = await pool.query(`
|
||||
@@ -353,8 +400,12 @@ app.post('/api/backups', requireAuth, requirePermission(
|
||||
'Backup access denied'
|
||||
), async (req, res) => {
|
||||
try {
|
||||
const backup = await createBackup(pool, req.currentUser.username);
|
||||
appendAudit('backup.created', req.currentUser.username, { filename: backup.filename, source: getAuditSource(req) });
|
||||
const settings = getSettings();
|
||||
const backup = await createBackup(pool, req.currentUser.username, {
|
||||
includeAppSnapshot: settings.backups.includeAppSnapshot,
|
||||
keepLast: settings.backups.keepLast,
|
||||
});
|
||||
appendAudit('backup.created', req.currentUser.username, { files: backup.files, source: getAuditSource(req) });
|
||||
res.json({ success: true, backup });
|
||||
} catch (err) {
|
||||
notifyError('Backup creation failed', err, { actor: req.currentUser.username }).catch(() => {});
|
||||
@@ -362,6 +413,28 @@ app.post('/api/backups', requireAuth, requirePermission(
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/settings', requireAuth, requirePermission(
|
||||
(permissions) => permissions.canManageUsers,
|
||||
'Settings access denied'
|
||||
), (req, res) => {
|
||||
res.json(getSettings());
|
||||
});
|
||||
|
||||
app.put('/api/settings', requireAuth, requirePermission(
|
||||
(permissions) => permissions.canManageUsers,
|
||||
'Settings access denied'
|
||||
), (req, res) => {
|
||||
try {
|
||||
const settings = validateSettings(req.body);
|
||||
const saved = saveSettings(settings);
|
||||
pruneBackups(saved.backups.keepLast);
|
||||
appendAudit('settings.updated', req.currentUser.username, { source: getAuditSource(req) });
|
||||
res.json({ success: true, settings: saved });
|
||||
} catch (err) {
|
||||
res.status(400).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/backups/:filename/download', requireAuth, requirePermission(
|
||||
(permissions) => permissions.canManageUsers,
|
||||
'Backup access denied'
|
||||
@@ -943,4 +1016,4 @@ app.listen(PORT, () => {
|
||||
console.log('📝 Make sure to configure your database in .env file');
|
||||
});
|
||||
|
||||
|
||||
startBackupScheduler();
|
||||
|
||||
Reference in New Issue
Block a user