From 76840a93da66f316a288e47e0bc95f56294b33f3 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Tue, 3 May 2022 14:45:32 +0200 Subject: [PATCH 1/5] Add new backups route to routes.js Create backups.js (controller?) that would handle the request. Import backups.js in index.js. --- core/server/api/canary/backups.js | 11 +++++++++++ core/server/api/canary/index.js | 4 ++++ core/server/web/api/canary/admin/routes.js | 4 ++++ 3 files changed, 19 insertions(+) create mode 100644 core/server/api/canary/backups.js diff --git a/core/server/api/canary/backups.js b/core/server/api/canary/backups.js new file mode 100644 index 00000000000..fc793990924 --- /dev/null +++ b/core/server/api/canary/backups.js @@ -0,0 +1,11 @@ + +module.exports = { + docName: 'backups', + download: { + statusCode: 200, + permissions: false, + query(frame) { + return + } + } +}; diff --git a/core/server/api/canary/index.js b/core/server/api/canary/index.js index b7d5173aef0..01d583cf0e0 100644 --- a/core/server/api/canary/index.js +++ b/core/server/api/canary/index.js @@ -20,6 +20,10 @@ module.exports = { return shared.pipeline(require('./identities'), localUtils); }, + get backups() { + return shared.pipeline(require('./backups'), localUtils); + }, + get integrations() { return shared.pipeline(require('./integrations'), localUtils); }, diff --git a/core/server/web/api/canary/admin/routes.js b/core/server/web/api/canary/admin/routes.js index d2c298f22c5..1cf17febb4a 100644 --- a/core/server/web/api/canary/admin/routes.js +++ b/core/server/web/api/canary/admin/routes.js @@ -261,6 +261,10 @@ module.exports = function apiRoutes() { http(api.files.upload) ); + // ## backups + // @TODO make images dynamic to allow multiple use cases in futurex + router.get('/backups/images/download', mw.authAdminApi, http(api.backups.download)); + // ## Invites router.get('/invites', mw.authAdminApi, http(api.invites.browse)); router.get('/invites/:id', mw.authAdminApi, http(api.invites.read)); From 702b892de4fbf855ad9aab7909f309b99d5d4b66 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Tue, 3 May 2022 14:50:47 +0200 Subject: [PATCH 2/5] Add serializer for backup route. Needed to add response data to the frame. Import backup serializer to index.js. --- .../api/canary/utils/serializers/output/backups.js | 9 +++++++++ core/server/api/canary/utils/serializers/output/index.js | 4 ++++ 2 files changed, 13 insertions(+) create mode 100644 core/server/api/canary/utils/serializers/output/backups.js diff --git a/core/server/api/canary/utils/serializers/output/backups.js b/core/server/api/canary/utils/serializers/output/backups.js new file mode 100644 index 00000000000..d2fb41332ef --- /dev/null +++ b/core/server/api/canary/utils/serializers/output/backups.js @@ -0,0 +1,9 @@ +const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:backups'); + +module.exports = { + all(data, apiConfig, frame) { + debug('all'); + + frame.response = data; + } +}; diff --git a/core/server/api/canary/utils/serializers/output/index.js b/core/server/api/canary/utils/serializers/output/index.js index 51b2080b270..a3a5d83643f 100644 --- a/core/server/api/canary/utils/serializers/output/index.js +++ b/core/server/api/canary/utils/serializers/output/index.js @@ -13,6 +13,10 @@ module.exports = { return require('./default'); }, + get backups() { + return require('./backups'); + }, + get authentication() { return require('./authentication'); }, From 9bc2b7dd16e2ed7bb55f44cee7222e822cc9a9e4 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Tue, 3 May 2022 15:45:19 +0200 Subject: [PATCH 3/5] Added backups as a service. Created BackupsExporter, backups.js & index. Index provides the exporter module, that would be initiated by the backups API controller. BackupsExporter is the main function with the logic. For now it creates a new folder inside content called backups and generates a file called images.zip and then serves it via the http request. Added /backups as a path / folder inside the shared helpers config file. --- core/server/api/canary/backups.js | 1 - .../services/backups/BackupsExporter.js | 57 +++++++++++++++++++ core/server/services/backups/backups.js | 14 +++++ core/server/services/backups/index.js | 10 ++++ core/server/web/api/canary/admin/routes.js | 2 +- core/shared/config/helpers.js | 2 + 6 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 core/server/services/backups/BackupsExporter.js create mode 100644 core/server/services/backups/backups.js create mode 100644 core/server/services/backups/index.js diff --git a/core/server/api/canary/backups.js b/core/server/api/canary/backups.js index fc793990924..c9a8a746558 100644 --- a/core/server/api/canary/backups.js +++ b/core/server/api/canary/backups.js @@ -1,4 +1,3 @@ - module.exports = { docName: 'backups', download: { diff --git a/core/server/services/backups/BackupsExporter.js b/core/server/services/backups/BackupsExporter.js new file mode 100644 index 00000000000..29a6ecf1d13 --- /dev/null +++ b/core/server/services/backups/BackupsExporter.js @@ -0,0 +1,57 @@ +const {compress} = require('@tryghost/zip'); +const path = require('path'); +const config = require('../../../shared/config'); +const fs = require('fs-extra'); + +/** + * Prototype for exporting images in bulk + */ + +class BackupsExporter { + + + getDirectories() { + return { + images: config.getContentPath('images'), + backups: config.getContentPath('backups') + }; + } + + /** + * + * @TODO: Perhaps let it multithread to avoid http timeouts and memory issues on big requests? + * Maybe add cases later for other types for backups? + */ + + serve() { + const self = this; + return function downloadbackups(req, res, next) { + const backupsPath = self.getDirectories().backups; + const imagesPath = self.getDirectories().images; + const zipPath = path.join(backupsPath, 'images.zip'); + let stream; + fs.ensureDir(backupsPath) + .then(async function () { + return await compress(imagesPath, zipPath); + }) + .then(function (result) { + res.set({ + 'Content-disposition': 'attachment; filename={backups}.zip'.replace('{backups}', 'images'), + 'Content-Type': 'application/zip', + 'Content-Length': result.size + }); + stream = fs.createReadStream(zipPath); + stream.pipe(res); + }) + .catch(function (err) { + next(err); + }) + .finally(function () { + return; + }); + }; + } +} + +module.exports = BackupsExporter; + diff --git a/core/server/services/backups/backups.js b/core/server/services/backups/backups.js new file mode 100644 index 00000000000..20d8468cec3 --- /dev/null +++ b/core/server/services/backups/backups.js @@ -0,0 +1,14 @@ +const BackupsExporter = require('./BackupsExporter'); + +let backupsExporter; + +const getStorage = () => { + backupsExporter = backupsExporter || new BackupsExporter(); + return backupsExporter; +}; + +module.exports = { + getZip: async () => { + return await getStorage().serve(); + } +}; \ No newline at end of file diff --git a/core/server/services/backups/index.js b/core/server/services/backups/index.js new file mode 100644 index 00000000000..45fc489f1bc --- /dev/null +++ b/core/server/services/backups/index.js @@ -0,0 +1,10 @@ +const {getZip} = require('./backups'); + +module.exports = { + /** + * Methods used in the API + */ + api: { + exporter: getZip + } +}; diff --git a/core/server/web/api/canary/admin/routes.js b/core/server/web/api/canary/admin/routes.js index 1cf17febb4a..0e2746d0646 100644 --- a/core/server/web/api/canary/admin/routes.js +++ b/core/server/web/api/canary/admin/routes.js @@ -262,7 +262,7 @@ module.exports = function apiRoutes() { ); // ## backups - // @TODO make images dynamic to allow multiple use cases in futurex + // @TODO make images dynamic to allow multiple use cases in future router.get('/backups/images/download', mw.authAdminApi, http(api.backups.download)); // ## Invites diff --git a/core/shared/config/helpers.js b/core/shared/config/helpers.js index 2155017762b..3c8dc772e01 100644 --- a/core/shared/config/helpers.js +++ b/core/shared/config/helpers.js @@ -86,6 +86,8 @@ const getContentPath = function getContentPath(type) { return path.join(this.get('paths:contentPath'), 'settings/'); case 'public': return path.join(this.get('paths:contentPath'), 'public/'); + case 'backups': + return path.join(this.get('paths:contentPath'), 'backups/'); default: // new Error is allowed here, as we do not want config to depend on @tryghost/error // @TODO: revisit this decision when @tryghost/error is no longer dependent on all of ghost-ignition From bec00baa2b5a865b4c18e6cd4a50da1296c01ed2 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Tue, 3 May 2022 16:17:23 +0200 Subject: [PATCH 4/5] added folder backups to .gitignore. Pointed API controller to backups service. --- .gitignore | 1 + config.development.json | 3 --- content/logs/README.md | 3 --- core/server/api/canary/backups.js | 7 ++++--- core/server/api/canary/utils/serializers/output/backups.js | 2 +- core/server/services/backups/BackupsExporter.js | 3 ++- 6 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 config.development.json delete mode 100644 content/logs/README.md diff --git a/.gitignore b/.gitignore index dbbf03e6bdb..11fe5cbadc5 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,7 @@ projectFilesBackup /content/media/**/* /content/files/**/* /content/public/* +/content/backups/* /content/adapters/storage/**/* /content/adapters/scheduling/**/* !/content/themes/casper diff --git a/config.development.json b/config.development.json deleted file mode 100644 index 81fb0f56c7f..00000000000 --- a/config.development.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "enableDeveloperExperiments": true -} diff --git a/content/logs/README.md b/content/logs/README.md deleted file mode 100644 index 49381f58e49..00000000000 --- a/content/logs/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Content / Logs - -This is the default log file location when Ghost runs in Production. diff --git a/core/server/api/canary/backups.js b/core/server/api/canary/backups.js index c9a8a746558..cf3f02ba771 100644 --- a/core/server/api/canary/backups.js +++ b/core/server/api/canary/backups.js @@ -1,10 +1,11 @@ +const backupsServices = require('../../services/backups'); + module.exports = { docName: 'backups', download: { - statusCode: 200, permissions: false, - query(frame) { - return + query() { + return backupsServices.api.exporter(); } } }; diff --git a/core/server/api/canary/utils/serializers/output/backups.js b/core/server/api/canary/utils/serializers/output/backups.js index d2fb41332ef..a1dbe620e5d 100644 --- a/core/server/api/canary/utils/serializers/output/backups.js +++ b/core/server/api/canary/utils/serializers/output/backups.js @@ -1,4 +1,4 @@ -const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:backups'); +const debug = require('@tryghost/debug'); module.exports = { all(data, apiConfig, frame) { diff --git a/core/server/services/backups/BackupsExporter.js b/core/server/services/backups/BackupsExporter.js index 29a6ecf1d13..a92cf065280 100644 --- a/core/server/services/backups/BackupsExporter.js +++ b/core/server/services/backups/BackupsExporter.js @@ -25,7 +25,8 @@ class BackupsExporter { serve() { const self = this; - return function downloadbackups(req, res, next) { + return function downloadBackup(req, res, next) { + console.log('running dlbk') const backupsPath = self.getDirectories().backups; const imagesPath = self.getDirectories().images; const zipPath = path.join(backupsPath, 'images.zip'); From a32af1be8e07b085506411fa58685aa9cf61ee60 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Tue, 3 May 2022 16:29:05 +0200 Subject: [PATCH 5/5] =?UTF-8?q?Modify=20BackupsExporter=20so=20that=20test?= =?UTF-8?q?s=20and=20linting=20pass=20=E2=9C=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/server/services/backups/BackupsExporter.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/server/services/backups/BackupsExporter.js b/core/server/services/backups/BackupsExporter.js index a92cf065280..a42067c2b7e 100644 --- a/core/server/services/backups/BackupsExporter.js +++ b/core/server/services/backups/BackupsExporter.js @@ -8,25 +8,20 @@ const fs = require('fs-extra'); */ class BackupsExporter { - - getDirectories() { return { images: config.getContentPath('images'), backups: config.getContentPath('backups') }; } - /** * * @TODO: Perhaps let it multithread to avoid http timeouts and memory issues on big requests? * Maybe add cases later for other types for backups? */ - serve() { const self = this; return function downloadBackup(req, res, next) { - console.log('running dlbk') const backupsPath = self.getDirectories().backups; const imagesPath = self.getDirectories().images; const zipPath = path.join(backupsPath, 'images.zip');