0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Moves storage module to use prototypes for inheritance and structure.

addresses #2852

- Moves storage modules to use prototypes and to create prototypes
that inherit from the base storage ctor.

- Makes storage/base conform to an all Promise interface.
This commit is contained in:
Harry Wolff 2014-08-28 23:48:49 -04:00
parent bd163ada46
commit 66845def85
7 changed files with 109 additions and 105 deletions

View file

@ -29,7 +29,7 @@ upload = {
* @returns {Promise} Success * @returns {Promise} Success
*/ */
add: function (options) { add: function (options) {
var store = storage.get_storage(), var store = storage.getStorage(),
type, type,
ext, ext,
filepath; filepath;

View file

@ -278,7 +278,7 @@ setupMiddleware = function (server) {
// Static assets // Static assets
expressServer.use('/shared', express['static'](path.join(corePath, '/shared'), {maxAge: utils.ONE_HOUR_MS})); expressServer.use('/shared', express['static'](path.join(corePath, '/shared'), {maxAge: utils.ONE_HOUR_MS}));
expressServer.use('/content/images', storage.get_storage().serve()); expressServer.use('/content/images', storage.getStorage().serve());
expressServer.use('/ghost/scripts', express['static'](path.join(corePath, '/built/scripts'), {maxAge: utils.ONE_YEAR_MS})); expressServer.use('/ghost/scripts', express['static'](path.join(corePath, '/built/scripts'), {maxAge: utils.ONE_YEAR_MS}));
expressServer.use('/public', express['static'](path.join(corePath, '/built/public'), {maxAge: utils.ONE_YEAR_MS})); expressServer.use('/public', express['static'](path.join(corePath, '/built/public'), {maxAge: utils.ONE_YEAR_MS}));

View file

@ -1,52 +1,48 @@
var moment = require('moment'), var moment = require('moment'),
path = require('path'), path = require('path');
Promise = require('bluebird'),
baseStore;
// TODO: would probably be better to put these on the prototype and have proper constructors etc function StorageBase() {
baseStore = { }
'getTargetDir': function (baseDir) {
var m = moment(new Date().getTime()),
month = m.format('MMM'),
year = m.format('YYYY');
if (baseDir) { StorageBase.prototype.getTargetDir = function (baseDir) {
return path.join(baseDir, year, month); var m = moment(new Date().getTime()),
} month = m.format('MMM'),
year = m.format('YYYY');
return path.join(year, month); if (baseDir) {
}, return path.join(baseDir, year, month);
'generateUnique': function (store, dir, name, ext, i, done) {
var self = this,
filename,
append = '';
if (i) {
append = '-' + i;
}
filename = path.join(dir, name + append + ext);
store.exists(filename).then(function (exists) {
if (exists) {
setImmediate(function () {
i = i + 1;
self.generateUnique(store, dir, name, ext, i, done);
});
} else {
done(filename);
}
});
},
'getUniqueFileName': function (store, image, targetDir) {
var ext = path.extname(image.name),
name = path.basename(image.name, ext).replace(/[\W]/gi, '-'),
self = this;
return new Promise(function (resolve) {
self.generateUnique(store, targetDir, name, ext, 0, resolve);
});
} }
return path.join(year, month);
}; };
module.exports = baseStore; StorageBase.prototype.generateUnique = function (store, dir, name, ext, i) {
var self = this,
filename,
append = '';
if (i) {
append = '-' + i;
}
filename = path.join(dir, name + append + ext);
return store.exists(filename).then(function (exists) {
if (exists) {
i = i + 1;
return self.generateUnique(store, dir, name, ext, i);
} else {
return filename;
}
});
};
StorageBase.prototype.getUniqueFileName = function (store, image, targetDir) {
var ext = path.extname(image.name),
name = path.basename(image.name, ext).replace(/[\W]/gi, '-'),
self = this;
return self.generateUnique(store, targetDir, name, ext, 0);
};
module.exports = StorageBase;

View file

@ -1,22 +1,26 @@
var errors = require('../errors'), var errors = require('../errors'),
storage; storage = {};
function get_storage() { function getStorage(storageChoice) {
// TODO: this is where the check for storage apps should go // TODO: this is where the check for storage apps should go
// Local file system is the default // Local file system is the default. Fow now that is all we support.
var storageChoice = 'localfilesystem'; storageChoice = 'localfilesystem';
if (storage) { if (storage[storageChoice]) {
return storage; return storage[storageChoice];
} }
try { try {
// TODO: determine if storage has all the necessary methods // TODO: determine if storage has all the necessary methods.
storage = require('./' + storageChoice); storage[storageChoice] = require('./' + storageChoice);
} catch (e) { } catch (e) {
errors.logError(e); errors.logError(e);
} }
return storage;
// Instantiate and cache the storage module instance.
storage[storageChoice] = new storage[storageChoice]();
return storage[storageChoice];
} }
module.exports.get_storage = get_storage; module.exports.getStorage = getStorage;

View file

@ -1,56 +1,57 @@
// # Local File System Image Storage module // # Local File System Image Storage module
// The (default) module for storing images, using the local file system // The (default) module for storing images, using the local file system
var _ = require('lodash'), var express = require('express'),
express = require('express'),
fs = require('fs-extra'), fs = require('fs-extra'),
path = require('path'), path = require('path'),
util = require('util'),
Promise = require('bluebird'), Promise = require('bluebird'),
errors = require('../errors'), errors = require('../errors'),
config = require('../config'), config = require('../config'),
utils = require('../utils'), utils = require('../utils'),
baseStore = require('./base'), baseStore = require('./base');
localFileStore; function LocalFileStore() {
}
util.inherits(LocalFileStore, baseStore);
localFileStore = _.extend(baseStore, { // ### Save
// ### Save // Saves the image to storage (the file system)
// Saves the image to storage (the file system) // - image is the express image object
// - image is the express image object // - returns a promise which ultimately returns the full url to the uploaded image
// - returns a promise which ultimately returns the full url to the uploaded image LocalFileStore.prototype.save = function (image) {
'save': function (image) { var targetDir = this.getTargetDir(config.paths.imagesPath),
var targetDir = this.getTargetDir(config.paths.imagesPath), targetFilename;
targetFilename;
return this.getUniqueFileName(this, image, targetDir).then(function (filename) { return this.getUniqueFileName(this, image, targetDir).then(function (filename) {
targetFilename = filename; targetFilename = filename;
return Promise.promisify(fs.mkdirs)(targetDir); return Promise.promisify(fs.mkdirs)(targetDir);
}).then(function () { }).then(function () {
return Promise.promisify(fs.copy)(image.path, targetFilename); return Promise.promisify(fs.copy)(image.path, targetFilename);
}).then(function () { }).then(function () {
// The src for the image must be in URI format, not a file system path, which in Windows uses \ // The src for the image must be in URI format, not a file system path, which in Windows uses \
// For local file system storage can use relative path so add a slash // For local file system storage can use relative path so add a slash
var fullUrl = (config.paths.subdir + '/' + config.paths.imagesRelPath + '/' + path.relative(config.paths.imagesPath, targetFilename)).replace(new RegExp('\\' + path.sep, 'g'), '/'); var fullUrl = (config.paths.subdir + '/' + config.paths.imagesRelPath + '/' +
return fullUrl; path.relative(config.paths.imagesPath, targetFilename)).replace(new RegExp('\\' + path.sep, 'g'), '/');
}).catch(function (e) { return fullUrl;
errors.logError(e); }).catch(function (e) {
return Promise.reject(e); errors.logError(e);
return Promise.reject(e);
});
};
LocalFileStore.prototype.exists = function (filename) {
return new Promise(function (resolve) {
fs.exists(filename, function (exists) {
resolve(exists);
}); });
}, });
};
'exists': function (filename) { // middleware for serving the files
return new Promise(function (resolve) { LocalFileStore.prototype.serve = function () {
fs.exists(filename, function (exists) { // For some reason send divides the max age number by 1000
resolve(exists); return express['static'](config.paths.imagesPath, {maxAge: utils.ONE_YEAR_MS});
}); };
});
},
// middleware for serving the files module.exports = LocalFileStore;
'serve': function () {
// For some reason send divides the max age number by 1000
return express['static'](config.paths.imagesPath, {maxAge: utils.ONE_YEAR_MS});
}
});
module.exports = localFileStore;

View file

@ -17,7 +17,7 @@ describe('Upload API', function () {
// Doesn't test the DB // Doesn't test the DB
afterEach(function () { afterEach(function () {
storage.get_storage.restore(); storage.getStorage.restore();
fs.unlink.restore(); fs.unlink.restore();
}); });
@ -26,7 +26,7 @@ describe('Upload API', function () {
store.save = sinon.stub().returns(Promise.resolve('URL')); store.save = sinon.stub().returns(Promise.resolve('URL'));
store.exists = sinon.stub().returns(Promise.resolve(true)); store.exists = sinon.stub().returns(Promise.resolve(true));
store.destroy = sinon.stub().returns(Promise.resolve()); store.destroy = sinon.stub().returns(Promise.resolve());
sinon.stub(storage, 'get_storage').returns(store); sinon.stub(storage, 'getStorage').returns(store);
sinon.stub(fs, 'unlink').yields(); sinon.stub(fs, 'unlink').yields();
}); });

View file

@ -7,7 +7,8 @@ var fs = require('fs-extra'),
rewire = require('rewire'), rewire = require('rewire'),
_ = require('lodash'), _ = require('lodash'),
config = rewire('../../server/config'), config = rewire('../../server/config'),
localfilesystem = rewire('../../server/storage/localfilesystem'); LocalFileStore = rewire('../../server/storage/localfilesystem'),
localfilesystem;
// To stop jshint complaining // To stop jshint complaining
should.equal(true, true); should.equal(true, true);
@ -16,10 +17,10 @@ describe('Local File System Storage', function () {
var image, var image,
overrideConfig = function (newConfig) { overrideConfig = function (newConfig) {
var existingConfig = localfilesystem.__get__('config'), var existingConfig = LocalFileStore.__get__('config'),
updatedConfig = _.extend(existingConfig, newConfig); updatedConfig = _.extend(existingConfig, newConfig);
config.set(updatedConfig); config.set(updatedConfig);
localfilesystem.__set__('config', updatedConfig); LocalFileStore.__set__('config', updatedConfig);
}; };
beforeEach(function () { beforeEach(function () {
@ -38,6 +39,8 @@ describe('Local File System Storage', function () {
// Sat Sep 07 2013 21:24 // Sat Sep 07 2013 21:24
this.clock = sinon.useFakeTimers(new Date(2013, 8, 7, 21, 24).getTime()); this.clock = sinon.useFakeTimers(new Date(2013, 8, 7, 21, 24).getTime());
localfilesystem = new LocalFileStore();
}); });
afterEach(function () { afterEach(function () {
@ -181,4 +184,4 @@ describe('Local File System Storage', function () {
}).catch(done); }).catch(done);
}); });
}); });
}); });