ghost 5.119.3 → 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 (129) 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.2697b46a5652693fc674.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/repositories/MemberRepository.js +92 -92
  99. package/core/server/services/members-events/LastSeenAtUpdater.js +1 -1
  100. package/core/server/services/settings-helpers/SettingsHelpers.js +1 -1
  101. package/core/server/services/staff/StaffServiceEmails.js +1 -1
  102. package/core/server/services/stats/PostsStatsService.js +28 -7
  103. package/core/server/web/api/app.js +0 -1
  104. package/core/server/web/api/endpoints/admin/app.js +0 -2
  105. package/core/server/web/api/endpoints/content/app.js +0 -2
  106. package/core/server/web/api/middleware/upload.js +2 -2
  107. package/core/shared/custom-theme-settings-cache/CustomThemeSettingsService.js +2 -1
  108. package/package.json +39 -97
  109. package/tsconfig.tsbuildinfo +1 -1
  110. package/yarn.lock +385 -517
  111. package/components/tryghost-api-framework-5.119.3.tgz +0 -0
  112. package/components/tryghost-custom-fonts-5.119.3.tgz +0 -0
  113. package/components/tryghost-domain-events-5.119.3.tgz +0 -0
  114. package/components/tryghost-email-addresses-5.119.3.tgz +0 -0
  115. package/components/tryghost-email-service-5.119.3.tgz +0 -0
  116. package/components/tryghost-html-to-plaintext-5.119.3.tgz +0 -0
  117. package/components/tryghost-i18n-5.119.3.tgz +0 -0
  118. package/components/tryghost-job-manager-5.119.3.tgz +0 -0
  119. package/components/tryghost-members-csv-5.119.3.tgz +0 -0
  120. package/components/tryghost-mw-error-handler-5.119.3.tgz +0 -0
  121. package/components/tryghost-mw-vhost-5.119.3.tgz +0 -0
  122. package/components/tryghost-prometheus-metrics-5.119.3.tgz +0 -0
  123. package/components/tryghost-security-5.119.3.tgz +0 -0
  124. package/core/built/admin/assets/chunk.524.c86e2e1b3e94d7cb1e4c.js +0 -35
  125. package/core/server/services/api-version-compatibility/APIVersionCompatibilityService.js +0 -99
  126. package/core/server/services/api-version-compatibility/VersionNotificationsDataService.js +0 -80
  127. package/core/server/services/api-version-compatibility/extract-api-key.js +0 -57
  128. package/core/server/services/api-version-compatibility/mw-api-version-mismatch.js +0 -31
  129. /package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js.LICENSE.txt → chunk.383.25fca2f09b4896656125.js.LICENSE.txt} +0 -0
