mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-18 02:21:47 -05:00
✨ Error creation (#7477)
refs #7116, refs #2001 - Changes the way Ghost errors are implemented to benefit from proper inheritance - Moves all error definitions into a single file - Changes the error constructor to take an options object, rather than needing the arguments to be passed in the correct order. - Provides a wrapper so that any errors that haven't already been converted to GhostErrors get converted before they are displayed. Summary of changes: * 🐛 set NODE_ENV in config handler * ✨ add GhostError implementation (core/server/errors.js) - register all errors in one file - inheritance from GhostError - option pattern * 🔥 remove all error files * ✨ wrap all errors into GhostError in case of HTTP * 🎨 adaptions - option pattern for errors - use GhostError when needed * 🎨 revert debug deletion and add TODO for error id's
This commit is contained in:
parent
32700a0e5a
commit
d81bc91bd2
108 changed files with 766 additions and 810 deletions
|
@ -8,8 +8,8 @@ var _ = require('lodash'),
|
|||
globalUtils = require('../utils'),
|
||||
utils = require('./utils'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
models = require('../models'),
|
||||
logging = require('../logging'),
|
||||
events = require('../events'),
|
||||
config = require('../config'),
|
||||
i18n = require('../i18n'),
|
||||
|
@ -43,7 +43,7 @@ function assertSetupCompleted(status) {
|
|||
notCompleted = i18n.t('errors.api.authentication.setupMustBeCompleted');
|
||||
|
||||
function throwReason(reason) {
|
||||
throw new errors.NoPermissionError(reason);
|
||||
throw new errors.NoPermissionError({message: reason});
|
||||
}
|
||||
|
||||
if (isSetup) {
|
||||
|
@ -78,9 +78,9 @@ function setupTasks(setupData) {
|
|||
|
||||
return User.findOne({role: 'Owner', status: 'all'}).then(function then(owner) {
|
||||
if (!owner) {
|
||||
throw new errors.InternalServerError(
|
||||
i18n.t('errors.api.authentication.setupUnableToRun')
|
||||
);
|
||||
throw new errors.GhostError({
|
||||
message: i18n.t('errors.api.authentication.setupUnableToRun')
|
||||
});
|
||||
}
|
||||
|
||||
return User.setup(userData, _.extend({id: owner.id}, context));
|
||||
|
@ -175,9 +175,9 @@ authentication = {
|
|||
var email = data.passwordreset[0].email;
|
||||
|
||||
if (typeof email !== 'string' || !validator.isEmail(email)) {
|
||||
throw new errors.BadRequestError(
|
||||
i18n.t('errors.api.authentication.noEmailProvided')
|
||||
);
|
||||
throw new errors.BadRequestError({
|
||||
message: i18n.t('errors.api.authentication.noEmailProvided')
|
||||
});
|
||||
}
|
||||
|
||||
return email;
|
||||
|
@ -274,8 +274,8 @@ authentication = {
|
|||
ne2Password: ne2Password,
|
||||
dbHash: response.settings[0].value
|
||||
});
|
||||
}).catch(function (error) {
|
||||
throw new errors.UnauthorizedError(error.message);
|
||||
}).catch(function (err) {
|
||||
throw new errors.UnauthorizedError({err: err});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -309,19 +309,19 @@ authentication = {
|
|||
return utils.checkObject(invitation, 'invitation')
|
||||
.then(function () {
|
||||
if (!invitation.invitation[0].token) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.api.authentication.noTokenProvided')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.authentication.noTokenProvided')}));
|
||||
}
|
||||
|
||||
if (!invitation.invitation[0].email) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.api.authentication.noEmailProvided')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.authentication.noEmailProvided')}));
|
||||
}
|
||||
|
||||
if (!invitation.invitation[0].password) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.api.authentication.noPasswordProvided')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.authentication.noPasswordProvided')}));
|
||||
}
|
||||
|
||||
if (!invitation.invitation[0].name) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.api.authentication.noNameProvided')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.authentication.noNameProvided')}));
|
||||
}
|
||||
|
||||
return invitation;
|
||||
|
@ -336,11 +336,11 @@ authentication = {
|
|||
invite = _invite;
|
||||
|
||||
if (!invite) {
|
||||
throw new errors.NotFoundError(i18n.t('errors.api.invites.inviteNotFound'));
|
||||
throw new errors.NotFoundError({message: i18n.t('errors.api.invites.inviteNotFound')});
|
||||
}
|
||||
|
||||
if (invite.get('expires') < Date.now()) {
|
||||
throw new errors.NotFoundError(i18n.t('errors.api.invites.inviteExpired'));
|
||||
throw new errors.NotFoundError({message: i18n.t('errors.api.invites.inviteExpired')});
|
||||
}
|
||||
|
||||
return models.User.add({
|
||||
|
@ -386,9 +386,9 @@ authentication = {
|
|||
var email = options.email;
|
||||
|
||||
if (typeof email !== 'string' || !validator.isEmail(email)) {
|
||||
throw new errors.BadRequestError(
|
||||
i18n.t('errors.api.authentication.invalidEmailReceived')
|
||||
);
|
||||
throw new errors.BadRequestError({
|
||||
message: i18n.t('errors.api.authentication.invalidEmailReceived')
|
||||
});
|
||||
}
|
||||
|
||||
return email;
|
||||
|
@ -489,10 +489,12 @@ authentication = {
|
|||
}]
|
||||
};
|
||||
|
||||
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);
|
||||
apiMail.send(payload, {context: {internal: true}}).catch(function (error) {
|
||||
logging.error(new errors.EmailError({
|
||||
err: error,
|
||||
context: i18n.t('errors.api.authentication.unableToSendWelcomeEmail'),
|
||||
help: i18n.t('errors.api.authentication.checkEmailConfigInstructions', {url: 'http://support.ghost.org/mail/'})
|
||||
}));
|
||||
});
|
||||
})
|
||||
.return(setupUser);
|
||||
|
@ -524,7 +526,7 @@ authentication = {
|
|||
|
||||
function processArgs(setupDetails, options) {
|
||||
if (!options.context || !options.context.user) {
|
||||
throw new errors.NoPermissionError(i18n.t('errors.api.authentication.notTheBlogOwner'));
|
||||
throw new errors.NoPermissionError({message: i18n.t('errors.api.authentication.notTheBlogOwner')});
|
||||
}
|
||||
|
||||
return _.assign({setupDetails: setupDetails}, options);
|
||||
|
@ -534,7 +536,7 @@ authentication = {
|
|||
return models.User.findOne({role: 'Owner', status: 'all'})
|
||||
.then(function (owner) {
|
||||
if (owner.id !== options.context.user) {
|
||||
throw new errors.NoPermissionError(i18n.t('errors.api.authentication.notTheBlogOwner'));
|
||||
throw new errors.NoPermissionError({message: i18n.t('errors.api.authentication.notTheBlogOwner')});
|
||||
}
|
||||
|
||||
return options.setupDetails;
|
||||
|
@ -591,9 +593,9 @@ authentication = {
|
|||
return destroyToken(providers.pop(), options, providers);
|
||||
})
|
||||
.catch(function () {
|
||||
throw new errors.TokenRevocationError(
|
||||
i18n.t('errors.api.authentication.tokenRevocationFailed')
|
||||
);
|
||||
throw new errors.TokenRevocationError({
|
||||
message: i18n.t('errors.api.authentication.tokenRevocationFailed')
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ clients = {
|
|||
return {clients: [result.toJSON(options)]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('common.api.clients.clientNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('common.api.clients.clientNotFound')}));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -37,8 +37,8 @@ db = {
|
|||
function exportContent() {
|
||||
return exporter.doExport().then(function (exportedData) {
|
||||
return {db: [exportedData]};
|
||||
}).catch(function (error) {
|
||||
return Promise.reject(new errors.InternalServerError(error.message || error));
|
||||
}).catch(function (err) {
|
||||
return Promise.reject(new errors.GhostError({err: err}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -99,8 +99,8 @@ db = {
|
|||
return Promise.each(collections, function then(Collection) {
|
||||
return Collection.invokeThen('destroy');
|
||||
}).return({db: []})
|
||||
.catch(function (error) {
|
||||
throw new errors.InternalServerError(error.message || error);
|
||||
.catch(function (err) {
|
||||
throw new errors.GhostError({err: err});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ invites = {
|
|||
return {invites: [result.toJSON(options)]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.invites.inviteNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.invites.inviteNotFound')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -65,7 +65,7 @@ invites = {
|
|||
return dataProvider.Invite.findOne({id: options.id}, _.omit(options, ['data']))
|
||||
.then(function (invite) {
|
||||
if (!invite) {
|
||||
throw new errors.NotFoundError(i18n.t('errors.api.invites.inviteNotFound'));
|
||||
throw new errors.NotFoundError({message: i18n.t('errors.api.invites.inviteNotFound')});
|
||||
}
|
||||
|
||||
return invite.destroy(options).return(null);
|
||||
|
@ -94,7 +94,7 @@ invites = {
|
|||
return dataProvider.User.findOne({id: loggedInUser}, options)
|
||||
.then(function (user) {
|
||||
if (!user) {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.users.userNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.users.userNotFound')}));
|
||||
}
|
||||
|
||||
loggedInUser = user;
|
||||
|
@ -172,11 +172,11 @@ invites = {
|
|||
var roleId;
|
||||
|
||||
if (!options.data.invites[0].email) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.api.invites.emailIsRequired')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.invites.emailIsRequired')}));
|
||||
}
|
||||
|
||||
if (!options.data.invites[0].roles || !options.data.invites[0].roles[0]) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.api.invites.roleIsRequired')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.invites.roleIsRequired')}));
|
||||
}
|
||||
|
||||
roleId = parseInt(options.data.invites[0].roles[0].id || options.data.invites[0].roles[0], 10);
|
||||
|
@ -185,7 +185,7 @@ invites = {
|
|||
// Make sure user is allowed to add a user with this role
|
||||
return dataProvider.Role.findOne({id: roleId}).then(function (role) {
|
||||
if (role.get('name') === 'Owner') {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.api.invites.notAllowedToInviteOwner')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.api.invites.notAllowedToInviteOwner')}));
|
||||
}
|
||||
}).then(function () {
|
||||
return options;
|
||||
|
|
|
@ -38,7 +38,7 @@ function sendMail(object) {
|
|||
);
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.EmailError(err.message));
|
||||
return Promise.reject(new errors.EmailError({err: err}));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ notifications = {
|
|||
return canThis(options.context).browse.notification().then(function () {
|
||||
return {notifications: notificationsStore};
|
||||
}, function () {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.api.notifications.noPermissionToBrowseNotif')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.api.notifications.noPermissionToBrowseNotif')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -72,7 +72,7 @@ notifications = {
|
|||
return canThis(options.context).add.notification().then(function () {
|
||||
return options;
|
||||
}, function () {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.api.notifications.noPermissionToAddNotif')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.api.notifications.noPermissionToAddNotif')}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,7 @@ notifications = {
|
|||
return canThis(options.context).destroy.notification().then(function () {
|
||||
return options;
|
||||
}, function () {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.api.notifications.noPermissionToDestroyNotif')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.api.notifications.noPermissionToDestroyNotif')}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -166,12 +166,12 @@ notifications = {
|
|||
|
||||
if (notification && !notification.dismissible) {
|
||||
return Promise.reject(
|
||||
new errors.NoPermissionError(i18n.t('errors.api.notifications.noPermissionToDismissNotif'))
|
||||
new errors.NoPermissionError({message: i18n.t('errors.api.notifications.noPermissionToDismissNotif')})
|
||||
);
|
||||
}
|
||||
|
||||
if (!notification) {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.notifications.notificationDoesNotExist')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.notifications.notificationDoesNotExist')}));
|
||||
}
|
||||
|
||||
notificationsStore = _.reject(notificationsStore, function (element) {
|
||||
|
@ -206,8 +206,11 @@ notifications = {
|
|||
notificationCounter = 0;
|
||||
|
||||
return notificationsStore;
|
||||
}, function () {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.api.notifications.noPermissionToDestroyNotif')));
|
||||
}, function (err) {
|
||||
return Promise.reject(new errors.NoPermissionError({
|
||||
err: err,
|
||||
context: i18n.t('errors.api.notifications.noPermissionToDestroyNotif')
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -107,7 +107,7 @@ posts = {
|
|||
return {posts: [result.toJSON(options)]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.posts.postNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.posts.postNotFound')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -154,7 +154,7 @@ posts = {
|
|||
return {posts: [post]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.posts.postNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.posts.postNotFound')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -223,7 +223,7 @@ posts = {
|
|||
return Post.findOne(data, fetchOpts).then(function () {
|
||||
return Post.destroy(options).return(null);
|
||||
}).catch(Post.NotFoundError, function () {
|
||||
throw new errors.NotFoundError(i18n.t('errors.api.posts.postNotFound'));
|
||||
throw new errors.NotFoundError({message: i18n.t('errors.api.posts.postNotFound')});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ exports.publishPost = function publishPost(object, options) {
|
|||
|
||||
// CASE: only the scheduler client is allowed to publish (hardcoded because of missing client permission system)
|
||||
if (!options.context || !options.context.client || options.context.client !== 'ghost-scheduler') {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.permissions.noPermissionToAction')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.permissions.noPermissionToAction')}));
|
||||
}
|
||||
|
||||
options.context = {internal: true};
|
||||
|
@ -41,11 +41,11 @@ exports.publishPost = function publishPost(object, options) {
|
|||
publishedAtMoment = moment(post.published_at);
|
||||
|
||||
if (publishedAtMoment.diff(moment(), 'minutes') > publishAPostBySchedulerToleranceInMinutes) {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.job.notFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.job.notFound')}));
|
||||
}
|
||||
|
||||
if (publishedAtMoment.diff(moment(), 'minutes') < publishAPostBySchedulerToleranceInMinutes * -1 && object.force !== true) {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.job.publishInThePast')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.job.publishInThePast')}));
|
||||
}
|
||||
|
||||
return apiPosts.edit({posts: [{status: 'published'}]}, _.pick(cleanOptions, ['context', 'id']));
|
||||
|
|
|
@ -43,10 +43,12 @@ updateConfigCache = function () {
|
|||
try {
|
||||
labsValue = JSON.parse(settingsCache.labs.value);
|
||||
} 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);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
message: i18n.t('errors.api.settings.invalidJsonInLabs'),
|
||||
context: i18n.t('errors.api.settings.labsColumnCouldNotBeParsed'),
|
||||
help: i18n.t('errors.api.settings.tryUpdatingLabs')
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,7 +252,7 @@ populateDefaultSetting = function (key) {
|
|||
}
|
||||
|
||||
// TODO: Different kind of error?
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.settings.problemFindingSetting', {key: key})));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.settings.problemFindingSetting', {key: key})}));
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -265,12 +267,12 @@ canEditAllSettings = function (settingsInfo, options) {
|
|||
var checkSettingPermissions = function (setting) {
|
||||
if (setting.type === 'core' && !(options.context && options.context.internal)) {
|
||||
return Promise.reject(
|
||||
new errors.NoPermissionError(i18n.t('errors.api.settings.accessCoreSettingFromExtReq'))
|
||||
new errors.NoPermissionError({message: i18n.t('errors.api.settings.accessCoreSettingFromExtReq')})
|
||||
);
|
||||
}
|
||||
|
||||
return canThis(options.context).edit.setting(setting.key).catch(function () {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.api.settings.noPermissionToEditSettings')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.api.settings.noPermissionToEditSettings')}));
|
||||
});
|
||||
},
|
||||
checks = _.map(settingsInfo, function (settingInfo) {
|
||||
|
@ -349,7 +351,7 @@ settings = {
|
|||
|
||||
if (setting.type === 'core' && !(options.context && options.context.internal)) {
|
||||
return Promise.reject(
|
||||
new errors.NoPermissionError(i18n.t('errors.api.settings.accessCoreSettingFromExtReq'))
|
||||
new errors.NoPermissionError({message: i18n.t('errors.api.settings.accessCoreSettingFromExtReq')})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -360,7 +362,7 @@ settings = {
|
|||
return canThis(options.context).read.setting(options.key).then(function () {
|
||||
return settingsResult(result);
|
||||
}, function () {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.api.settings.noPermissionToReadSettings')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.api.settings.noPermissionToReadSettings')}));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ slugs = {
|
|||
*/
|
||||
function checkAllowedTypes(options) {
|
||||
if (allowedTypes[options.type] === undefined) {
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.api.slugs.unknownSlugType', {type: options.type})));
|
||||
return Promise.reject(new errors.BadRequestError({message: i18n.t('errors.api.slugs.unknownSlugType', {type: options.type})}));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ slugs = {
|
|||
// Pipeline calls each task passing the result of one to be the arguments for the next
|
||||
return pipeline(tasks, options).then(function (slug) {
|
||||
if (!slug) {
|
||||
return Promise.reject(new errors.InternalServerError(i18n.t('errors.api.slugs.couldNotGenerateSlug')));
|
||||
return Promise.reject(new errors.GhostError({message: i18n.t('errors.api.slugs.couldNotGenerateSlug')}));
|
||||
}
|
||||
|
||||
return {slugs: [{slug: slug}]};
|
||||
|
|
|
@ -80,7 +80,7 @@ subscribers = {
|
|||
return {subscribers: [result.toJSON(options)]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.subscribers.subscriberNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.subscribers.subscriberNotFound')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -105,12 +105,12 @@ subscribers = {
|
|||
// we don't expose this information
|
||||
return Promise.resolve(subscriber);
|
||||
} else if (subscriber) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.api.subscribers.subscriberAlreadyExists')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.subscribers.subscriberAlreadyExists')}));
|
||||
}
|
||||
|
||||
return dataProvider.Subscriber.add(options.data.subscribers[0], _.omit(options, ['data'])).catch(function (error) {
|
||||
if (error.code && error.message.toLowerCase().indexOf('unique') !== -1) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.api.subscribers.subscriberAlreadyExists')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.subscribers.subscriberAlreadyExists')}));
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
|
@ -167,7 +167,7 @@ subscribers = {
|
|||
return {subscribers: [subscriber]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.subscribers.subscriberNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.subscribers.subscriberNotFound')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -241,8 +241,8 @@ subscribers = {
|
|||
function exportSubscribers() {
|
||||
return dataProvider.Subscriber.findPage(options).then(function (data) {
|
||||
return formatCSV(data.subscribers);
|
||||
}).catch(function (error) {
|
||||
return Promise.reject(new errors.InternalServerError(error.message || error));
|
||||
}).catch(function (err) {
|
||||
return Promise.reject(new errors.GhostError({err: err}));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ tags = {
|
|||
return {tags: [result.toJSON(options)]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.tags.tagNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.tags.tagNotFound')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -155,7 +155,7 @@ tags = {
|
|||
return {tags: [tag]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.tags.tagNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.tags.tagNotFound')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -35,15 +35,15 @@ themes = {
|
|||
options.originalname = options.originalname.toLowerCase();
|
||||
|
||||
var storageAdapter = storage.getStorage('themes'),
|
||||
zip = {
|
||||
path: options.path,
|
||||
name: options.originalname,
|
||||
shortName: storageAdapter.getSanitizedFileName(options.originalname.split('.zip')[0])
|
||||
}, theme;
|
||||
zip = {
|
||||
path: options.path,
|
||||
name: options.originalname,
|
||||
shortName: storageAdapter.getSanitizedFileName(options.originalname.split('.zip')[0])
|
||||
}, theme;
|
||||
|
||||
// check if zip name is casper.zip
|
||||
if (zip.name === 'casper.zip') {
|
||||
throw new errors.ValidationError(i18n.t('errors.api.themes.overrideCasper'));
|
||||
throw new errors.ValidationError({message: i18n.t('errors.api.themes.overrideCasper')});
|
||||
}
|
||||
|
||||
return apiUtils.handlePermissions('themes', 'add')(options)
|
||||
|
@ -58,10 +58,10 @@ themes = {
|
|||
return;
|
||||
}
|
||||
|
||||
throw new errors.ThemeValidationError(
|
||||
i18n.t('errors.api.themes.invalidTheme'),
|
||||
theme.results.error
|
||||
);
|
||||
throw new errors.ThemeValidationError({
|
||||
message: i18n.t('errors.api.themes.invalidTheme'),
|
||||
errorDetails: theme.results.error
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return storageAdapter.exists(config.getContentPath('themes') + '/' + zip.shortName);
|
||||
|
@ -104,7 +104,7 @@ themes = {
|
|||
// happens in background
|
||||
Promise.promisify(fs.removeSync)(zip.path)
|
||||
.catch(function (err) {
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({err: err}));
|
||||
});
|
||||
|
||||
// remove extracted dir from gscan
|
||||
|
@ -112,7 +112,7 @@ themes = {
|
|||
if (theme) {
|
||||
Promise.promisify(fs.removeSync)(theme.path)
|
||||
.catch(function (err) {
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({err: err}));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -124,7 +124,7 @@ themes = {
|
|||
storageAdapter = storage.getStorage('themes');
|
||||
|
||||
if (!theme) {
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.api.themes.invalidRequest')));
|
||||
return Promise.reject(new errors.BadRequestError({message: i18n.t('errors.api.themes.invalidRequest')}));
|
||||
}
|
||||
|
||||
return apiUtils.handlePermissions('themes', 'read')(options)
|
||||
|
@ -146,13 +146,13 @@ themes = {
|
|||
return apiUtils.handlePermissions('themes', 'destroy')(options)
|
||||
.then(function () {
|
||||
if (name === 'casper') {
|
||||
throw new errors.ValidationError(i18n.t('errors.api.themes.destroyCasper'));
|
||||
throw new errors.ValidationError({message: i18n.t('errors.api.themes.destroyCasper')});
|
||||
}
|
||||
|
||||
theme = config.get('paths').availableThemes[name];
|
||||
|
||||
if (!theme) {
|
||||
throw new errors.NotFoundError(i18n.t('errors.api.themes.themeDoesNotExist'));
|
||||
throw new errors.NotFoundError({message: i18n.t('errors.api.themes.themeDoesNotExist')});
|
||||
}
|
||||
|
||||
events.emit('theme.deleted', name);
|
||||
|
|
|
@ -90,7 +90,7 @@ users = {
|
|||
return {users: [result.toJSON(options)]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.users.userNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.users.userNotFound')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -145,14 +145,14 @@ users = {
|
|||
var contextRoleId = contextUser.related('roles').toJSON(options)[0].id;
|
||||
|
||||
if (roleId !== contextRoleId && editedUserId === contextUser.id) {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.api.users.cannotChangeOwnRole')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.api.users.cannotChangeOwnRole')}));
|
||||
}
|
||||
|
||||
return dataProvider.User.findOne({role: 'Owner'}).then(function (owner) {
|
||||
if (contextUser.id !== owner.id) {
|
||||
if (editedUserId === owner.id) {
|
||||
if (owner.related('roles').at(0).id !== roleId) {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.api.users.cannotChangeOwnersRole')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.api.users.cannotChangeOwnersRole')}));
|
||||
}
|
||||
} else if (roleId !== contextRoleId) {
|
||||
return canThis(options.context).assign.role(role).then(function () {
|
||||
|
@ -165,7 +165,10 @@ users = {
|
|||
});
|
||||
});
|
||||
}).catch(function handleError(err) {
|
||||
return Promise.reject(new errors.NoPermissionError(err.message, i18n.t('errors.api.users.noPermissionToEditUser')));
|
||||
return Promise.reject(new errors.NoPermissionError({
|
||||
err: err,
|
||||
context: i18n.t('errors.api.users.noPermissionToEditUser')
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -192,7 +195,7 @@ users = {
|
|||
return {users: [result.toJSON(options)]};
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.api.users.userNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.users.userNotFound')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -215,7 +218,10 @@ users = {
|
|||
options.status = 'all';
|
||||
return options;
|
||||
}).catch(function handleError(err) {
|
||||
return Promise.reject(new errors.NoPermissionError(err.message, i18n.t('errors.api.users.noPermissionToDestroyUser')));
|
||||
return Promise.reject(new errors.NoPermissionError({
|
||||
err: err,
|
||||
context: i18n.t('errors.api.users.noPermissionToDestroyUser')
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -236,7 +242,9 @@ users = {
|
|||
return dataProvider.User.destroy(options);
|
||||
}).return(null);
|
||||
}).catch(function (err) {
|
||||
return Promise.reject(new errors.NoPermissionError(err.message));
|
||||
return Promise.reject(new errors.NoPermissionError({
|
||||
err: err
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -271,7 +279,10 @@ users = {
|
|||
return canThis(options.context).edit.user(options.data.password[0].user_id).then(function permissionGranted() {
|
||||
return options;
|
||||
}).catch(function (err) {
|
||||
return Promise.reject(new errors.NoPermissionError(err.message, i18n.t('errors.api.users.noPermissionToChangeUsersPwd')));
|
||||
return Promise.reject(new errors.NoPermissionError({
|
||||
err: err,
|
||||
context: i18n.t('errors.api.users.noPermissionToChangeUsersPwd')
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -211,11 +211,15 @@ utils = {
|
|||
|
||||
return permsPromise.then(function permissionGranted() {
|
||||
return options;
|
||||
}).catch(errors.NoPermissionError, function handleNoPermissionError(error) {
|
||||
// pimp error message
|
||||
error.message = i18n.t('errors.api.utils.noPermissionToCall', {method: method, docName: docName});
|
||||
// forward error to next catch()
|
||||
return Promise.reject(error);
|
||||
}).catch(function handleNoPermissionError(err) {
|
||||
if (err instanceof errors.NoPermissionError) {
|
||||
err.message = i18n.t('errors.api.utils.noPermissionToCall', {method: method, docName: docName});
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.GhostError({
|
||||
err: err
|
||||
}));
|
||||
});
|
||||
};
|
||||
},
|
||||
|
@ -271,7 +275,9 @@ utils = {
|
|||
*/
|
||||
checkObject: function (object, docName, editId) {
|
||||
if (_.isEmpty(object) || _.isEmpty(object[docName]) || _.isEmpty(object[docName][0])) {
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.api.utils.noRootKeyProvided', {docName: docName})));
|
||||
return Promise.reject(new errors.BadRequestError({
|
||||
message: i18n.t('errors.api.utils.noRootKeyProvided', {docName: docName})
|
||||
}));
|
||||
}
|
||||
|
||||
// convert author property to author_id to match the name in the database
|
||||
|
@ -292,7 +298,9 @@ utils = {
|
|||
});
|
||||
|
||||
if (editId && object[docName][0].id && parseInt(editId, 10) !== parseInt(object[docName][0].id, 10)) {
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.api.utils.invalidIdProvided')));
|
||||
return Promise.reject(new errors.BadRequestError({
|
||||
message: i18n.t('errors.api.utils.invalidIdProvided')
|
||||
}));
|
||||
}
|
||||
|
||||
return Promise.resolve(object);
|
||||
|
|
|
@ -13,6 +13,8 @@ var hbs = require('express-hbs'),
|
|||
sanitizeHtml = require('sanitize-html'),
|
||||
config = require('../../../../config'),
|
||||
logging = require('../../../../logging'),
|
||||
i18n = require('../../../../i18n'),
|
||||
errors = require('../../../../errors'),
|
||||
makeAbsoluteUrl = require('../../../../utils/make-absolute-urls'),
|
||||
cheerio = require('cheerio'),
|
||||
amperize = new Amperize(),
|
||||
|
@ -126,10 +128,13 @@ function getAmperizeHTML(html, post) {
|
|||
amperize.parse(html, function (err, res) {
|
||||
if (err) {
|
||||
if (err.src) {
|
||||
err.context = 'AMP HTML couldn\'t get parsed: ' + err.src;
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
context: 'AMP HTML couldn\'t get parsed: ' + err.src,
|
||||
help: i18n.t('errors.apps.appWillNotBeLoaded.help')
|
||||
}));
|
||||
} else {
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({err: err}));
|
||||
}
|
||||
|
||||
// save it in cache to prevent multiple calls to Amperize until
|
||||
|
|
|
@ -182,9 +182,10 @@ describe('AMP getPostData', function () {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return error if postlookup returns NotFoundError', function (done) {
|
||||
postLookupStub = sandbox.stub();
|
||||
postLookupStub.returns(new Promise.reject(new errors.NotFoundError('not found')));
|
||||
postLookupStub.returns(new Promise.reject(new errors.NotFoundError({message: 'not found'})));
|
||||
|
||||
ampController.__set__('postLookup', postLookupStub);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
var _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
logging = require('../logging'),
|
||||
errors = require('../errors'),
|
||||
api = require('../api'),
|
||||
loader = require('./loader'),
|
||||
i18n = require('../i18n'),
|
||||
|
@ -47,10 +48,11 @@ module.exports = {
|
|||
appsToLoad = appsToLoad.concat(config.get('internalApps'));
|
||||
});
|
||||
} 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);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
context: i18n.t('errors.apps.failedToParseActiveAppsSettings.context'),
|
||||
help: i18n.t('errors.apps.failedToParseActiveAppsSettings.help')
|
||||
}));
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -87,9 +89,11 @@ module.exports = {
|
|||
// Extend the loadedApps onto the available apps
|
||||
_.extend(availableApps, loadedApps);
|
||||
}).catch(function (err) {
|
||||
err.context = i18n.t('errors.apps.appWillNotBeLoaded.error');
|
||||
err.help = i18n.t('errors.apps.appWillNotBeLoaded.help');
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
context: i18n.t('errors.apps.appWillNotBeLoaded.error'),
|
||||
help: i18n.t('errors.apps.appWillNotBeLoaded.help')
|
||||
}));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var config = require('../../config'),
|
||||
utils = require('../../utils'),
|
||||
errors = require('../../errors'),
|
||||
logging = require('../../logging'),
|
||||
i18n = require('../../i18n'),
|
||||
middleware = require('./lib/middleware'),
|
||||
|
@ -8,19 +9,19 @@ var config = require('../../config'),
|
|||
|
||||
module.exports = {
|
||||
activate: function activate(ghost) {
|
||||
var err, paths;
|
||||
var paths;
|
||||
|
||||
if (utils.url.getSubdir()) {
|
||||
paths = utils.url.getSubdir().split('/');
|
||||
|
||||
if (paths.pop() === config.get('routeKeywords').private) {
|
||||
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);
|
||||
logging.error(new errors.GhostError({
|
||||
message: i18n.t('errors.config.urlCannotContainPrivateSubdir.error'),
|
||||
context: i18n.t('errors.config.urlCannotContainPrivateSubdir.description'),
|
||||
help: i18n.t('errors.config.urlCannotContainPrivateSubdir.help')
|
||||
}));
|
||||
|
||||
// @TODO: why?
|
||||
// @TODO: why
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ var _ = require('lodash'),
|
|||
config = require('../../../config'),
|
||||
api = require('../../../api'),
|
||||
errors = require('../../../errors'),
|
||||
logging = require('../../../logging'),
|
||||
logging = require('../../../logging'),
|
||||
utils = require('../../../utils'),
|
||||
i18n = require('../../../i18n'),
|
||||
privateRoute = '/' + config.get('routeKeywords').private + '/',
|
||||
|
@ -56,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 next(new errors.NotFoundError(i18n.t('errors.errors.pageNotFound')));
|
||||
return next(new errors.NotFoundError({message: 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) {
|
||||
|
@ -146,8 +146,7 @@ privateBlogging = {
|
|||
ipCount = '',
|
||||
message = i18n.t('errors.middleware.spamprevention.tooManyAttempts'),
|
||||
deniedRateLimit = '',
|
||||
password = req.body.password,
|
||||
err;
|
||||
password = req.body.password;
|
||||
|
||||
if (password) {
|
||||
protectedSecurity.push({ip: remoteAddress, time: currentTime});
|
||||
|
@ -167,10 +166,10 @@ privateBlogging = {
|
|||
deniedRateLimit = (ipCount[remoteAddress] > rateProtectedAttempts);
|
||||
|
||||
if (deniedRateLimit) {
|
||||
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);
|
||||
logging.error(new errors.GhostError({
|
||||
message: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error', {rfa: rateProtectedAttempts, rfp: rateProtectedPeriod}),
|
||||
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
|
||||
}));
|
||||
|
||||
message += rateProtectedPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater');
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ function storeSubscriber(req, res, next) {
|
|||
req.body.status = 'subscribed';
|
||||
|
||||
if (_.isEmpty(req.body.email)) {
|
||||
return next(new errors.ValidationError('Email cannot be blank.'));
|
||||
return next(new errors.ValidationError({message: 'Email cannot be blank.'}));
|
||||
}
|
||||
|
||||
return api.subscribers.add({subscribers: [req.body]}, {context: {external: true}})
|
||||
|
|
|
@ -86,11 +86,11 @@ strategies = {
|
|||
invite = _invite;
|
||||
|
||||
if (!invite) {
|
||||
throw new errors.NotFoundError(i18n.t('errors.api.invites.inviteNotFound'));
|
||||
throw new errors.NotFoundError({message: i18n.t('errors.api.invites.inviteNotFound')});
|
||||
}
|
||||
|
||||
if (invite.get('expires') < Date.now()) {
|
||||
throw new errors.NotFoundError(i18n.t('errors.api.invites.inviteExpired'));
|
||||
throw new errors.NotFoundError({message: i18n.t('errors.api.invites.inviteExpired')});
|
||||
}
|
||||
|
||||
return models.User.add({
|
||||
|
@ -113,7 +113,7 @@ strategies = {
|
|||
return models.User.findOne({slug: 'ghost-owner', status: 'all'}, options)
|
||||
.then(function fetchedOwner(owner) {
|
||||
if (!owner) {
|
||||
throw new errors.NotFoundError(i18n.t('errors.models.user.userNotFound'));
|
||||
throw new errors.NotFoundError({message: i18n.t('errors.models.user.userNotFound')});
|
||||
}
|
||||
|
||||
return models.User.edit({
|
||||
|
|
|
@ -44,11 +44,11 @@ authenticate = {
|
|||
}
|
||||
|
||||
if (!req.body.client_id || !req.body.client_secret) {
|
||||
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 next(new errors.UnauthorizedError({
|
||||
message: i18n.t('errors.middleware.auth.accessDenied'),
|
||||
context: i18n.t('errors.middleware.auth.clientCredentialsNotProvided'),
|
||||
help: i18n.t('errors.middleware.auth.forInformationRead', {url: 'http://api.ghost.org/docs/client-authentication'})
|
||||
}));
|
||||
}
|
||||
|
||||
return passport.authenticate(['oauth2-client-password'], {session: false, failWithError: false},
|
||||
|
@ -62,11 +62,11 @@ authenticate = {
|
|||
delete req.body.client_secret;
|
||||
|
||||
if (!client) {
|
||||
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 next(new errors.UnauthorizedError({
|
||||
message: i18n.t('errors.middleware.auth.accessDenied'),
|
||||
context: i18n.t('errors.middleware.auth.clientCredentialsNotValid'),
|
||||
help: i18n.t('errors.middleware.auth.forInformationRead', {url: 'http://api.ghost.org/docs/client-authentication'})
|
||||
}));
|
||||
}
|
||||
|
||||
req.client = client;
|
||||
|
@ -92,13 +92,17 @@ authenticate = {
|
|||
events.emit('user.authenticated', user);
|
||||
return next(null, user, info);
|
||||
} else if (isBearerAutorizationHeader(req)) {
|
||||
return next(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')));
|
||||
return next(new errors.UnauthorizedError({
|
||||
message: i18n.t('errors.middleware.auth.accessDenied')
|
||||
}));
|
||||
} else if (req.client) {
|
||||
req.user = {id: 0};
|
||||
return next();
|
||||
}
|
||||
|
||||
return next(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')));
|
||||
return next(new errors.UnauthorizedError({
|
||||
message: i18n.t('errors.middleware.auth.accessDenied')
|
||||
}));
|
||||
}
|
||||
)(req, res, next);
|
||||
},
|
||||
|
@ -108,7 +112,7 @@ authenticate = {
|
|||
req.query.code = req.body.authorizationCode;
|
||||
|
||||
if (!req.query.code) {
|
||||
return next(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')));
|
||||
return next(new errors.UnauthorizedError({message: i18n.t('errors.middleware.auth.accessDenied')}));
|
||||
}
|
||||
|
||||
passport.authenticate('ghost', {session: false, failWithError: false}, function authenticate(err, user, info) {
|
||||
|
@ -117,7 +121,7 @@ authenticate = {
|
|||
}
|
||||
|
||||
if (!user) {
|
||||
return next(new errors.UnauthorizedError(i18n.t('errors.middleware.auth.accessDenied')));
|
||||
return next(new errors.UnauthorizedError({message: i18n.t('errors.middleware.auth.accessDenied')}));
|
||||
}
|
||||
|
||||
req.authInfo = info;
|
||||
|
|
|
@ -10,7 +10,7 @@ authorize = {
|
|||
if (req.user && req.user.id) {
|
||||
return next();
|
||||
} else {
|
||||
return next(new errors.NoPermissionError(i18n.t('errors.middleware.auth.pleaseSignIn')));
|
||||
return next(new errors.NoPermissionError({message: i18n.t('errors.middleware.auth.pleaseSignIn')}));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -22,7 +22,7 @@ authorize = {
|
|||
if (req.user && req.user.id) {
|
||||
return next();
|
||||
} else {
|
||||
return next(new errors.NoPermissionError(i18n.t('errors.middleware.auth.pleaseSignIn')));
|
||||
return next(new errors.NoPermissionError({message: i18n.t('errors.middleware.auth.pleaseSignIn')}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ function exchangeRefreshToken(client, refreshToken, scope, done) {
|
|||
models.Refreshtoken.findOne({token: refreshToken})
|
||||
.then(function then(model) {
|
||||
if (!model) {
|
||||
return done(new errors.NoPermissionError(i18n.t('errors.middleware.oauth.invalidRefreshToken')), false);
|
||||
return done(new errors.NoPermissionError({message: i18n.t('errors.middleware.oauth.invalidRefreshToken')}), false);
|
||||
} else {
|
||||
var token = model.toJSON(),
|
||||
accessToken = utils.uid(191),
|
||||
|
@ -33,7 +33,7 @@ function exchangeRefreshToken(client, refreshToken, scope, done) {
|
|||
return done(error, false);
|
||||
});
|
||||
} else {
|
||||
done(new errors.UnauthorizedError(i18n.t('errors.middleware.oauth.refreshTokenExpired')), false);
|
||||
done(new errors.UnauthorizedError({message: i18n.t('errors.middleware.oauth.refreshTokenExpired')}), false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -44,7 +44,7 @@ function exchangePassword(client, username, password, scope, done) {
|
|||
models.Client.findOne({slug: client.slug})
|
||||
.then(function then(client) {
|
||||
if (!client) {
|
||||
return done(new errors.NoPermissionError(i18n.t('errors.middleware.oauth.invalidClient')), false);
|
||||
return done(new errors.NoPermissionError({message: i18n.t('errors.middleware.oauth.invalidClient')}), false);
|
||||
}
|
||||
|
||||
// Validate the user
|
||||
|
|
|
@ -6,6 +6,7 @@ var ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy
|
|||
authStrategies = require('./auth-strategies'),
|
||||
utils = require('../utils'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
models = require('../models'),
|
||||
_private = {};
|
||||
|
||||
|
@ -44,11 +45,13 @@ _private.registerClient = function registerClient(options) {
|
|||
});
|
||||
})
|
||||
.catch(function publicClientRegistrationError(err) {
|
||||
logging.error(err);
|
||||
|
||||
if (retryCount < 0) {
|
||||
return done(new errors.IncorrectUsage(
|
||||
'Public client registration failed: ' + err.code || err.message,
|
||||
'Please verify that the url is reachable: ' + ghostOAuth2Strategy.url
|
||||
));
|
||||
return done(new errors.IncorrectUsageError({
|
||||
message: 'Public client registration failed: ' + err.code || err.message,
|
||||
context: 'Please verify that the url is reachable: ' + ghostOAuth2Strategy.url
|
||||
}));
|
||||
}
|
||||
|
||||
console.log('RETRY: Public Client Registration...');
|
||||
|
|
|
@ -4,8 +4,6 @@ var nconf = require('nconf'),
|
|||
packageInfo = require('../../../package.json'),
|
||||
env = process.env.NODE_ENV || 'development';
|
||||
|
||||
nconf.set('NODE_ENV', env);
|
||||
|
||||
/**
|
||||
* command line arguments
|
||||
*/
|
||||
|
@ -38,6 +36,7 @@ localUtils.makePathsAbsolute.bind(nconf)();
|
|||
* @TODO: ghost-cli?
|
||||
*/
|
||||
nconf.set('ghostVersion', packageInfo.version);
|
||||
nconf.set('env', env);
|
||||
|
||||
module.exports = nconf;
|
||||
module.exports.isPrivacyDisabled = localUtils.isPrivacyDisabled.bind(nconf);
|
||||
|
|
|
@ -3,6 +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'),
|
||||
|
@ -67,7 +68,9 @@ adminControllers = {
|
|||
});
|
||||
}).finally(function noMatterWhat() {
|
||||
renderIndex();
|
||||
}).catch(logging.error);
|
||||
}).catch(function (err) {
|
||||
logging.error(new errors.GhostError({err: err}));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ var express = require('express'),
|
|||
utils = require('../../utils'),
|
||||
channelConfig = require('./channel-config'),
|
||||
renderChannel = require('./render-channel'),
|
||||
|
||||
rssRouter,
|
||||
channelRouter;
|
||||
|
||||
|
@ -26,7 +25,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(i18n.t('errors.errors.pageNotFound')));
|
||||
return next(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
|
||||
} else {
|
||||
// Set req.params.page to the already parsed number, and continue
|
||||
req.params.page = page;
|
||||
|
|
|
@ -38,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(i18n.t('errors.errors.pageNotFound')));
|
||||
return next(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
|
||||
}
|
||||
|
||||
// @TODO: figure out if this can be removed, it's supposed to ensure that absolutely URLs get generated
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
var _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
db = require('../../data/db'),
|
||||
commands = require('../schema').commands,
|
||||
versioning = require('../schema').versioning,
|
||||
var _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
db = require('../../data/db'),
|
||||
commands = require('../schema').commands,
|
||||
versioning = require('../schema').versioning,
|
||||
serverUtils = require('../../utils'),
|
||||
errors = require('../../errors'),
|
||||
logging = require('../../logging'),
|
||||
settings = require('../../api/settings'),
|
||||
i18n = require('../../i18n'),
|
||||
|
||||
excludedTables = ['accesstokens', 'refreshtokens', 'clients', 'client_trusted_domains'],
|
||||
modelOptions = {context: {internal: true}},
|
||||
|
||||
|
@ -30,7 +29,7 @@ exportFileName = function exportFileName() {
|
|||
}
|
||||
return title + 'ghost.' + datetime + '.json';
|
||||
}).catch(function (err) {
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({err: err}));
|
||||
return 'ghost.' + datetime + '.json';
|
||||
});
|
||||
};
|
||||
|
@ -38,7 +37,7 @@ exportFileName = function exportFileName() {
|
|||
getVersionAndTables = function getVersionAndTables() {
|
||||
var props = {
|
||||
version: versioning.getDatabaseVersion(),
|
||||
tables: commands.getTables()
|
||||
tables: commands.getTables()
|
||||
};
|
||||
|
||||
return Promise.props(props);
|
||||
|
@ -75,7 +74,10 @@ doExport = function doExport() {
|
|||
|
||||
return exportData;
|
||||
}).catch(function (err) {
|
||||
return Promise.reject(new errors.InternalServerError(err.message, i18n.t('errors.data.export.errorExportingData')));
|
||||
return Promise.reject(new errors.DataExportError({
|
||||
err: err,
|
||||
context: i18n.t('errors.data.export.errorExportingData')
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ cleanError = function cleanError(error) {
|
|||
value = value || 'unknown';
|
||||
message = message || error.raw.message;
|
||||
|
||||
return new errors.DataImportError(message, offendingProperty, value);
|
||||
return new errors.DataImportError({message: message, property: offendingProperty, value: value});
|
||||
};
|
||||
|
||||
handleErrors = function handleErrors(errorList) {
|
||||
|
|
|
@ -81,9 +81,11 @@ utils = {
|
|||
// CASE: external context
|
||||
userMap[userToMap] = '0';
|
||||
} else {
|
||||
throw new errors.DataImportError(
|
||||
i18n.t('errors.data.import.utils.dataLinkedToUnknownUser', {userToMap: userToMap}), 'user.id', userToMap
|
||||
);
|
||||
throw new errors.DataImportError({
|
||||
message: i18n.t('errors.data.import.utils.dataLinkedToUnknownUser', {userToMap: userToMap}),
|
||||
property: 'user.id',
|
||||
value: userToMap
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ JSONHandler = {
|
|||
// if importData follows JSON-API format `{ db: [exportedData] }`
|
||||
if (_.keys(importData).length === 1) {
|
||||
if (!importData.db || !Array.isArray(importData.db)) {
|
||||
throw new Error(i18n.t('errors.data.importer.handlers.json.invalidJsonFormat'));
|
||||
throw new errors.GhostError({message: i18n.t('errors.data.importer.handlers.json.invalidJsonFormat')});
|
||||
}
|
||||
|
||||
importData = importData.db[0];
|
||||
|
@ -32,11 +32,11 @@ JSONHandler = {
|
|||
|
||||
return importData;
|
||||
} 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')
|
||||
));
|
||||
return Promise.reject(new errors.BadRequestError({
|
||||
err: err,
|
||||
context: i18n.t('errors.data.importer.handlers.json.apiDbImportContent'),
|
||||
help: i18n.t('errors.data.importer.handlers.json.checkImportJsonIsValid')
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -109,9 +109,11 @@ _.extend(ImportManager.prototype, {
|
|||
_.each(filesToDelete, function (fileToDelete) {
|
||||
fs.remove(fileToDelete, function (err) {
|
||||
if (err) {
|
||||
err.context = i18n.t('errors.data.importer.index.couldNotCleanUpFile.error');
|
||||
err.help = i18n.t('errors.data.importer.index.couldNotCleanUpFile.context');
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
context: i18n.t('errors.data.importer.index.couldNotCleanUpFile.error'),
|
||||
help: i18n.t('errors.data.importer.index.couldNotCleanUpFile.context')
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -150,9 +152,7 @@ _.extend(ImportManager.prototype, {
|
|||
|
||||
// This is a temporary extra message for the old format roon export which doesn't work with Ghost
|
||||
if (oldRoonMatches.length > 0) {
|
||||
throw new errors.UnsupportedMediaTypeError(
|
||||
i18n.t('errors.data.importer.index.unsupportedRoonExport')
|
||||
);
|
||||
throw new errors.UnsupportedMediaTypeError({message: i18n.t('errors.data.importer.index.unsupportedRoonExport')});
|
||||
}
|
||||
|
||||
// If this folder contains importable files or a content or images directory
|
||||
|
@ -161,12 +161,10 @@ _.extend(ImportManager.prototype, {
|
|||
}
|
||||
|
||||
if (extMatchesAll.length < 1) {
|
||||
throw new errors.UnsupportedMediaTypeError(
|
||||
i18n.t('errors.data.importer.index.noContentToImport'));
|
||||
throw new errors.UnsupportedMediaTypeError({message: i18n.t('errors.data.importer.index.noContentToImport')});
|
||||
}
|
||||
|
||||
throw new errors.UnsupportedMediaTypeError(
|
||||
i18n.t('errors.data.importer.index.invalidZipStructure'));
|
||||
throw new errors.UnsupportedMediaTypeError({message: i18n.t('errors.data.importer.index.invalidZipStructure')});
|
||||
},
|
||||
/**
|
||||
* Use the extract module to extract the given zip file to a temp directory & return the temp directory path
|
||||
|
@ -213,8 +211,9 @@ _.extend(ImportManager.prototype, {
|
|||
this.getExtensionGlob(this.getExtensions(), ALL_DIRS), {cwd: directory}
|
||||
);
|
||||
if (extMatchesAll.length < 1 || extMatchesAll[0].split('/') < 1) {
|
||||
throw new errors.ValidationError(i18n.t('errors.data.importer.index.invalidZipFileBaseDirectory'));
|
||||
throw new errors.ValidationError({message: i18n.t('errors.data.importer.index.invalidZipFileBaseDirectory')});
|
||||
}
|
||||
|
||||
return extMatchesAll[0].split('/')[0];
|
||||
},
|
||||
/**
|
||||
|
@ -240,9 +239,9 @@ _.extend(ImportManager.prototype, {
|
|||
_.each(self.handlers, function (handler) {
|
||||
if (importData.hasOwnProperty(handler.type)) {
|
||||
// This limitation is here to reduce the complexity of the importer for now
|
||||
return Promise.reject(new errors.UnsupportedMediaTypeError(
|
||||
i18n.t('errors.data.importer.index.zipContainsMultipleDataFormats')
|
||||
));
|
||||
return Promise.reject(new errors.UnsupportedMediaTypeError({
|
||||
message: i18n.t('errors.data.importer.index.zipContainsMultipleDataFormats')
|
||||
}));
|
||||
}
|
||||
|
||||
var files = self.getFilesFromZip(handler, zipDirectory);
|
||||
|
@ -257,9 +256,9 @@ _.extend(ImportManager.prototype, {
|
|||
});
|
||||
|
||||
if (ops.length === 0) {
|
||||
return Promise.reject(new errors.UnsupportedMediaTypeError(
|
||||
i18n.t('errors.data.importer.index.noContentToImport')
|
||||
));
|
||||
return Promise.reject(new errors.UnsupportedMediaTypeError({
|
||||
message: i18n.t('errors.data.importer.index.noContentToImport')
|
||||
}));
|
||||
}
|
||||
|
||||
return sequence(ops).then(function () {
|
||||
|
|
|
@ -49,7 +49,7 @@ populate = function populate(options) {
|
|||
});
|
||||
}).catch(function populateDatabaseError(err) {
|
||||
logger.warn('rolling back...');
|
||||
return Promise.reject(new errors.InternalServerError('Unable to populate database: ' + err.message));
|
||||
return Promise.reject(new errors.GhostError({err: err, context: 'Unable to populate database!'}));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -124,11 +124,13 @@ isDatabaseOutOfDate = function isDatabaseOutOfDate(options) {
|
|||
|
||||
// CASE: current database version is lower then we support
|
||||
if (fromVersion < versioning.canMigrateFromVersion) {
|
||||
return {error: new errors.DatabaseVersion(
|
||||
i18n.t('errors.data.versioning.index.cannotMigrate.error'),
|
||||
i18n.t('errors.data.versioning.index.cannotMigrate.context'),
|
||||
i18n.t('common.seeLinkForInstructions', {link: 'http://support.ghost.org/how-to-upgrade/'})
|
||||
)};
|
||||
return {
|
||||
error: new errors.DatabaseVersionError({
|
||||
message: i18n.t('errors.data.versioning.index.cannotMigrate.error'),
|
||||
context: i18n.t('errors.data.versioning.index.cannotMigrate.context'),
|
||||
help: i18n.t('common.seeLinkForInstructions', {link: 'http://support.ghost.org/how-to-upgrade/'})
|
||||
})
|
||||
};
|
||||
}
|
||||
// CASE: the database exists but is out of date
|
||||
else if (fromVersion < toVersion || forceMigration) {
|
||||
|
@ -140,7 +142,7 @@ isDatabaseOutOfDate = function isDatabaseOutOfDate(options) {
|
|||
}
|
||||
// CASE: we don't understand the version
|
||||
else {
|
||||
return {error: new errors.DatabaseVersion(i18n.t('errors.data.versioning.index.dbVersionNotRecognized'))};
|
||||
return {error: new errors.DatabaseVersionError({message: i18n.t('errors.data.versioning.index.dbVersionNotRecognized')})};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -9,16 +9,16 @@ module.exports = function bootUp() {
|
|||
.then(function successHandler(result) {
|
||||
if (!/^alpha/.test(result)) {
|
||||
// This database was not created with Ghost alpha, and is not compatible
|
||||
throw new errors.DatabaseVersion(
|
||||
'Your database version is not compatible with Ghost 1.0.0 Alpha (master branch)',
|
||||
'Want to keep your DB? Use Ghost < 1.0.0 or the "stable" branch. Otherwise please delete your DB and restart Ghost',
|
||||
'More information on the Ghost 1.0.0 Alpha at https://support.ghost.org/v1-0-alpha'
|
||||
);
|
||||
throw new errors.DatabaseVersionError({
|
||||
message: 'Your database version is not compatible with Ghost 1.0.0 Alpha (master branch)',
|
||||
context: 'Want to keep your DB? Use Ghost < 1.0.0 or the "stable" branch. Otherwise please delete your DB and restart Ghost',
|
||||
help: 'More information on the Ghost 1.0.0 Alpha at https://support.ghost.org/v1-0-alpha'
|
||||
});
|
||||
}
|
||||
},
|
||||
// We don't use .catch here, as it would catch the error from the successHandler
|
||||
function errorHandler(err) {
|
||||
if (err instanceof errors.DatabaseNotPopulated) {
|
||||
if (err instanceof errors.DatabaseNotPopulatedError) {
|
||||
return populate();
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ function getDatabaseVersion() {
|
|||
});
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.DatabaseNotPopulated(i18n.t('errors.data.versioning.index.databaseNotPopulated')));
|
||||
return Promise.reject(new errors.DatabaseNotPopulatedError({message: i18n.t('errors.data.versioning.index.databaseNotPopulated')}));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
var https = require('https'),
|
||||
url = require('url'),
|
||||
Promise = require('bluebird'),
|
||||
errors = require('../../errors'),
|
||||
logging = require('../../logging'),
|
||||
utils = require('../../utils'),
|
||||
events = require('../../events'),
|
||||
logging = require('../../logging'),
|
||||
|
@ -32,9 +34,11 @@ function makeRequest(reqOptions, reqPayload) {
|
|||
|
||||
req.write(reqPayload);
|
||||
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);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
context: i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.error'),
|
||||
help: i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.help', {url: 'http://support.ghost.org'})
|
||||
}));
|
||||
});
|
||||
|
||||
req.end();
|
||||
|
|
|
@ -65,7 +65,7 @@ validateSchema = function validateSchema(tableName, model) {
|
|||
&& schema[tableName][columnKey].nullable !== true) {
|
||||
if (validator.empty(strVal)) {
|
||||
message = i18n.t('notices.data.validation.index.valueCannotBeBlank', {tableName: tableName, columnKey: columnKey});
|
||||
validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey));
|
||||
validationErrors.push(new errors.ValidationError({message: message, context: tableName + '.' + columnKey}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ validateSchema = function validateSchema(tableName, model) {
|
|||
&& schema[tableName][columnKey].type === 'bool') {
|
||||
if (!(validator.isBoolean(strVal) || validator.empty(strVal))) {
|
||||
message = i18n.t('notices.data.validation.index.valueMustBeBoolean', {tableName: tableName, columnKey: columnKey});
|
||||
validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey));
|
||||
validationErrors.push(new errors.ValidationError({message: message, context: tableName + '.' + columnKey}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ validateSchema = function validateSchema(tableName, model) {
|
|||
if (!validator.isLength(strVal, 0, schema[tableName][columnKey].maxlength)) {
|
||||
message = i18n.t('notices.data.validation.index.valueExceedsMaxLength',
|
||||
{tableName: tableName, columnKey: columnKey, maxlength: schema[tableName][columnKey].maxlength});
|
||||
validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey));
|
||||
validationErrors.push(new errors.ValidationError({message: message, context: tableName + '.' + columnKey}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ validateSchema = function validateSchema(tableName, model) {
|
|||
if (schema[tableName][columnKey].hasOwnProperty('type')) {
|
||||
if (schema[tableName][columnKey].type === 'integer' && !validator.isInt(strVal)) {
|
||||
message = i18n.t('notices.data.validation.index.valueIsNotInteger', {tableName: tableName, columnKey: columnKey});
|
||||
validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey));
|
||||
validationErrors.push(new errors.ValidationError({message: message, context: tableName + '.' + columnKey}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ validateActiveTheme = function validateActiveTheme(themeName) {
|
|||
|
||||
return availableThemes.then(function then(themes) {
|
||||
if (!themes.hasOwnProperty(themeName)) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('notices.data.validation.index.themeCannotBeActivated', {themeName: themeName}), 'activeTheme'));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('notices.data.validation.index.themeCannotBeActivated', {themeName: themeName}), context: 'activeTheme'}));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -186,8 +186,9 @@ validate = function validate(value, key, validations) {
|
|||
|
||||
// equivalent of validator.isSomething(option1, option2)
|
||||
if (validator[validationName].apply(validator, validationOptions) !== goodResult) {
|
||||
validationErrors.push(new errors.ValidationError(i18n.t('notices.data.validation.index.validationFailed',
|
||||
{validationName: validationName, key: key})));
|
||||
validationErrors.push(new errors.ValidationError({
|
||||
message: i18n.t('notices.data.validation.index.validationFailed', {validationName: validationName, key: key})
|
||||
}));
|
||||
}
|
||||
|
||||
validationOptions.shift();
|
||||
|
|
|
@ -174,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(i18n.t('errors.errors.pageNotFound')));
|
||||
return next(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
|
||||
}
|
||||
|
||||
data.version = res.locals.safeVersion;
|
||||
|
|
|
@ -3,6 +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'),
|
||||
|
@ -67,11 +68,15 @@ function ping(post) {
|
|||
|
||||
req = http.request(options);
|
||||
req.write(pingXML);
|
||||
|
||||
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);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
context: i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.error'),
|
||||
help: i18n.t('errors.data.xml.xmlrpc.pingUpdateFailed.help', {url: 'http://support.ghost.org'})
|
||||
}));
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
|
162
core/server/errors.js
Normal file
162
core/server/errors.js
Normal file
|
@ -0,0 +1,162 @@
|
|||
var _ = require('lodash'),
|
||||
util = require('util');
|
||||
|
||||
function GhostError(options) {
|
||||
options = options || {};
|
||||
|
||||
if (_.isString(options)) {
|
||||
throw new Error('Please instantiate Errors with the option pattern. e.g. new errors.GhostError({message: ...})');
|
||||
}
|
||||
|
||||
Error.call(this);
|
||||
Error.captureStackTrace(this, GhostError);
|
||||
|
||||
/**
|
||||
* defaults
|
||||
* @TODO: I'd like to add the usage of an individual ID to errors, as we have in ignition
|
||||
*/
|
||||
this.statusCode = 500;
|
||||
this.errorType = 'InternalServerError';
|
||||
this.level = 'normal';
|
||||
|
||||
/**
|
||||
* custom overrides
|
||||
*/
|
||||
this.statusCode = options.statusCode || this.statusCode;
|
||||
this.level = options.level || this.level;
|
||||
this.context = options.context || this.context;
|
||||
this.help = options.help || this.help;
|
||||
this.errorType = this.name = options.errorType || this.errorType;
|
||||
this.errorDetails = options.errorDetails;
|
||||
|
||||
// @TODO: ?
|
||||
this.property = options.property;
|
||||
this.value = options.value;
|
||||
|
||||
this.message = options.message;
|
||||
this.hideStack = options.hideStack;
|
||||
|
||||
// error to inherit from, override!
|
||||
if (options.err) {
|
||||
this.message = options.err.message;
|
||||
this.stack = options.err.stack;
|
||||
}
|
||||
}
|
||||
|
||||
// jscs:disable
|
||||
var errors = {
|
||||
DataExportError: function DataExportError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 500,
|
||||
errorType: 'DataExportError'
|
||||
}, options));
|
||||
},
|
||||
DataImportError: function DataImportError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 500,
|
||||
errorType: 'DataImportError'
|
||||
}, options));
|
||||
},
|
||||
IncorrectUsageError: function IncorrectUsageError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 400,
|
||||
level: 'critical',
|
||||
errorType: 'IncorrectUsageError'
|
||||
}, options));
|
||||
},
|
||||
NotFoundError: function NotFoundError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 404,
|
||||
errorType: 'NotFoundError'
|
||||
}, options));
|
||||
},
|
||||
BadRequestError: function BadRequestError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 400,
|
||||
errorType: 'BadRequestError'
|
||||
}, options));
|
||||
},
|
||||
DatabaseVersionError: function DatabaseVersionError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
hideStack: true,
|
||||
statusCode: 500,
|
||||
errorType: 'DatabaseVersionError'
|
||||
}, options));
|
||||
},
|
||||
DatabaseNotPopulatedError: function DatabaseNotPopulatedError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 500,
|
||||
errorType: 'DatabaseNotPopulatedError'
|
||||
}, options));
|
||||
},
|
||||
UnauthorizedError: function UnauthorizedError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 401,
|
||||
errorType: 'UnauthorizedError'
|
||||
}, options));
|
||||
},
|
||||
NoPermissionError: function NoPermissionError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 403,
|
||||
errorType: 'NoPermissionError'
|
||||
}, options));
|
||||
},
|
||||
ValidationError: function ValidationError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 422,
|
||||
errorType: 'ValidationError'
|
||||
}, options));
|
||||
},
|
||||
UnsupportedMediaTypeError: function UnsupportedMediaTypeError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 415,
|
||||
errorType: 'UnsupportedMediaTypeError'
|
||||
}, options));
|
||||
},
|
||||
VersionMismatchError: function VersionMismatchError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 400,
|
||||
errorType: 'VersionMismatchError'
|
||||
}, options));
|
||||
},
|
||||
TokenRevocationError: function TokenRevocationError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 503,
|
||||
errorType: 'TokenRevocationError'
|
||||
}, options));
|
||||
},
|
||||
EmailError: function EmailError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 500,
|
||||
errorType: 'EmailError'
|
||||
}, options));
|
||||
},
|
||||
TooManyRequestsError: function TooManyRequestsError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 429,
|
||||
errorType: 'TooManyRequestsError'
|
||||
}, options));
|
||||
},
|
||||
MaintenanceError: function MaintenanceError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 503,
|
||||
errorType: 'MaintenanceError'
|
||||
}, options));
|
||||
},
|
||||
ThemeValidationError: function ThemeValidationError(options) {
|
||||
GhostError.call(this, _.merge({
|
||||
statusCode: 422,
|
||||
errorType: 'ThemeValidationError',
|
||||
errorDetails: {}
|
||||
}, options));
|
||||
}
|
||||
};
|
||||
|
||||
_.each(errors, function (error) {
|
||||
util.inherits(error, GhostError);
|
||||
});
|
||||
|
||||
module.exports = errors;
|
||||
module.exports.GhostError = GhostError;
|
||||
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// # Bad request error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
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);
|
||||
BadRequestError.prototype.name = 'BadRequestError';
|
||||
|
||||
module.exports = BadRequestError;
|
|
@ -1,16 +0,0 @@
|
|||
// # Data import error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function DataImportError(message, offendingProperty, value) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 500;
|
||||
this.errorType = this.name;
|
||||
this.property = offendingProperty || undefined;
|
||||
this.value = value || undefined;
|
||||
}
|
||||
|
||||
DataImportError.prototype = Object.create(Error.prototype);
|
||||
DataImportError.prototype.name = 'DataImportError';
|
||||
|
||||
module.exports = DataImportError;
|
|
@ -1,11 +0,0 @@
|
|||
function DatabaseNotPopulated(message) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 500;
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
DatabaseNotPopulated.prototype = Object.create(Error.prototype);
|
||||
DatabaseNotPopulated.prototype.name = 'DatabaseNotPopulated';
|
||||
|
||||
module.exports = DatabaseNotPopulated;
|
|
@ -1,13 +0,0 @@
|
|||
function DatabaseVersion(message, context, help) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 500;
|
||||
this.errorType = this.name;
|
||||
this.context = context;
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
DatabaseVersion.prototype = Object.create(Error.prototype);
|
||||
DatabaseVersion.prototype.name = 'DatabaseVersion';
|
||||
|
||||
module.exports = DatabaseVersion;
|
|
@ -1,16 +0,0 @@
|
|||
// # Email error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
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);
|
||||
EmailError.prototype.name = 'EmailError';
|
||||
|
||||
module.exports = EmailError;
|
|
@ -1,11 +0,0 @@
|
|||
function IncorrectUsage(message, context) {
|
||||
this.name = 'IncorrectUsage';
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 400;
|
||||
this.errorType = this.name;
|
||||
this.message = message;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
IncorrectUsage.prototype = Object.create(Error.prototype);
|
||||
module.exports = IncorrectUsage;
|
|
@ -1,41 +0,0 @@
|
|||
// # Errors
|
||||
/*jslint regexp: true */
|
||||
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');
|
||||
|
||||
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;
|
|
@ -1,16 +0,0 @@
|
|||
// # Internal Server Error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
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);
|
||||
InternalServerError.prototype.name = 'InternalServerError';
|
||||
|
||||
module.exports = InternalServerError;
|
|
@ -1,11 +0,0 @@
|
|||
function Maintenance(message) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 503;
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
Maintenance.prototype = Object.create(Error.prototype);
|
||||
Maintenance.prototype.name = 'Maintenance';
|
||||
|
||||
module.exports = Maintenance;
|
|
@ -1,14 +0,0 @@
|
|||
// # Not found error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function MethodNotAllowedError(message) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 405;
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
MethodNotAllowedError.prototype = Object.create(Error.prototype);
|
||||
MethodNotAllowedError.prototype.name = 'MethodNotAllowedError';
|
||||
|
||||
module.exports = MethodNotAllowedError;
|
|
@ -1,14 +0,0 @@
|
|||
// # No Permission Error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function NoPermissionError(message) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 403;
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
NoPermissionError.prototype = Object.create(Error.prototype);
|
||||
NoPermissionError.prototype.name = 'NoPermissionError';
|
||||
|
||||
module.exports = NoPermissionError;
|
|
@ -1,14 +0,0 @@
|
|||
// # Not found error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function NotFoundError(message) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 404;
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
NotFoundError.prototype = Object.create(Error.prototype);
|
||||
NotFoundError.prototype.name = 'NotFoundError';
|
||||
|
||||
module.exports = NotFoundError;
|
|
@ -1,14 +0,0 @@
|
|||
// # Request Entity Too Large Error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function RequestEntityTooLargeError(message) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 413;
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
RequestEntityTooLargeError.prototype = Object.create(Error.prototype);
|
||||
RequestEntityTooLargeError.prototype.name = 'RequestEntityTooLargeError';
|
||||
|
||||
module.exports = RequestEntityTooLargeError;
|
|
@ -1,18 +0,0 @@
|
|||
// # Theme Validation Error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function ThemeValidationError(message, details) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 422;
|
||||
if (details) {
|
||||
this.errorDetails = details;
|
||||
}
|
||||
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
ThemeValidationError.prototype = Object.create(Error.prototype);
|
||||
ThemeValidationError.prototype.name = 'ThemeValidationError';
|
||||
|
||||
module.exports = ThemeValidationError;
|
|
@ -1,14 +0,0 @@
|
|||
// # Token Revocation ERror
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function TokenRevocationError(message) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 503;
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
TokenRevocationError.prototype = Object.create(Error.prototype);
|
||||
TokenRevocationError.prototype.name = 'TokenRevocationError';
|
||||
|
||||
module.exports = TokenRevocationError;
|
|
@ -1,16 +0,0 @@
|
|||
// # Too Many Requests Error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
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);
|
||||
TooManyRequestsError.prototype.name = 'TooManyRequestsError';
|
||||
|
||||
module.exports = TooManyRequestsError;
|
|
@ -1,16 +0,0 @@
|
|||
// # Unauthorized error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
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);
|
||||
UnauthorizedError.prototype.name = 'UnauthorizedError';
|
||||
|
||||
module.exports = UnauthorizedError;
|
|
@ -1,14 +0,0 @@
|
|||
// # Unsupported Media Type
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function UnsupportedMediaTypeError(message) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 415;
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
UnsupportedMediaTypeError.prototype = Object.create(Error.prototype);
|
||||
UnsupportedMediaTypeError.prototype.name = 'UnsupportedMediaTypeError';
|
||||
|
||||
module.exports = UnsupportedMediaTypeError;
|
|
@ -1,19 +0,0 @@
|
|||
// # Validation Error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function ValidationError(message, offendingProperty, context, help) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 422;
|
||||
if (offendingProperty) {
|
||||
this.property = offendingProperty;
|
||||
}
|
||||
this.errorType = this.name;
|
||||
this.context = context;
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
ValidationError.prototype = Object.create(Error.prototype);
|
||||
ValidationError.prototype.name = 'ValidationError';
|
||||
|
||||
module.exports = ValidationError;
|
|
@ -1,14 +0,0 @@
|
|||
// # Version mismatch error
|
||||
// Custom error class with status code and type prefilled.
|
||||
|
||||
function VersionMismatchError(message) {
|
||||
this.message = message;
|
||||
this.stack = new Error().stack;
|
||||
this.statusCode = 400;
|
||||
this.errorType = this.name;
|
||||
}
|
||||
|
||||
VersionMismatchError.prototype = Object.create(Error.prototype);
|
||||
VersionMismatchError.prototype.name = 'VersionMismatchError';
|
||||
|
||||
module.exports = VersionMismatchError;
|
|
@ -76,17 +76,17 @@ GhostServer.prototype.start = function (externalApp) {
|
|||
|
||||
self.httpServer.on('error', function (error) {
|
||||
if (error.errno === 'EADDRINUSE') {
|
||||
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')
|
||||
));
|
||||
logging.error(new errors.GhostError({
|
||||
message: i18n.t('errors.httpServer.addressInUse.error'),
|
||||
context: i18n.t('errors.httpServer.addressInUse.context', {port: config.get('server').port}),
|
||||
help: i18n.t('errors.httpServer.addressInUse.help')
|
||||
}));
|
||||
} else {
|
||||
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')
|
||||
));
|
||||
logging.error(new errors.GhostError({
|
||||
message: i18n.t('errors.httpServer.otherError.error', {errorNumber: error.errno}),
|
||||
context: i18n.t('errors.httpServer.otherError.context'),
|
||||
help: i18n.t('errors.httpServer.otherError.help')
|
||||
}));
|
||||
}
|
||||
|
||||
process.exit(-1);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
var _ = require('lodash'),
|
||||
hbs = require('express-hbs'),
|
||||
Promise = require('bluebird'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
api = require('../api'),
|
||||
jsonpath = require('jsonpath'),
|
||||
|
@ -144,17 +145,18 @@ get = function get(resource, options) {
|
|||
};
|
||||
|
||||
module.exports = function getWithLabs(resource, options) {
|
||||
var self = this,
|
||||
err;
|
||||
var self = this, err;
|
||||
|
||||
if (labs.isSet('publicAPI') === true) {
|
||||
// get helper is active
|
||||
return get.call(self, resource, options);
|
||||
} else {
|
||||
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'});
|
||||
err = new errors.GhostError({
|
||||
message: i18n.t('warnings.helpers.get.helperNotAvailable'),
|
||||
context: i18n.t('warnings.helpers.get.apiMustBeEnabled'),
|
||||
help: i18n.t('warnings.helpers.get.seeLink', {url: 'http://support.ghost.org/public-api-beta'})
|
||||
});
|
||||
|
||||
logging.error(err);
|
||||
|
||||
return Promise.resolve(function noGetHelper() {
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
var hbs = require('express-hbs'),
|
||||
Promise = require('bluebird'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
utils = require('./utils'),
|
||||
i18n = require('../i18n'),
|
||||
coreHelpers = {},
|
||||
var hbs = require('express-hbs'),
|
||||
Promise = require('bluebird'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
utils = require('./utils'),
|
||||
i18n = require('../i18n'),
|
||||
coreHelpers = {},
|
||||
registerHelpers;
|
||||
|
||||
if (!utils.isProduction) {
|
||||
hbs.handlebars.logger.level = 0;
|
||||
}
|
||||
|
||||
coreHelpers.asset = require('./asset');
|
||||
coreHelpers.author = require('./author');
|
||||
coreHelpers.body_class = require('./body_class');
|
||||
coreHelpers.content = require('./content');
|
||||
coreHelpers.date = require('./date');
|
||||
coreHelpers.encode = require('./encode');
|
||||
coreHelpers.excerpt = require('./excerpt');
|
||||
coreHelpers.asset = require('./asset');
|
||||
coreHelpers.author = require('./author');
|
||||
coreHelpers.body_class = require('./body_class');
|
||||
coreHelpers.content = require('./content');
|
||||
coreHelpers.date = require('./date');
|
||||
coreHelpers.encode = require('./encode');
|
||||
coreHelpers.excerpt = require('./excerpt');
|
||||
coreHelpers.facebook_url = require('./facebook_url');
|
||||
coreHelpers.foreach = require('./foreach');
|
||||
coreHelpers.get = require('./get');
|
||||
|
@ -48,7 +48,9 @@ coreHelpers.helperMissing = function (arg) {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
logging.error(new errors.InternalServerError(i18n.t('warnings.helpers.index.missingHelper', {arg: arg})));
|
||||
logging.error(new errors.GhostError({
|
||||
message: i18n.t('warnings.helpers.index.missingHelper', {arg: arg})
|
||||
}));
|
||||
};
|
||||
|
||||
// Register an async handlebars helper for a given handlebars instance
|
||||
|
@ -64,8 +66,10 @@ function registerAsyncHelper(hbs, name, fn) {
|
|||
Promise.resolve(fn.call(this, context, options)).then(function (result) {
|
||||
cb(result);
|
||||
}).catch(function (err) {
|
||||
logging.warn('registerAsyncThemeHelper: ' + name);
|
||||
throw err;
|
||||
throw new errors.IncorrectUsageError({
|
||||
err: err,
|
||||
context: 'registerAsyncThemeHelper: ' + name
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
// `{{navigation}}`
|
||||
// Outputs navigation menu of static urls
|
||||
|
||||
var _ = require('lodash'),
|
||||
hbs = require('express-hbs'),
|
||||
i18n = require('../i18n'),
|
||||
errors = require('../errors'),
|
||||
template = require('./template'),
|
||||
var _ = require('lodash'),
|
||||
hbs = require('express-hbs'),
|
||||
i18n = require('../i18n'),
|
||||
errors = require('../errors'),
|
||||
template = require('./template'),
|
||||
navigation;
|
||||
|
||||
navigation = function (options) {
|
||||
|
@ -18,13 +18,17 @@ navigation = function (options) {
|
|||
data;
|
||||
|
||||
if (!_.isObject(navigationData) || _.isFunction(navigationData)) {
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.navigation.invalidData'));
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.navigation.invalidData')
|
||||
});
|
||||
}
|
||||
|
||||
if (navigationData.filter(function (e) {
|
||||
return (_.isUndefined(e.label) || _.isUndefined(e.url));
|
||||
}).length > 0) {
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.navigation.valuesMustBeDefined'));
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.navigation.valuesMustBeDefined')
|
||||
});
|
||||
}
|
||||
|
||||
// check for non-null string values
|
||||
|
@ -32,7 +36,9 @@ navigation = function (options) {
|
|||
return ((!_.isNull(e.label) && !_.isString(e.label)) ||
|
||||
(!_.isNull(e.url) && !_.isString(e.url)));
|
||||
}).length > 0) {
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.navigation.valuesMustBeString'));
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.navigation.valuesMustBeString')
|
||||
});
|
||||
}
|
||||
|
||||
function _slugify(label) {
|
||||
|
|
|
@ -4,29 +4,35 @@
|
|||
|
||||
var _ = require('lodash'),
|
||||
errors = require('../errors'),
|
||||
template = require('./template'),
|
||||
i18n = require('../i18n'),
|
||||
template = require('./template'),
|
||||
pagination;
|
||||
|
||||
pagination = function (options) {
|
||||
/*jshint unused:false*/
|
||||
if (!_.isObject(this.pagination) || _.isFunction(this.pagination)) {
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.pagination.invalidData'));
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.pagination.invalidData')
|
||||
});
|
||||
}
|
||||
|
||||
if (_.isUndefined(this.pagination.page) || _.isUndefined(this.pagination.pages) ||
|
||||
_.isUndefined(this.pagination.total) || _.isUndefined(this.pagination.limit)) {
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.pagination.valuesMustBeDefined'));
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.pagination.valuesMustBeDefined')
|
||||
});
|
||||
}
|
||||
|
||||
if ((!_.isNull(this.pagination.next) && !_.isNumber(this.pagination.next)) ||
|
||||
(!_.isNull(this.pagination.prev) && !_.isNumber(this.pagination.prev))) {
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.pagination.nextPrevValuesMustBeNumeric'));
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.pagination.nextPrevValuesMustBeNumeric')
|
||||
});
|
||||
}
|
||||
|
||||
if (!_.isNumber(this.pagination.page) || !_.isNumber(this.pagination.pages) ||
|
||||
!_.isNumber(this.pagination.total) || !_.isNumber(this.pagination.limit)) {
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.pagination.valuesMustBeNumeric'));
|
||||
throw new errors.IncorrectUsageError({message: i18n.t('warnings.helpers.pagination.valuesMustBeNumeric')});
|
||||
}
|
||||
|
||||
var data = _.merge({}, this.pagination);
|
||||
|
|
|
@ -10,14 +10,16 @@
|
|||
|
||||
var hbs = require('express-hbs'),
|
||||
errors = require('../errors'),
|
||||
_ = require('lodash'),
|
||||
i18n = require('../i18n'),
|
||||
_ = require('lodash'),
|
||||
plural;
|
||||
|
||||
plural = function (number, options) {
|
||||
if (_.isUndefined(options.hash) || _.isUndefined(options.hash.empty) ||
|
||||
_.isUndefined(options.hash.singular) || _.isUndefined(options.hash.plural)) {
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.plural.valuesMustBeDefined'));
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.plural.valuesMustBeDefined')
|
||||
});
|
||||
}
|
||||
|
||||
if (number === 0) {
|
||||
|
|
|
@ -11,7 +11,9 @@ templates.execute = function (name, context, options) {
|
|||
var partial = hbs.handlebars.partials[name];
|
||||
|
||||
if (partial === undefined) {
|
||||
throw new errors.IncorrectUsage(i18n.t('warnings.helpers.template.templateNotFound', {name: name}));
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.template.templateNotFound', {name: name})
|
||||
});
|
||||
}
|
||||
|
||||
// If the partial view is not compiled, it compiles and saves in handlebars
|
||||
|
|
|
@ -108,8 +108,10 @@ function init(options) {
|
|||
);
|
||||
}).then(function () {
|
||||
debug('Apps, XMLRPC, Slack done');
|
||||
|
||||
// Get reference to an express app instance.
|
||||
parentApp = express();
|
||||
|
||||
// ## Middleware and Routing
|
||||
middleware(parentApp);
|
||||
debug('Express done');
|
||||
|
|
|
@ -6,12 +6,9 @@ function checkVersionMatch(req, res, next) {
|
|||
currentVersion = res.locals.safeVersion;
|
||||
|
||||
if (requestVersion && requestVersion !== currentVersion) {
|
||||
return next(new errors.VersionMismatchError(
|
||||
i18n.t(
|
||||
'errors.middleware.api.versionMismatch',
|
||||
{requestVersion: requestVersion, currentVersion: currentVersion}
|
||||
)
|
||||
));
|
||||
return next(new errors.VersionMismatchError({
|
||||
message: i18n.t('errors.middleware.api.versionMismatch', {requestVersion: requestVersion, currentVersion: currentVersion})
|
||||
}));
|
||||
}
|
||||
|
||||
next();
|
||||
|
|
|
@ -2,6 +2,7 @@ var _ = require('lodash'),
|
|||
path = require('path'),
|
||||
hbs = require('express-hbs'),
|
||||
config = require('../config'),
|
||||
errors = require('../errors'),
|
||||
i18n = require('../i18n'),
|
||||
_private = {};
|
||||
|
||||
|
@ -88,6 +89,12 @@ module.exports = function errorHandler(err, req, res, next) {
|
|||
err = err[0];
|
||||
}
|
||||
|
||||
if (!(err instanceof errors.GhostError)) {
|
||||
err = new errors.GhostError({
|
||||
err: err
|
||||
});
|
||||
}
|
||||
|
||||
req.err = err;
|
||||
res.statusCode = err.statusCode;
|
||||
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
var debug = require('debug')('ghost:middleware'),
|
||||
bodyParser = require('body-parser'),
|
||||
compress = require('compression'),
|
||||
config = require('../config'),
|
||||
errors = require('../errors'),
|
||||
express = require('express'),
|
||||
hbs = require('express-hbs'),
|
||||
path = require('path'),
|
||||
routes = require('../routes'),
|
||||
netjet = require('netjet'),
|
||||
multer = require('multer'),
|
||||
tmpdir = require('os').tmpdir,
|
||||
serveStatic = require('express').static,
|
||||
slashes = require('connect-slashes'),
|
||||
routes = require('../routes'),
|
||||
config = require('../config'),
|
||||
storage = require('../storage'),
|
||||
logging = require('../logging'),
|
||||
errors = require('../errors'),
|
||||
i18n = require('../i18n'),
|
||||
utils = require('../utils'),
|
||||
sitemapHandler = require('../data/xml/sitemap/handler'),
|
||||
multer = require('multer'),
|
||||
tmpdir = require('os').tmpdir,
|
||||
cacheControl = require('./cache-control'),
|
||||
checkSSL = require('./check-ssl'),
|
||||
decideIsAdmin = require('./decide-is-admin'),
|
||||
|
@ -30,7 +31,6 @@ var debug = require('debug')('ghost:middleware'),
|
|||
versionMatch = require('./api/version-match'),
|
||||
cors = require('./cors'),
|
||||
validation = require('./validation'),
|
||||
netjet = require('netjet'),
|
||||
labs = require('./labs'),
|
||||
helpers = require('../helpers'),
|
||||
middleware,
|
||||
|
@ -52,6 +52,7 @@ middleware = {
|
|||
|
||||
setupMiddleware = function setupMiddleware(blogApp) {
|
||||
debug('Middleware start');
|
||||
|
||||
var corePath = config.get('paths').corePath,
|
||||
adminApp = express(),
|
||||
adminHbs = hbs.create();
|
||||
|
@ -123,9 +124,11 @@ setupMiddleware = function setupMiddleware(blogApp) {
|
|||
path.join(corePath, '/shared'),
|
||||
{maxAge: utils.ONE_HOUR_MS, fallthrough: false}
|
||||
));
|
||||
|
||||
blogApp.use('/content/images', storage.getStorage().serve());
|
||||
|
||||
debug('Static content done');
|
||||
|
||||
// First determine whether we're serving admin or theme content
|
||||
blogApp.use(decideIsAdmin);
|
||||
blogApp.use(themeHandler.updateActiveTheme);
|
||||
|
@ -211,7 +214,7 @@ setupMiddleware = function setupMiddleware(blogApp) {
|
|||
|
||||
// ### Error handlers
|
||||
blogApp.use(function pageNotFound(req, res, next) {
|
||||
next(new errors.NotFoundError(i18n.t('errors.errors.pageNotFound')));
|
||||
next(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
|
||||
});
|
||||
|
||||
blogApp.use(errorHandler);
|
||||
|
|
|
@ -4,9 +4,7 @@ var config = require('../config'),
|
|||
|
||||
module.exports = function maintenance(req, res, next) {
|
||||
if (config.get('maintenance').enabled) {
|
||||
return next(new errors.Maintenance(
|
||||
i18n.t('errors.general.maintenance')
|
||||
));
|
||||
return next(new errors.MaintenanceError({message: i18n.t('errors.general.maintenance')}));
|
||||
}
|
||||
|
||||
next();
|
||||
|
|
|
@ -30,7 +30,7 @@ spamPrevention = {
|
|||
} else if (req.body.grant_type === 'refresh_token') {
|
||||
return next();
|
||||
} else {
|
||||
return next(new errors.BadRequestError(i18n.t('errors.middleware.spamprevention.noUsername')));
|
||||
return next(new errors.BadRequestError({message: i18n.t('errors.middleware.spamprevention.noUsername')}));
|
||||
}
|
||||
|
||||
// filter entries that are older than rateSigninPeriod
|
||||
|
@ -43,11 +43,11 @@ spamPrevention = {
|
|||
deniedRateLimit = (ipCount[remoteAddress] > rateSigninAttempts);
|
||||
|
||||
if (deniedRateLimit) {
|
||||
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')
|
||||
));
|
||||
return next(new errors.TooManyRequestsError({
|
||||
message: i18n.t('errors.middleware.spamprevention.tooManyAttempts') + rateSigninPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater'),
|
||||
context: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.error', {rateSigninAttempts: rateSigninAttempts, rateSigninPeriod: rateSigninPeriod}),
|
||||
help: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
|
||||
}));
|
||||
}
|
||||
next();
|
||||
},
|
||||
|
@ -74,7 +74,7 @@ spamPrevention = {
|
|||
forgottenSecurity.push({ip: remoteAddress, time: currentTime, email: email, count: 0});
|
||||
}
|
||||
} else {
|
||||
return next(new errors.BadRequestError(i18n.t('errors.middleware.spamprevention.noEmail')));
|
||||
return next(new errors.BadRequestError({message: i18n.t('errors.middleware.spamprevention.noEmail')}));
|
||||
}
|
||||
|
||||
// filter entries that are older than rateForgottenPeriod
|
||||
|
@ -91,19 +91,22 @@ spamPrevention = {
|
|||
}
|
||||
|
||||
if (deniedEmailRateLimit) {
|
||||
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')
|
||||
));
|
||||
return next(new errors.TooManyRequestsError({
|
||||
message: i18n.t('errors.middleware.spamprevention.tooManyAttempts') + rateForgottenPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater'),
|
||||
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.error', {
|
||||
rfa: rateForgottenAttempts,
|
||||
rfp: rateForgottenPeriod
|
||||
}),
|
||||
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.context')
|
||||
}));
|
||||
}
|
||||
|
||||
if (deniedRateLimit) {
|
||||
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')
|
||||
));
|
||||
return next(new errors.TooManyRequestsError({
|
||||
message: i18n.t('errors.middleware.spamprevention.tooManyAttempts') + rateForgottenPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater'),
|
||||
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error', {rfa: rateForgottenAttempts, rfp: rateForgottenPeriod}),
|
||||
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
|
||||
}));
|
||||
}
|
||||
|
||||
next();
|
||||
|
|
|
@ -6,7 +6,7 @@ var _ = require('lodash'),
|
|||
config = require('../config'),
|
||||
logging = require('../logging'),
|
||||
errors = require('../errors'),
|
||||
i18n = require('../i18n'),
|
||||
i18n = require('../i18n'),
|
||||
themeHandler;
|
||||
|
||||
themeHandler = {
|
||||
|
@ -100,7 +100,9 @@ themeHandler = {
|
|||
// Change theme
|
||||
if (!config.get('paths').availableThemes.hasOwnProperty(activeTheme.value)) {
|
||||
if (!res.isAdmin) {
|
||||
return next(new errors.InternalServerError(i18n.t('errors.middleware.themehandler.missingTheme', {theme: activeTheme.value})));
|
||||
return next(new errors.NotFoundError({
|
||||
message: 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
|
||||
|
@ -108,7 +110,6 @@ themeHandler = {
|
|||
// processing can continue.
|
||||
blogApp.engine('hbs', hbs.express3());
|
||||
logging.warn(i18n.t('errors.middleware.themehandler.missingTheme', {theme: activeTheme.value}));
|
||||
|
||||
return next();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -17,12 +17,12 @@ module.exports = function upload(options) {
|
|||
|
||||
// Check if a file was provided
|
||||
if (!apiUtils.checkFileExists(req.file)) {
|
||||
return next(new errors.NoPermissionError(i18n.t('errors.api.' + type + '.missingFile')));
|
||||
return next(new errors.NoPermissionError({message: i18n.t('errors.api.' + type + '.missingFile')}));
|
||||
}
|
||||
|
||||
// Check if the file is valid
|
||||
if (!apiUtils.checkFileIsValid(req.file, contentTypes, extensions)) {
|
||||
return next(new errors.UnsupportedMediaTypeError(i18n.t('errors.api.' + type + '.invalidFile', {extensions: extensions})));
|
||||
return next(new errors.UnsupportedMediaTypeError({message: i18n.t('errors.api.' + type + '.invalidFile', {extensions: extensions})}));
|
||||
}
|
||||
|
||||
next();
|
||||
|
|
|
@ -173,7 +173,10 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
} else if (options.context && options.context.external) {
|
||||
return 0;
|
||||
} else {
|
||||
throw new errors.IncorrectUsage(i18n.t('errors.models.base.index.missingContext'));
|
||||
throw new errors.NotFoundError({
|
||||
message: i18n.t('errors.models.base.index.missingContext'),
|
||||
level: 'critical'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +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 +12,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) {
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({err: err, level: 'critical'}));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -61,11 +62,16 @@ events.on('settings.activeTimezone.edited', function (settingModel) {
|
|||
};
|
||||
})).each(function (result) {
|
||||
if (!result.isFulfilled()) {
|
||||
logging.error(result.reason());
|
||||
logging.error(new errors.GhostError({
|
||||
err: result.reason()
|
||||
}));
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(function (err) {
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
level: 'critical'
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -57,7 +57,7 @@ Basetoken = ghostBookshelf.Model.extend({
|
|||
});
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.models.base.token.noUserFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.models.base.token.noUserFound')}));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,13 +24,13 @@ filterUtils = {
|
|||
custom = _.map(custom, function (arg) {
|
||||
return _.isString(arg) ? gql.parse(arg) : arg;
|
||||
});
|
||||
} catch (error) {
|
||||
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'})
|
||||
);
|
||||
} catch (err) {
|
||||
throw new errors.ValidationError({
|
||||
err: err,
|
||||
property: 'filter',
|
||||
context: i18n.t('errors.models.plugins.filter.errorParsing'),
|
||||
help: i18n.t('errors.models.plugins.filter.forInformationRead', {url: 'http://api.ghost.org/docs/filter'})
|
||||
});
|
||||
}
|
||||
|
||||
// Merge custom filter options into a single set of statements
|
||||
|
|
|
@ -5,7 +5,6 @@ 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,
|
||||
|
@ -165,28 +164,28 @@ Post = ghostBookshelf.Model.extend({
|
|||
// CASE: disallow published -> scheduled
|
||||
// @TODO: remove when we have versioning based on updated_at
|
||||
if (newStatus !== olderStatus && newStatus === 'scheduled' && olderStatus === 'published') {
|
||||
return Promise.reject(new errors.ValidationError(
|
||||
i18n.t('errors.models.post.isAlreadyPublished', {key: 'status'})
|
||||
));
|
||||
return Promise.reject(new errors.ValidationError({
|
||||
message: i18n.t('errors.models.post.isAlreadyPublished', {key: 'status'})
|
||||
}));
|
||||
}
|
||||
|
||||
// CASE: both page and post can get scheduled
|
||||
if (newStatus === 'scheduled') {
|
||||
if (!publishedAt) {
|
||||
return Promise.reject(new errors.ValidationError(
|
||||
i18n.t('errors.models.post.valueCannotBeBlank', {key: 'published_at'})
|
||||
));
|
||||
return Promise.reject(new errors.ValidationError({
|
||||
message: i18n.t('errors.models.post.valueCannotBeBlank', {key: 'published_at'})
|
||||
}));
|
||||
} else if (!moment(publishedAt).isValid()) {
|
||||
return Promise.reject(new errors.ValidationError(
|
||||
i18n.t('errors.models.post.valueCannotBeBlank', {key: 'published_at'})
|
||||
));
|
||||
return Promise.reject(new errors.ValidationError({
|
||||
message: i18n.t('errors.models.post.valueCannotBeBlank', {key: 'published_at'})
|
||||
}));
|
||||
// CASE: to schedule/reschedule a post, a minimum diff of x minutes is needed (default configured is 2minutes)
|
||||
} else if (publishedAtHasChanged && moment(publishedAt).isBefore(moment().add(config.get('times').cannotScheduleAPostBeforeInMinutes, 'minutes'))) {
|
||||
return Promise.reject(new errors.ValidationError(
|
||||
i18n.t('errors.models.post.expectedPublishedAtInFuture', {
|
||||
return Promise.reject(new errors.ValidationError({
|
||||
message: i18n.t('errors.models.post.expectedPublishedAtInFuture', {
|
||||
cannotScheduleAPostBeforeInMinutes: config.get('times').cannotScheduleAPostBeforeInMinutes
|
||||
})
|
||||
));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,16 +375,12 @@ Post = ghostBookshelf.Model.extend({
|
|||
return doTagUpdates(options);
|
||||
}).then(function () {
|
||||
// Don't do anything, the transaction processed ok
|
||||
}).catch(function failure(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
|
||||
));
|
||||
}).catch(function failure(err) {
|
||||
return Promise.reject(new errors.GhostError({
|
||||
err: err,
|
||||
context: i18n.t('errors.models.post.tagUpdates.error'),
|
||||
help: i18n.t('errors.models.post.tagUpdates.help')
|
||||
}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -673,15 +668,15 @@ Post = ghostBookshelf.Model.extend({
|
|||
options = this.filterOptions(options, 'destroyByAuthor');
|
||||
|
||||
if (!authorId) {
|
||||
throw new errors.NotFoundError(i18n.t('errors.models.post.noUserFound'));
|
||||
throw new errors.NotFoundError({message: i18n.t('errors.models.post.noUserFound')});
|
||||
}
|
||||
|
||||
return postCollection
|
||||
.query('where', 'author_id', '=', authorId)
|
||||
.fetch(options)
|
||||
.call('invokeThen', 'destroy', options)
|
||||
.catch(function (error) {
|
||||
throw new errors.InternalServerError(error.message || error);
|
||||
.catch(function (err) {
|
||||
return Promise.reject(new errors.GhostError({err: err}));
|
||||
});
|
||||
}),
|
||||
|
||||
|
@ -714,7 +709,7 @@ Post = ghostBookshelf.Model.extend({
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.models.post.notEnoughPermission')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.models.post.notEnoughPermission')}));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ Role = ghostBookshelf.Model.extend({
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.models.role.notEnoughPermission')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.models.role.notEnoughPermission')}));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ Settings = ghostBookshelf.Model.extend({
|
|||
// Accept an array of models as input
|
||||
if (item.toJSON) { item = item.toJSON(); }
|
||||
if (!(_.isString(item.key) && item.key.length > 0)) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.settings.valueCannotBeBlank')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.settings.valueCannotBeBlank')}));
|
||||
}
|
||||
|
||||
item = self.filterData(item);
|
||||
|
@ -138,14 +138,14 @@ Settings = ghostBookshelf.Model.extend({
|
|||
return setting.save(saveData, options);
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.models.settings.unableToFindSetting', {key: item.key})));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.models.settings.unableToFindSetting', {key: item.key})}));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
populateDefault: function (key) {
|
||||
if (!getDefaultSettings()[key]) {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.models.settings.unableToFindDefaultSetting', {key: key})));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.models.settings.unableToFindDefaultSetting', {key: key})}));
|
||||
}
|
||||
|
||||
return this.findOne({key: key}).then(function then(foundSetting) {
|
||||
|
|
|
@ -72,7 +72,7 @@ Subscriber = ghostBookshelf.Model.extend({
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.models.subscriber.notEnoughPermission')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.models.subscriber.notEnoughPermission')}));
|
||||
},
|
||||
|
||||
// TODO: This is a copy paste of models/user.js!
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
var _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
errors = require('../errors'),
|
||||
utils = require('../utils'),
|
||||
gravatar = require('../utils/gravatar'),
|
||||
bcrypt = require('bcryptjs'),
|
||||
ghostBookshelf = require('./base'),
|
||||
crypto = require('crypto'),
|
||||
validator = require('validator'),
|
||||
ghostBookshelf = require('./base'),
|
||||
errors = require('../errors'),
|
||||
logging = require('../logging'),
|
||||
utils = require('../utils'),
|
||||
gravatar = require('../utils/gravatar'),
|
||||
validation = require('../data/validation'),
|
||||
events = require('../events'),
|
||||
logging = require('../logging'),
|
||||
i18n = require('../i18n'),
|
||||
|
||||
bcryptGenSalt = Promise.promisify(bcrypt.genSalt),
|
||||
|
@ -122,7 +122,9 @@ User = ghostBookshelf.Model.extend({
|
|||
} else if (this.get('id')) {
|
||||
return this.get('id');
|
||||
} else {
|
||||
throw new errors.IncorrectUsage(i18n.t('errors.models.user.missingContext'));
|
||||
throw new errors.NotFoundError({
|
||||
message: i18n.t('errors.models.user.missingContext')
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -312,7 +314,7 @@ User = ghostBookshelf.Model.extend({
|
|||
|
||||
if (data.roles && data.roles.length > 1) {
|
||||
return Promise.reject(
|
||||
new errors.ValidationError(i18n.t('errors.models.user.onlyOneRolePerUserSupported'))
|
||||
new errors.ValidationError({message: i18n.t('errors.models.user.onlyOneRolePerUserSupported')})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -335,7 +337,7 @@ User = ghostBookshelf.Model.extend({
|
|||
}).then(function then(roleToAssign) {
|
||||
if (roleToAssign && roleToAssign.get('name') === 'Owner') {
|
||||
return Promise.reject(
|
||||
new errors.ValidationError(i18n.t('errors.models.user.methodDoesNotSupportOwnerRole'))
|
||||
new errors.ValidationError({message: i18n.t('errors.models.user.methodDoesNotSupportOwnerRole')})
|
||||
);
|
||||
} else {
|
||||
// assign all other roles
|
||||
|
@ -370,11 +372,11 @@ User = ghostBookshelf.Model.extend({
|
|||
|
||||
// check for too many roles
|
||||
if (data.roles && data.roles.length > 1) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.onlyOneRolePerUserSupported')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.onlyOneRolePerUserSupported')}));
|
||||
}
|
||||
|
||||
if (!validatePasswordLength(userData.password)) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.passwordDoesNotComplyLength')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.passwordDoesNotComplyLength')}));
|
||||
}
|
||||
|
||||
function getAuthorRole() {
|
||||
|
@ -422,7 +424,7 @@ User = ghostBookshelf.Model.extend({
|
|||
userData = this.filterData(data);
|
||||
|
||||
if (!validatePasswordLength(userData.password)) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.passwordDoesNotComplyLength')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.passwordDoesNotComplyLength')}));
|
||||
}
|
||||
|
||||
options = this.filterOptions(options, 'setup');
|
||||
|
@ -458,6 +460,7 @@ User = ghostBookshelf.Model.extend({
|
|||
if (_.isNumber(userModelOrId) || _.isString(userModelOrId)) {
|
||||
// Grab the original args without the first one
|
||||
origArgs = _.toArray(arguments).slice(1);
|
||||
|
||||
// Get the actual user model
|
||||
return this.findOne({id: userModelOrId, status: 'all'}, {include: ['roles']}).then(function then(foundUserModel) {
|
||||
// Build up the original args but substitute with actual model
|
||||
|
@ -491,7 +494,7 @@ User = ghostBookshelf.Model.extend({
|
|||
if (action === 'destroy') {
|
||||
// Owner cannot be deleted EVER
|
||||
if (loadedPermissions.user && userModel.hasRole('Owner')) {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.models.user.notEnoughPermission')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.models.user.notEnoughPermission')}));
|
||||
}
|
||||
|
||||
// Users with the role 'Editor' have complex permissions when the action === 'destroy'
|
||||
|
@ -508,7 +511,7 @@ User = ghostBookshelf.Model.extend({
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.models.user.notEnoughPermission')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.models.user.notEnoughPermission')}));
|
||||
},
|
||||
|
||||
setWarning: function setWarning(user, options) {
|
||||
|
@ -538,7 +541,7 @@ User = ghostBookshelf.Model.extend({
|
|||
s;
|
||||
return this.getByEmail(object.email).then(function then(user) {
|
||||
if (!user) {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.models.user.noUserWithEnteredEmailAddr')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.models.user.noUserWithEnteredEmailAddr')}));
|
||||
}
|
||||
|
||||
if (user.get('status') !== 'locked') {
|
||||
|
@ -547,22 +550,19 @@ User = ghostBookshelf.Model.extend({
|
|||
return Promise.resolve(self.setWarning(user, {validate: false})).then(function then(remaining) {
|
||||
if (remaining === 0) {
|
||||
// If remaining attempts = 0, the account has been locked, so show a locked account message
|
||||
return Promise.reject(new errors.NoPermissionError(
|
||||
i18n.t('errors.models.user.accountLocked')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.models.user.accountLocked')}));
|
||||
}
|
||||
|
||||
s = (remaining > 1) ? 's' : '';
|
||||
return Promise.reject(new errors.UnauthorizedError(i18n.t('errors.models.user.incorrectPasswordAttempts', {remaining: remaining, s: s})));
|
||||
return Promise.reject(new errors.UnauthorizedError({message: 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(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.
|
||||
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.reject(new errors.UnauthorizedError({
|
||||
err: err,
|
||||
context: i18n.t('errors.models.user.incorrectPassword'),
|
||||
help: i18n.t('errors.models.user.userUpdateError.help')
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -570,19 +570,20 @@ User = ghostBookshelf.Model.extend({
|
|||
.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.
|
||||
err.context = i18n.t('errors.models.user.userUpdateError.context');
|
||||
err.help = i18n.t('errors.models.user.userUpdateError.help');
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
context: i18n.t('errors.models.user.userUpdateError.context'),
|
||||
help: i18n.t('errors.models.user.userUpdateError.help')
|
||||
}));
|
||||
|
||||
return user;
|
||||
});
|
||||
});
|
||||
}
|
||||
return Promise.reject(new errors.NoPermissionError(
|
||||
i18n.t('errors.models.user.accountLocked')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.models.user.accountLocked')}));
|
||||
}, function handleError(error) {
|
||||
if (error.message === 'NotFound' || error.message === 'EmptyResponse') {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.models.user.noUserWithEnteredEmailAddr')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.models.user.noUserWithEnteredEmailAddr')}));
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
|
@ -604,17 +605,17 @@ User = ghostBookshelf.Model.extend({
|
|||
|
||||
// If the two passwords do not match
|
||||
if (newPassword !== ne2Password) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.newPasswordsDoNotMatch')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.newPasswordsDoNotMatch')}));
|
||||
}
|
||||
|
||||
// If the old password is empty when changing current user's password
|
||||
if (userId === options.context.user && _.isEmpty(oldPassword)) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.passwordRequiredForOperation')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.passwordRequiredForOperation')}));
|
||||
}
|
||||
|
||||
// If password is not complex enough
|
||||
if (!validatePasswordLength(newPassword)) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.passwordDoesNotComplyLength')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.passwordDoesNotComplyLength')}));
|
||||
}
|
||||
|
||||
return self.forge({id: userId}).fetch({require: true}).then(function then(_user) {
|
||||
|
@ -627,7 +628,7 @@ User = ghostBookshelf.Model.extend({
|
|||
return true;
|
||||
}).then(function then(matched) {
|
||||
if (!matched) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.incorrectPassword')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.incorrectPassword')}));
|
||||
}
|
||||
|
||||
return generatePasswordHash(newPassword);
|
||||
|
@ -639,7 +640,7 @@ User = ghostBookshelf.Model.extend({
|
|||
generateResetToken: function generateResetToken(email, expires, dbHash) {
|
||||
return this.getByEmail(email).then(function then(foundUser) {
|
||||
if (!foundUser) {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.models.user.noUserWithEnteredEmailAddr')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.models.user.noUserWithEnteredEmailAddr')}));
|
||||
}
|
||||
|
||||
var hash = crypto.createHash('sha256'),
|
||||
|
@ -669,25 +670,25 @@ User = ghostBookshelf.Model.extend({
|
|||
|
||||
// Check if invalid structure
|
||||
if (!parts || parts.length !== 3) {
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.models.user.invalidTokenStructure')));
|
||||
return Promise.reject(new errors.BadRequestError({message: i18n.t('errors.models.user.invalidTokenStructure')}));
|
||||
}
|
||||
|
||||
expires = parseInt(parts[0], 10);
|
||||
email = parts[1];
|
||||
|
||||
if (isNaN(expires)) {
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.models.user.invalidTokenExpiration')));
|
||||
return Promise.reject(new errors.BadRequestError({message: i18n.t('errors.models.user.invalidTokenExpiration')}));
|
||||
}
|
||||
|
||||
// Check if token is expired to prevent replay attacks
|
||||
if (expires < Date.now()) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.expiredToken')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.expiredToken')}));
|
||||
}
|
||||
|
||||
// to prevent brute force attempts to reset the password the combination of email+expires is only allowed for
|
||||
// 10 attempts
|
||||
if (tokenSecurity[email + '+' + expires] && tokenSecurity[email + '+' + expires].count >= 10) {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.models.user.tokenLocked')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.models.user.tokenLocked')}));
|
||||
}
|
||||
|
||||
return this.generateResetToken(email, expires, dbHash).then(function then(generatedToken) {
|
||||
|
@ -712,7 +713,7 @@ User = ghostBookshelf.Model.extend({
|
|||
tokenSecurity[email + '+' + expires] = {
|
||||
count: tokenSecurity[email + '+' + expires] ? tokenSecurity[email + '+' + expires].count + 1 : 1
|
||||
};
|
||||
return Promise.reject(new errors.BadRequestError(i18n.t('errors.models.user.invalidToken')));
|
||||
return Promise.reject(new errors.BadRequestError({message: i18n.t('errors.models.user.invalidToken')}));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -724,11 +725,11 @@ User = ghostBookshelf.Model.extend({
|
|||
dbHash = options.dbHash;
|
||||
|
||||
if (newPassword !== ne2Password) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.newPasswordsDoNotMatch')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.newPasswordsDoNotMatch')}));
|
||||
}
|
||||
|
||||
if (!validatePasswordLength(newPassword)) {
|
||||
return Promise.reject(new errors.ValidationError(i18n.t('errors.models.user.passwordDoesNotComplyLength')));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.passwordDoesNotComplyLength')}));
|
||||
}
|
||||
|
||||
// Validate the token; returns the email address from token
|
||||
|
@ -740,7 +741,7 @@ User = ghostBookshelf.Model.extend({
|
|||
);
|
||||
}).then(function then(results) {
|
||||
if (!results[0]) {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.models.user.userNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.models.user.userNotFound')}));
|
||||
}
|
||||
|
||||
// Update the user with the new password hash
|
||||
|
@ -764,7 +765,7 @@ User = ghostBookshelf.Model.extend({
|
|||
// check if user has the owner role
|
||||
var currentRoles = contextUser.toJSON(options).roles;
|
||||
if (!_.some(currentRoles, {id: ownerRole.id})) {
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.models.user.onlyOwnerCanTransferOwnerRole')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.models.user.onlyOwnerCanTransferOwnerRole')}));
|
||||
}
|
||||
|
||||
return Promise.join(ghostBookshelf.model('Role').findOne({name: 'Administrator'}),
|
||||
|
@ -775,7 +776,7 @@ User = ghostBookshelf.Model.extend({
|
|||
currentRoles = user.toJSON(options).roles;
|
||||
|
||||
if (!_.some(currentRoles, {id: adminRole.id})) {
|
||||
return Promise.reject(new errors.ValidationError('errors.models.user.onlyAdmCanBeAssignedOwnerRole'));
|
||||
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.onlyAdmCanBeAssignedOwnerRole')}));
|
||||
}
|
||||
|
||||
// convert owner to admin
|
||||
|
|
|
@ -11,7 +11,7 @@ effective = {
|
|||
.then(function (foundUser) {
|
||||
// CASE: {context: {user: id}} where the id is not in our database
|
||||
if (!foundUser) {
|
||||
return Promise.reject(new errors.NotFoundError(i18n.t('errors.models.user.userNotFound')));
|
||||
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.models.user.userNotFound')}));
|
||||
}
|
||||
|
||||
var seenPerms = {},
|
||||
|
|
|
@ -56,7 +56,7 @@ function parseContext(context) {
|
|||
}
|
||||
|
||||
function applyStatusRules(docName, method, opts) {
|
||||
var err = new errors.NoPermissionError(i18n.t('errors.permissions.applyStatusRules.error', {docName: docName}));
|
||||
var err = new errors.NoPermissionError({message: i18n.t('errors.permissions.applyStatusRules.error', {docName: docName})});
|
||||
|
||||
// Enforce status 'active' for users
|
||||
if (docName === 'users') {
|
||||
|
@ -202,7 +202,7 @@ CanThisResult.prototype.buildObjectTypeHandlers = function (objTypes, actType, c
|
|||
return;
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NoPermissionError(i18n.t('errors.permissions.noPermissionToAction')));
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.permissions.noPermissionToAction')}));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ var util = require('util'),
|
|||
moment = require('moment'),
|
||||
request = require('superagent'),
|
||||
SchedulingBase = require(__dirname + '/SchedulingBase'),
|
||||
errors = require(__dirname + '/../errors'),
|
||||
logging = require(__dirname + '/../logging');
|
||||
|
||||
/**
|
||||
|
@ -212,7 +213,10 @@ SchedulingDefault.prototype._pingUrl = function (object) {
|
|||
}, self.retryTimeoutInMs);
|
||||
}
|
||||
|
||||
logging.error(err);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
level: 'critical'
|
||||
}));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -42,11 +42,11 @@ exports.init = function init(options) {
|
|||
client = null;
|
||||
|
||||
if (!config) {
|
||||
return Promise.reject(new errors.IncorrectUsage('post-scheduling: no config was provided'));
|
||||
return Promise.reject(new errors.IncorrectUsageError({message: 'post-scheduling: no config was provided'}));
|
||||
}
|
||||
|
||||
if (!apiUrl) {
|
||||
return Promise.reject(new errors.IncorrectUsage('post-scheduling: no apiUrl was provided'));
|
||||
return Promise.reject(new errors.IncorrectUsageError({message: 'post-scheduling: no apiUrl was provided'}));
|
||||
}
|
||||
|
||||
return _private.loadClient()
|
||||
|
|
|
@ -12,7 +12,7 @@ exports.createAdapter = function (options) {
|
|||
contentPath = options.contentPath;
|
||||
|
||||
if (!activeAdapter) {
|
||||
return Promise.reject(new errors.IncorrectUsage('Please provide an active adapter.'));
|
||||
return Promise.reject(new errors.IncorrectUsageError({message: 'Please provide an active adapter.'}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,7 @@ exports.createAdapter = function (options) {
|
|||
adapter = new (require(activeAdapter))(options);
|
||||
} catch (err) {
|
||||
if (err.code !== 'MODULE_NOT_FOUND') {
|
||||
return Promise.reject(new errors.IncorrectUsage(err.message));
|
||||
return Promise.reject(new errors.IncorrectUsageError({err: err}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,11 +34,11 @@ exports.createAdapter = function (options) {
|
|||
} catch (err) {
|
||||
// CASE: only throw error if module does exist
|
||||
if (err.code !== 'MODULE_NOT_FOUND') {
|
||||
return Promise.reject(new errors.IncorrectUsage(err.message));
|
||||
return Promise.reject(new errors.IncorrectUsageError({err: err}));
|
||||
}
|
||||
// CASE: if module not found it can be an error within the adapter (cannot find bluebird for example)
|
||||
else if (err.code === 'MODULE_NOT_FOUND' && err.message.indexOf(contentPath + activeAdapter) === -1) {
|
||||
return Promise.reject(new errors.IncorrectUsage(err.message));
|
||||
return Promise.reject(new errors.IncorrectUsageError({err: err}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,22 +50,22 @@ exports.createAdapter = function (options) {
|
|||
} catch (err) {
|
||||
// CASE: only throw error if module does exist
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
return Promise.reject(new errors.IncorrectUsage('We cannot find your adapter in: ' + contentPath + ' or: ' + internalPath));
|
||||
return Promise.reject(new errors.IncorrectUsageError({message: 'We cannot find your adapter in: ' + contentPath + ' or: ' + internalPath}));
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.IncorrectUsage(err.message));
|
||||
return Promise.reject(new errors.IncorrectUsageError({err: err}));
|
||||
}
|
||||
|
||||
if (!(adapter instanceof SchedulingBase)) {
|
||||
return Promise.reject(new errors.IncorrectUsage('Your adapter does not inherit from the SchedulingBase.'));
|
||||
return Promise.reject(new errors.IncorrectUsageError({message: 'Your adapter does not inherit from the SchedulingBase.'}));
|
||||
}
|
||||
|
||||
if (!adapter.requiredFns) {
|
||||
return Promise.reject(new errors.IncorrectUsage('Your adapter does not provide the minimum required functions.'));
|
||||
return Promise.reject(new errors.IncorrectUsageError({message: 'Your adapter does not provide the minimum required functions.'}));
|
||||
}
|
||||
|
||||
if (_.xor(adapter.requiredFns, Object.keys(_.pick(Object.getPrototypeOf(adapter), adapter.requiredFns))).length) {
|
||||
return Promise.reject(new errors.IncorrectUsage('Your adapter does not provide the minimum required functions.'));
|
||||
return Promise.reject(new errors.IncorrectUsageError({message: 'Your adapter does not provide the minimum required functions.'}));
|
||||
}
|
||||
|
||||
return Promise.resolve(adapter);
|
||||
|
|
|
@ -23,7 +23,9 @@ function getStorage(type) {
|
|||
|
||||
// CASE: type does not exist
|
||||
if (!storageChoice) {
|
||||
throw new errors.IncorrectUsage('No adapter found for type: ' + type);
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: 'No adapter found for type: ' + type
|
||||
});
|
||||
}
|
||||
|
||||
// cache?
|
||||
|
@ -37,11 +39,11 @@ function getStorage(type) {
|
|||
} catch (err) {
|
||||
// CASE: only throw error if module does exist
|
||||
if (err.code !== 'MODULE_NOT_FOUND') {
|
||||
throw new errors.IncorrectUsage(err.message);
|
||||
throw new errors.IncorrectUsageError({err: err});
|
||||
}
|
||||
// CASE: if module not found it can be an error within the adapter (cannot find bluebird for example)
|
||||
else if (err.code === 'MODULE_NOT_FOUND' && err.message.indexOf(config.getContentPath('storage') + storageChoice) === -1) {
|
||||
throw new errors.IncorrectUsage(err.message);
|
||||
throw new errors.IncorrectUsageError({err: err});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,24 +52,27 @@ function getStorage(type) {
|
|||
storage[storageChoice] = storage[storageChoice] || require(config.get('paths').internalStoragePath + storageChoice);
|
||||
} catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
throw new errors.IncorrectUsage('We cannot find your adapter in: ' + config.getContentPath('storage') + ' or: ' + config.get('paths').internalStoragePath);
|
||||
throw new errors.IncorrectUsageError({
|
||||
err: err,
|
||||
context: 'We cannot find your adapter in: ' + config.getContentPath('storage') + ' or: ' + config.get('paths').internalStoragePath
|
||||
});
|
||||
} else {
|
||||
throw new errors.IncorrectUsage(err.message);
|
||||
throw new errors.IncorrectUsageError({err: err});
|
||||
}
|
||||
}
|
||||
|
||||
storage[storageChoice] = new storage[storageChoice](storageConfig);
|
||||
|
||||
if (!(storage[storageChoice] instanceof Base)) {
|
||||
throw new errors.IncorrectUsage('Your storage adapter does not inherit from the Storage Base.');
|
||||
throw new errors.IncorrectUsageError({message: 'Your storage adapter does not inherit from the Storage Base.'});
|
||||
}
|
||||
|
||||
if (!storage[storageChoice].requiredFns) {
|
||||
throw new errors.IncorrectUsage('Your storage adapter does not provide the minimum required functions.');
|
||||
throw new errors.IncorrectUsageError({message:'Your storage adapter does not provide the minimum required functions.'});
|
||||
}
|
||||
|
||||
if (_.xor(storage[storageChoice].requiredFns, Object.keys(_.pick(Object.getPrototypeOf(storage[storageChoice]), storage[storageChoice].requiredFns))).length) {
|
||||
throw new errors.IncorrectUsage('Your storage adapter does not provide the minimum required functions.');
|
||||
throw new errors.IncorrectUsageError({message:'Your storage adapter does not provide the minimum required functions.'});
|
||||
}
|
||||
|
||||
return storage[storageChoice];
|
||||
|
|
|
@ -105,10 +105,10 @@ LocalFileStore.prototype.serve = function (options) {
|
|||
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(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
|
||||
}
|
||||
|
||||
return next(err);
|
||||
return next(new errors.GhostError({err: err}));
|
||||
}
|
||||
|
||||
next();
|
||||
|
|
|
@ -175,7 +175,8 @@
|
|||
"general": {
|
||||
"maintenance": "Ghost is currently undergoing maintenance, please wait a moment then retry.",
|
||||
"moreInfo": "\nMore info: {info}",
|
||||
"requiredOnFuture": "This will be required in future. Please see {link}"
|
||||
"requiredOnFuture": "This will be required in future. Please see {link}",
|
||||
"internalError": "Something went wrong."
|
||||
},
|
||||
"httpServer": {
|
||||
"addressInUse": {
|
||||
|
|
|
@ -31,21 +31,24 @@ var crypto = require('crypto'),
|
|||
api = require('./api'),
|
||||
config = require('./config'),
|
||||
logging = require('./logging'),
|
||||
errors = require('./errors'),
|
||||
i18n = require('./i18n'),
|
||||
internal = {context: {internal: true}},
|
||||
allowedCheckEnvironments = ['development', 'production'],
|
||||
checkEndpoint = 'updates.ghost.org',
|
||||
currentVersion = config.get('ghostVersion');
|
||||
|
||||
function updateCheckError(error) {
|
||||
function updateCheckError(err) {
|
||||
api.settings.edit(
|
||||
{settings: [{key: 'nextUpdateCheck', value: Math.round(Date.now() / 1000 + 24 * 3600)}]},
|
||||
internal
|
||||
);
|
||||
|
||||
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);
|
||||
logging.error(new errors.GhostError({
|
||||
err: err,
|
||||
context: i18n.t('errors.update-check.checkingForUpdatesFailed.error'),
|
||||
help: i18n.t('errors.update-check.checkingForUpdatesFailed.help', {url: 'http://support.ghost.org'})
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,7 +2,7 @@ var should = require('should'),
|
|||
sinon = require('sinon'),
|
||||
errors = require('../../../../server/errors'),
|
||||
|
||||
// Stuff we are testing
|
||||
// Stuff we are testing
|
||||
handleError = require('../../../../server/controllers/frontend/error'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
@ -21,7 +21,7 @@ describe('handleError', function () {
|
|||
});
|
||||
|
||||
it('should call next with no args for 404 errors', function () {
|
||||
var notFoundError = new errors.NotFoundError('Something wasn\'t found');
|
||||
var notFoundError = new errors.NotFoundError({message: 'Something wasn\'t found'});
|
||||
handleError(next)(notFoundError);
|
||||
|
||||
next.calledOnce.should.be.true();
|
||||
|
@ -29,7 +29,8 @@ describe('handleError', function () {
|
|||
});
|
||||
|
||||
it('should call next with error for other errors', function () {
|
||||
var otherError = new errors.MethodNotAllowedError('Something wasn\'t allowed');
|
||||
var otherError = new Error();
|
||||
otherError.message = 'Something wasn\'t allowed';
|
||||
|
||||
handleError(next)(otherError);
|
||||
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
|
||||
// Stuff we're testing
|
||||
db = require('../../server/data/db'),
|
||||
errors = require('../../server/errors'),
|
||||
exporter = require('../../server/data/export'),
|
||||
schema = require('../../server/data/schema'),
|
||||
settings = require('../../server/api/settings'),
|
||||
|
||||
schemaTables = Object.keys(schema.tables),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Exporter', function () {
|
||||
|
@ -85,12 +81,14 @@ describe('Exporter', function () {
|
|||
queryMock.select.returns(new Promise.reject({}));
|
||||
|
||||
// Execute
|
||||
exporter.doExport().then(function () {
|
||||
done(new Error('expected error on export data'));
|
||||
}).catch(function (err) {
|
||||
(err instanceof errors.InternalServerError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
exporter.doExport()
|
||||
.then(function () {
|
||||
done(new Error('expected error for export'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
(err instanceof errors.DataExportError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ describe('Theme Handler', function () {
|
|||
describe('updateActiveTheme', function () {
|
||||
it('updates the active theme if changed', function (done) {
|
||||
var activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme');
|
||||
|
||||
sandbox.stub(api.settings, 'read').withArgs(sandbox.match.has('key', 'activeTheme')).returns(Promise.resolve({
|
||||
settings: [{
|
||||
key: 'activeKey',
|
||||
|
@ -176,8 +177,8 @@ describe('Theme Handler', function () {
|
|||
});
|
||||
|
||||
it('throws only warns if theme is missing for admin req', function (done) {
|
||||
var warnSpy = sandbox.spy(logging, 'warn'),
|
||||
activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme');
|
||||
var activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme'),
|
||||
loggingWarnStub = sandbox.spy(logging, 'warn');
|
||||
|
||||
sandbox.stub(api.settings, 'read').withArgs(sandbox.match.has('key', 'activeTheme')).returns(Promise.resolve({
|
||||
settings: [{
|
||||
|
@ -185,14 +186,15 @@ describe('Theme Handler', function () {
|
|||
value: 'rasper'
|
||||
}]
|
||||
}));
|
||||
|
||||
res.isAdmin = true;
|
||||
blogApp.set('activeTheme', 'not-casper');
|
||||
configUtils.set({paths: {availableThemes: {casper: {}}}});
|
||||
|
||||
themeHandler.updateActiveTheme(req, res, function () {
|
||||
activateThemeSpy.called.should.be.false();
|
||||
warnSpy.called.should.be.true();
|
||||
warnSpy.calledWith('The currently active theme "rasper" is missing.').should.be.true();
|
||||
loggingWarnStub.called.should.be.true();
|
||||
loggingWarnStub.calledWith('The currently active theme "rasper" is missing.').should.be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue