ghost 4.23.0 → 4.26.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/.eslintrc.js +39 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/casper.js.map +1 -1
- package/content/themes/casper/assets/built/global.css +1 -1
- package/content/themes/casper/assets/built/global.css.map +1 -1
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/built/screen.css.map +1 -1
- package/content/themes/casper/assets/css/global.css +6 -1
- package/content/themes/casper/assets/css/screen.css +50 -215
- package/content/themes/casper/default.hbs +2 -2
- package/content/themes/casper/package.json +3 -2
- package/content/themes/casper/post.hbs +1 -1
- package/content/themes/casper/yarn.lock +173 -123
- package/core/app.js +20 -5
- package/core/boot.js +77 -36
- package/core/bridge.js +10 -10
- package/core/built/assets/ghost-dark-ef86e3bc7f0fb83d39d3d6a49bff8dd5.css +1 -0
- package/core/built/assets/ghost.min-57c1e677f42d596942d317ce93e8a62c.css +1 -0
- package/core/built/assets/{ghost.min-cccc107e881b74c7aaf1a73e1e5e0dee.js → ghost.min-f3c6886e191d34450e9ffca0c8fa056e.js} +543 -618
- package/core/built/assets/icons/audio-upload.svg +8 -0
- package/core/built/assets/{vendor.min-c9002845b6c30ac978abdadde9f33d7c.js → vendor.min-b6b8d2a31d61830c2d8f65c5ba54236a.js} +2656 -2118
- package/core/frontend/apps/amp/lib/helpers/amp_content.js +2 -2
- package/core/frontend/apps/amp/lib/views/amp.hbs +75 -0
- package/core/frontend/apps/private-blogging/index.js +1 -1
- package/core/frontend/helpers/url.js +18 -1
- package/core/frontend/services/apps/index.js +1 -1
- package/core/frontend/services/apps/loader.js +3 -3
- package/core/frontend/services/card-assets/index.js +0 -12
- package/core/frontend/services/card-assets/service.js +22 -21
- package/core/frontend/services/helpers/handlebars.js +1 -1
- package/core/frontend/services/theme-engine/middleware/ensure-active-theme.js +34 -0
- package/core/frontend/services/theme-engine/middleware/index.js +6 -0
- package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +116 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-data.js +9 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +57 -0
- package/core/frontend/src/cards/css/audio.css +186 -0
- package/core/frontend/src/cards/css/blockquote.css +27 -0
- package/core/frontend/src/cards/css/bookmark.css +7 -0
- package/core/frontend/src/cards/css/button.css +4 -0
- package/core/frontend/src/cards/css/callout.css +23 -15
- package/core/frontend/src/cards/css/gallery.css +13 -3
- package/core/frontend/src/cards/css/toggle.css +48 -15
- package/core/frontend/src/cards/js/audio.js +137 -0
- package/core/frontend/web/middleware/error-handler.js +93 -0
- package/core/frontend/web/middleware/handle-image-sizes.js +3 -6
- package/core/frontend/web/middleware/index.js +1 -0
- package/core/frontend/web/middleware/serve-public-file.js +25 -8
- package/core/frontend/web/site.js +2 -5
- package/core/server/adapters/scheduling/SchedulingDefault.js +2 -2
- package/core/server/adapters/storage/LocalStorageBase.js +2 -2
- package/core/server/api/canary/db.js +2 -2
- package/core/server/api/canary/media.js +3 -2
- package/core/server/api/canary/oembed.js +16 -1
- package/core/server/api/canary/session.js +1 -1
- package/core/server/api/canary/slugs.js +1 -1
- package/core/server/api/canary/utils/permissions.js +2 -2
- package/core/server/api/canary/utils/serializers/output/config.js +2 -6
- package/core/server/api/v2/db.js +2 -2
- package/core/server/api/v2/session.js +1 -1
- package/core/server/api/v2/slugs.js +1 -1
- package/core/server/api/v2/utils/permissions.js +2 -2
- package/core/server/api/v3/db.js +2 -2
- package/core/server/api/v3/session.js +1 -1
- package/core/server/api/v3/slugs.js +1 -1
- package/core/server/api/v3/utils/permissions.js +2 -2
- package/core/server/data/db/state-manager.js +4 -4
- package/core/server/data/exporter/export-filename.js +1 -1
- package/core/server/data/importer/handlers/json.js +1 -1
- package/core/server/data/importer/import-manager.js +1 -1
- package/core/server/data/importer/importers/data/base.js +1 -1
- package/core/server/data/migrations/utils.js +2 -2
- package/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +1 -0
- package/core/server/data/migrations/versions/3.1/08-add-uuid-values-to-members.js +1 -0
- package/core/server/data/migrations/versions/3.22/02-settings-key-renames.js +2 -0
- package/core/server/data/migrations/versions/3.22/05-migrate-members-subscription-settings.js +3 -0
- package/core/server/data/migrations/versions/3.22/06-migrate-stripe-connect-settings.js +2 -0
- package/core/server/data/migrations/versions/3.23/01-migrate-bulk-email-settings.js +1 -0
- package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -0
- package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -0
- package/core/server/data/migrations/versions/3.38/04-populate-recipient-filter-column.js +2 -0
- package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +2 -0
- package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +4 -0
- package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +5 -0
- package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -0
- package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -0
- package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +2 -1
- package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +1 -0
- package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +3 -0
- package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +1 -0
- package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +2 -0
- package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +1 -0
- package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -0
- package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +1 -0
- package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -0
- package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -0
- package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +1 -0
- package/core/server/data/schema/commands.js +2 -2
- package/core/server/ghost-server.js +2 -2
- package/core/server/lib/image/image-size.js +2 -2
- package/core/server/models/base/listeners.js +2 -2
- package/core/server/models/member-email-change-event.js +2 -2
- package/core/server/models/member-login-event.js +2 -2
- package/core/server/models/member-paid-subscription-event.js +3 -3
- package/core/server/models/member-payment-event.js +3 -3
- package/core/server/models/member-product-event.js +6 -6
- package/core/server/models/member-status-event.js +5 -3
- package/core/server/models/member-subscribe-event.js +9 -3
- package/core/server/models/relations/authors.js +1 -1
- package/core/server/models/settings.js +1 -1
- package/core/server/notify.js +1 -2
- package/core/server/services/auth/passwordreset.js +1 -1
- package/core/server/services/auth/setup.js +1 -1
- package/core/server/services/email-analytics/jobs/index.js +1 -1
- package/core/server/services/mega/mega.js +6 -4
- package/core/server/services/mega/template.js +47 -17
- package/core/server/services/members/api.js +22 -0
- package/core/server/services/members/config.js +1 -1
- package/core/server/services/members/emails/signup-paid.js +168 -0
- package/core/server/services/members/service.js +6 -2
- package/core/server/services/members/stripe-connect.js +4 -2
- package/core/server/services/nft-oembed.js +7 -2
- package/core/server/services/oembed.js +15 -3
- package/core/server/services/permissions/can-this.js +1 -1
- package/core/server/services/redirects/api.js +20 -25
- 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/default-settings-manager.js +1 -1
- package/core/server/services/route-settings/index.js +40 -17
- package/core/server/services/route-settings/route-settings.js +120 -115
- package/core/server/services/route-settings/settings-loader.js +18 -36
- package/core/server/services/route-settings/yaml-parser.js +1 -1
- package/core/server/services/slack.js +1 -1
- package/core/server/services/themes/activation-bridge.js +3 -3
- package/core/server/services/themes/storage.js +2 -2
- package/core/server/services/twitter-embed.js +81 -0
- 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/services/xmlrpc.js +2 -2
- 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/admin/middleware.js +1 -1
- 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/admin/middleware.js +1 -1
- 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/admin/middleware.js +1 -1
- 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 +2 -13
- package/core/server/web/parent/backend.js +2 -0
- package/core/server/web/shared/middleware/error-handler.js +57 -162
- package/core/server/web/shared/middleware/index.js +0 -4
- package/core/shared/config/defaults.json +7 -1
- package/core/shared/express.js +1 -1
- package/core/shared/labs.js +12 -7
- package/core/shared/sentry.js +1 -1
- package/package.json +41 -40
- package/yarn.lock +799 -948
- package/content/themes/casper/assets/js/gallery-card.js +0 -24
- package/core/built/assets/ghost-dark-42cf6e0c730578940ec069bda45aea41.css +0 -1
- package/core/built/assets/ghost.min-fcf6a0738421f86c47c55f20d00c5ba9.css +0 -1
- package/core/frontend/services/theme-engine/middleware.js +0 -209
- package/core/server/web/shared/middleware/maintenance.js +0 -25
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class LocalFileCache {
|
|
5
|
+
/**
|
|
6
|
+
* @param {Object} options
|
|
7
|
+
* @param {String} options.storagePath - cached storage path
|
|
8
|
+
* @param {Boolean} options.writeDisabled - controls if cache can write
|
|
9
|
+
*/
|
|
10
|
+
constructor({storagePath, writeDisabled}) {
|
|
11
|
+
const urlsStoragePath = path.join(storagePath, 'urls.json');
|
|
12
|
+
const resourcesCachePath = path.join(storagePath, 'resources.json');
|
|
13
|
+
|
|
14
|
+
this.storagePaths = {
|
|
15
|
+
urls: urlsStoragePath,
|
|
16
|
+
resources: resourcesCachePath
|
|
17
|
+
};
|
|
18
|
+
this.writeDisabled = writeDisabled;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Handles reading and parsing JSON from the filesystem.
|
|
23
|
+
* In case the file is corrupted or does not exist, returns null.
|
|
24
|
+
* @param {String} filePath path to read from
|
|
25
|
+
* @returns {Promise<Object>}
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
async readCacheFile(filePath) {
|
|
29
|
+
let cacheExists = false;
|
|
30
|
+
let cacheData = null;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
await fs.stat(filePath);
|
|
34
|
+
cacheExists = true;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
cacheExists = false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (cacheExists) {
|
|
40
|
+
try {
|
|
41
|
+
const cacheFile = await fs.readFile(filePath, 'utf8');
|
|
42
|
+
cacheData = JSON.parse(cacheFile);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
//noop as we'd start a long boot process if there are any errors in the file
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return cacheData;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
*
|
|
53
|
+
* @param {'urls'|'resources'} type
|
|
54
|
+
* @returns {Promise<Object>}
|
|
55
|
+
*/
|
|
56
|
+
async read(type) {
|
|
57
|
+
return await this.readCacheFile(this.storagePaths[type]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
*
|
|
62
|
+
* @param {'urls'|'resources'} type of data to persist
|
|
63
|
+
* @param {Object} data - data to be persisted
|
|
64
|
+
* @returns {Promise<Object>}
|
|
65
|
+
*/
|
|
66
|
+
async write(type, data) {
|
|
67
|
+
if (this.writeDisabled) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return fs.writeFile(this.storagePaths[type], JSON.stringify(data, null, 4));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = LocalFileCache;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
1
|
const _debug = require('@tryghost/debug')._base;
|
|
3
2
|
const debug = _debug('ghost:services:url:service');
|
|
4
3
|
const _ = require('lodash');
|
|
@@ -22,13 +21,13 @@ class UrlService {
|
|
|
22
21
|
/**
|
|
23
22
|
*
|
|
24
23
|
* @param {Object} options
|
|
25
|
-
* @param {
|
|
26
|
-
* @param {
|
|
24
|
+
* @param {Object} [options.cache] - cache handler instance
|
|
25
|
+
* @param {Function} [options.cache.read] - read cache by type
|
|
26
|
+
* @param {Function} [options.cache.write] - write into cache by type
|
|
27
27
|
*/
|
|
28
|
-
constructor({
|
|
28
|
+
constructor({cache} = {}) {
|
|
29
29
|
this.utils = urlUtils;
|
|
30
|
-
this.
|
|
31
|
-
this.resourcesCachePath = resourcesCachePath;
|
|
30
|
+
this.cache = cache;
|
|
32
31
|
this.onFinished = null;
|
|
33
32
|
this.finished = false;
|
|
34
33
|
this.urlGenerators = [];
|
|
@@ -319,26 +318,22 @@ class UrlService {
|
|
|
319
318
|
* @description Initializes components needed for the URL Service to function
|
|
320
319
|
* @param {Object} options
|
|
321
320
|
* @param {Function} [options.onFinished] - callback when url generation is finished
|
|
321
|
+
* @param {Boolean} [options.urlCache] - whether to init using url cache or not
|
|
322
322
|
*/
|
|
323
|
-
async init(
|
|
324
|
-
this.onFinished =
|
|
323
|
+
async init({onFinished, urlCache} = {}) {
|
|
324
|
+
this.onFinished = onFinished;
|
|
325
325
|
|
|
326
326
|
let persistedUrls;
|
|
327
327
|
let persistedResources;
|
|
328
328
|
|
|
329
|
-
if (labs.isSet('urlCache')) {
|
|
330
|
-
persistedUrls = await this.
|
|
331
|
-
persistedResources = await this.
|
|
329
|
+
if (this.cache && (labs.isSet('urlCache') || urlCache)) {
|
|
330
|
+
persistedUrls = await this.cache.read('urls');
|
|
331
|
+
persistedResources = await this.cache.read('resources');
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
if (persistedUrls && persistedResources) {
|
|
335
|
-
this.urls =
|
|
336
|
-
|
|
337
|
-
});
|
|
338
|
-
this.resources = new Resources({
|
|
339
|
-
queue: this.queue,
|
|
340
|
-
resources: persistedResources
|
|
341
|
-
});
|
|
335
|
+
this.urls.urls = persistedUrls;
|
|
336
|
+
this.resources.data = persistedResources;
|
|
342
337
|
this.resources.initResourceConfig();
|
|
343
338
|
this.resources.initEvenListeners();
|
|
344
339
|
|
|
@@ -361,35 +356,8 @@ class UrlService {
|
|
|
361
356
|
return null;
|
|
362
357
|
}
|
|
363
358
|
|
|
364
|
-
await this.
|
|
365
|
-
await this.
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
async persistToCacheFile(filePath, data) {
|
|
369
|
-
return fs.writeFile(filePath, JSON.stringify(data, null, 4));
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
async readCacheFile(filePath) {
|
|
373
|
-
let cacheExists = false;
|
|
374
|
-
let cacheData;
|
|
375
|
-
|
|
376
|
-
try {
|
|
377
|
-
await fs.stat(filePath);
|
|
378
|
-
cacheExists = true;
|
|
379
|
-
} catch (e) {
|
|
380
|
-
cacheExists = false;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (cacheExists) {
|
|
384
|
-
try {
|
|
385
|
-
const cacheFile = await fs.readFile(filePath, 'utf8');
|
|
386
|
-
cacheData = JSON.parse(cacheFile);
|
|
387
|
-
} catch (e) {
|
|
388
|
-
//noop as we'd start a long boot process if there are any errors in the file
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return cacheData;
|
|
359
|
+
await this.cache.write('urls', this.urls.urls);
|
|
360
|
+
await this.cache.write('resources', this.resources.getAll());
|
|
393
361
|
}
|
|
394
362
|
|
|
395
363
|
/**
|
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
1
|
const config = require('../../../shared/config');
|
|
2
|
+
const LocalFileCache = require('./LocalFileCache');
|
|
3
3
|
const UrlService = require('./UrlService');
|
|
4
4
|
|
|
5
5
|
// NOTE: instead of a path we could give UrlService a "data-resolver" of some sort
|
|
6
6
|
// so it doesn't have to contain the logic to read data at all. This would be
|
|
7
7
|
// a possible improvement in the future
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
let writeDisabled = false;
|
|
9
|
+
let storagePath = config.getContentPath('data');
|
|
10
|
+
|
|
11
|
+
// TODO: remove this hack in favor of loading from the content path when it's possible to do so
|
|
12
|
+
// by mocking content folders in pre-boot phase
|
|
13
|
+
if (process.env.NODE_ENV.startsWith('test')){
|
|
14
|
+
storagePath = config.get('paths').urlCache;
|
|
15
|
+
|
|
16
|
+
// NOTE: prevents test suites from overwriting cache fixtures.
|
|
17
|
+
// A better solution would be injecting a different implementation of the
|
|
18
|
+
// cache based on the environment, this approach should do the trick for now
|
|
19
|
+
writeDisabled = true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const cache = new LocalFileCache({storagePath, writeDisabled});
|
|
23
|
+
const urlService = new UrlService({cache});
|
|
11
24
|
|
|
12
25
|
// Singleton
|
|
13
26
|
module.exports = urlService;
|
|
@@ -87,7 +87,7 @@ function ping(post) {
|
|
|
87
87
|
if (!goodResponse.test(res.body)) {
|
|
88
88
|
const matches = res.body.match(errorMessage);
|
|
89
89
|
const message = matches ? matches[1] : res.body;
|
|
90
|
-
throw new errors.
|
|
90
|
+
throw new errors.InternalServerError({message});
|
|
91
91
|
}
|
|
92
92
|
})
|
|
93
93
|
.catch(function (err) {
|
|
@@ -100,7 +100,7 @@ function ping(post) {
|
|
|
100
100
|
help: tpl(messages.requestFailedHelp, {url: 'https://ghost.org/docs/'})
|
|
101
101
|
});
|
|
102
102
|
} else {
|
|
103
|
-
error = new errors.
|
|
103
|
+
error = new errors.InternalServerError({
|
|
104
104
|
err: err,
|
|
105
105
|
message: err.message,
|
|
106
106
|
context: tpl(messages.requestFailedError, {service: 'xmlrpc'}),
|
|
@@ -5,7 +5,7 @@ const config = require('../../../shared/config');
|
|
|
5
5
|
const constants = require('@tryghost/constants');
|
|
6
6
|
const urlUtils = require('../../../shared/url-utils');
|
|
7
7
|
const shared = require('../shared');
|
|
8
|
-
const
|
|
8
|
+
const redirectAdminUrls = require('./middleware/redirect-admin-urls');
|
|
9
9
|
|
|
10
10
|
module.exports = function setupAdminApp() {
|
|
11
11
|
debug('Admin setup start');
|
|
@@ -26,9 +26,6 @@ module.exports = function setupAdminApp() {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
// Render error page in case of maintenance
|
|
30
|
-
adminApp.use(shared.middleware.maintenance);
|
|
31
|
-
|
|
32
29
|
// Force SSL if required
|
|
33
30
|
// must happen AFTER asset loading and BEFORE routing
|
|
34
31
|
adminApp.use(shared.middleware.urlRedirects.adminSSLAndHostRedirect);
|
|
@@ -42,7 +39,7 @@ module.exports = function setupAdminApp() {
|
|
|
42
39
|
adminApp.use(shared.middleware.cacheControl('private'));
|
|
43
40
|
|
|
44
41
|
// Special redirects for the admin (these should have their own cache-control headers)
|
|
45
|
-
adminApp.use(
|
|
42
|
+
adminApp.use(redirectAdminUrls);
|
|
46
43
|
|
|
47
44
|
// Finally, routing
|
|
48
45
|
adminApp.get('*', require('./controller'));
|
|
@@ -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.26%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-57c1e677f42d596942d317ce93e8a62c.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-b6b8d2a31d61830c2d8f65c5ba54236a.js"></script>
|
|
63
|
+
<script src="assets/ghost.min-f3c6886e191d34450e9ffca0c8fa056e.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.26%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-57c1e677f42d596942d317ce93e8a62c.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-b6b8d2a31d61830c2d8f65c5ba54236a.js"></script>
|
|
63
|
+
<script src="assets/ghost.min-f3c6886e191d34450e9ffca0c8fa056e.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
|
|
@@ -3,16 +3,15 @@ const config = require('../../../shared/config');
|
|
|
3
3
|
const express = require('../../../shared/express');
|
|
4
4
|
const compress = require('compression');
|
|
5
5
|
const mw = require('./middleware');
|
|
6
|
-
const vhost = require('@tryghost/vhost-middleware');
|
|
7
6
|
|
|
8
|
-
module.exports = function setupParentApp(
|
|
7
|
+
module.exports = function setupParentApp() {
|
|
9
8
|
debug('ParentApp setup start');
|
|
10
9
|
const parentApp = express('parent');
|
|
11
10
|
|
|
12
11
|
parentApp.use(mw.requestId);
|
|
13
12
|
parentApp.use(mw.logRequest);
|
|
14
13
|
|
|
15
|
-
// Register event
|
|
14
|
+
// Register event emitter on req/res to trigger cache invalidation webhook event
|
|
16
15
|
parentApp.use(mw.emitEvents);
|
|
17
16
|
|
|
18
17
|
// enabled gzip compression by default
|
|
@@ -24,16 +23,6 @@ module.exports = function setupParentApp(options = {}) {
|
|
|
24
23
|
// @TODO: figure out if this is really needed everywhere? Is it not frontend only...
|
|
25
24
|
parentApp.use(mw.ghostLocals);
|
|
26
25
|
|
|
27
|
-
// Mount the express apps on the parentApp
|
|
28
|
-
|
|
29
|
-
// ADMIN + API
|
|
30
|
-
const backendApp = require('./backend')();
|
|
31
|
-
parentApp.use(vhost(config.getBackendMountPath(), backendApp));
|
|
32
|
-
|
|
33
|
-
// SITE + MEMBERS
|
|
34
|
-
const frontendApp = require('./frontend')(options);
|
|
35
|
-
parentApp.use(vhost(config.getFrontendMountPath(), frontendApp));
|
|
36
|
-
|
|
37
26
|
debug('ParentApp setup end');
|
|
38
27
|
|
|
39
28
|
return parentApp;
|
|
@@ -10,9 +10,11 @@ module.exports = () => {
|
|
|
10
10
|
// BACKEND
|
|
11
11
|
// Wrap the admin and API apps into a single express app for use with vhost
|
|
12
12
|
const backendApp = express('backend');
|
|
13
|
+
|
|
13
14
|
backendApp.lazyUse('/ghost/api', require('../api'));
|
|
14
15
|
backendApp.lazyUse('/ghost/oauth', require('../oauth'));
|
|
15
16
|
backendApp.lazyUse('/ghost/.well-known', require('../well-known'));
|
|
17
|
+
|
|
16
18
|
backendApp.use('/ghost', require('../../services/auth/session').createSessionFromToken, require('../admin')());
|
|
17
19
|
|
|
18
20
|
return backendApp;
|