@@ -0,0 +1,238 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width" />
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6
+ <!--[if mso]><xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch><o:AllowPNG/></o:OfficeDocumentSettings></xml><![endif]-->
7
+ <title>{{post.title}}</title>
8
+ {{>styles}}
9
+ </head>
10
+ <body>
11
+ <span class="preheader">{{preheader}}</span>
12
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" width="100%">
13
+ <!-- Outlook doesn't respect max-width so we need an extra centered table -->
14
+ <!--[if mso]>
15
+ <tr>
16
+ <td>
17
+ <center>
18
+ <table border="0" cellpadding="0" cellspacing="0" width="600">
19
+ <![endif]-->
20
+ <tr>
21
+ <td>&nbsp;</td>
22
+ <td class="container">
23
+ <div class="content">
24
+ <!-- START CENTERED WHITE CONTAINER -->
25
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="main" width="100%">
26
+
27
+ <!-- START MAIN CONTENT AREA -->
28
+ <tr>
29
+ <td class="wrapper">
30
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
31
+ {{#if headerImage}}
32
+ <tr class="header-image-row">
33
+ <td class="header-image" width="100%" align="center">
34
+ <a href="{{site.url}}">
35
+ <img
36
+ src="{{headerImage}}"
37
+ {{#if headerImageWidth}}
38
+ width="{{headerImageWidth}}"
39
+ {{/if}}
40
+ >
41
+ </a>
42
+ </td>
43
+ </tr>
44
+ {{/if}}
45
+
46
+ {{#if (or showHeaderIcon showHeaderTitle showHeaderName) }}
47
+ <tr class="site-info-row">
48
+ <td class="site-info" width="100%" align="center">
49
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
50
+ {{#if (and showHeaderIcon site.iconUrl) }}
51
+ <tr>
52
+ <td class="site-icon"><a href="{{site.url}}"><img src="{{site.iconUrl}}" alt="{{site.title}}" border="0" width="48" height="48"></a></td>
53
+ </tr>
54
+ {{/if}}
55
+ {{#if showHeaderTitle }}
56
+ <tr>
57
+ <td class="site-url {{#unless showHeaderName}}site-url-bottom-padding{{/unless}}"><div style="width: 100% !important;"><a href="{{site.url}}" class="site-title">{{site.title}}</a></div></td>
58
+ </tr>
59
+ {{/if}}
60
+ {{#if (and showHeaderName showHeaderTitle) }}
61
+ <tr>
62
+ <td class="site-url site-url-bottom-padding"><div style="width: 100% !important;"><a href="{{site.url}}" class="site-subtitle">{{newsletter.name}}</a></div></td>
63
+ </tr>
64
+ {{/if}}
65
+ {{#if (and showHeaderName (not showHeaderTitle)) }}
66
+ <tr>
67
+ <td class="site-url site-url-bottom-padding"><div style="width: 100% !important;"><a href="{{site.url}}" class="site-title">{{newsletter.name}}</a></div></td>
68
+ </tr>
69
+ {{/if}}
70
+
71
+ </table>
72
+ </td>
73
+ </tr>
74
+ {{/if}}
75
+
76
+ {{#if newsletter.showPostTitleSection}}
77
+ <tr>
78
+ <td class="{{classes.title}}">
79
+ <a href="{{post.url}}" class="{{classes.titleLink}}">{{post.title}}</a>
80
+ </td>
81
+ </tr>
82
+ {{#if (and newsletter.showExcerpt post.customExcerpt)}}
83
+ <tr>
84
+ <td class="post-excerpt-wrapper" style="width: 100%">
85
+ <p class="{{classes.excerpt}}">{{post.customExcerpt}}</p>
86
+ </td>
87
+ </tr>
88
+ {{/if}}
89
+ <tr>
90
+ <td style="width: 100%">
91
+ <table class="post-meta-wrapper" role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-bottom: 32px;">
92
+ <tr>
93
+ <td height="20" class="{{classes.meta}}" style="padding: 0;">
94
+ {{{t 'By {authors}' authors=post.authors }}} • <span class="post-meta-date">{{t '{date}' date=post.publishedAt}} </span>
95
+ </td>
96
+ <td class="{{classes.meta}} view-online desktop">
97
+ <a href="{{post.url}}" class="view-online-link">{{t 'View in browser'}}</a>
98
+ </td>
99
+ </tr>
100
+ <tr class="{{classes.meta}} view-online-mobile">
101
+ <td height="20" class="view-online">
102
+ <a href="{{post.url}}" class="view-online-link">{{t 'View in browser'}}</a>
103
+ </td>
104
+ </tr>
105
+ </table>
106
+ </td>
107
+ </tr>
108
+ {{/if}}
109
+
110
+ {{#if showFeatureImage }}
111
+ <tr class="feature-image-row">
112
+ <td class="feature-image
113
+ {{#if post.feature_image_caption }}
114
+ feature-image-with-caption
115
+ {{/if}}
116
+ " align="center"><img
117
+ src="{{post.feature_image}}"
118
+ {{#if post.feature_image_width }}
119
+ width="{{post.feature_image_width}}"
120
+ {{/if}}
121
+ {{#if post.feature_image_alt }}
122
+ alt="{{post.feature_image_alt}}"
123
+ {{/if}}
124
+ ></td>
125
+ </tr>
126
+
127
+ {{#if post.feature_image_caption }}
128
+ <tr>
129
+ <td align="center">
130
+ <div class="feature-image-caption">
131
+ {{{post.feature_image_caption}}}
132
+ </div>
133
+ </td>
134
+ </tr>
135
+ {{/if}}
136
+ {{/if}}
137
+ <tr class="post-content-row">
138
+ <td class="{{classes.body}}">
139
+ <!-- POST CONTENT START -->
140
+ {{{html}}}
141
+ <!-- POST CONTENT END -->
142
+
143
+ {{#if paywall}}
144
+ {{>paywall}}
145
+ {{/if}}
146
+ </td>
147
+ </tr>
148
+ </table>
149
+ </td>
150
+ </tr>
151
+
152
+ <!-- END MAIN CONTENT AREA -->
153
+
154
+ {{#if (or feedbackButtons newsletter.showCommentCta) }}
155
+ <tr>
156
+ <td dir="ltr" width="100%" style="background-color: #ffffff; text-align: center; padding: 32px 0 24px; border-bottom: 1px solid #e0e7eb;" align="center">
157
+ <table class="feedback-buttons" role="presentation" border="0" cellpadding="0" cellspacing="0">
158
+ <tr>
159
+ {{#if feedbackButtons }}
160
+ {{> feedbackButton feedbackButtons href=feedbackButtons.likeHref buttonText='More like this' iconUrl="https://static.ghost.org/v5.0.0/images/more-like-this-mobile.png" width="42" height="42" }}
161
+ {{> feedbackButton feedbackButtons href=feedbackButtons.dislikeHref buttonText='Less like this' iconUrl="https://static.ghost.org/v5.0.0/images/less-like-this-mobile.png" width="42" height="42" }}
162
+ {{/if}}
163
+ {{#if newsletter.showCommentCta}}
164
+ {{> feedbackButton href=post.commentUrl buttonText='Comment' iconUrl="https://static.ghost.org/v5.0.0/images/comment-mobile.png" width="42" height="42"}}
165
+ {{/if}}
166
+ </tr>
167
+ </table>
168
+ </td>
169
+ </tr>
170
+ {{/if}}
171
+
172
+ {{#if latestPosts.length}}
173
+ <tr>
174
+ <td style="padding: 24px 0; border-bottom: 1px solid #e0e7eb;">
175
+ <h3 class="latest-posts-header">{{t 'Keep reading'}}</h3>
176
+ {{> latestPosts}}
177
+ </td>
178
+ </tr>
179
+ {{/if}}
180
+
181
+ {{#if newsletter.showSubscriptionDetails}}
182
+ <tr>
183
+ <td class="subscription-box">
184
+ <h3>{{t 'Subscription details'}}</h3>
185
+ <p style="margin-bottom: 16px;">
186
+ <span>{{{t "You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {site}." site=site.title }}}</span> %%{status_text}%%
187
+ </p>
188
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
189
+ <tr>
190
+ <td class="subscription-details">
191
+ <p class="%%{name_class}%%">{{t 'Name'}}: %%{name, "not provided"}%%</p>
192
+ <p>{{t 'Email'}}: <a href="#">%%{email}%%</a></p>
193
+ <p>{{t 'Member since'}}: %%{created_at}%%</p>
194
+ </td>
195
+ <td align="right" valign="bottom" class="manage-subscription">
196
+ <a href="%%{manage_account_url}%%"> {{t 'Manage subscription'}} &rarr;</a>
197
+ </td>
198
+ </tr>
199
+ </table>
200
+ </td>
201
+ </tr>
202
+ {{/if}}
203
+
204
+ <tr>
205
+ <td class="wrapper" align="center">
206
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-top: 40px; padding-bottom: 30px;">
207
+ {{#if footerContent }}
208
+ <tr><td class="footer">{{{footerContent}}}</td></tr>
209
+ {{/if}}
210
+ <tr>
211
+ <td class="footer">{{site.title}} &copy; {{year}} – <a href="%%{unsubscribe_url}%%">{{t 'Unsubscribe'}}</a></td>
212
+ </tr>
213
+
214
+ {{#if showBadge }}
215
+ <tr>
216
+ <td class="footer-powered"><a href="https://ghost.org/?via=pbg-newsletter"><img src="https://static.ghost.org/v4.0.0/images/powered.png" border="0" width="142" height="30" class="gh-powered" alt="Powered by Ghost"></a></td>
217
+ </tr>
218
+ {{/if}}
219
+ </table>
220
+ </td>
221
+ </tr>
222
+
223
+ </table>
224
+ <!-- END CENTERED WHITE CONTAINER -->
225
+ </div>
226
+ </td>
227
+ <td>&nbsp;</td>
228
+ </tr>
229
+
230
+ <!--[if mso]>
231
+ </table>
232
+ </center>
233
+ </td>
234
+ </tr>
235
+ <![endif]-->
236
+ </table>
237
+ </body>
238
+ </html>
@@ -0,0 +1,63 @@
1
+ module.exports = class EmailBouncedEvent {
2
+ /**
3
+ * @readonly
4
+ * @type {string}
5
+ */
6
+ id;
7
+
8
+ /**
9
+ * @readonly
10
+ * @type {string}
11
+ */
12
+ email;
13
+
14
+ /**
15
+ * @readonly
16
+ * @type {string}
17
+ */
18
+ memberId;
19
+
20
+ /**
21
+ * @readonly
22
+ * @type {string}
23
+ */
24
+ emailId;
25
+
26
+ /**
27
+ * @readonly
28
+ * @type {{message: string, code: number, enhancedCode: string | null}|null}
29
+ */
30
+ error;
31
+
32
+ /**
33
+ * @readonly
34
+ * @type {string}
35
+ */
36
+ emailRecipientId;
37
+
38
+ /**
39
+ * @readonly
40
+ * @type {Date}
41
+ */
42
+ timestamp;
43
+
44
+ /**
45
+ * @private
46
+ */
47
+ constructor({id, email, memberId, emailId, error, emailRecipientId, timestamp}) {
48
+ this.id = id;
49
+ this.memberId = memberId;
50
+ this.emailId = emailId;
51
+ this.email = email;
52
+ this.error = error;
53
+ this.emailRecipientId = emailRecipientId;
54
+ this.timestamp = timestamp;
55
+ }
56
+
57
+ static create(data) {
58
+ return new EmailBouncedEvent({
59
+ ...data,
60
+ timestamp: data.timestamp || new Date
61
+ });
62
+ }
63
+ };
@@ -0,0 +1,49 @@
1
+ module.exports = class EmailDeliveredEvent {
2
+ /**
3
+ * @readonly
4
+ * @type {string}
5
+ */
6
+ email;
7
+
8
+ /**
9
+ * @readonly
10
+ * @type {string}
11
+ */
12
+ memberId;
13
+
14
+ /**
15
+ * @readonly
16
+ * @type {string}
17
+ */
18
+ emailId;
19
+
20
+ /**
21
+ * @readonly
22
+ * @type {string}
23
+ */
24
+ emailRecipientId;
25
+
26
+ /**
27
+ * @readonly
28
+ * @type {Date}
29
+ */
30
+ timestamp;
31
+
32
+ /**
33
+ * @private
34
+ */
35
+ constructor({email, memberId, emailId, emailRecipientId, timestamp}) {
36
+ this.email = email;
37
+ this.memberId = memberId;
38
+ this.emailId = emailId;
39
+ this.emailRecipientId = emailRecipientId;
40
+ this.timestamp = timestamp;
41
+ }
42
+
43
+ static create(data) {
44
+ return new EmailDeliveredEvent({
45
+ ...data,
46
+ timestamp: data.timestamp || new Date
47
+ });
48
+ }
49
+ };
@@ -0,0 +1,49 @@
1
+ module.exports = class EmailOpenedEvent {
2
+ /**
3
+ * @readonly
4
+ * @type {string}
5
+ */
6
+ email;
7
+
8
+ /**
9
+ * @readonly
10
+ * @type {string}
11
+ */
12
+ memberId;
13
+
14
+ /**
15
+ * @readonly
16
+ * @type {string}
17
+ */
18
+ emailId;
19
+
20
+ /**
21
+ * @readonly
22
+ * @type {string}
23
+ */
24
+ emailRecipientId;
25
+
26
+ /**
27
+ * @readonly
28
+ * @type {Date}
29
+ */
30
+ timestamp;
31
+
32
+ /**
33
+ * @private
34
+ */
35
+ constructor({email, memberId, emailId, emailRecipientId, timestamp}) {
36
+ this.memberId = memberId;
37
+ this.emailId = emailId;
38
+ this.emailRecipientId = emailRecipientId;
39
+ this.email = email;
40
+ this.timestamp = timestamp;
41
+ }
42
+
43
+ static create(data) {
44
+ return new EmailOpenedEvent({
45
+ ...data,
46
+ timestamp: data.timestamp || new Date
47
+ });
48
+ }
49
+ };
@@ -0,0 +1,63 @@
1
+ module.exports = class EmailTemporaryBouncedEvent {
2
+ /**
3
+ * @readonly
4
+ * @type {string}
5
+ */
6
+ id;
7
+
8
+ /**
9
+ * @readonly
10
+ * @type {string}
11
+ */
12
+ email;
13
+
14
+ /**
15
+ * @readonly
16
+ * @type {string}
17
+ */
18
+ memberId;
19
+
20
+ /**
21
+ * @readonly
22
+ * @type {string}
23
+ */
24
+ emailId;
25
+
26
+ /**
27
+ * @readonly
28
+ * @type {{message: string, code: number, enhancedCode: string | null}|null}
29
+ */
30
+ error;
31
+
32
+ /**
33
+ * @readonly
34
+ * @type {string}
35
+ */
36
+ emailRecipientId;
37
+
38
+ /**
39
+ * @readonly
40
+ * @type {Date}
41
+ */
42
+ timestamp;
43
+
44
+ /**
45
+ * @private
46
+ */
47
+ constructor({id, email, memberId, emailId, emailRecipientId, error, timestamp}) {
48
+ this.id = id;
49
+ this.memberId = memberId;
50
+ this.emailId = emailId;
51
+ this.email = email;
52
+ this.error = error;
53
+ this.emailRecipientId = emailRecipientId;
54
+ this.timestamp = timestamp;
55
+ }
56
+
57
+ static create(data) {
58
+ return new EmailTemporaryBouncedEvent({
59
+ ...data,
60
+ timestamp: data.timestamp || new Date
61
+ });
62
+ }
63
+ };
@@ -0,0 +1,42 @@
1
+ module.exports = class EmailUnsubscribedEvent {
2
+ /**
3
+ * @readonly
4
+ * @type {string}
5
+ */
6
+ email;
7
+
8
+ /**
9
+ * @readonly
10
+ * @type {string}
11
+ */
12
+ memberId;
13
+
14
+ /**
15
+ * @readonly
16
+ * @type {string}
17
+ */
18
+ emailId;
19
+
20
+ /**
21
+ * @readonly
22
+ * @type {Date}
23
+ */
24
+ timestamp;
25
+
26
+ /**
27
+ * @private
28
+ */
29
+ constructor({email, memberId, emailId, timestamp}) {
30
+ this.memberId = memberId;
31
+ this.emailId = emailId;
32
+ this.email = email;
33
+ this.timestamp = timestamp;
34
+ }
35
+
36
+ static create(data) {
37
+ return new EmailUnsubscribedEvent({
38
+ ...data,
39
+ timestamp: data.timestamp || new Date
40
+ });
41
+ }
42
+ };
@@ -0,0 +1,42 @@
1
+ module.exports = class SpamComplaintEvent {
2
+ /**
3
+ * @readonly
4
+ * @type {string}
5
+ */
6
+ email;
7
+
8
+ /**
9
+ * @readonly
10
+ * @type {string}
11
+ */
12
+ memberId;
13
+
14
+ /**
15
+ * @readonly
16
+ * @type {string}
17
+ */
18
+ emailId;
19
+
20
+ /**
21
+ * @readonly
22
+ * @type {Date}
23
+ */
24
+ timestamp;
25
+
26
+ /**
27
+ * @private
28
+ */
29
+ constructor({email, memberId, emailId, timestamp}) {
30
+ this.memberId = memberId;
31
+ this.emailId = emailId;
32
+ this.email = email;
33
+ this.timestamp = timestamp;
34
+ }
35
+
36
+ static create(data) {
37
+ return new SpamComplaintEvent({
38
+ ...data,
39
+ timestamp: data.timestamp || new Date
40
+ });
41
+ }
42
+ };
@@ -0,0 +1,59 @@
1
+ module.exports = {
2
+ registerHelpers(handlebars, labs, thist) {
3
+ handlebars.registerHelper('if', function (conditional, options) {
4
+ if (conditional) {
5
+ return options.fn(this);
6
+ } else {
7
+ return options.inverse(this);
8
+ }
9
+ });
10
+
11
+ handlebars.registerHelper('and', function () {
12
+ const len = arguments.length - 1;
13
+
14
+ for (let i = 0; i < len; i++) {
15
+ if (!arguments[i]) {
16
+ return false;
17
+ }
18
+ }
19
+
20
+ return true;
21
+ });
22
+
23
+ handlebars.registerHelper('not', function () {
24
+ const len = arguments.length - 1;
25
+
26
+ for (let i = 0; i < len; i++) {
27
+ if (!arguments[i]) {
28
+ return true;
29
+ }
30
+ }
31
+
32
+ return false;
33
+ });
34
+
35
+ handlebars.registerHelper('or', function () {
36
+ const len = arguments.length - 1;
37
+
38
+ for (let i = 0; i < len; i++) {
39
+ if (arguments[i]) {
40
+ return true;
41
+ }
42
+ }
43
+
44
+ return false;
45
+ });
46
+
47
+ handlebars.registerHelper('hasFeature', function (flag, options) {
48
+ if (labs.isSet(flag)) {
49
+ return options.fn(this);
50
+ } else {
51
+ return options.inverse(this);
52
+ }
53
+ });
54
+ handlebars.registerHelper('t', function (key, options) {
55
+ let hash = options?.hash;
56
+ return thist(key, hash || options || {});
57
+ });
58
+ }
59
+ };
@@ -1,5 +1,6 @@
1
1
  const {AbstractEmailSuppressionList, EmailSuppressionData, EmailSuppressedEvent} = require('./EmailSuppressionList');
2
- const {SpamComplaintEvent, EmailBouncedEvent} = require('@tryghost/email-service');
2
+ const SpamComplaintEvent = require('../email-service/events/SpamComplaintEvent');
3
+ const EmailBouncedEvent = require('../email-service/events/EmailBouncedEvent');
3
4
  const DomainEvents = require('@tryghost/domain-events');
4
5
  const logging = require('@tryghost/logging');
5
6
  const models = require('../../models');
@@ -27,5 +27,6 @@ module.exports.init = async function init() {
27
27
 
28
28
  // The final intention is to have this run on a schedule
29
29
  // For the initial version, we'll just ping when the server starts
30
- await explorePingService.ping();
30
+ // Without waiting for the response
31
+ explorePingService.ping();
31
32
  };
@@ -16,7 +16,7 @@ const messages = {
16
16
  reason: ' Reason: {reason}.',
17
17
  messageSent: 'Message sent. Double check inbox and spam folder!'
18
18
  };
19
- const {EmailAddressParser} = require('@tryghost/email-addresses');
19
+ const EmailAddressParser = require('../email-address/EmailAddressParser');
20
20
 
21
21
  function getDomain() {
22
22
  const domain = urlUtils.urlFor('home', true).match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i'));
@@ -70,8 +70,9 @@ class ExternalMediaInliner {
70
70
 
71
71
  /**
72
72
  *
73
+ * @param {string} requestURL - url of remote media
73
74
  * @param {Object} response - response from request
74
- * @returns {Object}
75
+ * @returns {Promise<Object>}
75
76
  */
76
77
  async extractFileDataFromResponse(requestURL, response) {
77
78
  let extension;