diff --git a/src/backups.js b/src/backups.js index 60a3a2dc4..5ad4a8e2d 100644 --- a/src/backups.js +++ b/src/backups.js @@ -15,7 +15,9 @@ exports = module.exports = { backupApp: backupApp, restoreApp: restoreApp, - backupBoxAndApps: backupBoxAndApps + backupBoxAndApps: backupBoxAndApps, + + getLocalFilePath: getLocalFilePath }; var addons = require('./addons.js'), @@ -454,3 +456,20 @@ function restoreApp(app, addonsToRestore, backupId, callback) { }); }); } + +function getLocalFilePath(backupId, callback) { + assert.strictEqual(typeof backupId, 'string'); + assert.strictEqual(typeof callback, 'function'); + + settings.getBackupConfig(function (error, backupConfig) { + if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + + api(backupConfig.provider).getLocalFilePath(backupConfig, backupId, function (error, result) { + if (error) return callback(error); + + debug('getLocalFilePath: id:%s path:%s', backupId, result.filePath); + + callback(null, result.filePath); + }); + }); +} diff --git a/src/routes/backups.js b/src/routes/backups.js index f87934367..7bac1ec21 100644 --- a/src/routes/backups.js +++ b/src/routes/backups.js @@ -3,7 +3,8 @@ exports = module.exports = { get: get, create: create, - createDownloadUrl: createDownloadUrl + createDownloadUrl: createDownloadUrl, + download: download }; var assert = require('assert'), @@ -52,3 +53,13 @@ function createDownloadUrl(req, res, next) { next(new HttpSuccess(200, result)); }); } + +function download(req, res, next) { + assert.strictEqual(typeof req.params.backupId, 'string'); + + backups.getLocalDownloadPath(req.params.backupId, function (error, result) { + if (error) return next(new HttpError(500, error)); + + res.sendFile(result); + }); +} diff --git a/src/server.js b/src/server.js index ed53900da..a8cb6ef0d 100644 --- a/src/server.js +++ b/src/server.js @@ -204,6 +204,7 @@ function initializeExpressSync() { router.get ('/api/v1/backups', settingsScope, routes.user.requireAdmin, routes.backups.get); router.post('/api/v1/backups', settingsScope, routes.user.requireAdmin, routes.backups.create); router.post('/api/v1/backups/:backupId/download_url', appsScope, routes.user.requireAdmin, routes.backups.createDownloadUrl); + router.post('/api/v1/backups/:backupId/download', appsScope, routes.user.requireAdmin, routes.backups.download); // disable server socket "idle" timeout. we use the timeout middleware to handle timeouts on a route level // we rely on nginx for timeouts on the TCP level (see client_header_timeout) diff --git a/src/storage/caas.js b/src/storage/caas.js index 30d8f37ea..7c80bb08d 100644 --- a/src/storage/caas.js +++ b/src/storage/caas.js @@ -5,6 +5,7 @@ exports = module.exports = { getAppBackupDetails: getAppBackupDetails, getRestoreUrl: getRestoreUrl, + getLocalFilePath: getLocalFilePath, copyObject: copyObject }; @@ -105,6 +106,14 @@ function getRestoreUrl(apiConfig, filename, callback) { }); } +function getLocalFilePath(apiConfig, filename, callback) { + assert.strictEqual(typeof apiConfig, 'object'); + assert.strictEqual(typeof filename, 'string'); + assert.strictEqual(typeof callback, 'function'); + + callback(new Error('not supported')); +} + function copyObject(apiConfig, from, to, callback) { assert.strictEqual(typeof apiConfig, 'object'); assert.strictEqual(typeof from, 'string'); diff --git a/src/storage/filesystem.js b/src/storage/filesystem.js index 12047d897..c6a3e5f38 100644 --- a/src/storage/filesystem.js +++ b/src/storage/filesystem.js @@ -5,6 +5,7 @@ exports = module.exports = { getAppBackupDetails: getAppBackupDetails, getRestoreUrl: getRestoreUrl, + getLocalFilePath: getLocalFilePath, copyObject: copyObject }; @@ -56,6 +57,16 @@ function getRestoreUrl(apiConfig, filename, callback) { callback(null, { url: restoreUrl }); } +function getLocalFilePath(apiConfig, filename, callback) { + assert.strictEqual(typeof apiConfig, 'object'); + assert.strictEqual(typeof filename, 'string'); + assert.strictEqual(typeof callback, 'function'); + + var backupFolder = apiConfig.backupFolder || FALLBACK_BACKUP_FOLDER; + + callback(null, { filePath: path.join(backupFolder, filename) }); +} + function copyObject(apiConfig, from, to, callback) { assert.strictEqual(typeof apiConfig, 'object'); assert.strictEqual(typeof from, 'string'); diff --git a/src/storage/interface.js b/src/storage/interface.js index cc57aa5cc..a8d82e91f 100644 --- a/src/storage/interface.js +++ b/src/storage/interface.js @@ -11,6 +11,7 @@ exports = module.exports = { getAppBackupDetails: getAppBackupDetails, getRestoreUrl: getRestoreUrl, + getLocalFilePath: getLocalFilePath, copyObject: copyObject }; @@ -52,6 +53,17 @@ function getRestoreUrl(apiConfig, filename, callback) { callback(new Error('not implemented')); } +function getLocalFilePath(apiConfig, filename, callback) { + assert.strictEqual(typeof apiConfig, 'object'); + assert.strictEqual(typeof filename, 'string'); + assert.strictEqual(typeof callback, 'function'); + + // Result: { filePath: } + // The resulting filePath is a local path to the backup file + + callback(new Error('not implemented')); +} + function copyObject(apiConfig, from, to, callback) { assert.strictEqual(typeof apiConfig, 'object'); assert.strictEqual(typeof from, 'string'); diff --git a/src/storage/s3.js b/src/storage/s3.js index 8f9f76853..6ac098d30 100644 --- a/src/storage/s3.js +++ b/src/storage/s3.js @@ -5,6 +5,7 @@ exports = module.exports = { getAppBackupDetails: getAppBackupDetails, getRestoreUrl: getRestoreUrl, + getLocalFilePath: getLocalFilePath, copyObject: copyObject }; @@ -85,6 +86,14 @@ function getRestoreUrl(apiConfig, filename, callback) { }); } +function getLocalFilePath(apiConfig, filename, callback) { + assert.strictEqual(typeof apiConfig, 'object'); + assert.strictEqual(typeof filename, 'string'); + assert.strictEqual(typeof callback, 'function'); + + callback(new Error('not supported')); +} + function copyObject(apiConfig, from, to, callback) { assert.strictEqual(typeof apiConfig, 'object'); assert.strictEqual(typeof from, 'string');