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.
Files changed (130) hide show
  1. package/components/tryghost-i18n-5.120.0.tgz +0 -0
  2. package/core/boot.js +0 -2
  3. package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +7555 -7216
  4. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-60ce658c.mjs → CodeEditorView-1c5b0683.mjs} +2 -2
  5. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
  6. package/core/built/admin/assets/admin-x-settings/{index-8480baa8.mjs → index-14e518a7.mjs} +3 -3
  7. package/core/built/admin/assets/admin-x-settings/{index-a2648c61.mjs → index-fc9f985b.mjs} +2 -2
  8. package/core/built/admin/assets/admin-x-settings/{modals-6900c1d5.mjs → modals-15bc6a0f.mjs} +7192 -6656
  9. package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js → chunk.383.25fca2f09b4896656125.js} +76 -59
  10. package/core/built/admin/assets/chunk.524.1657b12c0ab25dd9fb79.js +28 -0
  11. package/core/built/admin/assets/{chunk.582.98a820cbc4bb65f2e685.js → chunk.582.09869b1f1a3cc0ab81f6.js} +19 -26
  12. package/core/built/admin/assets/{ghost-843572e9507d099162ae744d791daba1.js → ghost-b3b44421acca3b3eec76bfbb6ba0e81b.js} +3 -3
  13. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +12578 -12352
  14. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +423 -211
  15. package/core/built/admin/assets/posts/posts.js +13680 -13671
  16. package/core/built/admin/assets/stats/stats.js +16457 -16635
  17. package/core/built/admin/assets/{vendor-8f805740fee4db959a5b2119001a56b1.js → vendor-4ce6d282a2a00fe486a0951e0591da19.js} +11 -9
  18. package/core/built/admin/index.html +5 -5
  19. package/core/frontend/helpers/match.js +6 -0
  20. package/core/frontend/services/routing/ParentRouter.js +1 -1
  21. package/core/frontend/services/routing/controllers/email-post.js +0 -2
  22. package/core/frontend/services/routing/controllers/previews.js +0 -3
  23. package/core/frontend/web/middleware/frontend-caching.js +2 -2
  24. package/core/server/api/endpoints/authentication.js +37 -73
  25. package/core/server/api/endpoints/authors-public.js +8 -9
  26. package/core/server/api/endpoints/db.js +34 -35
  27. package/core/server/api/endpoints/emails.js +8 -10
  28. package/core/server/api/endpoints/integrations.js +20 -18
  29. package/core/server/api/endpoints/invites.js +8 -10
  30. package/core/server/api/endpoints/labels.js +19 -23
  31. package/core/server/api/endpoints/notifications.js +3 -4
  32. package/core/server/api/endpoints/pages-public.js +8 -10
  33. package/core/server/api/endpoints/pages.js +14 -18
  34. package/core/server/api/endpoints/posts-public.js +8 -10
  35. package/core/server/api/endpoints/posts.js +6 -8
  36. package/core/server/api/endpoints/previews.js +8 -10
  37. package/core/server/api/endpoints/redirects.js +7 -8
  38. package/core/server/api/endpoints/schedules.js +5 -7
  39. package/core/server/api/endpoints/slugs.js +7 -9
  40. package/core/server/api/endpoints/snippets.js +16 -20
  41. package/core/server/api/endpoints/tags-public.js +8 -10
  42. package/core/server/api/endpoints/tags.js +19 -23
  43. package/core/server/api/endpoints/themes.js +6 -8
  44. package/core/server/api/endpoints/users.js +31 -36
  45. package/core/server/api/endpoints/utils/permissions.js +10 -10
  46. package/core/server/api/endpoints/utils/serializers/output/roles.js +9 -10
  47. package/core/server/api/endpoints/utils/validators/input/images.js +43 -52
  48. package/core/server/api/endpoints/utils/validators/input/invites.js +6 -8
  49. package/core/server/api/endpoints/webhooks.js +38 -42
  50. package/core/server/data/migrations/versions/5.120/2025-05-07-14-57-38-add-newsletters-button-corners-column.js +8 -0
  51. package/core/server/data/migrations/versions/5.120/2025-05-13-17-36-56-add-newsletters-button-style-column.js +8 -0
  52. package/core/server/data/migrations/versions/5.120/2025-05-14-20-00-15-add-newsletters-setting-columns.js +22 -0
  53. package/core/server/data/schema/schema.js +6 -1
  54. package/core/server/lib/image/Gravatar.js +12 -13
  55. package/core/server/lib/lexical.js +3 -1
  56. package/core/server/models/newsletter.js +6 -1
  57. package/core/server/services/api-version-compatibility/index.js +1 -33
  58. package/core/server/services/auth/session/emails/signin.js +3 -3
  59. package/core/server/services/email-address/EmailAddressParser.js +52 -0
  60. package/core/server/services/email-address/EmailAddressParser.js.d.ts +13 -0
  61. package/core/server/services/email-address/EmailAddressService.js +142 -0
  62. package/core/server/services/email-address/EmailAddressService.ts +183 -0
  63. package/core/server/services/email-address/EmailAddressServiceWrapper.js +2 -4
  64. package/core/server/services/email-analytics/EmailAnalyticsService.js +1 -1
  65. package/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js +2 -1
  66. package/core/server/services/email-service/BatchSendingService.js +703 -0
  67. package/core/server/services/email-service/EmailBodyCache.js +20 -0
  68. package/core/server/services/email-service/EmailController.js +94 -0
  69. package/core/server/services/email-service/EmailEventProcessor.js +267 -0
  70. package/core/server/services/email-service/EmailEventStorage.js +187 -0
  71. package/core/server/services/email-service/EmailRenderer.js +1263 -0
  72. package/core/server/services/email-service/EmailSegmenter.js +74 -0
  73. package/core/server/services/email-service/EmailService.js +310 -0
  74. package/core/server/services/email-service/EmailServiceWrapper.js +9 -2
  75. package/core/server/services/email-service/MailgunEmailProvider.js +191 -0
  76. package/core/server/services/email-service/SendingService.js +173 -0
  77. package/core/server/services/email-service/email-templates/partials/feedback-button.hbs +7 -0
  78. package/core/server/services/email-service/email-templates/partials/latest-posts.hbs +39 -0
  79. package/core/server/services/email-service/email-templates/partials/paywall.hbs +20 -0
  80. package/core/server/services/email-service/email-templates/partials/styles.hbs +2348 -0
  81. package/core/server/services/email-service/email-templates/template.hbs +238 -0
  82. package/core/server/services/email-service/events/EmailBouncedEvent.js +63 -0
  83. package/core/server/services/email-service/events/EmailDeliveredEvent.js +49 -0
  84. package/core/server/services/email-service/events/EmailOpenedEvent.js +49 -0
  85. package/core/server/services/email-service/events/EmailTemporaryBouncedEvent.js +63 -0
  86. package/core/server/services/email-service/events/EmailUnsubscribedEvent.js +42 -0
  87. package/core/server/services/email-service/events/SpamComplaintEvent.js +42 -0
  88. package/core/server/services/email-service/helpers/register-helpers.js +59 -0
  89. package/core/server/services/email-suppression-list/MailgunEmailSuppressionList.js +2 -1
  90. package/core/server/services/explore-ping/index.js +2 -1
  91. package/core/server/services/mail/GhostMailer.js +1 -1
  92. package/core/server/services/media-inliner/ExternalMediaInliner.js +2 -1
  93. package/core/server/services/members/api.js +15 -15
  94. package/core/server/services/members/emails/signin.js +4 -4
  95. package/core/server/services/members/emails/signup-paid.js +3 -4
  96. package/core/server/services/members/emails/signup.js +3 -3
  97. package/core/server/services/members/emails/subscribe.js +3 -3
  98. package/core/server/services/members/members-api/controllers/RouterController.js +50 -36
  99. package/core/server/services/members/members-api/repositories/MemberRepository.js +92 -92
  100. package/core/server/services/members-events/LastSeenAtUpdater.js +1 -1
  101. package/core/server/services/settings-helpers/SettingsHelpers.js +1 -1
  102. package/core/server/services/staff/StaffServiceEmails.js +1 -1
  103. package/core/server/services/stats/PostsStatsService.js +28 -7
  104. package/core/server/web/api/app.js +0 -1
  105. package/core/server/web/api/endpoints/admin/app.js +0 -2
  106. package/core/server/web/api/endpoints/content/app.js +0 -2
  107. package/core/server/web/api/middleware/upload.js +2 -2
  108. package/core/shared/custom-theme-settings-cache/CustomThemeSettingsService.js +2 -1
  109. package/package.json +39 -97
  110. package/tsconfig.tsbuildinfo +1 -1
  111. package/yarn.lock +385 -517
  112. package/components/tryghost-api-framework-5.119.2.tgz +0 -0
  113. package/components/tryghost-custom-fonts-5.119.2.tgz +0 -0
  114. package/components/tryghost-domain-events-5.119.2.tgz +0 -0
  115. package/components/tryghost-email-addresses-5.119.2.tgz +0 -0
  116. package/components/tryghost-email-service-5.119.2.tgz +0 -0
  117. package/components/tryghost-html-to-plaintext-5.119.2.tgz +0 -0
  118. package/components/tryghost-i18n-5.119.2.tgz +0 -0
  119. package/components/tryghost-job-manager-5.119.2.tgz +0 -0
  120. package/components/tryghost-members-csv-5.119.2.tgz +0 -0
  121. package/components/tryghost-mw-error-handler-5.119.2.tgz +0 -0
  122. package/components/tryghost-mw-vhost-5.119.2.tgz +0 -0
  123. package/components/tryghost-prometheus-metrics-5.119.2.tgz +0 -0
  124. package/components/tryghost-security-5.119.2.tgz +0 -0
  125. package/core/built/admin/assets/chunk.524.b8545af3bb714bc4f820.js +0 -35
  126. package/core/server/services/api-version-compatibility/APIVersionCompatibilityService.js +0 -99
  127. package/core/server/services/api-version-compatibility/VersionNotificationsDataService.js +0 -80
  128. package/core/server/services/api-version-compatibility/extract-api-key.js +0 -57
  129. package/core/server/services/api-version-compatibility/mw-api-version-mismatch.js +0 -31
  130. /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('@tryghost/email-addresses').EmailAddressService}
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('@tryghost/email-service').EmailEventProcessor} EmailEventProcessor
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 {EmailEventStorage, EmailEventProcessor} = require('@tryghost/email-service');
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');