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.
- package/README.md +305 -0
- package/dist/collections/OptInChannels.d.ts +3 -0
- package/dist/collections/OptInChannels.js +44 -0
- package/dist/collections/OptInChannels.js.map +1 -0
- package/dist/collections/Subscribers.d.ts +8 -0
- package/dist/collections/Subscribers.js +88 -0
- package/dist/collections/Subscribers.js.map +1 -0
- package/dist/collections/fields/OptedInChannels.d.ts +2 -0
- package/dist/collections/fields/OptedInChannels.js +12 -0
- package/dist/collections/fields/OptedInChannels.js.map +1 -0
- package/dist/components/BeforeDashboardClient.d.ts +1 -0
- package/dist/components/BeforeDashboardClient.js +40 -0
- package/dist/components/BeforeDashboardClient.js.map +1 -0
- package/dist/components/BeforeDashboardServer.d.ts +2 -0
- package/dist/components/BeforeDashboardServer.js +22 -0
- package/dist/components/BeforeDashboardServer.js.map +1 -0
- package/dist/components/BeforeDashboardServer.module.css +5 -0
- package/dist/components/app/RequestMagicLink.d.ts +16 -0
- package/dist/components/app/RequestMagicLink.js +114 -0
- package/dist/components/app/RequestMagicLink.js.map +1 -0
- package/dist/components/app/RequestMagicLink.module.css +5 -0
- package/dist/components/app/RequestOrSubscribe.d.ts +17 -0
- package/dist/components/app/RequestOrSubscribe.js +28 -0
- package/dist/components/app/RequestOrSubscribe.js.map +1 -0
- package/dist/components/app/SelectOptInChannels.d.ts +20 -0
- package/dist/components/app/SelectOptInChannels.js +120 -0
- package/dist/components/app/SelectOptInChannels.js.map +1 -0
- package/dist/components/app/SelectOptInChannels.module.css +5 -0
- package/dist/components/app/Subscribe.d.ts +18 -0
- package/dist/components/app/Subscribe.js +169 -0
- package/dist/components/app/Subscribe.js.map +1 -0
- package/dist/components/app/Subscribe.module.css +5 -0
- package/dist/components/app/SubscriberMenu.d.ts +7 -0
- package/dist/components/app/SubscriberMenu.js +44 -0
- package/dist/components/app/SubscriberMenu.js.map +1 -0
- package/dist/components/app/VerifyMagicLink.d.ts +23 -0
- package/dist/components/app/VerifyMagicLink.js +169 -0
- package/dist/components/app/VerifyMagicLink.js.map +1 -0
- package/dist/components/app/VerifyMagicLink.module.css +5 -0
- package/dist/components/app/helpers.d.ts +1 -0
- package/dist/components/app/helpers.js +5 -0
- package/dist/components/app/helpers.js.map +1 -0
- package/dist/components/app/shared.module.css +14 -0
- package/dist/contexts/SubscriberProvider.d.ts +15 -0
- package/dist/contexts/SubscriberProvider.js +105 -0
- package/dist/contexts/SubscriberProvider.js.map +1 -0
- package/dist/copied/payload-types.d.ts +395 -0
- package/dist/copied/payload-types.js +15 -0
- package/dist/copied/payload-types.js.map +1 -0
- package/dist/copied/payload.config.d.ts +2 -0
- package/dist/endpoints/customEndpointHandler.d.ts +2 -0
- package/dist/endpoints/customEndpointHandler.js +7 -0
- package/dist/endpoints/customEndpointHandler.js.map +1 -0
- package/dist/endpoints/getOptInChannels.d.ts +19 -0
- package/dist/endpoints/getOptInChannels.js +42 -0
- package/dist/endpoints/getOptInChannels.js.map +1 -0
- package/dist/endpoints/logout.d.ts +20 -0
- package/dist/endpoints/logout.js +60 -0
- package/dist/endpoints/logout.js.map +1 -0
- package/dist/endpoints/requestMagicLink.d.ts +20 -0
- package/dist/endpoints/requestMagicLink.js +122 -0
- package/dist/endpoints/requestMagicLink.js.map +1 -0
- package/dist/endpoints/subscribe.d.ts +24 -0
- package/dist/endpoints/subscribe.js +343 -0
- package/dist/endpoints/subscribe.js.map +1 -0
- package/dist/endpoints/subscriberAuth.d.ts +22 -0
- package/dist/endpoints/subscriberAuth.js +69 -0
- package/dist/endpoints/subscriberAuth.js.map +1 -0
- package/dist/endpoints/verifyMagicLink.d.ts +20 -0
- package/dist/endpoints/verifyMagicLink.js +142 -0
- package/dist/endpoints/verifyMagicLink.js.map +1 -0
- package/dist/exports/client.d.ts +1 -0
- package/dist/exports/client.js +3 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/index.d.ts +1 -0
- package/dist/exports/index.js +3 -0
- package/dist/exports/index.js.map +1 -0
- package/dist/exports/rsc.d.ts +1 -0
- package/dist/exports/rsc.js +3 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/exports/ui.d.ts +11 -0
- package/dist/exports/ui.js +9 -0
- package/dist/exports/ui.js.map +1 -0
- package/dist/helpers/serverConfig.d.ts +4 -0
- package/dist/helpers/serverConfig.js +22 -0
- package/dist/helpers/serverConfig.js.map +1 -0
- package/dist/helpers/testData.d.ts +2 -0
- package/dist/helpers/testData.js +4 -0
- package/dist/helpers/testData.js.map +1 -0
- package/dist/helpers/token.d.ts +9 -0
- package/dist/helpers/token.js +20 -0
- package/dist/helpers/token.js.map +1 -0
- package/dist/helpers/verifyOptIns.d.ts +5 -0
- package/dist/helpers/verifyOptIns.js +33 -0
- package/dist/helpers/verifyOptIns.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +147 -0
- package/dist/index.js.map +1 -0
- package/dist/react-hooks/useServerUrl.d.ts +3 -0
- package/dist/react-hooks/useServerUrl.js +19 -0
- package/dist/react-hooks/useServerUrl.js.map +1 -0
- package/dist/server-functions/serverUrl.d.ts +3 -0
- package/dist/server-functions/serverUrl.js +31 -0
- package/dist/server-functions/serverUrl.js.map +1 -0
- package/dist/server-functions/subscriberAuth.d.ts +11 -0
- package/package.json +94 -0
- package/src/collections/OptInChannels.ts +45 -0
- package/src/collections/Subscribers.ts +99 -0
- package/src/collections/fields/OptedInChannels.ts +12 -0
- package/src/components/app/RequestMagicLink.tsx +129 -0
- package/src/components/app/RequestOrSubscribe.tsx +58 -0
- package/src/components/app/SelectOptInChannels.tsx +147 -0
- package/src/components/app/Subscribe.tsx +190 -0
- package/src/components/app/SubscriberMenu.tsx +46 -0
- package/src/components/app/VerifyMagicLink.tsx +197 -0
- package/src/components/app/helpers.ts +6 -0
- package/src/components/app/shared.module.css +14 -0
- package/src/contexts/SubscriberProvider.tsx +122 -0
- package/src/copied/payload-types.ts +478 -0
- package/src/endpoints/getOptInChannels.ts +56 -0
- package/src/endpoints/logout.ts +104 -0
- package/src/endpoints/requestMagicLink.ts +139 -0
- package/src/endpoints/subscribe.ts +435 -0
- package/src/endpoints/subscriberAuth.ts +100 -0
- package/src/endpoints/verifyMagicLink.ts +164 -0
- package/src/exports/index.ts +1 -0
- package/src/exports/ui.ts +17 -0
- package/src/helpers/testData.ts +2 -0
- package/src/helpers/token.ts +14 -0
- package/src/helpers/verifyOptIns.ts +39 -0
- package/src/index.ts +207 -0
- package/src/react-hooks/useServerUrl.tsx +18 -0
- 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"}
|