ghost 5.116.2 → 5.118.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 (131) hide show
  1. package/components/{tryghost-api-framework-5.116.2.tgz → tryghost-api-framework-5.118.0.tgz} +0 -0
  2. package/components/tryghost-constants-5.118.0.tgz +0 -0
  3. package/components/tryghost-custom-fonts-5.118.0.tgz +0 -0
  4. package/components/tryghost-custom-theme-settings-service-5.118.0.tgz +0 -0
  5. package/components/tryghost-domain-events-5.118.0.tgz +0 -0
  6. package/components/tryghost-donations-5.118.0.tgz +0 -0
  7. package/components/tryghost-email-addresses-5.118.0.tgz +0 -0
  8. package/components/{tryghost-email-service-5.116.2.tgz → tryghost-email-service-5.118.0.tgz} +0 -0
  9. package/components/tryghost-email-suppression-list-5.118.0.tgz +0 -0
  10. package/components/tryghost-html-to-plaintext-5.118.0.tgz +0 -0
  11. package/components/tryghost-i18n-5.118.0.tgz +0 -0
  12. package/components/{tryghost-job-manager-5.116.2.tgz → tryghost-job-manager-5.118.0.tgz} +0 -0
  13. package/components/tryghost-link-replacer-5.118.0.tgz +0 -0
  14. package/components/{tryghost-magic-link-5.116.2.tgz → tryghost-magic-link-5.118.0.tgz} +0 -0
  15. package/components/{tryghost-member-attribution-5.116.2.tgz → tryghost-member-attribution-5.118.0.tgz} +0 -0
  16. package/components/tryghost-member-events-5.118.0.tgz +0 -0
  17. package/components/{tryghost-members-csv-5.116.2.tgz → tryghost-members-csv-5.118.0.tgz} +0 -0
  18. package/components/{tryghost-members-offers-5.116.2.tgz → tryghost-members-offers-5.118.0.tgz} +0 -0
  19. package/components/tryghost-mw-error-handler-5.118.0.tgz +0 -0
  20. package/components/tryghost-mw-vhost-5.118.0.tgz +0 -0
  21. package/components/{tryghost-post-events-5.116.2.tgz → tryghost-post-events-5.118.0.tgz} +0 -0
  22. package/components/tryghost-post-revisions-5.118.0.tgz +0 -0
  23. package/components/tryghost-posts-service-5.118.0.tgz +0 -0
  24. package/components/tryghost-prometheus-metrics-5.118.0.tgz +0 -0
  25. package/components/tryghost-security-5.118.0.tgz +0 -0
  26. package/components/tryghost-tiers-5.118.0.tgz +0 -0
  27. package/components/tryghost-webmentions-5.118.0.tgz +0 -0
  28. package/content/themes/casper/LICENSE +1 -1
  29. package/content/themes/casper/README.md +1 -1
  30. package/content/themes/casper/assets/built/screen.css +1 -1
  31. package/content/themes/casper/assets/built/screen.css.map +1 -1
  32. package/content/themes/casper/assets/css/screen.css +1 -1
  33. package/content/themes/casper/author.hbs +23 -2
  34. package/content/themes/casper/package.json +2 -2
  35. package/content/themes/casper/partials/icons/bluesky.hbs +3 -0
  36. package/content/themes/casper/partials/icons/instagram.hbs +5 -0
  37. package/content/themes/casper/partials/icons/linkedin.hbs +3 -0
  38. package/content/themes/casper/partials/icons/mastodon.hbs +3 -0
  39. package/content/themes/casper/partials/icons/threads.hbs +3 -0
  40. package/content/themes/casper/partials/icons/tiktok.hbs +3 -0
  41. package/content/themes/casper/partials/icons/twitter.hbs +3 -1
  42. package/content/themes/casper/partials/icons/youtube.hbs +3 -0
  43. package/content/themes/source/LICENSE +1 -1
  44. package/content/themes/source/README.md +1 -1
  45. package/content/themes/source/assets/built/screen.css +1 -1
  46. package/content/themes/source/assets/built/screen.css.map +1 -1
  47. package/content/themes/source/assets/css/screen.css +7 -12
  48. package/content/themes/source/author.hbs +24 -3
  49. package/content/themes/source/package.json +2 -2
  50. package/content/themes/source/partials/feature-image.hbs +2 -2
  51. package/content/themes/source/partials/icons/bluesky.hbs +3 -0
  52. package/content/themes/source/partials/icons/instagram.hbs +5 -0
  53. package/content/themes/source/partials/icons/linkedin.hbs +3 -0
  54. package/content/themes/source/partials/icons/mastodon.hbs +3 -0
  55. package/content/themes/source/partials/icons/threads.hbs +3 -0
  56. package/content/themes/source/partials/icons/tiktok.hbs +3 -0
  57. package/content/themes/source/partials/icons/youtube.hbs +3 -0
  58. package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +31793 -26588
  59. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-550846e0.mjs → CodeEditorView-1143c509.mjs} +2 -2
  60. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
  61. package/core/built/admin/assets/admin-x-settings/{index-f3cb3f4d.mjs → index-19ebc8ad.mjs} +2 -2
  62. package/core/built/admin/assets/admin-x-settings/{index-4ce2fcd1.mjs → index-ac104f42.mjs} +2635 -2607
  63. package/core/built/admin/assets/admin-x-settings/{modals-6bc20529.mjs → modals-994901ee.mjs} +6680 -6165
  64. package/core/built/admin/assets/{chunk.524.578de86e5014b911b05a.js → chunk.524.5710919eb507b9a81166.js} +8 -8
  65. package/core/built/admin/assets/{chunk.582.21bf3e37b5d84ac4b58a.js → chunk.582.c8cb99b85cfa13fc7df1.js} +10 -10
  66. package/core/built/admin/assets/{chunk.713.761d11035fe0bf3e557c.js → chunk.713.48f120c377bcaffdfddf.js} +6 -9
  67. package/core/built/admin/assets/{ghost-868c537d5c02ca65323d0122596a67ec.js → ghost-cd90a28b214ee800a007bb62cd45e6e6.js} +780 -775
  68. package/core/built/admin/assets/posts/posts.js +11561 -11302
  69. package/core/built/admin/assets/stats/stats.js +76076 -59355
  70. package/core/built/admin/index.html +4 -4
  71. package/core/frontend/helpers/social_url.js +31 -0
  72. package/core/server/api/endpoints/users.js +7 -0
  73. package/core/server/api/endpoints/utils/serializers/output/config.js +2 -1
  74. package/core/server/data/migrations/versions/5.117/2025-04-14-02-36-30-add-additional-social-accounts-columns-to-user-table.js +38 -0
  75. package/core/server/data/schema/schema.js +7 -0
  76. package/core/server/services/auth/session/index.js +5 -2
  77. package/core/server/services/auth/session/middleware.js +2 -1
  78. package/core/server/services/auth/session/session-service.js +7 -6
  79. package/core/server/services/members/api.js +2 -2
  80. package/core/server/services/members/members-api/controllers/MemberController.js +214 -0
  81. package/core/server/services/members/members-api/controllers/RouterController.js +667 -0
  82. package/core/server/services/members/members-api/controllers/WellKnownController.js +46 -0
  83. package/core/server/services/members/members-api/members-api.js +404 -0
  84. package/core/server/services/members/members-api/repositories/EventRepository.js +984 -0
  85. package/core/server/services/members/members-api/repositories/MemberRepository.js +1739 -0
  86. package/core/server/services/members/members-api/repositories/ProductRepository.js +662 -0
  87. package/core/server/services/members/members-api/services/GeolocationService.js +23 -0
  88. package/core/server/services/members/members-api/services/MemberBREADService.js +444 -0
  89. package/core/server/services/members/members-api/services/PaymentsService.js +522 -0
  90. package/core/server/services/members/members-api/services/TokenService.js +54 -0
  91. package/core/server/services/milestones/BookshelfMilestoneRepository.js +8 -9
  92. package/core/server/services/milestones/InMemoryMilestoneRepository.js +119 -0
  93. package/core/server/services/milestones/Milestone.js +231 -0
  94. package/core/server/services/milestones/MilestoneCreatedEvent.js +22 -0
  95. package/core/server/services/milestones/MilestonesService.js +327 -0
  96. package/core/server/services/milestones/service.js +2 -2
  97. package/core/server/services/newsletters/index.js +1 -1
  98. package/core/server/services/public-config/config.js +2 -1
  99. package/core/server/services/settings/settings-service.js +1 -1
  100. package/core/server/services/slack-notifications/SlackNotifications.js +1 -1
  101. package/core/server/services/slack-notifications/SlackNotificationsService.js +2 -2
  102. package/core/server/services/staff/StaffService.js +1 -1
  103. package/core/shared/config/defaults.json +3 -0
  104. package/core/shared/config/env/config.testing-mysql.json +3 -0
  105. package/core/shared/config/env/config.testing.json +3 -0
  106. package/core/shared/labs.js +2 -2
  107. package/package.json +63 -63
  108. package/tsconfig.tsbuildinfo +1 -1
  109. package/yarn.lock +306 -70
  110. package/components/tryghost-constants-5.116.2.tgz +0 -0
  111. package/components/tryghost-custom-fonts-5.116.2.tgz +0 -0
  112. package/components/tryghost-custom-theme-settings-service-5.116.2.tgz +0 -0
  113. package/components/tryghost-domain-events-5.116.2.tgz +0 -0
  114. package/components/tryghost-donations-5.116.2.tgz +0 -0
  115. package/components/tryghost-email-addresses-5.116.2.tgz +0 -0
  116. package/components/tryghost-email-suppression-list-5.116.2.tgz +0 -0
  117. package/components/tryghost-html-to-plaintext-5.116.2.tgz +0 -0
  118. package/components/tryghost-i18n-5.116.2.tgz +0 -0
  119. package/components/tryghost-link-replacer-5.116.2.tgz +0 -0
  120. package/components/tryghost-member-events-5.116.2.tgz +0 -0
  121. package/components/tryghost-members-api-5.116.2.tgz +0 -0
  122. package/components/tryghost-milestones-5.116.2.tgz +0 -0
  123. package/components/tryghost-mw-error-handler-5.116.2.tgz +0 -0
  124. package/components/tryghost-mw-vhost-5.116.2.tgz +0 -0
  125. package/components/tryghost-post-revisions-5.116.2.tgz +0 -0
  126. package/components/tryghost-posts-service-5.116.2.tgz +0 -0
  127. package/components/tryghost-prometheus-metrics-5.116.2.tgz +0 -0
  128. package/components/tryghost-security-5.116.2.tgz +0 -0
  129. package/components/tryghost-tiers-5.116.2.tgz +0 -0
  130. package/components/tryghost-webmentions-5.116.2.tgz +0 -0
  131. /package/core/built/admin/assets/{chunk.713.761d11035fe0bf3e557c.js.LICENSE.txt → chunk.713.48f120c377bcaffdfddf.js.LICENSE.txt} +0 -0
@@ -0,0 +1,46 @@
1
+ const errors = require('@tryghost/errors');
2
+ const logging = require('@tryghost/logging');
3
+ const tpl = require('@tryghost/tpl');
4
+
5
+ const messages = {
6
+ keyStoreError: 'There was an error with the keystore. Please check the settings.'
7
+ };
8
+
9
+ /**
10
+ * @typedef {import('node-jose').JWK[]} JWKS
11
+ */
12
+
13
+ /**
14
+ * @typedef {object} ITokenService
15
+ * @prop {() => Promise<JWKS>} getPublicKeys
16
+ */
17
+
18
+ module.exports = class WellKnownController {
19
+ /**
20
+ *
21
+ * @param {object} deps
22
+ * @param {ITokenService} deps.tokenService
23
+ */
24
+ constructor(deps) {
25
+ this._tokenService = deps.tokenService;
26
+ }
27
+
28
+ /**
29
+ *
30
+ * @param {import('express').Request} req
31
+ * @param {import('express').Response} res
32
+ */
33
+ async getPublicKeys(req, res) {
34
+ try {
35
+ const jwks = await this._tokenService.getPublicKeys();
36
+ res.json(jwks);
37
+ } catch (err) {
38
+ const error = new errors.InternalServerError({
39
+ message: tpl(messages.keyStoreError),
40
+ err
41
+ });
42
+ logging.error(error);
43
+ throw error;
44
+ }
45
+ }
46
+ };
@@ -0,0 +1,404 @@
1
+ const {Router} = require('express');
2
+ const body = require('body-parser');
3
+ const MagicLink = require('@tryghost/magic-link');
4
+ const errors = require('@tryghost/errors');
5
+ const logging = require('@tryghost/logging');
6
+
7
+ const PaymentsService = require('./services/PaymentsService');
8
+ const TokenService = require('./services/TokenService');
9
+ const GeolocationService = require('./services/GeolocationService');
10
+ const MemberBREADService = require('./services/MemberBREADService');
11
+ const MemberRepository = require('./repositories/MemberRepository');
12
+ const EventRepository = require('./repositories/EventRepository');
13
+ const ProductRepository = require('./repositories/ProductRepository');
14
+ const RouterController = require('./controllers/RouterController');
15
+ const MemberController = require('./controllers/MemberController');
16
+ const WellKnownController = require('./controllers/WellKnownController');
17
+
18
+ const {EmailSuppressedEvent} = require('@tryghost/email-suppression-list');
19
+ const DomainEvents = require('@tryghost/domain-events');
20
+
21
+ module.exports = function MembersAPI({
22
+ tokenConfig: {
23
+ issuer,
24
+ privateKey,
25
+ publicKey
26
+ },
27
+ auth: {
28
+ allowSelfSignup = () => true,
29
+ getSigninURL,
30
+ tokenProvider
31
+ },
32
+ mail: {
33
+ transporter,
34
+ getText,
35
+ getHTML,
36
+ getSubject
37
+ },
38
+ models: {
39
+ DonationPaymentEvent,
40
+ EmailRecipient,
41
+ StripeCustomer,
42
+ StripeCustomerSubscription,
43
+ Member,
44
+ MemberNewsletter,
45
+ MemberCancelEvent,
46
+ MemberSubscribeEvent,
47
+ MemberLoginEvent,
48
+ MemberPaidSubscriptionEvent,
49
+ MemberPaymentEvent,
50
+ MemberStatusEvent,
51
+ MemberProductEvent,
52
+ MemberEmailChangeEvent,
53
+ MemberCreatedEvent,
54
+ SubscriptionCreatedEvent,
55
+ MemberLinkClickEvent,
56
+ EmailSpamComplaintEvent,
57
+ Offer,
58
+ OfferRedemption,
59
+ StripeProduct,
60
+ StripePrice,
61
+ Product,
62
+ Settings,
63
+ Comment,
64
+ MemberFeedback
65
+ },
66
+ tiersService,
67
+ stripeAPIService,
68
+ offersAPI,
69
+ labsService,
70
+ newslettersService,
71
+ memberAttributionService,
72
+ emailSuppressionList,
73
+ settingsCache,
74
+ sentry,
75
+ settingsHelpers,
76
+ captchaService
77
+ }) {
78
+ const tokenService = new TokenService({
79
+ privateKey,
80
+ publicKey,
81
+ issuer
82
+ });
83
+
84
+ const productRepository = new ProductRepository({
85
+ Product,
86
+ Settings,
87
+ StripeProduct,
88
+ StripePrice,
89
+ stripeAPIService
90
+ });
91
+
92
+ const memberRepository = new MemberRepository({
93
+ stripeAPIService,
94
+ tokenService,
95
+ newslettersService,
96
+ labsService,
97
+ productRepository,
98
+ Member,
99
+ MemberNewsletter,
100
+ MemberCancelEvent,
101
+ MemberSubscribeEventModel: MemberSubscribeEvent,
102
+ MemberPaidSubscriptionEvent,
103
+ MemberEmailChangeEvent,
104
+ MemberStatusEvent,
105
+ MemberProductEvent,
106
+ OfferRedemption,
107
+ StripeCustomer,
108
+ StripeCustomerSubscription,
109
+ offerRepository: offersAPI.repository
110
+ });
111
+
112
+ const eventRepository = new EventRepository({
113
+ DonationPaymentEvent,
114
+ EmailRecipient,
115
+ MemberSubscribeEvent,
116
+ MemberPaidSubscriptionEvent,
117
+ MemberPaymentEvent,
118
+ MemberStatusEvent,
119
+ MemberLoginEvent,
120
+ MemberCreatedEvent,
121
+ SubscriptionCreatedEvent,
122
+ MemberLinkClickEvent,
123
+ MemberFeedback,
124
+ EmailSpamComplaintEvent,
125
+ Comment,
126
+ labsService,
127
+ memberAttributionService,
128
+ MemberEmailChangeEvent
129
+ });
130
+
131
+ const memberBREADService = new MemberBREADService({
132
+ offersAPI,
133
+ memberRepository,
134
+ emailService: {
135
+ async sendEmailWithMagicLink({email, requestedType}) {
136
+ return sendEmailWithMagicLink({
137
+ email,
138
+ requestedType,
139
+ options: {
140
+ forceEmailType: true
141
+ }
142
+ });
143
+ }
144
+ },
145
+ labsService,
146
+ stripeService: stripeAPIService,
147
+ memberAttributionService,
148
+ emailSuppressionList,
149
+ settingsHelpers
150
+ });
151
+
152
+ const geolocationService = new GeolocationService();
153
+
154
+ const magicLinkService = new MagicLink({
155
+ transporter,
156
+ tokenProvider,
157
+ getSigninURL,
158
+ getText,
159
+ getHTML,
160
+ getSubject,
161
+ sentry
162
+ });
163
+
164
+ const paymentsService = new PaymentsService({
165
+ StripeProduct,
166
+ StripePrice,
167
+ StripeCustomer,
168
+ Offer,
169
+ offersAPI,
170
+ stripeAPIService,
171
+ settingsCache
172
+ });
173
+
174
+ const memberController = new MemberController({
175
+ memberRepository,
176
+ productRepository,
177
+ paymentsService,
178
+ tiersService,
179
+ StripePrice,
180
+ tokenService,
181
+ sendEmailWithMagicLink,
182
+ settingsCache
183
+ });
184
+
185
+ const routerController = new RouterController({
186
+ offersAPI,
187
+ paymentsService,
188
+ tiersService,
189
+ memberRepository,
190
+ StripePrice,
191
+ allowSelfSignup,
192
+ magicLinkService,
193
+ stripeAPIService,
194
+ tokenService,
195
+ sendEmailWithMagicLink,
196
+ memberAttributionService,
197
+ labsService,
198
+ newslettersService,
199
+ settingsCache,
200
+ sentry
201
+ });
202
+
203
+ const wellKnownController = new WellKnownController({
204
+ tokenService
205
+ });
206
+
207
+ const users = memberRepository;
208
+
209
+ async function sendEmailWithMagicLink({email, requestedType, tokenData, options = {forceEmailType: false}, referrer = null}) {
210
+ let type = requestedType;
211
+ if (!options.forceEmailType) {
212
+ const member = await users.get({email});
213
+ if (member) {
214
+ type = 'signin';
215
+ } else if (type !== 'subscribe') {
216
+ type = 'signup';
217
+ }
218
+ }
219
+ return magicLinkService.sendMagicLink({email, type, tokenData: Object.assign({email, type}, tokenData), referrer});
220
+ }
221
+
222
+ /**
223
+ *
224
+ * @param {string} email
225
+ * @param {'signin'|'signup'} type When you specify 'signin' this will prevent the creation of a new member if no member is found with the provided email
226
+ * @param {*} [tokenData] Optional token data to add to the token
227
+ * @returns
228
+ */
229
+ function getMagicLink(email, type, tokenData = {}) {
230
+ return magicLinkService.getMagicLink({
231
+ tokenData: {email, ...tokenData},
232
+ type
233
+ });
234
+ }
235
+
236
+ async function getTokenDataFromMagicLinkToken(token) {
237
+ return await magicLinkService.getDataFromToken(token);
238
+ }
239
+
240
+ async function getMemberDataFromMagicLinkToken(token) {
241
+ const {email, labels = [], name = '', oldEmail, newsletters, attribution, reqIp, type} = await getTokenDataFromMagicLinkToken(token);
242
+ if (!email) {
243
+ return null;
244
+ }
245
+
246
+ const member = oldEmail ? await getMemberIdentityData(oldEmail) : await getMemberIdentityData(email);
247
+
248
+ if (member) {
249
+ await MemberLoginEvent.add({member_id: member.id});
250
+ if (oldEmail && (!type || type === 'updateEmail')) {
251
+ // user exists but wants to change their email address
252
+ await users.update({email}, {id: member.id});
253
+ return getMemberIdentityData(email);
254
+ }
255
+ return member;
256
+ }
257
+
258
+ // Note: old tokens can still have a missing type (we can remove this after a couple of weeks)
259
+ if (type && !['signup', 'subscribe'].includes(type)) {
260
+ // Don't allow sign up
261
+ // Note that we use the type from inside the magic token so this behaviour can't be changed
262
+ return null;
263
+ }
264
+
265
+ let geolocation;
266
+ if (reqIp) {
267
+ try {
268
+ geolocation = JSON.stringify(await geolocationService.getGeolocationFromIP(reqIp));
269
+ } catch (err) {
270
+ logging.warn(err);
271
+ // no-op, we don't want to stop anything working due to
272
+ // geolocation lookup failing
273
+ }
274
+ }
275
+
276
+ const newMember = await users.create({name, email, labels, newsletters, attribution, geolocation});
277
+
278
+ await MemberLoginEvent.add({member_id: newMember.id});
279
+ return getMemberIdentityData(email);
280
+ }
281
+
282
+ async function getMemberIdentityData(email) {
283
+ return memberBREADService.read({email});
284
+ }
285
+
286
+ async function getMemberIdentityDataFromTransientId(transientId) {
287
+ return memberBREADService.read({transient_id: transientId});
288
+ }
289
+
290
+ async function cycleTransientId(memberId) {
291
+ await users.cycleTransientId({id: memberId});
292
+ }
293
+
294
+ async function getMemberIdentityToken(transientId) {
295
+ const member = await getMemberIdentityDataFromTransientId(transientId);
296
+ if (!member) {
297
+ return null;
298
+ }
299
+ return tokenService.encodeIdentityToken({sub: member.email});
300
+ }
301
+
302
+ async function setMemberGeolocationFromIp(email, ip) {
303
+ if (!email || !ip) {
304
+ throw new errors.IncorrectUsageError({
305
+ message: 'setMemberGeolocationFromIp() expects email and ip arguments to be present'
306
+ });
307
+ }
308
+
309
+ // toJSON() is needed here otherwise users.update() will pick methods off
310
+ // the model object rather than data and fail to edit correctly
311
+ const member = (await users.get({email})).toJSON();
312
+
313
+ if (!member) {
314
+ throw new errors.NotFoundError({
315
+ message: `Member with email address ${email} does not exist`
316
+ });
317
+ }
318
+
319
+ // max request time is 500ms so shouldn't slow requests down too much
320
+ let geolocation = JSON.stringify(await geolocationService.getGeolocationFromIP(ip));
321
+ if (geolocation) {
322
+ await users.update({geolocation}, {id: member.id});
323
+ }
324
+
325
+ return getMemberIdentityData(email);
326
+ }
327
+
328
+ const forwardError = fn => async function forwardErrorMw(req, res, next) {
329
+ try {
330
+ await fn(req, res, next);
331
+ } catch (err) {
332
+ next(err);
333
+ }
334
+ };
335
+
336
+ const middleware = {
337
+ sendMagicLink: Router().use(
338
+ body.json(),
339
+ captchaService.getMiddleware(),
340
+ forwardError((req, res) => routerController.sendMagicLink(req, res))
341
+ ),
342
+ createCheckoutSession: Router().use(
343
+ body.json(),
344
+ forwardError((req, res) => routerController.createCheckoutSession(req, res))
345
+ ),
346
+ createCheckoutSetupSession: Router().use(
347
+ body.json(),
348
+ forwardError((req, res) => routerController.createCheckoutSetupSession(req, res))
349
+ ),
350
+ updateEmailAddress: Router().use(
351
+ body.json(),
352
+ forwardError((req, res) => memberController.updateEmailAddress(req, res))
353
+ ),
354
+ updateSubscription: Router({mergeParams: true}).use(
355
+ body.json(),
356
+ forwardError((req, res) => memberController.updateSubscription(req, res))
357
+ ),
358
+ wellKnown: Router()
359
+ .get('/jwks.json',
360
+ (req, res) => wellKnownController.getPublicKeys(req, res)
361
+ )
362
+ };
363
+
364
+ const getPublicConfig = function () {
365
+ return Promise.resolve({
366
+ publicKey,
367
+ issuer
368
+ });
369
+ };
370
+
371
+ const bus = new (require('events').EventEmitter)();
372
+
373
+ bus.emit('ready');
374
+
375
+ DomainEvents.subscribe(EmailSuppressedEvent, async function (event) {
376
+ const member = await memberRepository.get({email: event.data.emailAddress});
377
+ if (!member) {
378
+ return;
379
+ }
380
+ await memberRepository.update({email_disabled: true}, {id: member.id});
381
+ });
382
+
383
+ return {
384
+ middleware,
385
+ getMemberDataFromMagicLinkToken,
386
+ getMemberIdentityToken,
387
+ getMemberIdentityDataFromTransientId,
388
+ getMemberIdentityData,
389
+ cycleTransientId,
390
+ setMemberGeolocationFromIp,
391
+ getPublicConfig,
392
+ bus,
393
+ sendEmailWithMagicLink,
394
+ getMagicLink,
395
+ members: users,
396
+ memberBREADService,
397
+ events: eventRepository,
398
+ productRepository,
399
+
400
+ // Test helpers
401
+ getTokenDataFromMagicLinkToken,
402
+ paymentsService
403
+ };
404
+ };