payload-subscribers-plugin 0.0.1

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 (133) hide show
  1. package/README.md +305 -0
  2. package/dist/collections/OptInChannels.d.ts +3 -0
  3. package/dist/collections/OptInChannels.js +44 -0
  4. package/dist/collections/OptInChannels.js.map +1 -0
  5. package/dist/collections/Subscribers.d.ts +8 -0
  6. package/dist/collections/Subscribers.js +88 -0
  7. package/dist/collections/Subscribers.js.map +1 -0
  8. package/dist/collections/fields/OptedInChannels.d.ts +2 -0
  9. package/dist/collections/fields/OptedInChannels.js +12 -0
  10. package/dist/collections/fields/OptedInChannels.js.map +1 -0
  11. package/dist/components/BeforeDashboardClient.d.ts +1 -0
  12. package/dist/components/BeforeDashboardClient.js +40 -0
  13. package/dist/components/BeforeDashboardClient.js.map +1 -0
  14. package/dist/components/BeforeDashboardServer.d.ts +2 -0
  15. package/dist/components/BeforeDashboardServer.js +22 -0
  16. package/dist/components/BeforeDashboardServer.js.map +1 -0
  17. package/dist/components/BeforeDashboardServer.module.css +5 -0
  18. package/dist/components/app/RequestMagicLink.d.ts +16 -0
  19. package/dist/components/app/RequestMagicLink.js +114 -0
  20. package/dist/components/app/RequestMagicLink.js.map +1 -0
  21. package/dist/components/app/RequestMagicLink.module.css +5 -0
  22. package/dist/components/app/RequestOrSubscribe.d.ts +17 -0
  23. package/dist/components/app/RequestOrSubscribe.js +28 -0
  24. package/dist/components/app/RequestOrSubscribe.js.map +1 -0
  25. package/dist/components/app/SelectOptInChannels.d.ts +20 -0
  26. package/dist/components/app/SelectOptInChannels.js +120 -0
  27. package/dist/components/app/SelectOptInChannels.js.map +1 -0
  28. package/dist/components/app/SelectOptInChannels.module.css +5 -0
  29. package/dist/components/app/Subscribe.d.ts +18 -0
  30. package/dist/components/app/Subscribe.js +169 -0
  31. package/dist/components/app/Subscribe.js.map +1 -0
  32. package/dist/components/app/Subscribe.module.css +5 -0
  33. package/dist/components/app/SubscriberMenu.d.ts +7 -0
  34. package/dist/components/app/SubscriberMenu.js +44 -0
  35. package/dist/components/app/SubscriberMenu.js.map +1 -0
  36. package/dist/components/app/VerifyMagicLink.d.ts +23 -0
  37. package/dist/components/app/VerifyMagicLink.js +169 -0
  38. package/dist/components/app/VerifyMagicLink.js.map +1 -0
  39. package/dist/components/app/VerifyMagicLink.module.css +5 -0
  40. package/dist/components/app/helpers.d.ts +1 -0
  41. package/dist/components/app/helpers.js +5 -0
  42. package/dist/components/app/helpers.js.map +1 -0
  43. package/dist/components/app/shared.module.css +14 -0
  44. package/dist/contexts/SubscriberProvider.d.ts +15 -0
  45. package/dist/contexts/SubscriberProvider.js +105 -0
  46. package/dist/contexts/SubscriberProvider.js.map +1 -0
  47. package/dist/copied/payload-types.d.ts +395 -0
  48. package/dist/copied/payload-types.js +15 -0
  49. package/dist/copied/payload-types.js.map +1 -0
  50. package/dist/copied/payload.config.d.ts +2 -0
  51. package/dist/endpoints/customEndpointHandler.d.ts +2 -0
  52. package/dist/endpoints/customEndpointHandler.js +7 -0
  53. package/dist/endpoints/customEndpointHandler.js.map +1 -0
  54. package/dist/endpoints/getOptInChannels.d.ts +19 -0
  55. package/dist/endpoints/getOptInChannels.js +42 -0
  56. package/dist/endpoints/getOptInChannels.js.map +1 -0
  57. package/dist/endpoints/logout.d.ts +20 -0
  58. package/dist/endpoints/logout.js +60 -0
  59. package/dist/endpoints/logout.js.map +1 -0
  60. package/dist/endpoints/requestMagicLink.d.ts +20 -0
  61. package/dist/endpoints/requestMagicLink.js +122 -0
  62. package/dist/endpoints/requestMagicLink.js.map +1 -0
  63. package/dist/endpoints/subscribe.d.ts +24 -0
  64. package/dist/endpoints/subscribe.js +343 -0
  65. package/dist/endpoints/subscribe.js.map +1 -0
  66. package/dist/endpoints/subscriberAuth.d.ts +22 -0
  67. package/dist/endpoints/subscriberAuth.js +69 -0
  68. package/dist/endpoints/subscriberAuth.js.map +1 -0
  69. package/dist/endpoints/verifyMagicLink.d.ts +20 -0
  70. package/dist/endpoints/verifyMagicLink.js +142 -0
  71. package/dist/endpoints/verifyMagicLink.js.map +1 -0
  72. package/dist/exports/client.d.ts +1 -0
  73. package/dist/exports/client.js +3 -0
  74. package/dist/exports/client.js.map +1 -0
  75. package/dist/exports/index.d.ts +1 -0
  76. package/dist/exports/index.js +3 -0
  77. package/dist/exports/index.js.map +1 -0
  78. package/dist/exports/rsc.d.ts +1 -0
  79. package/dist/exports/rsc.js +3 -0
  80. package/dist/exports/rsc.js.map +1 -0
  81. package/dist/exports/ui.d.ts +11 -0
  82. package/dist/exports/ui.js +9 -0
  83. package/dist/exports/ui.js.map +1 -0
  84. package/dist/helpers/serverConfig.d.ts +4 -0
  85. package/dist/helpers/serverConfig.js +22 -0
  86. package/dist/helpers/serverConfig.js.map +1 -0
  87. package/dist/helpers/testData.d.ts +2 -0
  88. package/dist/helpers/testData.js +4 -0
  89. package/dist/helpers/testData.js.map +1 -0
  90. package/dist/helpers/token.d.ts +9 -0
  91. package/dist/helpers/token.js +20 -0
  92. package/dist/helpers/token.js.map +1 -0
  93. package/dist/helpers/verifyOptIns.d.ts +5 -0
  94. package/dist/helpers/verifyOptIns.js +33 -0
  95. package/dist/helpers/verifyOptIns.js.map +1 -0
  96. package/dist/index.d.ts +26 -0
  97. package/dist/index.js +147 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/react-hooks/useServerUrl.d.ts +3 -0
  100. package/dist/react-hooks/useServerUrl.js +19 -0
  101. package/dist/react-hooks/useServerUrl.js.map +1 -0
  102. package/dist/server-functions/serverUrl.d.ts +3 -0
  103. package/dist/server-functions/serverUrl.js +31 -0
  104. package/dist/server-functions/serverUrl.js.map +1 -0
  105. package/dist/server-functions/subscriberAuth.d.ts +11 -0
  106. package/package.json +94 -0
  107. package/src/collections/OptInChannels.ts +45 -0
  108. package/src/collections/Subscribers.ts +99 -0
  109. package/src/collections/fields/OptedInChannels.ts +12 -0
  110. package/src/components/app/RequestMagicLink.tsx +129 -0
  111. package/src/components/app/RequestOrSubscribe.tsx +58 -0
  112. package/src/components/app/SelectOptInChannels.tsx +147 -0
  113. package/src/components/app/Subscribe.tsx +190 -0
  114. package/src/components/app/SubscriberMenu.tsx +46 -0
  115. package/src/components/app/VerifyMagicLink.tsx +197 -0
  116. package/src/components/app/helpers.ts +6 -0
  117. package/src/components/app/shared.module.css +14 -0
  118. package/src/contexts/SubscriberProvider.tsx +122 -0
  119. package/src/copied/payload-types.ts +478 -0
  120. package/src/endpoints/getOptInChannels.ts +56 -0
  121. package/src/endpoints/logout.ts +104 -0
  122. package/src/endpoints/requestMagicLink.ts +139 -0
  123. package/src/endpoints/subscribe.ts +435 -0
  124. package/src/endpoints/subscriberAuth.ts +100 -0
  125. package/src/endpoints/verifyMagicLink.ts +164 -0
  126. package/src/exports/index.ts +1 -0
  127. package/src/exports/ui.ts +17 -0
  128. package/src/helpers/testData.ts +2 -0
  129. package/src/helpers/token.ts +14 -0
  130. package/src/helpers/verifyOptIns.ts +39 -0
  131. package/src/index.ts +207 -0
  132. package/src/react-hooks/useServerUrl.tsx +18 -0
  133. package/src/server-functions/serverUrl.ts +38 -0
@@ -0,0 +1,122 @@
1
+ import crypto from 'crypto';
2
+ import { defaultCollectionSlug } from '../collections/Subscribers.js';
3
+ import { getTokenAndHash } from '../helpers/token.js';
4
+ /**
5
+ * createEndpointRequestMagicLink
6
+ * @param options
7
+ * @returns
8
+ *
9
+ * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug
10
+ *
11
+ */ function createEndpointRequestMagicLink({ subscribersCollectionSlug = defaultCollectionSlug }) {
12
+ /**
13
+ * requestMagicLink Endpoint Handler
14
+ * @param req
15
+ * @data { email }
16
+ * @returns { status: 200, json: {message: string, now: date} }
17
+ * @returns { status: 400, json: {error: ('Bad data' | 'Unknown email result'), now: date} }
18
+ */ const requestMagicLinkHandler = async (req)=>{
19
+ const data = req?.json ? await req.json() : {};
20
+ const { email, forwardUrl } = data // if by POST data
21
+ ;
22
+ // const { email } = req.routeParams // if by path
23
+ if (!email) {
24
+ return Response.json({
25
+ error: 'Bad data',
26
+ now: new Date().toISOString()
27
+ }, {
28
+ status: 400
29
+ });
30
+ }
31
+ const userResults = await req.payload.find({
32
+ collection: subscribersCollectionSlug,
33
+ where: {
34
+ email: {
35
+ equals: email
36
+ }
37
+ }
38
+ });
39
+ const user = userResults.docs[0];
40
+ if (!user) {
41
+ //
42
+ // Create subscriber with status 'pending',
43
+ // and an invisible unknowable password,
44
+ //
45
+ const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable
46
+ ;
47
+ const createResult = await req.payload.create({
48
+ collection: subscribersCollectionSlug,
49
+ data: {
50
+ email,
51
+ password: tokenHash2,
52
+ status: 'pending'
53
+ },
54
+ draft: false
55
+ });
56
+ if (!createResult) {
57
+ return Response.json({
58
+ error: 'Bad data',
59
+ now: new Date().toISOString()
60
+ }, {
61
+ status: 400
62
+ });
63
+ }
64
+ }
65
+ // Update user with verificationToken
66
+ const token = crypto.randomBytes(32).toString('hex');
67
+ const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
68
+ const expiresAt = new Date(Date.now() + 15 * 60 * 1000) // 15 mins
69
+ ;
70
+ await req.payload.update({
71
+ collection: subscribersCollectionSlug,
72
+ data: {
73
+ verificationToken: tokenHash,
74
+ verificationTokenExpires: expiresAt.toISOString()
75
+ },
76
+ where: {
77
+ email: {
78
+ equals: user.email
79
+ }
80
+ }
81
+ });
82
+ // Send email
83
+ const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : '';
84
+ const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`;
85
+ const subject = data.subject || 'Your Magic Login Link';
86
+ const message = `
87
+ ${data.message || '<p>Use this link to log in:</p>'}
88
+ <p><a href="${magicLink}"><b>Login</b></a></p>
89
+ `;
90
+ const emailResult = await req.payload.sendEmail({
91
+ html: message,
92
+ subject,
93
+ to: user.email
94
+ });
95
+ // req.payload.logger.info(`email result: ${JSON.stringify(emailResult)}`)
96
+ // return data; // Return data to allow normal submission if needed
97
+ if (!emailResult) {
98
+ return Response.json({
99
+ error: 'Unknown email result',
100
+ now: new Date().toISOString()
101
+ }, {
102
+ status: 400
103
+ });
104
+ }
105
+ req.payload.logger.info(`requestMagicLinkHandler email sent \n ${magicLink}`);
106
+ return Response.json({
107
+ emailResult,
108
+ now: new Date().toISOString()
109
+ });
110
+ };
111
+ /**
112
+ * requestMagicLink Endpoint Config
113
+ */ const requestMagicLinkEndpoint = {
114
+ handler: requestMagicLinkHandler,
115
+ method: 'post',
116
+ path: '/emailToken'
117
+ };
118
+ return requestMagicLinkEndpoint;
119
+ }
120
+ export default createEndpointRequestMagicLink;
121
+
122
+ //# sourceMappingURL=requestMagicLink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/requestMagicLink.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint, PayloadHandler, PayloadRequest, TypedUser } from 'payload'\n\nimport crypto from 'crypto'\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\n\nimport { getTokenAndHash } from '../helpers/token.js'\n\nexport type RequestMagicLinkResponse =\n | {\n emailResult: any\n now: string\n }\n | {\n error: string\n now: string\n }\n\n/**\n * createEndpointRequestMagicLink\n * @param options\n * @returns\n *\n * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug\n *\n */\nfunction createEndpointRequestMagicLink({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * requestMagicLink Endpoint Handler\n * @param req\n * @data { email }\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('Bad data' | 'Unknown email result'), now: date} }\n */\n const requestMagicLinkHandler: PayloadHandler = async (req: PayloadRequest) => {\n const data = req?.json ? await req.json() : {}\n const { email, forwardUrl } = data // if by POST data\n // const { email } = req.routeParams // if by path\n\n if (!email) {\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as RequestMagicLinkResponse,\n { status: 400 },\n )\n }\n\n const userResults = await req.payload.find({\n collection: subscribersCollectionSlug,\n where: {\n email: { equals: email },\n },\n })\n const user = userResults.docs[0] as TypedUser\n\n if (!user) {\n //\n // Create subscriber with status 'pending',\n // and an invisible unknowable password,\n //\n const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable\n const createResult = await req.payload.create({\n collection: subscribersCollectionSlug,\n data: {\n email,\n password: tokenHash2,\n status: 'pending',\n },\n draft: false,\n })\n if (!createResult) {\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as RequestMagicLinkResponse,\n { status: 400 },\n )\n }\n }\n\n // Update user with verificationToken\n const token = crypto.randomBytes(32).toString('hex')\n const tokenHash = crypto.createHash('sha256').update(token).digest('hex')\n const expiresAt = new Date(Date.now() + 15 * 60 * 1000) // 15 mins\n await req.payload.update({\n collection: subscribersCollectionSlug,\n data: {\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt.toISOString(),\n },\n where: {\n email: { equals: user.email },\n },\n })\n\n // Send email\n const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : ''\n const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`\n const subject = data.subject || 'Your Magic Login Link'\n const message = `\n ${data.message || '<p>Use this link to log in:</p>'}\n <p><a href=\"${magicLink}\"><b>Login</b></a></p>\n `\n const emailResult = await req.payload.sendEmail({\n html: message,\n subject,\n to: user.email,\n })\n // req.payload.logger.info(`email result: ${JSON.stringify(emailResult)}`)\n // return data; // Return data to allow normal submission if needed\n if (!emailResult) {\n return Response.json(\n {\n error: 'Unknown email result',\n now: new Date().toISOString(),\n } as RequestMagicLinkResponse,\n { status: 400 },\n )\n }\n req.payload.logger.info(`requestMagicLinkHandler email sent \\n ${magicLink}`)\n return Response.json({\n emailResult,\n now: new Date().toISOString(),\n } as RequestMagicLinkResponse)\n }\n\n /**\n * requestMagicLink Endpoint Config\n */\n const requestMagicLinkEndpoint: Endpoint = {\n handler: requestMagicLinkHandler,\n method: 'post',\n path: '/emailToken',\n }\n\n return requestMagicLinkEndpoint\n}\n\nexport default createEndpointRequestMagicLink\n"],"names":["crypto","defaultCollectionSlug","getTokenAndHash","createEndpointRequestMagicLink","subscribersCollectionSlug","requestMagicLinkHandler","req","data","json","email","forwardUrl","Response","error","now","Date","toISOString","status","userResults","payload","find","collection","where","equals","user","docs","tokenHash","tokenHash2","createResult","create","password","draft","token","randomBytes","toString","createHash","update","digest","expiresAt","verificationToken","verificationTokenExpires","forwardUrlParam","encodeURI","magicLink","config","serverURL","subject","message","emailResult","sendEmail","html","to","logger","info","requestMagicLinkEndpoint","handler","method","path"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { CollectionSlug, Endpoint } from 'payload';
2
+ export type SubscribeResponse = {
3
+ email: string;
4
+ now: string;
5
+ optIns: string[];
6
+ } | {
7
+ emailResult: any;
8
+ now: string;
9
+ } | {
10
+ error: string;
11
+ now: string;
12
+ };
13
+ /**
14
+ * createEndpointLogout
15
+ * @param options
16
+ * @returns
17
+ *
18
+ * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug
19
+ *
20
+ */
21
+ declare function createEndpointSubscribe({ subscribersCollectionSlug, }: {
22
+ subscribersCollectionSlug: CollectionSlug;
23
+ }): Endpoint;
24
+ export default createEndpointSubscribe;
@@ -0,0 +1,343 @@
1
+ import { defaultCollectionSlug } from '../collections/Subscribers.js';
2
+ import { getTokenAndHash } from '../helpers/token.js';
3
+ import { verifyOptIns } from '../helpers/verifyOptIns.js';
4
+ /**
5
+ * createEndpointLogout
6
+ * @param options
7
+ * @returns
8
+ *
9
+ * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug
10
+ *
11
+ */ function createEndpointSubscribe({ subscribersCollectionSlug = defaultCollectionSlug }) {
12
+ /**
13
+ * subscribe Endpoint Handler
14
+ * @param req
15
+ * @data { email }
16
+ * @returns { status: 200, json: {message: string, now: date} }
17
+ * @returns { status: 400, json: {error: ('Bad data' | 'Already subscribed' | 'Unknown email result'), now: date} }
18
+ */ const subscribeHandler = async (req)=>{
19
+ const data = req?.json ? await req.json() : {};
20
+ const { afterVerifyUrl, email, optIns } = data // if by POST data
21
+ ;
22
+ // const { email } = req.routeParams // if by path
23
+ //
24
+ // HELPERS
25
+ // Some of these functions make use of the scope within handler,
26
+ // and would have to be refactored if moved out.
27
+ //
28
+ const createSubscriber = async ({ optIns, password, status, verificationToken, verificationTokenExpires })=>{
29
+ await req.payload.create({
30
+ collection: subscribersCollectionSlug,
31
+ data: {
32
+ email,
33
+ optIns,
34
+ password,
35
+ status: status || 'pending',
36
+ verificationToken,
37
+ verificationTokenExpires: verificationTokenExpires?.toISOString()
38
+ },
39
+ draft: false
40
+ });
41
+ };
42
+ const updateSubscriber = async ({ id, optIns, password, status, verificationToken, verificationTokenExpires })=>{
43
+ const updateResults = await req.payload.update({
44
+ id,
45
+ collection: subscribersCollectionSlug,
46
+ data: {
47
+ optIns,
48
+ password,
49
+ status,
50
+ verificationToken,
51
+ verificationTokenExpires: verificationTokenExpires?.toISOString() || null
52
+ },
53
+ depth: 0
54
+ });
55
+ return updateResults;
56
+ };
57
+ const sendVerifyEmail = async ({ email, forwardUrl, linkText, message, subject, token })=>{
58
+ const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : '';
59
+ const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`;
60
+ const html = message + `<p><a href="${magicLink}">${linkText}</a></p>`;
61
+ const emailResult = await req.payload.sendEmail({
62
+ html,
63
+ subject,
64
+ to: email
65
+ });
66
+ req.payload.logger.info(`subscribe email sent \n ${magicLink}`);
67
+ return emailResult;
68
+ };
69
+ //
70
+ // VALIDATE INPUT
71
+ //
72
+ // Require email
73
+ if (!email) {
74
+ req.payload.logger.error(JSON.stringify({
75
+ error: 'Bad data',
76
+ now: new Date().toISOString()
77
+ }, undefined, 2));
78
+ return Response.json({
79
+ error: 'Bad data',
80
+ now: new Date().toISOString()
81
+ }, {
82
+ status: 400
83
+ });
84
+ }
85
+ //
86
+ // Validate OptInChannels
87
+ const { invalidOptInsInput, verifiedOptInIDs } = await verifyOptIns(req.payload, optIns);
88
+ if (invalidOptInsInput) {
89
+ req.payload.logger.error(JSON.stringify({
90
+ error: 'Invalid input: ' + JSON.stringify(optIns),
91
+ now: new Date().toISOString()
92
+ }, undefined, 2));
93
+ return Response.json({
94
+ error: 'Invalid input: ' + JSON.stringify(optIns),
95
+ now: new Date().toISOString()
96
+ }, {
97
+ status: 400
98
+ });
99
+ }
100
+ //
101
+ // Verify subscriber exists
102
+ const userResults = await req.payload.find({
103
+ collection: subscribersCollectionSlug,
104
+ where: {
105
+ email: {
106
+ equals: email
107
+ }
108
+ }
109
+ });
110
+ const subscriber = userResults.docs[0];
111
+ //
112
+ // Now we have a subscriber and validatedOptIns
113
+ // Handle scenarios
114
+ //
115
+ // ********************************************************
116
+ //
117
+ if (req.user && req.user.email != email) {
118
+ //
119
+ // Error: Auth-ed user doesn't match subscriber email
120
+ //
121
+ req.payload.logger.error(JSON.stringify({
122
+ error: 'Unauthorized: ' + email,
123
+ now: new Date().toISOString()
124
+ }, undefined, 2));
125
+ return Response.json({
126
+ error: 'Unauthorized: ' + email,
127
+ now: new Date().toISOString()
128
+ }, {
129
+ status: 400
130
+ });
131
+ }
132
+ //
133
+ // ********************************************************
134
+ //
135
+ if (!subscriber) {
136
+ //
137
+ // Create subscriber with status 'pending',
138
+ // and an invisible unknowable password,
139
+ // and send a verify email
140
+ // Pass all optIns through verify link
141
+ //
142
+ const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link
143
+ ;
144
+ const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable
145
+ ;
146
+ await createSubscriber({
147
+ email,
148
+ optIns,
149
+ password: tokenHash2,
150
+ status: 'pending',
151
+ verificationToken: tokenHash,
152
+ verificationTokenExpires: expiresAt
153
+ });
154
+ //
155
+ // Send email
156
+ const emailResult = await sendVerifyEmail({
157
+ email,
158
+ forwardUrl: afterVerifyUrl,
159
+ linkText: '<b>Verify</b>',
160
+ message: data.message || `<p>Click here to verify your subscription:</p>`,
161
+ subject: data.subject || 'Please verify your subscription',
162
+ token
163
+ });
164
+ if (!emailResult) {
165
+ req.payload.logger.error(JSON.stringify({
166
+ error: 'Unknown email result',
167
+ now: new Date().toISOString()
168
+ }, undefined, 2));
169
+ return Response.json({
170
+ error: 'Unknown email result',
171
+ now: new Date().toISOString()
172
+ }, {
173
+ status: 400
174
+ });
175
+ }
176
+ return Response.json({
177
+ emailResult,
178
+ now: new Date().toISOString()
179
+ });
180
+ //
181
+ }
182
+ //
183
+ // ********************************************************
184
+ //
185
+ if (!req.user && subscriber) {
186
+ //
187
+ // Send magic link to log the user in
188
+ // Pass all optIns through verify link
189
+ //
190
+ const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link
191
+ ;
192
+ // Update subscriber with token for pending email
193
+ const updateResults = await updateSubscriber({
194
+ id: subscriber.id,
195
+ verificationToken: tokenHash,
196
+ verificationTokenExpires: expiresAt
197
+ });
198
+ if (!updateResults) {
199
+ req.payload.logger.error(JSON.stringify({
200
+ error: 'Unknown error',
201
+ now: new Date().toISOString()
202
+ }, undefined, 2));
203
+ return Response.json({
204
+ error: 'Unknown error',
205
+ now: new Date().toISOString()
206
+ }, {
207
+ status: 400
208
+ });
209
+ }
210
+ //
211
+ // Send email
212
+ const emailResult = await sendVerifyEmail({
213
+ email,
214
+ forwardUrl: afterVerifyUrl,
215
+ linkText: 'Verify',
216
+ message: data.message || `<h1>Click here to verify your subscription:</h1>`,
217
+ subject: data.subject || 'Please verify your subscription',
218
+ token
219
+ });
220
+ if (!emailResult) {
221
+ req.payload.logger.error(JSON.stringify({
222
+ error: 'Unknown email result',
223
+ now: new Date().toISOString()
224
+ }, undefined, 2));
225
+ return Response.json({
226
+ error: 'Unknown email result',
227
+ now: new Date().toISOString()
228
+ }, {
229
+ status: 400
230
+ });
231
+ }
232
+ return Response.json({
233
+ emailResult,
234
+ now: new Date().toISOString()
235
+ });
236
+ }
237
+ //
238
+ // ********************************************************
239
+ //
240
+ if (req.user && subscriber && subscriber.status == 'pending') {
241
+ //
242
+ // Send magic link to verify the email and log the user in
243
+ // Pass all optIns through verify link
244
+ //
245
+ const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link
246
+ ;
247
+ // Create subscriber with token for pending email
248
+ const updateResults = await updateSubscriber({
249
+ id: subscriber.id,
250
+ verificationToken: tokenHash,
251
+ verificationTokenExpires: expiresAt
252
+ });
253
+ if (!updateResults) {
254
+ req.payload.logger.error(JSON.stringify({
255
+ error: 'Unknown error',
256
+ now: new Date().toISOString()
257
+ }, undefined, 2));
258
+ return Response.json({
259
+ error: 'Unknown error',
260
+ now: new Date().toISOString()
261
+ }, {
262
+ status: 400
263
+ });
264
+ }
265
+ const emailResult = await sendVerifyEmail({
266
+ email,
267
+ forwardUrl: afterVerifyUrl,
268
+ linkText: 'Verify',
269
+ message: data.message || `<h1>Click here to verify your email:</h1>`,
270
+ subject: data.subject || 'Please verify your subscription',
271
+ token
272
+ });
273
+ if (!emailResult) {
274
+ req.payload.logger.error(JSON.stringify({
275
+ error: 'Unknown email result',
276
+ now: new Date().toISOString()
277
+ }, undefined, 2));
278
+ return Response.json({
279
+ error: 'Unknown email result',
280
+ now: new Date().toISOString()
281
+ }, {
282
+ status: 400
283
+ });
284
+ }
285
+ return Response.json({
286
+ emailResult,
287
+ now: new Date().toISOString()
288
+ });
289
+ }
290
+ //
291
+ // ********************************************************
292
+ //
293
+ if (req.user && subscriber && subscriber.status != 'pending') {
294
+ //
295
+ // Update subscriber with status 'subscribed',
296
+ // an invisible unknowable password,
297
+ // and if any optIns input exists, set subscriber optIns
298
+ // to EXACTLY verifiedOptInIDs (potentially unsubscribing from any not in verifiedOptInIDs)
299
+ //
300
+ const { tokenHash } = getTokenAndHash() // Use for magic link
301
+ ;
302
+ // Update subscriber with optIns
303
+ const updateResults = await updateSubscriber({
304
+ id: subscriber.id,
305
+ optIns: verifiedOptInIDs,
306
+ password: tokenHash,
307
+ status: 'subscribed',
308
+ verificationToken: '',
309
+ verificationTokenExpires: null
310
+ });
311
+ // Return results, including the verified optIns
312
+ return Response.json({
313
+ email: updateResults.email,
314
+ now: new Date().toISOString(),
315
+ optIns: updateResults.optIns
316
+ });
317
+ }
318
+ //
319
+ // Uncaught case
320
+ //
321
+ req.payload.logger.error(JSON.stringify({
322
+ error: 'Unknown error',
323
+ now: new Date().toISOString()
324
+ }, undefined, 2));
325
+ return Response.json({
326
+ error: 'Unknown error',
327
+ now: new Date().toISOString()
328
+ }, {
329
+ status: 400
330
+ });
331
+ };
332
+ /**
333
+ * subscribe Endpoint Config
334
+ */ const subscribeEndpoint = {
335
+ handler: subscribeHandler,
336
+ method: 'post',
337
+ path: '/subscribe'
338
+ };
339
+ return subscribeEndpoint;
340
+ }
341
+ export default createEndpointSubscribe;
342
+
343
+ //# sourceMappingURL=subscribe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/subscribe.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint, PayloadHandler } from 'payload'\nimport type { Subscriber } from 'src/copied/payload-types.js'\n\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\n\nimport { getTokenAndHash } from '../helpers/token.js'\nimport { verifyOptIns } from '../helpers/verifyOptIns.js'\n\nexport type SubscribeResponse =\n // When subscriber optIns are updated...\n | {\n email: string\n now: string\n optIns: string[]\n }\n // When a verify link is emailed...\n | {\n emailResult: any\n now: string\n }\n // When any error occurs...\n | {\n error: string\n now: string\n }\n\n/**\n * createEndpointLogout\n * @param options\n * @returns\n *\n * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug\n *\n */\nfunction createEndpointSubscribe({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * subscribe Endpoint Handler\n * @param req\n * @data { email }\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('Bad data' | 'Already subscribed' | 'Unknown email result'), now: date} }\n */\n const subscribeHandler: PayloadHandler = async (req) => {\n const data = req?.json ? await req.json() : {}\n const {\n afterVerifyUrl,\n email,\n optIns,\n }: { afterVerifyUrl: string; email: string; optIns: string[] } = data // if by POST data\n // const { email } = req.routeParams // if by path\n\n //\n // HELPERS\n // Some of these functions make use of the scope within handler,\n // and would have to be refactored if moved out.\n //\n const createSubscriber = async ({\n optIns,\n password,\n status,\n verificationToken,\n verificationTokenExpires,\n }: {\n email: string\n optIns?: string[]\n password?: string\n status?: 'pending' | 'subscribed' | 'unsubscribed'\n verificationToken?: string\n verificationTokenExpires?: Date\n }) => {\n await req.payload.create({\n collection: subscribersCollectionSlug,\n data: {\n email,\n optIns,\n password,\n status: status || 'pending',\n verificationToken,\n verificationTokenExpires: verificationTokenExpires?.toISOString(),\n },\n draft: false,\n })\n }\n const updateSubscriber = async ({\n id,\n optIns,\n password,\n status,\n verificationToken,\n verificationTokenExpires,\n }: {\n id: string\n optIns?: string[]\n password?: string\n status?: 'pending' | 'subscribed' | 'unsubscribed'\n verificationToken?: string\n verificationTokenExpires?: Date | null\n }) => {\n const updateResults = await req.payload.update({\n id,\n collection: subscribersCollectionSlug,\n data: {\n optIns,\n password,\n status,\n verificationToken,\n verificationTokenExpires: verificationTokenExpires?.toISOString() || null,\n },\n depth: 0,\n })\n return updateResults\n }\n const sendVerifyEmail = async ({\n email,\n forwardUrl,\n linkText,\n message,\n subject,\n token,\n }: {\n email: string\n forwardUrl?: string\n linkText: string\n message: string\n subject: string\n token: string\n }) => {\n const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : ''\n const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`\n const html = message + `<p><a href=\"${magicLink}\">${linkText}</a></p>`\n const emailResult = await req.payload.sendEmail({\n html,\n subject,\n to: email,\n })\n req.payload.logger.info(`subscribe email sent \\n ${magicLink}`)\n return emailResult\n }\n\n //\n // VALIDATE INPUT\n //\n // Require email\n if (!email) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Bad data', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // Validate OptInChannels\n const { invalidOptInsInput, verifiedOptInIDs } = await verifyOptIns(req.payload, optIns)\n\n if (invalidOptInsInput) {\n req.payload.logger.error(\n JSON.stringify(\n {\n error: 'Invalid input: ' + JSON.stringify(optIns),\n now: new Date().toISOString(),\n } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n {\n error: 'Invalid input: ' + JSON.stringify(optIns),\n now: new Date().toISOString(),\n } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // Verify subscriber exists\n const userResults = await req.payload.find({\n collection: subscribersCollectionSlug,\n where: {\n email: { equals: email },\n },\n })\n const subscriber = userResults.docs[0] as Subscriber\n\n //\n // Now we have a subscriber and validatedOptIns\n // Handle scenarios\n //\n // ********************************************************\n //\n if (req.user && req.user.email != email) {\n //\n // Error: Auth-ed user doesn't match subscriber email\n //\n req.payload.logger.error(\n JSON.stringify(\n {\n error: 'Unauthorized: ' + email,\n now: new Date().toISOString(),\n } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n {\n error: 'Unauthorized: ' + email,\n now: new Date().toISOString(),\n } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // ********************************************************\n //\n if (!subscriber) {\n //\n // Create subscriber with status 'pending',\n // and an invisible unknowable password,\n // and send a verify email\n // Pass all optIns through verify link\n //\n const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link\n const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable\n await createSubscriber({\n email,\n optIns,\n password: tokenHash2,\n status: 'pending',\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt,\n })\n\n //\n // Send email\n const emailResult = await sendVerifyEmail({\n email,\n forwardUrl: afterVerifyUrl,\n linkText: '<b>Verify</b>',\n message: data.message || `<p>Click here to verify your subscription:</p>`,\n subject: data.subject || 'Please verify your subscription',\n token,\n })\n if (!emailResult) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)\n //\n }\n //\n // ********************************************************\n //\n if (!req.user && subscriber) {\n //\n // Send magic link to log the user in\n // Pass all optIns through verify link\n //\n const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link\n // Update subscriber with token for pending email\n const updateResults = await updateSubscriber({\n id: subscriber.id,\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt,\n })\n if (!updateResults) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // Send email\n const emailResult = await sendVerifyEmail({\n email,\n forwardUrl: afterVerifyUrl,\n linkText: 'Verify',\n message: data.message || `<h1>Click here to verify your subscription:</h1>`,\n subject: data.subject || 'Please verify your subscription',\n token,\n })\n if (!emailResult) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)\n }\n //\n // ********************************************************\n //\n if (req.user && subscriber && subscriber.status == 'pending') {\n //\n // Send magic link to verify the email and log the user in\n // Pass all optIns through verify link\n //\n const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link\n // Create subscriber with token for pending email\n const updateResults = await updateSubscriber({\n id: subscriber.id,\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt,\n })\n if (!updateResults) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n const emailResult = await sendVerifyEmail({\n email,\n forwardUrl: afterVerifyUrl,\n linkText: 'Verify',\n message: data.message || `<h1>Click here to verify your email:</h1>`,\n subject: data.subject || 'Please verify your subscription',\n token,\n })\n if (!emailResult) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)\n }\n\n //\n // ********************************************************\n //\n if (req.user && subscriber && subscriber.status != 'pending') {\n //\n // Update subscriber with status 'subscribed',\n // an invisible unknowable password,\n // and if any optIns input exists, set subscriber optIns\n // to EXACTLY verifiedOptInIDs (potentially unsubscribing from any not in verifiedOptInIDs)\n //\n const { tokenHash } = getTokenAndHash() // Use for magic link\n // Update subscriber with optIns\n const updateResults = (await updateSubscriber({\n id: subscriber.id,\n optIns: verifiedOptInIDs,\n password: tokenHash,\n status: 'subscribed',\n verificationToken: '',\n verificationTokenExpires: null,\n })) as Subscriber\n\n // Return results, including the verified optIns\n return Response.json({\n email: updateResults.email,\n now: new Date().toISOString(),\n optIns: updateResults.optIns,\n } as SubscribeResponse)\n }\n //\n // Uncaught case\n //\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n /**\n * subscribe Endpoint Config\n */\n const subscribeEndpoint: Endpoint = {\n handler: subscribeHandler,\n method: 'post',\n path: '/subscribe',\n }\n\n return subscribeEndpoint\n}\n\nexport default createEndpointSubscribe\n"],"names":["defaultCollectionSlug","getTokenAndHash","verifyOptIns","createEndpointSubscribe","subscribersCollectionSlug","subscribeHandler","req","data","json","afterVerifyUrl","email","optIns","createSubscriber","password","status","verificationToken","verificationTokenExpires","payload","create","collection","toISOString","draft","updateSubscriber","id","updateResults","update","depth","sendVerifyEmail","forwardUrl","linkText","message","subject","token","forwardUrlParam","encodeURI","magicLink","config","serverURL","html","emailResult","sendEmail","to","logger","info","error","JSON","stringify","now","Date","undefined","Response","invalidOptInsInput","verifiedOptInIDs","userResults","find","where","equals","subscriber","docs","user","expiresAt","tokenHash","tokenHash2","subscribeEndpoint","handler","method","path"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { Subscriber } from '../copied/payload-types.js';
2
+ import type { CollectionSlug, Endpoint, Permissions } from 'payload';
3
+ export type SubscriberAuthResponse = {
4
+ error: string;
5
+ now: string;
6
+ } | {
7
+ now: string;
8
+ permissions: Permissions;
9
+ subscriber: null | Subscriber;
10
+ };
11
+ /**
12
+ * createEndpointLogout
13
+ * @param options
14
+ * @returns
15
+ *
16
+ * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug
17
+ *
18
+ */
19
+ declare function createEndpointSubscriberAuth({ subscribersCollectionSlug, }: {
20
+ subscribersCollectionSlug: CollectionSlug;
21
+ }): Endpoint;
22
+ export default createEndpointSubscriberAuth;
@@ -0,0 +1,69 @@
1
+ import { headers as nextHeaders } from 'next/headers.js';
2
+ import { defaultCollectionSlug } from '../collections/Subscribers.js';
3
+ /**
4
+ * createEndpointLogout
5
+ * @param options
6
+ * @returns
7
+ *
8
+ * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug
9
+ *
10
+ */ function createEndpointSubscriberAuth({ subscribersCollectionSlug = defaultCollectionSlug }) {
11
+ /**
12
+ * subscriberAuth Endpoint Handler
13
+ * @param req
14
+ * @returns { status: 200, json: {message: string, now: date} }
15
+ * @returns { status: 400, json: {error: ('No subscriber authed' | catchError | 'Unknown error'), now: date} }
16
+ */ const subscriberAuthHandler = async (req)=>{
17
+ // req.payload.logger.info('subscriberAuthHandler')
18
+ // Log the user in via Payload headers
19
+ const headers = await nextHeaders();
20
+ try {
21
+ const { permissions, user } = await req.payload.auth({
22
+ headers
23
+ });
24
+ // req.payload.logger.info(`user = ${JSON.stringify(user)}`)
25
+ // req.payload.logger.info(`permissions = ${JSON.stringify(permissions)}`)
26
+ if (user && user.collection == subscribersCollectionSlug) {
27
+ const subscriber = user;
28
+ if (subscriber.optIns) {
29
+ subscriber.optIns = subscriber.optIns.map((channel)=>typeof channel == 'string' ? channel : channel.id);
30
+ }
31
+ return Response.json({
32
+ now: new Date().toISOString(),
33
+ permissions,
34
+ subscriber
35
+ });
36
+ }
37
+ // req.payload.logger.info('subscriberAuthHandler: No subscriber authed')
38
+ return Response.json({
39
+ // error: 'No subscriber authed',
40
+ now: new Date().toISOString(),
41
+ permissions,
42
+ subscriber: null
43
+ }, {
44
+ headers,
45
+ status: 200
46
+ });
47
+ } catch (error) {
48
+ // req.payload.logger.info(`subscriberAuth error: ${JSON.stringify(error)}`)
49
+ return Response.json({
50
+ error,
51
+ now: new Date().toISOString()
52
+ }, {
53
+ headers,
54
+ status: 400
55
+ });
56
+ }
57
+ };
58
+ /**
59
+ * subscriberAuth Endpoint Config
60
+ */ const subscriberAuthEndpoint = {
61
+ handler: subscriberAuthHandler,
62
+ method: 'post',
63
+ path: '/subscriberAuth'
64
+ };
65
+ return subscriberAuthEndpoint;
66
+ }
67
+ export default createEndpointSubscriberAuth;
68
+
69
+ //# sourceMappingURL=subscriberAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/subscriberAuth.ts"],"sourcesContent":["import { headers as nextHeaders } from 'next/headers.js'\n\nimport type { Subscriber } from '../copied/payload-types.js'\n\n// If you're using Next.js, you'll have to import headers from next/headers, like so:\nimport type { CollectionSlug, Endpoint, PayloadHandler, Permissions } from 'payload'\n\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\n\nexport type SubscriberAuthResponse =\n | {\n error: string\n now: string\n }\n | {\n now: string\n permissions: Permissions\n subscriber: null | Subscriber\n }\n\n/**\n * createEndpointLogout\n * @param options\n * @returns\n *\n * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug\n *\n */\nfunction createEndpointSubscriberAuth({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * subscriberAuth Endpoint Handler\n * @param req\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('No subscriber authed' | catchError | 'Unknown error'), now: date} }\n */\n const subscriberAuthHandler: PayloadHandler = async (req) => {\n // req.payload.logger.info('subscriberAuthHandler')\n // Log the user in via Payload headers\n const headers = await nextHeaders()\n\n try {\n const { permissions, user } = await req.payload.auth({\n headers,\n })\n\n // req.payload.logger.info(`user = ${JSON.stringify(user)}`)\n // req.payload.logger.info(`permissions = ${JSON.stringify(permissions)}`)\n\n if (user && user.collection == subscribersCollectionSlug) {\n const subscriber: Subscriber = user as Subscriber\n if (subscriber.optIns) {\n subscriber.optIns = subscriber.optIns.map((channel) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n return Response.json({\n now: new Date().toISOString(),\n permissions,\n subscriber,\n } as SubscriberAuthResponse)\n }\n\n // req.payload.logger.info('subscriberAuthHandler: No subscriber authed')\n return Response.json(\n {\n // error: 'No subscriber authed',\n now: new Date().toISOString(),\n permissions,\n subscriber: null,\n } as SubscriberAuthResponse,\n { headers, status: 200 },\n )\n } catch (error: unknown) {\n // req.payload.logger.info(`subscriberAuth error: ${JSON.stringify(error)}`)\n return Response.json(\n {\n error,\n now: new Date().toISOString(),\n } as SubscriberAuthResponse,\n { headers, status: 400 },\n )\n }\n }\n\n /**\n * subscriberAuth Endpoint Config\n */\n const subscriberAuthEndpoint: Endpoint = {\n handler: subscriberAuthHandler,\n method: 'post',\n path: '/subscriberAuth',\n }\n return subscriberAuthEndpoint\n}\n\nexport default createEndpointSubscriberAuth\n"],"names":["headers","nextHeaders","defaultCollectionSlug","createEndpointSubscriberAuth","subscribersCollectionSlug","subscriberAuthHandler","req","permissions","user","payload","auth","collection","subscriber","optIns","map","channel","id","Response","json","now","Date","toISOString","status","error","subscriberAuthEndpoint","handler","method","path"],"mappings":"AAAA,SAASA,WAAWC,WAAW,QAAQ,kBAAiB;AAOxD,SAASC,qBAAqB,QAAQ,gCAA+B;AAarE;;;;;;;CAOC,GACD,SAASC,6BAA6B,EACpCC,4BAA4BF,qBAAqB,EAGlD;IACC;;;;;GAKC,GACD,MAAMG,wBAAwC,OAAOC;QACnD,mDAAmD;QACnD,sCAAsC;QACtC,MAAMN,UAAU,MAAMC;QAEtB,IAAI;YACF,MAAM,EAAEM,WAAW,EAAEC,IAAI,EAAE,GAAG,MAAMF,IAAIG,OAAO,CAACC,IAAI,CAAC;gBACnDV;YACF;YAEA,4DAA4D;YAC5D,0EAA0E;YAE1E,IAAIQ,QAAQA,KAAKG,UAAU,IAAIP,2BAA2B;gBACxD,MAAMQ,aAAyBJ;gBAC/B,IAAII,WAAWC,MAAM,EAAE;oBACrBD,WAAWC,MAAM,GAAGD,WAAWC,MAAM,CAACC,GAAG,CAAC,CAACC,UACzC,OAAOA,WAAW,WAAWA,UAAUA,QAAQC,EAAE;gBAErD;gBACA,OAAOC,SAASC,IAAI,CAAC;oBACnBC,KAAK,IAAIC,OAAOC,WAAW;oBAC3Bd;oBACAK;gBACF;YACF;YAEA,yEAAyE;YACzE,OAAOK,SAASC,IAAI,CAClB;gBACE,iCAAiC;gBACjCC,KAAK,IAAIC,OAAOC,WAAW;gBAC3Bd;gBACAK,YAAY;YACd,GACA;gBAAEZ;gBAASsB,QAAQ;YAAI;QAE3B,EAAE,OAAOC,OAAgB;YACvB,4EAA4E;YAC5E,OAAON,SAASC,IAAI,CAClB;gBACEK;gBACAJ,KAAK,IAAIC,OAAOC,WAAW;YAC7B,GACA;gBAAErB;gBAASsB,QAAQ;YAAI;QAE3B;IACF;IAEA;;GAEC,GACD,MAAME,yBAAmC;QACvCC,SAASpB;QACTqB,QAAQ;QACRC,MAAM;IACR;IACA,OAAOH;AACT;AAEA,eAAerB,6BAA4B"}