🎨 Ghost bootstrap: optimise requires (#8121)

* 🎨  Ghost bootstrap: optimise requires

no issue

- require as less as possible on bootstrap

* do not load icojs on bootstrap
This commit is contained in:
Katharina Irrgang 2017-03-13 21:07:12 +01:00 committed by Hannah Wolfe
parent e0cd5b55ce
commit 7556e68c48
9 changed files with 222 additions and 138 deletions

View File

@ -309,7 +309,7 @@ authentication = {
}));
}
spamPrevention.userLogin.reset(opts.ip, tokenParts.email + 'login');
spamPrevention.userLogin().reset(opts.ip, tokenParts.email + 'login');
return models.User.changePassword({
oldPassword: oldPassword,

View File

@ -8,19 +8,16 @@
// there if available. The cacheId is a combination of `updated_at` and the `slug`.
var hbs = require('express-hbs'),
Promise = require('bluebird'),
Amperize = require('amperize'),
moment = require('moment'),
sanitizeHtml = require('sanitize-html'),
logging = require('../../../../logging'),
i18n = require('../../../../i18n'),
errors = require('../../../../errors'),
makeAbsoluteUrl = require('../../../../utils/make-absolute-urls'),
utils = require('../../../../utils'),
cheerio = require('cheerio'),
amperize = new Amperize(),
amperizeCache = {},
allowedAMPTags = [],
allowedAMPAttributes = {},
amperize,
cleanHTML,
ampHTML;
@ -120,6 +117,9 @@ function getAmperizeHTML(html, post) {
return;
}
var Amperize = require('amperize');
amperize = amperize || new Amperize();
// make relative URLs abolute
html = makeAbsoluteUrl(html, utils.url.urlFor('home', true), post.url).html();
@ -154,7 +154,9 @@ function getAmperizeHTML(html, post) {
}
function ampContent() {
var amperizeHTML = {
var sanitizeHtml = require('sanitize-html'),
cheerio = require('cheerio'),
amperizeHTML = {
amperize: getAmperizeHTML(this.html, this)
};

View File

@ -17,7 +17,7 @@ function exchangeRefreshToken(client, refreshToken, scope, body, authInfo, done)
var token = model.toJSON();
if (token.expires > Date.now()) {
spamPrevention.userLogin.reset(authInfo.ip, body.refresh_token + 'login');
spamPrevention.userLogin().reset(authInfo.ip, body.refresh_token + 'login');
authUtils.createTokens({
clientId: token.client_id,
@ -54,7 +54,7 @@ function exchangePassword(client, username, password, scope, body, authInfo, don
});
})
.then(function then(response) {
spamPrevention.userLogin.reset(authInfo.ip, username + 'login');
spamPrevention.userLogin().reset(authInfo.ip, username + 'login');
return done(null, response.access_token, response.refresh_token, {expires_in: response.expires_in});
});
})
@ -83,7 +83,7 @@ function exchangeAuthorizationCode(req, res, next) {
}));
}
spamPrevention.userLogin.reset(req.authInfo.ip, req.body.authorizationCode + 'login');
spamPrevention.userLogin().reset(req.authInfo.ip, req.body.authorizationCode + 'login');
authUtils.createTokens({
clientId: req.client.id,

View File

@ -2,7 +2,6 @@
// Handles sending email for Ghost
var _ = require('lodash'),
Promise = require('bluebird'),
nodemailer = require('nodemailer'),
validator = require('validator'),
config = require('../config'),
settingsCache = require('../settings/cache'),
@ -10,13 +9,12 @@ var _ = require('lodash'),
utils = require('../utils');
function GhostMailer() {
var transport = config.get('mail') && config.get('mail').transport || 'direct',
var nodemailer = require('nodemailer'),
transport = config.get('mail') && config.get('mail').transport || 'direct',
options = config.get('mail') && _.clone(config.get('mail').options) || {};
this.state = {};
this.transport = nodemailer.createTransport(transport, options);
this.state.usingDirect = transport === 'direct';
}

View File

@ -1,8 +1,4 @@
var ExpressBrute = require('express-brute'),
BruteKnex = require('brute-knex'),
knexInstance = require('../../data/db/connection'),
store = new BruteKnex({tablename: 'brute', createTable:false, knex: knexInstance}),
moment = require('moment'),
var moment = require('moment'),
errors = require('../../errors'),
config = require('../../config'),
spam = config.get('spam') || {},
@ -14,9 +10,15 @@ var ExpressBrute = require('express-brute'),
spamUserLogin = spam.user_login || {},
i18n = require('../../i18n'),
store,
handleStoreError,
globalBlock,
globalReset,
privateBlogInstance,
globalResetInstance,
globalBlockInstance,
userLoginInstance,
userResetInstance,
privateBlog,
userLogin,
userReset,
@ -46,93 +48,163 @@ handleStoreError = function handleStoreError(err) {
// requests from a single IP
// We allow for a generous number of requests here to prevent communites on the same IP bing barred on account of a single suer
// Defaults to 50 attempts per hour and locks the endpoint for an hour
globalBlock = new ExpressBrute(store,
_.extend({
attachResetToRequest: false,
failCallback: function (req, res, next, nextValidRequestDate) {
return next(new errors.TooManyRequestsError({
message: 'Too many attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error',
{rfa: spamGlobalBlock.freeRetries + 1 || 5, rfp: spamGlobalBlock.lifetime || 60 * 60}),
help: i18n.t('errors.middleware.spamprevention.tooManyAttempts')
}));
},
handleStoreError: handleStoreError
}, _.pick(spamGlobalBlock, spamConfigKeys))
);
globalBlock = function globalBlock() {
var ExpressBrute = require('express-brute'),
BruteKnex = require('brute-knex'),
db = require('../../data/db');
globalReset = new ExpressBrute(store,
_.extend({
attachResetToRequest: false,
failCallback: function (req, res, next, nextValidRequestDate) {
// TODO use i18n again
return next(new errors.TooManyRequestsError({
message: 'Too many attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error',
{rfa: spamGlobalReset.freeRetries + 1 || 5, rfp: spamGlobalReset.lifetime || 60 * 60}),
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
}));
},
handleStoreError: handleStoreError
}, _.pick(spamGlobalReset, spamConfigKeys))
);
store = store || new BruteKnex({
tablename: 'brute',
createTable: false,
knex: db.knex
});
globalBlockInstance = globalBlockInstance || new ExpressBrute(store,
_.extend({
attachResetToRequest: false,
failCallback: function (req, res, next, nextValidRequestDate) {
return next(new errors.TooManyRequestsError({
message: 'Too many attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error',
{rfa: spamGlobalBlock.freeRetries + 1 || 5, rfp: spamGlobalBlock.lifetime || 60 * 60}),
help: i18n.t('errors.middleware.spamprevention.tooManyAttempts')
}));
},
handleStoreError: handleStoreError
}, _.pick(spamGlobalBlock, spamConfigKeys))
);
return globalBlockInstance;
};
globalReset = function globalReset() {
var ExpressBrute = require('express-brute'),
BruteKnex = require('brute-knex'),
db = require('../../data/db');
store = store || new BruteKnex({
tablename: 'brute',
createTable: false,
knex: db.knex
});
globalResetInstance = globalResetInstance || new ExpressBrute(store,
_.extend({
attachResetToRequest: false,
failCallback: function (req, res, next, nextValidRequestDate) {
// TODO use i18n again
return next(new errors.TooManyRequestsError({
message: 'Too many attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error',
{rfa: spamGlobalReset.freeRetries + 1 || 5, rfp: spamGlobalReset.lifetime || 60 * 60}),
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
}));
},
handleStoreError: handleStoreError
}, _.pick(spamGlobalReset, spamConfigKeys))
);
return globalResetInstance;
};
// Stops login attempts for a user+IP pair with an increasing time period starting from 10 minutes
// and rising to a week in a fibonnaci sequence
// The user+IP count is reset when on successful login
// Default value of 5 attempts per user+IP pair
userLogin = new ExpressBrute(store,
_.extend({
attachResetToRequest: true,
failCallback: function (req, res, next, nextValidRequestDate) {
return next(new errors.TooManyRequestsError({
message: 'Too many sign-in attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
// TODO add more options to i18n
context: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context'),
help: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
}));
},
handleStoreError: handleStoreError
}, _.pick(spamUserLogin, spamConfigKeys))
);
userLogin = function userLogin() {
var ExpressBrute = require('express-brute'),
BruteKnex = require('brute-knex'),
db = require('../../data/db');
store = store || new BruteKnex({
tablename: 'brute',
createTable: false,
knex: db.knex
});
userLoginInstance = userLoginInstance || new ExpressBrute(store,
_.extend({
attachResetToRequest: true,
failCallback: function (req, res, next, nextValidRequestDate) {
return next(new errors.TooManyRequestsError({
message: 'Too many sign-in attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
// TODO add more options to i18n
context: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context'),
help: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
}));
},
handleStoreError: handleStoreError
}, _.pick(spamUserLogin, spamConfigKeys))
);
return userLoginInstance;
};
// Stop password reset requests when there are (freeRetries + 1) requests per lifetime per email
// Defaults here are 5 attempts per hour for a user+IP pair
// The endpoint is then locked for an hour
userReset = new ExpressBrute(store,
_.extend({
attachResetToRequest: true,
failCallback: function (req, res, next, nextValidRequestDate) {
return next(new errors.TooManyRequestsError({
message: 'Too many password reset attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.error',
{rfa: spamUserReset.freeRetries + 1 || 5, rfp: spamUserReset.lifetime || 60 * 60}),
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.context')
}));
},
handleStoreError: handleStoreError
}, _.pick(spamUserReset, spamConfigKeys))
);
userReset = function userReset() {
var ExpressBrute = require('express-brute'),
BruteKnex = require('brute-knex'),
db = require('../../data/db');
store = store || new BruteKnex({
tablename: 'brute',
createTable: false,
knex: db.knex
});
userResetInstance = userResetInstance || new ExpressBrute(store,
_.extend({
attachResetToRequest: true,
failCallback: function (req, res, next, nextValidRequestDate) {
return next(new errors.TooManyRequestsError({
message: 'Too many password reset attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.error',
{rfa: spamUserReset.freeRetries + 1 || 5, rfp: spamUserReset.lifetime || 60 * 60}),
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.context')
}));
},
handleStoreError: handleStoreError
}, _.pick(spamUserReset, spamConfigKeys))
);
return userResetInstance;
};
// This protects a private blog from spam attacks. The defaults here allow 10 attempts per IP per hour
// The endpoint is then locked for an hour
privateBlog = new ExpressBrute(store,
_.extend({
attachResetToRequest: false,
failCallback: function (req, res, next, nextValidRequestDate) {
logging.error(new errors.GhostError({
message: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.error',
{rfa: spamPrivateBlog.freeRetries + 1 || 5, rfp: spamPrivateBlog.lifetime || 60 * 60}),
context: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
}));
privateBlog = function privateBlog() {
var ExpressBrute = require('express-brute'),
BruteKnex = require('brute-knex'),
db = require('../../data/db');
return next(new errors.GhostError({
message: 'Too many private sign-in attempts try again in ' + moment(nextValidRequestDate).fromNow(true)
}));
},
handleStoreError: handleStoreError
}, _.pick(spamPrivateBlog, spamConfigKeys))
);
store = store || new BruteKnex({
tablename: 'brute',
createTable: false,
knex: db.knex
});
privateBlogInstance = privateBlogInstance || new ExpressBrute(store,
_.extend({
attachResetToRequest: false,
failCallback: function (req, res, next, nextValidRequestDate) {
logging.error(new errors.GhostError({
message: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.error',
{rfa: spamPrivateBlog.freeRetries + 1 || 5, rfp: spamPrivateBlog.lifetime || 60 * 60}),
context: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
}));
return next(new errors.GhostError({
message: 'Too many private sign-in attempts try again in ' + moment(nextValidRequestDate).fromNow(true)
}));
},
handleStoreError: handleStoreError
}, _.pick(spamPrivateBlog, spamConfigKeys))
);
return privateBlogInstance;
};
module.exports = {
globalBlock: globalBlock,

View File

@ -9,56 +9,69 @@ module.exports = {
/**
* block per route per ip
*/
globalBlock: spamPrevention.globalBlock.getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
next(url.parse(req.url).pathname);
}
}),
globalBlock: function (req, res, next) {
return spamPrevention.globalBlock().getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
next(url.parse(req.url).pathname);
}
})(req, res, next);
},
/**
* block per route per ip
*/
globalReset: spamPrevention.globalReset.getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
next(url.parse(req.url).pathname);
}
}),
globalReset: function (req, res, next) {
return spamPrevention.globalReset().getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
next(url.parse(req.url).pathname);
}
})(req, res, next);
},
/**
* block per user
* username === email!
*/
userLogin: spamPrevention.userLogin.getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
if (req.body.username) {
return next(req.body.username + 'login');
}
userLogin: function (req, res, next) {
return spamPrevention.userLogin().getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
if (req.body.username) {
return next(req.body.username + 'login');
}
if (req.body.authorizationCode) {
return next(req.body.authorizationCode + 'login');
}
if (req.body.authorizationCode) {
return next(req.body.authorizationCode + 'login');
}
if (req.body.refresh_token) {
return next(req.body.refresh_token + 'login');
}
if (req.body.refresh_token) {
return next(req.body.refresh_token + 'login');
}
return next();
}
}),
return next();
}
})(req, res, next);
},
/**
* block per user
*/
userReset: spamPrevention.userReset.getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
next(req.body.username + 'reset');
}
}),
privateBlog: spamPrevention.privateBlog.getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
next('privateblog');
}
})
userReset: function (req, res, next) {
return spamPrevention.userReset().getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
next(req.body.username + 'reset');
}
})(req, res, next);
},
/**
* block per ip
*/
privateBlog: function (req, res, next) {
return spamPrevention.privateBlog().getMiddleware({
ignoreIP: false,
key: function (req, res, next) {
next('privateblog');
}
})(req, res, next);
}
};

