mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-27 22:49:56 -05:00
1f37ff6053
refs #5422 - we can support null titles after this PR if we want - user model: fix getAuthorRole - user model: support adding roles by name - we support this for roles as well, this makes it easier when importing related user roles (because usually roles already exists in the database and the related id's are wrong e.g. roles_users) - base model: support for null created_at or updated_at values - post or tag slugs are always safe strings - enable an import of a null slug, no need to crash or to cover this on import layer - add new DataImporter logic - uses a class inheritance mechanism to achieve an easier readability and maintenance - schema validation (happens on model layer) was ignored - allow to import unknown user id's (see https://github.com/TryGhost/Ghost/issues/8365) - most of the duplication handling happens on model layer (we can use the power of unique fields and errors from the database) - the import is splitted into three steps: - beforeImport --> prepares the data to import, sorts out relations (roles, tags), detects fields (for LTS) - doImport --> does the actual import - afterImport --> updates the data after successful import e.g. update all user reference fields e.g. published_by (compares the imported data with the current state of the database) - import images: markdown can be null - show error message when json handler can't parse file - do not request gravatar if email is null - return problems/warnings after successful import - optimise warnings in importer - do not return warnings for role duplications, no helpful information - error handler: return context information of error - we show the affected json entries as one line in the UI - show warning for: detected duplicated tag - schema validation: fix valueMustBeBoolean translation - remove context property from json parse error
172 lines
5.2 KiB
JavaScript
172 lines
5.2 KiB
JavaScript
var _ = require('lodash'),
|
|
hbs = require('express-hbs'),
|
|
config = require('../config'),
|
|
errors = require('../errors'),
|
|
i18n = require('../i18n'),
|
|
templates = require('../controllers/frontend/templates'),
|
|
escapeExpression = hbs.Utils.escapeExpression,
|
|
_private = {},
|
|
errorHandler = {};
|
|
|
|
/**
|
|
* This is a bare minimum setup, which allows us to render the error page
|
|
* It uses the {{asset}} helper, and nothing more
|
|
*/
|
|
_private.createHbsEngine = function createHbsEngine() {
|
|
var engine = hbs.create();
|
|
engine.registerHelper('asset', require('../helpers/asset'));
|
|
|
|
return engine.express4();
|
|
};
|
|
|
|
/**
|
|
* This function splits the stack into pieces, that are then rendered using the following handlebars code:
|
|
* ```
|
|
* {{#each stack}}
|
|
* <li>
|
|
* at
|
|
* {{#if function}}<em class="error-stack-function">{{function}}</em>{{/if}}
|
|
* <span class="error-stack-file">({{at}})</span>
|
|
* </li>
|
|
* {{/each}}
|
|
* ```
|
|
* @TODO revisit whether this is useful as part of #7491
|
|
*/
|
|
_private.parseStack = function parseStack(stack) {
|
|
if (!_.isString(stack)) {
|
|
return stack;
|
|
}
|
|
|
|
var stackRegex = /\s*at\s*(\w+)?\s*\(([^\)]+)\)\s*/i;
|
|
|
|
return (
|
|
stack
|
|
.split(/[\r\n]+/)
|
|
.slice(1)
|
|
.map(function (line) {
|
|
var parts = line.match(stackRegex);
|
|
if (!parts) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
function: parts[1],
|
|
at: parts[2]
|
|
};
|
|
})
|
|
.filter(function (line) {
|
|
return !!line;
|
|
})
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get an error ready to be shown the the user
|
|
*
|
|
* @TODO: support multiple errors within one single error, see https://github.com/TryGhost/Ghost/issues/7116#issuecomment-252231809
|
|
*/
|
|
_private.prepareError = function prepareError(err, req, res, next) {
|
|
if (_.isArray(err)) {
|
|
err = err[0];
|
|
}
|
|
|
|
if (!errors.utils.isIgnitionError(err)) {
|
|
// We need a special case for 404 errors
|
|
// @TODO look at adding this to the GhostError class
|
|
if (err.statusCode && err.statusCode === 404) {
|
|
err = new errors.NotFoundError({
|
|
err: err
|
|
});
|
|
} else {
|
|
err = new errors.GhostError({
|
|
err: err,
|
|
message: err.message,
|
|
statusCode: err.statusCode
|
|
});
|
|
}
|
|
}
|
|
|
|
// used for express logging middleware see core/server/app.js
|
|
req.err = err;
|
|
|
|
// alternative for res.status();
|
|
res.statusCode = err.statusCode;
|
|
|
|
// never cache errors
|
|
res.set({
|
|
'Cache-Control': 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'
|
|
});
|
|
|
|
next(err);
|
|
};
|
|
|
|
_private.JSONErrorRenderer = function JSONErrorRenderer(err, req, res, /*jshint unused:false */ next) {
|
|
// @TODO: jsonapi errors format (http://jsonapi.org/format/#error-objects)
|
|
res.json({
|
|
errors: [{
|
|
message: err.message,
|
|
context: err.context,
|
|
errorType: err.errorType,
|
|
errorDetails: err.errorDetails
|
|
}]
|
|
});
|
|
};
|
|
|
|
_private.HTMLErrorRenderer = function HTMLErrorRender(err, req, res, /*jshint unused:false */ next) {
|
|
var templateData = {
|
|
message: err.message,
|
|
code: err.statusCode
|
|
};
|
|
|
|
if (err.statusCode === 500 && config.get('printErrorStack')) {
|
|
templateData.stack = err.stack;
|
|
}
|
|
|
|
// It can be that something went wrong with the theme or otherwise loading handlebars
|
|
// This ensures that no matter what res.render will work here
|
|
if (_.isEmpty(req.app.engines)) {
|
|
req.app.engine('hbs', _private.createHbsEngine());
|
|
}
|
|
|
|
res.render(templates.error(err.statusCode), templateData, function renderResponse(err, html) {
|
|
if (!err) {
|
|
return res.send(html);
|
|
}
|
|
|
|
// And then try to explain things to the user...
|
|
// Cheat and output the error using handlebars escapeExpression
|
|
return res.status(500).send(
|
|
'<h1>' + i18n.t('errors.errors.oopsErrorTemplateHasError') + '</h1>' +
|
|
'<p>' + i18n.t('errors.errors.encounteredError') + '</p>' +
|
|
'<pre>' + escapeExpression(err.message || err) + '</pre>' +
|
|
'<br ><p>' + i18n.t('errors.errors.whilstTryingToRender') + '</p>' +
|
|
err.statusCode + ' ' + '<pre>' + escapeExpression(err.message || err) + '</pre>'
|
|
);
|
|
});
|
|
};
|
|
|
|
errorHandler.resourceNotFound = function resourceNotFound(req, res, next) {
|
|
// TODO, handle unknown resources & methods differently, so that we can also produce
|
|
// 405 Method Not Allowed
|
|
next(new errors.NotFoundError({message: i18n.t('errors.errors.resourceNotFound')}));
|
|
};
|
|
|
|
errorHandler.pageNotFound = function pageNotFound(req, res, next) {
|
|
next(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
|
|
};
|
|
|
|
errorHandler.handleJSONResponse = [
|
|
// Make sure the error can be served
|
|
_private.prepareError,
|
|
// Render the error using JSON format
|
|
_private.JSONErrorRenderer
|
|
];
|
|
|
|
errorHandler.handleHTMLResponse = [
|
|
// Make sure the error can be served
|
|
_private.prepareError,
|
|
// Render the error using HTML format
|
|
_private.HTMLErrorRenderer
|
|
];
|
|
|
|
module.exports = errorHandler;
|