ghost 4.23.0 → 4.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/app.js +12 -1
- package/core/boot.js +31 -17
- package/core/bridge.js +10 -10
- package/core/built/assets/{ghost-dark-42cf6e0c730578940ec069bda45aea41.css → ghost-dark-e7b57ab951512c5719aee89b16b9a448.css} +1 -1
- package/core/built/assets/{ghost.min-fcf6a0738421f86c47c55f20d00c5ba9.css → ghost.min-7f3603dbeb5ebf0ec09e207ae82fb4e3.css} +1 -1
- package/core/built/assets/{ghost.min-cccc107e881b74c7aaf1a73e1e5e0dee.js → ghost.min-d5595f9c71ebc534ccf9ac78483d357c.js} +84 -84
- package/core/built/assets/{vendor.min-c9002845b6c30ac978abdadde9f33d7c.js → vendor.min-1a84ac3ef74edf31c6e86810b45221cc.js} +148 -75
- package/core/frontend/services/card-assets/index.js +0 -12
- package/core/frontend/services/card-assets/service.js +22 -21
- package/core/frontend/src/cards/css/bookmark.css +7 -0
- package/core/frontend/src/cards/css/callout.css +1 -2
- package/core/frontend/src/cards/css/gallery.css +13 -3
- package/core/frontend/src/cards/css/toggle.css +20 -9
- package/core/frontend/web/middleware/serve-public-file.js +25 -8
- package/core/frontend/web/site.js +0 -3
- package/core/server/services/email-analytics/jobs/index.js +1 -1
- package/core/server/services/mega/template.js +5 -1
- package/core/server/services/redirects/api.js +18 -23
- package/core/server/services/redirects/index.js +18 -10
- package/core/server/services/redirects/utils.js +14 -0
- package/core/server/services/redirects/validation.js +10 -0
- package/core/server/services/route-settings/index.js +40 -17
- package/core/server/services/route-settings/route-settings.js +127 -114
- package/core/server/services/route-settings/settings-loader.js +14 -32
- package/core/server/services/themes/activation-bridge.js +3 -3
- package/core/server/services/url/LocalFileCache.js +75 -0
- package/core/server/services/url/UrlService.js +15 -47
- package/core/server/services/url/index.js +17 -4
- package/core/server/web/admin/app.js +2 -5
- package/core/server/web/admin/controller.js +35 -12
- package/core/server/web/admin/middleware/redirect-admin-urls.js +15 -0
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/api/canary/admin/app.js +0 -3
- package/core/server/web/api/canary/content/app.js +0 -3
- package/core/server/web/api/v2/admin/app.js +0 -3
- package/core/server/web/api/v2/content/app.js +0 -3
- package/core/server/web/api/v3/admin/app.js +0 -3
- package/core/server/web/api/v3/content/app.js +0 -3
- package/core/server/web/members/app.js +0 -3
- package/core/server/web/oauth/app.js +0 -4
- package/core/server/web/parent/app.js +17 -8
- package/core/server/web/shared/middleware/error-handler.js +70 -53
- package/core/server/web/shared/middleware/index.js +0 -4
- package/core/shared/labs.js +7 -2
- package/package.json +19 -18
- package/yarn.lock +267 -107
- package/core/server/web/shared/middleware/maintenance.js +0 -25
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('web:admin:controller');
|
|
2
|
+
const errors = require('@tryghost/errors');
|
|
3
|
+
const tpl = require('@tryghost/tpl');
|
|
2
4
|
const path = require('path');
|
|
3
5
|
const fs = require('fs');
|
|
4
6
|
const crypto = require('crypto');
|
|
5
7
|
const config = require('../../../shared/config');
|
|
6
8
|
const updateCheck = require('../../update-check');
|
|
7
9
|
|
|
10
|
+
const messages = {
|
|
11
|
+
templateError: {
|
|
12
|
+
message: 'Unable to find admin template file {templatePath}',
|
|
13
|
+
context: 'These template files are generated as part of the build process',
|
|
14
|
+
help: 'Please see {link}'
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
8
18
|
/**
|
|
9
19
|
* @description Admin controller to handle /ghost/ requests.
|
|
10
20
|
*
|
|
@@ -23,18 +33,31 @@ module.exports = function adminController(req, res) {
|
|
|
23
33
|
const templatePath = path.resolve(config.get('paths').adminViews, defaultTemplate);
|
|
24
34
|
const headers = {};
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
try {
|
|
37
|
+
// Generate our own ETag header
|
|
38
|
+
// `sendFile` by default uses filesize+lastmod date to generate an etag.
|
|
39
|
+
// That doesn't work for admin templates because the filesize doesn't change between versions
|
|
40
|
+
// and `npm pack` sets a fixed lastmod date for every file meaning the default etag never changes
|
|
41
|
+
const fileBuffer = fs.readFileSync(templatePath);
|
|
42
|
+
const hashSum = crypto.createHash('md5');
|
|
43
|
+
hashSum.update(fileBuffer);
|
|
44
|
+
headers.ETag = hashSum.digest('hex');
|
|
34
45
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
if (config.get('adminFrameProtection')) {
|
|
47
|
+
headers['X-Frame-Options'] = 'sameorigin';
|
|
48
|
+
}
|
|
38
49
|
|
|
39
|
-
|
|
50
|
+
res.sendFile(templatePath, {headers});
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error.code === 'ENOENT') {
|
|
53
|
+
throw new errors.IncorrectUsageError({
|
|
54
|
+
message: tpl(messages.templateError.message, {templatePath}),
|
|
55
|
+
context: tpl(messages.templateError.context),
|
|
56
|
+
help: tpl(messages.templateError.help, {link: 'https://ghost.org/docs/install/source/'}),
|
|
57
|
+
error: error
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
40
63
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const urlUtils = require('../../../../shared/url-utils');
|
|
2
|
+
|
|
3
|
+
function redirectAdminUrls(req, res, next) {
|
|
4
|
+
const subdir = urlUtils.getSubdir();
|
|
5
|
+
const ghostPathRegex = new RegExp(`^${subdir}/ghost/(.+)`);
|
|
6
|
+
const ghostPathMatch = req.originalUrl.match(ghostPathRegex);
|
|
7
|
+
|
|
8
|
+
if (ghostPathMatch) {
|
|
9
|
+
return res.redirect(urlUtils.urlJoin(urlUtils.urlFor('admin'), '#', ghostPathMatch[1]));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
next();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = redirectAdminUrls;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Ghost Admin</title>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%224.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%224.24%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%22%3Atrue%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
<link rel="stylesheet" href="assets/vendor.min-987af30228885bce50f05c4723fe6f53.css">
|
|
44
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
44
|
+
<link rel="stylesheet" href="assets/ghost.min-7f3603dbeb5ebf0ec09e207ae82fb4e3.css" title="light">
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
<script src="assets/vendor.min-
|
|
63
|
-
<script src="assets/ghost.min-
|
|
62
|
+
<script src="assets/vendor.min-1a84ac3ef74edf31c6e86810b45221cc.js"></script>
|
|
63
|
+
<script src="assets/ghost.min-d5595f9c71ebc534ccf9ac78483d357c.js"></script>
|
|
64
64
|
|
|
65
65
|
</body>
|
|
66
66
|
</html>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Ghost Admin</title>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%224.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%224.24%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%22%3Atrue%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
<link rel="stylesheet" href="assets/vendor.min-987af30228885bce50f05c4723fe6f53.css">
|
|
44
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
44
|
+
<link rel="stylesheet" href="assets/ghost.min-7f3603dbeb5ebf0ec09e207ae82fb4e3.css" title="light">
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
<script src="assets/vendor.min-
|
|
63
|
-
<script src="assets/ghost.min-
|
|
62
|
+
<script src="assets/vendor.min-1a84ac3ef74edf31c6e86810b45221cc.js"></script>
|
|
63
|
+
<script src="assets/ghost.min-d5595f9c71ebc534ccf9ac78483d357c.js"></script>
|
|
64
64
|
|
|
65
65
|
</body>
|
|
66
66
|
</html>
|
|
@@ -19,9 +19,6 @@ module.exports = function setupApiApp() {
|
|
|
19
19
|
// Query parsing
|
|
20
20
|
apiApp.use(boolParser());
|
|
21
21
|
|
|
22
|
-
// send 503 json response in case of maintenance
|
|
23
|
-
apiApp.use(shared.middleware.maintenance);
|
|
24
|
-
|
|
25
22
|
// Check version matches for API requests, depends on res.locals.safeVersion being set
|
|
26
23
|
// Therefore must come after themeHandler.ghostLocals, for now
|
|
27
24
|
apiApp.use(apiMw.versionMatch);
|
|
@@ -17,9 +17,6 @@ module.exports = function setupApiApp() {
|
|
|
17
17
|
// Query parsing
|
|
18
18
|
apiApp.use(boolParser());
|
|
19
19
|
|
|
20
|
-
// send 503 json response in case of maintenance
|
|
21
|
-
apiApp.use(shared.middleware.maintenance);
|
|
22
|
-
|
|
23
20
|
// API shouldn't be cached
|
|
24
21
|
apiApp.use(shared.middleware.cacheControl('private'));
|
|
25
22
|
|
|
@@ -19,9 +19,6 @@ module.exports = function setupApiApp() {
|
|
|
19
19
|
// Query parsing
|
|
20
20
|
apiApp.use(boolParser());
|
|
21
21
|
|
|
22
|
-
// send 503 json response in case of maintenance
|
|
23
|
-
apiApp.use(shared.middleware.maintenance);
|
|
24
|
-
|
|
25
22
|
// Check version matches for API requests, depends on res.locals.safeVersion being set
|
|
26
23
|
// Therefore must come after themeHandler.ghostLocals, for now
|
|
27
24
|
apiApp.use(apiMw.versionMatch);
|
|
@@ -17,9 +17,6 @@ module.exports = function setupApiApp() {
|
|
|
17
17
|
// Query parsing
|
|
18
18
|
apiApp.use(boolParser());
|
|
19
19
|
|
|
20
|
-
// send 503 json response in case of maintenance
|
|
21
|
-
apiApp.use(shared.middleware.maintenance);
|
|
22
|
-
|
|
23
20
|
// API shouldn't be cached
|
|
24
21
|
apiApp.use(shared.middleware.cacheControl('private'));
|
|
25
22
|
|
|
@@ -19,9 +19,6 @@ module.exports = function setupApiApp() {
|
|
|
19
19
|
// Query parsing
|
|
20
20
|
apiApp.use(boolParser());
|
|
21
21
|
|
|
22
|
-
// send 503 json response in case of maintenance
|
|
23
|
-
apiApp.use(shared.middleware.maintenance);
|
|
24
|
-
|
|
25
22
|
// Check version matches for API requests, depends on res.locals.safeVersion being set
|
|
26
23
|
// Therefore must come after themeHandler.ghostLocals, for now
|
|
27
24
|
apiApp.use(apiMw.versionMatch);
|
|
@@ -17,9 +17,6 @@ module.exports = function setupApiApp() {
|
|
|
17
17
|
// Query parsing
|
|
18
18
|
apiApp.use(boolParser());
|
|
19
19
|
|
|
20
|
-
// send 503 json response in case of maintenance
|
|
21
|
-
apiApp.use(shared.middleware.maintenance);
|
|
22
|
-
|
|
23
20
|
// API shouldn't be cached
|
|
24
21
|
apiApp.use(shared.middleware.cacheControl('private'));
|
|
25
22
|
|
|
@@ -13,9 +13,6 @@ module.exports = function setupMembersApp() {
|
|
|
13
13
|
debug('Members App setup start');
|
|
14
14
|
const membersApp = express('members');
|
|
15
15
|
|
|
16
|
-
// send 503 json response in case of maintenance
|
|
17
|
-
membersApp.use(shared.middleware.maintenance);
|
|
18
|
-
|
|
19
16
|
// Members API shouldn't be cached
|
|
20
17
|
membersApp.use(shared.middleware.cacheControl('private'));
|
|
21
18
|
|
|
@@ -2,7 +2,6 @@ const debug = require('@tryghost/debug')('web:oauth:app');
|
|
|
2
2
|
const {URL} = require('url');
|
|
3
3
|
const express = require('../../../shared/express');
|
|
4
4
|
const urlUtils = require('../../../shared/url-utils');
|
|
5
|
-
const shared = require('../shared');
|
|
6
5
|
const settingsCache = require('../../../shared/settings-cache');
|
|
7
6
|
const models = require('../../models');
|
|
8
7
|
const auth = require('../../services/auth');
|
|
@@ -24,9 +23,6 @@ module.exports = function setupOAuthApp() {
|
|
|
24
23
|
}
|
|
25
24
|
oauthApp.use(labsMiddleware);
|
|
26
25
|
|
|
27
|
-
// send 503 json response in case of maintenance
|
|
28
|
-
oauthApp.use(shared.middleware.maintenance);
|
|
29
|
-
|
|
30
26
|
/**
|
|
31
27
|
* Configure the passport.authenticate middleware
|
|
32
28
|
* We need to configure it on each request because clientId and secret
|
|
@@ -5,7 +5,13 @@ const compress = require('compression');
|
|
|
5
5
|
const mw = require('./middleware');
|
|
6
6
|
const vhost = require('@tryghost/vhost-middleware');
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* @param {Object} options
|
|
10
|
+
* @param {Boolean} [options.start]
|
|
11
|
+
* @param {Boolean} [options.backend]
|
|
12
|
+
* @param {Boolean} [options.frontend]
|
|
13
|
+
*/
|
|
14
|
+
module.exports = function setupParentApp({start, frontend = true, backend = true}) {
|
|
9
15
|
debug('ParentApp setup start');
|
|
10
16
|
const parentApp = express('parent');
|
|
11
17
|
|
|
@@ -26,14 +32,17 @@ module.exports = function setupParentApp(options = {}) {
|
|
|
26
32
|
|
|
27
33
|
// Mount the express apps on the parentApp
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const frontendApp = require('./frontend')(options);
|
|
35
|
-
parentApp.use(vhost(config.getFrontendMountPath(), frontendApp));
|
|
35
|
+
if (backend) {
|
|
36
|
+
debug('Mounting bakcend: ADMIN + API');
|
|
37
|
+
const backendApp = require('./backend')();
|
|
38
|
+
parentApp.use(vhost(config.getBackendMountPath(), backendApp));
|
|
39
|
+
}
|
|
36
40
|
|
|
41
|
+
if (frontend) {
|
|
42
|
+
debug('Mounting frontend: SITE + MEMBERS');
|
|
43
|
+
const frontendApp = require('./frontend')({start});
|
|
44
|
+
parentApp.use(vhost(config.getFrontendMountPath(), frontendApp));
|
|
45
|
+
}
|
|
37
46
|
debug('ParentApp setup end');
|
|
38
47
|
|
|
39
48
|
return parentApp;
|
|
@@ -60,6 +60,32 @@ _private.createHbsEngine = () => {
|
|
|
60
60
|
return engine.express4();
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
+
_private.updateStack = (err) => {
|
|
64
|
+
let stackbits = err.stack.split(/\n/g);
|
|
65
|
+
|
|
66
|
+
// We build this up backwards, so we always insert at position 1
|
|
67
|
+
|
|
68
|
+
if (process.env.NODE_ENV === 'production' || err.statusCode === 404) {
|
|
69
|
+
// In production mode, remove the stack trace
|
|
70
|
+
stackbits.splice(1, stackbits.length - 1);
|
|
71
|
+
} else {
|
|
72
|
+
// In dev mode, clearly mark the strack trace
|
|
73
|
+
stackbits.splice(1, 0, `Stack Trace:`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Add in our custom cotext and help methods
|
|
77
|
+
|
|
78
|
+
if (err.help) {
|
|
79
|
+
stackbits.splice(1, 0, `${err.help}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (err.context) {
|
|
83
|
+
stackbits.splice(1, 0, `${err.context}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return stackbits.join('\n');
|
|
87
|
+
};
|
|
88
|
+
|
|
63
89
|
/**
|
|
64
90
|
* Get an error ready to be shown the the user
|
|
65
91
|
*
|
|
@@ -79,14 +105,6 @@ _private.prepareError = (err, req, res, next) => {
|
|
|
79
105
|
err = new errors.NotFoundError({
|
|
80
106
|
err: err
|
|
81
107
|
});
|
|
82
|
-
} else if (err.stack.match(/node_modules\/handlebars\//)) {
|
|
83
|
-
// Temporary handling of theme errors from handlebars
|
|
84
|
-
// @TODO remove this when #10496 is solved properly
|
|
85
|
-
err = new errors.IncorrectUsageError({
|
|
86
|
-
err: err,
|
|
87
|
-
message: err.message,
|
|
88
|
-
statusCode: err.statusCode
|
|
89
|
-
});
|
|
90
108
|
} else {
|
|
91
109
|
err = new errors.GhostError({
|
|
92
110
|
err: err,
|
|
@@ -102,6 +120,8 @@ _private.prepareError = (err, req, res, next) => {
|
|
|
102
120
|
// alternative for res.status();
|
|
103
121
|
res.statusCode = err.statusCode;
|
|
104
122
|
|
|
123
|
+
err.stack = _private.updateStack(err);
|
|
124
|
+
|
|
105
125
|
// never cache errors
|
|
106
126
|
res.set({
|
|
107
127
|
'Cache-Control': 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'
|
|
@@ -123,6 +143,23 @@ _private.JSONErrorRenderer = (err, req, res, next) => { // eslint-disable-line n
|
|
|
123
143
|
});
|
|
124
144
|
};
|
|
125
145
|
|
|
146
|
+
_private.JSONErrorRendererV2 = (err, req, res, next) => { // eslint-disable-line no-unused-vars
|
|
147
|
+
const userError = _private.prepareUserMessage(err, req);
|
|
148
|
+
|
|
149
|
+
res.json({
|
|
150
|
+
errors: [{
|
|
151
|
+
message: userError.message || null,
|
|
152
|
+
context: userError.context || null,
|
|
153
|
+
type: err.errorType || null,
|
|
154
|
+
details: err.errorDetails || null,
|
|
155
|
+
property: err.property || null,
|
|
156
|
+
help: err.help || null,
|
|
157
|
+
code: err.code || null,
|
|
158
|
+
id: err.id || null
|
|
159
|
+
}]
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
|
|
126
163
|
_private.prepareUserMessage = (err, res) => {
|
|
127
164
|
const userError = {
|
|
128
165
|
message: err.message,
|
|
@@ -169,23 +206,6 @@ _private.prepareUserMessage = (err, res) => {
|
|
|
169
206
|
return userError;
|
|
170
207
|
};
|
|
171
208
|
|
|
172
|
-
_private.JSONErrorRendererV2 = (err, req, res, next) => { // eslint-disable-line no-unused-vars
|
|
173
|
-
const userError = _private.prepareUserMessage(err, req);
|
|
174
|
-
|
|
175
|
-
res.json({
|
|
176
|
-
errors: [{
|
|
177
|
-
message: userError.message || null,
|
|
178
|
-
context: userError.context || null,
|
|
179
|
-
type: err.errorType || null,
|
|
180
|
-
details: err.errorDetails || null,
|
|
181
|
-
property: err.property || null,
|
|
182
|
-
help: err.help || null,
|
|
183
|
-
code: err.code || null,
|
|
184
|
-
id: err.id || null
|
|
185
|
-
}]
|
|
186
|
-
});
|
|
187
|
-
};
|
|
188
|
-
|
|
189
209
|
_private.ErrorFallbackMessage = err => `<h1>${tpl(messages.oopsErrorTemplateHasError)}</h1>
|
|
190
210
|
<p>${tpl(messages.encounteredError)}</p>
|
|
191
211
|
<pre>${escapeExpression(err.message || err)}</pre>
|
|
@@ -241,39 +261,36 @@ _private.ThemeErrorRenderer = (err, req, res, next) => {
|
|
|
241
261
|
});
|
|
242
262
|
};
|
|
243
263
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
statusCode: err.statusCode,
|
|
248
|
-
errorDetails: err.errorDetails || []
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
// e.g. if you serve the admin /ghost and Ghost returns a 503 because it generates the urls at the moment.
|
|
252
|
-
// This ensures that no matter what res.render will work here
|
|
253
|
-
// @TODO: put to prepare error function?
|
|
254
|
-
if (_.isEmpty(req.app.engines)) {
|
|
255
|
-
res._template = 'error';
|
|
256
|
-
req.app.engine('hbs', _private.createHbsEngine());
|
|
257
|
-
req.app.set('view engine', 'hbs');
|
|
258
|
-
req.app.set('views', config.get('paths').defaultViews);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
res.render('error', data, (_err, html) => {
|
|
262
|
-
if (!_err) {
|
|
263
|
-
return res.send(html);
|
|
264
|
-
}
|
|
264
|
+
/**
|
|
265
|
+
* Borrowed heavily from finalHandler
|
|
266
|
+
*/
|
|
265
267
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
+
const DOUBLE_SPACE_REGEXP = /\x20{2}/g;
|
|
269
|
+
const NEWLINE_REGEXP = /\n/g;
|
|
270
|
+
|
|
271
|
+
function createHtmlDocument(status, message) {
|
|
272
|
+
let body = escapeExpression(message)
|
|
273
|
+
.replace(NEWLINE_REGEXP, '<br>')
|
|
274
|
+
.replace(DOUBLE_SPACE_REGEXP, ' ');
|
|
275
|
+
|
|
276
|
+
return `<!DOCTYPE html>\n
|
|
277
|
+
<html lang="en">\n
|
|
278
|
+
<head>\n
|
|
279
|
+
<meta charset="utf-8">\n
|
|
280
|
+
<title>${status} Error</title>\n
|
|
281
|
+
</head>\n
|
|
282
|
+
<body>\n
|
|
283
|
+
<pre>${status} ${body}</pre>\n
|
|
284
|
+
</body>\n
|
|
285
|
+
</html>\n`;
|
|
286
|
+
}
|
|
268
287
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
return res.status(500).send(_private.ErrorFallbackMessage(_err));
|
|
272
|
-
});
|
|
288
|
+
_private.HTMLErrorRenderer = (err, req, res, next) => { // eslint-disable-line no-unused-vars
|
|
289
|
+
return res.send(createHtmlDocument(res.statusCode, err.stack));
|
|
273
290
|
};
|
|
274
291
|
|
|
275
292
|
_private.BasicErrorRenderer = (err, req, res, next) => { // eslint-disable-line no-unused-vars
|
|
276
|
-
return res.send(res.statusCode + ' ' + err.
|
|
293
|
+
return res.send(res.statusCode + ' ' + err.stack);
|
|
277
294
|
};
|
|
278
295
|
|
|
279
296
|
errorHandler.resourceNotFound = (req, res, next) => {
|
package/core/shared/labs.js
CHANGED
|
@@ -37,7 +37,12 @@ const ALPHA_FEATURES = [
|
|
|
37
37
|
'calloutCard',
|
|
38
38
|
'nftCard',
|
|
39
39
|
'accordionCard',
|
|
40
|
-
'gifsCard'
|
|
40
|
+
'gifsCard',
|
|
41
|
+
'fileCard',
|
|
42
|
+
'audioCard',
|
|
43
|
+
'videoCard',
|
|
44
|
+
'productCard',
|
|
45
|
+
'quoteStyles'
|
|
41
46
|
];
|
|
42
47
|
|
|
43
48
|
module.exports.GA_KEYS = [...GA_FEATURES];
|
|
@@ -47,7 +52,7 @@ module.exports.getAll = () => {
|
|
|
47
52
|
const labs = _.cloneDeep(settingsCache.get('labs')) || {};
|
|
48
53
|
|
|
49
54
|
ALPHA_FEATURES.forEach((alphaKey) => {
|
|
50
|
-
if (labs[alphaKey] && !(config.get('enableDeveloperExperiments') || process.env.NODE_ENV.
|
|
55
|
+
if (labs[alphaKey] && !(config.get('enableDeveloperExperiments') || process.env.NODE_ENV.startsWith('test'))) {
|
|
51
56
|
delete labs[alphaKey];
|
|
52
57
|
}
|
|
53
58
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghost",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.24.0",
|
|
4
4
|
"description": "The professional publishing platform",
|
|
5
5
|
"author": "Ghost Foundation",
|
|
6
6
|
"homepage": "https://ghost.org",
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"@tryghost/express-dynamic-redirects": "0.2.1",
|
|
71
71
|
"@tryghost/helpers": "1.1.54",
|
|
72
72
|
"@tryghost/image-transform": "1.0.18",
|
|
73
|
-
"@tryghost/job-manager": "0.8.
|
|
73
|
+
"@tryghost/job-manager": "0.8.13",
|
|
74
74
|
"@tryghost/kg-card-factory": "3.1.0",
|
|
75
75
|
"@tryghost/kg-default-atoms": "3.1.0",
|
|
76
|
-
"@tryghost/kg-default-cards": "5.8.
|
|
76
|
+
"@tryghost/kg-default-cards": "5.8.5",
|
|
77
77
|
"@tryghost/kg-markdown-html-renderer": "5.1.0",
|
|
78
78
|
"@tryghost/kg-mobiledoc-html-renderer": "5.2.0",
|
|
79
79
|
"@tryghost/limit-service": "1.0.0",
|
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
"@tryghost/root-utils": "0.3.7",
|
|
95
95
|
"@tryghost/security": "0.2.13",
|
|
96
96
|
"@tryghost/session-service": "0.1.28",
|
|
97
|
+
"@tryghost/settings-path-manager": "0.1.2",
|
|
97
98
|
"@tryghost/social-urls": "0.1.27",
|
|
98
99
|
"@tryghost/string": "0.1.21",
|
|
99
100
|
"@tryghost/tpl": "0.1.8",
|
|
@@ -104,7 +105,7 @@
|
|
|
104
105
|
"@tryghost/vhost-middleware": "1.0.19",
|
|
105
106
|
"@tryghost/zip": "1.1.18",
|
|
106
107
|
"amperize": "0.6.1",
|
|
107
|
-
"analytics-node": "
|
|
108
|
+
"analytics-node": "6.0.0",
|
|
108
109
|
"bluebird": "3.7.2",
|
|
109
110
|
"body-parser": "1.19.0",
|
|
110
111
|
"bookshelf": "1.2.0",
|
|
@@ -129,7 +130,7 @@
|
|
|
129
130
|
"ghost-storage-base": "1.0.0",
|
|
130
131
|
"glob": "7.2.0",
|
|
131
132
|
"got": "9.6.0",
|
|
132
|
-
"gscan": "4.
|
|
133
|
+
"gscan": "4.13.2",
|
|
133
134
|
"html-to-text": "5.1.1",
|
|
134
135
|
"image-size": "1.0.0",
|
|
135
136
|
"intl": "1.2.5",
|
|
@@ -144,15 +145,15 @@
|
|
|
144
145
|
"lodash": "4.17.21",
|
|
145
146
|
"luxon": "2.1.1",
|
|
146
147
|
"mailgun-js": "0.22.0",
|
|
147
|
-
"metascraper": "5.25.
|
|
148
|
-
"metascraper-author": "5.25.
|
|
149
|
-
"metascraper-description": "5.25.
|
|
150
|
-
"metascraper-image": "5.25.
|
|
151
|
-
"metascraper-logo": "5.25.
|
|
152
|
-
"metascraper-logo-favicon": "5.25.
|
|
153
|
-
"metascraper-publisher": "5.25.
|
|
154
|
-
"metascraper-title": "5.25.
|
|
155
|
-
"metascraper-url": "5.25.
|
|
148
|
+
"metascraper": "5.25.2",
|
|
149
|
+
"metascraper-author": "5.25.2",
|
|
150
|
+
"metascraper-description": "5.25.2",
|
|
151
|
+
"metascraper-image": "5.25.2",
|
|
152
|
+
"metascraper-logo": "5.25.2",
|
|
153
|
+
"metascraper-logo-favicon": "5.25.2",
|
|
154
|
+
"metascraper-publisher": "5.25.2",
|
|
155
|
+
"metascraper-title": "5.25.2",
|
|
156
|
+
"metascraper-url": "5.25.2",
|
|
156
157
|
"moment": "2.24.0",
|
|
157
158
|
"moment-timezone": "0.5.23",
|
|
158
159
|
"multer": "1.4.3",
|
|
@@ -165,7 +166,7 @@
|
|
|
165
166
|
"path-match": "1.2.4",
|
|
166
167
|
"probe-image-size": "5.0.0",
|
|
167
168
|
"rss": "1.2.2",
|
|
168
|
-
"sanitize-html": "2.
|
|
169
|
+
"sanitize-html": "2.6.0",
|
|
169
170
|
"semver": "7.3.5",
|
|
170
171
|
"stoppable": "1.1.0",
|
|
171
172
|
"tough-cookie": "4.0.0",
|
|
@@ -173,7 +174,7 @@
|
|
|
173
174
|
"xml": "1.0.1"
|
|
174
175
|
},
|
|
175
176
|
"optionalDependencies": {
|
|
176
|
-
"@tryghost/html-to-mobiledoc": "1.3.
|
|
177
|
+
"@tryghost/html-to-mobiledoc": "1.3.4",
|
|
177
178
|
"sqlite3": "5.0.2"
|
|
178
179
|
},
|
|
179
180
|
"devDependencies": {
|
|
@@ -182,7 +183,7 @@
|
|
|
182
183
|
"coffeescript": "2.6.1",
|
|
183
184
|
"cssnano": "5.0.11",
|
|
184
185
|
"eslint": "7.32.0",
|
|
185
|
-
"eslint-plugin-ghost": "2.
|
|
186
|
+
"eslint-plugin-ghost": "2.8.0",
|
|
186
187
|
"grunt": "1.4.1",
|
|
187
188
|
"grunt-bg-shell": "2.3.3",
|
|
188
189
|
"grunt-contrib-clean": "2.0.0",
|
|
@@ -200,7 +201,7 @@
|
|
|
200
201
|
"mock-knex": "0.4.10",
|
|
201
202
|
"nock": "13.2.1",
|
|
202
203
|
"papaparse": "5.3.1",
|
|
203
|
-
"postcss": "8.
|
|
204
|
+
"postcss": "8.4.1",
|
|
204
205
|
"rewire": "5.0.0",
|
|
205
206
|
"should": "13.2.3",
|
|
206
207
|
"sinon": "11.1.2",
|