ghost 5.82.10 → 5.82.12
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/components/{tryghost-adapter-cache-memory-ttl-5.82.10.tgz → tryghost-adapter-cache-memory-ttl-5.82.12.tgz} +0 -0
- package/components/{tryghost-adapter-cache-redis-5.82.10.tgz → tryghost-adapter-cache-redis-5.82.12.tgz} +0 -0
- package/components/{tryghost-adapter-manager-5.82.10.tgz → tryghost-adapter-manager-5.82.12.tgz} +0 -0
- package/components/tryghost-announcement-bar-settings-5.82.12.tgz +0 -0
- package/components/tryghost-api-framework-5.82.12.tgz +0 -0
- package/components/{tryghost-api-version-compatibility-service-5.82.10.tgz → tryghost-api-version-compatibility-service-5.82.12.tgz} +0 -0
- package/components/{tryghost-audience-feedback-5.82.10.tgz → tryghost-audience-feedback-5.82.12.tgz} +0 -0
- package/components/tryghost-bookshelf-repository-5.82.12.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.82.12.tgz +0 -0
- package/components/tryghost-collections-5.82.12.tgz +0 -0
- package/components/tryghost-constants-5.82.12.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.82.12.tgz +0 -0
- package/components/{tryghost-data-generator-5.82.10.tgz → tryghost-data-generator-5.82.12.tgz} +0 -0
- package/components/tryghost-domain-events-5.82.12.tgz +0 -0
- package/components/{tryghost-donations-5.82.10.tgz → tryghost-donations-5.82.12.tgz} +0 -0
- package/components/tryghost-dynamic-routing-events-5.82.12.tgz +0 -0
- package/components/{tryghost-email-addresses-5.82.10.tgz → tryghost-email-addresses-5.82.12.tgz} +0 -0
- package/components/{tryghost-email-analytics-provider-mailgun-5.82.10.tgz → tryghost-email-analytics-provider-mailgun-5.82.12.tgz} +0 -0
- package/components/{tryghost-email-analytics-service-5.82.10.tgz → tryghost-email-analytics-service-5.82.12.tgz} +0 -0
- package/components/{tryghost-email-content-generator-5.82.10.tgz → tryghost-email-content-generator-5.82.12.tgz} +0 -0
- package/components/{tryghost-email-events-5.82.10.tgz → tryghost-email-events-5.82.12.tgz} +0 -0
- package/components/{tryghost-email-service-5.82.10.tgz → tryghost-email-service-5.82.12.tgz} +0 -0
- package/components/{tryghost-email-suppression-list-5.82.10.tgz → tryghost-email-suppression-list-5.82.12.tgz} +0 -0
- package/components/{tryghost-express-dynamic-redirects-5.82.10.tgz → tryghost-express-dynamic-redirects-5.82.12.tgz} +0 -0
- package/components/{tryghost-external-media-inliner-5.82.10.tgz → tryghost-external-media-inliner-5.82.12.tgz} +0 -0
- package/components/tryghost-extract-api-key-5.82.12.tgz +0 -0
- package/components/tryghost-ghost-5.82.12.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.82.12.tgz +0 -0
- package/components/tryghost-i18n-5.82.12.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.82.12.tgz +0 -0
- package/components/{tryghost-importer-revue-5.82.10.tgz → tryghost-importer-revue-5.82.12.tgz} +0 -0
- package/components/tryghost-in-memory-repository-5.82.12.tgz +0 -0
- package/components/{tryghost-job-manager-5.82.10.tgz → tryghost-job-manager-5.82.12.tgz} +0 -0
- package/components/{tryghost-link-redirects-5.82.10.tgz → tryghost-link-redirects-5.82.12.tgz} +0 -0
- package/components/{tryghost-link-replacer-5.82.10.tgz → tryghost-link-replacer-5.82.12.tgz} +0 -0
- package/components/{tryghost-link-tracking-5.82.10.tgz → tryghost-link-tracking-5.82.12.tgz} +0 -0
- package/components/{tryghost-magic-link-5.82.10.tgz → tryghost-magic-link-5.82.12.tgz} +0 -0
- package/components/{tryghost-mail-events-5.82.10.tgz → tryghost-mail-events-5.82.12.tgz} +0 -0
- package/components/{tryghost-mailgun-client-5.82.10.tgz → tryghost-mailgun-client-5.82.12.tgz} +0 -0
- package/components/{tryghost-member-attribution-5.82.10.tgz → tryghost-member-attribution-5.82.12.tgz} +0 -0
- package/components/{tryghost-member-events-5.82.10.tgz → tryghost-member-events-5.82.12.tgz} +0 -0
- package/components/tryghost-members-api-5.82.12.tgz +0 -0
- package/components/{tryghost-members-csv-5.82.10.tgz → tryghost-members-csv-5.82.12.tgz} +0 -0
- package/components/{tryghost-members-events-service-5.82.10.tgz → tryghost-members-events-service-5.82.12.tgz} +0 -0
- package/components/{tryghost-members-importer-5.82.10.tgz → tryghost-members-importer-5.82.12.tgz} +0 -0
- package/components/{tryghost-members-offers-5.82.10.tgz → tryghost-members-offers-5.82.12.tgz} +0 -0
- package/components/{tryghost-members-payments-5.82.10.tgz → tryghost-members-payments-5.82.12.tgz} +0 -0
- package/components/tryghost-members-ssr-5.82.12.tgz +0 -0
- package/components/{tryghost-members-stripe-service-5.82.10.tgz → tryghost-members-stripe-service-5.82.12.tgz} +0 -0
- package/components/{tryghost-mentions-email-report-5.82.10.tgz → tryghost-mentions-email-report-5.82.12.tgz} +0 -0
- package/components/tryghost-milestones-5.82.12.tgz +0 -0
- package/components/{tryghost-minifier-5.82.10.tgz → tryghost-minifier-5.82.12.tgz} +0 -0
- package/components/{tryghost-model-to-domain-event-interceptor-5.82.10.tgz → tryghost-model-to-domain-event-interceptor-5.82.12.tgz} +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.82.12.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.82.12.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.82.12.tgz +0 -0
- package/components/{tryghost-mw-session-from-token-5.82.10.tgz → tryghost-mw-session-from-token-5.82.12.tgz} +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.82.12.tgz +0 -0
- package/components/tryghost-mw-version-match-5.82.12.tgz +0 -0
- package/components/tryghost-mw-vhost-5.82.12.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.82.12.tgz +0 -0
- package/components/{tryghost-oembed-service-5.82.10.tgz → tryghost-oembed-service-5.82.12.tgz} +0 -0
- package/components/tryghost-package-json-5.82.12.tgz +0 -0
- package/components/tryghost-post-events-5.82.12.tgz +0 -0
- package/components/{tryghost-post-revisions-5.82.10.tgz → tryghost-post-revisions-5.82.12.tgz} +0 -0
- package/components/tryghost-posts-service-5.82.12.tgz +0 -0
- package/components/{tryghost-recommendations-5.82.10.tgz → tryghost-recommendations-5.82.12.tgz} +0 -0
- package/components/tryghost-referrers-5.82.12.tgz +0 -0
- package/components/{tryghost-security-5.82.10.tgz → tryghost-security-5.82.12.tgz} +0 -0
- package/components/tryghost-session-service-5.82.12.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.82.12.tgz +0 -0
- package/components/tryghost-slack-notifications-5.82.12.tgz +0 -0
- package/components/{tryghost-staff-service-5.82.10.tgz → tryghost-staff-service-5.82.12.tgz} +0 -0
- package/components/{tryghost-stats-service-5.82.10.tgz → tryghost-stats-service-5.82.12.tgz} +0 -0
- package/components/tryghost-tiers-5.82.12.tgz +0 -0
- package/components/{tryghost-update-check-service-5.82.10.tgz → tryghost-update-check-service-5.82.12.tgz} +0 -0
- package/components/{tryghost-verification-trigger-5.82.10.tgz → tryghost-verification-trigger-5.82.12.tgz} +0 -0
- package/components/{tryghost-version-notifications-data-service-5.82.10.tgz → tryghost-version-notifications-data-service-5.82.12.tgz} +0 -0
- package/components/{tryghost-webmentions-5.82.10.tgz → tryghost-webmentions-5.82.12.tgz} +0 -0
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +128 -127
- package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +2 -2
- package/core/built/admin/assets/admin-x-demo/{index-f544d6cb.mjs → index-672397d7.mjs} +1082 -1054
- package/core/built/admin/assets/admin-x-demo/{modals-6544d395.mjs → modals-e68b00c5.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-a1b9ba69.mjs → CodeEditorView-e6ecff28.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-d54677fb.mjs → index-3a124f37.mjs} +3015 -2975
- package/core/built/admin/assets/admin-x-settings/{index-8d83c7e9.mjs → index-ee4ab0db.mjs} +3586 -3427
- package/core/built/admin/assets/admin-x-settings/{modals-73b6c803.mjs → modals-8b47269b.mjs} +4962 -4997
- package/core/built/admin/assets/{chunk.799.5bcf6feef26f9fe94f0a.js → chunk.300.07bd40373843db34827c.js} +1177 -802
- package/core/built/admin/assets/{chunk.524.7bc3c52f480b7779363e.js → chunk.524.d0e760b5fbf40755f861.js} +5 -5
- package/core/built/admin/assets/{chunk.582.596e95cb181ba9f41b4e.js → chunk.582.b5b5252c47ef49f8f87a.js} +4 -4
- package/core/built/admin/assets/ghost-67652794f5902e84ccb0c67948563c1e.css +1 -0
- package/core/built/admin/assets/{ghost-377ce682ddf6e53f0ccb91aeae235f29.js → ghost-6f45467fa855b0c1da51c5f6c31f4744.js} +161 -143
- package/core/built/admin/assets/ghost-dark-e0cbaf72cee27f9de1eff5aea1152c46.css +1 -0
- package/core/built/admin/assets/koenig-lexical/index.css +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +19043 -18668
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +134 -134
- package/core/built/admin/assets/{vendor-96853bcc300bd0b0cda2913c8edb0d14.js → vendor-0d51bbe51e0393cca143d7b52d073d8a.js} +44 -33
- package/core/built/admin/index.html +6 -6
- package/core/frontend/public/ghost.css +13 -2
- package/core/frontend/public/ghost.min.css +1 -1
- package/core/frontend/utils/images.js +4 -0
- package/core/frontend/web/middleware/frontend-caching.js +77 -0
- package/core/frontend/web/middleware/index.js +1 -0
- package/core/frontend/web/site.js +32 -14
- package/core/server/api/endpoints/utils/serializers/input/posts.js +40 -1
- package/core/server/services/members/middleware.js +65 -0
- package/core/server/services/offers/OfferBookshelfRepository.js +3 -3
- package/core/server/views/maintenance.html +1 -0
- package/core/server/web/members/app.js +8 -1
- package/core/shared/sentry.js +0 -1
- package/package.json +161 -160
- package/yarn.lock +708 -636
- package/components/tryghost-announcement-bar-settings-5.82.10.tgz +0 -0
- package/components/tryghost-api-framework-5.82.10.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.82.10.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.82.10.tgz +0 -0
- package/components/tryghost-collections-5.82.10.tgz +0 -0
- package/components/tryghost-constants-5.82.10.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.82.10.tgz +0 -0
- package/components/tryghost-domain-events-5.82.10.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.82.10.tgz +0 -0
- package/components/tryghost-extract-api-key-5.82.10.tgz +0 -0
- package/components/tryghost-ghost-5.82.10.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.82.10.tgz +0 -0
- package/components/tryghost-i18n-5.82.10.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.82.10.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.82.10.tgz +0 -0
- package/components/tryghost-members-api-5.82.10.tgz +0 -0
- package/components/tryghost-members-ssr-5.82.10.tgz +0 -0
- package/components/tryghost-milestones-5.82.10.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.82.10.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.82.10.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.82.10.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.82.10.tgz +0 -0
- package/components/tryghost-mw-version-match-5.82.10.tgz +0 -0
- package/components/tryghost-mw-vhost-5.82.10.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.82.10.tgz +0 -0
- package/components/tryghost-package-json-5.82.10.tgz +0 -0
- package/components/tryghost-post-events-5.82.10.tgz +0 -0
- package/components/tryghost-posts-service-5.82.10.tgz +0 -0
- package/components/tryghost-referrers-5.82.10.tgz +0 -0
- package/components/tryghost-session-service-5.82.10.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.82.10.tgz +0 -0
- package/components/tryghost-slack-notifications-5.82.10.tgz +0 -0
- package/components/tryghost-tiers-5.82.10.tgz +0 -0
- package/core/built/admin/assets/ghost-dark-330dba70f72cf7c29a9c02c0fdef7377.css +0 -1
- package/core/built/admin/assets/ghost-e031f7ab21c73aa9a2a05ef2703e39cb.css +0 -1
- /package/core/built/admin/assets/{chunk.799.5bcf6feef26f9fe94f0a.js.LICENSE.txt → chunk.300.07bd40373843db34827c.js.LICENSE.txt} +0 -0
|
@@ -22,6 +22,7 @@ const shared = require('../../server/web/shared');
|
|
|
22
22
|
const errorHandler = require('@tryghost/mw-error-handler');
|
|
23
23
|
const mw = require('./middleware');
|
|
24
24
|
const labs = require('../../shared/labs');
|
|
25
|
+
const bodyParser = require('body-parser');
|
|
25
26
|
|
|
26
27
|
const STATIC_IMAGE_URL_PREFIX = `/${urlUtils.STATIC_IMAGE_URL_PREFIX}`;
|
|
27
28
|
const STATIC_MEDIA_URL_PREFIX = `/${constants.STATIC_MEDIA_URL_PREFIX}`;
|
|
@@ -50,6 +51,21 @@ module.exports = function setupSiteApp(routerConfig) {
|
|
|
50
51
|
// enable CORS headers (allows admin client to hit front-end when configured on separate URLs)
|
|
51
52
|
siteApp.use(mw.cors);
|
|
52
53
|
|
|
54
|
+
const jsonParser = bodyParser.json({
|
|
55
|
+
type: ['application/activity+json', 'application/ld+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'],
|
|
56
|
+
// TODO: The @RawBody decorator in nest isn't working without this atm...
|
|
57
|
+
verify: function (req, res, buf) {
|
|
58
|
+
req.rawBody = buf;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
siteApp.use(async function nestBodyParser(req, res, next) {
|
|
62
|
+
if (labs.isSet('NestPlayground') || labs.isSet('ActivityPub')) {
|
|
63
|
+
jsonParser(req, res, next);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
return next();
|
|
67
|
+
});
|
|
68
|
+
|
|
53
69
|
siteApp.use(async function nestApp(req, res, next) {
|
|
54
70
|
if (labs.isSet('NestPlayground') || labs.isSet('ActivityPub')) {
|
|
55
71
|
const originalExpressApp = req.app;
|
|
@@ -105,9 +121,6 @@ module.exports = function setupSiteApp(routerConfig) {
|
|
|
105
121
|
// Serve site files using the storage adapter
|
|
106
122
|
siteApp.use(STATIC_FILES_URL_PREFIX, storage.getStorage('files').serve());
|
|
107
123
|
|
|
108
|
-
// Global handling for member session, ensures a member is logged in to the frontend
|
|
109
|
-
siteApp.use(membersService.middleware.loadMemberSession);
|
|
110
|
-
|
|
111
124
|
// /member/.well-known/* serves files (e.g. jwks.json) so it needs to be mounted before the prettyUrl mw to avoid trailing slashes
|
|
112
125
|
siteApp.use(
|
|
113
126
|
'/members/.well-known',
|
|
@@ -132,18 +145,23 @@ module.exports = function setupSiteApp(routerConfig) {
|
|
|
132
145
|
|
|
133
146
|
// Theme static assets/files
|
|
134
147
|
siteApp.use(mw.staticTheme());
|
|
148
|
+
|
|
149
|
+
// Serve robots.txt if not found in theme
|
|
150
|
+
siteApp.use(mw.servePublicFile('static', 'robots.txt', 'text/plain', config.get('caching:robotstxt:maxAge')));
|
|
151
|
+
|
|
135
152
|
debug('Static content done');
|
|
136
153
|
|
|
154
|
+
// site map - this should probably be refactored to be an internal app
|
|
155
|
+
sitemapHandler(siteApp);
|
|
156
|
+
|
|
157
|
+
// Global handling for member session, ensures a member is logged in to the frontend
|
|
158
|
+
siteApp.use(membersService.middleware.loadMemberSession);
|
|
159
|
+
|
|
137
160
|
// Theme middleware
|
|
138
161
|
// This should happen AFTER any shared assets are served, as it only changes things to do with templates
|
|
139
162
|
siteApp.use(themeMiddleware);
|
|
140
163
|
debug('Themes done');
|
|
141
164
|
|
|
142
|
-
// Serve robots.txt if not found in theme
|
|
143
|
-
siteApp.use(mw.servePublicFile('static', 'robots.txt', 'text/plain', config.get('caching:robotstxt:maxAge')));
|
|
144
|
-
|
|
145
|
-
// site map - this should probably be refactored to be an internal app
|
|
146
|
-
sitemapHandler(siteApp);
|
|
147
165
|
debug('Internal apps done');
|
|
148
166
|
|
|
149
167
|
// Add in all trailing slashes & remove uppercase
|
|
@@ -151,12 +169,12 @@ module.exports = function setupSiteApp(routerConfig) {
|
|
|
151
169
|
siteApp.use(shared.middleware.prettyUrls);
|
|
152
170
|
|
|
153
171
|
// ### Caching
|
|
154
|
-
siteApp.use(function frontendCaching(req, res, next) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
return
|
|
172
|
+
siteApp.use(async function frontendCaching(req, res, next) {
|
|
173
|
+
try {
|
|
174
|
+
const middleware = await mw.frontendCaching.getMiddleware();
|
|
175
|
+
return middleware(req, res, next);
|
|
176
|
+
} catch {
|
|
177
|
+
return next();
|
|
160
178
|
}
|
|
161
179
|
});
|
|
162
180
|
|
|
@@ -7,6 +7,7 @@ const slugFilterOrder = require('./utils/slug-filter-order');
|
|
|
7
7
|
const localUtils = require('../../index');
|
|
8
8
|
const mobiledoc = require('../../../../../lib/mobiledoc');
|
|
9
9
|
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
|
|
10
|
+
const postsSchema = require('../../../../../data/schema').tables.posts;
|
|
10
11
|
const clean = require('./utils/clean');
|
|
11
12
|
const lexical = require('../../../../../lib/lexical');
|
|
12
13
|
const sentry = require('../../../../../../shared/sentry');
|
|
@@ -16,6 +17,16 @@ const messages = {
|
|
|
16
17
|
failedHtmlToLexical: 'Failed to convert HTML to Lexical'
|
|
17
18
|
};
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Selects all allowed columns for the given frame.
|
|
22
|
+
*
|
|
23
|
+
* NOTE: This doesn't stop them from being FETCHED, just returned in the response. This causes
|
|
24
|
+
* the output serializer to remove them from the data object before returning.
|
|
25
|
+
*
|
|
26
|
+
* NOTE: This is only intended for the Content API. We need these fields within Admin API responses.
|
|
27
|
+
*
|
|
28
|
+
* @param {Object} frame - The frame object.
|
|
29
|
+
*/
|
|
19
30
|
function removeSourceFormats(frame) {
|
|
20
31
|
if (frame.options.formats?.includes('mobiledoc') || frame.options.formats?.includes('lexical')) {
|
|
21
32
|
frame.options.formats = frame.options.formats.filter((format) => {
|
|
@@ -24,6 +35,33 @@ function removeSourceFormats(frame) {
|
|
|
24
35
|
}
|
|
25
36
|
}
|
|
26
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Selects all allowed columns for the given frame.
|
|
40
|
+
*
|
|
41
|
+
* This removes the lexical and mobiledoc columns from the query. This is a performance improvement as we never intend
|
|
42
|
+
* to expose those columns in the content API and they are very large datasets to be passing around and de/serializing.
|
|
43
|
+
*
|
|
44
|
+
* NOTE: This is only intended for the Content API. We need these fields within Admin API responses.
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} frame - The frame object.
|
|
47
|
+
*/
|
|
48
|
+
function selectAllAllowedColumns(frame) {
|
|
49
|
+
if (!frame.options.columns && !frame.options.selectRaw) {
|
|
50
|
+
// Because we're returning columns directly from the table we need to remove info columns like @@UNIQUE_CONSTRAINTS@@
|
|
51
|
+
frame.options.selectRaw = _.keys(_.omit(postsSchema, ['lexical','mobiledoc','@@UNIQUE_CONSTRAINTS@@'])).join(',');
|
|
52
|
+
} else if (frame.options.columns) {
|
|
53
|
+
frame.options.columns = frame.options.columns.filter((column) => {
|
|
54
|
+
return !['mobiledoc', 'lexical'].includes(column);
|
|
55
|
+
});
|
|
56
|
+
} else if (frame.options.selectRaw) {
|
|
57
|
+
frame.options.selectRaw = frame.options.selectRaw.split(',').map((column) => {
|
|
58
|
+
return column.trim();
|
|
59
|
+
}).filter((column) => {
|
|
60
|
+
return !['mobiledoc', 'lexical'].includes(column);
|
|
61
|
+
}).join(',');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
27
65
|
/**
|
|
28
66
|
* Map names of relations to the internal names
|
|
29
67
|
*/
|
|
@@ -128,7 +166,8 @@ module.exports = {
|
|
|
128
166
|
*/
|
|
129
167
|
if (localUtils.isContentAPI(frame)) {
|
|
130
168
|
// CASE: the content api endpoint for posts should not return mobiledoc or lexical
|
|
131
|
-
removeSourceFormats(frame);
|
|
169
|
+
removeSourceFormats(frame); // remove from the format field
|
|
170
|
+
selectAllAllowedColumns(frame); // remove from any specified column or selectRaw options
|
|
132
171
|
|
|
133
172
|
setDefaultOrder(frame);
|
|
134
173
|
forceVisibilityColumn(frame);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
1
2
|
const _ = require('lodash');
|
|
2
3
|
const logging = require('@tryghost/logging');
|
|
3
4
|
const membersService = require('./service');
|
|
@@ -11,12 +12,65 @@ const {
|
|
|
11
12
|
} = require('./utils');
|
|
12
13
|
const errors = require('@tryghost/errors');
|
|
13
14
|
const tpl = require('@tryghost/tpl');
|
|
15
|
+
const onHeaders = require('on-headers');
|
|
16
|
+
const tiersService = require('../tiers/service');
|
|
17
|
+
const config = require('../../../shared/config');
|
|
14
18
|
|
|
15
19
|
const messages = {
|
|
16
20
|
missingUuid: 'Missing uuid.',
|
|
17
21
|
invalidUuid: 'Invalid uuid.'
|
|
18
22
|
};
|
|
19
23
|
|
|
24
|
+
const getFreeTier = async function getFreeTier() {
|
|
25
|
+
const response = await tiersService.api.browse();
|
|
26
|
+
const freeTier = response.data.find(tier => tier.type === 'free');
|
|
27
|
+
return freeTier;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Sets the ghost-access and ghost-access-hmac cookies on the response object
|
|
32
|
+
* @param {object} member - The member object
|
|
33
|
+
* @param {import('express').Response} res - The express response object to set the cookies on
|
|
34
|
+
* @returns
|
|
35
|
+
*/
|
|
36
|
+
const setAccessCookies = function setAccessCookies(member = undefined, res, freeTier) {
|
|
37
|
+
if (!member) {
|
|
38
|
+
const accessCookie = `ghost-access=null; Max-Age=0; Path=/; HttpOnly; SameSite=Strict;`;
|
|
39
|
+
const hmacCookie = `ghost-access-hmac=null; Max-Age=0; Path=/; HttpOnly; SameSite=Strict;`;
|
|
40
|
+
const existingCookies = res.getHeader('Set-Cookie') || [];
|
|
41
|
+
const cookiesToSet = [accessCookie, hmacCookie].concat(existingCookies);
|
|
42
|
+
|
|
43
|
+
res.setHeader('Set-Cookie', cookiesToSet);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const hmacSecret = config.get('cacheMembersContent:hmacSecret');
|
|
47
|
+
if (!hmacSecret) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const activeSubscription = member.subscriptions?.find(sub => sub.status === 'active');
|
|
51
|
+
|
|
52
|
+
const cookieTimestamp = Math.floor(Date.now() / 1000); // to mitigate a cookie replay attack
|
|
53
|
+
const memberTier = activeSubscription && activeSubscription.tier.id || freeTier.id;
|
|
54
|
+
const memberTierAndTimestamp = `${memberTier}:${cookieTimestamp}`;
|
|
55
|
+
const memberTierHmac = crypto.createHmac('sha256', hmacSecret).update(memberTierAndTimestamp).digest('hex');
|
|
56
|
+
|
|
57
|
+
const maxAge = 3600;
|
|
58
|
+
const accessCookie = `ghost-access=${memberTierAndTimestamp}; Max-Age=${maxAge}; Path=/; HttpOnly; SameSite=Strict;`;
|
|
59
|
+
const hmacCookie = `ghost-access-hmac=${memberTierHmac}; Max-Age=${maxAge}; Path=/; HttpOnly; SameSite=Strict;`;
|
|
60
|
+
|
|
61
|
+
const existingCookies = res.getHeader('Set-Cookie') || [];
|
|
62
|
+
const cookiesToSet = [accessCookie, hmacCookie].concat(existingCookies);
|
|
63
|
+
res.setHeader('Set-Cookie', cookiesToSet);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const accessInfoSession = async function accessInfoSession(req, res, next) {
|
|
67
|
+
const freeTier = await getFreeTier();
|
|
68
|
+
onHeaders(res, function () {
|
|
69
|
+
setAccessCookies(req.member, res, freeTier);
|
|
70
|
+
});
|
|
71
|
+
next();
|
|
72
|
+
};
|
|
73
|
+
|
|
20
74
|
// @TODO: This piece of middleware actually belongs to the frontend, not to the member app
|
|
21
75
|
// Need to figure a way to separate these things (e.g. frontend actually talks to members API)
|
|
22
76
|
const loadMemberSession = async function loadMemberSession(req, res, next) {
|
|
@@ -242,6 +296,16 @@ const createSessionFromMagicLink = async function createSessionFromMagicLink(req
|
|
|
242
296
|
// Note: don't reset 'member_login', or that would give an easy way around user enumeration by logging in to a manually created account
|
|
243
297
|
const subscriptions = member && member.subscriptions || [];
|
|
244
298
|
|
|
299
|
+
if (config.get('cacheMembersContent:enabled')) {
|
|
300
|
+
// Set the ghost-access cookies to enable tier-based caching
|
|
301
|
+
try {
|
|
302
|
+
const freeTier = await getFreeTier();
|
|
303
|
+
setAccessCookies(member, res, freeTier);
|
|
304
|
+
} catch {
|
|
305
|
+
// This is a non-critical operation, so we can safely ignore any errors
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
245
309
|
const action = req.query.action;
|
|
246
310
|
|
|
247
311
|
if (action === 'signup' || action === 'signup-paid' || action === 'subscribe') {
|
|
@@ -322,5 +386,6 @@ module.exports = {
|
|
|
322
386
|
updateMemberData,
|
|
323
387
|
updateMemberNewsletters,
|
|
324
388
|
deleteSession,
|
|
389
|
+
accessInfoSession,
|
|
325
390
|
deleteSuppression
|
|
326
391
|
};
|
|
@@ -33,12 +33,12 @@ const mongoTransformer = flowRight(statusTransformer, rejectNonStatusTransformer
|
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* @typedef {object} BaseOptions
|
|
36
|
-
* @prop {import('knex').Transaction} transacting
|
|
36
|
+
* @prop {import('knex').Knex.Transaction} transacting
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* @typedef {object} ListOptions
|
|
41
|
-
* @prop {import('knex').Transaction} transacting
|
|
41
|
+
* @prop {import('knex').Knex.Transaction} transacting
|
|
42
42
|
* @prop {string} filter
|
|
43
43
|
*/
|
|
44
44
|
|
|
@@ -56,7 +56,7 @@ class OfferBookshelfRepository {
|
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* @template T
|
|
59
|
-
* @param {(t: import('knex').Transaction) => Promise<T>} cb
|
|
59
|
+
* @param {(t: import('knex').Knex.Transaction) => Promise<T>} cb
|
|
60
60
|
* @returns {Promise<T>}
|
|
61
61
|
*/
|
|
62
62
|
async createTransaction(cb) {
|
|
@@ -45,7 +45,14 @@ module.exports = function setupMembersApp() {
|
|
|
45
45
|
membersApp.put('/api/member/newsletters', bodyParser.json({limit: '50mb'}), middleware.updateMemberNewsletters);
|
|
46
46
|
|
|
47
47
|
// Get and update member data
|
|
48
|
-
|
|
48
|
+
// Caching members content is an experimental feature
|
|
49
|
+
const shouldCacheMembersContent = config.get('cacheMembersContent:enabled');
|
|
50
|
+
if (shouldCacheMembersContent) {
|
|
51
|
+
membersApp.get('/api/member', middleware.loadMemberSession, middleware.accessInfoSession, middleware.getMemberData);
|
|
52
|
+
} else {
|
|
53
|
+
membersApp.get('/api/member', middleware.getMemberData);
|
|
54
|
+
}
|
|
55
|
+
|
|
49
56
|
membersApp.put('/api/member', bodyParser.json({limit: '50mb'}), middleware.updateMemberData);
|
|
50
57
|
membersApp.post('/api/member/email', bodyParser.json({limit: '50mb'}), (req, res) => membersService.api.middleware.updateEmailAddress(req, res));
|
|
51
58
|
|
package/core/shared/sentry.js
CHANGED