View File

@ -1,6 +1,5 @@
var errors = require('../../errors'),
config = require('../../config'),
ICO = require('icojs'),
fs = require('fs'),
Promise = require('bluebird'),
sizeOf = require('image-size'),
@ -15,7 +14,8 @@ validIconSize = function validIconSize(size) {
getIconDimensions = function getIconDimensions(icon) {
return new Promise(function getImageSize(resolve, reject) {
var arrayBuffer;
var arrayBuffer,
ICO = require('icojs');
// image-size doesn't support .ico files
if (icon.name.match(/.ico$/i)) {

View File

@ -1,8 +1,8 @@
var archiver = require('archiver'),
fs = require('fs');
var fs = require('fs');
module.exports = function zipFolder(folderToZip, destination, callback) {
var output = fs.createWriteStream(destination),
var archiver = require('archiver'),
output = fs.createWriteStream(destination),
archive = archiver.create('zip', {});
output.on('close', function () {
@ -17,4 +17,3 @@ module.exports = function zipFolder(folderToZip, destination, callback) {
archive.pipe(output);
archive.finalize();
};

View File

@ -22,7 +22,7 @@ describe('OAuth', function () {
res = {};
next = sandbox.spy();
sandbox.stub(spamPrevention.userLogin, 'reset');
sandbox.stub(spamPrevention.userLogin(), 'reset');
});
afterEach(function () {
@ -80,7 +80,7 @@ describe('OAuth', function () {
json.should.have.property('expires_in');
json.should.have.property('token_type', 'Bearer');
next.called.should.eql(false);
spamPrevention.userLogin.reset.called.should.eql(true);
spamPrevention.userLogin().reset.called.should.eql(true);
done();
} catch (err) {
done(err);