ghost 5.37.0 → 5.38.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.37.0.tgz → tryghost-adapter-cache-memory-ttl-5.38.0.tgz} +0 -0
- package/components/tryghost-adapter-cache-redis-5.38.0.tgz +0 -0
- package/components/{tryghost-adapter-manager-5.37.0.tgz → tryghost-adapter-manager-5.38.0.tgz} +0 -0
- package/components/{tryghost-api-framework-5.37.0.tgz → tryghost-api-framework-5.38.0.tgz} +0 -0
- package/components/{tryghost-api-version-compatibility-service-5.37.0.tgz → tryghost-api-version-compatibility-service-5.38.0.tgz} +0 -0
- package/components/tryghost-audience-feedback-5.38.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.38.0.tgz +0 -0
- package/components/{tryghost-constants-5.37.0.tgz → tryghost-constants-5.38.0.tgz} +0 -0
- package/components/tryghost-custom-theme-settings-service-5.38.0.tgz +0 -0
- package/components/{tryghost-data-generator-5.37.0.tgz → tryghost-data-generator-5.38.0.tgz} +0 -0
- package/components/tryghost-domain-events-5.38.0.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.38.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.38.0.tgz +0 -0
- package/components/{tryghost-email-analytics-service-5.37.0.tgz → tryghost-email-analytics-service-5.38.0.tgz} +0 -0
- package/components/tryghost-email-content-generator-5.38.0.tgz +0 -0
- package/components/tryghost-email-events-5.38.0.tgz +0 -0
- package/components/tryghost-email-service-5.38.0.tgz +0 -0
- package/components/{tryghost-email-suppression-list-5.37.0.tgz → tryghost-email-suppression-list-5.38.0.tgz} +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.38.0.tgz +0 -0
- package/components/{tryghost-express-dynamic-redirects-5.37.0.tgz → tryghost-express-dynamic-redirects-5.38.0.tgz} +0 -0
- package/components/tryghost-external-media-inliner-5.38.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.38.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.38.0.tgz +0 -0
- package/components/tryghost-i18n-5.38.0.tgz +0 -0
- package/components/{tryghost-importer-handler-content-files-5.37.0.tgz → tryghost-importer-handler-content-files-5.38.0.tgz} +0 -0
- package/components/tryghost-importer-revue-5.38.0.tgz +0 -0
- package/components/tryghost-job-manager-5.38.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.38.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.38.0.tgz +0 -0
- package/components/{tryghost-link-tracking-5.37.0.tgz → tryghost-link-tracking-5.38.0.tgz} +0 -0
- package/components/{tryghost-magic-link-5.37.0.tgz → tryghost-magic-link-5.38.0.tgz} +0 -0
- package/components/tryghost-mailgun-client-5.38.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.38.0.tgz +0 -0
- package/components/tryghost-member-events-5.38.0.tgz +0 -0
- package/components/tryghost-members-api-5.38.0.tgz +0 -0
- package/components/tryghost-members-csv-5.38.0.tgz +0 -0
- package/components/{tryghost-members-events-service-5.37.0.tgz → tryghost-members-events-service-5.38.0.tgz} +0 -0
- package/components/{tryghost-members-importer-5.37.0.tgz → tryghost-members-importer-5.38.0.tgz} +0 -0
- package/components/tryghost-members-offers-5.38.0.tgz +0 -0
- package/components/tryghost-members-payments-5.38.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.38.0.tgz +0 -0
- package/components/{tryghost-members-stripe-service-5.37.0.tgz → tryghost-members-stripe-service-5.38.0.tgz} +0 -0
- package/components/tryghost-milestones-5.38.0.tgz +0 -0
- package/components/tryghost-minifier-5.38.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.38.0.tgz +0 -0
- package/components/{tryghost-mw-cache-control-5.37.0.tgz → tryghost-mw-cache-control-5.38.0.tgz} +0 -0
- package/components/tryghost-mw-error-handler-5.38.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.38.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.38.0.tgz +0 -0
- package/components/tryghost-mw-version-match-5.38.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.38.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.38.0.tgz +0 -0
- package/components/tryghost-package-json-5.38.0.tgz +0 -0
- package/components/{tryghost-referrers-5.37.0.tgz → tryghost-referrers-5.38.0.tgz} +0 -0
- package/components/tryghost-security-5.38.0.tgz +0 -0
- package/components/tryghost-session-service-5.38.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.38.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.38.0.tgz +0 -0
- package/components/tryghost-staff-service-5.38.0.tgz +0 -0
- package/components/tryghost-stats-service-5.38.0.tgz +0 -0
- package/components/tryghost-tiers-5.38.0.tgz +0 -0
- package/components/{tryghost-update-check-service-5.37.0.tgz → tryghost-update-check-service-5.38.0.tgz} +0 -0
- package/components/tryghost-verification-trigger-5.38.0.tgz +0 -0
- package/components/{tryghost-version-notifications-data-service-5.37.0.tgz → tryghost-version-notifications-data-service-5.38.0.tgz} +0 -0
- package/components/tryghost-webmentions-5.38.0.tgz +0 -0
- package/core/boot.js +11 -4
- package/core/built/admin/assets/{chunk.143.27cd10a38f877e715b35.js → chunk.143.c6802c882a911797ce4f.js} +6 -6
- package/core/built/admin/assets/{chunk.178.dd6cf17fb0986acf19d6.js → chunk.178.09faefd4027fcba4113d.js} +4 -4
- package/core/built/admin/assets/{chunk.652.bb618bc5abf23bed4e87.js → chunk.220.9ca2950240aba3fced21.js} +1836 -1774
- package/core/built/admin/assets/{chunk.79.4a959c324df25480b90e.js → chunk.79.acb7dd01e1c785f4920c.js} +12 -11
- package/core/built/admin/assets/{ghost-2948791640be026b987b88f89034bc85.js → ghost-35103ff053c43f1dfa7f35821c3c2412.js} +29 -29
- package/core/built/admin/assets/{ghost-efbe4dcc249d119a955b038aae5c980d.css → ghost-a9307c9cfe26a4bc621e02cd3bae421a.css} +1 -1
- package/core/built/admin/assets/{ghost-dark-6ea4b338f17a43c204b7c1e207b90cd7.css → ghost-dark-f309cf445255344e4861a95ecb8f1920.css} +1 -1
- package/core/built/admin/assets/vendor-b982e3bf1020bff77b2a3c44d5f59e55.js +269 -269
- package/core/built/admin/index.html +5 -5
- package/core/frontend/helpers/ghost_head.js +4 -1
- package/core/frontend/services/routing/StaticPagesRouter.js +1 -1
- package/core/frontend/services/sitemap/base-generator.js +5 -1
- package/core/server/adapters/storage/LocalImagesStorage.js +1 -1
- package/core/server/api/endpoints/email-previews.js +2 -43
- package/core/server/api/endpoints/emails.js +1 -22
- package/core/server/api/endpoints/utils/serializers/output/mappers/emails.js +14 -8
- package/core/server/data/importer/import-manager.js +8 -1
- package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -1
- package/core/server/lib/common/events.js +16 -23
- package/core/server/models/base/plugins/relations.js +5 -3
- package/core/server/models/index.js +5 -0
- package/core/server/services/comments/emails.js +2 -2
- package/core/server/services/email-service/wrapper.js +2 -0
- package/core/server/services/link-tracking/LinkClickRepository.js +1 -1
- package/core/server/services/media-inliner/service.js +49 -3
- package/core/server/services/mentions/service.js +6 -1
- package/core/server/services/posts/posts-service.js +3 -14
- package/core/server/services/staff/index.js +2 -0
- package/core/server/services/url/Urls.js +10 -2
- package/core/shared/labs.js +0 -1
- package/package.json +138 -138
- package/yarn.lock +267 -259
- package/components/tryghost-adapter-cache-redis-5.37.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.37.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.37.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.37.0.tgz +0 -0
- package/components/tryghost-domain-events-5.37.0.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.37.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.37.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.37.0.tgz +0 -0
- package/components/tryghost-email-events-5.37.0.tgz +0 -0
- package/components/tryghost-email-service-5.37.0.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.37.0.tgz +0 -0
- package/components/tryghost-external-media-inliner-5.37.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.37.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.37.0.tgz +0 -0
- package/components/tryghost-i18n-5.37.0.tgz +0 -0
- package/components/tryghost-importer-revue-5.37.0.tgz +0 -0
- package/components/tryghost-job-manager-5.37.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.37.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.37.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.37.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.37.0.tgz +0 -0
- package/components/tryghost-member-events-5.37.0.tgz +0 -0
- package/components/tryghost-members-api-5.37.0.tgz +0 -0
- package/components/tryghost-members-csv-5.37.0.tgz +0 -0
- package/components/tryghost-members-offers-5.37.0.tgz +0 -0
- package/components/tryghost-members-payments-5.37.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.37.0.tgz +0 -0
- package/components/tryghost-milestones-5.37.0.tgz +0 -0
- package/components/tryghost-minifier-5.37.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.37.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.37.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.37.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.37.0.tgz +0 -0
- package/components/tryghost-mw-version-match-5.37.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.37.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.37.0.tgz +0 -0
- package/components/tryghost-package-json-5.37.0.tgz +0 -0
- package/components/tryghost-security-5.37.0.tgz +0 -0
- package/components/tryghost-session-service-5.37.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.37.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.37.0.tgz +0 -0
- package/components/tryghost-staff-service-5.37.0.tgz +0 -0
- package/components/tryghost-stats-service-5.37.0.tgz +0 -0
- package/components/tryghost-tiers-5.37.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.37.0.tgz +0 -0
- package/components/tryghost-webmentions-5.37.0.tgz +0 -0
- package/core/server/services/bulk-email/bulk-email-processor.js +0 -289
- package/core/server/services/bulk-email/index.js +0 -1
- package/core/server/services/mega/email-preview.js +0 -54
- package/core/server/services/mega/feedback-buttons.js +0 -66
- package/core/server/services/mega/index.js +0 -14
- package/core/server/services/mega/mega.js +0 -626
- package/core/server/services/mega/post-email-serializer.js +0 -559
- package/core/server/services/mega/segment-parser.js +0 -20
- package/core/server/services/mega/template.js +0 -1319
- /package/core/built/admin/assets/{chunk.652.bb618bc5abf23bed4e87.js.LICENSE.txt → chunk.220.9ca2950240aba3fced21.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%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%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.38%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%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
<link integrity="" rel="stylesheet" href="assets/vendor-3e6947aa681f0fb82b193090e520dc73.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-a9307c9cfe26a4bc621e02cd3bae421a.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
58
58
|
|
|
59
59
|
<script src="assets/vendor-b982e3bf1020bff77b2a3c44d5f59e55.js"></script>
|
|
60
|
-
<script src="assets/chunk.
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
60
|
+
<script src="assets/chunk.220.9ca2950240aba3fced21.js"></script>
|
|
61
|
+
<script src="assets/chunk.143.c6802c882a911797ce4f.js"></script>
|
|
62
|
+
<script src="assets/ghost-35103ff053c43f1dfa7f35821c3c2412.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -64,7 +64,10 @@ function getMembersHelper(data, frontendKey) {
|
|
|
64
64
|
let membersHelper = `<script defer src="${scriptUrl}" ${dataAttributes} crossorigin="anonymous"></script>`;
|
|
65
65
|
membersHelper += (`<style id="gh-members-styles">${templateStyles}</style>`);
|
|
66
66
|
if (settingsCache.get('paid_members_enabled')) {
|
|
67
|
-
|
|
67
|
+
// disable fraud detection for e2e tests to reduce waiting time
|
|
68
|
+
const isFraudSignalsEnabled = process.env.NODE_ENV === 'testing-browser' ? '?advancedFraudSignals=false' : '';
|
|
69
|
+
|
|
70
|
+
membersHelper += `<script async src="https://js.stripe.com/v3/${isFraudSignalsEnabled}"></script>`;
|
|
68
71
|
}
|
|
69
72
|
return membersHelper;
|
|
70
73
|
}
|
|
@@ -52,7 +52,7 @@ class StaticPagesRouter extends ParentRouter {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
* @description Prepare context for
|
|
55
|
+
* @description Prepare context for further middleware/controllers.
|
|
56
56
|
* @param {Object} req
|
|
57
57
|
* @param {Object} res
|
|
58
58
|
* @param {Function} next
|
|
@@ -22,6 +22,10 @@ class BaseSiteMapGenerator {
|
|
|
22
22
|
this.maxPerPage = 50000;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
hasCanonicalUrl(datum) {
|
|
26
|
+
return Boolean(datum?.canonical_url);
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
generateXmlFromNodes(page) {
|
|
26
30
|
// Get a mapping of node to timestamp
|
|
27
31
|
let nodesToProcess = _.map(this.nodeLookup, (node, id) => {
|
|
@@ -75,7 +79,7 @@ class BaseSiteMapGenerator {
|
|
|
75
79
|
addUrl(url, datum) {
|
|
76
80
|
const node = this.createUrlNodeFromDatum(url, datum);
|
|
77
81
|
|
|
78
|
-
if (node) {
|
|
82
|
+
if (node && !this.hasCanonicalUrl(datum)) {
|
|
79
83
|
this.updateLastModified(datum);
|
|
80
84
|
this.updateLookups(datum, node);
|
|
81
85
|
// force regeneration of xml
|
|
@@ -27,7 +27,7 @@ class LocalImagesStorage extends LocalStorageBase {
|
|
|
27
27
|
/**
|
|
28
28
|
* Saves a buffer in the targetPath
|
|
29
29
|
* @param {Buffer} buffer is an instance of Buffer
|
|
30
|
-
* @param {String} targetPath path to which the buffer should be written
|
|
30
|
+
* @param {String} targetPath relative path NOT including storage path to which the buffer should be written
|
|
31
31
|
* @returns {Promise<String>} a URL to retrieve the data
|
|
32
32
|
*/
|
|
33
33
|
async saveRaw(buffer, targetPath) {
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
const models = require('../../models');
|
|
2
|
-
const tpl = require('@tryghost/tpl');
|
|
3
|
-
const errors = require('@tryghost/errors');
|
|
4
|
-
const mega = require('../../services/mega');
|
|
5
1
|
const emailService = require('../../services/email-service');
|
|
6
|
-
const labs = require('../../../shared/labs');
|
|
7
|
-
const messages = {
|
|
8
|
-
postNotFound: 'Post not found.'
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const emailPreview = new mega.EmailPreview();
|
|
12
2
|
|
|
13
3
|
module.exports = {
|
|
14
4
|
docName: 'email_previews',
|
|
@@ -30,25 +20,7 @@ module.exports = {
|
|
|
30
20
|
],
|
|
31
21
|
permissions: true,
|
|
32
22
|
async query(frame) {
|
|
33
|
-
|
|
34
|
-
return await emailService.controller.previewEmail(frame);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const options = Object.assign(frame.options, {formats: 'html,plaintext', withRelated: ['authors', 'posts_meta']});
|
|
38
|
-
const data = Object.assign(frame.data, {status: 'all'});
|
|
39
|
-
|
|
40
|
-
const model = await models.Post.findOne(data, options);
|
|
41
|
-
|
|
42
|
-
if (!model) {
|
|
43
|
-
throw new errors.NotFoundError({
|
|
44
|
-
message: tpl(messages.postNotFound)
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return emailPreview.generateEmailContent(model, {
|
|
49
|
-
newsletter: frame.options.newsletter,
|
|
50
|
-
memberSegment: frame.options.memberSegment
|
|
51
|
-
});
|
|
23
|
+
return await emailService.controller.previewEmail(frame);
|
|
52
24
|
}
|
|
53
25
|
},
|
|
54
26
|
sendTestEmail: {
|
|
@@ -66,20 +38,7 @@ module.exports = {
|
|
|
66
38
|
},
|
|
67
39
|
permissions: true,
|
|
68
40
|
async query(frame) {
|
|
69
|
-
|
|
70
|
-
return await emailService.controller.sendTestEmail(frame);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const options = Object.assign(frame.options, {status: 'all'});
|
|
74
|
-
let model = await models.Post.findOne(options, {withRelated: ['authors']});
|
|
75
|
-
|
|
76
|
-
if (!model) {
|
|
77
|
-
throw new errors.NotFoundError({
|
|
78
|
-
message: tpl(messages.postNotFound)
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
const {emails = [], memberSegment, newsletter = ''} = frame.data;
|
|
82
|
-
return await mega.mega.sendTestEmail(model, emails, memberSegment, newsletter);
|
|
41
|
+
return await emailService.controller.sendTestEmail(frame);
|
|
83
42
|
}
|
|
84
43
|
}
|
|
85
44
|
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
const models = require('../../models');
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
|
-
const megaService = require('../../services/mega');
|
|
5
4
|
const emailService = require('../../services/email-service');
|
|
6
|
-
const labs = require('../../../shared/labs');
|
|
7
5
|
const emailAnalytics = require('../../services/email-analytics');
|
|
8
6
|
|
|
9
7
|
const messages = {
|
|
@@ -63,27 +61,8 @@ module.exports = {
|
|
|
63
61
|
'id'
|
|
64
62
|
],
|
|
65
63
|
permissions: true,
|
|
66
|
-
// (complexity removed with new labs flag)
|
|
67
|
-
// eslint-disable-next-line ghost/ghost-custom/max-api-complexity
|
|
68
64
|
async query(frame) {
|
|
69
|
-
|
|
70
|
-
return await emailService.controller.retryFailedEmail(frame);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const model = await models.Email.findOne(frame.data, frame.options);
|
|
74
|
-
if (!model) {
|
|
75
|
-
throw new errors.NotFoundError({
|
|
76
|
-
message: tpl(messages.emailNotFound)
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (model.get('status') !== 'failed') {
|
|
81
|
-
throw new errors.IncorrectUsageError({
|
|
82
|
-
message: tpl(messages.retryNotAllowed)
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return await megaService.mega.retryFailedEmail(model);
|
|
65
|
+
return await emailService.controller.retryFailedEmail(frame);
|
|
87
66
|
}
|
|
88
67
|
},
|
|
89
68
|
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
const mega = require('../../../../../../services/mega');
|
|
2
1
|
const labs = require('../../../../../../../shared/labs');
|
|
3
2
|
const config = require('../../../../../../../shared/config');
|
|
3
|
+
const emailService = require('../../../../../../services/email-service');
|
|
4
4
|
|
|
5
5
|
module.exports = (model, frame) => {
|
|
6
6
|
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
|
|
7
7
|
|
|
8
8
|
// Ensure we're not outputting unwanted replacement strings when viewing email contents
|
|
9
9
|
// TODO: extract this to a utility, it's duplicated in the email-preview API controller
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
if (jsonModel.html) {
|
|
11
|
+
const replacements = emailService.renderer.buildReplacementDefinitions({html: jsonModel.html, newsletterUuid: 'preview'});
|
|
12
|
+
const exampleMember = emailService.service.getDefaultExampleMember();
|
|
13
|
+
|
|
14
|
+
jsonModel.html = emailService.service.replaceDefinitions(jsonModel.html, replacements, exampleMember);
|
|
15
|
+
|
|
16
|
+
if (jsonModel.plaintext) {
|
|
17
|
+
jsonModel.plaintext = emailService.service.replaceDefinitions(jsonModel.plaintext, replacements, exampleMember);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
17
20
|
|
|
18
21
|
if (!labs.isSet('emailErrors') && !!(config.get('bulkEmail') && config.get('bulkEmail').mailgun)) {
|
|
19
22
|
if (jsonModel.status === 'failed') {
|
|
@@ -21,5 +24,8 @@ module.exports = (model, frame) => {
|
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
// Removed loaded post relation if set
|
|
28
|
+
delete jsonModel.post;
|
|
29
|
+
|
|
24
30
|
return jsonModel;
|
|
25
31
|
};
|
|
@@ -301,7 +301,14 @@ class ImportManager {
|
|
|
301
301
|
const baseDir = this.getBaseDirectory(zipDirectory);
|
|
302
302
|
|
|
303
303
|
for (const handler of this.handlers) {
|
|
304
|
-
|
|
304
|
+
let files = [];
|
|
305
|
+
if (handler.directories?.length > 0) {
|
|
306
|
+
for (const dir of handler.directories) {
|
|
307
|
+
files.push(...this.getFilesFromZip(handler, path.join(zipDirectory, (baseDir || ''), dir)));
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
files.push(...this.getFilesFromZip(handler, zipDirectory));
|
|
311
|
+
}
|
|
305
312
|
|
|
306
313
|
debug('handler', handler.type, files);
|
|
307
314
|
|
|
@@ -4,7 +4,7 @@ const htmlToPlaintext = require('@tryghost/html-to-plaintext');
|
|
|
4
4
|
const mobiledocLib = require('../../../../lib/mobiledoc');
|
|
5
5
|
const {createTransactionalMigration} = require('../../utils');
|
|
6
6
|
|
|
7
|
-
// in Ghost versions 4.6.1-4.8.4 the 4.0 migration that
|
|
7
|
+
// in Ghost versions 4.6.1-4.8.4 the 4.0 migration that transformed URLs had a bug
|
|
8
8
|
// that meant urls inside cards in mobiledoc content was not being transformed
|
|
9
9
|
//
|
|
10
10
|
// if the migrations table indicates an upgrade was made from 3.x to 4.6-4.8 then
|
|
@@ -12,29 +12,22 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const events = require('events');
|
|
15
|
-
const util = require('util');
|
|
16
|
-
let EventRegistry;
|
|
17
|
-
let EventRegistryInstance;
|
|
18
15
|
|
|
19
|
-
EventRegistry
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
class EventRegistry extends events.EventEmitter {
|
|
17
|
+
/**
|
|
18
|
+
* This is method is semi-hack to make sure listeners are only registered once
|
|
19
|
+
* during the lifetime of the process. And example problem it solves is
|
|
20
|
+
* registering duplicate listeners between Ghost instance reboots when running tests.
|
|
21
|
+
* @param {String} eventName
|
|
22
|
+
* @param {String} listenerName named function name registered as a listener for the event
|
|
23
|
+
* @returns {Boolean}
|
|
24
|
+
*/
|
|
25
|
+
hasRegisteredListener(eventName, listenerName) {
|
|
26
|
+
return !!(this.listeners(eventName).find(listener => (listener.name === listenerName)));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
const eventRegistryInstance = new EventRegistry();
|
|
31
|
+
eventRegistryInstance.setMaxListeners(100);
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
* This is method is semi-hack to make sure listeners are only registered once
|
|
27
|
-
* during the lifetime of the process. And example problem it solves is
|
|
28
|
-
* registering duplicate listeners between Ghost instance reboots when running tests.
|
|
29
|
-
* @param {String} eventName
|
|
30
|
-
* @param {String} listenerName named function name registered as a listener for the event
|
|
31
|
-
* @returns {Boolean}
|
|
32
|
-
*/
|
|
33
|
-
EventRegistry.prototype.hasRegisteredListener = function (eventName, listenerName) {
|
|
34
|
-
return !!(this.listeners(eventName).find(listener => (listener.name === listenerName)));
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
EventRegistryInstance = new EventRegistry();
|
|
38
|
-
EventRegistryInstance.setMaxListeners(100);
|
|
39
|
-
|
|
40
|
-
module.exports = EventRegistryInstance;
|
|
33
|
+
module.exports = eventRegistryInstance;
|
|
@@ -7,7 +7,7 @@ module.exports = function (Bookshelf) {
|
|
|
7
7
|
* Return a relation, and load it if it hasn't been loaded already (or force a refresh with the forceRefresh option).
|
|
8
8
|
* refs https://github.com/TryGhost/Team/issues/1626
|
|
9
9
|
* @param {string} name Name of the relation to load
|
|
10
|
-
* @param {Object} [options] Options to pass to the fetch when not yet loaded (or when force refreshing)
|
|
10
|
+
* @param {Object} [options] Options to pass to the fetch when not yet loaded (or when force refreshing)
|
|
11
11
|
* @param {boolean} [options.forceRefresh] If true, the relation will be fetched again even if it has already been loaded.
|
|
12
12
|
* @returns {Promise<import('bookshelf').Model|import('bookshelf').Collection|null>}
|
|
13
13
|
*/
|
|
@@ -23,8 +23,10 @@ module.exports = function (Bookshelf) {
|
|
|
23
23
|
// Not yet loaded, or force refresh
|
|
24
24
|
// Note that we don't use .refresh on the relation on options.forceRefresh
|
|
25
25
|
// Because the relation can also be a collection, which doesn't have a refresh method
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const instance = this[name]();
|
|
27
|
+
await instance.fetch(options);
|
|
28
|
+
this.relations[name] = instance;
|
|
29
|
+
return instance;
|
|
28
30
|
}
|
|
29
31
|
});
|
|
30
32
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const _ = require('lodash');
|
|
6
|
+
const debug = require('@tryghost/debug')('models');
|
|
6
7
|
const glob = require('glob');
|
|
7
8
|
|
|
8
9
|
// enable event listeners
|
|
@@ -14,12 +15,16 @@ require('./base/listeners');
|
|
|
14
15
|
exports = module.exports;
|
|
15
16
|
|
|
16
17
|
function init() {
|
|
18
|
+
const baseNow = Date.now();
|
|
17
19
|
exports.Base = require('./base');
|
|
20
|
+
debug(`${Date.now() - baseNow}ms - Base.js require`);
|
|
18
21
|
|
|
19
22
|
let modelsFiles = glob.sync('!(index).js', {cwd: __dirname});
|
|
20
23
|
modelsFiles.forEach((model) => {
|
|
21
24
|
const name = model.replace(/.js$/, '');
|
|
25
|
+
const modelNow = Date.now();
|
|
22
26
|
_.extend(exports, require('./' + name));
|
|
27
|
+
debug(`${Date.now() - modelNow}ms - ${model} require`);
|
|
23
28
|
});
|
|
24
29
|
}
|
|
25
30
|
|
|
@@ -2,7 +2,7 @@ const {promises: fs} = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const moment = require('moment');
|
|
4
4
|
const htmlToPlaintext = require('@tryghost/html-to-plaintext');
|
|
5
|
-
const
|
|
5
|
+
const emailService = require('../email-service');
|
|
6
6
|
|
|
7
7
|
class CommentsServiceEmails {
|
|
8
8
|
constructor({config, logging, models, mailer, settingsCache, settingsHelpers, urlService, urlUtils}) {
|
|
@@ -95,7 +95,7 @@ class CommentsServiceEmails {
|
|
|
95
95
|
accentColor: this.settingsCache.get('accent_color'),
|
|
96
96
|
fromEmail: this.notificationFromAddress,
|
|
97
97
|
toEmail: to,
|
|
98
|
-
profileUrl:
|
|
98
|
+
profileUrl: emailService.renderer.createUnsubscribeUrl(member.get('uuid'), {comments: true})
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
const {html, text} = await this.renderEmailTemplate('new-comment-reply', templateData);
|
|
@@ -56,7 +56,7 @@ module.exports = class LinkClickRepository {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const model = await this.#MemberLinkClickEventModel.add({
|
|
59
|
-
// Only store the
|
|
59
|
+
// Only store the pathname (no support for variable query strings)
|
|
60
60
|
redirect_id: linkClick.link_id.toHexString(),
|
|
61
61
|
member_id: member.id
|
|
62
62
|
}, {});
|
|
@@ -1,12 +1,58 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
async init() {
|
|
3
3
|
const debug = require('@tryghost/debug')('mediaInliner');
|
|
4
|
+
const MediaInliner = require('@tryghost/external-media-inliner');
|
|
5
|
+
const models = require('../../models');
|
|
6
|
+
const jobsService = require('../jobs');
|
|
7
|
+
|
|
8
|
+
const mediaStorage = require('../../adapters/storage').getStorage('media');
|
|
9
|
+
const imageStorage = require('../../adapters/storage').getStorage('images');
|
|
10
|
+
const fileStorage = require('../../adapters/storage').getStorage('files');
|
|
11
|
+
|
|
12
|
+
const config = require('../../../shared/config');
|
|
13
|
+
|
|
14
|
+
const mediaInliner = new MediaInliner({
|
|
15
|
+
PostModel: models.Post,
|
|
16
|
+
TagModel: models.Tag,
|
|
17
|
+
UserModel: models.User,
|
|
18
|
+
PostMetaModel: models.PostsMeta,
|
|
19
|
+
getMediaStorage: (extension) => {
|
|
20
|
+
if (config.get('uploads').images.extensions.includes(extension)) {
|
|
21
|
+
return imageStorage;
|
|
22
|
+
} else if (config.get('uploads').media.extensions.includes(extension)) {
|
|
23
|
+
return mediaStorage;
|
|
24
|
+
} else if (config.get('uploads').files.extensions.includes(extension)) {
|
|
25
|
+
return fileStorage;
|
|
26
|
+
} else {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
4
31
|
|
|
5
32
|
this.api = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
33
|
+
|
|
34
|
+
startMediaInliner: async (domains) => {
|
|
35
|
+
if (!domains || !domains.length) {
|
|
36
|
+
// default domains to inline from if none are provided
|
|
37
|
+
domains = [
|
|
38
|
+
'https://s3.amazonaws.com/revue',
|
|
39
|
+
'https://substackcdn.com'
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
9
43
|
debug('[Inliner] Starting media inlining job for domains: ', domains);
|
|
44
|
+
|
|
45
|
+
// @NOTE: the job is "inline" (aka non-offloaded into a thread), because usecases are currently
|
|
46
|
+
// limited to migrational, so there is no expectations for site's availability etc.
|
|
47
|
+
await jobsService.addJob({
|
|
48
|
+
name: 'external-media-inliner',
|
|
49
|
+
job: (data) => {
|
|
50
|
+
return mediaInliner.inline(data.domains);
|
|
51
|
+
},
|
|
52
|
+
data: {domains},
|
|
53
|
+
offloaded: false
|
|
54
|
+
});
|
|
55
|
+
|
|
10
56
|
return {
|
|
11
57
|
status: 'success'
|
|
12
58
|
};
|
|
@@ -26,7 +26,12 @@ function getPostUrl(post) {
|
|
|
26
26
|
|
|
27
27
|
module.exports = {
|
|
28
28
|
controller: new MentionController(),
|
|
29
|
+
didInit: false,
|
|
29
30
|
async init() {
|
|
31
|
+
if (this.didInit) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this.didInit = true;
|
|
30
35
|
const repository = new BookshelfMentionRepository({
|
|
31
36
|
MentionModel: models.Mention,
|
|
32
37
|
DomainEvents
|
|
@@ -97,6 +102,6 @@ module.exports = {
|
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
});
|
|
100
|
-
sendingService.listen(events);
|
|
105
|
+
sendingService.listen(events);
|
|
101
106
|
}
|
|
102
107
|
};
|
|
@@ -8,8 +8,7 @@ const messages = {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
class PostsService {
|
|
11
|
-
constructor({
|
|
12
|
-
this.mega = mega;
|
|
11
|
+
constructor({urlUtils, models, isSet, stats, emailService}) {
|
|
13
12
|
this.urlUtils = urlUtils;
|
|
14
13
|
this.models = models;
|
|
15
14
|
this.isSet = isSet;
|
|
@@ -45,17 +44,9 @@ class PostsService {
|
|
|
45
44
|
let email;
|
|
46
45
|
|
|
47
46
|
if (!postEmail) {
|
|
48
|
-
|
|
49
|
-
email = await this.emailService.createEmail(model);
|
|
50
|
-
} else {
|
|
51
|
-
email = await this.mega.addEmail(model, frame.options);
|
|
52
|
-
}
|
|
47
|
+
email = await this.emailService.createEmail(model);
|
|
53
48
|
} else if (postEmail && postEmail.get('status') === 'failed') {
|
|
54
|
-
|
|
55
|
-
email = await this.emailService.retryEmail(postEmail);
|
|
56
|
-
} else {
|
|
57
|
-
email = await this.mega.retryFailedEmail(postEmail);
|
|
58
|
-
}
|
|
49
|
+
email = await this.emailService.retryEmail(postEmail);
|
|
59
50
|
}
|
|
60
51
|
if (email) {
|
|
61
52
|
model.set('email', email);
|
|
@@ -130,7 +121,6 @@ class PostsService {
|
|
|
130
121
|
*/
|
|
131
122
|
const getPostServiceInstance = () => {
|
|
132
123
|
const urlUtils = require('../../../shared/url-utils');
|
|
133
|
-
const {mega} = require('../mega');
|
|
134
124
|
const labs = require('../../../shared/labs');
|
|
135
125
|
const models = require('../../models');
|
|
136
126
|
const PostStats = require('./stats/post-stats');
|
|
@@ -139,7 +129,6 @@ const getPostServiceInstance = () => {
|
|
|
139
129
|
const postStats = new PostStats();
|
|
140
130
|
|
|
141
131
|
return new PostsService({
|
|
142
|
-
mega: mega,
|
|
143
132
|
urlUtils: urlUtils,
|
|
144
133
|
models: models,
|
|
145
134
|
isSet: flag => labs.isSet(flag), // don't use bind, that breaks test subbing of labs
|
|
@@ -12,6 +12,7 @@ class StaffServiceWrapper {
|
|
|
12
12
|
|
|
13
13
|
const logging = require('@tryghost/logging');
|
|
14
14
|
const models = require('../../models');
|
|
15
|
+
const memberAttribution = require('../member-attribution');
|
|
15
16
|
const {GhostMailer} = require('../mail');
|
|
16
17
|
const mailer = new GhostMailer();
|
|
17
18
|
const settingsCache = require('../../../shared/settings-cache');
|
|
@@ -26,6 +27,7 @@ class StaffServiceWrapper {
|
|
|
26
27
|
settingsCache,
|
|
27
28
|
urlUtils,
|
|
28
29
|
DomainEvents,
|
|
30
|
+
memberAttributionService: memberAttribution.service,
|
|
29
31
|
labs
|
|
30
32
|
});
|
|
31
33
|
|
|
@@ -44,10 +44,18 @@ class Urls {
|
|
|
44
44
|
debug('cache', url);
|
|
45
45
|
|
|
46
46
|
if (this.urls[resource.data.id]) {
|
|
47
|
-
|
|
47
|
+
const error = new errors.InternalServerError({
|
|
48
48
|
message: 'This should not happen.',
|
|
49
49
|
code: 'URLSERVICE_RESOURCE_DUPLICATE'
|
|
50
|
-
})
|
|
50
|
+
});
|
|
51
|
+
if (process.env.NODE_ENV.startsWith('test')) {
|
|
52
|
+
logging.warn({
|
|
53
|
+
message: 'Duplicate URL',
|
|
54
|
+
err: error
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
logging.error(error);
|
|
58
|
+
}
|
|
51
59
|
|
|
52
60
|
this.removeResourceId(resource.data.id);
|
|
53
61
|
}
|