payload-subscribers-plugin 0.0.1 → 0.0.4

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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +2 -3
  3. package/dist/components/BeforeDashboardClient.d.ts +0 -1
  4. package/dist/components/BeforeDashboardClient.js +0 -40
  5. package/dist/components/BeforeDashboardClient.js.map +0 -1
  6. package/dist/components/BeforeDashboardServer.d.ts +0 -2
  7. package/dist/components/BeforeDashboardServer.js +0 -22
  8. package/dist/components/BeforeDashboardServer.js.map +0 -1
  9. package/dist/components/BeforeDashboardServer.module.css +0 -5
  10. package/dist/components/app/RequestMagicLink.module.css +0 -5
  11. package/dist/components/app/SelectOptInChannels.module.css +0 -5
  12. package/dist/components/app/Subscribe.module.css +0 -5
  13. package/dist/components/app/VerifyMagicLink.module.css +0 -5
  14. package/dist/copied/payload.config.d.ts +0 -2
  15. package/dist/endpoints/customEndpointHandler.d.ts +0 -2
  16. package/dist/endpoints/customEndpointHandler.js +0 -7
  17. package/dist/endpoints/customEndpointHandler.js.map +0 -1
  18. package/dist/exports/client.d.ts +0 -1
  19. package/dist/exports/client.js +0 -3
  20. package/dist/exports/client.js.map +0 -1
  21. package/dist/exports/rsc.d.ts +0 -1
  22. package/dist/exports/rsc.js +0 -3
  23. package/dist/exports/rsc.js.map +0 -1
  24. package/dist/helpers/serverConfig.d.ts +0 -4
  25. package/dist/helpers/serverConfig.js +0 -22
  26. package/dist/helpers/serverConfig.js.map +0 -1
  27. package/dist/server-functions/subscriberAuth.d.ts +0 -11
  28. package/src/collections/OptInChannels.ts +0 -45
  29. package/src/collections/Subscribers.ts +0 -99
  30. package/src/collections/fields/OptedInChannels.ts +0 -12
  31. package/src/components/app/RequestMagicLink.tsx +0 -129
  32. package/src/components/app/RequestOrSubscribe.tsx +0 -58
  33. package/src/components/app/SelectOptInChannels.tsx +0 -147
  34. package/src/components/app/Subscribe.tsx +0 -190
  35. package/src/components/app/SubscriberMenu.tsx +0 -46
  36. package/src/components/app/VerifyMagicLink.tsx +0 -197
  37. package/src/components/app/helpers.ts +0 -6
  38. package/src/components/app/shared.module.css +0 -14
  39. package/src/contexts/SubscriberProvider.tsx +0 -122
  40. package/src/copied/payload-types.ts +0 -478
  41. package/src/endpoints/getOptInChannels.ts +0 -56
  42. package/src/endpoints/logout.ts +0 -104
  43. package/src/endpoints/requestMagicLink.ts +0 -139
  44. package/src/endpoints/subscribe.ts +0 -435
  45. package/src/endpoints/subscriberAuth.ts +0 -100
  46. package/src/endpoints/verifyMagicLink.ts +0 -164
  47. package/src/exports/index.ts +0 -1
  48. package/src/exports/ui.ts +0 -17
  49. package/src/helpers/testData.ts +0 -2
  50. package/src/helpers/token.ts +0 -14
  51. package/src/helpers/verifyOptIns.ts +0 -39
  52. package/src/index.ts +0 -207
  53. package/src/react-hooks/useServerUrl.tsx +0 -18
  54. package/src/server-functions/serverUrl.ts +0 -38
@@ -1,104 +0,0 @@
1
- import type { CollectionSlug, Endpoint, PayloadHandler } from 'payload'
2
-
3
- import { headers as nextHeaders } from 'next/headers.js'
4
-
5
- import { defaultCollectionSlug } from '../collections/Subscribers.js'
6
-
7
- export type LogoutResponse =
8
- | {
9
- error: string
10
- now: string
11
- }
12
- | {
13
- message: string
14
- now: string
15
- }
16
-
17
- /**
18
- * createEndpointLogout
19
- * @param options
20
- * @returns
21
- *
22
- * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug
23
- *
24
- */
25
- function createEndpointLogout({
26
- subscribersCollectionSlug = defaultCollectionSlug,
27
- }: {
28
- subscribersCollectionSlug: CollectionSlug
29
- }): Endpoint {
30
- const logoutHandler: PayloadHandler = async (req) => {
31
- const headers = await nextHeaders()
32
-
33
- try {
34
- const logoutResult = await fetch(
35
- `${req.payload.config.serverURL}/api/${subscribersCollectionSlug}/logout`,
36
- {
37
- headers,
38
- method: 'POST',
39
- },
40
- )
41
-
42
- const logoutResultData = await logoutResult.json()
43
-
44
- if (logoutResult.ok) {
45
- return Response.json({
46
- message: logoutResultData.message,
47
- now: new Date().toISOString(),
48
- } as LogoutResponse)
49
- }
50
-
51
- if (
52
- logoutResult.status == 400 &&
53
- logoutResultData.errors?.map((e: { message: string }) => e.message).includes('No User')
54
- ) {
55
- return Response.json(
56
- {
57
- error: `Logout failed: 'No User'`,
58
- now: new Date().toISOString(),
59
- } as LogoutResponse,
60
- {
61
- status: 400,
62
- },
63
- )
64
- }
65
- return Response.json(
66
- {
67
- error: `Logout failed: ${
68
- logoutResultData.errors
69
- ? logoutResultData.errors?.map((e: { message: string }) => e.message).join(' // ')
70
- : JSON.stringify(logoutResultData)
71
- }`,
72
- now: new Date().toISOString(),
73
- } as LogoutResponse,
74
- {
75
- status: 400,
76
- },
77
- )
78
- } catch (error) {
79
- // throw new Error(`Logout failed: ${error instanceof Error ? error.message : 'Unknown error'}`)
80
- return Response.json(
81
- {
82
- error: `Logout failed: ${JSON.stringify(error)}`,
83
- now: new Date().toISOString(),
84
- } as LogoutResponse,
85
- {
86
- status: 400,
87
- },
88
- )
89
- }
90
- }
91
-
92
- /**
93
- * logout Endpoint Config
94
- */
95
- const logoutEndpoint: Endpoint = {
96
- handler: logoutHandler,
97
- method: 'post',
98
- path: '/logout',
99
- }
100
-
101
- return logoutEndpoint
102
- }
103
-
104
- export default createEndpointLogout
@@ -1,139 +0,0 @@
1
- import type { CollectionSlug, Endpoint, PayloadHandler, PayloadRequest, TypedUser } from 'payload'
2
-
3
- import crypto from 'crypto'
4
- import { defaultCollectionSlug } from '../collections/Subscribers.js'
5
-
6
- import { getTokenAndHash } from '../helpers/token.js'
7
-
8
- export type RequestMagicLinkResponse =
9
- | {
10
- emailResult: any
11
- now: string
12
- }
13
- | {
14
- error: string
15
- now: string
16
- }
17
-
18
- /**
19
- * createEndpointRequestMagicLink
20
- * @param options
21
- * @returns
22
- *
23
- * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug
24
- *
25
- */
26
- function createEndpointRequestMagicLink({
27
- subscribersCollectionSlug = defaultCollectionSlug,
28
- }: {
29
- subscribersCollectionSlug: CollectionSlug
30
- }): Endpoint {
31
- /**
32
- * requestMagicLink Endpoint Handler
33
- * @param req
34
- * @data { email }
35
- * @returns { status: 200, json: {message: string, now: date} }
36
- * @returns { status: 400, json: {error: ('Bad data' | 'Unknown email result'), now: date} }
37
- */
38
- const requestMagicLinkHandler: PayloadHandler = async (req: PayloadRequest) => {
39
- const data = req?.json ? await req.json() : {}
40
- const { email, forwardUrl } = data // if by POST data
41
- // const { email } = req.routeParams // if by path
42
-
43
- if (!email) {
44
- return Response.json(
45
- { error: 'Bad data', now: new Date().toISOString() } as RequestMagicLinkResponse,
46
- { status: 400 },
47
- )
48
- }
49
-
50
- const userResults = await req.payload.find({
51
- collection: subscribersCollectionSlug,
52
- where: {
53
- email: { equals: email },
54
- },
55
- })
56
- const user = userResults.docs[0] as TypedUser
57
-
58
- if (!user) {
59
- //
60
- // Create subscriber with status 'pending',
61
- // and an invisible unknowable password,
62
- //
63
- const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable
64
- const createResult = await req.payload.create({
65
- collection: subscribersCollectionSlug,
66
- data: {
67
- email,
68
- password: tokenHash2,
69
- status: 'pending',
70
- },
71
- draft: false,
72
- })
73
- if (!createResult) {
74
- return Response.json(
75
- { error: 'Bad data', now: new Date().toISOString() } as RequestMagicLinkResponse,
76
- { status: 400 },
77
- )
78
- }
79
- }
80
-
81
- // Update user with verificationToken
82
- const token = crypto.randomBytes(32).toString('hex')
83
- const tokenHash = crypto.createHash('sha256').update(token).digest('hex')
84
- const expiresAt = new Date(Date.now() + 15 * 60 * 1000) // 15 mins
85
- await req.payload.update({
86
- collection: subscribersCollectionSlug,
87
- data: {
88
- verificationToken: tokenHash,
89
- verificationTokenExpires: expiresAt.toISOString(),
90
- },
91
- where: {
92
- email: { equals: user.email },
93
- },
94
- })
95
-
96
- // Send email
97
- const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : ''
98
- const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`
99
- const subject = data.subject || 'Your Magic Login Link'
100
- const message = `
101
- ${data.message || '<p>Use this link to log in:</p>'}
102
- <p><a href="${magicLink}"><b>Login</b></a></p>
103
- `
104
- const emailResult = await req.payload.sendEmail({
105
- html: message,
106
- subject,
107
- to: user.email,
108
- })
109
- // req.payload.logger.info(`email result: ${JSON.stringify(emailResult)}`)
110
- // return data; // Return data to allow normal submission if needed
111
- if (!emailResult) {
112
- return Response.json(
113
- {
114
- error: 'Unknown email result',
115
- now: new Date().toISOString(),
116
- } as RequestMagicLinkResponse,
117
- { status: 400 },
118
- )
119
- }
120
- req.payload.logger.info(`requestMagicLinkHandler email sent \n ${magicLink}`)
121
- return Response.json({
122
- emailResult,
123
- now: new Date().toISOString(),
124
- } as RequestMagicLinkResponse)
125
- }
126
-
127
- /**
128
- * requestMagicLink Endpoint Config
129
- */
130
- const requestMagicLinkEndpoint: Endpoint = {
131
- handler: requestMagicLinkHandler,
132
- method: 'post',
133
- path: '/emailToken',
134
- }
135
-
136
- return requestMagicLinkEndpoint
137
- }
138
-
139
- export default createEndpointRequestMagicLink
@@ -1,435 +0,0 @@
1
- import type { CollectionSlug, Endpoint, PayloadHandler } from 'payload'
2
- import type { Subscriber } from 'src/copied/payload-types.js'
3
-
4
- import { defaultCollectionSlug } from '../collections/Subscribers.js'
5
-
6
- import { getTokenAndHash } from '../helpers/token.js'
7
- import { verifyOptIns } from '../helpers/verifyOptIns.js'
8
-
9
- export type SubscribeResponse =
10
- // When subscriber optIns are updated...
11
- | {
12
- email: string
13
- now: string
14
- optIns: string[]
15
- }
16
- // When a verify link is emailed...
17
- | {
18
- emailResult: any
19
- now: string
20
- }
21
- // When any error occurs...
22
- | {
23
- error: string
24
- now: string
25
- }
26
-
27
- /**
28
- * createEndpointLogout
29
- * @param options
30
- * @returns
31
- *
32
- * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug
33
- *
34
- */
35
- function createEndpointSubscribe({
36
- subscribersCollectionSlug = defaultCollectionSlug,
37
- }: {
38
- subscribersCollectionSlug: CollectionSlug
39
- }): Endpoint {
40
- /**
41
- * subscribe Endpoint Handler
42
- * @param req
43
- * @data { email }
44
- * @returns { status: 200, json: {message: string, now: date} }
45
- * @returns { status: 400, json: {error: ('Bad data' | 'Already subscribed' | 'Unknown email result'), now: date} }
46
- */
47
- const subscribeHandler: PayloadHandler = async (req) => {
48
- const data = req?.json ? await req.json() : {}
49
- const {
50
- afterVerifyUrl,
51
- email,
52
- optIns,
53
- }: { afterVerifyUrl: string; email: string; optIns: string[] } = data // if by POST data
54
- // const { email } = req.routeParams // if by path
55
-
56
- //
57
- // HELPERS
58
- // Some of these functions make use of the scope within handler,
59
- // and would have to be refactored if moved out.
60
- //
61
- const createSubscriber = async ({
62
- optIns,
63
- password,
64
- status,
65
- verificationToken,
66
- verificationTokenExpires,
67
- }: {
68
- email: string
69
- optIns?: string[]
70
- password?: string
71
- status?: 'pending' | 'subscribed' | 'unsubscribed'
72
- verificationToken?: string
73
- verificationTokenExpires?: Date
74
- }) => {
75
- await req.payload.create({
76
- collection: subscribersCollectionSlug,
77
- data: {
78
- email,
79
- optIns,
80
- password,
81
- status: status || 'pending',
82
- verificationToken,
83
- verificationTokenExpires: verificationTokenExpires?.toISOString(),
84
- },
85
- draft: false,
86
- })
87
- }
88
- const updateSubscriber = async ({
89
- id,
90
- optIns,
91
- password,
92
- status,
93
- verificationToken,
94
- verificationTokenExpires,
95
- }: {
96
- id: string
97
- optIns?: string[]
98
- password?: string
99
- status?: 'pending' | 'subscribed' | 'unsubscribed'
100
- verificationToken?: string
101
- verificationTokenExpires?: Date | null
102
- }) => {
103
- const updateResults = await req.payload.update({
104
- id,
105
- collection: subscribersCollectionSlug,
106
- data: {
107
- optIns,
108
- password,
109
- status,
110
- verificationToken,
111
- verificationTokenExpires: verificationTokenExpires?.toISOString() || null,
112
- },
113
- depth: 0,
114
- })
115
- return updateResults
116
- }
117
- const sendVerifyEmail = async ({
118
- email,
119
- forwardUrl,
120
- linkText,
121
- message,
122
- subject,
123
- token,
124
- }: {
125
- email: string
126
- forwardUrl?: string
127
- linkText: string
128
- message: string
129
- subject: string
130
- token: string
131
- }) => {
132
- const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : ''
133
- const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`
134
- const html = message + `<p><a href="${magicLink}">${linkText}</a></p>`
135
- const emailResult = await req.payload.sendEmail({
136
- html,
137
- subject,
138
- to: email,
139
- })
140
- req.payload.logger.info(`subscribe email sent \n ${magicLink}`)
141
- return emailResult
142
- }
143
-
144
- //
145
- // VALIDATE INPUT
146
- //
147
- // Require email
148
- if (!email) {
149
- req.payload.logger.error(
150
- JSON.stringify(
151
- { error: 'Bad data', now: new Date().toISOString() } as SubscribeResponse,
152
- undefined,
153
- 2,
154
- ),
155
- )
156
- return Response.json(
157
- { error: 'Bad data', now: new Date().toISOString() } as SubscribeResponse,
158
- { status: 400 },
159
- )
160
- }
161
-
162
- //
163
- // Validate OptInChannels
164
- const { invalidOptInsInput, verifiedOptInIDs } = await verifyOptIns(req.payload, optIns)
165
-
166
- if (invalidOptInsInput) {
167
- req.payload.logger.error(
168
- JSON.stringify(
169
- {
170
- error: 'Invalid input: ' + JSON.stringify(optIns),
171
- now: new Date().toISOString(),
172
- } as SubscribeResponse,
173
- undefined,
174
- 2,
175
- ),
176
- )
177
- return Response.json(
178
- {
179
- error: 'Invalid input: ' + JSON.stringify(optIns),
180
- now: new Date().toISOString(),
181
- } as SubscribeResponse,
182
- { status: 400 },
183
- )
184
- }
185
-
186
- //
187
- // Verify subscriber exists
188
- const userResults = await req.payload.find({
189
- collection: subscribersCollectionSlug,
190
- where: {
191
- email: { equals: email },
192
- },
193
- })
194
- const subscriber = userResults.docs[0] as Subscriber
195
-
196
- //
197
- // Now we have a subscriber and validatedOptIns
198
- // Handle scenarios
199
- //
200
- // ********************************************************
201
- //
202
- if (req.user && req.user.email != email) {
203
- //
204
- // Error: Auth-ed user doesn't match subscriber email
205
- //
206
- req.payload.logger.error(
207
- JSON.stringify(
208
- {
209
- error: 'Unauthorized: ' + email,
210
- now: new Date().toISOString(),
211
- } as SubscribeResponse,
212
- undefined,
213
- 2,
214
- ),
215
- )
216
- return Response.json(
217
- {
218
- error: 'Unauthorized: ' + email,
219
- now: new Date().toISOString(),
220
- } as SubscribeResponse,
221
- { status: 400 },
222
- )
223
- }
224
-
225
- //
226
- // ********************************************************
227
- //
228
- if (!subscriber) {
229
- //
230
- // Create subscriber with status 'pending',
231
- // and an invisible unknowable password,
232
- // and send a verify email
233
- // Pass all optIns through verify link
234
- //
235
- const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link
236
- const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable
237
- await createSubscriber({
238
- email,
239
- optIns,
240
- password: tokenHash2,
241
- status: 'pending',
242
- verificationToken: tokenHash,
243
- verificationTokenExpires: expiresAt,
244
- })
245
-
246
- //
247
- // Send email
248
- const emailResult = await sendVerifyEmail({
249
- email,
250
- forwardUrl: afterVerifyUrl,
251
- linkText: '<b>Verify</b>',
252
- message: data.message || `<p>Click here to verify your subscription:</p>`,
253
- subject: data.subject || 'Please verify your subscription',
254
- token,
255
- })
256
- if (!emailResult) {
257
- req.payload.logger.error(
258
- JSON.stringify(
259
- { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,
260
- undefined,
261
- 2,
262
- ),
263
- )
264
- return Response.json(
265
- { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,
266
- { status: 400 },
267
- )
268
- }
269
- return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)
270
- //
271
- }
272
- //
273
- // ********************************************************
274
- //
275
- if (!req.user && subscriber) {
276
- //
277
- // Send magic link to log the user in
278
- // Pass all optIns through verify link
279
- //
280
- const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link
281
- // Update subscriber with token for pending email
282
- const updateResults = await updateSubscriber({
283
- id: subscriber.id,
284
- verificationToken: tokenHash,
285
- verificationTokenExpires: expiresAt,
286
- })
287
- if (!updateResults) {
288
- req.payload.logger.error(
289
- JSON.stringify(
290
- { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,
291
- undefined,
292
- 2,
293
- ),
294
- )
295
- return Response.json(
296
- { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,
297
- { status: 400 },
298
- )
299
- }
300
-
301
- //
302
- // Send email
303
- const emailResult = await sendVerifyEmail({
304
- email,
305
- forwardUrl: afterVerifyUrl,
306
- linkText: 'Verify',
307
- message: data.message || `<h1>Click here to verify your subscription:</h1>`,
308
- subject: data.subject || 'Please verify your subscription',
309
- token,
310
- })
311
- if (!emailResult) {
312
- req.payload.logger.error(
313
- JSON.stringify(
314
- { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,
315
- undefined,
316
- 2,
317
- ),
318
- )
319
- return Response.json(
320
- { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,
321
- { status: 400 },
322
- )
323
- }
324
- return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)
325
- }
326
- //
327
- // ********************************************************
328
- //
329
- if (req.user && subscriber && subscriber.status == 'pending') {
330
- //
331
- // Send magic link to verify the email and log the user in
332
- // Pass all optIns through verify link
333
- //
334
- const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link
335
- // Create subscriber with token for pending email
336
- const updateResults = await updateSubscriber({
337
- id: subscriber.id,
338
- verificationToken: tokenHash,
339
- verificationTokenExpires: expiresAt,
340
- })
341
- if (!updateResults) {
342
- req.payload.logger.error(
343
- JSON.stringify(
344
- { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,
345
- undefined,
346
- 2,
347
- ),
348
- )
349
- return Response.json(
350
- { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,
351
- { status: 400 },
352
- )
353
- }
354
-
355
- const emailResult = await sendVerifyEmail({
356
- email,
357
- forwardUrl: afterVerifyUrl,
358
- linkText: 'Verify',
359
- message: data.message || `<h1>Click here to verify your email:</h1>`,
360
- subject: data.subject || 'Please verify your subscription',
361
- token,
362
- })
363
- if (!emailResult) {
364
- req.payload.logger.error(
365
- JSON.stringify(
366
- { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,
367
- undefined,
368
- 2,
369
- ),
370
- )
371
- return Response.json(
372
- { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,
373
- { status: 400 },
374
- )
375
- }
376
- return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)
377
- }
378
-
379
- //
380
- // ********************************************************
381
- //
382
- if (req.user && subscriber && subscriber.status != 'pending') {
383
- //
384
- // Update subscriber with status 'subscribed',
385
- // an invisible unknowable password,
386
- // and if any optIns input exists, set subscriber optIns
387
- // to EXACTLY verifiedOptInIDs (potentially unsubscribing from any not in verifiedOptInIDs)
388
- //
389
- const { tokenHash } = getTokenAndHash() // Use for magic link
390
- // Update subscriber with optIns
391
- const updateResults = (await updateSubscriber({
392
- id: subscriber.id,
393
- optIns: verifiedOptInIDs,
394
- password: tokenHash,
395
- status: 'subscribed',
396
- verificationToken: '',
397
- verificationTokenExpires: null,
398
- })) as Subscriber
399
-
400
- // Return results, including the verified optIns
401
- return Response.json({
402
- email: updateResults.email,
403
- now: new Date().toISOString(),
404
- optIns: updateResults.optIns,
405
- } as SubscribeResponse)
406
- }
407
- //
408
- // Uncaught case
409
- //
410
- req.payload.logger.error(
411
- JSON.stringify(
412
- { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,
413
- undefined,
414
- 2,
415
- ),
416
- )
417
- return Response.json(
418
- { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,
419
- { status: 400 },
420
- )
421
- }
422
-
423
- /**
424
- * subscribe Endpoint Config
425
- */
426
- const subscribeEndpoint: Endpoint = {
427
- handler: subscribeHandler,
428
- method: 'post',
429
- path: '/subscribe',
430
- }
431
-
432
- return subscribeEndpoint
433
- }
434
-
435
- export default createEndpointSubscribe