From 2a7ef77a7b09816caba48e6cc19b7375d9acbd1b Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 9 Nov 2021 16:05:18 +0400 Subject: [PATCH] Added ability to delete existing files through storage adapters refs https://github.com/TryGhost/Toolbox/issues/120 - When editing an uploaded media thumbnail file there'a need to remove existing thumbnail to keep media files:thumbnails 1:1. - Because the API client only has a public URL under which the resource is served it can only provide that as an API input, the `urlToPath` was also added to the base class of LocalStorageAdapter (it might be moved up to the BaseAdapter in the future if we see a need) --- .../adapters/storage/LocalFilesStorage.js | 3 +- .../adapters/storage/LocalImagesStorage.js | 1 + .../adapters/storage/LocalMediaStorage.js | 3 +- .../adapters/storage/LocalStorageBase.js | 35 ++++++++++++++++--- .../adapters/storage/LocalBaseStorage.test.js | 32 +++++++++++++++++ 5 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 test/unit/server/adapters/storage/LocalBaseStorage.test.js diff --git a/core/server/adapters/storage/LocalFilesStorage.js b/core/server/adapters/storage/LocalFilesStorage.js index 4797c77d3f..6583035207 100644 --- a/core/server/adapters/storage/LocalFilesStorage.js +++ b/core/server/adapters/storage/LocalFilesStorage.js @@ -1,4 +1,4 @@ -// # Local File System Video Storage module +// # Local File System Storage module // The (default) module for storing media, using the local file system const config = require('../../../shared/config'); const constants = require('@tryghost/constants'); @@ -8,6 +8,7 @@ class LocalFilesStorage extends LocalStorageBase { constructor() { super({ storagePath: config.getContentPath('files'), + siteUrl: config.getSiteUrl(), staticFileURLPrefix: constants.STATIC_FILES_URL_PREFIX }); } diff --git a/core/server/adapters/storage/LocalImagesStorage.js b/core/server/adapters/storage/LocalImagesStorage.js index 7ea4f83fc9..7f68ef5135 100644 --- a/core/server/adapters/storage/LocalImagesStorage.js +++ b/core/server/adapters/storage/LocalImagesStorage.js @@ -19,6 +19,7 @@ class LocalImagesStorage extends LocalStorageBase { super({ storagePath: config.getContentPath('images'), staticFileURLPrefix: urlUtils.STATIC_IMAGE_URL_PREFIX, + siteUrl: config.getSiteUrl(), errorMessages: messages }); } diff --git a/core/server/adapters/storage/LocalMediaStorage.js b/core/server/adapters/storage/LocalMediaStorage.js index 85ebfb363e..9740570ae5 100644 --- a/core/server/adapters/storage/LocalMediaStorage.js +++ b/core/server/adapters/storage/LocalMediaStorage.js @@ -1,4 +1,4 @@ -// # Local File System Video Storage module +// # Local File System Media Storage module // The (default) module for storing media, using the local file system const config = require('../../../shared/config'); const constants = require('@tryghost/constants'); @@ -15,6 +15,7 @@ class LocalMediaStore extends LocalStorageBase { super({ storagePath: config.getContentPath('media'), staticFileURLPrefix: constants.STATIC_MEDIA_URL_PREFIX, + siteUrl: config.getSiteUrl(), errorMessages: messages }); } diff --git a/core/server/adapters/storage/LocalStorageBase.js b/core/server/adapters/storage/LocalStorageBase.js index e323401013..8ac5d81fe6 100644 --- a/core/server/adapters/storage/LocalStorageBase.js +++ b/core/server/adapters/storage/LocalStorageBase.js @@ -16,7 +16,8 @@ const StorageBase = require('ghost-storage-base'); const messages = { notFound: 'File not found', notFoundWithRef: 'File not found: {file}', - cannotRead: 'Could not read file: {file}' + cannotRead: 'Could not read file: {file}', + invalidUrlParameter: `The URL "{url}" is not a valid URL for this site.` }; class LocalStorageBase extends StorageBase { @@ -24,17 +25,20 @@ class LocalStorageBase extends StorageBase { * * @param {Object} options * @param {String} options.storagePath + * @param {String} options.siteUrl * @param {String} [options.staticFileURLPrefix] * @param {Object} [options.errorMessages] * @param {String} [options.errorMessages.notFound] * @param {String} [options.errorMessages.notFoundWithRef] * @param {String} [options.errorMessages.cannotRead] */ - constructor({storagePath, staticFileURLPrefix, errorMessages}) { + constructor({storagePath, staticFileURLPrefix, siteUrl, errorMessages}) { super(); this.storagePath = storagePath; this.staticFileURLPrefix = staticFileURLPrefix; + this.siteUrl = siteUrl; + this.staticFileUrl = `${siteUrl}${staticFileURLPrefix}`; this.errorMessages = errorMessages || messages; } @@ -71,6 +75,26 @@ class LocalStorageBase extends StorageBase { return fullUrl; } + /** + * + * @param {String} url full url under which the stored content is served, result of save method + * @returns {String} path under which the content is stored + */ + urlToPath(url) { + let filePath; + + if (url.match(this.staticFileUrl)) { + filePath = url.replace(this.staticFileUrl, ''); + filePath = path.join(this.storagePath, filePath); + } else { + throw new errors.IncorrectUsageError({ + message: tpl(messages.invalidUrlParameter, {url}) + }); + } + + return filePath; + } + exists(fileName, targetDir) { const filePath = path.join(targetDir || this.storagePath, fileName); @@ -132,11 +156,12 @@ class LocalStorageBase extends StorageBase { } /** - * Not implemented. + * @param {String} filePath * @returns {Promise.<*>} */ - delete() { - return Promise.reject('not implemented'); + async delete(fileName, targetDir) { + const filePath = path.join(targetDir, fileName); + return await fs.remove(filePath); } /** diff --git a/test/unit/server/adapters/storage/LocalBaseStorage.test.js b/test/unit/server/adapters/storage/LocalBaseStorage.test.js new file mode 100644 index 0000000000..dd0724ee24 --- /dev/null +++ b/test/unit/server/adapters/storage/LocalBaseStorage.test.js @@ -0,0 +1,32 @@ +const should = require('should'); +const LocalStorageBase = require('../../../../../core/server/adapters/storage/LocalStorageBase'); + +describe('Local Storage Base', function () { + describe('urlToPath', function () { + it('returns path from url', function () { + let localStorageBase = new LocalStorageBase({ + storagePath: '/media-storage/path/', + staticFileURLPrefix: 'content/media', + siteUrl: 'http://example.com/blog/' + }); + + localStorageBase.urlToPath('http://example.com/blog/content/media/2021/11/media.mp4') + .should.eql('/media-storage/path/2021/11/media.mp4'); + }); + + it('throws if the url does not match current site', function () { + let localStorageBase = new LocalStorageBase({ + storagePath: '/media-storage/path/', + staticFileURLPrefix: 'content/media', + url: 'http://example.com/blog/' + }); + + try { + localStorageBase.urlToPath('http://anothersite.com/blog/content/media/2021/11/media.mp4'); + should.fail('urlToPath when urls do not match'); + } catch (error) { + error.message.should.eql('The URL "http://anothersite.com/blog/content/media/2021/11/media.mp4" is not a valid URL for this site.'); + } + }); + }); +});