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,20 @@
|
|
|
1
|
+
import type { CollectionSlug, Endpoint } from 'payload';
|
|
2
|
+
export type VerifyMagicLinkResponse = {
|
|
3
|
+
error: string;
|
|
4
|
+
now: string;
|
|
5
|
+
} | {
|
|
6
|
+
message: string;
|
|
7
|
+
now: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* createEndpointLogout
|
|
11
|
+
* @param options
|
|
12
|
+
* @returns
|
|
13
|
+
*
|
|
14
|
+
* Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
declare function createEndpointVerifyMagicLink({ subscribersCollectionSlug, }: {
|
|
18
|
+
subscribersCollectionSlug: CollectionSlug;
|
|
19
|
+
}): Endpoint;
|
|
20
|
+
export default createEndpointVerifyMagicLink;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { defaultCollectionSlug } from '../collections/Subscribers.js';
|
|
2
|
+
import { getHash, getTokenAndHash } from '../helpers/token.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 createEndpointVerifyMagicLink({ subscribersCollectionSlug = defaultCollectionSlug }) {
|
|
11
|
+
/**
|
|
12
|
+
* verifyMagicLink Endpoint Handler
|
|
13
|
+
* @param req
|
|
14
|
+
* @data { email, forwardUrl, token }
|
|
15
|
+
* @returns { status: 200, json: {message: string, now: date} }
|
|
16
|
+
* @returns { status: 400, json: {error: ('Bad data' | 'Token not verified' | 'Token expired'), now: date} }
|
|
17
|
+
*/ const verifyMagicLinkHandler = async (req)=>{
|
|
18
|
+
const reqData = req?.json ? await req.json() : {};
|
|
19
|
+
const { email, token } = reqData // if by POST reqData
|
|
20
|
+
;
|
|
21
|
+
// const { email, token } = req.routeParams // if by path
|
|
22
|
+
if (!email || !token) {
|
|
23
|
+
return Response.json({
|
|
24
|
+
error: 'Bad data',
|
|
25
|
+
now: new Date().toISOString()
|
|
26
|
+
}, {
|
|
27
|
+
status: 400
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const userResults = await req.payload.find({
|
|
31
|
+
collection: subscribersCollectionSlug,
|
|
32
|
+
where: {
|
|
33
|
+
email: {
|
|
34
|
+
equals: email
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const user = userResults.docs[0];
|
|
39
|
+
if (!user) {
|
|
40
|
+
return Response.json({
|
|
41
|
+
error: 'Bad data',
|
|
42
|
+
now: new Date().toISOString()
|
|
43
|
+
}, {
|
|
44
|
+
status: 400
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const { tokenHash } = getHash(token);
|
|
48
|
+
// req.payload.logger.info(
|
|
49
|
+
// `verifyMagicLinkHandler ${email} \n ${tokenHash} \n ${user.verificationTokenExpires} \n ${user.verificationToken}`,
|
|
50
|
+
// )
|
|
51
|
+
if (!user.verificationTokenExpires || tokenHash != user.verificationToken) {
|
|
52
|
+
// req.payload.logger.info(`Token not verified: ${tokenHash} != ${user.verificationToken}`)
|
|
53
|
+
return Response.json({
|
|
54
|
+
error: 'Token not verified',
|
|
55
|
+
now: new Date().toISOString()
|
|
56
|
+
}, {
|
|
57
|
+
status: 400
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (new Date(Date.now()) > new Date(user.verificationTokenExpires)) {
|
|
61
|
+
return Response.json({
|
|
62
|
+
error: 'Token expired',
|
|
63
|
+
now: new Date().toISOString()
|
|
64
|
+
}, {
|
|
65
|
+
status: 400
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Update user
|
|
69
|
+
await req.payload.update({
|
|
70
|
+
collection: subscribersCollectionSlug,
|
|
71
|
+
data: {
|
|
72
|
+
password: tokenHash
|
|
73
|
+
},
|
|
74
|
+
where: {
|
|
75
|
+
email: {
|
|
76
|
+
equals: user.email
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// Log the user in via Payload headers
|
|
81
|
+
let headers;
|
|
82
|
+
try {
|
|
83
|
+
const loginReq = await fetch(`${req.payload.config.serverURL}/api/${subscribersCollectionSlug}/login`, {
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
email,
|
|
86
|
+
password: tokenHash
|
|
87
|
+
}),
|
|
88
|
+
credentials: 'include',
|
|
89
|
+
headers: {
|
|
90
|
+
'Content-Type': 'application/json'
|
|
91
|
+
},
|
|
92
|
+
method: 'POST'
|
|
93
|
+
});
|
|
94
|
+
if (loginReq && loginReq.ok) {
|
|
95
|
+
headers = loginReq.headers;
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
// console.log(error)
|
|
99
|
+
return Response.json({
|
|
100
|
+
error
|
|
101
|
+
}, {
|
|
102
|
+
status: 400
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// console.log('login', headers)
|
|
106
|
+
const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable
|
|
107
|
+
;
|
|
108
|
+
const data = {
|
|
109
|
+
password: tokenHash2,
|
|
110
|
+
status: 'subscribed',
|
|
111
|
+
verificationToken: '',
|
|
112
|
+
verificationTokenExpires: null
|
|
113
|
+
};
|
|
114
|
+
// Update user
|
|
115
|
+
await req.payload.update({
|
|
116
|
+
collection: subscribersCollectionSlug,
|
|
117
|
+
data,
|
|
118
|
+
where: {
|
|
119
|
+
email: {
|
|
120
|
+
equals: user.email
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return Response.json({
|
|
125
|
+
message: 'Token verified',
|
|
126
|
+
now: new Date().toISOString()
|
|
127
|
+
}, {
|
|
128
|
+
headers
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* verifyMagicLink Endpoint Config
|
|
133
|
+
*/ const verifyMagicLinkEndpoint = {
|
|
134
|
+
handler: verifyMagicLinkHandler,
|
|
135
|
+
method: 'post',
|
|
136
|
+
path: '/verifyToken'
|
|
137
|
+
};
|
|
138
|
+
return verifyMagicLinkEndpoint;
|
|
139
|
+
}
|
|
140
|
+
export default createEndpointVerifyMagicLink;
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=verifyMagicLink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/verifyMagicLink.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'\nimport { getHash, getTokenAndHash } from '../helpers/token.js'\n\nexport type VerifyMagicLinkResponse =\n | {\n error: string\n now: string\n }\n | {\n message: 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 createEndpointVerifyMagicLink({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * verifyMagicLink Endpoint Handler\n * @param req\n * @data { email, forwardUrl, token }\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('Bad data' | 'Token not verified' | 'Token expired'), now: date} }\n */\n const verifyMagicLinkHandler: PayloadHandler = async (req) => {\n const reqData = req?.json ? await req.json() : {}\n const { email, token }: { email: string; token: string } = reqData // if by POST reqData\n // const { email, token } = req.routeParams // if by path\n\n if (!email || !token) {\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as VerifyMagicLinkResponse,\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\n type SubscriberType = {\n // @ts-expect-error Why is this not correct, isn't it how Payload does it?\n collection: subscribersCollectionSlug\n } & Subscriber\n\n const user = userResults.docs[0] as SubscriberType\n\n if (!user) {\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as VerifyMagicLinkResponse,\n { status: 400 },\n )\n }\n\n const { tokenHash } = getHash(token)\n\n // req.payload.logger.info(\n // `verifyMagicLinkHandler ${email} \\n ${tokenHash} \\n ${user.verificationTokenExpires} \\n ${user.verificationToken}`,\n // )\n if (!user.verificationTokenExpires || tokenHash != user.verificationToken) {\n // req.payload.logger.info(`Token not verified: ${tokenHash} != ${user.verificationToken}`)\n return Response.json(\n { error: 'Token not verified', now: new Date().toISOString() } as VerifyMagicLinkResponse,\n { status: 400 },\n )\n }\n\n if (new Date(Date.now()) > new Date(user.verificationTokenExpires)) {\n return Response.json(\n { error: 'Token expired', now: new Date().toISOString() } as VerifyMagicLinkResponse,\n { status: 400 },\n )\n }\n\n // Update user\n await req.payload.update({\n collection: subscribersCollectionSlug,\n data: {\n password: tokenHash,\n },\n where: {\n email: { equals: user.email },\n },\n })\n\n // Log the user in via Payload headers\n let headers\n try {\n const loginReq = await fetch(\n `${req.payload.config.serverURL}/api/${subscribersCollectionSlug}/login`,\n {\n body: JSON.stringify({\n email,\n password: tokenHash,\n }),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n },\n )\n if (loginReq && loginReq.ok) {\n headers = loginReq.headers\n }\n } catch (error) {\n // console.log(error)\n return Response.json({ error } as VerifyMagicLinkResponse, { status: 400 })\n }\n // console.log('login', headers)\n\n const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable\n const data = {\n password: tokenHash2,\n status: 'subscribed' as 'pending' | 'subscribed' | 'unsubscribed' | undefined,\n verificationToken: '',\n verificationTokenExpires: null,\n }\n // Update user\n await req.payload.update({\n collection: subscribersCollectionSlug,\n data,\n where: {\n email: { equals: user.email },\n },\n })\n\n return Response.json(\n {\n message: 'Token verified',\n now: new Date().toISOString(),\n } as VerifyMagicLinkResponse,\n { headers },\n )\n }\n\n /**\n * verifyMagicLink Endpoint Config\n */\n const verifyMagicLinkEndpoint: Endpoint = {\n handler: verifyMagicLinkHandler,\n method: 'post',\n path: '/verifyToken',\n }\n\n return verifyMagicLinkEndpoint\n}\n\nexport default createEndpointVerifyMagicLink\n"],"names":["defaultCollectionSlug","getHash","getTokenAndHash","createEndpointVerifyMagicLink","subscribersCollectionSlug","verifyMagicLinkHandler","req","reqData","json","email","token","Response","error","now","Date","toISOString","status","userResults","payload","find","collection","where","equals","user","docs","tokenHash","verificationTokenExpires","verificationToken","update","data","password","headers","loginReq","fetch","config","serverURL","body","JSON","stringify","credentials","method","ok","tokenHash2","message","verifyMagicLinkEndpoint","handler","path"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js'\n"],"names":["BeforeDashboardClient"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ui.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/index.ts"],"sourcesContent":["export * from './ui.js'\n"],"names":[],"mappings":"AAAA,cAAc,UAAS"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js'\n"],"names":["BeforeDashboardServer"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type { RequestMagicLinkResponse } from '../components/app/RequestMagicLink.js';
|
|
2
|
+
export { RequestMagicLink } from '../components/app/RequestMagicLink.js';
|
|
3
|
+
export { RequestOrSubscribe } from '../components/app/RequestOrSubscribe.js';
|
|
4
|
+
export type { SubscribeResponse } from '../components/app/Subscribe.js';
|
|
5
|
+
export { Subscribe } from '../components/app/Subscribe.js';
|
|
6
|
+
export { SubscriberMenu } from '../components/app/SubscriberMenu.js';
|
|
7
|
+
export type { VerifyMagicLinkResponse } from '../components/app/VerifyMagicLink.js';
|
|
8
|
+
export { VerifyMagicLink } from '../components/app/VerifyMagicLink.js';
|
|
9
|
+
export type { SubscriberContextType } from '../contexts/SubscriberProvider.js';
|
|
10
|
+
export { SubscriberProvider, useSubscriber } from '../contexts/SubscriberProvider.js';
|
|
11
|
+
export { getServerUrl } from '../server-functions/serverUrl.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { RequestMagicLink } from '../components/app/RequestMagicLink.js';
|
|
2
|
+
export { RequestOrSubscribe } from '../components/app/RequestOrSubscribe.js';
|
|
3
|
+
export { Subscribe } from '../components/app/Subscribe.js';
|
|
4
|
+
export { SubscriberMenu } from '../components/app/SubscriberMenu.js';
|
|
5
|
+
export { VerifyMagicLink } from '../components/app/VerifyMagicLink.js';
|
|
6
|
+
export { SubscriberProvider, useSubscriber } from '../contexts/SubscriberProvider.js';
|
|
7
|
+
export { getServerUrl } from '../server-functions/serverUrl.js';
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/ui.ts"],"sourcesContent":["export type { RequestMagicLinkResponse } from '../components/app/RequestMagicLink.js'\nexport { RequestMagicLink } from '../components/app/RequestMagicLink.js'\n\nexport { RequestOrSubscribe } from '../components/app/RequestOrSubscribe.js'\n\nexport type { SubscribeResponse } from '../components/app/Subscribe.js'\nexport { Subscribe } from '../components/app/Subscribe.js'\n\nexport { SubscriberMenu } from '../components/app/SubscriberMenu.js'\n\nexport type { VerifyMagicLinkResponse } from '../components/app/VerifyMagicLink.js'\nexport { VerifyMagicLink } from '../components/app/VerifyMagicLink.js'\n\nexport type { SubscriberContextType } from '../contexts/SubscriberProvider.js'\nexport { SubscriberProvider, useSubscriber } from '../contexts/SubscriberProvider.js'\n\nexport { getServerUrl } from '../server-functions/serverUrl.js'\n"],"names":["RequestMagicLink","RequestOrSubscribe","Subscribe","SubscriberMenu","VerifyMagicLink","SubscriberProvider","useSubscriber","getServerUrl"],"mappings":"AACA,SAASA,gBAAgB,QAAQ,wCAAuC;AAExE,SAASC,kBAAkB,QAAQ,0CAAyC;AAG5E,SAASC,SAAS,QAAQ,iCAAgC;AAE1D,SAASC,cAAc,QAAQ,sCAAqC;AAGpE,SAASC,eAAe,QAAQ,uCAAsC;AAGtE,SAASC,kBAAkB,EAAEC,aAAa,QAAQ,oCAAmC;AAErF,SAASC,YAAY,QAAQ,mCAAkC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
|
|
2
|
+
export const getServerSideURL = ()=>{
|
|
3
|
+
const serverSideURL = process.env.NEXT_PUBLIC_VERCEL_URL ? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}` : process.env.VERCEL_PROJECT_PRODUCTION_URL ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}` : process.env.NEXT_PUBLIC_DEV_URL ? `http://${process.env.NEXT_PUBLIC_DEV_URL}` : 'http://localhost:3000';
|
|
4
|
+
// console.log(`process.env.NEXT_PUBLIC_DEV_URL: ${process.env.NEXT_PUBLIC_DEV_URL}`)
|
|
5
|
+
// console.log(`serverSideURL: ${serverSideURL}`)
|
|
6
|
+
return serverSideURL;
|
|
7
|
+
};
|
|
8
|
+
export const getClientSideURL = ()=>{
|
|
9
|
+
if (canUseDOM) {
|
|
10
|
+
const protocol = window.location.protocol;
|
|
11
|
+
const domain = window.location.hostname;
|
|
12
|
+
const port = window.location.port;
|
|
13
|
+
// `${window.location.protocol}//${window.location.host}
|
|
14
|
+
const clientSideURL = `${protocol}//${domain}${port ? `:${port}` : ''}`;
|
|
15
|
+
// console.log(`clientSideURL: ${clientSideURL}`)
|
|
16
|
+
return clientSideURL;
|
|
17
|
+
}
|
|
18
|
+
return getServerSideURL();
|
|
19
|
+
};
|
|
20
|
+
export const serverURL = getClientSideURL();
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=serverConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/serverConfig.ts"],"sourcesContent":["export const canUseDOM = !!(\n typeof window !== 'undefined' &&\n window.document &&\n window.document.createElement\n)\n\nexport const getServerSideURL = () => {\n const serverSideURL = process.env.NEXT_PUBLIC_VERCEL_URL\n ? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`\n : process.env.VERCEL_PROJECT_PRODUCTION_URL\n ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`\n : process.env.NEXT_PUBLIC_DEV_URL\n ? `http://${process.env.NEXT_PUBLIC_DEV_URL}`\n : 'http://localhost:3000'\n // console.log(`process.env.NEXT_PUBLIC_DEV_URL: ${process.env.NEXT_PUBLIC_DEV_URL}`)\n // console.log(`serverSideURL: ${serverSideURL}`)\n return serverSideURL\n}\n\nexport const getClientSideURL = () => {\n if (canUseDOM) {\n const protocol = window.location.protocol\n const domain = window.location.hostname\n const port = window.location.port\n // `${window.location.protocol}//${window.location.host}\n const clientSideURL = `${protocol}//${domain}${port ? `:${port}` : ''}`\n // console.log(`clientSideURL: ${clientSideURL}`)\n return clientSideURL\n }\n\n return getServerSideURL()\n}\n\nexport const serverURL = getClientSideURL()\n"],"names":["canUseDOM","window","document","createElement","getServerSideURL","serverSideURL","process","env","NEXT_PUBLIC_VERCEL_URL","VERCEL_PROJECT_PRODUCTION_URL","NEXT_PUBLIC_DEV_URL","getClientSideURL","protocol","location","domain","hostname","port","clientSideURL","serverURL"],"mappings":"AAAA,OAAO,MAAMA,YAAY,CAAC,CACxB,CAAA,OAAOC,WAAW,eAClBA,OAAOC,QAAQ,IACfD,OAAOC,QAAQ,CAACC,aAAa,AAAD,EAC7B;AAED,OAAO,MAAMC,mBAAmB;IAC9B,MAAMC,gBAAgBC,QAAQC,GAAG,CAACC,sBAAsB,GACpD,CAAC,QAAQ,EAAEF,QAAQC,GAAG,CAACC,sBAAsB,EAAE,GAC/CF,QAAQC,GAAG,CAACE,6BAA6B,GACvC,CAAC,QAAQ,EAAEH,QAAQC,GAAG,CAACE,6BAA6B,EAAE,GACtDH,QAAQC,GAAG,CAACG,mBAAmB,GAC7B,CAAC,OAAO,EAAEJ,QAAQC,GAAG,CAACG,mBAAmB,EAAE,GAC3C;IACR,qFAAqF;IACrF,iDAAiD;IACjD,OAAOL;AACT,EAAC;AAED,OAAO,MAAMM,mBAAmB;IAC9B,IAAIX,WAAW;QACb,MAAMY,WAAWX,OAAOY,QAAQ,CAACD,QAAQ;QACzC,MAAME,SAASb,OAAOY,QAAQ,CAACE,QAAQ;QACvC,MAAMC,OAAOf,OAAOY,QAAQ,CAACG,IAAI;QACjC,wDAAwD;QACxD,MAAMC,gBAAgB,GAAGL,SAAS,EAAE,EAAEE,SAASE,OAAO,CAAC,CAAC,EAAEA,MAAM,GAAG,IAAI;QACvE,iDAAiD;QACjD,OAAOC;IACT;IAEA,OAAOb;AACT,EAAC;AAED,OAAO,MAAMc,YAAYP,mBAAkB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/testData.ts"],"sourcesContent":["export const testEmail = 'seeded-by-plugin@crume.org'\nexport const getTestEmail = () => testEmail\n"],"names":["testEmail","getTestEmail"],"mappings":"AAAA,OAAO,MAAMA,YAAY,6BAA4B;AACrD,OAAO,MAAMC,eAAe,IAAMD,UAAS"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
export const getTokenAndHash = (milliseconds)=>{
|
|
3
|
+
const token = crypto.randomBytes(32).toString('hex');
|
|
4
|
+
const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
|
|
5
|
+
const expiresAt = milliseconds ? new Date(Date.now() + milliseconds) : undefined;
|
|
6
|
+
return {
|
|
7
|
+
expiresAt,
|
|
8
|
+
token,
|
|
9
|
+
tokenHash
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export const getHash = (token)=>{
|
|
13
|
+
const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
|
|
14
|
+
return {
|
|
15
|
+
token,
|
|
16
|
+
tokenHash
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/token.ts"],"sourcesContent":["import crypto from 'crypto'\n\nexport const getTokenAndHash = (milliseconds?: number) => {\n const token = crypto.randomBytes(32).toString('hex')\n const tokenHash = crypto.createHash('sha256').update(token).digest('hex')\n const expiresAt = milliseconds ? new Date(Date.now() + milliseconds) : undefined\n\n return { expiresAt, token, tokenHash }\n}\n\nexport const getHash = (token: string) => {\n const tokenHash = crypto.createHash('sha256').update(token).digest('hex')\n return { token, tokenHash }\n}\n"],"names":["crypto","getTokenAndHash","milliseconds","token","randomBytes","toString","tokenHash","createHash","update","digest","expiresAt","Date","now","undefined","getHash"],"mappings":"AAAA,OAAOA,YAAY,SAAQ;AAE3B,OAAO,MAAMC,kBAAkB,CAACC;IAC9B,MAAMC,QAAQH,OAAOI,WAAW,CAAC,IAAIC,QAAQ,CAAC;IAC9C,MAAMC,YAAYN,OAAOO,UAAU,CAAC,UAAUC,MAAM,CAACL,OAAOM,MAAM,CAAC;IACnE,MAAMC,YAAYR,eAAe,IAAIS,KAAKA,KAAKC,GAAG,KAAKV,gBAAgBW;IAEvE,OAAO;QAAEH;QAAWP;QAAOG;IAAU;AACvC,EAAC;AAED,OAAO,MAAMQ,UAAU,CAACX;IACtB,MAAMG,YAAYN,OAAOO,UAAU,CAAC,UAAUC,MAAM,CAACL,OAAOM,MAAM,CAAC;IACnE,OAAO;QAAEN;QAAOG;IAAU;AAC5B,EAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import OptInChannels from '../collections/OptInChannels.js';
|
|
2
|
+
export const verifyOptIns = async (payload, optIns)=>{
|
|
3
|
+
if (optIns) {
|
|
4
|
+
//
|
|
5
|
+
// Get all matching OptInChannels
|
|
6
|
+
const optInChannelResults = await payload.find({
|
|
7
|
+
collection: OptInChannels.slug,
|
|
8
|
+
where: {
|
|
9
|
+
id: {
|
|
10
|
+
in: optIns
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
const verifiedOptInIDs = optInChannelResults.docs.map((channel)=>channel.id);
|
|
15
|
+
//
|
|
16
|
+
// Separate all non-matching OptInChannels
|
|
17
|
+
const checkInvalidOptInsInput = optIns?.filter((channelID)=>!verifiedOptInIDs.includes(channelID));
|
|
18
|
+
const invalidOptInsInput = checkInvalidOptInsInput.length > 0 ? checkInvalidOptInsInput : undefined;
|
|
19
|
+
// req.payload.logger.info(`optIns = ${JSON.stringify(optIns)}`)
|
|
20
|
+
// req.payload.logger.info(`invalidOptInsInput = ${JSON.stringify(invalidOptInsInput)}`)
|
|
21
|
+
// req.payload.logger.info(`verifiedOptInIDs = ${JSON.stringify(verifiedOptInIDs)}`)
|
|
22
|
+
return {
|
|
23
|
+
invalidOptInsInput,
|
|
24
|
+
verifiedOptInIDs
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
invalidOptInsInput: undefined,
|
|
29
|
+
verifiedOptInIDs: undefined
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=verifyOptIns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/verifyOptIns.ts"],"sourcesContent":["import type { BasePayload, CollectionSlug } from 'payload'\n\nimport OptInChannels from '../collections/OptInChannels.js'\n\nexport const verifyOptIns = async (\n payload: BasePayload,\n optIns?: string[],\n): Promise<{\n invalidOptInsInput: string[] | undefined\n verifiedOptInIDs: string[] | undefined\n}> => {\n if (optIns) {\n //\n // Get all matching OptInChannels\n const optInChannelResults = await payload.find({\n collection: OptInChannels.slug as CollectionSlug,\n where: {\n id: { in: optIns },\n },\n })\n const verifiedOptInIDs: string[] | undefined = optInChannelResults.docs.map(\n (channel) => channel.id,\n )\n\n //\n // Separate all non-matching OptInChannels\n const checkInvalidOptInsInput: string[] | undefined = optIns?.filter(\n (channelID) => !verifiedOptInIDs.includes(channelID),\n )\n const invalidOptInsInput: string[] | undefined =\n checkInvalidOptInsInput.length > 0 ? checkInvalidOptInsInput : undefined\n\n // req.payload.logger.info(`optIns = ${JSON.stringify(optIns)}`)\n // req.payload.logger.info(`invalidOptInsInput = ${JSON.stringify(invalidOptInsInput)}`)\n // req.payload.logger.info(`verifiedOptInIDs = ${JSON.stringify(verifiedOptInIDs)}`)\n return { invalidOptInsInput, verifiedOptInIDs }\n }\n return { invalidOptInsInput: undefined, verifiedOptInIDs: undefined }\n}\n"],"names":["OptInChannels","verifyOptIns","payload","optIns","optInChannelResults","find","collection","slug","where","id","in","verifiedOptInIDs","docs","map","channel","checkInvalidOptInsInput","filter","channelID","includes","invalidOptInsInput","length","undefined"],"mappings":"AAEA,OAAOA,mBAAmB,kCAAiC;AAE3D,OAAO,MAAMC,eAAe,OAC1BC,SACAC;IAKA,IAAIA,QAAQ;QACV,EAAE;QACF,iCAAiC;QACjC,MAAMC,sBAAsB,MAAMF,QAAQG,IAAI,CAAC;YAC7CC,YAAYN,cAAcO,IAAI;YAC9BC,OAAO;gBACLC,IAAI;oBAAEC,IAAIP;gBAAO;YACnB;QACF;QACA,MAAMQ,mBAAyCP,oBAAoBQ,IAAI,CAACC,GAAG,CACzE,CAACC,UAAYA,QAAQL,EAAE;QAGzB,EAAE;QACF,0CAA0C;QAC1C,MAAMM,0BAAgDZ,QAAQa,OAC5D,CAACC,YAAc,CAACN,iBAAiBO,QAAQ,CAACD;QAE5C,MAAME,qBACJJ,wBAAwBK,MAAM,GAAG,IAAIL,0BAA0BM;QAEjE,gEAAgE;QAChE,wFAAwF;QACxF,oFAAoF;QACpF,OAAO;YAAEF;YAAoBR;QAAiB;IAChD;IACA,OAAO;QAAEQ,oBAAoBE;QAAWV,kBAAkBU;IAAU;AACtE,EAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CollectionSlug, Config } from 'payload';
|
|
2
|
+
export type PayloadSubscribersConfig = {
|
|
3
|
+
/**
|
|
4
|
+
* List of collections to add a custom field
|
|
5
|
+
*/
|
|
6
|
+
collections?: Partial<Record<CollectionSlug, true>>;
|
|
7
|
+
/**
|
|
8
|
+
* Defaults to false-y. When true:
|
|
9
|
+
* - Database schema changes are still made and seeded
|
|
10
|
+
* - APIs return null or undefined success
|
|
11
|
+
* - Admin components are not added
|
|
12
|
+
* - App components return nothing
|
|
13
|
+
*/
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The collection to make the subscribers collection
|
|
17
|
+
* - Set auth if not
|
|
18
|
+
* - Add optIns field
|
|
19
|
+
*/
|
|
20
|
+
subscribersCollectionSlug?: CollectionSlug;
|
|
21
|
+
/**
|
|
22
|
+
* Defaults to 30 minutes
|
|
23
|
+
*/
|
|
24
|
+
tokenExpiration?: number;
|
|
25
|
+
};
|
|
26
|
+
export declare const payloadSubscribersPlugin: (pluginOptions: PayloadSubscribersConfig) => (config: Config) => Config;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { OptedInChannels } from './collections/fields/OptedInChannels.js';
|
|
2
|
+
import OptInChannels from './collections/OptInChannels.js';
|
|
3
|
+
import { defaultTokenExpiration, SubscribersCollectionFactory, subscribersCollectionFields } from './collections/Subscribers.js';
|
|
4
|
+
import getOptInChannelsEndpoint from './endpoints/getOptInChannels.js';
|
|
5
|
+
import createEndpointLogout from './endpoints/logout.js';
|
|
6
|
+
import createEndpointRequestMagicLink from './endpoints/requestMagicLink.js';
|
|
7
|
+
import createEndpointSubscribe from './endpoints/subscribe.js';
|
|
8
|
+
import createEndpointSubscriberAuth from './endpoints/subscriberAuth.js';
|
|
9
|
+
import createEndpointVerifyMagicLink from './endpoints/verifyMagicLink.js';
|
|
10
|
+
import { getTestEmail } from './helpers/testData.js';
|
|
11
|
+
import { getTokenAndHash } from './helpers/token.js';
|
|
12
|
+
export const payloadSubscribersPlugin = (pluginOptions)=>(config)=>{
|
|
13
|
+
if (!config.collections) {
|
|
14
|
+
config.collections = [];
|
|
15
|
+
}
|
|
16
|
+
config.collections.push(OptInChannels);
|
|
17
|
+
let subscribersCollection = pluginOptions.subscribersCollectionSlug ? config.collections.find((collection)=>collection.slug == pluginOptions.subscribersCollectionSlug) : undefined;
|
|
18
|
+
if (subscribersCollection) {
|
|
19
|
+
// Configure the input collection to be the subscribers collection
|
|
20
|
+
config.collections = config.collections.filter((collection)=>collection.slug != subscribersCollection?.slug);
|
|
21
|
+
subscribersCollection.fields.push(...subscribersCollectionFields);
|
|
22
|
+
if (!subscribersCollection.auth) {
|
|
23
|
+
subscribersCollection = {
|
|
24
|
+
...subscribersCollection,
|
|
25
|
+
auth: {
|
|
26
|
+
tokenExpiration: defaultTokenExpiration
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (!subscribersCollection.admin?.useAsTitle) {
|
|
31
|
+
if (!subscribersCollection.admin) {
|
|
32
|
+
subscribersCollection.admin = {
|
|
33
|
+
useAsTitle: 'email'
|
|
34
|
+
};
|
|
35
|
+
} else {
|
|
36
|
+
// Throw error? Or override?
|
|
37
|
+
subscribersCollection.admin.useAsTitle = 'email';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
config.collections.push(subscribersCollection);
|
|
41
|
+
} else {
|
|
42
|
+
// Configure the default built-in subscribers collection
|
|
43
|
+
subscribersCollection = SubscribersCollectionFactory({
|
|
44
|
+
slug: pluginOptions.subscribersCollectionSlug,
|
|
45
|
+
tokenExpiration: pluginOptions.tokenExpiration
|
|
46
|
+
});
|
|
47
|
+
config.collections.push(subscribersCollection);
|
|
48
|
+
}
|
|
49
|
+
if (pluginOptions.collections) {
|
|
50
|
+
for(const collectionSlug in pluginOptions.collections){
|
|
51
|
+
const collection = config.collections.find((collection)=>collection.slug === collectionSlug);
|
|
52
|
+
if (collection) {
|
|
53
|
+
collection.fields.push(OptedInChannels);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* If the plugin is disabled, we still want to keep added collections/fields so the database schema is consistent which is important for migrations.
|
|
59
|
+
* If your plugin heavily modifies the database schema, you may want to remove this property.
|
|
60
|
+
*/ if (pluginOptions.disabled) {
|
|
61
|
+
return config;
|
|
62
|
+
}
|
|
63
|
+
if (!config.admin) {
|
|
64
|
+
config.admin = {};
|
|
65
|
+
}
|
|
66
|
+
if (!config.admin.components) {
|
|
67
|
+
config.admin.components = {};
|
|
68
|
+
}
|
|
69
|
+
if (!config.admin.components.beforeDashboard) {
|
|
70
|
+
config.admin.components.beforeDashboard = [];
|
|
71
|
+
}
|
|
72
|
+
if (!config.endpoints) {
|
|
73
|
+
config.endpoints = [];
|
|
74
|
+
}
|
|
75
|
+
config.endpoints.push(getOptInChannelsEndpoint, createEndpointLogout({
|
|
76
|
+
subscribersCollectionSlug: subscribersCollection.slug
|
|
77
|
+
}), createEndpointRequestMagicLink({
|
|
78
|
+
subscribersCollectionSlug: subscribersCollection.slug
|
|
79
|
+
}), createEndpointSubscribe({
|
|
80
|
+
subscribersCollectionSlug: subscribersCollection.slug
|
|
81
|
+
}), createEndpointSubscriberAuth({
|
|
82
|
+
subscribersCollectionSlug: subscribersCollection.slug
|
|
83
|
+
}), createEndpointVerifyMagicLink({
|
|
84
|
+
subscribersCollectionSlug: subscribersCollection.slug
|
|
85
|
+
}));
|
|
86
|
+
const incomingOnInit = config.onInit;
|
|
87
|
+
const genInit = (testData)=>async (payload)=>{
|
|
88
|
+
// Ensure we are executing any existing onInit functions before running our own.
|
|
89
|
+
if (incomingOnInit) {
|
|
90
|
+
await incomingOnInit(payload);
|
|
91
|
+
}
|
|
92
|
+
// console.log('Object.keys(payload.collections)', Object.keys(payload.collections))
|
|
93
|
+
const { totalDocs: totalOptIns } = await payload.count({
|
|
94
|
+
collection: 'opt-in-channels',
|
|
95
|
+
where: {
|
|
96
|
+
title: {
|
|
97
|
+
equals: 'seeded-by-plugin'
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
if (totalOptIns === 0) {
|
|
102
|
+
await payload.create({
|
|
103
|
+
collection: 'opt-in-channels',
|
|
104
|
+
data: {
|
|
105
|
+
active: true,
|
|
106
|
+
title: 'seeded-by-plugin'
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// const { seededChannel } = await payload.find({
|
|
111
|
+
// collection: 'opt-in-channels',
|
|
112
|
+
// where: {
|
|
113
|
+
// title: {
|
|
114
|
+
// equals: 'seeded-by-plugin',
|
|
115
|
+
// },
|
|
116
|
+
// },
|
|
117
|
+
// })
|
|
118
|
+
const { totalDocs: totalSubscribers } = await payload.count({
|
|
119
|
+
collection: subscribersCollection.slug,
|
|
120
|
+
where: {
|
|
121
|
+
email: {
|
|
122
|
+
equals: testData.testEmail
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
const { tokenHash } = getTokenAndHash() // Unknowable
|
|
127
|
+
;
|
|
128
|
+
// payload.logger.info(`testData.testEmail == '${testData.testEmail}'`)
|
|
129
|
+
if (totalSubscribers === 0) {
|
|
130
|
+
await payload.create({
|
|
131
|
+
collection: subscribersCollection.slug,
|
|
132
|
+
data: {
|
|
133
|
+
email: testData.testEmail,
|
|
134
|
+
password: tokenHash,
|
|
135
|
+
status: 'pending'
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
// console.log(`getTestEmail == '${getTestEmail()}'`)
|
|
141
|
+
config.onInit = genInit({
|
|
142
|
+
testEmail: getTestEmail()
|
|
143
|
+
});
|
|
144
|
+
return config;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { BasePayload, CollectionSlug, Config } from 'payload'\n\nimport { OptedInChannels } from './collections/fields/OptedInChannels.js'\nimport OptInChannels from './collections/OptInChannels.js'\nimport {\n defaultTokenExpiration,\n SubscribersCollectionFactory,\n subscribersCollectionFields,\n} from './collections/Subscribers.js'\nimport getOptInChannelsEndpoint from './endpoints/getOptInChannels.js'\nimport createEndpointLogout from './endpoints/logout.js'\nimport createEndpointRequestMagicLink from './endpoints/requestMagicLink.js'\nimport createEndpointSubscribe from './endpoints/subscribe.js'\nimport createEndpointSubscriberAuth from './endpoints/subscriberAuth.js'\nimport createEndpointVerifyMagicLink from './endpoints/verifyMagicLink.js'\nimport { getTestEmail } from './helpers/testData.js'\nimport { getTokenAndHash } from './helpers/token.js'\n\nexport type PayloadSubscribersConfig = {\n /**\n * List of collections to add a custom field\n */\n collections?: Partial<Record<CollectionSlug, true>>\n /**\n * Defaults to false-y. When true:\n * - Database schema changes are still made and seeded\n * - APIs return null or undefined success\n * - Admin components are not added\n * - App components return nothing\n */\n disabled?: boolean\n /**\n * The collection to make the subscribers collection\n * - Set auth if not\n * - Add optIns field\n */\n subscribersCollectionSlug?: CollectionSlug\n /**\n * Defaults to 30 minutes\n */\n tokenExpiration?: number\n}\n\nexport const payloadSubscribersPlugin =\n (pluginOptions: PayloadSubscribersConfig) =>\n (config: Config): Config => {\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(OptInChannels)\n let subscribersCollection = pluginOptions.subscribersCollectionSlug\n ? config.collections.find(\n (collection) => collection.slug == pluginOptions.subscribersCollectionSlug,\n )\n : undefined\n\n if (subscribersCollection) {\n // Configure the input collection to be the subscribers collection\n config.collections = config.collections.filter(\n (collection) => collection.slug != subscribersCollection?.slug,\n )\n subscribersCollection.fields.push(...subscribersCollectionFields)\n if (!subscribersCollection.auth) {\n subscribersCollection = {\n ...subscribersCollection,\n auth: { tokenExpiration: defaultTokenExpiration },\n }\n }\n if (!subscribersCollection.admin?.useAsTitle) {\n if (!subscribersCollection.admin) {\n subscribersCollection.admin = { useAsTitle: 'email' }\n } else {\n // Throw error? Or override?\n subscribersCollection.admin.useAsTitle = 'email'\n }\n }\n config.collections.push(subscribersCollection)\n } else {\n // Configure the default built-in subscribers collection\n subscribersCollection = SubscribersCollectionFactory({\n slug: pluginOptions.subscribersCollectionSlug,\n tokenExpiration: pluginOptions.tokenExpiration,\n })\n config.collections.push(subscribersCollection)\n }\n\n if (pluginOptions.collections) {\n for (const collectionSlug in pluginOptions.collections) {\n const collection = config.collections.find(\n (collection) => collection.slug === collectionSlug,\n )\n\n if (collection) {\n collection.fields.push(OptedInChannels)\n }\n }\n }\n\n /**\n * If the plugin is disabled, we still want to keep added collections/fields so the database schema is consistent which is important for migrations.\n * If your plugin heavily modifies the database schema, you may want to remove this property.\n */\n if (pluginOptions.disabled) {\n return config\n }\n\n if (!config.admin) {\n config.admin = {}\n }\n\n if (!config.admin.components) {\n config.admin.components = {}\n }\n\n if (!config.admin.components.beforeDashboard) {\n config.admin.components.beforeDashboard = []\n }\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n config.endpoints.push(\n getOptInChannelsEndpoint,\n createEndpointLogout({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointRequestMagicLink({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointSubscribe({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointSubscriberAuth({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointVerifyMagicLink({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n )\n\n const incomingOnInit = config.onInit\n\n const genInit = (testData: { testEmail: string }) => async (payload: BasePayload) => {\n // Ensure we are executing any existing onInit functions before running our own.\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n\n // console.log('Object.keys(payload.collections)', Object.keys(payload.collections))\n const { totalDocs: totalOptIns } = await payload.count({\n collection: 'opt-in-channels',\n where: {\n title: {\n equals: 'seeded-by-plugin',\n },\n },\n })\n\n if (totalOptIns === 0) {\n await payload.create({\n collection: 'opt-in-channels',\n data: {\n active: true,\n title: 'seeded-by-plugin',\n },\n })\n }\n\n // const { seededChannel } = await payload.find({\n // collection: 'opt-in-channels',\n // where: {\n // title: {\n // equals: 'seeded-by-plugin',\n // },\n // },\n // })\n\n const { totalDocs: totalSubscribers } = await payload.count({\n collection: subscribersCollection.slug as CollectionSlug,\n where: {\n email: {\n equals: testData.testEmail,\n },\n },\n })\n\n const { tokenHash } = getTokenAndHash() // Unknowable\n // payload.logger.info(`testData.testEmail == '${testData.testEmail}'`)\n if (totalSubscribers === 0) {\n await payload.create({\n collection: subscribersCollection.slug as CollectionSlug,\n data: {\n email: testData.testEmail,\n password: tokenHash,\n status: 'pending',\n },\n })\n }\n }\n\n // console.log(`getTestEmail == '${getTestEmail()}'`)\n config.onInit = genInit({ testEmail: getTestEmail() })\n\n return config\n }\n"],"names":["OptedInChannels","OptInChannels","defaultTokenExpiration","SubscribersCollectionFactory","subscribersCollectionFields","getOptInChannelsEndpoint","createEndpointLogout","createEndpointRequestMagicLink","createEndpointSubscribe","createEndpointSubscriberAuth","createEndpointVerifyMagicLink","getTestEmail","getTokenAndHash","payloadSubscribersPlugin","pluginOptions","config","collections","push","subscribersCollection","subscribersCollectionSlug","find","collection","slug","undefined","filter","fields","auth","tokenExpiration","admin","useAsTitle","collectionSlug","disabled","components","beforeDashboard","endpoints","incomingOnInit","onInit","genInit","testData","payload","totalDocs","totalOptIns","count","where","title","equals","create","data","active","totalSubscribers","email","testEmail","tokenHash","password","status"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { getServerUrl } from '../server-functions/serverUrl.js';
|
|
4
|
+
// Custom hook to easily consume the context and add error handling
|
|
5
|
+
export function useServerUrl() {
|
|
6
|
+
const [serverURL, setServerURL] = useState();
|
|
7
|
+
useEffect(()=>{
|
|
8
|
+
const fetchServerUrl = async ()=>{
|
|
9
|
+
const { serverURL } = await getServerUrl();
|
|
10
|
+
setServerURL(serverURL);
|
|
11
|
+
};
|
|
12
|
+
void fetchServerUrl();
|
|
13
|
+
}, []);
|
|
14
|
+
return {
|
|
15
|
+
serverURL
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=useServerUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react-hooks/useServerUrl.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useState } from 'react'\n\nimport { getServerUrl } from '../server-functions/serverUrl.js'\n\n// Custom hook to easily consume the context and add error handling\nexport function useServerUrl() {\n const [serverURL, setServerURL] = useState<string>()\n useEffect(() => {\n const fetchServerUrl = async () => {\n const { serverURL } = await getServerUrl()\n setServerURL(serverURL)\n }\n void fetchServerUrl()\n }, [])\n return { serverURL }\n}\n"],"names":["useEffect","useState","getServerUrl","useServerUrl","serverURL","setServerURL","fetchServerUrl"],"mappings":"AAAA;AAEA,SAASA,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAE3C,SAASC,YAAY,QAAQ,mCAAkC;AAE/D,mEAAmE;AACnE,OAAO,SAASC;IACd,MAAM,CAACC,WAAWC,aAAa,GAAGJ;IAClCD,UAAU;QACR,MAAMM,iBAAiB;YACrB,MAAM,EAAEF,SAAS,EAAE,GAAG,MAAMF;YAC5BG,aAAaD;QACf;QACA,KAAKE;IACP,GAAG,EAAE;IACL,OAAO;QAAEF;IAAU;AACrB"}
|