mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-11 00:37:55 +03:00
🎨 configurable logging with bunyan (#7431)
- 🛠 add bunyan and prettyjson, remove morgan - ✨ add logging module - GhostLogger class that handles setup of bunyan - PrettyStream for stdout - ✨ config for logging - @TODO: testing level fatal? - ✨ log each request via GhostLogger (express middleware) - @TODO: add errors to output - 🔥 remove errors.updateActiveTheme - we can read the value from config - 🔥 remove 15 helper functions in core/server/errors/index.js - all these functions get replaced by modules: 1. logging 2. error middleware handling for html/json 3. error creation (which will be part of PR #7477) - ✨ add express error handler for html/json - one true error handler for express responses - contains still some TODO's, but they are not high priority for first implementation/integration - this middleware only takes responsibility of either rendering html responses or return json error responses - 🎨 use new express error handler in middleware/index - 404 and 500 handling - 🎨 return error instead of error message in permissions/index.js - the rule for error handling should be: if you call a unit, this unit should return a custom Ghost error - 🎨 wrap serve static module - rule: if you call a module/unit, you should always wrap this error - it's always the same rule - so the caller never has to worry about what comes back - it's always a clear error instance - in this case: we return our notfounderror if serve static does not find the resource - this avoid having checks everywhere - 🎨 replace usages of errors/index.js functions and adapt tests - use logging.error, logging.warn - make tests green - remove some usages of logging and throwing api errors -> because when a request is involved, logging happens automatically - 🐛 return errorDetails to Ghost-Admin - errorDetails is used for Theme error handling - 🎨 use 500er error for theme is missing error in theme-handler - 🎨 extend file rotation to 1w
This commit is contained in:
parent
dea984565d
commit
1882278b5b
@ -8,6 +8,7 @@ var _ = require('lodash'),
|
||||
globalUtils = require('../utils'),
|
||||
utils = require('./utils'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
models = require('../models'),
|
||||
events = require('../events'),
|
||||
config = require('../config'),
|
||||
@ -488,14 +489,10 @@ authentication = {
|
||||
}]
|
||||
};
|
||||
|
||||
apiMail.send(payload, {context: {internal: true}}).catch(function (error) {
|
||||
errors.logError(
|
||||
error.message,
|
||||
i18n.t(
|
||||
'errors.api.authentication.unableToSendWelcomeEmail'
|
||||
),
|
||||
i18n.t('errors.api.authentication.checkEmailConfigInstructions', {url: 'http://support.ghost.org/mail/'})
|
||||
);
|
||||
apiMail.send(payload, {context: {internal: true}}).catch(function (err) {
|
||||
err.context = i18n.t('errors.api.authentication.unableToSendWelcomeEmail');
|
||||
err.help = i18n.t('errors.api.authentication.checkEmailConfigInstructions', {url: 'http://support.ghost.org/mail/'});
|
||||
logging.error(err);
|
||||
});
|
||||
})
|
||||
.return(setupUser);
|
||||
|
@ -8,6 +8,7 @@ var _ = require('lodash'),
|
||||
globalUtils = require('../utils'),
|
||||
utils = require('./utils'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
config = require('../config'),
|
||||
i18n = require('../i18n'),
|
||||
docName = 'invites',
|
||||
@ -144,8 +145,7 @@ invites = {
|
||||
if (error && error.errorType === 'EmailError') {
|
||||
error.message = i18n.t('errors.api.invites.errorSendingEmail.error', {message: error.message}) + ' ' +
|
||||
i18n.t('errors.api.invites.errorSendingEmail.help');
|
||||
|
||||
errors.logWarn(error.message);
|
||||
logging.warn(error.message);
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
|
@ -6,6 +6,7 @@ var _ = require('lodash'),
|
||||
config = require('../config'),
|
||||
canThis = require('../permissions').canThis,
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
utils = require('./utils'),
|
||||
i18n = require('../i18n'),
|
||||
|
||||
@ -36,17 +37,16 @@ var _ = require('lodash'),
|
||||
* @private
|
||||
*/
|
||||
updateConfigCache = function () {
|
||||
var errorMessages = [
|
||||
i18n.t('errors.api.settings.invalidJsonInLabs'),
|
||||
i18n.t('errors.api.settings.labsColumnCouldNotBeParsed'),
|
||||
i18n.t('errors.api.settings.tryUpdatingLabs')
|
||||
], labsValue = {};
|
||||
var labsValue = {};
|
||||
|
||||
if (settingsCache.labs && settingsCache.labs.value) {
|
||||
try {
|
||||
labsValue = JSON.parse(settingsCache.labs.value);
|
||||
} catch (e) {
|
||||
errors.logError.apply(this, errorMessages);
|
||||
} catch (err) {
|
||||
err.message = i18n.t('errors.api.settings.invalidJsonInLabs');
|
||||
err.context = i18n.t('errors.api.settings.labsColumnCouldNotBeParsed');
|
||||
err.help = i18n.t('errors.api.settings.tryUpdatingLabs');
|
||||
logging.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ var Promise = require('bluebird'),
|
||||
config = require('../config'),
|
||||
errors = require('../errors'),
|
||||
events = require('../events'),
|
||||
logging = require('../logging'),
|
||||
storage = require('../storage'),
|
||||
settings = require('./settings'),
|
||||
apiUtils = require('./utils'),
|
||||
@ -103,7 +104,7 @@ themes = {
|
||||
// happens in background
|
||||
Promise.promisify(fs.removeSync)(zip.path)
|
||||
.catch(function (err) {
|
||||
errors.logError(err);
|
||||
logging.error(err);
|
||||
});
|
||||
|
||||
// remove extracted dir from gscan
|
||||
@ -111,7 +112,7 @@ themes = {
|
||||
if (theme) {
|
||||
Promise.promisify(fs.removeSync)(theme.path)
|
||||
.catch(function (err) {
|
||||
errors.logError(err);
|
||||
logging.error(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -164,8 +164,8 @@ users = {
|
||||
return options;
|
||||
});
|
||||
});
|
||||
}).catch(function handleError(error) {
|
||||
return errors.formatAndRejectAPIError(error, i18n.t('errors.api.users.noPermissionToEditUser'));
|
||||
}).catch(function handleError(err) {
|
||||
return Promise.reject(new errors.NoPermissionError(err.message, i18n.t('errors.api.users.noPermissionToEditUser')));
|
||||
});
|
||||
}
|
||||
|
||||
@ -214,8 +214,8 @@ users = {
|
||||
return canThis(options.context).destroy.user(options.id).then(function permissionGranted() {
|
||||
options.status = 'all';
|
||||
return options;
|
||||
}).catch(function handleError(error) {
|
||||
return errors.formatAndRejectAPIError(error, i18n.t('errors.api.users.noPermissionToDestroyUser'));
|
||||
}).catch(function handleError(err) {
|
||||
return Promise.reject(new errors.NoPermissionError(err.message, i18n.t('errors.api.users.noPermissionToDestroyUser')));
|
||||
});
|
||||
}
|
||||
|
||||
@ -235,8 +235,8 @@ users = {
|
||||
]).then(function () {
|
||||
return dataProvider.User.destroy(options);
|
||||
}).return(null);
|
||||
}).catch(function (error) {
|
||||
return errors.formatAndRejectAPIError(error);
|
||||
}).catch(function (err) {
|
||||
return Promise.reject(new errors.NoPermissionError(err.message));
|
||||
});
|
||||
}
|
||||
|
||||
@ -270,8 +270,8 @@ users = {
|
||||
function handlePermissions(options) {
|
||||
return canThis(options.context).edit.user(options.data.password[0].user_id).then(function permissionGranted() {
|
||||
return options;
|
||||
}).catch(function (error) {
|
||||
return errors.formatAndRejectAPIError(error, i18n.t('errors.api.users.noPermissionToChangeUsersPwd'));
|
||||
}).catch(function (err) {
|
||||
return Promise.reject(new errors.NoPermissionError(err.message, i18n.t('errors.api.users.noPermissionToChangeUsersPwd')));
|
||||
});
|
||||
}
|
||||
|
||||
@ -322,8 +322,6 @@ users = {
|
||||
return canThis(options.context).assign.role(ownerRole);
|
||||
}).then(function () {
|
||||
return options;
|
||||
}).catch(function (error) {
|
||||
return errors.formatAndRejectAPIError(error);
|
||||
});
|
||||
}
|
||||
|
||||
@ -348,8 +346,6 @@ users = {
|
||||
// Pipeline calls each task passing the result of one to be the arguments for the next
|
||||
return pipeline(tasks, object, options).then(function formatResult(result) {
|
||||
return Promise.resolve({users: result});
|
||||
}).catch(function (error) {
|
||||
return errors.formatAndRejectAPIError(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -91,7 +91,7 @@ utils = {
|
||||
}
|
||||
|
||||
// For now, we can only handle showing the first validation error
|
||||
return errors.logAndRejectError(validationErrors[0]);
|
||||
return Promise.reject(validationErrors[0]);
|
||||
}
|
||||
|
||||
// If we got an object, check that too
|
||||
@ -187,8 +187,6 @@ utils = {
|
||||
|
||||
return permsPromise.then(function permissionGranted() {
|
||||
return options;
|
||||
}).catch(function handleError(error) {
|
||||
return errors.formatAndRejectAPIError(error);
|
||||
});
|
||||
};
|
||||
},
|
||||
@ -218,8 +216,6 @@ utils = {
|
||||
error.message = i18n.t('errors.api.utils.noPermissionToCall', {method: method, docName: docName});
|
||||
// forward error to next catch()
|
||||
return Promise.reject(error);
|
||||
}).catch(function handleError(error) {
|
||||
return errors.formatAndRejectAPIError(error);
|
||||
});
|
||||
};
|
||||
},
|
||||
@ -275,7 +271,7 @@ utils = {
|
||||
*/
|
||||
checkObject: function (object, docName, editId) {
|
||||
if (_.isEmpty(object) || _.isEmpty(object[docName]) || _.isEmpty(object[docName][0])) {
|
||||
return errors.logAndRejectError(new errors.BadRequestError(i18n.t('errors.api.utils.noRootKeyProvided', {docName: docName})));
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.api.utils.noRootKeyProvided', {docName: docName})));
|
||||
}
|
||||
|
||||
// convert author property to author_id to match the name in the database
|
||||
@ -296,7 +292,7 @@ utils = {
|
||||
});
|
||||
|
||||
if (editId && object[docName][0].id && parseInt(editId, 10) !== parseInt(object[docName][0].id, 10)) {
|
||||
return errors.logAndRejectError(new errors.BadRequestError(i18n.t('errors.api.utils.invalidIdProvided')));
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.api.utils.invalidIdProvided')));
|
||||
}
|
||||
|
||||
return Promise.resolve(object);
|
||||
|
@ -12,7 +12,7 @@ var hbs = require('express-hbs'),
|
||||
moment = require('moment'),
|
||||
sanitizeHtml = require('sanitize-html'),
|
||||
config = require('../../../../config'),
|
||||
errors = require('../../../../errors'),
|
||||
logging = require('../../../../logging'),
|
||||
makeAbsoluteUrl = require('../../../../utils/make-absolute-urls'),
|
||||
cheerio = require('cheerio'),
|
||||
amperize = new Amperize(),
|
||||
@ -126,9 +126,10 @@ function getAmperizeHTML(html, post) {
|
||||
amperize.parse(html, function (err, res) {
|
||||
if (err) {
|
||||
if (err.src) {
|
||||
errors.logError(err.message, 'AMP HTML couldn\'t get parsed: ' + err.src);
|
||||
err.context = 'AMP HTML couldn\'t get parsed: ' + err.src;
|
||||
logging.error(err);
|
||||
} else {
|
||||
errors.logError(err);
|
||||
logging.error(err);
|
||||
}
|
||||
|
||||
// save it in cache to prevent multiple calls to Amperize until
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
var _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
api = require('../api'),
|
||||
loader = require('./loader'),
|
||||
i18n = require('../i18n'),
|
||||
@ -46,12 +46,11 @@ module.exports = {
|
||||
|
||||
appsToLoad = appsToLoad.concat(config.get('internalApps'));
|
||||
});
|
||||
} catch (e) {
|
||||
errors.logError(
|
||||
i18n.t('errors.apps.failedToParseActiveAppsSettings.error', {message: e.message}),
|
||||
i18n.t('errors.apps.failedToParseActiveAppsSettings.context'),
|
||||
i18n.t('errors.apps.failedToParseActiveAppsSettings.help')
|
||||
);
|
||||
} catch (err) {
|
||||
err.message = i18n.t('errors.apps.failedToParseActiveAppsSettings.error', {message: err.message});
|
||||
err.help = i18n.t('errors.apps.failedToParseActiveAppsSettings.context');
|
||||
err.context = i18n.t('errors.apps.failedToParseActiveAppsSettings.help');
|
||||
logging.error(err);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -88,11 +87,9 @@ module.exports = {
|
||||
// Extend the loadedApps onto the available apps
|
||||
_.extend(availableApps, loadedApps);
|
||||
}).catch(function (err) {
|
||||
errors.logError(
|
||||
err.message || err,
|
||||
i18n.t('errors.apps.appWillNotBeLoaded.error'),
|
||||
i18n.t('errors.apps.appWillNotBeLoaded.help')
|
||||
);
|
||||
err.context = i18n.t('errors.apps.appWillNotBeLoaded.error');
|
||||
err.help = i18n.t('errors.apps.appWillNotBeLoaded.help');
|
||||
logging.error(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -1,21 +1,26 @@
|
||||
var config = require('../../config'),
|
||||
utils = require('../../utils'),
|
||||
errors = require('../../errors'),
|
||||
logging = require('../../logging'),
|
||||
i18n = require('../../i18n'),
|
||||
middleware = require('./lib/middleware'),
|
||||
router = require('./lib/router');
|
||||
|
||||
module.exports = {
|
||||
activate: function activate() {
|
||||
var err, paths;
|
||||
|
||||
if (utils.url.getSubdir()) {
|
||||
var paths = utils.url.getSubdir().split('/');
|
||||
paths = utils.url.getSubdir().split('/');
|
||||
|
||||
if (paths.pop() === config.get('routeKeywords').private) {
|
||||
errors.logErrorAndExit(
|
||||
new Error(i18n.t('errors.config.urlCannotContainPrivateSubdir.error')),
|
||||
i18n.t('errors.config.urlCannotContainPrivateSubdir.description'),
|
||||
i18n.t('errors.config.urlCannotContainPrivateSubdir.help')
|
||||
);
|
||||
err = new Error();
|
||||
err.message = i18n.t('errors.config.urlCannotContainPrivateSubdir.error');
|
||||
err.context = i18n.t('errors.config.urlCannotContainPrivateSubdir.description');
|
||||
err.help = i18n.t('errors.config.urlCannotContainPrivateSubdir.help');
|
||||
logging.error(err);
|
||||
|
||||
// @TODO: why?
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1,12 +1,13 @@
|
||||
var _ = require('lodash'),
|
||||
fs = require('fs'),
|
||||
config = require('../../../config'),
|
||||
session = require('cookie-session'),
|
||||
crypto = require('crypto'),
|
||||
path = require('path'),
|
||||
api = require('../../../api'),
|
||||
Promise = require('bluebird'),
|
||||
config = require('../../../config'),
|
||||
api = require('../../../api'),
|
||||
errors = require('../../../errors'),
|
||||
session = require('cookie-session'),
|
||||
logging = require('../../../logging'),
|
||||
utils = require('../../../utils'),
|
||||
i18n = require('../../../i18n'),
|
||||
privateRoute = '/' + config.get('routeKeywords').private + '/',
|
||||
@ -55,7 +56,7 @@ privateBlogging = {
|
||||
if (req.path.lastIndexOf('/rss/', 0) === 0 ||
|
||||
req.path.lastIndexOf('/rss/') === req.url.length - 5 ||
|
||||
(req.path.lastIndexOf('/sitemap', 0) === 0 && req.path.lastIndexOf('.xml') === req.path.length - 4)) {
|
||||
return errors.error404(req, res, next);
|
||||
return next(new errors.NotFoundError(i18n.t('errors.errors.pageNotFound')));
|
||||
} else if (req.url.lastIndexOf('/robots.txt', 0) === 0) {
|
||||
fs.readFile(path.resolve(__dirname, '../', 'robots.txt'), function readFile(err, buf) {
|
||||
if (err) {
|
||||
@ -145,7 +146,8 @@ privateBlogging = {
|
||||
ipCount = '',
|
||||
message = i18n.t('errors.middleware.spamprevention.tooManyAttempts'),
|
||||
deniedRateLimit = '',
|
||||
password = req.body.password;
|
||||
password = req.body.password,
|
||||
err;
|
||||
|
||||
if (password) {
|
||||
protectedSecurity.push({ip: remoteAddress, time: currentTime});
|
||||
@ -165,15 +167,19 @@ privateBlogging = {
|
||||
deniedRateLimit = (ipCount[remoteAddress] > rateProtectedAttempts);
|
||||
|
||||
if (deniedRateLimit) {
|
||||
errors.logError(
|
||||
i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error', {rfa: rateProtectedAttempts, rfp: rateProtectedPeriod}),
|
||||
i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
|
||||
);
|
||||
err = new Error();
|
||||
err.message = i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error', {rfa: rateProtectedAttempts, rfp: rateProtectedPeriod});
|
||||
err.context = i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context');
|
||||
logging.error(err);
|
||||
|
||||
message += rateProtectedPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater');
|
||||
|
||||
// @TODO: why?
|
||||
res.error = {
|
||||
message: message
|
||||
};
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
};
|
||||
|
@ -92,11 +92,8 @@ describe('Private Blogging', function () {
|
||||
});
|
||||
|
||||
describe('private', function () {
|
||||
var errorSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
res.isPrivateBlog = true;
|
||||
errorSpy = sandbox.spy(errors, 'error404');
|
||||
res = {
|
||||
status: function () {
|
||||
return this;
|
||||
@ -121,39 +118,62 @@ describe('Private Blogging', function () {
|
||||
|
||||
it('filterPrivateRoutes should throw 404 if url is sitemap', function () {
|
||||
req.path = req.url = '/sitemap.xml';
|
||||
var next = function next(err) {
|
||||
(err instanceof errors.NotFoundError).should.eql(true);
|
||||
};
|
||||
|
||||
privateBlogging.filterPrivateRoutes(req, res, next);
|
||||
errorSpy.called.should.be.true();
|
||||
});
|
||||
|
||||
it('filterPrivateRoutes should throw 404 if url is sitemap with param', function () {
|
||||
req.url = '/sitemap.xml?weird=param';
|
||||
req.path = '/sitemap.xml';
|
||||
|
||||
var next = function next(err) {
|
||||
(err instanceof errors.NotFoundError).should.eql(true);
|
||||
};
|
||||
|
||||
privateBlogging.filterPrivateRoutes(req, res, next);
|
||||
errorSpy.called.should.be.true();
|
||||
});
|
||||
|
||||
it('filterPrivateRoutes should throw 404 if url is rss', function () {
|
||||
req.path = req.url = '/rss/';
|
||||
|
||||
var next = function next(err) {
|
||||
(err instanceof errors.NotFoundError).should.eql(true);
|
||||
};
|
||||
|
||||
privateBlogging.filterPrivateRoutes(req, res, next);
|
||||
errorSpy.called.should.be.true();
|
||||
});
|
||||
|
||||
it('filterPrivateRoutes should throw 404 if url is author rss', function () {
|
||||
req.path = req.url = '/author/halfdan/rss/';
|
||||
|
||||
var next = function next(err) {
|
||||
(err instanceof errors.NotFoundError).should.eql(true);
|
||||
};
|
||||
|
||||
privateBlogging.filterPrivateRoutes(req, res, next);
|
||||
errorSpy.called.should.be.true();
|
||||
});
|
||||
|
||||
it('filterPrivateRoutes should throw 404 if url is tag rss', function () {
|
||||
req.path = req.url = '/tag/slimer/rss/';
|
||||
|
||||
var next = function next(err) {
|
||||
(err instanceof errors.NotFoundError).should.eql(true);
|
||||
};
|
||||
|
||||
privateBlogging.filterPrivateRoutes(req, res, next);
|
||||
errorSpy.called.should.be.true();
|
||||
});
|
||||
|
||||
it('filterPrivateRoutes should throw 404 if url is rss plus something', function () {
|
||||
req.path = req.url = '/rss/sometag';
|
||||
|
||||
var next = function next(err) {
|
||||
(err instanceof errors.NotFoundError).should.eql(true);
|
||||
};
|
||||
|
||||
privateBlogging.filterPrivateRoutes(req, res, next);
|
||||
errorSpy.called.should.be.true();
|
||||
});
|
||||
|
||||
it('filterPrivateRoutes should render custom robots.txt', function () {
|
||||
|
@ -5,7 +5,7 @@ var _ = require('lodash'),
|
||||
|
||||
// Dirty requires
|
||||
config = require('../../config'),
|
||||
errors = require('../../errors'),
|
||||
logging = require('../../logging'),
|
||||
i18n = require('../../i18n'),
|
||||
labs = require('../../utils/labs'),
|
||||
template = require('../../helpers/template'),
|
||||
@ -53,11 +53,7 @@ function subscribeFormHelper(options) {
|
||||
|
||||
module.exports = {
|
||||
activate: function activate(ghost) {
|
||||
var errorMessages = [
|
||||
i18n.t('warnings.helpers.helperNotAvailable', {helperName: 'subscribe_form'}),
|
||||
i18n.t('warnings.helpers.apiMustBeEnabled', {helperName: 'subscribe_form', flagName: 'subscribers'}),
|
||||
i18n.t('warnings.helpers.seeLink', {url: 'http://support.ghost.org/subscribers-beta/'})
|
||||
];
|
||||
var err;
|
||||
|
||||
// Correct way to register a helper from an app
|
||||
ghost.helpers.register('subscribe_form', function labsEnabledHelper() {
|
||||
@ -65,8 +61,13 @@ module.exports = {
|
||||
return subscribeFormHelper.apply(this, arguments);
|
||||
}
|
||||
|
||||
errors.logError.apply(this, errorMessages);
|
||||
return new hbs.handlebars.SafeString('<script>console.error("' + errorMessages.join(' ') + '");</script>');
|
||||
err = new Error();
|
||||
err.message = i18n.t('warnings.helpers.helperNotAvailable', {helperName: 'subscribe_form'});
|
||||
err.context = i18n.t('warnings.helpers.apiMustBeEnabled', {helperName: 'subscribe_form', flagName: 'subscribers'});
|
||||
err.help = i18n.t('warnings.helpers.seeLink', {url: 'http://support.ghost.org/subscribers-beta/'});
|
||||
logging.error(err);
|
||||
|
||||
return new hbs.handlebars.SafeString('<script>console.error(' + JSON.stringify(err) + ');</script>');
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -44,12 +44,11 @@ authenticate = {
|
||||
}
|
||||
|
||||
if (!req.body.client_id || !req.body.client_secret) {
|
||||
errors.logError(
|
||||
i18n.t('errors.middleware.auth.clientAuthenticationFailed'),
|
||||
return next(new errors.UnauthorizedError(
|
||||
i18n.t('errors.middleware.auth.accessDenied')),
|
||||
i18n.t('errors.middleware.auth.clientCredentialsNotProvided'),
|
||||
i18n.t('errors.middleware.auth.forInformationRead', {url: 'http://api.ghost.org/docs/client-authentication'})
|
||||
);
|
||||
return errors.handleAPIError(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')), req, res, next);
|
||||
}
|
||||
|
||||
return passport.authenticate(['oauth2-client-password'], {session: false, failWithError: false},
|
||||
@ -63,12 +62,11 @@ authenticate = {
|
||||
delete req.body.client_secret;
|
||||
|
||||
if (!client) {
|
||||
errors.logError(
|
||||
i18n.t('errors.middleware.auth.clientAuthenticationFailed'),
|
||||
return next(new errors.UnauthorizedError(
|
||||
i18n.t('errors.middleware.auth.accessDenied')),
|
||||
i18n.t('errors.middleware.auth.clientCredentialsNotValid'),
|
||||
i18n.t('errors.middleware.auth.forInformationRead', {url: 'http://api.ghost.org/docs/client-authentication'})
|
||||
);
|
||||
return errors.handleAPIError(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')), req, res, next);
|
||||
}
|
||||
|
||||
req.client = client;
|
||||
@ -94,13 +92,13 @@ authenticate = {
|
||||
events.emit('user.authenticated', user);
|
||||
return next(null, user, info);
|
||||
} else if (isBearerAutorizationHeader(req)) {
|
||||
return errors.handleAPIError(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')), req, res, next);
|
||||
return next(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')));
|
||||
} else if (req.client) {
|
||||
req.user = {id: 0};
|
||||
return next();
|
||||
}
|
||||
|
||||
return errors.handleAPIError(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')), req, res, next);
|
||||
return next(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')));
|
||||
}
|
||||
)(req, res, next);
|
||||
},
|
||||
@ -110,7 +108,7 @@ authenticate = {
|
||||
req.query.code = req.body.authorizationCode;
|
||||
|
||||
if (!req.query.code) {
|
||||
return errors.handleAPIError(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')), req, res, next);
|
||||
return next(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')));
|
||||
}
|
||||
|
||||
passport.authenticate('ghost', {session: false, failWithError: false}, function authenticate(err, user, info) {
|
||||
@ -119,7 +117,7 @@ authenticate = {
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return errors.handleAPIError(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')), req, res, next);
|
||||
return next(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')));
|
||||
}
|
||||
|
||||
req.authInfo = info;
|
||||
|
@ -10,7 +10,7 @@ authorize = {
|
||||
if (req.user && req.user.id) {
|
||||
return next();
|
||||
} else {
|
||||
return errors.handleAPIError(new errors.NoPermissionError(i18n.t('errors.middleware.auth.pleaseSignIn')), req, res, next);
|
||||
return next(new errors.NoPermissionError(i18n.t('errors.middleware.auth.pleaseSignIn')));
|
||||
}
|
||||
},
|
||||
|
||||
@ -22,7 +22,7 @@ authorize = {
|
||||
if (req.user && req.user.id) {
|
||||
return next();
|
||||
} else {
|
||||
return errors.handleAPIError(new errors.NoPermissionError(i18n.t('errors.middleware.auth.pleaseSignIn')), req, res, next);
|
||||
return next(new errors.NoPermissionError(i18n.t('errors.middleware.auth.pleaseSignIn')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,5 +28,10 @@
|
||||
"auth": {
|
||||
"type": "ghost",
|
||||
"url": "http://devauth.ghost.org:8080"
|
||||
},
|
||||
"logging": {
|
||||
"level": "info",
|
||||
"rotation": false,
|
||||
"transports": ["stdout"]
|
||||
}
|
||||
}
|
||||
|
@ -8,5 +8,10 @@
|
||||
},
|
||||
"paths": {
|
||||
"contentPath": "content/"
|
||||
},
|
||||
"logging": {
|
||||
"level": "info",
|
||||
"rotation": true,
|
||||
"transports": ["file"]
|
||||
}
|
||||
}
|
||||
|
@ -15,5 +15,7 @@
|
||||
"auth": {
|
||||
"type": "password"
|
||||
},
|
||||
"logging": false
|
||||
"logging": {
|
||||
"level": "fatal"
|
||||
}
|
||||
}
|
||||
|
4
core/server/config/env/config.testing.json
vendored
4
core/server/config/env/config.testing.json
vendored
@ -12,5 +12,7 @@
|
||||
"auth": {
|
||||
"type": "password"
|
||||
},
|
||||
"logging": false
|
||||
"logging": {
|
||||
"level": "fatal"
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ var debug = require('debug')('ghost:admin:controller'),
|
||||
Promise = require('bluebird'),
|
||||
api = require('../api'),
|
||||
config = require('../config'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
updateCheck = require('../update-check'),
|
||||
i18n = require('../i18n'),
|
||||
adminControllers;
|
||||
@ -67,7 +67,7 @@ adminControllers = {
|
||||
});
|
||||
}).finally(function noMatterWhat() {
|
||||
renderIndex();
|
||||
}).catch(errors.logError);
|
||||
}).catch(logging.error);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2,9 +2,9 @@ var express = require('express'),
|
||||
_ = require('lodash'),
|
||||
config = require('../../config'),
|
||||
errors = require('../../errors'),
|
||||
i18n = require('../../i18n'),
|
||||
rss = require('../../data/xml/rss'),
|
||||
utils = require('../../utils'),
|
||||
|
||||
channelConfig = require('./channel-config'),
|
||||
renderChannel = require('./render-channel'),
|
||||
|
||||
@ -26,7 +26,7 @@ function handlePageParam(req, res, next, page) {
|
||||
}
|
||||
} else if (page < 1 || isNaN(page)) {
|
||||
// Nothing less than 1 is a valid page number, go straight to a 404
|
||||
return next(new errors.NotFoundError());
|
||||
return next(new errors.NotFoundError(i18n.t('errors.errors.pageNotFound')));
|
||||
} else {
|
||||
// Set req.params.page to the already parsed number, and continue
|
||||
req.params.page = page;
|
||||
|
@ -1,6 +1,7 @@
|
||||
var debug = require('debug')('ghost:channels:render'),
|
||||
_ = require('lodash'),
|
||||
errors = require('../../errors'),
|
||||
i18n = require('../../i18n'),
|
||||
filters = require('../../filters'),
|
||||
safeString = require('../../utils/index').safeString,
|
||||
labs = require('../../utils/labs'),
|
||||
@ -37,7 +38,7 @@ function renderChannel(req, res, next) {
|
||||
return fetchData(channelOpts).then(function handleResult(result) {
|
||||
// If page is greater than number of pages we have, go straight to 404
|
||||
if (pageParam > result.meta.pagination.pages) {
|
||||
return next(new errors.NotFoundError());
|
||||
return next(new errors.NotFoundError(i18n.t('errors.errors.pageNotFound')));
|
||||
}
|
||||
|
||||
// @TODO: figure out if this can be removed, it's supposed to ensure that absolutely URLs get generated
|
||||
|
@ -5,6 +5,7 @@ var _ = require('lodash'),
|
||||
versioning = require('../schema').versioning,
|
||||
serverUtils = require('../../utils'),
|
||||
errors = require('../../errors'),
|
||||
logging = require('../../logging'),
|
||||
settings = require('../../api/settings'),
|
||||
i18n = require('../../i18n'),
|
||||
|
||||
@ -29,7 +30,7 @@ exportFileName = function exportFileName() {
|
||||
}
|
||||
return title + 'ghost.' + datetime + '.json';
|
||||
}).catch(function (err) {
|
||||
errors.logError(err);
|
||||
logging.error(err);
|
||||
return 'ghost.' + datetime + '.json';
|
||||
});
|
||||
};
|
||||
@ -74,7 +75,7 @@ doExport = function doExport() {
|
||||
|
||||
return exportData;
|
||||
}).catch(function (err) {
|
||||
errors.logAndThrowError(err, i18n.t('errors.data.export.errorExportingData'), '');
|
||||
return Promise.reject(new errors.InternalServerError(err.message, i18n.t('errors.data.export.errorExportingData')));
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -31,10 +31,12 @@ JSONHandler = {
|
||||
}
|
||||
|
||||
return importData;
|
||||
} catch (e) {
|
||||
errors.logError(e, i18n.t('errors.data.importer.handlers.json.apiDbImportContent'),
|
||||
i18n.t('errors.data.importer.handlers.json.checkImportJsonIsValid'));
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.data.importer.handlers.json.failedToParseImportJson')));
|
||||
} catch (err) {
|
||||
return Promise.reject(new errors.BadRequestError(
|
||||
i18n.t('errors.data.importer.handlers.json.failedToParseImportJson'),
|
||||
i18n.t('errors.data.importer.handlers.json.apiDbImportContent'),
|
||||
i18n.t('errors.data.importer.handlers.json.checkImportJsonIsValid')
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ var _ = require('lodash'),
|
||||
uuid = require('node-uuid'),
|
||||
extract = require('extract-zip-fork'),
|
||||
errors = require('../../errors'),
|
||||
logging = require('../../logging'),
|
||||
ImageHandler = require('./handlers/image'),
|
||||
JSONHandler = require('./handlers/json'),
|
||||
MarkdownHandler = require('./handlers/markdown'),
|
||||
@ -108,8 +109,9 @@ _.extend(ImportManager.prototype, {
|
||||
_.each(filesToDelete, function (fileToDelete) {
|
||||
fs.remove(fileToDelete, function (err) {
|
||||
if (err) {
|
||||
errors.logError(err, i18n.t('errors.data.importer.index.couldNotCleanUpFile.error'),
|
||||
i18n.t('errors.data.importer.index.couldNotCleanUpFile.context'));
|
||||
err.context = i18n.t('errors.data.importer.index.couldNotCleanUpFile.error');
|
||||
err.help = i18n.t('errors.data.importer.index.couldNotCleanUpFile.context');
|
||||
logging.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -4,19 +4,19 @@ var Promise = require('bluebird'),
|
||||
_ = require('lodash'),
|
||||
commands = require('../schema').commands,
|
||||
fixtures = require('./fixtures'),
|
||||
errors = require('../../errors'),
|
||||
db = require('../../data/db'),
|
||||
logging = require('../../logging'),
|
||||
errors = require('../../errors'),
|
||||
schema = require('../schema').tables,
|
||||
schemaTables = Object.keys(schema),
|
||||
populate, logger;
|
||||
|
||||
// @TODO: remove me asap!
|
||||
logger = {
|
||||
info: function info(message) {
|
||||
errors.logComponentInfo('Migrations', message);
|
||||
logging.info('Migrations:' + message);
|
||||
},
|
||||
warn: function warn(message) {
|
||||
errors.logComponentWarn('Skipping Migrations', message);
|
||||
logging.warn('Skipping Migrations:' + message);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5,6 +5,7 @@ var Promise = require('bluebird'),
|
||||
backup = require('./backup'),
|
||||
fixtures = require('./fixtures'),
|
||||
errors = require('../../errors'),
|
||||
logging = require('../../logging'),
|
||||
i18n = require('../../i18n'),
|
||||
db = require('../../data/db'),
|
||||
versioning = require('../schema').versioning,
|
||||
@ -22,13 +23,12 @@ var Promise = require('bluebird'),
|
||||
migrateToDatabaseVersion,
|
||||
execute, logger, isDatabaseOutOfDate;
|
||||
|
||||
// @TODO: remove me asap!
|
||||
logger = {
|
||||
info: function info(message) {
|
||||
errors.logComponentInfo('Migrations', message);
|
||||
logging.info('Migrations:' + message);
|
||||
},
|
||||
warn: function warn(message) {
|
||||
errors.logComponentWarn('Skipping Migrations', message);
|
||||
logging.warn('Skipping Migrations:' + message);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
var https = require('https'),
|
||||
errors = require('../../errors'),
|
||||
url = require('url'),
|
||||
Promise = require('bluebird'),
|
||||
utils = require('../../utils'),
|
||||
events = require('../../events'),
|
||||
logging = require('../../logging'),
|
||||
api = require('../../api/settings'),
|
||||
i18n = require('../../i18n'),
|
||||
schema = require('../schema').checks,
|
||||
@ -31,12 +31,10 @@ function makeRequest(reqOptions, reqPayload) {
|
||||
reqPayload = JSON.stringify(reqPayload);
|
||||
|
||||
req.write(reqPayload);
|
||||
req.on('error', function (error) {
|
||||
errors.logError(
|
||||
error,
|
||||
i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.error'),
|
||||
i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.help', {url: 'http://support.ghost.org'})
|
||||
);
|
||||
req.on('error', function (err) {
|
||||
err.context = i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.error');
|
||||
err.help = i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.help', {url: 'http://support.ghost.org'});
|
||||
logging.error(err);
|
||||
});
|
||||
|
||||
req.end();
|
||||
|
@ -4,6 +4,7 @@ var crypto = require('crypto'),
|
||||
config = require('../../../config'),
|
||||
utils = require('../../../utils'),
|
||||
errors = require('../../../errors'),
|
||||
i18n = require('../../../i18n'),
|
||||
filters = require('../../../filters'),
|
||||
processUrls = require('../../../utils/make-absolute-urls'),
|
||||
labs = require('../../../utils/labs'),
|
||||
@ -173,7 +174,7 @@ generate = function generate(req, res, next) {
|
||||
|
||||
// If page is greater than number of pages we have, redirect to last page
|
||||
if (pageParam > maxPage) {
|
||||
return next(new errors.NotFoundError());
|
||||
return next(new errors.NotFoundError(i18n.t('errors.errors.pageNotFound')));
|
||||
}
|
||||
|
||||
data.version = res.locals.safeVersion;
|
||||
|
@ -3,7 +3,7 @@ var _ = require('lodash'),
|
||||
xml = require('xml'),
|
||||
config = require('../../config'),
|
||||
utils = require('../../utils'),
|
||||
errors = require('../../errors'),
|
||||
logging = require('../../logging'),
|
||||
events = require('../../events'),
|
||||
i18n = require('../../i18n'),
|
||||
pingList;
|
||||
@ -67,14 +67,11 @@ function ping(post) {
|
||||
|
||||
req = http.request(options);
|
||||
req.write(pingXML);
|
||||
req.on('error', function handleError(error) {
|
||||
errors.logError(
|
||||
error,
|
||||
i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.error'),
|
||||
i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.help', {url: 'http://support.ghost.org'})
|
||||
);
|
||||
}
|
||||
);
|
||||
req.on('error', function handleError(err) {
|
||||
err.context = i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.error');
|
||||
err.help = i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.help', {url: 'http://support.ghost.org'});
|
||||
logging.error(err);
|
||||
});
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
// # Bad request error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function BadRequestError(message) {
|
||||
function BadRequestError(message, context, help) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 400;
|
||||
this.errorType = this.name;
|
||||
this.context = context;
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
BadRequestError.prototype = Object.create(Error.prototype);
|
||||
|
@ -1,11 +1,13 @@
|
||||
// # Email error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function EmailError(message) {
|
||||
function EmailError(message, context, help) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 500;
|
||||
this.errorType = this.name;
|
||||
this.context = context;
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
EmailError.prototype = Object.create(Error.prototype);
|
||||
|
@ -1,465 +1,41 @@
|
||||
// # Errors
|
||||
/*jslint regexp: true */
|
||||
var _ = require('lodash'),
|
||||
chalk = require('chalk'),
|
||||
path = require('path'),
|
||||
Promise = require('bluebird'),
|
||||
hbs = require('express-hbs'),
|
||||
NotFoundError = require('./not-found-error'),
|
||||
BadRequestError = require('./bad-request-error'),
|
||||
InternalServerError = require('./internal-server-error'),
|
||||
NoPermissionError = require('./no-permission-error'),
|
||||
MethodNotAllowedError = require('./method-not-allowed-error'),
|
||||
var NotFoundError = require('./not-found-error'),
|
||||
BadRequestError = require('./bad-request-error'),
|
||||
InternalServerError = require('./internal-server-error'),
|
||||
NoPermissionError = require('./no-permission-error'),
|
||||
MethodNotAllowedError = require('./method-not-allowed-error'),
|
||||
RequestEntityTooLargeError = require('./request-too-large-error'),
|
||||
UnauthorizedError = require('./unauthorized-error'),
|
||||
ValidationError = require('./validation-error'),
|
||||
ThemeValidationError = require('./theme-validation-error'),
|
||||
UnsupportedMediaTypeError = require('./unsupported-media-type-error'),
|
||||
EmailError = require('./email-error'),
|
||||
DataImportError = require('./data-import-error'),
|
||||
TooManyRequestsError = require('./too-many-requests-error'),
|
||||
TokenRevocationError = require('./token-revocation-error'),
|
||||
VersionMismatchError = require('./version-mismatch-error'),
|
||||
IncorrectUsage = require('./incorrect-usage'),
|
||||
Maintenance = require('./maintenance'),
|
||||
DatabaseNotPopulated = require('./database-not-populated'),
|
||||
DatabaseVersion = require('./database-version'),
|
||||
i18n = require('../i18n'),
|
||||
config = require('../config'),
|
||||
errors,
|
||||
|
||||
// Paths for views
|
||||
userErrorTemplateExists = false;
|
||||
|
||||
function isValidErrorStatus(status) {
|
||||
return _.isNumber(status) && status >= 400 && status < 600;
|
||||
}
|
||||
|
||||
function getStatusCode(error) {
|
||||
if (error.statusCode) {
|
||||
return error.statusCode;
|
||||
}
|
||||
|
||||
if (error.status && isValidErrorStatus(error.status)) {
|
||||
error.statusCode = error.status;
|
||||
return error.statusCode;
|
||||
}
|
||||
|
||||
if (error.code && isValidErrorStatus(error.code)) {
|
||||
error.statusCode = error.code;
|
||||
return error.statusCode;
|
||||
}
|
||||
|
||||
error.statusCode = 500;
|
||||
return error.statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic error handling helpers
|
||||
*/
|
||||
errors = {
|
||||
updateActiveTheme: function (activeTheme) {
|
||||
userErrorTemplateExists = config.get('paths').availableThemes[activeTheme].hasOwnProperty('error.hbs');
|
||||
},
|
||||
|
||||
throwError: function (err) {
|
||||
if (!err) {
|
||||
err = new Error(i18n.t('errors.errors.anErrorOccurred'));
|
||||
}
|
||||
|
||||
if (_.isString(err)) {
|
||||
throw new Error(err);
|
||||
}
|
||||
|
||||
throw err;
|
||||
},
|
||||
|
||||
// ## Reject Error
|
||||
// Used to pass through promise errors when we want to handle them at a later time
|
||||
rejectError: function (err) {
|
||||
return Promise.reject(err);
|
||||
},
|
||||
|
||||
logComponentInfo: function (component, info) {
|
||||
if (process.env.NODE_LEVEL === 'DEBUG' ||
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.NODE_ENV === 'staging' ||
|
||||
process.env.NODE_ENV === 'production') {
|
||||
console.info(chalk.cyan(component + ':', info));
|
||||
}
|
||||
},
|
||||
|
||||
logComponentWarn: function (component, warning) {
|
||||
if (process.env.NODE_LEVEL === 'DEBUG' ||
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.NODE_ENV === 'staging' ||
|
||||
process.env.NODE_ENV === 'production') {
|
||||
console.info(chalk.yellow(component + ':', warning));
|
||||
}
|
||||
},
|
||||
|
||||
logWarn: function (warn, context, help) {
|
||||
if (process.env.NODE_LEVEL === 'DEBUG' ||
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.NODE_ENV === 'staging' ||
|
||||
process.env.NODE_ENV === 'production') {
|
||||
warn = warn || i18n.t('errors.errors.noMessageSupplied');
|
||||
var msgs = [chalk.yellow(i18n.t('errors.errors.warning'), warn), '\n'];
|
||||
|
||||
if (context) {
|
||||
msgs.push(chalk.white(context), '\n');
|
||||
}
|
||||
|
||||
if (help) {
|
||||
msgs.push(chalk.green(help));
|
||||
}
|
||||
|
||||
// add a new line
|
||||
msgs.push('\n');
|
||||
|
||||
console.log.apply(console, msgs);
|
||||
}
|
||||
},
|
||||
|
||||
logError: function (err, context, help) {
|
||||
var self = this,
|
||||
origArgs = _.toArray(arguments).slice(1),
|
||||
stack,
|
||||
msgs,
|
||||
hideStack = false;
|
||||
|
||||
// DatabaseVersion errors are usually fatal, we output a nice message
|
||||
// And the stack is not at all useful in this case
|
||||
if (err instanceof DatabaseVersion) {
|
||||
hideStack = true;
|
||||
}
|
||||
|
||||
if (_.isArray(err)) {
|
||||
_.each(err, function (e) {
|
||||
var newArgs = [e].concat(origArgs);
|
||||
errors.logError.apply(self, newArgs);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
stack = err ? err.stack : null;
|
||||
|
||||
if (!_.isString(err)) {
|
||||
if (_.isObject(err) && _.isString(err.message)) {
|
||||
err = err.message;
|
||||
} else {
|
||||
err = i18n.t('errors.errors.unknownErrorOccurred');
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite error to provide information that this is probably a permission problem
|
||||
// TODO: https://github.com/TryGhost/Ghost/issues/3687
|
||||
if (err.indexOf('SQLITE_READONLY') !== -1) {
|
||||
context = i18n.t('errors.errors.databaseIsReadOnly');
|
||||
help = i18n.t('errors.errors.checkDatabase');
|
||||
}
|
||||
|
||||
// TODO: Logging framework hookup
|
||||
// Eventually we'll have better logging which will know about envs
|
||||
// you can use DEBUG=true when running tests and need error stdout
|
||||
if ((process.env.NODE_LEVEL === 'DEBUG' ||
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.NODE_ENV === 'staging' ||
|
||||
process.env.NODE_ENV === 'production')) {
|
||||
msgs = [chalk.red(i18n.t('errors.errors.error'), err), '\n'];
|
||||
|
||||
if (context) {
|
||||
msgs.push(chalk.white(context), '\n');
|
||||
}
|
||||
|
||||
if (help) {
|
||||
msgs.push(chalk.green(help));
|
||||
}
|
||||
|
||||
// add a new line
|
||||
msgs.push('\n');
|
||||
|
||||
if (stack && !hideStack) {
|
||||
msgs.push(stack, '\n');
|
||||
}
|
||||
|
||||
console.error.apply(console, msgs);
|
||||
}
|
||||
},
|
||||
|
||||
logErrorAndExit: function (err, context, help) {
|
||||
this.logError(err, context, help);
|
||||
// Exit with 0 to prevent npm errors as we have our own
|
||||
process.exit(0);
|
||||
},
|
||||
|
||||
logAndThrowError: function (err, context, help) {
|
||||
this.logError(err, context, help);
|
||||
|
||||
this.throwError(err, context, help);
|
||||
},
|
||||
|
||||
logAndRejectError: function (err, context, help) {
|
||||
this.logError(err, context, help);
|
||||
|
||||
return this.rejectError(err, context, help);
|
||||
},
|
||||
|
||||
logErrorWithRedirect: function (msg, context, help, redirectTo, req, res) {
|
||||
/*jshint unused:false*/
|
||||
var self = this;
|
||||
|
||||
return function () {
|
||||
self.logError(msg, context, help);
|
||||
|
||||
if (_.isFunction(res.redirect)) {
|
||||
res.redirect(redirectTo);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* ### Format HTTP Errors
|
||||
* Converts the error response from the API into a format which can be returned over HTTP
|
||||
*
|
||||
* @private
|
||||
* @param {Array} error
|
||||
* @return {{errors: Array, statusCode: number}}
|
||||
*/
|
||||
formatHttpErrors: function formatHttpErrors(error) {
|
||||
var statusCode = 500,
|
||||
errors = [];
|
||||
|
||||
if (!_.isArray(error)) {
|
||||
error = [].concat(error);
|
||||
}
|
||||
|
||||
_.each(error, function each(errorItem) {
|
||||
var errorContent = {};
|
||||
|
||||
// TODO: add logic to set the correct status code
|
||||
statusCode = getStatusCode(errorItem);
|
||||
|
||||
errorContent.message = _.isString(errorItem) ? errorItem :
|
||||
(_.isObject(errorItem) ? errorItem.message : i18n.t('errors.errors.unknownApiError'));
|
||||
errorContent.errorType = errorItem.errorType || 'InternalServerError';
|
||||
|
||||
if (errorItem.errorType === 'ThemeValidationError' && errorItem.errorDetails) {
|
||||
errorContent.errorDetails = errorItem.errorDetails;
|
||||
}
|
||||
|
||||
errors.push(errorContent);
|
||||
});
|
||||
|
||||
return {errors: errors, statusCode: statusCode};
|
||||
},
|
||||
|
||||
formatAndRejectAPIError: function (error, permsMessage) {
|
||||
if (!error) {
|
||||
return this.rejectError(
|
||||
new this.NoPermissionError(permsMessage || i18n.t('errors.errors.notEnoughPermission'))
|
||||
);
|
||||
}
|
||||
|
||||
if (_.isString(error)) {
|
||||
return this.rejectError(new this.NoPermissionError(error));
|
||||
}
|
||||
|
||||
if (error.errorType) {
|
||||
return this.rejectError(error);
|
||||
}
|
||||
|
||||
// handle database errors
|
||||
if (error.code && (error.errno || error.detail)) {
|
||||
error.db_error_code = error.code;
|
||||
error.errorType = 'DatabaseError';
|
||||
error.statusCode = 500;
|
||||
|
||||
return this.rejectError(error);
|
||||
}
|
||||
|
||||
return this.rejectError(new this.InternalServerError(error));
|
||||
},
|
||||
|
||||
handleAPIError: function errorHandler(err, req, res, next) {
|
||||
/*jshint unused:false */
|
||||
var httpErrors = this.formatHttpErrors(err);
|
||||
this.logError(err);
|
||||
// Send a properly formatted HTTP response containing the errors
|
||||
res.status(httpErrors.statusCode).json({errors: httpErrors.errors});
|
||||
},
|
||||
|
||||
renderErrorPage: function (statusCode, err, req, res, next) {
|
||||
/*jshint unused:false*/
|
||||
var self = this,
|
||||
defaultErrorTemplatePath = path.resolve(config.get('paths').adminViews, 'user-error.hbs');
|
||||
|
||||
function parseStack(stack) {
|
||||
if (!_.isString(stack)) {
|
||||
return stack;
|
||||
}
|
||||
|
||||
// TODO: split out line numbers
|
||||
var stackRegex = /\s*at\s*(\w+)?\s*\(([^\)]+)\)\s*/i;
|
||||
|
||||
return (
|
||||
stack
|
||||
.split(/[\r\n]+/)
|
||||
.slice(1)
|
||||
.map(function (line) {
|
||||
var parts = line.match(stackRegex);
|
||||
if (!parts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
function: parts[1],
|
||||
at: parts[2]
|
||||
};
|
||||
})
|
||||
.filter(function (line) {
|
||||
return !!line;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Render the error!
|
||||
function renderErrorInt(errorView) {
|
||||
var stack = null;
|
||||
|
||||
// Not Found and Maintenance Errors don't need a stack trace
|
||||
if (statusCode !== 404 && statusCode !== 503 && process.env.NODE_ENV !== 'production' && err.stack) {
|
||||
stack = parseStack(err.stack);
|
||||
}
|
||||
|
||||
res.status(statusCode).render((errorView || 'error'), {
|
||||
message: err.message || err,
|
||||
// We have to use code here, as it's the variable passed to the template
|
||||
// And error templates can be customised... therefore this constitutes API
|
||||
// In future I recommend we make this be used for a combo-version of statusCode & errorCode
|
||||
code: statusCode,
|
||||
// Adding this as being distinctly, the status code, as opposed to any other code see #6526
|
||||
statusCode: statusCode,
|
||||
stack: stack
|
||||
}, function (templateErr, html) {
|
||||
if (!templateErr) {
|
||||
return res.status(statusCode).send(html);
|
||||
}
|
||||
// There was an error trying to render the error page, output the error
|
||||
self.logError(templateErr, i18n.t('errors.errors.errorWhilstRenderingError'), i18n.t('errors.errors.errorTemplateHasError'));
|
||||
|
||||
// And then try to explain things to the user...
|
||||
// Cheat and output the error using handlebars escapeExpression
|
||||
return res.status(500).send(
|
||||
'<h1>' + i18n.t('errors.errors.oopsErrorTemplateHasError') + '</h1>' +
|
||||
'<p>' + i18n.t('errors.errors.encounteredError') + '</p>' +
|
||||
'<pre>' + hbs.handlebars.Utils.escapeExpression(templateErr.message || templateErr) + '</pre>' +
|
||||
'<br ><p>' + i18n.t('errors.errors.whilstTryingToRender') + '</p>' +
|
||||
statusCode + ' ' + '<pre>' + hbs.handlebars.Utils.escapeExpression(err.message || err) + '</pre>'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (statusCode >= 500) {
|
||||
this.logError(err, i18n.t('errors.errors.renderingErrorPage'), i18n.t('errors.errors.caughtProcessingError'));
|
||||
}
|
||||
|
||||
// Are we admin? If so, don't worry about the user template
|
||||
if ((res.isAdmin && req.user && req.user.id) || userErrorTemplateExists === true) {
|
||||
return renderErrorInt();
|
||||
}
|
||||
|
||||
// We're not admin and the template doesn't exist. Render the default.
|
||||
return renderErrorInt(defaultErrorTemplatePath);
|
||||
},
|
||||
|
||||
error404: function (req, res, next) {
|
||||
var message = i18n.t('errors.errors.pageNotFound');
|
||||
|
||||
// do not cache 404 error
|
||||
res.set({'Cache-Control': 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'});
|
||||
if (req.method === 'GET') {
|
||||
this.renderErrorPage(404, message, req, res, next);
|
||||
} else {
|
||||
res.status(404).send(message);
|
||||
}
|
||||
},
|
||||
|
||||
error500: function (err, req, res, next) {
|
||||
var statusCode = getStatusCode(err),
|
||||
returnErrors = [];
|
||||
|
||||
// 500 errors should never be cached
|
||||
res.set({'Cache-Control': 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'});
|
||||
|
||||
if (statusCode === 404) {
|
||||
return this.error404(req, res, next);
|
||||
}
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (!err || !(err instanceof Error)) {
|
||||
next();
|
||||
}
|
||||
errors.renderErrorPage(statusCode, err, req, res, next);
|
||||
} else {
|
||||
if (!_.isArray(err)) {
|
||||
err = [].concat(err);
|
||||
}
|
||||
|
||||
_.each(err, function (errorItem) {
|
||||
var errorContent = {};
|
||||
|
||||
errorContent.message = _.isString(errorItem) ? errorItem :
|
||||
(_.isObject(errorItem) ? errorItem.message : i18n.t('errors.errors.unknownError'));
|
||||
errorContent.errorType = errorItem.errorType || 'InternalServerError';
|
||||
returnErrors.push(errorContent);
|
||||
});
|
||||
|
||||
res.status(statusCode).json({errors: returnErrors});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Ensure our 'this' context for methods and preserve method arity by
|
||||
// using Function#bind for expressjs
|
||||
_.each([
|
||||
'logWarn',
|
||||
'logComponentInfo',
|
||||
'logComponentWarn',
|
||||
'rejectError',
|
||||
'throwError',
|
||||
'logError',
|
||||
'logAndThrowError',
|
||||
'logAndRejectError',
|
||||
'logErrorAndExit',
|
||||
'logErrorWithRedirect',
|
||||
'handleAPIError',
|
||||
'formatAndRejectAPIError',
|
||||
'formatHttpErrors',
|
||||
'renderErrorPage',
|
||||
'error404',
|
||||
'error500'
|
||||
], function (funcName) {
|
||||
errors[funcName] = errors[funcName].bind(errors);
|
||||
});
|
||||
|
||||
module.exports = errors;
|
||||
module.exports.NotFoundError = NotFoundError;
|
||||
module.exports.BadRequestError = BadRequestError;
|
||||
module.exports.InternalServerError = InternalServerError;
|
||||
module.exports.NoPermissionError = NoPermissionError;
|
||||
module.exports.UnauthorizedError = UnauthorizedError;
|
||||
module.exports.ValidationError = ValidationError;
|
||||
module.exports.ThemeValidationError = ThemeValidationError;
|
||||
UnauthorizedError = require('./unauthorized-error'),
|
||||
ValidationError = require('./validation-error'),
|
||||
ThemeValidationError = require('./theme-validation-error'),
|
||||
UnsupportedMediaTypeError = require('./unsupported-media-type-error'),
|
||||
EmailError = require('./email-error'),
|
||||
DataImportError = require('./data-import-error'),
|
||||
TooManyRequestsError = require('./too-many-requests-error'),
|
||||
TokenRevocationError = require('./token-revocation-error'),
|
||||
VersionMismatchError = require('./version-mismatch-error'),
|
||||
IncorrectUsage = require('./incorrect-usage'),
|
||||
Maintenance = require('./maintenance'),
|
||||
DatabaseNotPopulated = require('./database-not-populated'),
|
||||
DatabaseVersion = require('./database-version');
|
||||
|
||||
module.exports.NotFoundError = NotFoundError;
|
||||
module.exports.BadRequestError = BadRequestError;
|
||||
module.exports.InternalServerError = InternalServerError;
|
||||
module.exports.NoPermissionError = NoPermissionError;
|
||||
module.exports.UnauthorizedError = UnauthorizedError;
|
||||
module.exports.ValidationError = ValidationError;
|
||||
module.exports.ThemeValidationError = ThemeValidationError;
|
||||
module.exports.RequestEntityTooLargeError = RequestEntityTooLargeError;
|
||||
module.exports.UnsupportedMediaTypeError = UnsupportedMediaTypeError;
|
||||
module.exports.EmailError = EmailError;
|
||||
module.exports.DataImportError = DataImportError;
|
||||
module.exports.MethodNotAllowedError = MethodNotAllowedError;
|
||||
module.exports.TooManyRequestsError = TooManyRequestsError;
|
||||
module.exports.TokenRevocationError = TokenRevocationError;
|
||||
module.exports.VersionMismatchError = VersionMismatchError;
|
||||
module.exports.IncorrectUsage = IncorrectUsage;
|
||||
module.exports.Maintenance = Maintenance;
|
||||
module.exports.DatabaseNotPopulated = DatabaseNotPopulated;
|
||||
module.exports.DatabaseVersion = DatabaseVersion;
|
||||
module.exports.UnsupportedMediaTypeError = UnsupportedMediaTypeError;
|
||||
module.exports.EmailError = EmailError;
|
||||
module.exports.DataImportError = DataImportError;
|
||||
module.exports.MethodNotAllowedError = MethodNotAllowedError;
|
||||
module.exports.TooManyRequestsError = TooManyRequestsError;
|
||||
module.exports.TokenRevocationError = TokenRevocationError;
|
||||
module.exports.VersionMismatchError = VersionMismatchError;
|
||||
module.exports.IncorrectUsage = IncorrectUsage;
|
||||
module.exports.Maintenance = Maintenance;
|
||||
module.exports.DatabaseNotPopulated = DatabaseNotPopulated;
|
||||
module.exports.DatabaseVersion = DatabaseVersion;
|
||||
|
@ -1,11 +1,13 @@
|
||||
// # Internal Server Error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function InternalServerError(message) {
|
||||
function InternalServerError(message, context, help) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 500;
|
||||
this.errorType = this.name;
|
||||
this.context = context;
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
InternalServerError.prototype = Object.create(Error.prototype);
|
||||
|
@ -1,11 +1,13 @@
|
||||
// # Too Many Requests Error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function TooManyRequestsError(message) {
|
||||
function TooManyRequestsError(message, context, help) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 429;
|
||||
this.errorType = this.name;
|
||||
this.context = context;
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
TooManyRequestsError.prototype = Object.create(Error.prototype);
|
||||
|
@ -1,11 +1,13 @@
|
||||
// # Unauthorized error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function UnauthorizedError(message) {
|
||||
function UnauthorizedError(message, context, help) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 401;
|
||||
this.errorType = this.name;
|
||||
this.context = context;
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
UnauthorizedError.prototype = Object.create(Error.prototype);
|
||||
|
@ -1,7 +1,7 @@
|
||||
// # Validation Error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function ValidationError(message, offendingProperty) {
|
||||
function ValidationError(message, offendingProperty, context, help) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 422;
|
||||
@ -9,6 +9,8 @@ function ValidationError(message, offendingProperty) {
|
||||
this.property = offendingProperty;
|
||||
}
|
||||
this.errorType = this.name;
|
||||
this.context = context;
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
ValidationError.prototype = Object.create(Error.prototype);
|
||||
|
@ -7,6 +7,7 @@ var debug = require('debug')('ghost:server'),
|
||||
path = require('path'),
|
||||
_ = require('lodash'),
|
||||
errors = require('./errors'),
|
||||
logging = require('./logging'),
|
||||
config = require('./config'),
|
||||
i18n = require('./i18n'),
|
||||
moment = require('moment');
|
||||
@ -75,18 +76,19 @@ GhostServer.prototype.start = function (externalApp) {
|
||||
|
||||
self.httpServer.on('error', function (error) {
|
||||
if (error.errno === 'EADDRINUSE') {
|
||||
errors.logError(
|
||||
logging.error(new errors.InternalServerError(
|
||||
i18n.t('errors.httpServer.addressInUse.error'),
|
||||
i18n.t('errors.httpServer.addressInUse.context', {port: config.get('server').port}),
|
||||
i18n.t('errors.httpServer.addressInUse.help')
|
||||
);
|
||||
));
|
||||
} else {
|
||||
errors.logError(
|
||||
logging.error(new errors.InternalServerError(
|
||||
i18n.t('errors.httpServer.otherError.error', {errorNumber: error.errno}),
|
||||
i18n.t('errors.httpServer.otherError.context'),
|
||||
i18n.t('errors.httpServer.otherError.help')
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
process.exit(-1);
|
||||
});
|
||||
self.httpServer.on('connection', self.connection.bind(self));
|
||||
|
@ -4,7 +4,7 @@
|
||||
// Block helper designed for looping through posts
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
i18n = require('../i18n'),
|
||||
labs = require('../utils/labs'),
|
||||
utils = require('./utils'),
|
||||
@ -33,7 +33,7 @@ function filterItemsByVisibility(items, options) {
|
||||
|
||||
foreach = function (items, options) {
|
||||
if (!options) {
|
||||
errors.logWarn(i18n.t('warnings.helpers.foreach.iteratorNeeded'));
|
||||
logging.warn(i18n.t('warnings.helpers.foreach.iteratorNeeded'));
|
||||
}
|
||||
|
||||
if (hbsUtils.isFunction(items)) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
var _ = require('lodash'),
|
||||
hbs = require('express-hbs'),
|
||||
Promise = require('bluebird'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
api = require('../api'),
|
||||
jsonpath = require('jsonpath'),
|
||||
labs = require('../utils/labs'),
|
||||
@ -101,13 +101,13 @@ get = function get(resource, options) {
|
||||
|
||||
if (!options.fn) {
|
||||
data.error = i18n.t('warnings.helpers.get.mustBeCalledAsBlock');
|
||||
errors.logWarn(data.error);
|
||||
logging.warn(data.error);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (!_.includes(resources, resource)) {
|
||||
data.error = i18n.t('warnings.helpers.get.invalidResource');
|
||||
errors.logWarn(data.error);
|
||||
logging.warn(data.error);
|
||||
return Promise.resolve(options.inverse(self, {data: data}));
|
||||
}
|
||||
|
||||
@ -145,19 +145,20 @@ get = function get(resource, options) {
|
||||
|
||||
module.exports = function getWithLabs(resource, options) {
|
||||
var self = this,
|
||||
errorMessages = [
|
||||
i18n.t('warnings.helpers.get.helperNotAvailable'),
|
||||
i18n.t('warnings.helpers.get.apiMustBeEnabled'),
|
||||
i18n.t('warnings.helpers.get.seeLink', {url: 'http://support.ghost.org/public-api-beta'})
|
||||
];
|
||||
err;
|
||||
|
||||
if (labs.isSet('publicAPI') === true) {
|
||||
// get helper is active
|
||||
return get.call(self, resource, options);
|
||||
} else {
|
||||
errors.logError.apply(this, errorMessages);
|
||||
err = new Error();
|
||||
err.message = i18n.t('warnings.helpers.get.helperNotAvailable');
|
||||
err.context = i18n.t('warnings.helpers.get.apiMustBeEnabled');
|
||||
err.help = i18n.t('warnings.helpers.get.seeLink', {url: 'http://support.ghost.org/public-api-beta'});
|
||||
logging.error(err);
|
||||
|
||||
return Promise.resolve(function noGetHelper() {
|
||||
return '<script>console.error("' + errorMessages.join(' ') + '");</script>';
|
||||
return '<script>console.error(' + JSON.stringify(err) + ');</script>';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -4,7 +4,7 @@
|
||||
// Checks if a post has a particular property
|
||||
|
||||
var _ = require('lodash'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
i18n = require('../i18n'),
|
||||
has;
|
||||
|
||||
@ -41,7 +41,7 @@ has = function (options) {
|
||||
}
|
||||
|
||||
if (!tagList && !authorList) {
|
||||
errors.logWarn(i18n.t('warnings.helpers.has.invalidAttribute'));
|
||||
logging.warn(i18n.t('warnings.helpers.has.invalidAttribute'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
var hbs = require('express-hbs'),
|
||||
Promise = require('bluebird'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
utils = require('./utils'),
|
||||
i18n = require('../i18n'),
|
||||
coreHelpers = {},
|
||||
@ -48,7 +49,8 @@ coreHelpers.helperMissing = function (arg) {
|
||||
if (arguments.length === 2) {
|
||||
return undefined;
|
||||
}
|
||||
errors.logError(i18n.t('warnings.helpers.index.missingHelper', {arg: arg}));
|
||||
|
||||
logging.error(new errors.InternalServerError(i18n.t('warnings.helpers.index.missingHelper', {arg: arg})));
|
||||
};
|
||||
|
||||
// Register an async handlebars helper for a given handlebars instance
|
||||
@ -64,7 +66,8 @@ function registerAsyncHelper(hbs, name, fn) {
|
||||
Promise.resolve(fn.call(this, context, options)).then(function (result) {
|
||||
cb(result);
|
||||
}).catch(function (err) {
|
||||
errors.logAndThrowError(err, 'registerAsyncThemeHelper: ' + name);
|
||||
logging.warn('registerAsyncThemeHelper: ' + name);
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Usage: `{{#is "paged"}}`, `{{#is "index, paged"}}`
|
||||
// Checks whether we're in a given context.
|
||||
var _ = require('lodash'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
i18n = require('../i18n'),
|
||||
is;
|
||||
|
||||
@ -12,7 +12,7 @@ is = function (context, options) {
|
||||
var currentContext = options.data.root.context;
|
||||
|
||||
if (!_.isString(context)) {
|
||||
errors.logWarn(i18n.t('warnings.helpers.is.invalidAttribute'));
|
||||
logging.warn(i18n.t('warnings.helpers.is.invalidAttribute'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
var _ = require('lodash'),
|
||||
hbs = require('express-hbs'),
|
||||
i18n = require('../i18n'),
|
||||
|
||||
errors = require('../errors'),
|
||||
template = require('./template'),
|
||||
navigation;
|
||||
@ -19,13 +18,13 @@ navigation = function (options) {
|
||||
data;
|
||||
|
||||
if (!_.isObject(navigationData) || _.isFunction(navigationData)) {
|
||||
return errors.logAndThrowError(i18n.t('warnings.helpers.navigation.invalidData'));
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.navigation.invalidData'));
|
||||
}
|
||||
|
||||
if (navigationData.filter(function (e) {
|
||||
return (_.isUndefined(e.label) || _.isUndefined(e.url));
|
||||
}).length > 0) {
|
||||
return errors.logAndThrowError(i18n.t('warnings.helpers.navigation.valuesMustBeDefined'));
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.navigation.valuesMustBeDefined'));
|
||||
}
|
||||
|
||||
// check for non-null string values
|
||||
@ -33,7 +32,7 @@ navigation = function (options) {
|
||||
return ((!_.isNull(e.label) && !_.isString(e.label)) ||
|
||||
(!_.isNull(e.url) && !_.isString(e.url)));
|
||||
}).length > 0) {
|
||||
return errors.logAndThrowError(i18n.t('warnings.helpers.navigation.valuesMustBeString'));
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.navigation.valuesMustBeString'));
|
||||
}
|
||||
|
||||
function _slugify(label) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
//
|
||||
// We use the name page_url to match the helper for consistency:
|
||||
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
|
||||
var errors = require('../errors'),
|
||||
var logging = require('../logging'),
|
||||
i18n = require('../i18n'),
|
||||
getPaginatedUrl = require('../data/meta/paginated_url'),
|
||||
page_url,
|
||||
@ -31,7 +31,7 @@ page_url = function (page, options) {
|
||||
// context. This helper is deprecated and will be removed in future versions.
|
||||
//
|
||||
pageUrl = function (pageNum, options) {
|
||||
errors.logWarn(i18n.t('warnings.helpers.page_url.isDeprecated'));
|
||||
logging.warn(i18n.t('warnings.helpers.page_url.isDeprecated'));
|
||||
|
||||
/*jshint unused:false*/
|
||||
var self = this;
|
||||
|
@ -11,22 +11,22 @@ var _ = require('lodash'),
|
||||
pagination = function (options) {
|
||||
/*jshint unused:false*/
|
||||
if (!_.isObject(this.pagination) || _.isFunction(this.pagination)) {
|
||||
return errors.logAndThrowError(i18n.t('warnings.helpers.pagination.invalidData'));
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.pagination.invalidData'));
|
||||
}
|
||||
|
||||
if (_.isUndefined(this.pagination.page) || _.isUndefined(this.pagination.pages) ||
|
||||
_.isUndefined(this.pagination.total) || _.isUndefined(this.pagination.limit)) {
|
||||
return errors.logAndThrowError(i18n.t('warnings.helpers.pagination.valuesMustBeDefined'));
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.pagination.valuesMustBeDefined'));
|
||||
}
|
||||
|
||||
if ((!_.isNull(this.pagination.next) && !_.isNumber(this.pagination.next)) ||
|
||||
(!_.isNull(this.pagination.prev) && !_.isNumber(this.pagination.prev))) {
|
||||
return errors.logAndThrowError(i18n.t('warnings.helpers.pagination.nextPrevValuesMustBeNumeric'));
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.pagination.nextPrevValuesMustBeNumeric'));
|
||||
}
|
||||
|
||||
if (!_.isNumber(this.pagination.page) || !_.isNumber(this.pagination.pages) ||
|
||||
!_.isNumber(this.pagination.total) || !_.isNumber(this.pagination.limit)) {
|
||||
return errors.logAndThrowError(i18n.t('warnings.helpers.pagination.valuesMustBeNumeric'));
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.pagination.valuesMustBeNumeric'));
|
||||
}
|
||||
|
||||
var data = _.merge({}, this.pagination);
|
||||
|
@ -17,7 +17,7 @@ var hbs = require('express-hbs'),
|
||||
plural = function (number, options) {
|
||||
if (_.isUndefined(options.hash) || _.isUndefined(options.hash.empty) ||
|
||||
_.isUndefined(options.hash.singular) || _.isUndefined(options.hash.plural)) {
|
||||
return errors.logAndThrowError(i18n.t('warnings.helpers.plural.valuesMustBeDefined'));
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.plural.valuesMustBeDefined'));
|
||||
}
|
||||
|
||||
if (number === 0) {
|
||||
|
@ -11,8 +11,7 @@ templates.execute = function (name, context, options) {
|
||||
var partial = hbs.handlebars.partials[name];
|
||||
|
||||
if (partial === undefined) {
|
||||
errors.logAndThrowError(i18n.t('warnings.helpers.template.templateNotFound', {name: name}));
|
||||
return;
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.template.templateNotFound', {name: name}));
|
||||
}
|
||||
|
||||
// If the partial view is not compiled, it compiles and saves in handlebars
|
||||
|
@ -20,7 +20,7 @@ var debug = require('debug')('ghost:boot:init'),
|
||||
i18n = require('./i18n'),
|
||||
api = require('./api'),
|
||||
config = require('./config'),
|
||||
errors = require('./errors'),
|
||||
logging = require('./logging'),
|
||||
middleware = require('./middleware'),
|
||||
db = require('./data/schema'),
|
||||
models = require('./models'),
|
||||
@ -121,11 +121,11 @@ function init(options) {
|
||||
.catch(function (result) {
|
||||
// TODO: change `result` to something better
|
||||
result.errors.forEach(function (err) {
|
||||
errors.logError(err.message, err.context, err.help);
|
||||
logging.error(err);
|
||||
});
|
||||
|
||||
result.warnings.forEach(function (warn) {
|
||||
errors.logWarn(warn.message, warn.context, warn.help);
|
||||
logging.warn(warn.message);
|
||||
});
|
||||
});
|
||||
|
||||
|
210
core/server/logging/GhostLogger.js
Normal file
210
core/server/logging/GhostLogger.js
Normal file
@ -0,0 +1,210 @@
|
||||
var bunyan = require('bunyan'),
|
||||
_ = require('lodash'),
|
||||
GhostPrettyStream = require('./PrettyStream');
|
||||
|
||||
function GhostLogger(options) {
|
||||
this.env = options.env;
|
||||
this.transports = options.transports || ['stdout'];
|
||||
this.level = options.level || 'info';
|
||||
this.mode = options.mode || 'short';
|
||||
this.path = options.path || 'ghost.log';
|
||||
this.rotation = options.rotation || false;
|
||||
this.loggers = {};
|
||||
|
||||
this.setSerializers();
|
||||
this.setLoggers();
|
||||
this.setStreams();
|
||||
}
|
||||
|
||||
// @TODO: add correlation identifier
|
||||
// @TODO: res.on('finish') has no access to the response body
|
||||
GhostLogger.prototype.setSerializers = function setSerializers() {
|
||||
var self = this;
|
||||
|
||||
this.serializers = {
|
||||
req: function (req) {
|
||||
return {
|
||||
url: req.url,
|
||||
method: req.method,
|
||||
originalUrl: req.originalUrl,
|
||||
params: req.params,
|
||||
headers: self.removeSensitiveData(req.headers),
|
||||
body: self.removeSensitiveData(req.body),
|
||||
query: self.removeSensitiveData(req.query)
|
||||
};
|
||||
},
|
||||
res: function (res) {
|
||||
return {
|
||||
_headers: self.removeSensitiveData(res._headers),
|
||||
statusCode: res.statusCode
|
||||
};
|
||||
},
|
||||
err: function (err) {
|
||||
return {
|
||||
name: err.errorType,
|
||||
statusCode: err.statusCode,
|
||||
level: err.level,
|
||||
message: err.message,
|
||||
context: err.context,
|
||||
help: err.help,
|
||||
stack: err.stack,
|
||||
hideStack: err.hideStack
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
GhostLogger.prototype.setLoggers = function setLoggers() {
|
||||
var self = this;
|
||||
|
||||
this.log = {
|
||||
info: function (options) {
|
||||
var req = options.req,
|
||||
res = options.res;
|
||||
|
||||
_.each(self.loggers, function (logger) {
|
||||
logger.log.info({
|
||||
req: req,
|
||||
res: res
|
||||
});
|
||||
});
|
||||
},
|
||||
debug: function (options) {
|
||||
var req = options.req,
|
||||
res = options.res;
|
||||
|
||||
_.each(self.loggers, function (logger) {
|
||||
logger.log.debug({
|
||||
req: req,
|
||||
res: res
|
||||
});
|
||||
});
|
||||
},
|
||||
error: function (options) {
|
||||
var req = options.req,
|
||||
res = options.res,
|
||||
err = options.err;
|
||||
|
||||
_.each(self.loggers, function (logger) {
|
||||
logger.log.error({
|
||||
req: req,
|
||||
res: res,
|
||||
err: err
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
GhostLogger.prototype.setStreams = function setStreams() {
|
||||
var self = this,
|
||||
streams = [],
|
||||
prettyStdOut;
|
||||
|
||||
_.each(self.transports, function (transport) {
|
||||
if (transport === 'file') {
|
||||
streams.push({
|
||||
name: 'file',
|
||||
stream: {
|
||||
path: self.path,
|
||||
level: self.level
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (transport === 'stdout') {
|
||||
prettyStdOut = new GhostPrettyStream();
|
||||
prettyStdOut.pipe(process.stdout);
|
||||
|
||||
streams.push({
|
||||
name: 'stdout',
|
||||
stream: {
|
||||
type: 'raw',
|
||||
stream: prettyStdOut,
|
||||
level: self.level
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (self.rotation) {
|
||||
streams.push({
|
||||
name: 'rotation',
|
||||
stream: {
|
||||
type: 'rotating-file',
|
||||
path: self.path,
|
||||
period: '1w',
|
||||
count: 3,
|
||||
level: self.level
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// the env defines which streams are available
|
||||
_.each(streams, function (stream) {
|
||||
self.loggers[stream.name] = {
|
||||
name: stream.name,
|
||||
log: bunyan.createLogger({
|
||||
name: 'Log',
|
||||
streams: [stream.stream],
|
||||
serializers: self.serializers
|
||||
})
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
GhostLogger.prototype.removeSensitiveData = function removeSensitiveData(obj) {
|
||||
var newObj = {};
|
||||
|
||||
_.each(obj, function (value, key) {
|
||||
if (!key.match(/pin|password|authorization|cookie/gi)) {
|
||||
newObj[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return newObj;
|
||||
};
|
||||
|
||||
GhostLogger.prototype.info = function info() {
|
||||
var print = '';
|
||||
|
||||
_.each(arguments, function (value) {
|
||||
print += value;
|
||||
print += ' ';
|
||||
});
|
||||
|
||||
this.loggers.stdout.log.info(print);
|
||||
};
|
||||
|
||||
GhostLogger.prototype.warn = function warn() {
|
||||
var print = '';
|
||||
|
||||
_.each(arguments, function (value) {
|
||||
print += value;
|
||||
print += ' ';
|
||||
});
|
||||
|
||||
this.loggers.stdout.log.warn(print);
|
||||
};
|
||||
|
||||
GhostLogger.prototype.debug = function debug(options) {
|
||||
this.loggers.stdout.log.debug(options);
|
||||
};
|
||||
|
||||
GhostLogger.prototype.error = function error(err) {
|
||||
this.log.error({err: err});
|
||||
};
|
||||
|
||||
GhostLogger.prototype.request = function request(options) {
|
||||
var req = options.req,
|
||||
res = options.res,
|
||||
err = options.err;
|
||||
|
||||
if (err) {
|
||||
this.log.error({req: req, res: res, err: err});
|
||||
} else {
|
||||
this.log.info({req: req, res: res});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = GhostLogger;
|
154
core/server/logging/PrettyStream.js
Normal file
154
core/server/logging/PrettyStream.js
Normal file
@ -0,0 +1,154 @@
|
||||
// jscs:disable
|
||||
var _ = require('lodash'),
|
||||
moment = require('moment'),
|
||||
Stream = require('stream').Stream,
|
||||
util = require('util'),
|
||||
format = util.format,
|
||||
prettyjson = require('prettyjson'),
|
||||
__private__ = {
|
||||
levelFromName: {
|
||||
10: 'trace',
|
||||
20: 'debug',
|
||||
30: 'info',
|
||||
40: 'warn',
|
||||
50: 'error',
|
||||
60: 'fatal'
|
||||
},
|
||||
colorForLevel: {
|
||||
10: 'grey',
|
||||
20: 'grey',
|
||||
30: 'cyan',
|
||||
40: 'magenta',
|
||||
50: 'red',
|
||||
60: 'inverse'
|
||||
},
|
||||
colors: {
|
||||
'bold': [1, 22],
|
||||
'italic': [3, 23],
|
||||
'underline': [4, 24],
|
||||
'inverse': [7, 27],
|
||||
'white': [37, 39],
|
||||
'grey': [90, 39],
|
||||
'black': [30, 39],
|
||||
'blue': [34, 39],
|
||||
'cyan': [36, 39],
|
||||
'green': [32, 39],
|
||||
'magenta': [35, 39],
|
||||
'red': [31, 39],
|
||||
'yellow': [33, 39]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function PrettyStream() {
|
||||
}
|
||||
util.inherits(PrettyStream, Stream);
|
||||
|
||||
|
||||
function colorize(color, value) {
|
||||
return '\x1B[' + __private__.colors[color][0] + 'm' + value + '\x1B[' + __private__.colors[color][1] + 'm';
|
||||
}
|
||||
|
||||
|
||||
PrettyStream.prototype.write = function write(data) {
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (err) {
|
||||
this.emit('data', err);
|
||||
}
|
||||
}
|
||||
|
||||
var body = {},
|
||||
time = moment(data.time).format('YYYY-MM-DD HH:mm:ss'),
|
||||
logLevel = __private__.levelFromName[data.level].toUpperCase(),
|
||||
codes = __private__.colors[__private__.colorForLevel[data.level]],
|
||||
bodyPretty = '';
|
||||
|
||||
logLevel = '\x1B[' + codes[0] + 'm' + logLevel + '\x1B[' + codes[1] + 'm';
|
||||
|
||||
if (data.msg) {
|
||||
body.msg = data.msg;
|
||||
}
|
||||
|
||||
if (data.req && data.res) {
|
||||
_.each(data.req, function (value, key) {
|
||||
if (['headers', 'query', 'body'].indexOf(key) !== -1 && !_.isEmpty(value)) {
|
||||
bodyPretty += colorize('yellow', key.toUpperCase()) + '\n';
|
||||
bodyPretty += prettyjson.render(value, {}) + '\n';
|
||||
}
|
||||
});
|
||||
|
||||
bodyPretty += '\n';
|
||||
if (data.err) {
|
||||
if (data.err.level) {
|
||||
bodyPretty += colorize('yellow', 'ERROR (' + data.err.level + ')') + '\n';
|
||||
} else {
|
||||
bodyPretty += colorize('yellow', 'ERROR\n');
|
||||
}
|
||||
|
||||
_.each(data.err, function (value, key) {
|
||||
if (['message', 'context', 'help', 'stack'].indexOf(key) !== -1 && !_.isEmpty(value)) {
|
||||
bodyPretty += value + '\n';
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (data.err) {
|
||||
_.each(data.err, function (value, key) {
|
||||
if (_.isEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === 'level') {
|
||||
bodyPretty += colorize('underline', key + ':' + value) + '\n\n';
|
||||
}
|
||||
else if (key === 'message') {
|
||||
bodyPretty += colorize('red', value) + '\n';
|
||||
}
|
||||
else if (key === 'context') {
|
||||
bodyPretty += colorize('white', value) + '\n';
|
||||
}
|
||||
else if (key === 'help') {
|
||||
bodyPretty += colorize('yellow', value) + '\n';
|
||||
}
|
||||
else if (key === 'stack' && !data.err['hideStack']) {
|
||||
bodyPretty += colorize('white', value) + '\n';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// print string
|
||||
bodyPretty += data.msg;
|
||||
}
|
||||
|
||||
try {
|
||||
if (data.req && data.res) {
|
||||
this.emit('data', format('[%s] %s --> %s %s (%s) \n%s\n\n',
|
||||
time,
|
||||
logLevel,
|
||||
data.req.method,
|
||||
data.req.url,
|
||||
data.res.statusCode,
|
||||
colorize('grey', bodyPretty)
|
||||
));
|
||||
} else if (data.err) {
|
||||
this.emit('data', format('[%s] %s \n%s\n\n',
|
||||
time,
|
||||
logLevel,
|
||||
colorize('grey', bodyPretty)
|
||||
));
|
||||
} else {
|
||||
this.emit('data', format('[%s] %s %s\n',
|
||||
time,
|
||||
logLevel,
|
||||
colorize('grey', bodyPretty)
|
||||
));
|
||||
}
|
||||
} catch (err) {
|
||||
this.emit('data', err);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
module.exports = PrettyStream;
|
12
core/server/logging/index.js
Normal file
12
core/server/logging/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
var config = require('../config'),
|
||||
GhostLogger = require('./GhostLogger'),
|
||||
adapter = new GhostLogger({
|
||||
env: config.get('env'),
|
||||
mode: process.env.NODE_MODE,
|
||||
level: process.env.NODE_LEVEL || config.get('logging:level'),
|
||||
transports: config.get('logging:transports'),
|
||||
rotation: config.get('logging:rotation'),
|
||||
path: config.get('paths:appRoot') + '/ghost.log'
|
||||
});
|
||||
|
||||
module.exports = adapter;
|
110
core/server/middleware/error-handler.js
Normal file
110
core/server/middleware/error-handler.js
Normal file
@ -0,0 +1,110 @@
|
||||
var _ = require('lodash'),
|
||||
path = require('path'),
|
||||
hbs = require('express-hbs'),
|
||||
config = require('../config'),
|
||||
i18n = require('../i18n'),
|
||||
_private = {};
|
||||
|
||||
_private.parseStack = function (stack) {
|
||||
if (!_.isString(stack)) {
|
||||
return stack;
|
||||
}
|
||||
|
||||
// TODO: split out line numbers
|
||||
var stackRegex = /\s*at\s*(\w+)?\s*\(([^\)]+)\)\s*/i;
|
||||
|
||||
return (
|
||||
stack
|
||||
.split(/[\r\n]+/)
|
||||
.slice(1)
|
||||
.map(function (line) {
|
||||
var parts = line.match(stackRegex);
|
||||
if (!parts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
function: parts[1],
|
||||
at: parts[2]
|
||||
};
|
||||
})
|
||||
.filter(function (line) {
|
||||
return !!line;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
_private.handleHTMLResponse = function handleHTMLResponse(err, req, res) {
|
||||
return function handleHTMLResponse() {
|
||||
var availableTheme = config.get('paths').availableThemes[req.app.get('activeTheme')] || {},
|
||||
defaultTemplate = availableTheme['error.hbs'] ||
|
||||
path.resolve(config.get('paths').adminViews, 'user-error.hbs') ||
|
||||
'error';
|
||||
|
||||
res.render(defaultTemplate, {
|
||||
message: err.message,
|
||||
code: err.statusCode,
|
||||
stack: _private.parseStack(err.stack)
|
||||
}, function renderResponse(err, html) {
|
||||
if (!err) {
|
||||
return res.send(html);
|
||||
}
|
||||
|
||||
// And then try to explain things to the user...
|
||||
// Cheat and output the error using handlebars escapeExpression
|
||||
return res.status(500).send(
|
||||
'<h1>' + i18n.t('errors.errors.oopsErrorTemplateHasError') + '</h1>' +
|
||||
'<p>' + i18n.t('errors.errors.encounteredError') + '</p>' +
|
||||
'<pre>' + hbs.handlebars.Utils.escapeExpression(err.message || err) + '</pre>' +
|
||||
'<br ><p>' + i18n.t('errors.errors.whilstTryingToRender') + '</p>' +
|
||||
err.statusCode + ' ' + '<pre>' + hbs.handlebars.Utils.escapeExpression(err.message || err) + '</pre>'
|
||||
);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @TODO: jsonapi errors format
|
||||
*/
|
||||
_private.handleJSONResponse = function handleJSONResponse(err, req, res) {
|
||||
return function handleJSONResponse() {
|
||||
res.json({
|
||||
errors: [{
|
||||
message: err.message,
|
||||
errorType: err.errorType,
|
||||
errorDetails: err.errorDetails
|
||||
}]
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @TODO: test uncaught exception (wrap into custom error!)
|
||||
* @TODO: support multiple errors
|
||||
* @TODO: decouple req.err
|
||||
*/
|
||||
module.exports = function errorHandler(err, req, res, next) {
|
||||
if (_.isArray(err)) {
|
||||
err = err[0];
|
||||
}
|
||||
|
||||
req.err = err;
|
||||
res.statusCode = err.statusCode;
|
||||
|
||||
// never cache errors
|
||||
res.set({
|
||||
'Cache-Control': 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'
|
||||
});
|
||||
|
||||
// @TODO: does this resolves all use cases?
|
||||
if (!req.headers.accept && !req.headers.hasOwnProperty('content-type')) {
|
||||
req.headers.accept = 'text/html';
|
||||
}
|
||||
|
||||
// jscs:disable
|
||||
res.format({
|
||||
json: _private.handleJSONResponse(err, req, res, next),
|
||||
html: _private.handleHTMLResponse(err, req, res, next),
|
||||
'default': _private.handleHTMLResponse(err, req, res, next)
|
||||
});
|
||||
};
|
@ -5,12 +5,13 @@ var debug = require('debug')('ghost:middleware'),
|
||||
errors = require('../errors'),
|
||||
express = require('express'),
|
||||
hbs = require('express-hbs'),
|
||||
logger = require('morgan'),
|
||||
path = require('path'),
|
||||
routes = require('../routes'),
|
||||
serveStatic = require('express').static,
|
||||
slashes = require('connect-slashes'),
|
||||
storage = require('../storage'),
|
||||
logging = require('../logging'),
|
||||
i18n = require('../i18n'),
|
||||
utils = require('../utils'),
|
||||
sitemapHandler = require('../data/xml/sitemap/handler'),
|
||||
multer = require('multer'),
|
||||
@ -24,7 +25,8 @@ var debug = require('debug')('ghost:middleware'),
|
||||
staticTheme = require('./static-theme'),
|
||||
themeHandler = require('./theme-handler'),
|
||||
uncapitalise = require('./uncapitalise'),
|
||||
maintenance = require('./maintenance'),
|
||||
maintenance = require('./maintenance'),
|
||||
errorHandler = require('./error-handler'),
|
||||
versionMatch = require('./api/version-match'),
|
||||
cors = require('./cors'),
|
||||
validation = require('./validation'),
|
||||
@ -40,7 +42,7 @@ middleware = {
|
||||
cacheControl: cacheControl,
|
||||
spamPrevention: spamPrevention,
|
||||
api: {
|
||||
errorHandler: errors.handleAPIError,
|
||||
errorHandler: errorHandler,
|
||||
cors: cors,
|
||||
labs: labs,
|
||||
versionMatch: versionMatch,
|
||||
@ -50,8 +52,7 @@ middleware = {
|
||||
|
||||
setupMiddleware = function setupMiddleware(blogApp) {
|
||||
debug('Middleware start');
|
||||
var logging = config.get('logging'),
|
||||
corePath = config.get('paths').corePath,
|
||||
var corePath = config.get('paths').corePath,
|
||||
adminApp = express(),
|
||||
adminHbs = hbs.create();
|
||||
|
||||
@ -79,14 +80,16 @@ setupMiddleware = function setupMiddleware(blogApp) {
|
||||
// (X-Forwarded-Proto header will be checked, if present)
|
||||
blogApp.enable('trust proxy');
|
||||
|
||||
// Logging configuration
|
||||
if (logging !== false) {
|
||||
if (blogApp.get('env') !== 'development') {
|
||||
blogApp.use(logger('combined', logging));
|
||||
} else {
|
||||
blogApp.use(logger('dev', logging));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* request logging
|
||||
*/
|
||||
blogApp.use(function expressLogging(req, res, next) {
|
||||
res.once('finish', function () {
|
||||
logging.request({req: req, res: res, err: req.err});
|
||||
});
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
if (debug.enabled) {
|
||||
// debug keeps a timer, so this is super useful
|
||||
@ -210,12 +213,12 @@ setupMiddleware = function setupMiddleware(blogApp) {
|
||||
// Set up Frontend routes (including private blogging routes)
|
||||
blogApp.use(routes.frontend());
|
||||
|
||||
// ### Error handling
|
||||
// 404 Handler
|
||||
blogApp.use(errors.error404);
|
||||
// ### Error handlers
|
||||
blogApp.use(function pageNotFound(req, res, next) {
|
||||
next(new errors.NotFoundError(i18n.t('errors.errors.pageNotFound')));
|
||||
});
|
||||
|
||||
// 500 Handler
|
||||
blogApp.use(errors.error500);
|
||||
blogApp.use(errorHandler);
|
||||
debug('Middleware end');
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@ labs = {
|
||||
if (labsUtil.isSet('subscribers') === true) {
|
||||
return next();
|
||||
} else {
|
||||
return errors.handleAPIError(new errors.NotFoundError(), req, res, next);
|
||||
return next(new errors.NotFoundError());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -22,7 +22,6 @@ spamPrevention = {
|
||||
remoteAddress = req.connection.remoteAddress,
|
||||
deniedRateLimit = '',
|
||||
ipCount = '',
|
||||
message = i18n.t('errors.middleware.spamprevention.tooManyAttempts'),
|
||||
rateSigninPeriod = config.rateSigninPeriod || 3600,
|
||||
rateSigninAttempts = config.rateSigninAttempts || 10;
|
||||
|
||||
@ -44,12 +43,11 @@ spamPrevention = {
|
||||
deniedRateLimit = (ipCount[remoteAddress] > rateSigninAttempts);
|
||||
|
||||
if (deniedRateLimit) {
|
||||
errors.logError(
|
||||
return next(new errors.TooManyRequestsError(
|
||||
i18n.t('errors.middleware.spamprevention.tooManyAttempts') + rateSigninPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater'),
|
||||
i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.error', {rateSigninAttempts: rateSigninAttempts, rateSigninPeriod: rateSigninPeriod}),
|
||||
i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
|
||||
);
|
||||
message += rateSigninPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater');
|
||||
return next(new errors.TooManyRequestsError(message));
|
||||
));
|
||||
}
|
||||
next();
|
||||
},
|
||||
@ -65,7 +63,6 @@ spamPrevention = {
|
||||
ipCount = '',
|
||||
deniedRateLimit = '',
|
||||
deniedEmailRateLimit = '',
|
||||
message = i18n.t('errors.middleware.spamprevention.tooManyAttempts'),
|
||||
index = _.findIndex(forgottenSecurity, function findIndex(logTime) {
|
||||
return (logTime.ip === remoteAddress && logTime.email === email);
|
||||
});
|
||||
@ -94,22 +91,19 @@ spamPrevention = {
|
||||
}
|
||||
|
||||
if (deniedEmailRateLimit) {
|
||||
errors.logError(
|
||||
return next(new errors.TooManyRequestsError(
|
||||
i18n.t('errors.middleware.spamprevention.tooManyAttempts') + rateForgottenPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater'),
|
||||
i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.error', {rfa: rateForgottenAttempts, rfp: rateForgottenPeriod}),
|
||||
i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.context')
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
if (deniedRateLimit) {
|
||||
errors.logError(
|
||||
return next(new errors.TooManyRequestsError(
|
||||
i18n.t('errors.middleware.spamprevention.tooManyAttempts') + rateForgottenPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater'),
|
||||
i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error', {rfa: rateForgottenAttempts, rfp: rateForgottenPeriod}),
|
||||
i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
|
||||
);
|
||||
}
|
||||
|
||||
if (deniedEmailRateLimit || deniedRateLimit) {
|
||||
message += rateForgottenPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater');
|
||||
return next(new errors.TooManyRequestsError(message));
|
||||
));
|
||||
}
|
||||
|
||||
next();
|
||||
|
@ -4,6 +4,7 @@ var _ = require('lodash'),
|
||||
hbs = require('express-hbs'),
|
||||
api = require('../api'),
|
||||
config = require('../config'),
|
||||
logging = require('../logging'),
|
||||
errors = require('../errors'),
|
||||
i18n = require('../i18n'),
|
||||
themeHandler;
|
||||
@ -80,9 +81,6 @@ themeHandler = {
|
||||
|
||||
blogApp.engine('hbs', hbs.express3(hbsOptions));
|
||||
|
||||
// Update user error template
|
||||
errors.updateActiveTheme(activeTheme);
|
||||
|
||||
// Set active theme variable on the express server
|
||||
blogApp.set('activeTheme', activeTheme);
|
||||
},
|
||||
@ -102,15 +100,14 @@ themeHandler = {
|
||||
// Change theme
|
||||
if (!config.get('paths').availableThemes.hasOwnProperty(activeTheme.value)) {
|
||||
if (!res.isAdmin) {
|
||||
// Throw an error if the theme is not available, but not on the admin UI
|
||||
return errors.throwError(i18n.t('errors.middleware.themehandler.missingTheme', {theme: activeTheme.value}));
|
||||
return next(new errors.InternalServerError(i18n.t('errors.middleware.themehandler.missingTheme', {theme: activeTheme.value})));
|
||||
} else {
|
||||
// At this point the activated theme is not present and the current
|
||||
// request is for the admin client. In order to allow the user access
|
||||
// to the admin client we set an hbs instance on the app so that middleware
|
||||
// processing can continue.
|
||||
blogApp.engine('hbs', hbs.express3());
|
||||
errors.logWarn(i18n.t('errors.middleware.themehandler.missingTheme', {theme: activeTheme.value}));
|
||||
logging.warn(i18n.t('errors.middleware.themehandler.missingTheme', {theme: activeTheme.value}));
|
||||
|
||||
return next();
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
} else if (options.context && options.context.external) {
|
||||
return 0;
|
||||
} else {
|
||||
errors.logAndThrowError(new Error(i18n.t('errors.models.base.index.missingContext')));
|
||||
throw new errors.IncorrectUsage(i18n.t('errors.models.base.index.missingContext'));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
var config = require('../../config'),
|
||||
events = require(config.get('paths:corePath') + '/server/events'),
|
||||
models = require(config.get('paths:corePath') + '/server/models'),
|
||||
errors = require(config.get('paths:corePath') + '/server/errors'),
|
||||
logging = require(config.get('paths:corePath') + '/server/logging'),
|
||||
sequence = require(config.get('paths:corePath') + '/server/utils/sequence'),
|
||||
moment = require('moment-timezone');
|
||||
|
||||
@ -11,7 +11,7 @@ var config = require('../../config'),
|
||||
events.on('token.added', function (tokenModel) {
|
||||
models.User.edit({last_login: moment().toDate()}, {id: tokenModel.get('user_id')})
|
||||
.catch(function (err) {
|
||||
errors.logError(err);
|
||||
logging.error(err);
|
||||
});
|
||||
});
|
||||
|
||||
@ -61,11 +61,11 @@ events.on('settings.activeTimezone.edited', function (settingModel) {
|
||||
};
|
||||
})).each(function (result) {
|
||||
if (!result.isFulfilled()) {
|
||||
errors.logError(result.reason());
|
||||
logging.error(result.reason());
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(function (err) {
|
||||
errors.logError(err);
|
||||
logging.error(err);
|
||||
});
|
||||
});
|
||||
|
@ -25,8 +25,9 @@ filterUtils = {
|
||||
return _.isString(arg) ? gql.parse(arg) : arg;
|
||||
});
|
||||
} catch (error) {
|
||||
errors.logAndThrowError(
|
||||
new errors.ValidationError(error.message, 'filter'),
|
||||
throw new errors.ValidationError(
|
||||
error.message,
|
||||
'filter',
|
||||
i18n.t('errors.models.plugins.filter.errorParsing'),
|
||||
i18n.t('errors.models.plugins.filter.forInformationRead', {url: 'http://api.ghost.org/docs/filter'})
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ var _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
sequence = require('../utils/sequence'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
Showdown = require('showdown-ghost'),
|
||||
legacyConverter = new Showdown.converter({extensions: ['ghostgfm', 'footnotes', 'highlight']}),
|
||||
Mobiledoc = require('mobiledoc-html-renderer').default,
|
||||
@ -376,11 +377,12 @@ Post = ghostBookshelf.Model.extend({
|
||||
}).then(function () {
|
||||
// Don't do anything, the transaction processed ok
|
||||
}).catch(function failure(error) {
|
||||
errors.logError(
|
||||
error,
|
||||
logging.error(new errors.InternalServerError(
|
||||
error.message,
|
||||
i18n.t('errors.models.post.tagUpdates.error'),
|
||||
i18n.t('errors.models.post.tagUpdates.help')
|
||||
);
|
||||
));
|
||||
|
||||
return Promise.reject(new errors.InternalServerError(
|
||||
i18n.t('errors.models.post.tagUpdates.error') + ' ' + i18n.t('errors.models.post.tagUpdates.help') + error
|
||||
));
|
||||
@ -700,7 +702,7 @@ Post = ghostBookshelf.Model.extend({
|
||||
var newArgs = [foundPostModel].concat(origArgs);
|
||||
|
||||
return self.permissible.apply(self, newArgs);
|
||||
}, errors.logAndThrowError);
|
||||
});
|
||||
}
|
||||
|
||||
if (postModel) {
|
||||
|
@ -51,13 +51,14 @@ Role = ghostBookshelf.Model.extend({
|
||||
if (_.isNumber(roleModelOrId) || _.isString(roleModelOrId)) {
|
||||
// Grab the original args without the first one
|
||||
origArgs = _.toArray(arguments).slice(1);
|
||||
|
||||
// Get the actual role model
|
||||
return this.findOne({id: roleModelOrId, status: 'all'}).then(function then(foundRoleModel) {
|
||||
// Build up the original args but substitute with actual model
|
||||
var newArgs = [foundRoleModel].concat(origArgs);
|
||||
|
||||
return self.permissible.apply(self, newArgs);
|
||||
}, errors.logAndThrowError);
|
||||
});
|
||||
}
|
||||
|
||||
if (action === 'assign' && loadedPermissions.user) {
|
||||
|
@ -139,7 +139,7 @@ Settings = ghostBookshelf.Model.extend({
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.models.settings.unableToFindSetting', {key: item.key})));
|
||||
}, errors.logAndThrowError);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -9,6 +9,7 @@ var _ = require('lodash'),
|
||||
validator = require('validator'),
|
||||
validation = require('../data/validation'),
|
||||
events = require('../events'),
|
||||
logging = require('../logging'),
|
||||
i18n = require('../i18n'),
|
||||
|
||||
bcryptGenSalt = Promise.promisify(bcrypt.genSalt),
|
||||
@ -121,7 +122,7 @@ User = ghostBookshelf.Model.extend({
|
||||
} else if (this.get('id')) {
|
||||
return this.get('id');
|
||||
} else {
|
||||
errors.logAndThrowError(new errors.NotFoundError(i18n.t('errors.models.user.missingContext')));
|
||||
throw new errors.IncorrectUsage(i18n.t('errors.models.user.missingContext'));
|
||||
}
|
||||
},
|
||||
|
||||
@ -463,7 +464,7 @@ User = ghostBookshelf.Model.extend({
|
||||
var newArgs = [foundUserModel].concat(origArgs);
|
||||
|
||||
return self.permissible.apply(self, newArgs);
|
||||
}, errors.logAndThrowError);
|
||||
});
|
||||
}
|
||||
|
||||
if (action === 'edit') {
|
||||
@ -554,30 +555,28 @@ User = ghostBookshelf.Model.extend({
|
||||
return Promise.reject(new errors.UnauthorizedError(i18n.t('errors.models.user.incorrectPasswordAttempts', {remaining: remaining, s: s})));
|
||||
|
||||
// Use comma structure, not .catch, because we don't want to catch incorrect passwords
|
||||
}, function handleError(error) {
|
||||
}, function handleError(err) {
|
||||
// If we get a validation or other error during this save, catch it and log it, but don't
|
||||
// cause a login error because of it. The user validation is not important here.
|
||||
errors.logError(
|
||||
error,
|
||||
i18n.t('errors.models.user.userUpdateError.context'),
|
||||
i18n.t('errors.models.user.userUpdateError.help')
|
||||
);
|
||||
err.context = i18n.t('errors.models.user.userUpdateError.context');
|
||||
err.help = i18n.t('errors.models.user.userUpdateError.help');
|
||||
logging.error(err);
|
||||
|
||||
return Promise.reject(new errors.UnauthorizedError(i18n.t('errors.models.user.incorrectPassword')));
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(user.set({status: 'active', last_login: new Date()}).save({validate: false}))
|
||||
.catch(function handleError(error) {
|
||||
.catch(function handleError(err) {
|
||||
// If we get a validation or other error during this save, catch it and log it, but don't
|
||||
// cause a login error because of it. The user validation is not important here.
|
||||
errors.logError(
|
||||
error,
|
||||
i18n.t('errors.models.user.userUpdateError.context'),
|
||||
i18n.t('errors.models.user.userUpdateError.help')
|
||||
);
|
||||
err.context = i18n.t('errors.models.user.userUpdateError.context');
|
||||
err.help = i18n.t('errors.models.user.userUpdateError.help');
|
||||
logging.error(err);
|
||||
|
||||
return user;
|
||||
});
|
||||
}, errors.logAndThrowError);
|
||||
});
|
||||
}
|
||||
return Promise.reject(new errors.NoPermissionError(
|
||||
i18n.t('errors.models.user.accountLocked')));
|
||||
|
@ -38,7 +38,7 @@ effective = {
|
||||
});
|
||||
|
||||
return {permissions: allPerms, roles: user.roles};
|
||||
}, errors.logAndThrowError);
|
||||
});
|
||||
},
|
||||
|
||||
app: function (appName) {
|
||||
@ -49,7 +49,7 @@ effective = {
|
||||
}
|
||||
|
||||
return {permissions: foundApp.related('permissions').models};
|
||||
}, errors.logAndThrowError);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -56,14 +56,14 @@ function parseContext(context) {
|
||||
}
|
||||
|
||||
function applyStatusRules(docName, method, opts) {
|
||||
var errorMsg = i18n.t('errors.permissions.applyStatusRules.error', {docName: docName});
|
||||
var err = new errors.NoPermissionError(i18n.t('errors.permissions.applyStatusRules.error', {docName: docName}));
|
||||
|
||||
// Enforce status 'active' for users
|
||||
if (docName === 'users') {
|
||||
if (!opts.status) {
|
||||
return 'active';
|
||||
} else if (opts.status !== 'active') {
|
||||
throw errorMsg;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ function applyStatusRules(docName, method, opts) {
|
||||
return opts.status;
|
||||
} else if (opts.status !== 'published') {
|
||||
// any other parameter would make this a permissions error
|
||||
throw errorMsg;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ var util = require('util'),
|
||||
moment = require('moment'),
|
||||
request = require('superagent'),
|
||||
SchedulingBase = require(__dirname + '/SchedulingBase'),
|
||||
errors = require(__dirname + '/../errors');
|
||||
logging = require(__dirname + '/../logging');
|
||||
|
||||
/**
|
||||
* allJobs is a sorted list by time attribute
|
||||
@ -212,7 +212,7 @@ SchedulingDefault.prototype._pingUrl = function (object) {
|
||||
}, self.retryTimeoutInMs);
|
||||
}
|
||||
|
||||
errors.logError(err);
|
||||
logging.error(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -7,8 +7,9 @@ var serveStatic = require('express').static,
|
||||
path = require('path'),
|
||||
util = require('util'),
|
||||
Promise = require('bluebird'),
|
||||
errors = require('../errors'),
|
||||
config = require('../config'),
|
||||
errors = require('../errors'),
|
||||
i18n = require('../i18n'),
|
||||
utils = require('../utils'),
|
||||
BaseStore = require('./base'),
|
||||
remove = Promise.promisify(fs.remove);
|
||||
@ -44,7 +45,6 @@ LocalFileStore.prototype.save = function (image, targetDir) {
|
||||
|
||||
return fullUrl;
|
||||
}).catch(function (e) {
|
||||
errors.logError(e);
|
||||
return Promise.reject(e);
|
||||
});
|
||||
};
|
||||
@ -100,7 +100,20 @@ LocalFileStore.prototype.serve = function (options) {
|
||||
// CASE: serve images
|
||||
// For some reason send divides the max age number by 1000
|
||||
// Fallthrough: false ensures that if an image isn't found, it automatically 404s
|
||||
return serveStatic(config.getContentPath('images'), {maxAge: utils.ONE_YEAR_MS, fallthrough: false});
|
||||
// Wrap server static errors
|
||||
return function serveStaticContent(req, res, next) {
|
||||
return serveStatic(config.getContentPath('images'), {maxAge: utils.ONE_YEAR_MS, fallthrough: false})(req, res, function (err) {
|
||||
if (err) {
|
||||
if (err.statusCode === 404) {
|
||||
return next(new errors.NotFoundError(i18n.t('errors.errors.pageNotFound')));
|
||||
}
|
||||
|
||||
return next(err);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -28,10 +28,9 @@ var crypto = require('crypto'),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash'),
|
||||
url = require('url'),
|
||||
|
||||
api = require('./api'),
|
||||
config = require('./config'),
|
||||
errors = require('./errors'),
|
||||
logging = require('./logging'),
|
||||
i18n = require('./i18n'),
|
||||
internal = {context: {internal: true}},
|
||||
allowedCheckEnvironments = ['development', 'production'],
|
||||
@ -44,11 +43,9 @@ function updateCheckError(error) {
|
||||
internal
|
||||
);
|
||||
|
||||
errors.logError(
|
||||
error,
|
||||
i18n.t('errors.update-check.checkingForUpdatesFailed.error'),
|
||||
i18n.t('errors.update-check.checkingForUpdatesFailed.help', {url: 'http://support.ghost.org'})
|
||||
);
|
||||
error.context = i18n.t('errors.update-check.checkingForUpdatesFailed.error');
|
||||
error.help = i18n.t('errors.update-check.checkingForUpdatesFailed.help', {url: 'http://support.ghost.org'});
|
||||
logging.error(error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,6 +58,7 @@ describe('Authentication API', function () {
|
||||
it('can\'t authenticate unknown user', function (done) {
|
||||
request.post(testUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: 'invalid@email.com',
|
||||
@ -81,6 +82,7 @@ describe('Authentication API', function () {
|
||||
it('can\'t authenticate invalid password user', function (done) {
|
||||
request.post(testUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: user.email,
|
||||
@ -145,6 +147,7 @@ describe('Authentication API', function () {
|
||||
it('can\'t request new access token with invalid refresh token', function (done) {
|
||||
request.post(testUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: 'invalid',
|
||||
|
@ -66,6 +66,7 @@ describe('DB API', function () {
|
||||
it('import should fail without file', function (done) {
|
||||
request.post(testUtils.API.getApiQuery('db/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(403)
|
||||
.end(function (err) {
|
||||
|
@ -30,6 +30,7 @@ describe('Unauthorized', function () {
|
||||
describe('Unauthorized API', function () {
|
||||
it('can\'t retrieve posts', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/'))
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(401)
|
||||
.end(function firstRequest(err, res) {
|
||||
|
@ -325,6 +325,7 @@ describe('Post API', function () {
|
||||
it('can\'t retrieve non existent post', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/99/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
@ -345,6 +346,7 @@ describe('Post API', function () {
|
||||
it('can\'t retrieve a draft post', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/5/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
@ -365,6 +367,7 @@ describe('Post API', function () {
|
||||
it('can\'t retrieve a draft page', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/8/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
@ -929,6 +932,7 @@ describe('Post API', function () {
|
||||
it('can\'t delete a non existent post', function (done) {
|
||||
request.del(testUtils.API.getApiQuery('posts/99/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
|
@ -160,6 +160,7 @@ describe('Public API', function () {
|
||||
it('denies access with invalid client_secret', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=invalid_secret'))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(401)
|
||||
@ -180,6 +181,7 @@ describe('Public API', function () {
|
||||
it('denies access with invalid client_id', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/?client_id=invalid-id&client_secret=not_available'))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(401)
|
||||
@ -200,6 +202,7 @@ describe('Public API', function () {
|
||||
it('does not send CORS headers on an invalid origin', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=not_available'))
|
||||
.set('Origin', 'http://invalid-origin')
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
@ -218,6 +221,7 @@ describe('Public API', function () {
|
||||
it('denies access to settings endpoint', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('settings/?client_id=ghost-admin&client_secret=not_available'))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(403)
|
||||
@ -238,6 +242,7 @@ describe('Public API', function () {
|
||||
it('throws version mismatch error when request includes a version', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=not_available'))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-Ghost-Version', '0.3')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
|
@ -76,6 +76,7 @@ describe('Settings API', function () {
|
||||
it('can\'t retrieve non existent setting', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('settings/testsetting/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
@ -138,6 +139,7 @@ describe('Settings API', function () {
|
||||
it('can\'t edit settings with invalid accesstoken', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('settings/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
@ -168,6 +170,7 @@ describe('Settings API', function () {
|
||||
it('can\'t edit non existent setting', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('settings/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
|
@ -123,6 +123,7 @@ describe('Slug API', function () {
|
||||
it('should not be able to get a slug for an unknown type', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('slugs/unknown/who knows/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(400)
|
||||
|
@ -89,6 +89,7 @@ describe('Upload API', function () {
|
||||
it('import should fail without file', function (done) {
|
||||
request.post(testUtils.API.getApiQuery('uploads'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(403)
|
||||
.end(function (err) {
|
||||
|
@ -276,6 +276,7 @@ describe('User API', function () {
|
||||
it('can\'t retrieve non existent user by id', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('users/99/'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
@ -296,6 +297,7 @@ describe('User API', function () {
|
||||
it('can\'t retrieve non existent user by slug', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('users/slug/blargh/'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
|
@ -139,6 +139,7 @@ describe('Frontend Routing', function () {
|
||||
|
||||
it('should 404 for unknown frontend route', function (done) {
|
||||
request.get('/spectacular/marvellous/')
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
|
@ -1,8 +1,6 @@
|
||||
var testUtils = require('../../utils'),
|
||||
should = require('should'),
|
||||
_ = require('lodash'),
|
||||
|
||||
// Stuff we are testing
|
||||
RoleAPI = require('../../../server/api/roles'),
|
||||
context = testUtils.context;
|
||||
|
||||
|
@ -2,10 +2,9 @@ var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
_ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
|
||||
permissions = require('../../server/permissions'),
|
||||
errors = require('../../server/errors'),
|
||||
apiUtils = require('../../server/api/utils'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('API Utils', function () {
|
||||
@ -485,7 +484,7 @@ describe('API Utils', function () {
|
||||
it('should throw a permissions error if permission is not granted', function (done) {
|
||||
var cTMethodStub = {
|
||||
test: {
|
||||
test: sandbox.stub().returns(Promise.reject())
|
||||
test: sandbox.stub().returns(Promise.reject(new errors.NoPermissionError()))
|
||||
}
|
||||
},
|
||||
cTStub = sandbox.stub(permissions, 'canThis').returns(cTMethodStub);
|
||||
@ -497,7 +496,7 @@ describe('API Utils', function () {
|
||||
cTMethodStub.test.test.calledOnce.should.eql(true);
|
||||
err.errorType.should.eql('NoPermissionError');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ var sinon = require('sinon'),
|
||||
rewire = require('rewire'),
|
||||
errors = require('../../../server/errors'),
|
||||
auth = rewire('../../../server/auth'),
|
||||
logging = require('../../../server/logging'),
|
||||
BearerStrategy = require('passport-http-bearer').Strategy,
|
||||
ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy,
|
||||
user = {id: 1},
|
||||
@ -84,13 +85,13 @@ function registerFaultyClientPasswordStrategy() {
|
||||
}
|
||||
|
||||
describe('Auth', function () {
|
||||
var res, req, next, errorStub;
|
||||
var res, req, next, loggingStub;
|
||||
|
||||
beforeEach(function () {
|
||||
req = {};
|
||||
res = {};
|
||||
next = sandbox.spy();
|
||||
errorStub = sandbox.stub(errors, 'logError');
|
||||
loggingStub = sandbox.stub(logging, 'error');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
@ -110,18 +111,13 @@ describe('Auth', function () {
|
||||
req.user = false;
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(403);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('NoPermissionError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(403);
|
||||
(err instanceof errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
auth.authorize.requiresAuthorizedUser(req, res, next);
|
||||
next.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
|
||||
describe('User Authentication', function () {
|
||||
@ -154,40 +150,28 @@ describe('Auth', function () {
|
||||
req.headers.authorization = 'Bearer ' + token;
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(401);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('UnauthorizedError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(401);
|
||||
(err instanceof errors.UnauthorizedError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
registerUnsuccessfulBearerStrategy();
|
||||
auth.authenticate.authenticateUser(req, res, next);
|
||||
|
||||
next.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
|
||||
it('shouldn\'t authenticate without bearer token', function (done) {
|
||||
req.headers = {};
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(401);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('UnauthorizedError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(401);
|
||||
(err instanceof errors.UnauthorizedError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
registerUnsuccessfulBearerStrategy();
|
||||
auth.authenticate.authenticateUser(req, res, next);
|
||||
|
||||
next.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
|
||||
it('shouldn\'t authenticate with bearer token and client', function (done) {
|
||||
@ -196,20 +180,14 @@ describe('Auth', function () {
|
||||
req.client = {id: 1};
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(401);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('UnauthorizedError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(401);
|
||||
(err instanceof errors.UnauthorizedError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
registerUnsuccessfulBearerStrategy();
|
||||
auth.authenticate.authenticateUser(req, res, next);
|
||||
|
||||
next.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
|
||||
it('shouldn\'t authenticate when error', function (done) {
|
||||
@ -242,36 +220,26 @@ describe('Auth', function () {
|
||||
req.headers.authorization = 'Bearer';
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(401);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('UnauthorizedError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(401);
|
||||
(err instanceof errors.UnauthorizedError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
auth.authenticate.authenticateClient(req, res, next);
|
||||
next.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
|
||||
it('shouldn\'t authenticate client without client_id/client_secret', function (done) {
|
||||
req.body = {};
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(401);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('UnauthorizedError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(401);
|
||||
(err instanceof errors.UnauthorizedError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
auth.authenticate.authenticateClient(req, res, next);
|
||||
next.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
|
||||
it('shouldn\'t authenticate client without client_id', function (done) {
|
||||
@ -279,18 +247,13 @@ describe('Auth', function () {
|
||||
req.body.client_secret = testSecret;
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(401);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('UnauthorizedError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(401);
|
||||
(err instanceof errors.UnauthorizedError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
auth.authenticate.authenticateClient(req, res, next);
|
||||
next.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
|
||||
it('shouldn\'t authenticate client without client_secret', function (done) {
|
||||
@ -298,18 +261,13 @@ describe('Auth', function () {
|
||||
req.body.client_id = testClient;
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(401);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('UnauthorizedError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(401);
|
||||
(err instanceof errors.UnauthorizedError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
auth.authenticate.authenticateClient(req, res, next);
|
||||
next.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
|
||||
it('shouldn\'t authenticate without full client credentials', function (done) {
|
||||
@ -317,22 +275,14 @@ describe('Auth', function () {
|
||||
req.body.client_id = testClient;
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(401);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('UnauthorizedError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(401);
|
||||
(err instanceof errors.UnauthorizedError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
registerUnsuccessfulClientPasswordStrategy();
|
||||
auth.authenticate.authenticateClient(req, res, next);
|
||||
next.called.should.be.false();
|
||||
errorStub.calledTwice.should.be.true();
|
||||
errorStub.getCall(0).args[1].should.eql('Client credentials were not provided');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('shouldn\'t authenticate invalid/unknown client', function (done) {
|
||||
@ -341,22 +291,14 @@ describe('Auth', function () {
|
||||
req.body.client_secret = testSecret;
|
||||
res.status = {};
|
||||
|
||||
sandbox.stub(res, 'status', function (statusCode) {
|
||||
statusCode.should.eql(401);
|
||||
return {
|
||||
json: function (err) {
|
||||
err.errors[0].errorType.should.eql('UnauthorizedError');
|
||||
}
|
||||
};
|
||||
});
|
||||
var next = function next(err) {
|
||||
err.statusCode.should.eql(401);
|
||||
(err instanceof errors.UnauthorizedError).should.eql(true);
|
||||
done();
|
||||
};
|
||||
|
||||
registerUnsuccessfulClientPasswordStrategy();
|
||||
auth.authenticate.authenticateClient(req, res, next);
|
||||
next.called.should.be.false();
|
||||
errorStub.calledTwice.should.be.true();
|
||||
errorStub.getCall(0).args[1].should.eql('Client credentials were not valid');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should authenticate valid/known client', function (done) {
|
||||
|
@ -1,499 +0,0 @@
|
||||
var should = require('should'),
|
||||
Promise = require('bluebird'),
|
||||
sinon = require('sinon'),
|
||||
express = require('express'),
|
||||
rewire = require('rewire'),
|
||||
|
||||
// Stuff we are testing
|
||||
chalk = require('chalk'),
|
||||
errors = rewire('../../server/errors'),
|
||||
configUtils = require('../utils/configUtils'),
|
||||
|
||||
// storing current environment
|
||||
currentEnv = process.env.NODE_ENV;
|
||||
|
||||
describe('Error handling', function () {
|
||||
// Just getting rid of jslint unused error
|
||||
should.exist(errors);
|
||||
|
||||
describe('Throwing', function () {
|
||||
it('throws error objects', function () {
|
||||
var toThrow = new Error('test1'),
|
||||
runThrowError = function () {
|
||||
errors.throwError(toThrow);
|
||||
};
|
||||
|
||||
runThrowError.should.throw('test1');
|
||||
});
|
||||
|
||||
it('throws error strings', function () {
|
||||
var toThrow = 'test2',
|
||||
runThrowError = function () {
|
||||
errors.throwError(toThrow);
|
||||
};
|
||||
|
||||
runThrowError.should.throw('test2');
|
||||
});
|
||||
|
||||
it('throws error even if nothing passed', function () {
|
||||
var runThrowError = function () {
|
||||
errors.throwError();
|
||||
};
|
||||
|
||||
runThrowError.should.throw('An error occurred');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Warn Logging', function () {
|
||||
var logStub,
|
||||
// Can't use afterEach here, because mocha uses console.log to output the checkboxes
|
||||
// which we've just stubbed, so we need to restore it before the test ends to see ticks.
|
||||
resetEnvironment = function () {
|
||||
logStub.restore();
|
||||
process.env.NODE_ENV = currentEnv;
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
logStub = sinon.stub(console, 'log');
|
||||
process.env.NODE_ENV = 'development';
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
logStub.restore();
|
||||
});
|
||||
|
||||
it('logs default warn with no message supplied', function () {
|
||||
errors.logWarn();
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(
|
||||
chalk.yellow('\nWarning: no message supplied'), '\n');
|
||||
|
||||
// Future tests: This is important here!
|
||||
resetEnvironment();
|
||||
});
|
||||
|
||||
it('logs warn with only message', function () {
|
||||
var errorText = 'Error1';
|
||||
|
||||
errors.logWarn(errorText);
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(chalk.yellow('\nWarning: ' + errorText), '\n');
|
||||
|
||||
// Future tests: This is important here!
|
||||
resetEnvironment();
|
||||
});
|
||||
|
||||
it('logs warn with message and context', function () {
|
||||
var errorText = 'Error1',
|
||||
contextText = 'Context1';
|
||||
|
||||
errors.logWarn(errorText, contextText);
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(
|
||||
chalk.yellow('\nWarning: ' + errorText), '\n', chalk.white(contextText), '\n'
|
||||
);
|
||||
|
||||
// Future tests: This is important here!
|
||||
resetEnvironment();
|
||||
});
|
||||
|
||||
it('logs warn with message and context and help', function () {
|
||||
var errorText = 'Error1',
|
||||
contextText = 'Context1',
|
||||
helpText = 'Help1';
|
||||
|
||||
errors.logWarn(errorText, contextText, helpText);
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(
|
||||
chalk.yellow('\nWarning: ' + errorText), '\n', chalk.white(contextText), '\n', chalk.green(helpText), '\n'
|
||||
);
|
||||
|
||||
// Future tests: This is important here!
|
||||
resetEnvironment();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Logging', function () {
|
||||
var logStub;
|
||||
|
||||
beforeEach(function () {
|
||||
logStub = sinon.stub(console, 'error');
|
||||
// give environment a value that will console log
|
||||
process.env.NODE_ENV = 'development';
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
logStub.restore();
|
||||
// reset the environment
|
||||
process.env.NODE_ENV = currentEnv;
|
||||
});
|
||||
|
||||
it('logs errors from error objects', function () {
|
||||
var err = new Error('test1');
|
||||
|
||||
errors.logError(err);
|
||||
|
||||
// Calls log with message on Error objects
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(chalk.red('\nERROR:', err.message), '\n', '\n', err.stack, '\n').should.be.true();
|
||||
});
|
||||
|
||||
it('logs errors from strings', function () {
|
||||
var err = 'test2';
|
||||
|
||||
errors.logError(err);
|
||||
|
||||
// Calls log with string on strings
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(chalk.red('\nERROR:', err), '\n').should.be.true();
|
||||
});
|
||||
|
||||
it('logs errors from an error object and two string arguments', function () {
|
||||
var err = new Error('test1'),
|
||||
message = 'Testing';
|
||||
|
||||
errors.logError(err, message, message);
|
||||
|
||||
// Calls log with message on Error objects
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(
|
||||
chalk.red('\nERROR:', err.message), '\n', chalk.white(message), '\n', chalk.green(message), '\n', err.stack, '\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('logs errors from three string arguments', function () {
|
||||
var message = 'Testing';
|
||||
|
||||
errors.logError(message, message, message);
|
||||
|
||||
// Calls log with message on Error objects
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(
|
||||
chalk.red('\nERROR:', message), '\n', chalk.white(message), '\n', chalk.green(message), '\n'
|
||||
).should.be.true();
|
||||
});
|
||||
|
||||
it('logs errors from an undefined error argument', function () {
|
||||
var message = 'Testing';
|
||||
|
||||
errors.logError(undefined, message, message);
|
||||
|
||||
// Calls log with message on Error objects
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(
|
||||
chalk.red('\nERROR:', 'An unknown error occurred.'), '\n', chalk.white(message), '\n', chalk.green(message), '\n'
|
||||
).should.be.true();
|
||||
});
|
||||
|
||||
it('logs errors from an undefined context argument', function () {
|
||||
var message = 'Testing';
|
||||
|
||||
errors.logError(message, undefined, message);
|
||||
|
||||
// Calls log with message on Error objects
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(chalk.red('\nERROR:', message), '\n', chalk.green(message), '\n').should.be.true();
|
||||
});
|
||||
|
||||
it('logs errors from an undefined help argument', function () {
|
||||
var message = 'Testing';
|
||||
|
||||
errors.logError(message, message, undefined);
|
||||
|
||||
// Calls log with message on Error objects
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(chalk.red('\nERROR:', message), '\n', chalk.white(message), '\n').should.be.true();
|
||||
});
|
||||
|
||||
it('logs errors from a null error argument', function () {
|
||||
var message = 'Testing';
|
||||
|
||||
errors.logError(null, message, message);
|
||||
|
||||
// Calls log with message on Error objects
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.calledWith(
|
||||
chalk.red('\nERROR:', 'An unknown error occurred.'), '\n', chalk.white(message), '\n', chalk.green(message), '\n'
|
||||
).should.be.true();
|
||||
});
|
||||
|
||||
it('logs errors from a null context argument', function () {
|
||||
var message = 'Testing';
|
||||
|
||||
errors.logError(message, null, message);
|
||||
|
||||
// Calls log with message on Error objects
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.firstCall.calledWith(chalk.red('\nERROR:', message), '\n', chalk.green(message), '\n').should.be.true();
|
||||
});
|
||||
|
||||
it('logs errors from a null help argument', function () {
|
||||
var message = 'Testing';
|
||||
|
||||
errors.logError(message, message, null);
|
||||
|
||||
// Calls log with message on Error objects
|
||||
|
||||
logStub.calledOnce.should.be.true();
|
||||
logStub.firstCall.calledWith(chalk.red('\nERROR:', message), '\n', chalk.white(message), '\n').should.be.true();
|
||||
});
|
||||
|
||||
it('logs promise errors and redirects', function (done) {
|
||||
var req = null,
|
||||
res = {
|
||||
redirect: function () {
|
||||
return;
|
||||
}
|
||||
},
|
||||
redirectStub = sinon.stub(res, 'redirect');
|
||||
|
||||
// give environment a value that will console log
|
||||
Promise.reject().then(function () {
|
||||
throw new Error('Ran success handler');
|
||||
}, errors.logErrorWithRedirect('test1', null, null, '/testurl', req, res));
|
||||
|
||||
Promise.reject().catch(function () {
|
||||
logStub.calledWith(chalk.red('\nERROR:', 'test1')).should.equal(true);
|
||||
logStub.restore();
|
||||
|
||||
redirectStub.calledWith('/testurl').should.equal(true);
|
||||
redirectStub.restore();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('API Error Handlers', function () {
|
||||
var sandbox, req, res, next;
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
req = {};
|
||||
res = {};
|
||||
res.json = sandbox.spy();
|
||||
res.status = sandbox.stub().returns(res);
|
||||
next = sandbox.spy();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('handleAPIError: sends a JSON error response', function () {
|
||||
errors.logError = sandbox.spy(errors, 'logError');
|
||||
errors.formatHttpErrors = sandbox.spy(errors, 'formatHttpErrors');
|
||||
|
||||
var msg = 'Something got lost',
|
||||
err = new errors.NotFoundError(msg);
|
||||
|
||||
errors.handleAPIError(err, req, res, next);
|
||||
|
||||
next.called.should.be.false();
|
||||
errors.logError.calledOnce.should.be.true();
|
||||
errors.formatHttpErrors.calledOnce.should.be.true();
|
||||
|
||||
res.status.calledWith(404).should.be.true();
|
||||
res.json.calledOnce.should.be.true();
|
||||
res.json.firstCall.args[0].errors[0].message.should.eql(msg);
|
||||
res.json.firstCall.args[0].errors[0].errorType.should.eql('NotFoundError');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rendering', function () {
|
||||
var app,
|
||||
sandbox;
|
||||
|
||||
before(function () {
|
||||
configUtils.set({
|
||||
paths: {
|
||||
themePath: '/content/themes',
|
||||
availableThemes: {
|
||||
casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'page.hbs': '/content/themes/casper/page.hbs',
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs'
|
||||
},
|
||||
'theme-with-error': {
|
||||
'error.hbs': ''
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
errors.updateActiveTheme('casper');
|
||||
});
|
||||
|
||||
after(function () {
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
app = express();
|
||||
sandbox = sinon.sandbox.create();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('Renders end-of-middleware 404 errors correctly', function (done) {
|
||||
var req = {method: 'GET'},
|
||||
res = app.response;
|
||||
|
||||
sandbox.stub(res, 'render', function (view, options/*, fn*/) {
|
||||
view.should.match(/user-error\.hbs/);
|
||||
|
||||
// Test that the message is correct
|
||||
options.message.should.equal('Page not found');
|
||||
// Template variable
|
||||
options.code.should.equal(404);
|
||||
this.statusCode.should.equal(404);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
sandbox.stub(res, 'status', function (status) {
|
||||
this.statusCode = status;
|
||||
return this;
|
||||
});
|
||||
|
||||
sandbox.stub(res, 'set', function (value) {
|
||||
// Test that the headers are correct
|
||||
value['Cache-Control'].should.eql('no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
|
||||
return this;
|
||||
});
|
||||
|
||||
errors.error404(req, res, done);
|
||||
});
|
||||
|
||||
it('Renders thrown 404 errors correctly', function (done) {
|
||||
var err = new Error('A thing was not found'),
|
||||
req = {method: 'GET'},
|
||||
res = app.response;
|
||||
|
||||
sandbox.stub(res, 'render', function (view, options/*, fn*/) {
|
||||
view.should.match(/user-error\.hbs/);
|
||||
|
||||
// Test that the message is correct
|
||||
options.message.should.equal('Page not found');
|
||||
// Template variable
|
||||
options.code.should.equal(404);
|
||||
this.statusCode.should.equal(404);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
sandbox.stub(res, 'status', function (status) {
|
||||
this.statusCode = status;
|
||||
return this;
|
||||
});
|
||||
|
||||
sandbox.stub(res, 'set', function (value) {
|
||||
// Test that the headers are correct
|
||||
value['Cache-Control'].should.eql('no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
|
||||
return this;
|
||||
});
|
||||
|
||||
err.status = 404;
|
||||
errors.error500(err, req, res, null);
|
||||
});
|
||||
|
||||
it('Renders thrown errors correctly', function (done) {
|
||||
var err = new Error('I am a big bad error'),
|
||||
req = {method: 'GET'},
|
||||
res = app.response;
|
||||
|
||||
sandbox.stub(res, 'render', function (view, options/*, fn*/) {
|
||||
view.should.match(/user-error\.hbs/);
|
||||
|
||||
// Test that the message is correct
|
||||
options.message.should.equal('I am a big bad error');
|
||||
// Template variable
|
||||
options.code.should.equal(500);
|
||||
this.statusCode.should.equal(500);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
sandbox.stub(res, 'set', function (value) {
|
||||
// Test that the headers are correct
|
||||
value['Cache-Control'].should.eql('no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
|
||||
return this;
|
||||
});
|
||||
|
||||
sandbox.stub(res, 'status', function (status) {
|
||||
this.statusCode = status;
|
||||
return this;
|
||||
});
|
||||
|
||||
errors.error500(err, req, res, null);
|
||||
});
|
||||
|
||||
it('Renders 500 errors correctly', function (done) {
|
||||
var err = new Error('I am a big bad error'),
|
||||
req = {method: 'GET'},
|
||||
res = app.response;
|
||||
|
||||
sandbox.stub(res, 'render', function (view, options/*, fn*/) {
|
||||
view.should.match(/user-error\.hbs/);
|
||||
|
||||
// Test that the message is correct
|
||||
options.message.should.equal('I am a big bad error');
|
||||
// Template variable
|
||||
options.code.should.equal(500);
|
||||
this.statusCode.should.equal(500);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
sandbox.stub(res, 'status', function (status) {
|
||||
this.statusCode = status;
|
||||
return this;
|
||||
});
|
||||
|
||||
sandbox.stub(res, 'set', function (value) {
|
||||
// Test that the headers are correct
|
||||
value['Cache-Control'].should.eql('no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
|
||||
return this;
|
||||
});
|
||||
|
||||
err.statusCode = 500;
|
||||
errors.error500(err, req, res, null);
|
||||
});
|
||||
|
||||
it('Renders custom error template if one exists', function (done) {
|
||||
var statusCode = 404,
|
||||
error = {message: 'Custom view test'},
|
||||
req = {
|
||||
session: null
|
||||
},
|
||||
res = {
|
||||
status: function (statusCode) {
|
||||
/*jshint unused:false*/
|
||||
return this;
|
||||
},
|
||||
render: function (view, model, fn) {
|
||||
/*jshint unused:false*/
|
||||
view.should.eql('error');
|
||||
errors.updateActiveTheme('casper');
|
||||
done();
|
||||
}
|
||||
},
|
||||
next = null;
|
||||
errors.updateActiveTheme('theme-with-error');
|
||||
errors.renderErrorPage(statusCode, error, req, res, next);
|
||||
});
|
||||
});
|
||||
});
|
@ -82,15 +82,15 @@ describe('Exporter', function () {
|
||||
|
||||
it('should catch and log any errors', function (done) {
|
||||
// Setup for failure
|
||||
var errorStub = sandbox.stub(errors, 'logAndThrowError');
|
||||
queryMock.select.returns(new Promise.reject({}));
|
||||
|
||||
// Execute
|
||||
exporter.doExport().then(function (exportData) {
|
||||
should.not.exist(exportData);
|
||||
errorStub.calledOnce.should.be.true();
|
||||
exporter.doExport().then(function () {
|
||||
done(new Error('expected error on export data'));
|
||||
}).catch(function (err) {
|
||||
(err instanceof errors.InternalServerError).should.eql(true);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -2,14 +2,11 @@ var sinon = require('sinon'),
|
||||
should = require('should'),
|
||||
express = require('express'),
|
||||
Promise = require('bluebird'),
|
||||
|
||||
// Stuff we test
|
||||
fs = require('fs'),
|
||||
hbs = require('express-hbs'),
|
||||
themeHandler = require('../../../server/middleware/theme-handler'),
|
||||
errors = require('../../../server/errors'),
|
||||
logging = require('../../../server/logging'),
|
||||
api = require('../../../server/api'),
|
||||
|
||||
configUtils = require('../../utils/configUtils'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
@ -45,15 +42,13 @@ describe('Theme Handler', function () {
|
||||
|
||||
describe('activateTheme', function () {
|
||||
it('should activate new theme with partials', function () {
|
||||
var errorStub = sandbox.stub(errors, 'updateActiveTheme'),
|
||||
fsStub = sandbox.stub(fs, 'stat', function (path, cb) {
|
||||
var fsStub = sandbox.stub(fs, 'stat', function (path, cb) {
|
||||
cb(null, {isDirectory: function () { return true; }});
|
||||
}),
|
||||
hbsStub = sandbox.spy(hbs, 'express3');
|
||||
|
||||
themeHandler.activateTheme(blogApp, 'casper');
|
||||
|
||||
errorStub.calledWith('casper').should.be.true();
|
||||
fsStub.calledOnce.should.be.true();
|
||||
hbsStub.calledOnce.should.be.true();
|
||||
hbsStub.firstCall.args[0].should.be.an.Object().and.have.property('partialsDir');
|
||||
@ -62,15 +57,13 @@ describe('Theme Handler', function () {
|
||||
});
|
||||
|
||||
it('should activate new theme without partials', function () {
|
||||
var errorStub = sandbox.stub(errors, 'updateActiveTheme'),
|
||||
fsStub = sandbox.stub(fs, 'stat', function (path, cb) {
|
||||
var fsStub = sandbox.stub(fs, 'stat', function (path, cb) {
|
||||
cb(null, null);
|
||||
}),
|
||||
hbsStub = sandbox.spy(hbs, 'express3');
|
||||
|
||||
themeHandler.activateTheme(blogApp, 'casper');
|
||||
|
||||
errorStub.calledWith('casper').should.be.true();
|
||||
fsStub.calledOnce.should.be.true();
|
||||
hbsStub.calledOnce.should.be.true();
|
||||
hbsStub.firstCall.args[0].should.be.an.Object().and.have.property('partialsDir');
|
||||
@ -162,8 +155,7 @@ describe('Theme Handler', function () {
|
||||
});
|
||||
|
||||
it('throws error if theme is missing', function (done) {
|
||||
var errorSpy = sandbox.spy(errors, 'throwError'),
|
||||
activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme');
|
||||
var activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme');
|
||||
|
||||
sandbox.stub(api.settings, 'read').withArgs(sandbox.match.has('key', 'activeTheme')).returns(Promise.resolve({
|
||||
settings: [{
|
||||
@ -171,12 +163,12 @@ describe('Theme Handler', function () {
|
||||
value: 'rasper'
|
||||
}]
|
||||
}));
|
||||
|
||||
blogApp.set('activeTheme', 'not-casper');
|
||||
configUtils.set({paths: {availableThemes: {casper: {}}}});
|
||||
|
||||
themeHandler.updateActiveTheme(req, res, function (err) {
|
||||
should.exist(err);
|
||||
errorSpy.called.should.be.true();
|
||||
activateThemeSpy.called.should.be.false();
|
||||
err.message.should.eql('The currently active theme "rasper" is missing.');
|
||||
done();
|
||||
@ -184,8 +176,7 @@ describe('Theme Handler', function () {
|
||||
});
|
||||
|
||||
it('throws only warns if theme is missing for admin req', function (done) {
|
||||
var errorSpy = sandbox.spy(errors, 'throwError'),
|
||||
warnSpy = sandbox.spy(errors, 'logWarn'),
|
||||
var warnSpy = sandbox.spy(logging, 'warn'),
|
||||
activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme');
|
||||
|
||||
sandbox.stub(api.settings, 'read').withArgs(sandbox.match.has('key', 'activeTheme')).returns(Promise.resolve({
|
||||
@ -199,7 +190,6 @@ describe('Theme Handler', function () {
|
||||
configUtils.set({paths: {availableThemes: {casper: {}}}});
|
||||
|
||||
themeHandler.updateActiveTheme(req, res, function () {
|
||||
errorSpy.called.should.be.false();
|
||||
activateThemeSpy.called.should.be.false();
|
||||
warnSpy.called.should.be.true();
|
||||
warnSpy.calledWith('The currently active theme "rasper" is missing.').should.be.true();
|
||||
|
@ -3,13 +3,9 @@ var testUtils = require('../utils'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash'),
|
||||
|
||||
// Stuff we are testing
|
||||
Models = require('../../server/models'),
|
||||
errors = require('../../server/errors'),
|
||||
permissions = require('../../server/permissions'),
|
||||
// effectivePerms = require('../../server/permissions/effective'),
|
||||
// context = testUtils.context.owner,
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Permissions', function () {
|
||||
@ -174,7 +170,7 @@ describe('Permissions', function () {
|
||||
permissions.applyPublicRules('posts', 'read', _.cloneDeep(draft)).then(function () {
|
||||
done('Did not throw an error for draft');
|
||||
}).catch(function (err) {
|
||||
err.should.be.a.String();
|
||||
(err instanceof errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -185,7 +181,7 @@ describe('Permissions', function () {
|
||||
permissions.applyPublicRules('posts', 'browse', _.cloneDeep(draft)).then(function () {
|
||||
done('Did not throw an error for draft');
|
||||
}).catch(function (err) {
|
||||
err.should.be.a.String();
|
||||
(err instanceof errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -214,7 +210,7 @@ describe('Permissions', function () {
|
||||
permissions.applyPublicRules('posts', 'browse', _.cloneDeep(draft)).then(function () {
|
||||
done('Did not throw an error for draft');
|
||||
}).catch(function (err) {
|
||||
err.should.be.a.String();
|
||||
(err instanceof errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -225,7 +221,7 @@ describe('Permissions', function () {
|
||||
permissions.applyPublicRules('posts', 'browse', _.cloneDeep(draft)).then(function () {
|
||||
done('Did not throw an error for draft');
|
||||
}).catch(function (err) {
|
||||
err.should.be.a.String();
|
||||
(err instanceof errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -236,13 +232,14 @@ describe('Permissions', function () {
|
||||
permissions.applyPublicRules('posts', 'read', _.cloneDeep(draft)).then(function () {
|
||||
done('Did not throw an error for draft');
|
||||
}).catch(function (err) {
|
||||
err.should.be.a.String();
|
||||
(err instanceof errors.NoPermissionError).should.eql(true);
|
||||
|
||||
draft = {context: {}, data: {status: 'draft', uuid: '1234-abcd', slug: 'abcd'}};
|
||||
|
||||
return permissions.applyPublicRules('posts', 'read', _.cloneDeep(draft)).then(function () {
|
||||
done('Did not throw an error for draft');
|
||||
}).catch(function (err) {
|
||||
err.should.be.a.String();
|
||||
(err instanceof errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -275,7 +272,7 @@ describe('Permissions', function () {
|
||||
permissions.applyPublicRules('users', 'browse', _.cloneDeep(inactive)).then(function () {
|
||||
done('Did not throw an error for inactive');
|
||||
}).catch(function (err) {
|
||||
err.should.be.a.String();
|
||||
(err instanceof errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -2,11 +2,9 @@ var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers'),
|
||||
errors = require('../../../server/errors');
|
||||
logging = require('../../../server/logging');
|
||||
|
||||
describe('{{#is}} helper', function () {
|
||||
before(function () {
|
||||
@ -63,7 +61,7 @@ describe('{{#is}} helper', function () {
|
||||
it('should log warning with no args', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy(),
|
||||
logWarn = sinon.stub(errors, 'logWarn');
|
||||
logWarn = sinon.stub(logging, 'warn');
|
||||
|
||||
helpers.is.call(
|
||||
{},
|
||||
|
@ -108,12 +108,12 @@ describe('XMLRPC', function () {
|
||||
var ping1 = nock('http://blogsearch.google.com').post('/ping/RPC2').reply(200),
|
||||
ping2 = nock('http://rpc.pingomatic.com').post('/').replyWithError('ping site is down'),
|
||||
testPost = _.clone(testUtils.DataGenerator.Content.posts[2]),
|
||||
errorMock, resetXmlRpc;
|
||||
loggingMock, resetXmlRpc;
|
||||
|
||||
errorMock = {
|
||||
logError: function logError(error) {
|
||||
should.exist(error);
|
||||
error.message.should.eql('ping site is down');
|
||||
loggingMock = {
|
||||
error: function onError(err) {
|
||||
should.exist(err);
|
||||
err.message.should.eql('ping site is down');
|
||||
|
||||
// Reset xmlrpc handleError method and exit test
|
||||
resetXmlRpc();
|
||||
@ -121,7 +121,7 @@ describe('XMLRPC', function () {
|
||||
}
|
||||
};
|
||||
|
||||
resetXmlRpc = xmlrpc.__set__('errors', errorMock);
|
||||
resetXmlRpc = xmlrpc.__set__('logging', loggingMock);
|
||||
|
||||
ping(testPost);
|
||||
|
||||
|
@ -55,7 +55,11 @@ function forkGhost(newConfig, envName) {
|
||||
newConfig.url = url.format(_.extend({}, url.parse(config.get('url')), {port: newConfig.server.port, host: null}));
|
||||
}
|
||||
|
||||
newConfig.logging = false;
|
||||
newConfig.logging = {
|
||||
level: 'fatal',
|
||||
transports: ['stdout'],
|
||||
rotation: false
|
||||
};
|
||||
|
||||
var newConfigFile = path.join(config.get('paths').appRoot, 'config.test.' + envName + '.json');
|
||||
|
||||
|
5
index.js
5
index.js
@ -3,7 +3,7 @@
|
||||
var ghost = require('./core'),
|
||||
debug = require('debug')('ghost:boot:index'),
|
||||
express = require('express'),
|
||||
errors = require('./core/server/errors'),
|
||||
logging = require('./core/server/logging'),
|
||||
utils = require('./core/server/utils'),
|
||||
parentApp = express();
|
||||
|
||||
@ -16,5 +16,6 @@ ghost().then(function (ghostServer) {
|
||||
// Let Ghost handle starting our server instance.
|
||||
ghostServer.start(parentApp);
|
||||
}).catch(function (err) {
|
||||
errors.logErrorAndExit(err, err.context, err.help);
|
||||
logging.error(err);
|
||||
process.exit(0);
|
||||
});
|
||||
|
@ -31,6 +31,7 @@
|
||||
"bluebird": "3.4.6",
|
||||
"body-parser": "1.15.2",
|
||||
"bookshelf": "0.10.1",
|
||||
"bunyan": "1.8.1",
|
||||
"chalk": "1.1.3",
|
||||
"cheerio": "0.22.0",
|
||||
"compression": "1.6.2",
|
||||
@ -57,7 +58,6 @@
|
||||
"mobiledoc-html-renderer": "0.3.0",
|
||||
"moment": "2.15.1",
|
||||
"moment-timezone": "0.5.5",
|
||||
"morgan": "1.7.0",
|
||||
"multer": "1.2.0",
|
||||
"nconf": "0.8.4",
|
||||
"netjet": "1.1.3",
|
||||
@ -69,6 +69,7 @@
|
||||
"passport-http-bearer": "1.0.1",
|
||||
"passport-oauth2-client-password": "0.1.2",
|
||||
"path-match": "1.2.4",
|
||||
"prettyjson": "1.1.3",
|
||||
"rss": "1.2.1",
|
||||
"sanitize-html": "1.13.0",
|
||||
"semver": "5.3.0",
|
||||
|
Loading…
Reference in New Issue
Block a user