payload-subscribers-plugin 0.0.4 → 0.0.6

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 CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  A plugin to manage subscribers and the "channels" they can subscribe to.
4
4
 
5
+ This includes ways to allow your subscribers to:
6
+ * Sign up or sign in by requesting a magic link email
7
+ * Verify the magic link to authenticate
8
+ * Opt in or out of "opt-in channels"
9
+
10
+ You manage the opt-in channels via the Payload admin.
11
+
12
+ The plugin relies on your email adapter configured in your payload config to send emails.
13
+
14
+ That is all this plugin does currently. Potential features might include email authoring and send scheduler or simple CRM features.
15
+
16
+
5
17
  ## Installation
6
18
 
7
19
  ```bash
@@ -10,6 +22,8 @@ pnpm add payload-subscribers-plugin
10
22
 
11
23
  ## Usage
12
24
 
25
+ You need to have an email adapter configured in your Payload config.
26
+
13
27
  Add the plugin to your Payload config.
14
28
 
15
29
  ```typescript
@@ -18,12 +32,22 @@ Add the plugin to your Payload config.
18
32
  export default buildConfig({
19
33
  plugins: [
20
34
  payloadSubscribersPlugin({
35
+
36
+ // Add slugs of your collections which should have a relationship field to the optInChannels.
21
37
  collections: {
22
- // Add slugs of your collections which should have a relationship field to the optInChannels.
23
38
  posts: true,
24
39
  },
40
+
25
41
  // Easily disable the collection logic.
26
42
  disabled: false,
43
+
44
+ // Specify the collection to use as the subscribers collection
45
+ // - Optional. If not specified, the plugin will add a 'subscribers' collection.
46
+ // - Sets auth if not already
47
+ // - Adds (or overrides) fields: email, firstName, status, optIns,
48
+ // verificationToken, verificationTokenExpires, and source
49
+ subscribersCollectionSlug?: CollectionSlug
50
+
27
51
  // Provide a custom expiration for magic link tokens. The default is 30 minutes.
28
52
  tokenExpiration: 60 * 60,
29
53
  }),
@@ -172,7 +196,7 @@ The **subscribe** endpoint will remove all optIns. But need a way to set the sub
172
196
 
173
197
  #### **RequestOrSubscribe**
174
198
 
175
- Shows Subscribe to authenticated subscribers, otherwise shows RequestMagicLink.
199
+ Shows the [Subscribe](#subscribe) component to authenticated subscribers, otherwise shows [RequestMagicLink](#RequestMagicLink).
176
200
 
177
201
  <!-- <div style="border: 1px solid #ccc; padding: 15px; border-radius: 5px;">
178
202
  </div> -->
@@ -207,6 +231,8 @@ Shows Subscribe to authenticated subscribers, otherwise shows RequestMagicLink.
207
231
 
208
232
  Form to input email address and get a magic link email sent.
209
233
 
234
+ <img src="./docs/images/RequestMagicLink.png" alt="RequestMagicLink" style="max-width: 500px; width: 100%;">
235
+
210
236
  ```typescript
211
237
  <RequestMagicLink
212
238
  // Provide your own global class names to add to the component elements. Optional
@@ -233,6 +259,8 @@ Form to input email address and get a magic link email sent.
233
259
 
234
260
  Component that verifies a magic link using expected url parameters.
235
261
 
262
+ <img src="./docs/images/VerifyMagicLink.png" alt="VerifyMagicLink" style="max-width: 500px; width: 100%;">
263
+
236
264
  ```typescript
237
265
  <VerifyMagicLink
238
266
  // Provide your own global class names to add to the component elements. Optional
@@ -269,6 +297,8 @@ Component that verifies a magic link using expected url parameters.
269
297
 
270
298
  Allows a subscriber to select from among all active optInChannels.
271
299
 
300
+ <img src="./docs/images/Subscribe.png" alt="Subscribe" style="max-width: 500px; width: 100%;">
301
+
272
302
  ```typescript
273
303
  <Subscribe
274
304
  // Provide your own global class names to add to the component elements. Optional
@@ -295,6 +325,8 @@ Allows a subscriber to select from among all active optInChannels.
295
325
 
296
326
  #### **SubscriberMenu**
297
327
 
328
+ A simple user menu, most useful for testing. Seen in the screenshots above. Includes a "welcome" message, a link to a /subscribe route, and a log out button.
329
+
298
330
  ```typescript
299
331
  // classNames prop
300
332
 
@@ -303,3 +335,7 @@ export type SubscriberMenuClasses = {
303
335
  container?: string
304
336
  }
305
337
  ```
338
+
339
+ ## Contributing
340
+
341
+ Community contributions are welcome! I haven't organized around that yet, so let me know if you're interested by opening an issue on the GitHub repo.
@@ -59,11 +59,6 @@ export const subscribersCollectionFields = [
59
59
  ],
60
60
  required: true
61
61
  },
62
- {
63
- name: 'source',
64
- type: 'text',
65
- label: 'Signup Source'
66
- },
67
62
  {
68
63
  name: 'verificationToken',
69
64
  type: 'text',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collections/Subscribers.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug, Field } from 'payload'\n\nimport { OptedInChannels } from './fields/OptedInChannels.js'\n\nexport const defaultTokenExpiration = 30 * 60 // 30 minutes\n\nexport const defaultCollectionSlug = 'subscribers'\n\nexport const SubscribersCollectionFactory = ({\n slug,\n tokenExpiration = defaultTokenExpiration,\n}: {\n slug?: CollectionSlug\n tokenExpiration?: number\n}) => {\n const Subscribers: CollectionConfig = {\n slug: slug ? slug : defaultCollectionSlug,\n access: {\n // Public access for creation (signup form)\n create: () => true,\n // Admin-only access for reading, updating, and deleting\n delete: ({ req }) => (req.user ? true : false),\n read: ({ req }) => (req.user ? true : false),\n update: ({ req }) => (req.user ? true : false),\n },\n admin: { useAsTitle: 'email' },\n auth: {\n tokenExpiration,\n // verify: true, // Require email verification before being allowed to authenticate\n // maxLoginAttempts: 5, // Automatically lock a user out after X amount of failed logins\n // lockTime: 600 * 1000, // Time period to allow the max login attempts\n },\n fields: [...subscribersCollectionFields],\n }\n\n return Subscribers\n}\n\nexport const subscribersCollectionFields: Field[] = [\n {\n name: 'email',\n type: 'email', // Enforces valid email format\n label: 'Email Address',\n required: true,\n unique: true, // Ensures no duplicate emails\n },\n {\n name: 'firstName',\n type: 'text',\n label: 'First Name',\n },\n {\n name: 'status',\n type: 'select',\n defaultValue: 'pending', // Default to pending until verified\n label: 'Subscription Status',\n options: [\n {\n label: 'Subscribed',\n value: 'subscribed',\n },\n {\n label: 'Unsubscribed',\n value: 'unsubscribed',\n },\n {\n label: 'Pending Verification',\n value: 'pending',\n },\n ],\n required: true,\n },\n {\n name: 'source',\n type: 'text', // e.g., 'Homepage form', 'Blog post A', etc.\n label: 'Signup Source',\n },\n {\n name: 'verificationToken',\n type: 'text',\n admin: {\n hidden: true, // Hide this field in the admin panel for security/cleanliness\n },\n label: 'Verification Token',\n },\n {\n name: 'verificationTokenExpires',\n type: 'date',\n admin: {\n hidden: true, // Hide this field in the admin panel for security/cleanliness\n },\n label: 'Verification Token Expiration',\n },\n\n /**\n * Plugin field relationship to optinchannels\n */\n OptedInChannels,\n]\n"],"names":["OptedInChannels","defaultTokenExpiration","defaultCollectionSlug","SubscribersCollectionFactory","slug","tokenExpiration","Subscribers","access","create","delete","req","user","read","update","admin","useAsTitle","auth","fields","subscribersCollectionFields","name","type","label","required","unique","defaultValue","options","value","hidden"],"mappings":"AAEA,SAASA,eAAe,QAAQ,8BAA6B;AAE7D,OAAO,MAAMC,yBAAyB,KAAK,GAAG,aAAa;CAAd;AAE7C,OAAO,MAAMC,wBAAwB,cAAa;AAElD,OAAO,MAAMC,+BAA+B,CAAC,EAC3CC,IAAI,EACJC,kBAAkBJ,sBAAsB,EAIzC;IACC,MAAMK,cAAgC;QACpCF,MAAMA,OAAOA,OAAOF;QACpBK,QAAQ;YACN,2CAA2C;YAC3CC,QAAQ,IAAM;YACd,wDAAwD;YACxDC,QAAQ,CAAC,EAAEC,GAAG,EAAE,GAAMA,IAAIC,IAAI,GAAG,OAAO;YACxCC,MAAM,CAAC,EAAEF,GAAG,EAAE,GAAMA,IAAIC,IAAI,GAAG,OAAO;YACtCE,QAAQ,CAAC,EAAEH,GAAG,EAAE,GAAMA,IAAIC,IAAI,GAAG,OAAO;QAC1C;QACAG,OAAO;YAAEC,YAAY;QAAQ;QAC7BC,MAAM;YACJX;QAIF;QACAY,QAAQ;eAAIC;SAA4B;IAC1C;IAEA,OAAOZ;AACT,EAAC;AAED,OAAO,MAAMY,8BAAuC;IAClD;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;QACPC,UAAU;QACVC,QAAQ;IACV;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;IACT;IACA;QACEF,MAAM;QACNC,MAAM;QACNI,cAAc;QACdH,OAAO;QACPI,SAAS;YACP;gBACEJ,OAAO;gBACPK,OAAO;YACT;YACA;gBACEL,OAAO;gBACPK,OAAO;YACT;YACA;gBACEL,OAAO;gBACPK,OAAO;YACT;SACD;QACDJ,UAAU;IACZ;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;IACT;IACA;QACEF,MAAM;QACNC,MAAM;QACNN,OAAO;YACLa,QAAQ;QACV;QACAN,OAAO;IACT;IACA;QACEF,MAAM;QACNC,MAAM;QACNN,OAAO;YACLa,QAAQ;QACV;QACAN,OAAO;IACT;IAEA;;GAEC,GACDrB;CACD,CAAA"}
1
+ {"version":3,"sources":["../../src/collections/Subscribers.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug, Field } from 'payload'\n\nimport { OptedInChannels } from './fields/OptedInChannels.js'\n\nexport const defaultTokenExpiration = 30 * 60 // 30 minutes\n\nexport const defaultCollectionSlug = 'subscribers'\n\nexport const SubscribersCollectionFactory = ({\n slug,\n tokenExpiration = defaultTokenExpiration,\n}: {\n slug?: CollectionSlug\n tokenExpiration?: number\n}) => {\n const Subscribers: CollectionConfig = {\n slug: slug ? slug : defaultCollectionSlug,\n access: {\n // Public access for creation (signup form)\n create: () => true,\n // Admin-only access for reading, updating, and deleting\n delete: ({ req }) => (req.user ? true : false),\n read: ({ req }) => (req.user ? true : false),\n update: ({ req }) => (req.user ? true : false),\n },\n admin: { useAsTitle: 'email' },\n auth: {\n tokenExpiration,\n // verify: true, // Require email verification before being allowed to authenticate\n // maxLoginAttempts: 5, // Automatically lock a user out after X amount of failed logins\n // lockTime: 600 * 1000, // Time period to allow the max login attempts\n },\n fields: [...subscribersCollectionFields],\n }\n\n return Subscribers\n}\n\nexport const subscribersCollectionFields: Field[] = [\n {\n name: 'email',\n type: 'email', // Enforces valid email format\n label: 'Email Address',\n required: true,\n unique: true, // Ensures no duplicate emails\n },\n {\n name: 'firstName',\n type: 'text',\n label: 'First Name',\n },\n {\n name: 'status',\n type: 'select',\n defaultValue: 'pending', // Default to pending until verified\n label: 'Subscription Status',\n options: [\n {\n label: 'Subscribed',\n value: 'subscribed',\n },\n {\n label: 'Unsubscribed',\n value: 'unsubscribed',\n },\n {\n label: 'Pending Verification',\n value: 'pending',\n },\n ],\n required: true,\n },\n {\n name: 'verificationToken',\n type: 'text',\n admin: {\n hidden: true, // Hide this field in the admin panel for security/cleanliness\n },\n label: 'Verification Token',\n },\n {\n name: 'verificationTokenExpires',\n type: 'date',\n admin: {\n hidden: true, // Hide this field in the admin panel for security/cleanliness\n },\n label: 'Verification Token Expiration',\n },\n\n /**\n * Plugin field relationship to optinchannels\n */\n OptedInChannels,\n]\n"],"names":["OptedInChannels","defaultTokenExpiration","defaultCollectionSlug","SubscribersCollectionFactory","slug","tokenExpiration","Subscribers","access","create","delete","req","user","read","update","admin","useAsTitle","auth","fields","subscribersCollectionFields","name","type","label","required","unique","defaultValue","options","value","hidden"],"mappings":"AAEA,SAASA,eAAe,QAAQ,8BAA6B;AAE7D,OAAO,MAAMC,yBAAyB,KAAK,GAAG,aAAa;CAAd;AAE7C,OAAO,MAAMC,wBAAwB,cAAa;AAElD,OAAO,MAAMC,+BAA+B,CAAC,EAC3CC,IAAI,EACJC,kBAAkBJ,sBAAsB,EAIzC;IACC,MAAMK,cAAgC;QACpCF,MAAMA,OAAOA,OAAOF;QACpBK,QAAQ;YACN,2CAA2C;YAC3CC,QAAQ,IAAM;YACd,wDAAwD;YACxDC,QAAQ,CAAC,EAAEC,GAAG,EAAE,GAAMA,IAAIC,IAAI,GAAG,OAAO;YACxCC,MAAM,CAAC,EAAEF,GAAG,EAAE,GAAMA,IAAIC,IAAI,GAAG,OAAO;YACtCE,QAAQ,CAAC,EAAEH,GAAG,EAAE,GAAMA,IAAIC,IAAI,GAAG,OAAO;QAC1C;QACAG,OAAO;YAAEC,YAAY;QAAQ;QAC7BC,MAAM;YACJX;QAIF;QACAY,QAAQ;eAAIC;SAA4B;IAC1C;IAEA,OAAOZ;AACT,EAAC;AAED,OAAO,MAAMY,8BAAuC;IAClD;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;QACPC,UAAU;QACVC,QAAQ;IACV;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;IACT;IACA;QACEF,MAAM;QACNC,MAAM;QACNI,cAAc;QACdH,OAAO;QACPI,SAAS;YACP;gBACEJ,OAAO;gBACPK,OAAO;YACT;YACA;gBACEL,OAAO;gBACPK,OAAO;YACT;YACA;gBACEL,OAAO;gBACPK,OAAO;YACT;SACD;QACDJ,UAAU;IACZ;IACA;QACEH,MAAM;QACNC,MAAM;QACNN,OAAO;YACLa,QAAQ;QACV;QACAN,OAAO;IACT;IACA;QACEF,MAAM;QACNC,MAAM;QACNN,OAAO;YACLa,QAAQ;QACV;QACAN,OAAO;IACT;IAEA;;GAEC,GACDrB;CACD,CAAA"}
@@ -14,27 +14,44 @@ export const SubscriberMenu = ({ classNames = {
14
14
  classNames.container
15
15
  ]),
16
16
  children: subscriber && /*#__PURE__*/ _jsxs("div", {
17
+ style: {
18
+ display: 'flex'
19
+ },
17
20
  children: [
18
- "Welcome, ",
19
- subscriber?.email,
20
- " - ",
21
- /*#__PURE__*/ _jsx("a", {
22
- href: '/subscribe',
23
- children: "Manage subscriptions"
21
+ /*#__PURE__*/ _jsxs("div", {
22
+ style: {
23
+ flexGrow: 1
24
+ },
25
+ children: [
26
+ "Welcome, ",
27
+ subscriber?.email
28
+ ]
29
+ }),
30
+ /*#__PURE__*/ _jsx("div", {
31
+ style: {
32
+ flexGrow: 1
33
+ },
34
+ children: /*#__PURE__*/ _jsx("a", {
35
+ href: '/subscribe',
36
+ children: "Manage subscriptions"
37
+ })
24
38
  }),
25
- " -",
26
- ' ',
27
- /*#__PURE__*/ _jsx("button", {
28
- className: mergeClassNames([
29
- styles.button,
30
- classNames.button
31
- ]),
32
- onClick: (e)=>{
33
- e.preventDefault();
34
- logOut();
39
+ /*#__PURE__*/ _jsx("div", {
40
+ style: {
41
+ flexGrow: 1
35
42
  },
36
- type: "button",
37
- children: "Log out"
43
+ children: /*#__PURE__*/ _jsx("button", {
44
+ className: mergeClassNames([
45
+ styles.button,
46
+ classNames.button
47
+ ]),
48
+ onClick: (e)=>{
49
+ e.preventDefault();
50
+ logOut();
51
+ },
52
+ type: "button",
53
+ children: "Log out"
54
+ })
38
55
  })
39
56
  ]
40
57
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/SubscriberMenu.tsx"],"sourcesContent":["'use client'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\n\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\n// interface IAuth {\n// props?: any\n// }\n\nexport type SubscriberMenuClasses = {\n button?: string\n container?: string\n}\n\nexport const SubscriberMenu = ({\n classNames = {\n button: '',\n container: '',\n },\n}: {\n classNames?: SubscriberMenuClasses\n}) => {\n const { logOut, subscriber } = useSubscriber()\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n {/* <pre>{JSON.stringify(result, null, 2)}</pre> */}\n {subscriber && (\n <div>\n Welcome, {subscriber?.email} - <a href={'/subscribe'}>Manage subscriptions</a> -{' '}\n <button\n className={mergeClassNames([styles.button, classNames.button])}\n onClick={(e) => {\n e.preventDefault()\n logOut()\n }}\n type=\"button\"\n >\n Log out\n </button>\n </div>\n )}\n </div>\n )\n}\n"],"names":["useSubscriber","mergeClassNames","styles","SubscriberMenu","classNames","button","container","logOut","subscriber","div","className","email","a","href","onClick","e","preventDefault","type"],"mappings":"AAAA;;AAEA,SAASA,aAAa,QAAQ,uCAAsC;AAEpE,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AAWxC,OAAO,MAAMC,iBAAiB,CAAC,EAC7BC,aAAa;IACXC,QAAQ;IACRC,WAAW;AACb,CAAC,EAGF;IACC,MAAM,EAAEC,MAAM,EAAEC,UAAU,EAAE,GAAGR;IAC/B,qBACE,KAACS;QAAIC,WAAWT,gBAAgB;YAACC,OAAOI,SAAS;YAAEF,WAAWE,SAAS;SAAC;kBAErEE,4BACC,MAACC;;gBAAI;gBACOD,YAAYG;gBAAM;8BAAG,KAACC;oBAAEC,MAAM;8BAAc;;gBAAwB;gBAAG;8BACjF,KAACR;oBACCK,WAAWT,gBAAgB;wBAACC,OAAOG,MAAM;wBAAED,WAAWC,MAAM;qBAAC;oBAC7DS,SAAS,CAACC;wBACRA,EAAEC,cAAc;wBAChBT;oBACF;oBACAU,MAAK;8BACN;;;;;AAOX,EAAC"}
1
+ {"version":3,"sources":["../../../src/components/app/SubscriberMenu.tsx"],"sourcesContent":["'use client'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\n// interface IAuth {\n// props?: any\n// }\n\nexport type SubscriberMenuClasses = {\n button?: string\n container?: string\n}\n\nexport const SubscriberMenu = ({\n classNames = {\n button: '',\n container: '',\n },\n}: {\n classNames?: SubscriberMenuClasses\n}) => {\n const { logOut, subscriber } = useSubscriber()\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n {/* <pre>{JSON.stringify(result, null, 2)}</pre> */}\n {subscriber && (\n <div style={{ display: 'flex' }}>\n <div style={{ flexGrow: 1 }}>Welcome, {subscriber?.email}</div>\n <div style={{ flexGrow: 1 }}>\n <a href={'/subscribe'}>Manage subscriptions</a>\n </div>\n <div style={{ flexGrow: 1 }}>\n <button\n className={mergeClassNames([styles.button, classNames.button])}\n onClick={(e) => {\n e.preventDefault()\n logOut()\n }}\n type=\"button\"\n >\n Log out\n </button>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"names":["useSubscriber","mergeClassNames","styles","SubscriberMenu","classNames","button","container","logOut","subscriber","div","className","style","display","flexGrow","email","a","href","onClick","e","preventDefault","type"],"mappings":"AAAA;;AAEA,SAASA,aAAa,QAAQ,uCAAsC;AACpE,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AAWxC,OAAO,MAAMC,iBAAiB,CAAC,EAC7BC,aAAa;IACXC,QAAQ;IACRC,WAAW;AACb,CAAC,EAGF;IACC,MAAM,EAAEC,MAAM,EAAEC,UAAU,EAAE,GAAGR;IAC/B,qBACE,KAACS;QAAIC,WAAWT,gBAAgB;YAACC,OAAOI,SAAS;YAAEF,WAAWE,SAAS;SAAC;kBAErEE,4BACC,MAACC;YAAIE,OAAO;gBAAEC,SAAS;YAAO;;8BAC5B,MAACH;oBAAIE,OAAO;wBAAEE,UAAU;oBAAE;;wBAAG;wBAAUL,YAAYM;;;8BACnD,KAACL;oBAAIE,OAAO;wBAAEE,UAAU;oBAAE;8BACxB,cAAA,KAACE;wBAAEC,MAAM;kCAAc;;;8BAEzB,KAACP;oBAAIE,OAAO;wBAAEE,UAAU;oBAAE;8BACxB,cAAA,KAACR;wBACCK,WAAWT,gBAAgB;4BAACC,OAAOG,MAAM;4BAAED,WAAWC,MAAM;yBAAC;wBAC7DY,SAAS,CAACC;4BACRA,EAAEC,cAAc;4BAChBZ;wBACF;wBACAa,MAAK;kCACN;;;;;;AAQb,EAAC"}
@@ -1,9 +1,9 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { PayloadSDK } from '@payloadcms/sdk';
4
4
  import { useSearchParams } from 'next/navigation.js';
5
5
  import { useCallback, useEffect, useState } from 'react';
6
- import { useSubscriber } from '../../exports/ui.js';
6
+ import { RequestMagicLink, useSubscriber } from '../../exports/ui.js';
7
7
  import { useServerUrl } from '../../react-hooks/useServerUrl.js';
8
8
  import { mergeClassNames } from './helpers.js';
9
9
  import styles from './shared.module.css';
@@ -47,18 +47,50 @@ export const VerifyMagicLink = ({ classNames = {
47
47
  // const [email, setEmail] = useState('')
48
48
  const { refreshSubscriber } = useSubscriber();
49
49
  const callVerify = useCallback(async ()=>{
50
- const sdk = new PayloadSDK({
51
- baseURL: serverURL || ''
52
- });
53
- const verifyResult = await sdk.request({
54
- json: {
55
- email,
56
- token
57
- },
58
- method: 'POST',
59
- path: '/api/verifyToken'
60
- });
61
- return verifyResult;
50
+ if (!email || !token) {
51
+ console.info('Invalid input');
52
+ return {
53
+ error: 'Invalid input'
54
+ };
55
+ }
56
+ try {
57
+ // I tried using PayloadSDK.request, but when the endpoint
58
+ // returns a not-okay status, PayloadSDK.request returns its
59
+ // own "Bad request" error, and doesn't share the endpoint
60
+ // result data.
61
+ const verifyEndpointResult = await fetch(serverURL + '/api/verifyToken', {
62
+ body: JSON.stringify({
63
+ email,
64
+ token
65
+ }),
66
+ method: 'POST'
67
+ });
68
+ // return verifyEndpointResult
69
+ if (verifyEndpointResult && verifyEndpointResult.json) {
70
+ console.log(1);
71
+ const resultJson = await verifyEndpointResult.json();
72
+ return {
73
+ error: resultJson.error,
74
+ message: resultJson.message
75
+ };
76
+ } else if (verifyEndpointResult && verifyEndpointResult.text) {
77
+ console.log(2);
78
+ const resultText = await verifyEndpointResult.text();
79
+ return {
80
+ error: resultText
81
+ };
82
+ } else {
83
+ console.log(3);
84
+ return {
85
+ error: verifyEndpointResult.status
86
+ };
87
+ }
88
+ } catch (error) {
89
+ console.log('catch');
90
+ return {
91
+ error
92
+ };
93
+ }
62
94
  }, [
63
95
  email,
64
96
  serverURL,
@@ -66,22 +98,10 @@ export const VerifyMagicLink = ({ classNames = {
66
98
  ]);
67
99
  useEffect(()=>{
68
100
  async function verify() {
69
- const verifyResult = await callVerify();
70
- if (verifyResult.ok) {
71
- const resultJson = await verifyResult.json();
72
- setResult(resultJson.message || resultJson.error);
73
- setIsError(resultJson.error && !resultJson.message);
74
- // // This is causing out of control rendering. Not totally sure why, or of another way to do it.
75
- // refreshSubscriber()
76
- // // This is also causing out of control rendering. Not totally sure why, or of another way to do it.
77
- // if (handleMagicLinkVerified) {
78
- // handleMagicLinkVerified(resultJson)
79
- // }
80
- } else {
81
- // const resultText = await verifyResult.text()
82
- setResult('An error occured. Please try again');
83
- setIsError(true);
84
- }
101
+ const { error, message } = await callVerify();
102
+ setResult(message || `An error occured. Please try again. (${error})`);
103
+ setIsError(error && !message);
104
+ // console.info('callVerify not okay', { error, message })
85
105
  }
86
106
  if (!subscriber) {
87
107
  void verify();
@@ -95,7 +115,7 @@ export const VerifyMagicLink = ({ classNames = {
95
115
  subscriber,
96
116
  token
97
117
  ]);
98
- const handleRequestMagicLink = async ()=>{
118
+ const handleRequestAnother = async ()=>{
99
119
  const sdk = new PayloadSDK({
100
120
  baseURL: serverURL || ''
101
121
  });
@@ -120,45 +140,52 @@ export const VerifyMagicLink = ({ classNames = {
120
140
  setIsError(true);
121
141
  }
122
142
  };
123
- return /*#__PURE__*/ _jsxs("div", {
124
- className: mergeClassNames([
125
- styles.container,
126
- classNames.container
127
- ]),
143
+ return /*#__PURE__*/ _jsxs(_Fragment, {
128
144
  children: [
129
- !result && /*#__PURE__*/ _jsx("p", {
130
- className: mergeClassNames([
131
- styles.loading,
132
- classNames.loading
133
- ]),
134
- children: "verifying..."
145
+ (!email || !token) && /*#__PURE__*/ _jsx(RequestMagicLink, {
146
+ classNames: classNames
135
147
  }),
136
- result && /*#__PURE__*/ _jsx("p", {
148
+ email && token && /*#__PURE__*/ _jsxs("div", {
137
149
  className: mergeClassNames([
138
- styles.message,
139
- classNames.message,
140
- isError ? [
141
- styles.error,
142
- classNames.error
143
- ] : []
144
- ]),
145
- children: result
146
- }),
147
- /*#__PURE__*/ _jsxs("div", {
148
- className: mergeClassNames([
149
- styles.form,
150
- classNames.form
150
+ styles.container,
151
+ classNames.container
151
152
  ]),
152
153
  children: [
153
- result && isError && renderButton({
154
- name: 'request',
155
- onClick: handleRequestMagicLink,
156
- text: 'Request another magic link'
154
+ !result && /*#__PURE__*/ _jsx("p", {
155
+ className: mergeClassNames([
156
+ styles.loading,
157
+ classNames.loading
158
+ ]),
159
+ children: "verifying..."
160
+ }),
161
+ result && /*#__PURE__*/ _jsx("p", {
162
+ className: mergeClassNames([
163
+ styles.message,
164
+ classNames.message,
165
+ isError ? [
166
+ styles.error,
167
+ classNames.error
168
+ ] : []
169
+ ]),
170
+ children: result
157
171
  }),
158
- result && forwardUrl && renderButton({
159
- name: 'continue',
160
- forwardUrl,
161
- text: 'Continue'
172
+ /*#__PURE__*/ _jsxs("div", {
173
+ className: mergeClassNames([
174
+ styles.form,
175
+ classNames.form
176
+ ]),
177
+ children: [
178
+ result && isError && renderButton({
179
+ name: 'request',
180
+ onClick: handleRequestAnother,
181
+ text: 'Request another magic link'
182
+ }),
183
+ result && forwardUrl && renderButton({
184
+ name: 'continue',
185
+ forwardUrl,
186
+ text: 'Continue'
187
+ })
188
+ ]
162
189
  })
163
190
  ]
164
191
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/VerifyMagicLink.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { useSearchParams } from 'next/navigation.js'\nimport { useCallback, useEffect, useState } from 'react'\n\nimport type { RequestMagicLinkResponse } from '../..//endpoints/requestMagicLink.js'\nimport type { Config } from '../../copied/payload-types.js'\nimport type { VerifyMagicLinkResponse } from '../../endpoints/verifyMagicLink.js'\n\nexport { VerifyMagicLinkResponse }\nimport { useSubscriber } from '../../exports/ui.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\n// const payload = await getPayload({\n// config: configPromise,\n// })\n\n// Pass your config from generated types as generic\n\nexport interface IVerifyMagicLink {\n classNames?: VerifyMagicLinkClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleMagicLinkVerified?: (result: VerifyMagicLinkResponse) => void\n renderButton?: (props: {\n forwardUrl?: string\n name?: string\n onClick?: () => any\n text?: string\n }) => React.ReactNode\n}\n\nexport type VerifyMagicLinkClasses = {\n button?: string\n container?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n}\n\nexport const VerifyMagicLink = ({\n classNames = {\n button: '',\n container: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n },\n handleMagicLinkRequested,\n handleMagicLinkVerified,\n renderButton = ({ name, forwardUrl, onClick, text }) =>\n forwardUrl ? (\n <a href={forwardUrl}>\n <button\n className={mergeClassNames([styles.button, classNames.button])}\n name={name}\n type=\"button\"\n >\n {text}\n </button>\n </a>\n ) : (\n <button\n className={mergeClassNames([styles.button, classNames.button])}\n name={name}\n onClick={onClick}\n type=\"button\"\n >\n {text}\n </button>\n ),\n}: IVerifyMagicLink) => {\n const { serverURL } = useServerUrl()\n const {\n // refreshSubscriber,\n subscriber,\n } = useSubscriber()\n\n const searchParams = useSearchParams()\n const email = searchParams.get('email')\n const forwardUrl = searchParams.get('forwardUrl')\n const token = searchParams.get('token')\n\n const [result, setResult] = useState<string>()\n const [isError, setIsError] = useState<boolean>(false)\n // const [email, setEmail] = useState('')\n\n const { refreshSubscriber } = useSubscriber()\n\n const callVerify = useCallback(async () => {\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const verifyResult = await sdk.request({\n json: {\n email,\n token,\n },\n method: 'POST',\n path: '/api/verifyToken',\n })\n\n return verifyResult\n }, [email, serverURL, token])\n\n useEffect(() => {\n async function verify() {\n const verifyResult = await callVerify()\n if (verifyResult.ok) {\n const resultJson = await verifyResult.json()\n setResult(resultJson.message || resultJson.error)\n setIsError(resultJson.error && !resultJson.message)\n\n // // This is causing out of control rendering. Not totally sure why, or of another way to do it.\n // refreshSubscriber()\n\n // // This is also causing out of control rendering. Not totally sure why, or of another way to do it.\n // if (handleMagicLinkVerified) {\n // handleMagicLinkVerified(resultJson)\n // }\n } else {\n // const resultText = await verifyResult.text()\n setResult('An error occured. Please try again')\n setIsError(true)\n }\n }\n if (!subscriber) {\n void verify()\n }\n }, [callVerify, serverURL, email, handleMagicLinkVerified, refreshSubscriber, subscriber, token])\n\n const handleRequestMagicLink = async () => {\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const emailResult = await sdk.request({\n json: {\n email,\n forwardUrl,\n },\n method: 'POST',\n path: '/api/emailToken',\n })\n if (emailResult.ok) {\n const resultJson = await emailResult.json()\n setResult('An email has been sent containing your magic link.')\n setIsError(false)\n if (handleMagicLinkRequested) {\n handleMagicLinkRequested(resultJson)\n }\n } else {\n // const resultText = await emailResult.text()\n setResult('An error occured. Please try again.')\n setIsError(true)\n }\n }\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n {!result && (\n <p className={mergeClassNames([styles.loading, classNames.loading])}>verifying...</p>\n )}\n {result && (\n <p\n className={mergeClassNames([\n styles.message,\n classNames.message,\n isError ? [styles.error, classNames.error] : [],\n ])}\n >\n {result}\n </p>\n )}\n <div className={mergeClassNames([styles.form, classNames.form])}>\n {result &&\n isError &&\n renderButton({\n name: 'request',\n onClick: handleRequestMagicLink,\n text: 'Request another magic link',\n })}\n {result &&\n forwardUrl &&\n renderButton({\n name: 'continue',\n forwardUrl,\n text: 'Continue',\n })}\n </div>\n </div>\n )\n}\n"],"names":["PayloadSDK","useSearchParams","useCallback","useEffect","useState","useSubscriber","useServerUrl","mergeClassNames","styles","VerifyMagicLink","classNames","button","container","error","form","loading","message","handleMagicLinkRequested","handleMagicLinkVerified","renderButton","name","forwardUrl","onClick","text","a","href","className","type","serverURL","subscriber","searchParams","email","get","token","result","setResult","isError","setIsError","refreshSubscriber","callVerify","sdk","baseURL","verifyResult","request","json","method","path","verify","ok","resultJson","handleRequestMagicLink","emailResult","div","p"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/app/VerifyMagicLink.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { useSearchParams } from 'next/navigation.js'\nimport { useCallback, useEffect, useState } from 'react'\n\nimport type { RequestMagicLinkResponse } from '../..//endpoints/requestMagicLink.js'\nimport type { Config } from '../../copied/payload-types.js'\nimport type { VerifyMagicLinkResponse } from '../../endpoints/verifyMagicLink.js'\n\nexport { VerifyMagicLinkResponse }\nimport { RequestMagicLink, useSubscriber } from '../../exports/ui.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\n// const payload = await getPayload({\n// config: configPromise,\n// })\n\n// Pass your config from generated types as generic\n\nexport interface IVerifyMagicLink {\n classNames?: VerifyMagicLinkClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleMagicLinkVerified?: (result: VerifyMagicLinkResponse) => void\n renderButton?: (props: {\n forwardUrl?: string\n name?: string\n onClick?: () => any\n text?: string\n }) => React.ReactNode\n}\n\nexport type VerifyMagicLinkClasses = {\n button?: string\n container?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n}\n\nexport const VerifyMagicLink = ({\n classNames = {\n button: '',\n container: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n },\n handleMagicLinkRequested,\n handleMagicLinkVerified,\n renderButton = ({ name, forwardUrl, onClick, text }) =>\n forwardUrl ? (\n <a href={forwardUrl}>\n <button\n className={mergeClassNames([styles.button, classNames.button])}\n name={name}\n type=\"button\"\n >\n {text}\n </button>\n </a>\n ) : (\n <button\n className={mergeClassNames([styles.button, classNames.button])}\n name={name}\n onClick={onClick}\n type=\"button\"\n >\n {text}\n </button>\n ),\n}: IVerifyMagicLink) => {\n const { serverURL } = useServerUrl()\n const {\n // refreshSubscriber,\n subscriber,\n } = useSubscriber()\n\n const searchParams = useSearchParams()\n const email = searchParams.get('email')\n const forwardUrl = searchParams.get('forwardUrl')\n const token = searchParams.get('token')\n\n const [result, setResult] = useState<string>()\n const [isError, setIsError] = useState<boolean>(false)\n // const [email, setEmail] = useState('')\n\n const { refreshSubscriber } = useSubscriber()\n\n const callVerify = useCallback(async () => {\n if (!email || !token) {\n console.info('Invalid input')\n return { error: 'Invalid input' }\n }\n try {\n // I tried using PayloadSDK.request, but when the endpoint\n // returns a not-okay status, PayloadSDK.request returns its\n // own \"Bad request\" error, and doesn't share the endpoint\n // result data.\n const verifyEndpointResult = await fetch(serverURL + '/api/verifyToken', {\n body: JSON.stringify({\n email,\n token,\n }),\n method: 'POST',\n })\n\n // return verifyEndpointResult\n if (verifyEndpointResult && verifyEndpointResult.json) {\n console.log(1)\n const resultJson = await verifyEndpointResult.json()\n return { error: resultJson.error, message: resultJson.message }\n } else if (verifyEndpointResult && verifyEndpointResult.text) {\n console.log(2)\n const resultText = await verifyEndpointResult.text()\n return { error: resultText }\n } else {\n console.log(3)\n return { error: verifyEndpointResult.status }\n }\n } catch (error: unknown) {\n console.log('catch')\n return { error }\n }\n }, [email, serverURL, token])\n\n useEffect(() => {\n async function verify() {\n const { error, message } = await callVerify()\n setResult(message || `An error occured. Please try again. (${error})`)\n setIsError(error && !message)\n // console.info('callVerify not okay', { error, message })\n }\n if (!subscriber) {\n void verify()\n }\n }, [callVerify, serverURL, email, handleMagicLinkVerified, refreshSubscriber, subscriber, token])\n\n const handleRequestAnother = async () => {\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const emailResult = await sdk.request({\n json: {\n email,\n forwardUrl,\n },\n method: 'POST',\n path: '/api/emailToken',\n })\n if (emailResult.ok) {\n const resultJson = await emailResult.json()\n setResult('An email has been sent containing your magic link.')\n setIsError(false)\n if (handleMagicLinkRequested) {\n handleMagicLinkRequested(resultJson)\n }\n } else {\n // const resultText = await emailResult.text()\n setResult('An error occured. Please try again.')\n setIsError(true)\n }\n }\n\n return (\n <>\n {(!email || !token) && <RequestMagicLink classNames={classNames} />}\n {email && token && (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n {!result && (\n <p className={mergeClassNames([styles.loading, classNames.loading])}>verifying...</p>\n )}\n {result && (\n <p\n className={mergeClassNames([\n styles.message,\n classNames.message,\n isError ? [styles.error, classNames.error] : [],\n ])}\n >\n {result}\n </p>\n )}\n <div className={mergeClassNames([styles.form, classNames.form])}>\n {result &&\n isError &&\n renderButton({\n name: 'request',\n onClick: handleRequestAnother,\n text: 'Request another magic link',\n })}\n {result &&\n forwardUrl &&\n renderButton({\n name: 'continue',\n forwardUrl,\n text: 'Continue',\n })}\n </div>\n </div>\n )}\n </>\n )\n}\n"],"names":["PayloadSDK","useSearchParams","useCallback","useEffect","useState","RequestMagicLink","useSubscriber","useServerUrl","mergeClassNames","styles","VerifyMagicLink","classNames","button","container","error","form","loading","message","handleMagicLinkRequested","handleMagicLinkVerified","renderButton","name","forwardUrl","onClick","text","a","href","className","type","serverURL","subscriber","searchParams","email","get","token","result","setResult","isError","setIsError","refreshSubscriber","callVerify","console","info","verifyEndpointResult","fetch","body","JSON","stringify","method","json","log","resultJson","resultText","status","verify","handleRequestAnother","sdk","baseURL","emailResult","request","path","ok","div","p"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { PayloadSDK } from '@payloadcms/sdk';
4
3
  import { useCallback, useEffect } from 'react';
5
4
  import { createContext, useContext, useMemo, useState } from 'react';
6
5
  import { useServerUrl } from '../react-hooks/useServerUrl.js';
@@ -16,13 +15,9 @@ export function SubscriberProvider({ children }) {
16
15
  const initSubscriber = async ()=>{
17
16
  setIsLoaded(false);
18
17
  try {
19
- const sdk = new PayloadSDK({
20
- baseURL: serverURL || ''
21
- });
22
- const authResponse = await sdk.request({
23
- json: {},
24
- method: 'POST',
25
- path: '/api/subscriberAuth'
18
+ const authResponse = await fetch('/api/subscriberAuth', {
19
+ // body: JSON.stringify({}),
20
+ method: 'POST'
26
21
  });
27
22
  if (authResponse.ok) {
28
23
  // Call the server function to get the user data
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/contexts/SubscriberProvider.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ReactNode, useCallback, useEffect } from 'react'\nimport { createContext, useContext, useMemo, useState } from 'react'\n\nimport type { Config, Subscriber } from '../copied/payload-types.js'\n\nimport { useServerUrl } from '../react-hooks/useServerUrl.js'\n\nexport type SubscriberContextType = {\n isLoaded: boolean\n logOut: () => void\n permissions: any\n refreshSubscriber: () => void\n subscriber: null | Subscriber\n}\n\nconst SubscriberContext = createContext<SubscriberContextType | undefined>(undefined)\n\ninterface ProviderProps {\n children?: ReactNode // Recommended type for children\n}\n\nexport function SubscriberProvider({ children }: ProviderProps) {\n // eslint-disable-next-line\n const [subscriber, setSubscriber] = useState<null | (Subscriber & { optIns: string[] })>(null)\n\n const { serverURL } = useServerUrl()\n\n // Keep track of if the selection content is loaded yet\n const [isLoaded, setIsLoaded] = useState(false)\n\n const [permissions, setPermissions] = useState<any>()\n\n const refreshSubscriber = useCallback(async () => {\n const initSubscriber = async () => {\n setIsLoaded(false)\n try {\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n const authResponse = await sdk.request({\n json: {},\n method: 'POST',\n path: '/api/subscriberAuth',\n })\n\n if (authResponse.ok) {\n // Call the server function to get the user data\n const { permissions, subscriber } = await authResponse.json()\n // console.log(`subscriber = `, subscriber)\n // console.log(`permissions = `, permissions)\n setPermissions(permissions)\n setSubscriber(subscriber)\n } else {\n setPermissions(null)\n setSubscriber(null)\n }\n } catch (error: unknown) {\n console.log(`authResponse error`, error)\n }\n setIsLoaded(true)\n }\n await initSubscriber()\n }, [serverURL])\n\n const logOut = useCallback(async () => {\n setIsLoaded(false)\n try {\n // const sdk = new PayloadSDK<Config>({\n // baseURL: serverURL || '',\n // })\n // const logoutResponse = await sdk.request({\n // json: {},\n // method: 'POST',\n // path: '/api/logout',\n // })\n // Unsure why sdk isn't working here\n const logoutResponse = await fetch('/api/logout', {\n method: 'POST',\n })\n\n // console.log(`logoutResponse`, logoutResponse)\n\n if (logoutResponse.ok) {\n setSubscriber(null)\n setPermissions(null)\n }\n } catch (error: unknown) {\n console.log(`logoutResponse error`, error)\n }\n setIsLoaded(true)\n }, [])\n\n useEffect(() => {\n void refreshSubscriber()\n }, [refreshSubscriber]) // Empty dependency array for mount/unmount\n\n // Memoize the value to prevent unnecessary re-renders in consumers\n const contextValue: SubscriberContextType = useMemo(\n () => ({\n isLoaded,\n logOut,\n permissions,\n refreshSubscriber,\n subscriber,\n }),\n [isLoaded, logOut, permissions, refreshSubscriber, subscriber],\n )\n\n return <SubscriberContext.Provider value={contextValue}>{children}</SubscriberContext.Provider>\n}\n\n// Custom hook to easily consume the context and add error handling\nexport function useSubscriber() {\n const context = useContext(SubscriberContext)\n if (context === undefined) {\n throw new Error('useSubscriber must be used within a SubscriberProvider')\n }\n return context\n}\n"],"names":["PayloadSDK","useCallback","useEffect","createContext","useContext","useMemo","useState","useServerUrl","SubscriberContext","undefined","SubscriberProvider","children","subscriber","setSubscriber","serverURL","isLoaded","setIsLoaded","permissions","setPermissions","refreshSubscriber","initSubscriber","sdk","baseURL","authResponse","request","json","method","path","ok","error","console","log","logOut","logoutResponse","fetch","contextValue","Provider","value","useSubscriber","context","Error"],"mappings":"AAAA;;AAEA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAAyBC,WAAW,EAAEC,SAAS,QAAQ,QAAO;AAC9D,SAASC,aAAa,EAAEC,UAAU,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AAIpE,SAASC,YAAY,QAAQ,iCAAgC;AAU7D,MAAMC,kCAAoBL,cAAiDM;AAM3E,OAAO,SAASC,mBAAmB,EAAEC,QAAQ,EAAiB;IAC5D,2BAA2B;IAC3B,MAAM,CAACC,YAAYC,cAAc,GAAGP,SAAqD;IAEzF,MAAM,EAAEQ,SAAS,EAAE,GAAGP;IAEtB,uDAAuD;IACvD,MAAM,CAACQ,UAAUC,YAAY,GAAGV,SAAS;IAEzC,MAAM,CAACW,aAAaC,eAAe,GAAGZ;IAEtC,MAAMa,oBAAoBlB,YAAY;QACpC,MAAMmB,iBAAiB;YACrBJ,YAAY;YACZ,IAAI;gBACF,MAAMK,MAAM,IAAIrB,WAAmB;oBACjCsB,SAASR,aAAa;gBACxB;gBACA,MAAMS,eAAe,MAAMF,IAAIG,OAAO,CAAC;oBACrCC,MAAM,CAAC;oBACPC,QAAQ;oBACRC,MAAM;gBACR;gBAEA,IAAIJ,aAAaK,EAAE,EAAE;oBACnB,gDAAgD;oBAChD,MAAM,EAAEX,WAAW,EAAEL,UAAU,EAAE,GAAG,MAAMW,aAAaE,IAAI;oBAC3D,2CAA2C;oBAC3C,6CAA6C;oBAC7CP,eAAeD;oBACfJ,cAAcD;gBAChB,OAAO;oBACLM,eAAe;oBACfL,cAAc;gBAChB;YACF,EAAE,OAAOgB,OAAgB;gBACvBC,QAAQC,GAAG,CAAC,CAAC,kBAAkB,CAAC,EAAEF;YACpC;YACAb,YAAY;QACd;QACA,MAAMI;IACR,GAAG;QAACN;KAAU;IAEd,MAAMkB,SAAS/B,YAAY;QACzBe,YAAY;QACZ,IAAI;YACF,uCAAuC;YACvC,8BAA8B;YAC9B,KAAK;YACL,6CAA6C;YAC7C,cAAc;YACd,oBAAoB;YACpB,yBAAyB;YACzB,KAAK;YACL,oCAAoC;YACpC,MAAMiB,iBAAiB,MAAMC,MAAM,eAAe;gBAChDR,QAAQ;YACV;YAEA,gDAAgD;YAEhD,IAAIO,eAAeL,EAAE,EAAE;gBACrBf,cAAc;gBACdK,eAAe;YACjB;QACF,EAAE,OAAOW,OAAgB;YACvBC,QAAQC,GAAG,CAAC,CAAC,oBAAoB,CAAC,EAAEF;QACtC;QACAb,YAAY;IACd,GAAG,EAAE;IAELd,UAAU;QACR,KAAKiB;IACP,GAAG;QAACA;KAAkB,GAAE,2CAA2C;IAEnE,mEAAmE;IACnE,MAAMgB,eAAsC9B,QAC1C,IAAO,CAAA;YACLU;YACAiB;YACAf;YACAE;YACAP;QACF,CAAA,GACA;QAACG;QAAUiB;QAAQf;QAAaE;QAAmBP;KAAW;IAGhE,qBAAO,KAACJ,kBAAkB4B,QAAQ;QAACC,OAAOF;kBAAexB;;AAC3D;AAEA,mEAAmE;AACnE,OAAO,SAAS2B;IACd,MAAMC,UAAUnC,WAAWI;IAC3B,IAAI+B,YAAY9B,WAAW;QACzB,MAAM,IAAI+B,MAAM;IAClB;IACA,OAAOD;AACT"}
1
+ {"version":3,"sources":["../../src/contexts/SubscriberProvider.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ReactNode, useCallback, useEffect } from 'react'\nimport { createContext, useContext, useMemo, useState } from 'react'\n\nimport type { Config, Subscriber } from '../copied/payload-types.js'\n\nimport { useServerUrl } from '../react-hooks/useServerUrl.js'\n\nexport type SubscriberContextType = {\n isLoaded: boolean\n logOut: () => void\n permissions: any\n refreshSubscriber: () => void\n subscriber: null | Subscriber\n}\n\nconst SubscriberContext = createContext<SubscriberContextType | undefined>(undefined)\n\ninterface ProviderProps {\n children?: ReactNode // Recommended type for children\n}\n\nexport function SubscriberProvider({ children }: ProviderProps) {\n // eslint-disable-next-line\n const [subscriber, setSubscriber] = useState<null | (Subscriber & { optIns: string[] })>(null)\n\n const { serverURL } = useServerUrl()\n\n // Keep track of if the selection content is loaded yet\n const [isLoaded, setIsLoaded] = useState(false)\n\n const [permissions, setPermissions] = useState<any>()\n\n const refreshSubscriber = useCallback(async () => {\n const initSubscriber = async () => {\n setIsLoaded(false)\n try {\n const authResponse = await fetch('/api/subscriberAuth', {\n // body: JSON.stringify({}),\n method: 'POST',\n })\n\n if (authResponse.ok) {\n // Call the server function to get the user data\n const { permissions, subscriber } = await authResponse.json()\n // console.log(`subscriber = `, subscriber)\n // console.log(`permissions = `, permissions)\n setPermissions(permissions)\n setSubscriber(subscriber)\n } else {\n setPermissions(null)\n setSubscriber(null)\n }\n } catch (error: unknown) {\n console.log(`authResponse error`, error)\n }\n setIsLoaded(true)\n }\n await initSubscriber()\n }, [serverURL])\n\n const logOut = useCallback(async () => {\n setIsLoaded(false)\n try {\n // const sdk = new PayloadSDK<Config>({\n // baseURL: serverURL || '',\n // })\n // const logoutResponse = await sdk.request({\n // json: {},\n // method: 'POST',\n // path: '/api/logout',\n // })\n // Unsure why sdk isn't working here\n const logoutResponse = await fetch('/api/logout', {\n method: 'POST',\n })\n\n // console.log(`logoutResponse`, logoutResponse)\n\n if (logoutResponse.ok) {\n setSubscriber(null)\n setPermissions(null)\n }\n } catch (error: unknown) {\n console.log(`logoutResponse error`, error)\n }\n setIsLoaded(true)\n }, [])\n\n useEffect(() => {\n void refreshSubscriber()\n }, [refreshSubscriber]) // Empty dependency array for mount/unmount\n\n // Memoize the value to prevent unnecessary re-renders in consumers\n const contextValue: SubscriberContextType = useMemo(\n () => ({\n isLoaded,\n logOut,\n permissions,\n refreshSubscriber,\n subscriber,\n }),\n [isLoaded, logOut, permissions, refreshSubscriber, subscriber],\n )\n\n return <SubscriberContext.Provider value={contextValue}>{children}</SubscriberContext.Provider>\n}\n\n// Custom hook to easily consume the context and add error handling\nexport function useSubscriber() {\n const context = useContext(SubscriberContext)\n if (context === undefined) {\n throw new Error('useSubscriber must be used within a SubscriberProvider')\n }\n return context\n}\n"],"names":["useCallback","useEffect","createContext","useContext","useMemo","useState","useServerUrl","SubscriberContext","undefined","SubscriberProvider","children","subscriber","setSubscriber","serverURL","isLoaded","setIsLoaded","permissions","setPermissions","refreshSubscriber","initSubscriber","authResponse","fetch","method","ok","json","error","console","log","logOut","logoutResponse","contextValue","Provider","value","useSubscriber","context","Error"],"mappings":"AAAA;;AAGA,SAAyBA,WAAW,EAAEC,SAAS,QAAQ,QAAO;AAC9D,SAASC,aAAa,EAAEC,UAAU,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AAIpE,SAASC,YAAY,QAAQ,iCAAgC;AAU7D,MAAMC,kCAAoBL,cAAiDM;AAM3E,OAAO,SAASC,mBAAmB,EAAEC,QAAQ,EAAiB;IAC5D,2BAA2B;IAC3B,MAAM,CAACC,YAAYC,cAAc,GAAGP,SAAqD;IAEzF,MAAM,EAAEQ,SAAS,EAAE,GAAGP;IAEtB,uDAAuD;IACvD,MAAM,CAACQ,UAAUC,YAAY,GAAGV,SAAS;IAEzC,MAAM,CAACW,aAAaC,eAAe,GAAGZ;IAEtC,MAAMa,oBAAoBlB,YAAY;QACpC,MAAMmB,iBAAiB;YACrBJ,YAAY;YACZ,IAAI;gBACF,MAAMK,eAAe,MAAMC,MAAM,uBAAuB;oBACtD,4BAA4B;oBAC5BC,QAAQ;gBACV;gBAEA,IAAIF,aAAaG,EAAE,EAAE;oBACnB,gDAAgD;oBAChD,MAAM,EAAEP,WAAW,EAAEL,UAAU,EAAE,GAAG,MAAMS,aAAaI,IAAI;oBAC3D,2CAA2C;oBAC3C,6CAA6C;oBAC7CP,eAAeD;oBACfJ,cAAcD;gBAChB,OAAO;oBACLM,eAAe;oBACfL,cAAc;gBAChB;YACF,EAAE,OAAOa,OAAgB;gBACvBC,QAAQC,GAAG,CAAC,CAAC,kBAAkB,CAAC,EAAEF;YACpC;YACAV,YAAY;QACd;QACA,MAAMI;IACR,GAAG;QAACN;KAAU;IAEd,MAAMe,SAAS5B,YAAY;QACzBe,YAAY;QACZ,IAAI;YACF,uCAAuC;YACvC,8BAA8B;YAC9B,KAAK;YACL,6CAA6C;YAC7C,cAAc;YACd,oBAAoB;YACpB,yBAAyB;YACzB,KAAK;YACL,oCAAoC;YACpC,MAAMc,iBAAiB,MAAMR,MAAM,eAAe;gBAChDC,QAAQ;YACV;YAEA,gDAAgD;YAEhD,IAAIO,eAAeN,EAAE,EAAE;gBACrBX,cAAc;gBACdK,eAAe;YACjB;QACF,EAAE,OAAOQ,OAAgB;YACvBC,QAAQC,GAAG,CAAC,CAAC,oBAAoB,CAAC,EAAEF;QACtC;QACAV,YAAY;IACd,GAAG,EAAE;IAELd,UAAU;QACR,KAAKiB;IACP,GAAG;QAACA;KAAkB,GAAE,2CAA2C;IAEnE,mEAAmE;IACnE,MAAMY,eAAsC1B,QAC1C,IAAO,CAAA;YACLU;YACAc;YACAZ;YACAE;YACAP;QACF,CAAA,GACA;QAACG;QAAUc;QAAQZ;QAAaE;QAAmBP;KAAW;IAGhE,qBAAO,KAACJ,kBAAkBwB,QAAQ;QAACC,OAAOF;kBAAepB;;AAC3D;AAEA,mEAAmE;AACnE,OAAO,SAASuB;IACd,MAAMC,UAAU/B,WAAWI;IAC3B,IAAI2B,YAAY1B,WAAW;QACzB,MAAM,IAAI2B,MAAM;IAClB;IACA,OAAOD;AACT"}
@@ -42,7 +42,7 @@ import { defaultCollectionSlug } from '../collections/Subscribers.js';
42
42
  subscriber: null
43
43
  }, {
44
44
  headers,
45
- status: 200
45
+ status: 400
46
46
  });
47
47
  } catch (error) {
48
48
  // req.payload.logger.info(`subscriberAuth error: ${JSON.stringify(error)}`)
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/subscriberAuth.ts"],"sourcesContent":["import { headers as nextHeaders } from 'next/headers.js'\n\nimport type { Subscriber } from '../copied/payload-types.js'\n\n// If you're using Next.js, you'll have to import headers from next/headers, like so:\nimport type { CollectionSlug, Endpoint, PayloadHandler, Permissions } from 'payload'\n\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\n\nexport type SubscriberAuthResponse =\n | {\n error: string\n now: string\n }\n | {\n now: string\n permissions: Permissions\n subscriber: null | Subscriber\n }\n\n/**\n * createEndpointLogout\n * @param options\n * @returns\n *\n * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug\n *\n */\nfunction createEndpointSubscriberAuth({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * subscriberAuth Endpoint Handler\n * @param req\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('No subscriber authed' | catchError | 'Unknown error'), now: date} }\n */\n const subscriberAuthHandler: PayloadHandler = async (req) => {\n // req.payload.logger.info('subscriberAuthHandler')\n // Log the user in via Payload headers\n const headers = await nextHeaders()\n\n try {\n const { permissions, user } = await req.payload.auth({\n headers,\n })\n\n // req.payload.logger.info(`user = ${JSON.stringify(user)}`)\n // req.payload.logger.info(`permissions = ${JSON.stringify(permissions)}`)\n\n if (user && user.collection == subscribersCollectionSlug) {\n const subscriber: Subscriber = user as Subscriber\n if (subscriber.optIns) {\n subscriber.optIns = subscriber.optIns.map((channel) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n return Response.json({\n now: new Date().toISOString(),\n permissions,\n subscriber,\n } as SubscriberAuthResponse)\n }\n\n // req.payload.logger.info('subscriberAuthHandler: No subscriber authed')\n return Response.json(\n {\n // error: 'No subscriber authed',\n now: new Date().toISOString(),\n permissions,\n subscriber: null,\n } as SubscriberAuthResponse,\n { headers, status: 200 },\n )\n } catch (error: unknown) {\n // req.payload.logger.info(`subscriberAuth error: ${JSON.stringify(error)}`)\n return Response.json(\n {\n error,\n now: new Date().toISOString(),\n } as SubscriberAuthResponse,\n { headers, status: 400 },\n )\n }\n }\n\n /**\n * subscriberAuth Endpoint Config\n */\n const subscriberAuthEndpoint: Endpoint = {\n handler: subscriberAuthHandler,\n method: 'post',\n path: '/subscriberAuth',\n }\n return subscriberAuthEndpoint\n}\n\nexport default createEndpointSubscriberAuth\n"],"names":["headers","nextHeaders","defaultCollectionSlug","createEndpointSubscriberAuth","subscribersCollectionSlug","subscriberAuthHandler","req","permissions","user","payload","auth","collection","subscriber","optIns","map","channel","id","Response","json","now","Date","toISOString","status","error","subscriberAuthEndpoint","handler","method","path"],"mappings":"AAAA,SAASA,WAAWC,WAAW,QAAQ,kBAAiB;AAOxD,SAASC,qBAAqB,QAAQ,gCAA+B;AAarE;;;;;;;CAOC,GACD,SAASC,6BAA6B,EACpCC,4BAA4BF,qBAAqB,EAGlD;IACC;;;;;GAKC,GACD,MAAMG,wBAAwC,OAAOC;QACnD,mDAAmD;QACnD,sCAAsC;QACtC,MAAMN,UAAU,MAAMC;QAEtB,IAAI;YACF,MAAM,EAAEM,WAAW,EAAEC,IAAI,EAAE,GAAG,MAAMF,IAAIG,OAAO,CAACC,IAAI,CAAC;gBACnDV;YACF;YAEA,4DAA4D;YAC5D,0EAA0E;YAE1E,IAAIQ,QAAQA,KAAKG,UAAU,IAAIP,2BAA2B;gBACxD,MAAMQ,aAAyBJ;gBAC/B,IAAII,WAAWC,MAAM,EAAE;oBACrBD,WAAWC,MAAM,GAAGD,WAAWC,MAAM,CAACC,GAAG,CAAC,CAACC,UACzC,OAAOA,WAAW,WAAWA,UAAUA,QAAQC,EAAE;gBAErD;gBACA,OAAOC,SAASC,IAAI,CAAC;oBACnBC,KAAK,IAAIC,OAAOC,WAAW;oBAC3Bd;oBACAK;gBACF;YACF;YAEA,yEAAyE;YACzE,OAAOK,SAASC,IAAI,CAClB;gBACE,iCAAiC;gBACjCC,KAAK,IAAIC,OAAOC,WAAW;gBAC3Bd;gBACAK,YAAY;YACd,GACA;gBAAEZ;gBAASsB,QAAQ;YAAI;QAE3B,EAAE,OAAOC,OAAgB;YACvB,4EAA4E;YAC5E,OAAON,SAASC,IAAI,CAClB;gBACEK;gBACAJ,KAAK,IAAIC,OAAOC,WAAW;YAC7B,GACA;gBAAErB;gBAASsB,QAAQ;YAAI;QAE3B;IACF;IAEA;;GAEC,GACD,MAAME,yBAAmC;QACvCC,SAASpB;QACTqB,QAAQ;QACRC,MAAM;IACR;IACA,OAAOH;AACT;AAEA,eAAerB,6BAA4B"}
1
+ {"version":3,"sources":["../../src/endpoints/subscriberAuth.ts"],"sourcesContent":["import { headers as nextHeaders } from 'next/headers.js'\n\nimport type { Subscriber } from '../copied/payload-types.js'\n\n// If you're using Next.js, you'll have to import headers from next/headers, like so:\nimport type { CollectionSlug, Endpoint, PayloadHandler, Permissions } from 'payload'\n\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\n\nexport type SubscriberAuthResponse =\n | {\n error: string\n now: string\n }\n | {\n now: string\n permissions: Permissions\n subscriber: null | Subscriber\n }\n\n/**\n * createEndpointLogout\n * @param options\n * @returns\n *\n * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug\n *\n */\nfunction createEndpointSubscriberAuth({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * subscriberAuth Endpoint Handler\n * @param req\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('No subscriber authed' | catchError | 'Unknown error'), now: date} }\n */\n const subscriberAuthHandler: PayloadHandler = async (req) => {\n // req.payload.logger.info('subscriberAuthHandler')\n // Log the user in via Payload headers\n const headers = await nextHeaders()\n\n try {\n const { permissions, user } = await req.payload.auth({\n headers,\n })\n\n // req.payload.logger.info(`user = ${JSON.stringify(user)}`)\n // req.payload.logger.info(`permissions = ${JSON.stringify(permissions)}`)\n\n if (user && user.collection == subscribersCollectionSlug) {\n const subscriber: Subscriber = user as Subscriber\n if (subscriber.optIns) {\n subscriber.optIns = subscriber.optIns.map((channel) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n return Response.json({\n now: new Date().toISOString(),\n permissions,\n subscriber,\n } as SubscriberAuthResponse)\n }\n\n // req.payload.logger.info('subscriberAuthHandler: No subscriber authed')\n return Response.json(\n {\n // error: 'No subscriber authed',\n now: new Date().toISOString(),\n permissions,\n subscriber: null,\n } as SubscriberAuthResponse,\n { headers, status: 400 },\n )\n } catch (error: unknown) {\n // req.payload.logger.info(`subscriberAuth error: ${JSON.stringify(error)}`)\n return Response.json(\n {\n error,\n now: new Date().toISOString(),\n } as SubscriberAuthResponse,\n { headers, status: 400 },\n )\n }\n }\n\n /**\n * subscriberAuth Endpoint Config\n */\n const subscriberAuthEndpoint: Endpoint = {\n handler: subscriberAuthHandler,\n method: 'post',\n path: '/subscriberAuth',\n }\n return subscriberAuthEndpoint\n}\n\nexport default createEndpointSubscriberAuth\n"],"names":["headers","nextHeaders","defaultCollectionSlug","createEndpointSubscriberAuth","subscribersCollectionSlug","subscriberAuthHandler","req","permissions","user","payload","auth","collection","subscriber","optIns","map","channel","id","Response","json","now","Date","toISOString","status","error","subscriberAuthEndpoint","handler","method","path"],"mappings":"AAAA,SAASA,WAAWC,WAAW,QAAQ,kBAAiB;AAOxD,SAASC,qBAAqB,QAAQ,gCAA+B;AAarE;;;;;;;CAOC,GACD,SAASC,6BAA6B,EACpCC,4BAA4BF,qBAAqB,EAGlD;IACC;;;;;GAKC,GACD,MAAMG,wBAAwC,OAAOC;QACnD,mDAAmD;QACnD,sCAAsC;QACtC,MAAMN,UAAU,MAAMC;QAEtB,IAAI;YACF,MAAM,EAAEM,WAAW,EAAEC,IAAI,EAAE,GAAG,MAAMF,IAAIG,OAAO,CAACC,IAAI,CAAC;gBACnDV;YACF;YAEA,4DAA4D;YAC5D,0EAA0E;YAE1E,IAAIQ,QAAQA,KAAKG,UAAU,IAAIP,2BAA2B;gBACxD,MAAMQ,aAAyBJ;gBAC/B,IAAII,WAAWC,MAAM,EAAE;oBACrBD,WAAWC,MAAM,GAAGD,WAAWC,MAAM,CAACC,GAAG,CAAC,CAACC,UACzC,OAAOA,WAAW,WAAWA,UAAUA,QAAQC,EAAE;gBAErD;gBACA,OAAOC,SAASC,IAAI,CAAC;oBACnBC,KAAK,IAAIC,OAAOC,WAAW;oBAC3Bd;oBACAK;gBACF;YACF;YAEA,yEAAyE;YACzE,OAAOK,SAASC,IAAI,CAClB;gBACE,iCAAiC;gBACjCC,KAAK,IAAIC,OAAOC,WAAW;gBAC3Bd;gBACAK,YAAY;YACd,GACA;gBAAEZ;gBAASsB,QAAQ;YAAI;QAE3B,EAAE,OAAOC,OAAgB;YACvB,4EAA4E;YAC5E,OAAON,SAASC,IAAI,CAClB;gBACEK;gBACAJ,KAAK,IAAIC,OAAOC,WAAW;YAC7B,GACA;gBAAErB;gBAASsB,QAAQ;YAAI;QAE3B;IACF;IAEA;;GAEC,GACD,MAAME,yBAAmC;QACvCC,SAASpB;QACTqB,QAAQ;QACRC,MAAM;IACR;IACA,OAAOH;AACT;AAEA,eAAerB,6BAA4B"}
package/dist/index.d.ts CHANGED
@@ -13,9 +13,10 @@ export type PayloadSubscribersConfig = {
13
13
  */
14
14
  disabled?: boolean;
15
15
  /**
16
- * The collection to make the subscribers collection
17
- * - Set auth if not
18
- * - Add optIns field
16
+ * The collection to use as the subscribers collection
17
+ * - Optional. If not specified, the plugin will add a 'subscribers' collection.
18
+ * - Sets the collection auth if not already.
19
+ * - Adds (or overrides) fields: email, firstName, status, optIns, verificationToken, verificationTokenExpires.
19
20
  */
20
21
  subscribersCollectionSlug?: CollectionSlug;
21
22
  /**
package/dist/index.js.map CHANGED
@@ -1 +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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"}
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 use as the subscribers collection\n * - Optional. If not specified, the plugin will add a 'subscribers' collection.\n * - Sets the collection auth if not already.\n * - Adds (or overrides) fields: email, firstName, status, optIns, verificationToken, verificationTokenExpires.\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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"}
package/package.json CHANGED
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "registry": "https://registry.npmjs.org/",
71
71
  "dependencies": {},
72
- "version": "0.0.4",
72
+ "version": "0.0.6",
73
73
  "scripts": {
74
74
  "build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
75
75
  "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",