ghost 5.76.1 → 5.77.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/components/tryghost-adapter-cache-memory-ttl-5.77.0.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.77.0.tgz +0 -0
- package/components/{tryghost-adapter-manager-5.76.1.tgz → tryghost-adapter-manager-5.77.0.tgz} +0 -0
- package/components/tryghost-announcement-bar-settings-5.77.0.tgz +0 -0
- package/components/tryghost-api-framework-5.77.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.77.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.77.0.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.77.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.77.0.tgz +0 -0
- package/components/tryghost-collections-5.77.0.tgz +0 -0
- package/components/tryghost-constants-5.77.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.77.0.tgz +0 -0
- package/components/{tryghost-data-generator-5.76.1.tgz → tryghost-data-generator-5.77.0.tgz} +0 -0
- package/components/{tryghost-domain-events-5.76.1.tgz → tryghost-domain-events-5.77.0.tgz} +0 -0
- package/components/tryghost-donations-5.77.0.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.77.0.tgz +0 -0
- package/components/tryghost-email-addresses-5.77.0.tgz +0 -0
- package/components/{tryghost-email-analytics-provider-mailgun-5.76.1.tgz → tryghost-email-analytics-provider-mailgun-5.77.0.tgz} +0 -0
- package/components/tryghost-email-analytics-service-5.77.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.77.0.tgz +0 -0
- package/components/{tryghost-email-events-5.76.1.tgz → tryghost-email-events-5.77.0.tgz} +0 -0
- package/components/tryghost-email-service-5.77.0.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.77.0.tgz +0 -0
- package/components/{tryghost-express-dynamic-redirects-5.76.1.tgz → tryghost-express-dynamic-redirects-5.77.0.tgz} +0 -0
- package/components/{tryghost-external-media-inliner-5.76.1.tgz → tryghost-external-media-inliner-5.77.0.tgz} +0 -0
- package/components/tryghost-extract-api-key-5.77.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.77.0.tgz +0 -0
- package/components/tryghost-i18n-5.77.0.tgz +0 -0
- package/components/{tryghost-importer-handler-content-files-5.76.1.tgz → tryghost-importer-handler-content-files-5.77.0.tgz} +0 -0
- package/components/tryghost-importer-revue-5.77.0.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.77.0.tgz +0 -0
- package/components/{tryghost-job-manager-5.76.1.tgz → tryghost-job-manager-5.77.0.tgz} +0 -0
- package/components/tryghost-link-redirects-5.77.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.77.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.77.0.tgz +0 -0
- package/components/{tryghost-magic-link-5.76.1.tgz → tryghost-magic-link-5.77.0.tgz} +0 -0
- package/components/{tryghost-mail-events-5.76.1.tgz → tryghost-mail-events-5.77.0.tgz} +0 -0
- package/components/{tryghost-mailgun-client-5.76.1.tgz → tryghost-mailgun-client-5.77.0.tgz} +0 -0
- package/components/{tryghost-member-attribution-5.76.1.tgz → tryghost-member-attribution-5.77.0.tgz} +0 -0
- package/components/tryghost-member-events-5.77.0.tgz +0 -0
- package/components/tryghost-members-api-5.77.0.tgz +0 -0
- package/components/tryghost-members-csv-5.77.0.tgz +0 -0
- package/components/{tryghost-members-events-service-5.76.1.tgz → tryghost-members-events-service-5.77.0.tgz} +0 -0
- package/components/tryghost-members-importer-5.77.0.tgz +0 -0
- package/components/{tryghost-members-offers-5.76.1.tgz → tryghost-members-offers-5.77.0.tgz} +0 -0
- package/components/{tryghost-members-payments-5.76.1.tgz → tryghost-members-payments-5.77.0.tgz} +0 -0
- package/components/{tryghost-members-ssr-5.76.1.tgz → tryghost-members-ssr-5.77.0.tgz} +0 -0
- package/components/{tryghost-members-stripe-service-5.76.1.tgz → tryghost-members-stripe-service-5.77.0.tgz} +0 -0
- package/components/tryghost-mentions-email-report-5.77.0.tgz +0 -0
- package/components/tryghost-milestones-5.77.0.tgz +0 -0
- package/components/tryghost-minifier-5.77.0.tgz +0 -0
- package/components/tryghost-model-to-domain-event-interceptor-5.77.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.77.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.77.0.tgz +0 -0
- package/components/{tryghost-mw-error-handler-5.76.1.tgz → tryghost-mw-error-handler-5.77.0.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.77.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.77.0.tgz +0 -0
- package/components/{tryghost-mw-version-match-5.76.1.tgz → tryghost-mw-version-match-5.77.0.tgz} +0 -0
- package/components/tryghost-mw-vhost-5.77.0.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.77.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.77.0.tgz +0 -0
- package/components/{tryghost-package-json-5.76.1.tgz → tryghost-package-json-5.77.0.tgz} +0 -0
- package/components/tryghost-post-events-5.77.0.tgz +0 -0
- package/components/tryghost-post-revisions-5.77.0.tgz +0 -0
- package/components/tryghost-posts-service-5.77.0.tgz +0 -0
- package/components/tryghost-recommendations-5.77.0.tgz +0 -0
- package/components/tryghost-referrers-5.77.0.tgz +0 -0
- package/components/{tryghost-security-5.76.1.tgz → tryghost-security-5.77.0.tgz} +0 -0
- package/components/{tryghost-session-service-5.76.1.tgz → tryghost-session-service-5.77.0.tgz} +0 -0
- package/components/tryghost-settings-path-manager-5.77.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.77.0.tgz +0 -0
- package/components/{tryghost-staff-service-5.76.1.tgz → tryghost-staff-service-5.77.0.tgz} +0 -0
- package/components/tryghost-stats-service-5.77.0.tgz +0 -0
- package/components/tryghost-tiers-5.77.0.tgz +0 -0
- package/components/{tryghost-update-check-service-5.76.1.tgz → tryghost-update-check-service-5.77.0.tgz} +0 -0
- package/components/{tryghost-verification-trigger-5.76.1.tgz → tryghost-verification-trigger-5.77.0.tgz} +0 -0
- package/components/tryghost-version-notifications-data-service-5.77.0.tgz +0 -0
- package/components/tryghost-webmentions-5.77.0.tgz +0 -0
- package/core/boot.js +3 -1
- package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +1 -1
- package/core/built/admin/assets/admin-x-demo/{index-3d0e9e6b.mjs → index-eee22c84.mjs} +348 -346
- package/core/built/admin/assets/admin-x-demo/{modals-f20fa267.mjs → modals-e4b12fba.mjs} +38 -38
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-05a0f378.mjs → CodeEditorView-c3b34635.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-6cc60dce.mjs → index-28680861.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-518e7d26.mjs → index-8808e054.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-1c923713.mjs → index-99bd65a2.mjs} +2579 -2583
- package/core/built/admin/assets/admin-x-settings/{modals-516e0c98.mjs → modals-f491bd44.mjs} +5248 -5253
- package/core/built/admin/assets/{chunk.143.16780ac5d51ee6a46eb2.js → chunk.143.16228699a88898175515.js} +5 -5
- package/core/built/admin/assets/{chunk.178.19c31dac4249a6e158b6.js → chunk.178.09e01be47e7daa486981.js} +4 -4
- package/core/built/admin/assets/{chunk.664.0bd9e83a6b03130112ae.js → chunk.664.99d0951b803d7ec00d8b.js} +2 -2
- package/core/built/admin/assets/{ghost-971615b195b524bf97f678a350865195.js → ghost-90accaf45b85c7bc55ca781472d27e8e.js} +74 -91
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +8 -8
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +4 -4
- package/core/built/admin/index.html +4 -4
- package/core/frontend/helpers/excerpt.js +3 -2
- package/core/frontend/helpers/get.js +38 -22
- package/core/frontend/services/routing/controllers/previews.js +3 -2
- package/core/server/api/endpoints/newsletters.js +1 -2
- package/core/server/api/endpoints/stats.js +40 -5
- package/core/server/api/endpoints/utils/serializers/output/mappers/newsletters.js +12 -0
- package/core/server/services/explore/ExploreService.js +1 -1
- package/core/server/services/newsletters/NewslettersService.js +22 -3
- package/core/server/services/posts-public/service.js +4 -20
- package/core/server/services/segment/index.js +0 -14
- package/core/server/services/stats/service.js +26 -3
- package/core/server/services/tags-public/service.js +4 -19
- package/core/shared/labs.js +1 -1
- package/core/shared/sentry.js +28 -1
- package/package.json +150 -152
- package/yarn.lock +607 -1542
- package/components/tryghost-adapter-cache-memory-ttl-5.76.1.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.76.1.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.76.1.tgz +0 -0
- package/components/tryghost-api-framework-5.76.1.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.76.1.tgz +0 -0
- package/components/tryghost-audience-feedback-5.76.1.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.76.1.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.76.1.tgz +0 -0
- package/components/tryghost-collections-5.76.1.tgz +0 -0
- package/components/tryghost-constants-5.76.1.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.76.1.tgz +0 -0
- package/components/tryghost-donations-5.76.1.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.76.1.tgz +0 -0
- package/components/tryghost-email-addresses-5.76.1.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.76.1.tgz +0 -0
- package/components/tryghost-email-content-generator-5.76.1.tgz +0 -0
- package/components/tryghost-email-service-5.76.1.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.76.1.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.76.1.tgz +0 -0
- package/components/tryghost-extract-api-key-5.76.1.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.76.1.tgz +0 -0
- package/components/tryghost-i18n-5.76.1.tgz +0 -0
- package/components/tryghost-importer-revue-5.76.1.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.76.1.tgz +0 -0
- package/components/tryghost-link-redirects-5.76.1.tgz +0 -0
- package/components/tryghost-link-replacer-5.76.1.tgz +0 -0
- package/components/tryghost-link-tracking-5.76.1.tgz +0 -0
- package/components/tryghost-member-events-5.76.1.tgz +0 -0
- package/components/tryghost-members-api-5.76.1.tgz +0 -0
- package/components/tryghost-members-csv-5.76.1.tgz +0 -0
- package/components/tryghost-members-importer-5.76.1.tgz +0 -0
- package/components/tryghost-mentions-email-report-5.76.1.tgz +0 -0
- package/components/tryghost-milestones-5.76.1.tgz +0 -0
- package/components/tryghost-minifier-5.76.1.tgz +0 -0
- package/components/tryghost-model-to-domain-event-interceptor-5.76.1.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.76.1.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.76.1.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.76.1.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.76.1.tgz +0 -0
- package/components/tryghost-mw-vhost-5.76.1.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.76.1.tgz +0 -0
- package/components/tryghost-oembed-service-5.76.1.tgz +0 -0
- package/components/tryghost-post-events-5.76.1.tgz +0 -0
- package/components/tryghost-post-revisions-5.76.1.tgz +0 -0
- package/components/tryghost-posts-service-5.76.1.tgz +0 -0
- package/components/tryghost-recommendations-5.76.1.tgz +0 -0
- package/components/tryghost-referrers-5.76.1.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.76.1.tgz +0 -0
- package/components/tryghost-slack-notifications-5.76.1.tgz +0 -0
- package/components/tryghost-stats-service-5.76.1.tgz +0 -0
- package/components/tryghost-tiers-5.76.1.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.76.1.tgz +0 -0
- package/components/tryghost-webmentions-5.76.1.tgz +0 -0
- package/core/server/services/segment/ModelEventsAnalytics.js +0 -108
- /package/core/built/admin/assets/{chunk.664.0bd9e83a6b03130112ae.js.LICENSE.txt → chunk.664.99d0951b803d7ec00d8b.js.LICENSE.txt} +0 -0
|
@@ -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%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%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%225.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%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%225.77%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22ember-websockets%22%3A%7B%22socketIO%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%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%22a9a58f16a8%22%2C%22adminXDemoFilename%22%3A%22admin-x-demo.js%22%2C%22adminXDemoHash%22%3A%2233d2c60ad0%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%22d07813452e%22%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
58
58
|
|
|
59
59
|
<script src="assets/vendor-6b83da0c6e6fede335947219c59e1543.js"></script>
|
|
60
|
-
<script src="assets/chunk.664.
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
60
|
+
<script src="assets/chunk.664.99d0951b803d7ec00d8b.js"></script>
|
|
61
|
+
<script src="assets/chunk.143.16228699a88898175515.js"></script>
|
|
62
|
+
<script src="assets/ghost-90accaf45b85c7bc55ca781472d27e8e.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -22,7 +22,7 @@ module.exports = function excerpt(options) {
|
|
|
22
22
|
} else {
|
|
23
23
|
excerptText = '';
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
excerptText = _.escape(excerptText);
|
|
27
27
|
|
|
28
28
|
truncateOptions = _.reduce(truncateOptions, (_truncateOptions, value, key) => {
|
|
@@ -32,8 +32,9 @@ module.exports = function excerpt(options) {
|
|
|
32
32
|
return _truncateOptions;
|
|
33
33
|
}, {});
|
|
34
34
|
|
|
35
|
+
// For custom excerpts, make sure we truncate them only based on length
|
|
35
36
|
if (!_.isEmpty(this.custom_excerpt)) {
|
|
36
|
-
truncateOptions.characters =
|
|
37
|
+
truncateOptions.characters = excerptText.length; // length is expanded by use of escaped characters
|
|
37
38
|
if (truncateOptions.words) {
|
|
38
39
|
delete truncateOptions.words;
|
|
39
40
|
}
|
|
@@ -7,6 +7,7 @@ const {hbs} = require('../services/handlebars');
|
|
|
7
7
|
const logging = require('@tryghost/logging');
|
|
8
8
|
const errors = require('@tryghost/errors');
|
|
9
9
|
const tpl = require('@tryghost/tpl');
|
|
10
|
+
const Sentry = require('@sentry/node');
|
|
10
11
|
|
|
11
12
|
const _ = require('lodash');
|
|
12
13
|
const jsonpath = require('jsonpath');
|
|
@@ -206,32 +207,47 @@ module.exports = async function get(resource, options) {
|
|
|
206
207
|
|
|
207
208
|
// Parse the options we're going to pass to the API
|
|
208
209
|
apiOptions = parseOptions(ghostGlobals, this, apiOptions);
|
|
210
|
+
let apiOptionsString = Object.entries(apiOptions)
|
|
211
|
+
.map(([key, value]) => ` ${key}="${value}"`)
|
|
212
|
+
.join('');
|
|
209
213
|
apiOptions.context = {member: data.member};
|
|
210
|
-
|
|
211
214
|
try {
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
// `{{#get "posts" as |result pageInfo|}}`
|
|
224
|
-
const blockParams = [response[resource]];
|
|
225
|
-
if (response.meta && response.meta.pagination) {
|
|
226
|
-
response.pagination = response.meta.pagination;
|
|
227
|
-
blockParams.push(response.meta.pagination);
|
|
228
|
-
}
|
|
215
|
+
const spanName = `{{#get "${resource}"${apiOptionsString}}} ${data.member ? 'member' : 'public'}`;
|
|
216
|
+
const result = await Sentry.startSpan({
|
|
217
|
+
op: 'frontend.helpers.get',
|
|
218
|
+
name: spanName,
|
|
219
|
+
tags: {
|
|
220
|
+
resource,
|
|
221
|
+
...apiOptions,
|
|
222
|
+
context: data.member ? 'member' : 'public'
|
|
223
|
+
}
|
|
224
|
+
}, async (span) => {
|
|
225
|
+
const response = await makeAPICall(resource, controllerName, action, apiOptions);
|
|
229
226
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
227
|
+
// prepare data properties for use with handlebars
|
|
228
|
+
if (response[resource] && response[resource].length) {
|
|
229
|
+
response[resource].forEach(prepareContextResource);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// used for logging details of slow requests
|
|
233
|
+
returnedRowsCount = response[resource] && response[resource].length;
|
|
234
|
+
span?.setTag('returnedRows', returnedRowsCount);
|
|
235
|
+
|
|
236
|
+
// block params allows the theme developer to name the data using something like
|
|
237
|
+
// `{{#get "posts" as |result pageInfo|}}`
|
|
238
|
+
const blockParams = [response[resource]];
|
|
239
|
+
if (response.meta && response.meta.pagination) {
|
|
240
|
+
response.pagination = response.meta.pagination;
|
|
241
|
+
blockParams.push(response.meta.pagination);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Call the main template function
|
|
245
|
+
return options.fn(response, {
|
|
246
|
+
data: data,
|
|
247
|
+
blockParams: blockParams
|
|
248
|
+
});
|
|
234
249
|
});
|
|
250
|
+
return result;
|
|
235
251
|
} catch (error) {
|
|
236
252
|
logging.error(error);
|
|
237
253
|
data.error = error.message;
|
|
@@ -49,12 +49,13 @@ module.exports = function previewController(req, res, next) {
|
|
|
49
49
|
return next();
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// published content should only resolve to /:slug - /p/:uuid is for drafts only in lieu of an actual preview api
|
|
52
53
|
if (post.status === 'published') {
|
|
53
54
|
return urlUtils.redirect301(res, routerManager.getUrlByResourceId(post.id, {withSubdirectory: true}));
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
//
|
|
57
|
-
if (post.status
|
|
57
|
+
// once an email-only post has been sent it shouldn't be available via /p/ to avoid leaking members-only content
|
|
58
|
+
if (post.status === 'sent') {
|
|
58
59
|
return urlUtils.redirect301(res, urlUtils.urlJoin('/email', post.uuid, '/'));
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const models = require('../../models');
|
|
2
1
|
const allowedIncludes = ['count.posts', 'count.members', 'count.active_members'];
|
|
3
2
|
|
|
4
3
|
const newslettersService = require('../../services/newsletters');
|
|
@@ -27,7 +26,7 @@ module.exports = {
|
|
|
27
26
|
},
|
|
28
27
|
permissions: true,
|
|
29
28
|
query(frame) {
|
|
30
|
-
return
|
|
29
|
+
return newslettersService.browse(frame.options);
|
|
31
30
|
}
|
|
32
31
|
},
|
|
33
32
|
|
|
@@ -10,8 +10,14 @@ module.exports = {
|
|
|
10
10
|
docName: 'members',
|
|
11
11
|
method: 'browse'
|
|
12
12
|
},
|
|
13
|
+
cache: statsService.cache,
|
|
14
|
+
generateCacheKeyData() {
|
|
15
|
+
return {
|
|
16
|
+
method: 'memberCountHistory'
|
|
17
|
+
};
|
|
18
|
+
},
|
|
13
19
|
async query() {
|
|
14
|
-
return await statsService.getMemberCountHistory();
|
|
20
|
+
return await statsService.api.getMemberCountHistory();
|
|
15
21
|
}
|
|
16
22
|
},
|
|
17
23
|
mrr: {
|
|
@@ -22,8 +28,14 @@ module.exports = {
|
|
|
22
28
|
docName: 'members',
|
|
23
29
|
method: 'browse'
|
|
24
30
|
},
|
|
31
|
+
cache: statsService.cache,
|
|
32
|
+
generateCacheKeyData() {
|
|
33
|
+
return {
|
|
34
|
+
method: 'mrr'
|
|
35
|
+
};
|
|
36
|
+
},
|
|
25
37
|
async query() {
|
|
26
|
-
return await statsService.getMRRHistory();
|
|
38
|
+
return await statsService.api.getMRRHistory();
|
|
27
39
|
}
|
|
28
40
|
},
|
|
29
41
|
subscriptions: {
|
|
@@ -34,8 +46,15 @@ module.exports = {
|
|
|
34
46
|
docName: 'members',
|
|
35
47
|
method: 'browse'
|
|
36
48
|
},
|
|
49
|
+
cache: statsService.cache,
|
|
50
|
+
generateCacheKeyData() {
|
|
51
|
+
return {
|
|
52
|
+
method: 'subscriptions'
|
|
53
|
+
|
|
54
|
+
};
|
|
55
|
+
},
|
|
37
56
|
async query() {
|
|
38
|
-
return await statsService.getSubscriptionCountHistory();
|
|
57
|
+
return await statsService.api.getSubscriptionCountHistory();
|
|
39
58
|
}
|
|
40
59
|
},
|
|
41
60
|
postReferrers: {
|
|
@@ -49,8 +68,18 @@ module.exports = {
|
|
|
49
68
|
docName: 'posts',
|
|
50
69
|
method: 'browse'
|
|
51
70
|
},
|
|
71
|
+
cache: statsService.cache,
|
|
72
|
+
generateCacheKeyData(frame) {
|
|
73
|
+
return {
|
|
74
|
+
method: 'postReferrers',
|
|
75
|
+
data: {
|
|
76
|
+
id: frame.data.id
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
};
|
|
80
|
+
},
|
|
52
81
|
async query(frame) {
|
|
53
|
-
return await statsService.getPostReferrers(frame.data.id);
|
|
82
|
+
return await statsService.api.getPostReferrers(frame.data.id);
|
|
54
83
|
}
|
|
55
84
|
},
|
|
56
85
|
referrersHistory: {
|
|
@@ -64,8 +93,14 @@ module.exports = {
|
|
|
64
93
|
docName: 'posts',
|
|
65
94
|
method: 'browse'
|
|
66
95
|
},
|
|
96
|
+
cache: statsService.cache,
|
|
97
|
+
generateCacheKeyData() {
|
|
98
|
+
return {
|
|
99
|
+
method: 'referrersHistory'
|
|
100
|
+
};
|
|
101
|
+
},
|
|
67
102
|
async query() {
|
|
68
|
-
return await statsService.getReferrersHistory();
|
|
103
|
+
return await statsService.api.getReferrersHistory();
|
|
69
104
|
}
|
|
70
105
|
}
|
|
71
106
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const utils = require('../../../index');
|
|
2
|
+
const emailAddressService = require('../../../../../../services/email-address');
|
|
2
3
|
|
|
3
4
|
module.exports = (model, frame) => {
|
|
4
5
|
const jsonModel = model.toJSON(frame.options);
|
|
@@ -19,6 +20,17 @@ module.exports = (model, frame) => {
|
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
return serialized;
|
|
23
|
+
} else {
|
|
24
|
+
if (jsonModel.sender_email && jsonModel.sender_reply_to === 'newsletter') {
|
|
25
|
+
// If sender_email is not allowed, we'll return it as the sender_reply_to instead, so we display the current situation correctly in the frontend
|
|
26
|
+
// If one of the properties was changed, we need to reset sender_email in case it was not changed but is invalid in the database
|
|
27
|
+
// which can happen after a config change (= auto correcting behaviour)
|
|
28
|
+
const validated = emailAddressService.service.validate(jsonModel.sender_email, 'from');
|
|
29
|
+
if (!validated.allowed) {
|
|
30
|
+
jsonModel.sender_reply_to = jsonModel.sender_email;
|
|
31
|
+
jsonModel.sender_email = null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
22
34
|
}
|
|
23
35
|
|
|
24
36
|
return jsonModel;
|
|
@@ -24,7 +24,7 @@ module.exports = class ExploreService {
|
|
|
24
24
|
*/
|
|
25
25
|
async fetchData() {
|
|
26
26
|
const totalMembers = await this.MembersService.stats.getTotalMembers();
|
|
27
|
-
const mrrStats = await this.StatsService.getMRRHistory();
|
|
27
|
+
const mrrStats = await this.StatsService.api.getMRRHistory();
|
|
28
28
|
|
|
29
29
|
const {description, icon, title, url, accent_color: accentColor, locale} = this.PublicConfigService.site;
|
|
30
30
|
|
|
@@ -92,7 +92,7 @@ class NewslettersService {
|
|
|
92
92
|
* @public
|
|
93
93
|
* @param {Object} options data (id, uuid, slug...)
|
|
94
94
|
* @param {Object} [options] options
|
|
95
|
-
* @returns {Promise<object>}
|
|
95
|
+
* @returns {Promise<object>}
|
|
96
96
|
*/
|
|
97
97
|
async read(data, options = {}) {
|
|
98
98
|
const newsletter = await this.NewsletterModel.findOne(data, options);
|
|
@@ -108,11 +108,14 @@ class NewslettersService {
|
|
|
108
108
|
/**
|
|
109
109
|
* @public
|
|
110
110
|
* @param {Object} [options] options
|
|
111
|
-
* @returns {Promise<object>}
|
|
111
|
+
* @returns {Promise<object>}
|
|
112
112
|
*/
|
|
113
113
|
async browse(options = {}) {
|
|
114
|
-
|
|
114
|
+
return await this.NewsletterModel.findPage(options);
|
|
115
|
+
}
|
|
115
116
|
|
|
117
|
+
async getAll(options = {}) {
|
|
118
|
+
const newsletters = await this.NewsletterModel.findAll(options);
|
|
116
119
|
return newsletters.toJSON();
|
|
117
120
|
}
|
|
118
121
|
|
|
@@ -298,6 +301,10 @@ class NewslettersService {
|
|
|
298
301
|
}
|
|
299
302
|
|
|
300
303
|
if (validated.verificationEmailRequired) {
|
|
304
|
+
if (type === 'replyTo' && email === newsletter.get('sender_email')) {
|
|
305
|
+
// This is some custom behaviour that allows swapping sender_email to sender_reply_to without requiring validation again
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
301
308
|
delete cleanedAttrs[property];
|
|
302
309
|
emailsToVerify.push({email, property});
|
|
303
310
|
}
|
|
@@ -311,6 +318,18 @@ class NewslettersService {
|
|
|
311
318
|
}
|
|
312
319
|
}
|
|
313
320
|
|
|
321
|
+
// If one of the properties was changed, we need to reset sender_email in case it was not changed but is invalid in the database
|
|
322
|
+
// which can happen after a config change (= auto correcting behaviour)
|
|
323
|
+
const didChangeReplyTo = newsletter && attrs.sender_reply_to !== undefined && newsletter.get('sender_reply_to') !== attrs.sender_reply_to;
|
|
324
|
+
const didChangeSenderEmail = newsletter && (attrs.sender_email !== undefined && newsletter.get('sender_email') !== attrs.sender_email);
|
|
325
|
+
if (didChangeReplyTo && !didChangeSenderEmail && newsletter.get('sender_email')) {
|
|
326
|
+
const validated = this.emailAddressService.service.validate(newsletter.get('sender_email'), 'from');
|
|
327
|
+
if (!validated.allowed) {
|
|
328
|
+
logging.info(`Resetting sender_email for newsletter ${newsletter.id} because it became invalid`);
|
|
329
|
+
cleanedAttrs.sender_email = null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
314
333
|
return {cleanedAttrs, emailsToVerify};
|
|
315
334
|
}
|
|
316
335
|
|
|
@@ -8,34 +8,18 @@ class PostsPublicServiceWrapper {
|
|
|
8
8
|
// Wire up all the dependencies
|
|
9
9
|
const adapterManager = require('../adapter-manager');
|
|
10
10
|
const config = require('../../../shared/config');
|
|
11
|
-
const EventAwareCacheWrapper = require('@tryghost/event-aware-cache-wrapper');
|
|
12
11
|
const EventRegistry = require('../../lib/common/events');
|
|
13
12
|
|
|
14
13
|
let postsCache;
|
|
15
14
|
if (config.get('hostSettings:postsPublicCache:enabled')) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
resetEvents: ['site.changed'],
|
|
20
|
-
eventRegistry: EventRegistry
|
|
15
|
+
postsCache = adapterManager.getAdapter('cache:postsPublic');
|
|
16
|
+
EventRegistry.on('site.changed', () => {
|
|
17
|
+
postsCache.reset();
|
|
21
18
|
});
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
let cache;
|
|
25
|
-
if (postsCache) {
|
|
26
|
-
// @NOTE: exposing cache through getter and setter to not loose the context of "this"
|
|
27
|
-
cache = {
|
|
28
|
-
get() {
|
|
29
|
-
return postsCache.get(...arguments);
|
|
30
|
-
},
|
|
31
|
-
set() {
|
|
32
|
-
return postsCache.set(...arguments);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
21
|
this.api = {
|
|
38
|
-
cache:
|
|
22
|
+
cache: postsCache
|
|
39
23
|
};
|
|
40
24
|
}
|
|
41
25
|
}
|
|
@@ -3,9 +3,7 @@ const config = require('../../../shared/config');
|
|
|
3
3
|
const sentry = require('../../../shared/sentry');
|
|
4
4
|
const logging = require('@tryghost/logging');
|
|
5
5
|
const DomainEvents = require('@tryghost/domain-events');
|
|
6
|
-
const events = require('../../lib/common/events');
|
|
7
6
|
|
|
8
|
-
const ModelEventsAnalytics = require('./ModelEventsAnalytics');
|
|
9
7
|
const DomainEventsAnalytics = require('./DomainEventsAnalytics');
|
|
10
8
|
|
|
11
9
|
module.exports.init = function () {
|
|
@@ -22,18 +20,6 @@ module.exports.init = function () {
|
|
|
22
20
|
logging
|
|
23
21
|
});
|
|
24
22
|
|
|
25
|
-
const modelEventsAnalytics = new ModelEventsAnalytics({
|
|
26
|
-
analytics,
|
|
27
|
-
trackDefaults,
|
|
28
|
-
prefix,
|
|
29
|
-
exceptionHandler: sentry,
|
|
30
|
-
events,
|
|
31
|
-
logging
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// Listen to model events
|
|
35
|
-
modelEventsAnalytics.subscribeToEvents();
|
|
36
|
-
|
|
37
23
|
// Listen to domain events
|
|
38
24
|
subscribeToDomainEvents.subscribeToEvents();
|
|
39
25
|
};
|
|
@@ -1,4 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
class StatsServiceWrapper {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.api = null;
|
|
4
|
+
this.cache = null;
|
|
5
|
+
}
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
async init() {
|
|
8
|
+
if (this.api) {
|
|
9
|
+
// Already done
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const StatsService = require('@tryghost/stats-service');
|
|
14
|
+
const db = require('../../data/db');
|
|
15
|
+
|
|
16
|
+
this.api = StatsService.create({knex: db.knex});
|
|
17
|
+
|
|
18
|
+
const adapterManager = require('../adapter-manager');
|
|
19
|
+
const config = require('../../../shared/config');
|
|
20
|
+
|
|
21
|
+
if (config.get('hostSettings:statsCache:enabled')) {
|
|
22
|
+
this.cache = adapterManager.getAdapter('cache:stats');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = new StatsServiceWrapper();
|
|
@@ -8,33 +8,18 @@ class TagsPublicServiceWrapper {
|
|
|
8
8
|
// Wire up all the dependencies
|
|
9
9
|
const adapterManager = require('../adapter-manager');
|
|
10
10
|
const config = require('../../../shared/config');
|
|
11
|
-
const EventAwareCacheWrapper = require('@tryghost/event-aware-cache-wrapper');
|
|
12
11
|
const EventRegistry = require('../../lib/common/events');
|
|
13
12
|
|
|
14
13
|
let tagsCache;
|
|
15
14
|
if (config.get('hostSettings:tagsPublicCache:enabled')) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
resetEvents: ['site.changed'],
|
|
20
|
-
eventRegistry: EventRegistry
|
|
15
|
+
tagsCache = adapterManager.getAdapter('cache:tagsPublic');
|
|
16
|
+
EventRegistry.on('site.changed', () => {
|
|
17
|
+
tagsCache.reset();
|
|
21
18
|
});
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
let cache;
|
|
25
|
-
if (tagsCache) {
|
|
26
|
-
// @NOTE: exposing cache through getter and setter to not loose the context of "this"
|
|
27
|
-
cache = {
|
|
28
|
-
get() {
|
|
29
|
-
return tagsCache.get(...arguments);
|
|
30
|
-
},
|
|
31
|
-
set() {
|
|
32
|
-
return tagsCache.set(...arguments);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
21
|
this.api = {
|
|
37
|
-
cache:
|
|
22
|
+
cache: tagsCache
|
|
38
23
|
};
|
|
39
24
|
}
|
|
40
25
|
}
|
package/core/shared/labs.js
CHANGED
package/core/shared/sentry.js
CHANGED
|
@@ -57,6 +57,31 @@ const beforeSend = function (event, hint) {
|
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
+
const ALLOWED_HTTP_TRANSACTIONS = [
|
|
61
|
+
'/ghost/api', // any Ghost API call
|
|
62
|
+
'/members/api', // any Members API call
|
|
63
|
+
'/:slug', // any frontend post/page
|
|
64
|
+
'/author', // any frontend author page
|
|
65
|
+
'/tag' // any frontend tag page
|
|
66
|
+
].map((path) => {
|
|
67
|
+
// Sentry names HTTP transactions like: "<HTTP_METHOD> <PATH>" i.e. "GET /ghost/api/content/settings"
|
|
68
|
+
// Match any of the paths above with any HTTP method, and also the homepage "GET /"
|
|
69
|
+
return new RegExp(`^(GET|POST|PUT|DELETE)\\s(?<path>${path}\\/.+|\\/$)`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const beforeSendTransaction = function (event) {
|
|
73
|
+
// Drop transactions that are not in the allowed list
|
|
74
|
+
for (const transaction of ALLOWED_HTTP_TRANSACTIONS) {
|
|
75
|
+
const match = event.transaction.match(transaction);
|
|
76
|
+
|
|
77
|
+
if (match?.groups?.path) {
|
|
78
|
+
return event;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return null;
|
|
83
|
+
};
|
|
84
|
+
|
|
60
85
|
if (sentryConfig && !sentryConfig.disabled) {
|
|
61
86
|
const Sentry = require('@sentry/node');
|
|
62
87
|
const version = require('@tryghost/version').full;
|
|
@@ -72,7 +97,8 @@ if (sentryConfig && !sentryConfig.disabled) {
|
|
|
72
97
|
environment: environment,
|
|
73
98
|
maxValueLength: 1000,
|
|
74
99
|
integrations: [],
|
|
75
|
-
beforeSend
|
|
100
|
+
beforeSend,
|
|
101
|
+
beforeSendTransaction
|
|
76
102
|
};
|
|
77
103
|
|
|
78
104
|
// Enable tracing if sentry.tracing.enabled is true
|
|
@@ -117,6 +143,7 @@ if (sentryConfig && !sentryConfig.disabled) {
|
|
|
117
143
|
captureException: Sentry.captureException,
|
|
118
144
|
captureMessage: Sentry.captureMessage,
|
|
119
145
|
beforeSend: beforeSend,
|
|
146
|
+
beforeSendTransaction: beforeSendTransaction,
|
|
120
147
|
initQueryTracing: (knex) => {
|
|
121
148
|
if (sentryConfig.tracing?.enabled === true) {
|
|
122
149
|
const integration = new SentryKnexTracingIntegration(knex);
|