ghost 5.119.2 → 5.120.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-i18n-5.120.0.tgz +0 -0
- package/core/boot.js +0 -2
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +7555 -7216
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-60ce658c.mjs → CodeEditorView-1c5b0683.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-8480baa8.mjs → index-14e518a7.mjs} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-a2648c61.mjs → index-fc9f985b.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{modals-6900c1d5.mjs → modals-15bc6a0f.mjs} +7192 -6656
- package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js → chunk.383.25fca2f09b4896656125.js} +76 -59
- package/core/built/admin/assets/chunk.524.1657b12c0ab25dd9fb79.js +28 -0
- package/core/built/admin/assets/{chunk.582.98a820cbc4bb65f2e685.js → chunk.582.09869b1f1a3cc0ab81f6.js} +19 -26
- package/core/built/admin/assets/{ghost-843572e9507d099162ae744d791daba1.js → ghost-b3b44421acca3b3eec76bfbb6ba0e81b.js} +3 -3
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +12578 -12352
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +423 -211
- package/core/built/admin/assets/posts/posts.js +13680 -13671
- package/core/built/admin/assets/stats/stats.js +16457 -16635
- package/core/built/admin/assets/{vendor-8f805740fee4db959a5b2119001a56b1.js → vendor-4ce6d282a2a00fe486a0951e0591da19.js} +11 -9
- package/core/built/admin/index.html +5 -5
- package/core/frontend/helpers/match.js +6 -0
- package/core/frontend/services/routing/ParentRouter.js +1 -1
- package/core/frontend/services/routing/controllers/email-post.js +0 -2
- package/core/frontend/services/routing/controllers/previews.js +0 -3
- package/core/frontend/web/middleware/frontend-caching.js +2 -2
- package/core/server/api/endpoints/authentication.js +37 -73
- package/core/server/api/endpoints/authors-public.js +8 -9
- package/core/server/api/endpoints/db.js +34 -35
- package/core/server/api/endpoints/emails.js +8 -10
- package/core/server/api/endpoints/integrations.js +20 -18
- package/core/server/api/endpoints/invites.js +8 -10
- package/core/server/api/endpoints/labels.js +19 -23
- package/core/server/api/endpoints/notifications.js +3 -4
- package/core/server/api/endpoints/pages-public.js +8 -10
- package/core/server/api/endpoints/pages.js +14 -18
- package/core/server/api/endpoints/posts-public.js +8 -10
- package/core/server/api/endpoints/posts.js +6 -8
- package/core/server/api/endpoints/previews.js +8 -10
- package/core/server/api/endpoints/redirects.js +7 -8
- package/core/server/api/endpoints/schedules.js +5 -7
- package/core/server/api/endpoints/slugs.js +7 -9
- package/core/server/api/endpoints/snippets.js +16 -20
- package/core/server/api/endpoints/tags-public.js +8 -10
- package/core/server/api/endpoints/tags.js +19 -23
- package/core/server/api/endpoints/themes.js +6 -8
- package/core/server/api/endpoints/users.js +31 -36
- package/core/server/api/endpoints/utils/permissions.js +10 -10
- package/core/server/api/endpoints/utils/serializers/output/roles.js +9 -10
- package/core/server/api/endpoints/utils/validators/input/images.js +43 -52
- package/core/server/api/endpoints/utils/validators/input/invites.js +6 -8
- package/core/server/api/endpoints/webhooks.js +38 -42
- package/core/server/data/migrations/versions/5.120/2025-05-07-14-57-38-add-newsletters-button-corners-column.js +8 -0
- package/core/server/data/migrations/versions/5.120/2025-05-13-17-36-56-add-newsletters-button-style-column.js +8 -0
- package/core/server/data/migrations/versions/5.120/2025-05-14-20-00-15-add-newsletters-setting-columns.js +22 -0
- package/core/server/data/schema/schema.js +6 -1
- package/core/server/lib/image/Gravatar.js +12 -13
- package/core/server/lib/lexical.js +3 -1
- package/core/server/models/newsletter.js +6 -1
- package/core/server/services/api-version-compatibility/index.js +1 -33
- package/core/server/services/auth/session/emails/signin.js +3 -3
- package/core/server/services/email-address/EmailAddressParser.js +52 -0
- package/core/server/services/email-address/EmailAddressParser.js.d.ts +13 -0
- package/core/server/services/email-address/EmailAddressService.js +142 -0
- package/core/server/services/email-address/EmailAddressService.ts +183 -0
- package/core/server/services/email-address/EmailAddressServiceWrapper.js +2 -4
- package/core/server/services/email-analytics/EmailAnalyticsService.js +1 -1
- package/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js +2 -1
- package/core/server/services/email-service/BatchSendingService.js +703 -0
- package/core/server/services/email-service/EmailBodyCache.js +20 -0
- package/core/server/services/email-service/EmailController.js +94 -0
- package/core/server/services/email-service/EmailEventProcessor.js +267 -0
- package/core/server/services/email-service/EmailEventStorage.js +187 -0
- package/core/server/services/email-service/EmailRenderer.js +1263 -0
- package/core/server/services/email-service/EmailSegmenter.js +74 -0
- package/core/server/services/email-service/EmailService.js +310 -0
- package/core/server/services/email-service/EmailServiceWrapper.js +9 -2
- package/core/server/services/email-service/MailgunEmailProvider.js +191 -0
- package/core/server/services/email-service/SendingService.js +173 -0
- package/core/server/services/email-service/email-templates/partials/feedback-button.hbs +7 -0
- package/core/server/services/email-service/email-templates/partials/latest-posts.hbs +39 -0
- package/core/server/services/email-service/email-templates/partials/paywall.hbs +20 -0
- package/core/server/services/email-service/email-templates/partials/styles.hbs +2348 -0
- package/core/server/services/email-service/email-templates/template.hbs +238 -0
- package/core/server/services/email-service/events/EmailBouncedEvent.js +63 -0
- package/core/server/services/email-service/events/EmailDeliveredEvent.js +49 -0
- package/core/server/services/email-service/events/EmailOpenedEvent.js +49 -0
- package/core/server/services/email-service/events/EmailTemporaryBouncedEvent.js +63 -0
- package/core/server/services/email-service/events/EmailUnsubscribedEvent.js +42 -0
- package/core/server/services/email-service/events/SpamComplaintEvent.js +42 -0
- package/core/server/services/email-service/helpers/register-helpers.js +59 -0
- package/core/server/services/email-suppression-list/MailgunEmailSuppressionList.js +2 -1
- package/core/server/services/explore-ping/index.js +2 -1
- package/core/server/services/mail/GhostMailer.js +1 -1
- package/core/server/services/media-inliner/ExternalMediaInliner.js +2 -1
- package/core/server/services/members/api.js +15 -15
- package/core/server/services/members/emails/signin.js +4 -4
- package/core/server/services/members/emails/signup-paid.js +3 -4
- package/core/server/services/members/emails/signup.js +3 -3
- package/core/server/services/members/emails/subscribe.js +3 -3
- package/core/server/services/members/members-api/controllers/RouterController.js +50 -36
- package/core/server/services/members/members-api/repositories/MemberRepository.js +92 -92
- package/core/server/services/members-events/LastSeenAtUpdater.js +1 -1
- package/core/server/services/settings-helpers/SettingsHelpers.js +1 -1
- package/core/server/services/staff/StaffServiceEmails.js +1 -1
- package/core/server/services/stats/PostsStatsService.js +28 -7
- package/core/server/web/api/app.js +0 -1
- package/core/server/web/api/endpoints/admin/app.js +0 -2
- package/core/server/web/api/endpoints/content/app.js +0 -2
- package/core/server/web/api/middleware/upload.js +2 -2
- package/core/shared/custom-theme-settings-cache/CustomThemeSettingsService.js +2 -1
- package/package.json +39 -97
- package/tsconfig.tsbuildinfo +1 -1
- package/yarn.lock +385 -517
- package/components/tryghost-api-framework-5.119.2.tgz +0 -0
- package/components/tryghost-custom-fonts-5.119.2.tgz +0 -0
- package/components/tryghost-domain-events-5.119.2.tgz +0 -0
- package/components/tryghost-email-addresses-5.119.2.tgz +0 -0
- package/components/tryghost-email-service-5.119.2.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.119.2.tgz +0 -0
- package/components/tryghost-i18n-5.119.2.tgz +0 -0
- package/components/tryghost-job-manager-5.119.2.tgz +0 -0
- package/components/tryghost-members-csv-5.119.2.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.119.2.tgz +0 -0
- package/components/tryghost-mw-vhost-5.119.2.tgz +0 -0
- package/components/tryghost-prometheus-metrics-5.119.2.tgz +0 -0
- package/components/tryghost-security-5.119.2.tgz +0 -0
- package/core/built/admin/assets/chunk.524.b8545af3bb714bc4f820.js +0 -35
- package/core/server/services/api-version-compatibility/APIVersionCompatibilityService.js +0 -99
- package/core/server/services/api-version-compatibility/VersionNotificationsDataService.js +0 -80
- package/core/server/services/api-version-compatibility/extract-api-key.js +0 -57
- package/core/server/services/api-version-compatibility/mw-api-version-mismatch.js +0 -31
- /package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js.LICENSE.txt → chunk.383.25fca2f09b4896656125.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/* eslint-disable ghost/filenames/match-exported-class */
|
|
2
|
+
import logging from '@tryghost/logging';
|
|
3
|
+
import EmailAddressParser, {EmailAddress} from './EmailAddressParser.js';
|
|
4
|
+
|
|
5
|
+
export type EmailAddresses = {
|
|
6
|
+
from: EmailAddress,
|
|
7
|
+
replyTo?: EmailAddress
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type EmailAddressesValidation = {
|
|
11
|
+
allowed: boolean,
|
|
12
|
+
verificationEmailRequired: boolean,
|
|
13
|
+
reason?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type EmailAddressType = 'from' | 'replyTo';
|
|
17
|
+
|
|
18
|
+
type LabsService = {
|
|
19
|
+
isSet: (flag: string) => boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class EmailAddressService {
|
|
23
|
+
#getManagedEmailEnabled: () => boolean;
|
|
24
|
+
#getSendingDomain: () => string | null;
|
|
25
|
+
#getDefaultEmail: () => EmailAddress;
|
|
26
|
+
#isValidEmailAddress: (email: string) => boolean;
|
|
27
|
+
#labs: LabsService;
|
|
28
|
+
|
|
29
|
+
constructor(dependencies: {
|
|
30
|
+
getManagedEmailEnabled: () => boolean,
|
|
31
|
+
getSendingDomain: () => string | null,
|
|
32
|
+
getDefaultEmail: () => EmailAddress,
|
|
33
|
+
isValidEmailAddress: (email: string) => boolean,
|
|
34
|
+
labs: LabsService
|
|
35
|
+
|
|
36
|
+
}) {
|
|
37
|
+
this.#getManagedEmailEnabled = dependencies.getManagedEmailEnabled;
|
|
38
|
+
this.#getSendingDomain = dependencies.getSendingDomain;
|
|
39
|
+
this.#getDefaultEmail = dependencies.getDefaultEmail;
|
|
40
|
+
this.#isValidEmailAddress = dependencies.isValidEmailAddress;
|
|
41
|
+
this.#labs = dependencies.labs;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get sendingDomain(): string | null {
|
|
45
|
+
return this.#getSendingDomain();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get managedEmailEnabled(): boolean {
|
|
49
|
+
return this.#getManagedEmailEnabled();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get defaultFromEmail(): EmailAddress {
|
|
53
|
+
return this.#getDefaultEmail();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getAddressFromString(from: string, replyTo?: string): EmailAddresses {
|
|
57
|
+
const parsedFrom = EmailAddressParser.parse(from);
|
|
58
|
+
const parsedReplyTo = replyTo ? EmailAddressParser.parse(replyTo) : undefined;
|
|
59
|
+
|
|
60
|
+
return this.getAddress({
|
|
61
|
+
from: parsedFrom ?? this.defaultFromEmail,
|
|
62
|
+
replyTo: parsedReplyTo ?? undefined
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* When sending an email, we should always ensure DMARC alignment.
|
|
68
|
+
* Because of that, we restrict which email addresses we send from. All emails should be either
|
|
69
|
+
* send from a configured domain (hostSettings.managedEmail.sendingDomains), or from the configured email address (mail.from).
|
|
70
|
+
*
|
|
71
|
+
* If we send an email from an email address that doesn't pass, we'll just default to the default email address,
|
|
72
|
+
* and instead add a replyTo email address from the requested from address.
|
|
73
|
+
*/
|
|
74
|
+
getAddress(preferred: EmailAddresses): EmailAddresses {
|
|
75
|
+
if (preferred.replyTo && !this.#isValidEmailAddress(preferred.replyTo.address)) {
|
|
76
|
+
// Remove invalid replyTo addresses
|
|
77
|
+
logging.error(`[EmailAddresses] Invalid replyTo address: ${preferred.replyTo.address}`);
|
|
78
|
+
preferred.replyTo = undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Validate the from address
|
|
82
|
+
if (!this.#isValidEmailAddress(preferred.from.address)) {
|
|
83
|
+
// Never allow an invalid email address
|
|
84
|
+
return {
|
|
85
|
+
from: this.defaultFromEmail,
|
|
86
|
+
replyTo: preferred.replyTo || undefined
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!this.managedEmailEnabled) {
|
|
91
|
+
// Self hoster or legacy Ghost Pro
|
|
92
|
+
return preferred;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Case: always allow the default from address
|
|
96
|
+
if (preferred.from.address === this.defaultFromEmail.address) {
|
|
97
|
+
if (!preferred.from.name) {
|
|
98
|
+
// Use the default sender name if it is missing
|
|
99
|
+
preferred.from.name = this.defaultFromEmail.name;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return preferred;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (this.sendingDomain) {
|
|
106
|
+
// Check if FROM address is from the sending domain
|
|
107
|
+
if (preferred.from.address.endsWith(`@${this.sendingDomain}`)) {
|
|
108
|
+
return preferred;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Invalid configuration: don't allow to send from this sending domain
|
|
112
|
+
logging.error(`[EmailAddresses] Invalid configuration: cannot send emails from ${preferred.from.address} when sending domain is ${this.sendingDomain}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Only allow to send from the configured from address
|
|
116
|
+
const address = {
|
|
117
|
+
from: this.defaultFromEmail,
|
|
118
|
+
replyTo: preferred.replyTo || preferred.from
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Do allow to change the sender name if requested
|
|
122
|
+
if (preferred.from.name) {
|
|
123
|
+
address.from.name = preferred.from.name;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (address.replyTo.address === address.from.address) {
|
|
127
|
+
return {
|
|
128
|
+
from: address.from
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return address;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* When changing any from or reply to addresses in the system, we need to validate them
|
|
137
|
+
*/
|
|
138
|
+
validate(email: string, type: EmailAddressType): EmailAddressesValidation {
|
|
139
|
+
if (!this.#isValidEmailAddress(email)) {
|
|
140
|
+
// Never allow an invalid email address
|
|
141
|
+
return {
|
|
142
|
+
allowed: email === this.defaultFromEmail.address, // Localhost email noreply@127.0.0.1 is marked as invalid, but we should allow it
|
|
143
|
+
verificationEmailRequired: false,
|
|
144
|
+
reason: 'invalid'
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!this.managedEmailEnabled) {
|
|
149
|
+
// Self hoster or legacy Ghost Pro
|
|
150
|
+
return {
|
|
151
|
+
allowed: true,
|
|
152
|
+
verificationEmailRequired: false // Self hosters don't need to verify email addresses
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (this.sendingDomain) {
|
|
157
|
+
// Only allow it if it ends with the sending domain
|
|
158
|
+
if (email.endsWith(`@${this.sendingDomain}`)) {
|
|
159
|
+
return {
|
|
160
|
+
allowed: true,
|
|
161
|
+
verificationEmailRequired: false
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Use same restrictions as one without a sending domain for other addresses
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Only allow to edit the replyTo address, with verification
|
|
169
|
+
if (type === 'replyTo') {
|
|
170
|
+
return {
|
|
171
|
+
allowed: true,
|
|
172
|
+
verificationEmailRequired: email !== this.defaultFromEmail.address
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Not allowed to change from
|
|
177
|
+
return {
|
|
178
|
+
allowed: email === this.defaultFromEmail.address,
|
|
179
|
+
verificationEmailRequired: false,
|
|
180
|
+
reason: 'not allowed'
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class EmailAddressServiceWrapper {
|
|
2
2
|
/**
|
|
3
|
-
* @type {import('
|
|
3
|
+
* @type {import('./EmailAddressService').EmailAddressService}
|
|
4
4
|
*/
|
|
5
5
|
service;
|
|
6
6
|
|
|
@@ -14,9 +14,7 @@ class EmailAddressServiceWrapper {
|
|
|
14
14
|
const settingsHelpers = require('../settings-helpers');
|
|
15
15
|
const validator = require('@tryghost/validator');
|
|
16
16
|
|
|
17
|
-
const {
|
|
18
|
-
EmailAddressService
|
|
19
|
-
} = require('@tryghost/email-addresses');
|
|
17
|
+
const {EmailAddressService} = require('./EmailAddressService');
|
|
20
18
|
|
|
21
19
|
this.service = new EmailAddressService({
|
|
22
20
|
labs,
|
|
@@ -3,7 +3,7 @@ const logging = require('@tryghost/logging');
|
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @typedef {import('
|
|
6
|
+
* @typedef {import('../email-service/EmailEventProcessor')} EmailEventProcessor
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -7,7 +7,8 @@ class EmailAnalyticsServiceWrapper {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const EmailAnalyticsService = require('./EmailAnalyticsService');
|
|
10
|
-
const
|
|
10
|
+
const EmailEventStorage = require('../email-service/EmailEventStorage');
|
|
11
|
+
const EmailEventProcessor = require('../email-service/EmailEventProcessor');
|
|
11
12
|
const MailgunProvider = require('./EmailAnalyticsProviderMailgun');
|
|
12
13
|
const {EmailRecipientFailure, EmailSpamComplaintEvent, Email} = require('../../models');
|
|
13
14
|
const StartEmailAnalyticsJobEvent = require('./events/StartEmailAnalyticsJobEvent');
|