mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
feature: storage adapter for images and themes (#7241)
refs #2852 - we offer the option to define a storage for themes and a storage for images
This commit is contained in:
parent
f174d4d56b
commit
41ae8c03b9
4 changed files with 163 additions and 28 deletions
|
@ -96,7 +96,6 @@ ConfigManager.prototype.set = function (config) {
|
|||
activeStorageAdapter,
|
||||
activeSchedulingAdapter,
|
||||
contentPath,
|
||||
storagePath,
|
||||
schedulingPath,
|
||||
subdir,
|
||||
assetHash;
|
||||
|
@ -162,11 +161,25 @@ ConfigManager.prototype.set = function (config) {
|
|||
this._config.scheduling = this._config.scheduling || {};
|
||||
activeSchedulingAdapter = this._config.scheduling.active || defaultSchedulingAdapter;
|
||||
|
||||
// we support custom adapters located in content folder
|
||||
if (activeStorageAdapter === defaultStorageAdapter) {
|
||||
storagePath = path.join(corePath, '/server/storage/');
|
||||
// storage.active can be an object like {images: 'my-custom-image-storage-adapter', themes: 'local-file-storage'}
|
||||
// we ensure that passing a string to storage.active still works, but internal it's always an object
|
||||
if (_.isString(activeStorageAdapter)) {
|
||||
this._config.storage = _.merge(this._config.storage, {
|
||||
active: {
|
||||
images: activeStorageAdapter,
|
||||
themes: defaultStorageAdapter
|
||||
}
|
||||
});
|
||||
} else {
|
||||
storagePath = path.join(contentPath, 'storage');
|
||||
// ensure there is a default image storage adapter
|
||||
if (!this._config.storage.active.images) {
|
||||
this._config.storage.active.images = defaultSchedulingAdapter;
|
||||
}
|
||||
|
||||
// ensure there is a default theme storage adapter
|
||||
if (!this._config.storage.active.themes) {
|
||||
this._config.storage.active.themes = defaultSchedulingAdapter;
|
||||
}
|
||||
}
|
||||
|
||||
if (activeSchedulingAdapter === defaultSchedulingAdapter) {
|
||||
|
@ -184,7 +197,10 @@ ConfigManager.prototype.set = function (config) {
|
|||
configExample: path.join(appRoot, 'config.example.js'),
|
||||
corePath: corePath,
|
||||
|
||||
storage: path.join(storagePath, activeStorageAdapter),
|
||||
storagePath: {
|
||||
default: path.join(corePath, '/server/storage/'),
|
||||
custom: path.join(contentPath, 'storage/')
|
||||
},
|
||||
|
||||
contentPath: contentPath,
|
||||
themePath: path.resolve(contentPath, 'themes'),
|
||||
|
@ -205,9 +221,6 @@ ConfigManager.prototype.set = function (config) {
|
|||
active: activeSchedulingAdapter,
|
||||
path: schedulingPath
|
||||
},
|
||||
storage: {
|
||||
active: activeStorageAdapter
|
||||
},
|
||||
theme: {
|
||||
// normalise the URL by removing any trailing slash
|
||||
url: this._config.url ? this._config.url.replace(/\/$/, '') : ''
|
||||
|
|
|
@ -1,26 +1,39 @@
|
|||
var errors = require('../errors'),
|
||||
config = require('../config'),
|
||||
var errors = require('../errors'),
|
||||
config = require('../config'),
|
||||
storage = {};
|
||||
|
||||
function getStorage(storageChoice) {
|
||||
var storagePath,
|
||||
storageConfig;
|
||||
/**
|
||||
* type: images|themes
|
||||
*/
|
||||
function getStorage(type) {
|
||||
type = type || 'images';
|
||||
|
||||
storageChoice = config.storage.active;
|
||||
storagePath = config.paths.storage;
|
||||
storageConfig = config.storage[storageChoice];
|
||||
var storageChoice = config.storage.active[type],
|
||||
storageConfig = config.storage[storageChoice];
|
||||
|
||||
// CASE: type does not exist
|
||||
if (!storageChoice) {
|
||||
throw new errors.IncorrectUsage('No adapter found for type: ' + type);
|
||||
}
|
||||
|
||||
// cache?
|
||||
if (storage[storageChoice]) {
|
||||
return storage[storageChoice];
|
||||
}
|
||||
|
||||
// CASE: load adapter from custom path (.../content/storage)
|
||||
// CASE: load adapter from default path (.../server/storage)
|
||||
try {
|
||||
// TODO: determine if storage has all the necessary methods.
|
||||
storage[storageChoice] = require(storagePath);
|
||||
} catch (e) {
|
||||
errors.logError(e);
|
||||
storage[storageChoice] = require(config.paths.storagePath.custom + storageChoice);
|
||||
} catch (err1) {
|
||||
try {
|
||||
storage[storageChoice] = require(config.paths.storagePath.default + storageChoice);
|
||||
} catch (err2) {
|
||||
throw err2;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: determine if storage has all the necessary methods.
|
||||
// Instantiate and cache the storage module instance.
|
||||
storage[storageChoice] = new storage[storageChoice](storageConfig);
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ describe('Config', function () {
|
|||
'subdir',
|
||||
'config',
|
||||
'configExample',
|
||||
'storage',
|
||||
'storagePath',
|
||||
'contentPath',
|
||||
'corePath',
|
||||
'themePath',
|
||||
|
@ -175,13 +175,18 @@ describe('Config', function () {
|
|||
|
||||
describe('Storage', function () {
|
||||
it('should default to local-file-store', function () {
|
||||
var storagePath = path.join(config.paths.corePath, '/server/storage/', 'local-file-store');
|
||||
config.paths.should.have.property('storagePath', {
|
||||
default: path.join(config.paths.corePath, '/server/storage/'),
|
||||
custom: path.join(config.paths.contentPath, 'storage/')
|
||||
});
|
||||
|
||||
config.paths.should.have.property('storage', storagePath);
|
||||
config.storage.should.have.property('active', 'local-file-store');
|
||||
config.storage.should.have.property('active', {
|
||||
images: 'local-file-store',
|
||||
themes: 'local-file-store'
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow setting a custom active storage', function () {
|
||||
it('should allow setting a custom active storage as string', function () {
|
||||
var storagePath = path.join(config.paths.contentPath, 'storage', 's3');
|
||||
|
||||
configUtils.set({
|
||||
|
@ -191,10 +196,48 @@ describe('Config', function () {
|
|||
}
|
||||
});
|
||||
|
||||
config.paths.should.have.property('storage', storagePath);
|
||||
config.storage.should.have.property('active', 's3');
|
||||
config.storage.should.have.property('active', {
|
||||
images: 's3',
|
||||
themes: 'local-file-store'
|
||||
});
|
||||
|
||||
config.storage.should.have.property('s3', {});
|
||||
});
|
||||
|
||||
it('should allow setting a custom active storage as object', function () {
|
||||
var storagePath = path.join(config.paths.contentPath, 'storage', 's3');
|
||||
|
||||
configUtils.set({
|
||||
storage: {
|
||||
active: {
|
||||
themes: 's3'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
config.storage.should.have.property('active', {
|
||||
images: 'local-file-store',
|
||||
themes: 's3'
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow setting a custom active storage as object', function () {
|
||||
var storagePath = path.join(config.paths.contentPath, 'storage', 's3');
|
||||
|
||||
configUtils.set({
|
||||
storage: {
|
||||
active: {
|
||||
images: 's2',
|
||||
themes: 's3'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
config.storage.should.have.property('active', {
|
||||
images: 's2',
|
||||
themes: 's3'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Url', function () {
|
||||
|
|
66
core/test/unit/storage/index_spec.js
Normal file
66
core/test/unit/storage/index_spec.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
var fs = require('fs-extra'),
|
||||
should = require('should'),
|
||||
configUtils = require('../../utils/configUtils'),
|
||||
storage = require('../../../server/storage'),
|
||||
errors = require('../../../server/errors'),
|
||||
localFileStorage = require('../../../server/storage/local-file-store');
|
||||
|
||||
// to stop jshint complaining
|
||||
should.equal(true, true);
|
||||
|
||||
describe('storage: index_spec', function () {
|
||||
describe('default ghost storage config', function () {
|
||||
it('load without a type', function () {
|
||||
var chosenStorage = storage.getStorage();
|
||||
(chosenStorage instanceof localFileStorage).should.eql(true);
|
||||
});
|
||||
|
||||
it('load with themes type', function () {
|
||||
var chosenStorage = storage.getStorage('themes');
|
||||
(chosenStorage instanceof localFileStorage).should.eql(true);
|
||||
});
|
||||
|
||||
it('load with unknown type', function () {
|
||||
try {
|
||||
storage.getStorage('theme');
|
||||
} catch (err) {
|
||||
(err instanceof errors.IncorrectUsage).should.eql(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom ghost storage config', function () {
|
||||
it('images storage adapter is custom, themes is default', function () {
|
||||
configUtils.set({
|
||||
storage: {
|
||||
active: {
|
||||
images: 'custom-adapter'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var jsFile = '' +
|
||||
'var util = require(\'util\');' +
|
||||
'var StorageBase = require(__dirname + \'/../../core/server/storage/base\');' +
|
||||
'var AnotherAdapter = function (){ StorageBase.call(this); };' +
|
||||
'util.inherits(AnotherAdapter, StorageBase);' +
|
||||
'AnotherAdapter.prototype.exists = function (){};' +
|
||||
'AnotherAdapter.prototype.save = function (){};' +
|
||||
'module.exports = AnotherAdapter', chosenStorage;
|
||||
|
||||
if (!fs.existsSync(configUtils.config.paths.storagePath.custom)) {
|
||||
fs.mkdirSync(configUtils.config.paths.storagePath.custom);
|
||||
}
|
||||
|
||||
fs.writeFileSync(configUtils.config.paths.storagePath.custom + 'custom-adapter.js', jsFile);
|
||||
|
||||
chosenStorage = storage.getStorage('themes');
|
||||
(chosenStorage instanceof localFileStorage).should.eql(true);
|
||||
|
||||
chosenStorage = storage.getStorage('images');
|
||||
(chosenStorage instanceof localFileStorage).should.eql(false);
|
||||
|
||||
fs.unlinkSync(configUtils.config.paths.storagePath.custom + 'custom-adapter.js');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue