payload-subscribers-plugin 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +305 -0
  2. package/dist/collections/OptInChannels.d.ts +3 -0
  3. package/dist/collections/OptInChannels.js +44 -0
  4. package/dist/collections/OptInChannels.js.map +1 -0
  5. package/dist/collections/Subscribers.d.ts +8 -0
  6. package/dist/collections/Subscribers.js +88 -0
  7. package/dist/collections/Subscribers.js.map +1 -0
  8. package/dist/collections/fields/OptedInChannels.d.ts +2 -0
  9. package/dist/collections/fields/OptedInChannels.js +12 -0
  10. package/dist/collections/fields/OptedInChannels.js.map +1 -0
  11. package/dist/components/BeforeDashboardClient.d.ts +1 -0
  12. package/dist/components/BeforeDashboardClient.js +40 -0
  13. package/dist/components/BeforeDashboardClient.js.map +1 -0
  14. package/dist/components/BeforeDashboardServer.d.ts +2 -0
  15. package/dist/components/BeforeDashboardServer.js +22 -0
  16. package/dist/components/BeforeDashboardServer.js.map +1 -0
  17. package/dist/components/BeforeDashboardServer.module.css +5 -0
  18. package/dist/components/app/RequestMagicLink.d.ts +16 -0
  19. package/dist/components/app/RequestMagicLink.js +114 -0
  20. package/dist/components/app/RequestMagicLink.js.map +1 -0
  21. package/dist/components/app/RequestMagicLink.module.css +5 -0
  22. package/dist/components/app/RequestOrSubscribe.d.ts +17 -0
  23. package/dist/components/app/RequestOrSubscribe.js +28 -0
  24. package/dist/components/app/RequestOrSubscribe.js.map +1 -0
  25. package/dist/components/app/SelectOptInChannels.d.ts +20 -0
  26. package/dist/components/app/SelectOptInChannels.js +120 -0
  27. package/dist/components/app/SelectOptInChannels.js.map +1 -0
  28. package/dist/components/app/SelectOptInChannels.module.css +5 -0
  29. package/dist/components/app/Subscribe.d.ts +18 -0
  30. package/dist/components/app/Subscribe.js +169 -0
  31. package/dist/components/app/Subscribe.js.map +1 -0
  32. package/dist/components/app/Subscribe.module.css +5 -0
  33. package/dist/components/app/SubscriberMenu.d.ts +7 -0
  34. package/dist/components/app/SubscriberMenu.js +44 -0
  35. package/dist/components/app/SubscriberMenu.js.map +1 -0
  36. package/dist/components/app/VerifyMagicLink.d.ts +23 -0
  37. package/dist/components/app/VerifyMagicLink.js +169 -0
  38. package/dist/components/app/VerifyMagicLink.js.map +1 -0
  39. package/dist/components/app/VerifyMagicLink.module.css +5 -0
  40. package/dist/components/app/helpers.d.ts +1 -0
  41. package/dist/components/app/helpers.js +5 -0
  42. package/dist/components/app/helpers.js.map +1 -0
  43. package/dist/components/app/shared.module.css +14 -0
  44. package/dist/contexts/SubscriberProvider.d.ts +15 -0
  45. package/dist/contexts/SubscriberProvider.js +105 -0
  46. package/dist/contexts/SubscriberProvider.js.map +1 -0
  47. package/dist/copied/payload-types.d.ts +395 -0
  48. package/dist/copied/payload-types.js +15 -0
  49. package/dist/copied/payload-types.js.map +1 -0
  50. package/dist/copied/payload.config.d.ts +2 -0
  51. package/dist/endpoints/customEndpointHandler.d.ts +2 -0
  52. package/dist/endpoints/customEndpointHandler.js +7 -0
  53. package/dist/endpoints/customEndpointHandler.js.map +1 -0
  54. package/dist/endpoints/getOptInChannels.d.ts +19 -0
  55. package/dist/endpoints/getOptInChannels.js +42 -0
  56. package/dist/endpoints/getOptInChannels.js.map +1 -0
  57. package/dist/endpoints/logout.d.ts +20 -0
  58. package/dist/endpoints/logout.js +60 -0
  59. package/dist/endpoints/logout.js.map +1 -0
  60. package/dist/endpoints/requestMagicLink.d.ts +20 -0
  61. package/dist/endpoints/requestMagicLink.js +122 -0
  62. package/dist/endpoints/requestMagicLink.js.map +1 -0
  63. package/dist/endpoints/subscribe.d.ts +24 -0
  64. package/dist/endpoints/subscribe.js +343 -0
  65. package/dist/endpoints/subscribe.js.map +1 -0
  66. package/dist/endpoints/subscriberAuth.d.ts +22 -0
  67. package/dist/endpoints/subscriberAuth.js +69 -0
  68. package/dist/endpoints/subscriberAuth.js.map +1 -0
  69. package/dist/endpoints/verifyMagicLink.d.ts +20 -0
  70. package/dist/endpoints/verifyMagicLink.js +142 -0
  71. package/dist/endpoints/verifyMagicLink.js.map +1 -0
  72. package/dist/exports/client.d.ts +1 -0
  73. package/dist/exports/client.js +3 -0
  74. package/dist/exports/client.js.map +1 -0
  75. package/dist/exports/index.d.ts +1 -0
  76. package/dist/exports/index.js +3 -0
  77. package/dist/exports/index.js.map +1 -0
  78. package/dist/exports/rsc.d.ts +1 -0
  79. package/dist/exports/rsc.js +3 -0
  80. package/dist/exports/rsc.js.map +1 -0
  81. package/dist/exports/ui.d.ts +11 -0
  82. package/dist/exports/ui.js +9 -0
  83. package/dist/exports/ui.js.map +1 -0
  84. package/dist/helpers/serverConfig.d.ts +4 -0
  85. package/dist/helpers/serverConfig.js +22 -0
  86. package/dist/helpers/serverConfig.js.map +1 -0
  87. package/dist/helpers/testData.d.ts +2 -0
  88. package/dist/helpers/testData.js +4 -0
  89. package/dist/helpers/testData.js.map +1 -0
  90. package/dist/helpers/token.d.ts +9 -0
  91. package/dist/helpers/token.js +20 -0
  92. package/dist/helpers/token.js.map +1 -0
  93. package/dist/helpers/verifyOptIns.d.ts +5 -0
  94. package/dist/helpers/verifyOptIns.js +33 -0
  95. package/dist/helpers/verifyOptIns.js.map +1 -0
  96. package/dist/index.d.ts +26 -0
  97. package/dist/index.js +147 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/react-hooks/useServerUrl.d.ts +3 -0
  100. package/dist/react-hooks/useServerUrl.js +19 -0
  101. package/dist/react-hooks/useServerUrl.js.map +1 -0
  102. package/dist/server-functions/serverUrl.d.ts +3 -0
  103. package/dist/server-functions/serverUrl.js +31 -0
  104. package/dist/server-functions/serverUrl.js.map +1 -0
  105. package/dist/server-functions/subscriberAuth.d.ts +11 -0
  106. package/package.json +94 -0
  107. package/src/collections/OptInChannels.ts +45 -0
  108. package/src/collections/Subscribers.ts +99 -0
  109. package/src/collections/fields/OptedInChannels.ts +12 -0
  110. package/src/components/app/RequestMagicLink.tsx +129 -0
  111. package/src/components/app/RequestOrSubscribe.tsx +58 -0
  112. package/src/components/app/SelectOptInChannels.tsx +147 -0
  113. package/src/components/app/Subscribe.tsx +190 -0
  114. package/src/components/app/SubscriberMenu.tsx +46 -0
  115. package/src/components/app/VerifyMagicLink.tsx +197 -0
  116. package/src/components/app/helpers.ts +6 -0
  117. package/src/components/app/shared.module.css +14 -0
  118. package/src/contexts/SubscriberProvider.tsx +122 -0
  119. package/src/copied/payload-types.ts +478 -0
  120. package/src/endpoints/getOptInChannels.ts +56 -0
  121. package/src/endpoints/logout.ts +104 -0
  122. package/src/endpoints/requestMagicLink.ts +139 -0
  123. package/src/endpoints/subscribe.ts +435 -0
  124. package/src/endpoints/subscriberAuth.ts +100 -0
  125. package/src/endpoints/verifyMagicLink.ts +164 -0
  126. package/src/exports/index.ts +1 -0
  127. package/src/exports/ui.ts +17 -0
  128. package/src/helpers/testData.ts +2 -0
  129. package/src/helpers/token.ts +14 -0
  130. package/src/helpers/verifyOptIns.ts +39 -0
  131. package/src/index.ts +207 -0
  132. package/src/react-hooks/useServerUrl.tsx +18 -0
  133. package/src/server-functions/serverUrl.ts +38 -0
@@ -0,0 +1,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,3 @@
1
+ export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js';
2
+
3
+ //# sourceMappingURL=client.js.map
@@ -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,3 @@
1
+ export * from './ui.js';
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -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,3 @@
1
+ export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js';
2
+
3
+ //# sourceMappingURL=rsc.js.map
@@ -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,4 @@
1
+ export declare const canUseDOM: boolean;
2
+ export declare const getServerSideURL: () => string;
3
+ export declare const getClientSideURL: () => string;
4
+ export declare const serverURL: string;
@@ -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,2 @@
1
+ export declare const testEmail = "seeded-by-plugin@crume.org";
2
+ export declare const getTestEmail: () => string;
@@ -0,0 +1,4 @@
1
+ export const testEmail = 'seeded-by-plugin@crume.org';
2
+ export const getTestEmail = ()=>testEmail;
3
+
4
+ //# sourceMappingURL=testData.js.map
@@ -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,9 @@
1
+ export declare const getTokenAndHash: (milliseconds?: number) => {
2
+ expiresAt: Date | undefined;
3
+ token: string;
4
+ tokenHash: string;
5
+ };
6
+ export declare const getHash: (token: string) => {
7
+ token: string;
8
+ tokenHash: string;
9
+ };
@@ -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,5 @@
1
+ import type { BasePayload } from 'payload';
2
+ export declare const verifyOptIns: (payload: BasePayload, optIns?: string[]) => Promise<{
3
+ invalidOptInsInput: string[] | undefined;
4
+ verifiedOptInIDs: string[] | undefined;
5
+ }>;
@@ -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"}
@@ -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,3 @@
1
+ export declare function useServerUrl(): {
2
+ serverURL: string | undefined;
3
+ };
@@ -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"}
@@ -0,0 +1,3 @@
1
+ export declare const getServerUrl: () => Promise<{
2
+ serverURL: string;
3
+ }>;