diff --git a/src/backups.js b/src/backups.js index 4bb0b0d90..b78154e50 100644 --- a/src/backups.js +++ b/src/backups.js @@ -27,6 +27,7 @@ var addons = require('./addons.js'), config = require('./config.js'), DatabaseError = require('./databaseerror.js'), debug = require('debug')('box:backups'), + eventlog = require('./eventlog.js'), locker = require('./locker.js'), path = require('path'), paths = require('./paths.js'), @@ -408,19 +409,22 @@ function backupBoxAndApps(callback) { }); } -function backup(callback) { +function backup(eventSource, callback) { + assert.strictEqual(typeof eventSource, 'object'); assert.strictEqual(typeof callback, 'function'); var error = locker.lock(locker.OP_FULL_BACKUP); if (error) return callback(new BackupsError(BackupsError.BAD_STATE, error.message)); - // ensure tools can 'wait' on progress - progress.set(progress.BACKUP, 0, 'Starting'); + eventlog.add(eventlog.ACTION_BACKUP_START, eventSource, { }); - // start the backup operation in the background - backupBoxAndApps(function (error) { + progress.set(progress.BACKUP, 0, 'Starting'); // ensure tools can 'wait' on progress + + backupBoxAndApps(function (error, filename) { // start the backup operation in the background if (error) console.error('backup failed.', error); + eventlog.add(eventlog.ACTION_BACKUP_FINISH, eventSource, { errorMessage: error ? error.message : null, filename: filename }); + locker.unlock(locker.OP_FULL_BACKUP); }); diff --git a/src/eventlog.js b/src/eventlog.js index 8292310b5..a933267f3 100644 --- a/src/eventlog.js +++ b/src/eventlog.js @@ -14,7 +14,8 @@ exports = module.exports = { ACTION_APP_RESTORE: 'app.restore', ACTION_APP_UNINSTALL: 'app.uninstall', ACTION_APP_UPDATE: 'app.update', - ACTION_BACKUP: 'cloudron.backup', + ACTION_BACKUP_FINISH: 'backup.finish', + ACTION_BACKUP_START: 'backup.start', ACTION_CERTIFICATE_RENEWAL: 'certificate.renew', ACTION_CLI_MODE: 'settings.climode', ACTION_PROFILE: 'user.profile', diff --git a/src/routes/backups.js b/src/routes/backups.js index a7164973f..eda7ae288 100644 --- a/src/routes/backups.js +++ b/src/routes/backups.js @@ -9,10 +9,14 @@ exports = module.exports = { var assert = require('assert'), backups = require('../backups.js'), BackupsError = require('../backups.js').BackupsError, - eventlog = require('../eventlog.js'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess; +function auditSource(req) { + var ip = req.headers['x-forwarded-for'] || req.ip || null; + return { ip: ip, username: req.user ? req.user.username : null, userId: req.user ? req.user.id : null }; +} + function get(req, res, next) { var page = typeof req.query.page !== 'undefined' ? parseInt(req.query.page) : 1; if (!page || page < 0) return next(new HttpError(400, 'page query param has to be a postive number')); @@ -31,12 +35,10 @@ function get(req, res, next) { function create(req, res, next) { // note that cloudron.backup only waits for backup initiation and not for backup to complete // backup progress can be checked up ny polling the progress api call - backups.backup(function (error) { + backups.backup(auditSource(req), function (error) { if (error && error.reason === BackupsError.BAD_STATE) return next(new HttpError(409, error.message)); if (error) return next(new HttpError(500, error)); - eventlog.add(eventlog.ACTION_BACKUP, req, { }); - next(new HttpSuccess(202, {})); }); } diff --git a/src/routes/sysadmin.js b/src/routes/sysadmin.js index 074b205d2..946e49305 100644 --- a/src/routes/sysadmin.js +++ b/src/routes/sysadmin.js @@ -19,7 +19,7 @@ function backup(req, res, next) { // note that cloudron.backup only waits for backup initiation and not for backup to complete // backup progress can be checked up ny polling the progress api call - backups.backup(function (error) { + backups.backup({ userId: null, username: 'sysadmin' }, function (error) { if (error && error.reason === BackupsError.BAD_STATE) return next(new HttpError(409, error.message)); if (error) return next(new HttpError(500, error)); diff --git a/webadmin/src/js/index.js b/webadmin/src/js/index.js index 9b26a3eab..e7fcbaddb 100644 --- a/webadmin/src/js/index.js +++ b/webadmin/src/js/index.js @@ -217,7 +217,9 @@ var ACTION_APP_INSTALL = 'app.install'; var ACTION_APP_RESTORE = 'app.restore'; var ACTION_APP_UNINSTALL = 'app.uninstall'; var ACTION_APP_UPDATE = 'app.update'; -var ACTION_BACKUP = 'cloudron.backup'; +var ACTION_APP_UPDATE = 'app.update'; +var ACTION_BACKUP_FINISH = 'backup.finish'; +var ACTION_BACKUP_START = 'backup.start'; var ACTION_CERTIFICATE_RENEWAL = 'certificate.renew'; var ACTION_CLI_MODE = 'settings.climode'; var ACTION_PROFILE = 'user.profile'; @@ -230,6 +232,7 @@ var ACTION_USER_UPDATE = 'user.update'; app.filter('eventLogDetails', function() { return function(eventLog) { + var source = eventLog.source; var data = eventLog.data; switch (eventLog.action) { @@ -239,7 +242,8 @@ app.filter('eventLogDetails', function() { case ACTION_APP_RESTORE: return 'App ' + data.id + ' restored'; case ACTION_APP_UNINSTALL: return 'App ' + data.id + ' uninstalled'; case ACTION_APP_UPDATE: return 'App ' + data.id + ' updated to version ' + data.appStoreId + '@' + data.toVersion; - case ACTION_BACKUP: return 'Backup'; + case ACTION_BACKUP_START: return 'Backup started by ' + source.username; + case ACTION_BACKUP_FINISH: return 'Backup finished. ' + (('error:' + data.errorMessage) || ('id:' + data.filename)); case ACTION_CERTIFICATE_RENEWAL: return 'Certificate renewal for ' + data.domain + (data.error ? 'succeeded' : 'failed'); case ACTION_CLI_MODE: return 'CLI mode was ' + (data.enabled ? 'enabled' : 'disabled'); case ACTION_PROFILE: return 'User profile change';