payload-subscribers-plugin 0.0.6 → 0.0.7

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
@@ -3,16 +3,16 @@
3
3
  A plugin to manage subscribers and the "channels" they can subscribe to.
4
4
 
5
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"
6
+
7
+ - Sign up or sign in by requesting a magic link email
8
+ - Verify the magic link to authenticate
9
+ - Opt in or out of "opt-in channels"
9
10
 
10
11
  You manage the opt-in channels via the Payload admin.
11
12
 
12
13
  The plugin relies on your email adapter configured in your payload config to send emails.
13
14
 
14
- That is all this plugin does currently. Potential features might include email authoring and send scheduler or simple CRM features.
15
-
15
+ That is all this plugin does currently. Potential features might include email authoring and send scheduler or simple CRM features.
16
16
 
17
17
  ## Installation
18
18
 
@@ -44,7 +44,7 @@ export default buildConfig({
44
44
  // Specify the collection to use as the subscribers collection
45
45
  // - Optional. If not specified, the plugin will add a 'subscribers' collection.
46
46
  // - Sets auth if not already
47
- // - Adds (or overrides) fields: email, firstName, status, optIns,
47
+ // - Adds (or overrides) fields: email, firstName, status, optIns,
48
48
  // verificationToken, verificationTokenExpires, and source
49
49
  subscribersCollectionSlug?: CollectionSlug
50
50
 
@@ -202,7 +202,9 @@ Shows the [Subscribe](#subscribe) component to authenticated subscribers, otherw
202
202
  </div> -->
203
203
 
204
204
  ```typescript
205
- <RequestMagicLink
205
+ <RequestOrSubscribe
206
+ // Provide the URL the user should go to after clicking the link in the email and having it verified
207
+ afterVerifyUrl={new URL(window.href)}
206
208
  // Provide your own global class names to add to the component elements. Optional
207
209
  classNames={{
208
210
  button: 'customCssClassNames',
@@ -224,6 +226,8 @@ Shows the [Subscribe](#subscribe) component to authenticated subscribers, otherw
224
226
  {text}
225
227
  </button>
226
228
  }
229
+ // Provide the URL to your route that has the VerifyMagicLink component on it.
230
+ verifyUrl={verifyUrl}
227
231
  />
228
232
  ```
229
233
 
@@ -235,6 +239,8 @@ Form to input email address and get a magic link email sent.
235
239
 
236
240
  ```typescript
237
241
  <RequestMagicLink
242
+ // Provide the URL the user should go to after clicking the link in the email and having it verified
243
+ afterVerifyUrl={new URL(window.href)}
238
244
  // Provide your own global class names to add to the component elements. Optional
239
245
  classNames={{
240
246
  button: 'customCssClassNames',
@@ -252,6 +258,8 @@ Form to input email address and get a magic link email sent.
252
258
  {text}
253
259
  </button>
254
260
  }
261
+ // Provide the URL to your route that has the VerifyMagicLink component on it.
262
+ verifyUrl={verifyUrl}
255
263
  />
256
264
  ```
257
265
 
@@ -277,20 +285,25 @@ Component that verifies a magic link using expected url parameters.
277
285
  // Called after a magic link has been verified. Optional
278
286
  handleMagicLinkVerified={async (result: RequestMagicLinkResponse) => {}}
279
287
  // Provided your own button component. Optional
280
- renderButton={({ name, forwardUrl, onClick, text }) =>
281
- forwardUrl ? (
282
- <a href={forwardUrl}>
283
- <button name={name} type="button">
284
- {text}
285
- </button>
286
- </a>
287
- ) : (
288
+ renderButton={({ name, onClick, text }) =>
288
289
  <button name={name} onClick={onClick} type="button">
289
290
  {text}
290
291
  </button>
291
- )
292
292
  }
293
- />
293
+ // Provide the URL to your route that has the VerifyMagicLink component on it.
294
+ // Used when this VerifyMagicLink component provides an option to request another link
295
+ // when verifying the current one fails.
296
+ verifyUrl={verifyUrl}
297
+ >
298
+ // Provide children to render after link is verified. Optional
299
+ // Since you provide the verifyUrl to any of the plugin components, you can include a forwardUrl
300
+ // as a search param, which your route can then use here.
301
+ <a href={forwardUrl}>
302
+ <button className={'customCss'} name={'continue'} type="button">
303
+ Continue
304
+ </button>
305
+ </a>
306
+ </VerifyMagicLink>
294
307
  ```
295
308
 
296
309
  #### **Subscribe**
@@ -301,6 +314,8 @@ Allows a subscriber to select from among all active optInChannels.
301
314
 
302
315
  ```typescript
303
316
  <Subscribe
317
+ // Provide the URL the user should go to after clicking the link in the email and having it verified
318
+ afterVerifyUrl={new URL(window.href)}
304
319
  // Provide your own global class names to add to the component elements. Optional
305
320
  classNames={{
306
321
  button: 'customCssClassNames',
@@ -320,6 +335,8 @@ Allows a subscriber to select from among all active optInChannels.
320
335
  {text}
321
336
  </button>
322
337
  }
338
+ // Provide the URL to your route that has the VerifyMagicLink component on it.
339
+ verifyUrl={verifyUrl}
323
340
  />
324
341
  ```
325
342
 
@@ -4,6 +4,7 @@ export interface IRequestMagicLink {
4
4
  classNames?: RequestMagicLinkClasses;
5
5
  handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void;
6
6
  props?: any;
7
+ verifyUrl?: URL;
7
8
  }
8
9
  export type RequestMagicLinkClasses = {
9
10
  button?: string;
@@ -13,4 +14,4 @@ export type RequestMagicLinkClasses = {
13
14
  form?: string;
14
15
  message?: string;
15
16
  };
16
- export declare const RequestMagicLink: ({ classNames, handleMagicLinkRequested, }: IRequestMagicLink) => import("react").JSX.Element;
17
+ export declare const RequestMagicLink: ({ classNames, handleMagicLinkRequested, verifyUrl, }: IRequestMagicLink) => import("react").JSX.Element;
@@ -13,7 +13,7 @@ export const RequestMagicLink = ({ classNames = {
13
13
  error: '',
14
14
  form: '',
15
15
  message: ''
16
- }, handleMagicLinkRequested })=>{
16
+ }, handleMagicLinkRequested, verifyUrl })=>{
17
17
  const { subscriber } = useSubscriber();
18
18
  const { serverURL } = useServerUrl();
19
19
  const [status, setStatus] = useState('default');
@@ -29,11 +29,10 @@ export const RequestMagicLink = ({ classNames = {
29
29
  ]);
30
30
  const handleSubmit = async (e)=>{
31
31
  e.preventDefault();
32
- const forwardUrl = window.location.pathname + '?now=' + new Date().toISOString();
33
32
  const emailTokenResponse = await sdk.request({
34
33
  json: {
35
34
  email,
36
- forwardUrl
35
+ verifyUrl: verifyUrl?.href
37
36
  },
38
37
  method: 'POST',
39
38
  path: '/api/emailToken'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/RequestMagicLink.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ChangeEvent, type SubmitEvent, useEffect, useState } from 'react'\n\nimport type { Config } from '../../copied/payload-types.js'\nimport type { RequestMagicLinkResponse } from '../../endpoints/requestMagicLink.js'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\nexport { RequestMagicLinkResponse }\n\nexport interface IRequestMagicLink {\n classNames?: RequestMagicLinkClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n props?: any\n}\n\nexport type RequestMagicLinkClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n message?: string\n}\n\ntype statusValues = 'default' | 'error' | 'sent'\n\nexport const RequestMagicLink = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n message: '',\n },\n handleMagicLinkRequested,\n}: IRequestMagicLink) => {\n const { subscriber } = useSubscriber()\n const { serverURL } = useServerUrl()\n\n const [status, setStatus] = useState<statusValues>('default')\n\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const [result, setResult] = useState<string>()\n const [email, setEmail] = useState(subscriber?.email || '')\n\n useEffect(() => {\n setEmail(subscriber?.email || '')\n }, [subscriber])\n\n const handleSubmit = async (e: SubmitEvent<HTMLFormElement>) => {\n e.preventDefault()\n const forwardUrl = window.location.pathname + '?now=' + new Date().toISOString()\n const emailTokenResponse = await sdk.request({\n json: {\n email,\n forwardUrl,\n },\n method: 'POST',\n path: '/api/emailToken',\n })\n if (emailTokenResponse.ok) {\n const emailTokenResponseJson: RequestMagicLinkResponse = await emailTokenResponse.json()\n if (handleMagicLinkRequested) {\n handleMagicLinkRequested(emailTokenResponseJson)\n }\n // @ts-expect-error One or the other exists\n const { emailResult, error } = emailTokenResponseJson\n if (error) {\n setStatus('error')\n setResult(`An error occured. Please try again. \\n ${error}`)\n } else if (emailResult) {\n setStatus('sent')\n setResult('An email has been sent containing your magic link.')\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n } else {\n const emailTokenResponseText = await emailTokenResponse.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\n${emailTokenResponseText}`)\n }\n }\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n {result ? (\n <p\n className={mergeClassNames([\n styles.message,\n classNames.message,\n status == 'error' ? [styles.error, classNames.error] : [],\n ])}\n >\n {result}\n </p>\n ) : (\n <></>\n )}\n <form\n className={mergeClassNames([styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={handleSubmit}\n >\n <input\n aria-label=\"enter your email\"\n className={mergeClassNames([styles.emailInput, classNames.emailInput])}\n onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}\n placeholder=\"enter your email\"\n type=\"email\"\n value={email}\n />\n <button className={mergeClassNames([styles.button, classNames.button])} type=\"submit\">\n Request magic link\n </button>\n </form>\n </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useSubscriber","useServerUrl","mergeClassNames","styles","RequestMagicLink","classNames","button","container","emailInput","error","form","message","handleMagicLinkRequested","subscriber","serverURL","status","setStatus","sdk","baseURL","result","setResult","email","setEmail","handleSubmit","e","preventDefault","forwardUrl","window","location","pathname","Date","toISOString","emailTokenResponse","request","json","method","path","ok","emailTokenResponseJson","emailResult","emailTokenResponseText","text","div","className","p","onSubmit","input","aria-label","onChange","target","value","placeholder","type"],"mappings":"AAAA;;AAEA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAA6CC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAK/E,SAASC,aAAa,QAAQ,uCAAsC;AACpE,SAASC,YAAY,QAAQ,oCAAmC;AAChE,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AAqBxC,OAAO,MAAMC,mBAAmB,CAAC,EAC/BC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACN;IAClB,MAAM,EAAEC,UAAU,EAAE,GAAGb;IACvB,MAAM,EAAEc,SAAS,EAAE,GAAGb;IAEtB,MAAM,CAACc,QAAQC,UAAU,GAAGjB,SAAuB;IAEnD,MAAMkB,MAAM,IAAIpB,WAAmB;QACjCqB,SAASJ,aAAa;IACxB;IAEA,MAAM,CAACK,QAAQC,UAAU,GAAGrB;IAC5B,MAAM,CAACsB,OAAOC,SAAS,GAAGvB,SAASc,YAAYQ,SAAS;IAExDvB,UAAU;QACRwB,SAAST,YAAYQ,SAAS;IAChC,GAAG;QAACR;KAAW;IAEf,MAAMU,eAAe,OAAOC;QAC1BA,EAAEC,cAAc;QAChB,MAAMC,aAAaC,OAAOC,QAAQ,CAACC,QAAQ,GAAG,UAAU,IAAIC,OAAOC,WAAW;QAC9E,MAAMC,qBAAqB,MAAMf,IAAIgB,OAAO,CAAC;YAC3CC,MAAM;gBACJb;gBACAK;YACF;YACAS,QAAQ;YACRC,MAAM;QACR;QACA,IAAIJ,mBAAmBK,EAAE,EAAE;YACzB,MAAMC,yBAAmD,MAAMN,mBAAmBE,IAAI;YACtF,IAAItB,0BAA0B;gBAC5BA,yBAAyB0B;YAC3B;YACA,2CAA2C;YAC3C,MAAM,EAAEC,WAAW,EAAE9B,KAAK,EAAE,GAAG6B;YAC/B,IAAI7B,OAAO;gBACTO,UAAU;gBACVI,UAAU,CAAC,uCAAuC,EAAEX,OAAO;YAC7D,OAAO,IAAI8B,aAAa;gBACtBvB,UAAU;gBACVI,UAAU;YACZ,OAAO;gBACLJ,UAAU;gBACVI,UAAU,CAAC,oDAAoD,CAAC;YAClE;QACF,OAAO;YACL,MAAMoB,yBAAyB,MAAMR,mBAAmBS,IAAI;YAC5DzB,UAAU;YACVI,UAAU,CAAC,sCAAsC,EAAEoB,wBAAwB;QAC7E;IACF;IAEA,qBACE,MAACE;QAAIC,WAAWzC,gBAAgB;YAACC,OAAOI,SAAS;YAAEF,WAAWE,SAAS;SAAC;;YACrEY,uBACC,KAACyB;gBACCD,WAAWzC,gBAAgB;oBACzBC,OAAOQ,OAAO;oBACdN,WAAWM,OAAO;oBAClBI,UAAU,UAAU;wBAACZ,OAAOM,KAAK;wBAAEJ,WAAWI,KAAK;qBAAC,GAAG,EAAE;iBAC1D;0BAEAU;+BAGH;0BAEF,MAACT;gBACCiC,WAAWzC,gBAAgB;oBAACC,OAAOO,IAAI;oBAAEL,WAAWK,IAAI;iBAAC;gBACzDyB,QAAO;gBACPU,UAAUtB;;kCAEV,KAACuB;wBACCC,cAAW;wBACXJ,WAAWzC,gBAAgB;4BAACC,OAAOK,UAAU;4BAAEH,WAAWG,UAAU;yBAAC;wBACrEwC,UAAU,CAACxB,IAAqCF,SAASE,EAAEyB,MAAM,CAACC,KAAK;wBACvEC,aAAY;wBACZC,MAAK;wBACLF,OAAO7B;;kCAET,KAACf;wBAAOqC,WAAWzC,gBAAgB;4BAACC,OAAOG,MAAM;4BAAED,WAAWC,MAAM;yBAAC;wBAAG8C,MAAK;kCAAS;;;;;;AAM9F,EAAC"}
1
+ {"version":3,"sources":["../../../src/components/app/RequestMagicLink.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ChangeEvent, type SubmitEvent, useEffect, useState } from 'react'\n\nimport type { Config } from '../../copied/payload-types.js'\nimport type { RequestMagicLinkResponse } from '../../endpoints/requestMagicLink.js'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\nexport { RequestMagicLinkResponse }\n\nexport interface IRequestMagicLink {\n classNames?: RequestMagicLinkClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n props?: any\n verifyUrl?: URL\n}\n\nexport type RequestMagicLinkClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n message?: string\n}\n\ntype statusValues = 'default' | 'error' | 'sent'\n\nexport const RequestMagicLink = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n message: '',\n },\n handleMagicLinkRequested,\n verifyUrl,\n}: IRequestMagicLink) => {\n const { subscriber } = useSubscriber()\n const { serverURL } = useServerUrl()\n const [status, setStatus] = useState<statusValues>('default')\n\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const [result, setResult] = useState<string>()\n const [email, setEmail] = useState(subscriber?.email || '')\n\n useEffect(() => {\n setEmail(subscriber?.email || '')\n }, [subscriber])\n\n const handleSubmit = async (e: SubmitEvent<HTMLFormElement>) => {\n e.preventDefault()\n const emailTokenResponse = await sdk.request({\n json: {\n email,\n verifyUrl: verifyUrl?.href,\n },\n method: 'POST',\n path: '/api/emailToken',\n })\n if (emailTokenResponse.ok) {\n const emailTokenResponseJson: RequestMagicLinkResponse = await emailTokenResponse.json()\n if (handleMagicLinkRequested) {\n handleMagicLinkRequested(emailTokenResponseJson)\n }\n // @ts-expect-error One or the other exists\n const { emailResult, error } = emailTokenResponseJson\n if (error) {\n setStatus('error')\n setResult(`An error occured. Please try again. \\n ${error}`)\n } else if (emailResult) {\n setStatus('sent')\n setResult('An email has been sent containing your magic link.')\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n } else {\n const emailTokenResponseText = await emailTokenResponse.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\n${emailTokenResponseText}`)\n }\n }\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n {result ? (\n <p\n className={mergeClassNames([\n styles.message,\n classNames.message,\n status == 'error' ? [styles.error, classNames.error] : [],\n ])}\n >\n {result}\n </p>\n ) : (\n <></>\n )}\n <form\n className={mergeClassNames([styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={handleSubmit}\n >\n <input\n aria-label=\"enter your email\"\n className={mergeClassNames([styles.emailInput, classNames.emailInput])}\n onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}\n placeholder=\"enter your email\"\n type=\"email\"\n value={email}\n />\n <button className={mergeClassNames([styles.button, classNames.button])} type=\"submit\">\n Request magic link\n </button>\n </form>\n </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useSubscriber","useServerUrl","mergeClassNames","styles","RequestMagicLink","classNames","button","container","emailInput","error","form","message","handleMagicLinkRequested","verifyUrl","subscriber","serverURL","status","setStatus","sdk","baseURL","result","setResult","email","setEmail","handleSubmit","e","preventDefault","emailTokenResponse","request","json","href","method","path","ok","emailTokenResponseJson","emailResult","emailTokenResponseText","text","div","className","p","onSubmit","input","aria-label","onChange","target","value","placeholder","type"],"mappings":"AAAA;;AAEA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAA6CC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAK/E,SAASC,aAAa,QAAQ,uCAAsC;AACpE,SAASC,YAAY,QAAQ,oCAAmC;AAChE,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AAsBxC,OAAO,MAAMC,mBAAmB,CAAC,EAC/BC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACxBC,SAAS,EACS;IAClB,MAAM,EAAEC,UAAU,EAAE,GAAGd;IACvB,MAAM,EAAEe,SAAS,EAAE,GAAGd;IACtB,MAAM,CAACe,QAAQC,UAAU,GAAGlB,SAAuB;IAEnD,MAAMmB,MAAM,IAAIrB,WAAmB;QACjCsB,SAASJ,aAAa;IACxB;IAEA,MAAM,CAACK,QAAQC,UAAU,GAAGtB;IAC5B,MAAM,CAACuB,OAAOC,SAAS,GAAGxB,SAASe,YAAYQ,SAAS;IAExDxB,UAAU;QACRyB,SAAST,YAAYQ,SAAS;IAChC,GAAG;QAACR;KAAW;IAEf,MAAMU,eAAe,OAAOC;QAC1BA,EAAEC,cAAc;QAChB,MAAMC,qBAAqB,MAAMT,IAAIU,OAAO,CAAC;YAC3CC,MAAM;gBACJP;gBACAT,WAAWA,WAAWiB;YACxB;YACAC,QAAQ;YACRC,MAAM;QACR;QACA,IAAIL,mBAAmBM,EAAE,EAAE;YACzB,MAAMC,yBAAmD,MAAMP,mBAAmBE,IAAI;YACtF,IAAIjB,0BAA0B;gBAC5BA,yBAAyBsB;YAC3B;YACA,2CAA2C;YAC3C,MAAM,EAAEC,WAAW,EAAE1B,KAAK,EAAE,GAAGyB;YAC/B,IAAIzB,OAAO;gBACTQ,UAAU;gBACVI,UAAU,CAAC,uCAAuC,EAAEZ,OAAO;YAC7D,OAAO,IAAI0B,aAAa;gBACtBlB,UAAU;gBACVI,UAAU;YACZ,OAAO;gBACLJ,UAAU;gBACVI,UAAU,CAAC,oDAAoD,CAAC;YAClE;QACF,OAAO;YACL,MAAMe,yBAAyB,MAAMT,mBAAmBU,IAAI;YAC5DpB,UAAU;YACVI,UAAU,CAAC,sCAAsC,EAAEe,wBAAwB;QAC7E;IACF;IAEA,qBACE,MAACE;QAAIC,WAAWrC,gBAAgB;YAACC,OAAOI,SAAS;YAAEF,WAAWE,SAAS;SAAC;;YACrEa,uBACC,KAACoB;gBACCD,WAAWrC,gBAAgB;oBACzBC,OAAOQ,OAAO;oBACdN,WAAWM,OAAO;oBAClBK,UAAU,UAAU;wBAACb,OAAOM,KAAK;wBAAEJ,WAAWI,KAAK;qBAAC,GAAG,EAAE;iBAC1D;0BAEAW;+BAGH;0BAEF,MAACV;gBACC6B,WAAWrC,gBAAgB;oBAACC,OAAOO,IAAI;oBAAEL,WAAWK,IAAI;iBAAC;gBACzDqB,QAAO;gBACPU,UAAUjB;;kCAEV,KAACkB;wBACCC,cAAW;wBACXJ,WAAWrC,gBAAgB;4BAACC,OAAOK,UAAU;4BAAEH,WAAWG,UAAU;yBAAC;wBACrEoC,UAAU,CAACnB,IAAqCF,SAASE,EAAEoB,MAAM,CAACC,KAAK;wBACvEC,aAAY;wBACZC,MAAK;wBACLF,OAAOxB;;kCAET,KAAChB;wBAAOiC,WAAWrC,gBAAgB;4BAACC,OAAOG,MAAM;4BAAED,WAAWC,MAAM;yBAAC;wBAAG0C,MAAK;kCAAS;;;;;;AAM9F,EAAC"}
@@ -10,8 +10,9 @@ export type RequestOrSubscribeClasses = {
10
10
  message?: string;
11
11
  section?: string;
12
12
  };
13
- export declare function RequestOrSubscribe({ classNames, handleMagicLinkRequested, handleSubscribe, }: {
13
+ export declare function RequestOrSubscribe({ classNames, handleMagicLinkRequested, handleSubscribe, verifyUrl, }: {
14
14
  classNames?: RequestOrSubscribeClasses;
15
15
  handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void;
16
16
  handleSubscribe?: (result: SubscribeResponse) => void;
17
+ verifyUrl?: URL;
17
18
  }): import("react").JSX.Element;
@@ -11,16 +11,18 @@ export function RequestOrSubscribe({ classNames = {
11
11
  loading: '',
12
12
  message: '',
13
13
  section: ''
14
- }, handleMagicLinkRequested, handleSubscribe }) {
14
+ }, handleMagicLinkRequested, handleSubscribe, verifyUrl }) {
15
15
  const { subscriber } = useSubscriber();
16
16
  // Example: Conditionally render something or pass the state to children
17
17
  return /*#__PURE__*/ _jsx(_Fragment, {
18
18
  children: subscriber ? /*#__PURE__*/ _jsx(Subscribe, {
19
19
  classNames: classNames,
20
- handleSubscribe: handleSubscribe
20
+ handleSubscribe: handleSubscribe,
21
+ verifyUrl: verifyUrl
21
22
  }) : /*#__PURE__*/ _jsx(RequestMagicLink, {
22
23
  classNames: classNames,
23
- handleMagicLinkRequested: handleMagicLinkRequested
24
+ handleMagicLinkRequested: handleMagicLinkRequested,
25
+ verifyUrl: verifyUrl
24
26
  })
25
27
  });
26
28
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/RequestOrSubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport {\n RequestMagicLink,\n type RequestMagicLinkResponse,\n Subscribe,\n type SubscribeResponse,\n} from '../../exports/ui.js'\n\nexport type { RequestMagicLinkResponse, SubscribeResponse }\n\nexport type RequestOrSubscribeClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n section?: string\n}\n\nexport function RequestOrSubscribe({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleMagicLinkRequested,\n handleSubscribe,\n}: {\n classNames?: RequestOrSubscribeClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleSubscribe?: (result: SubscribeResponse) => void\n}) {\n const { subscriber } = useSubscriber()\n\n // Example: Conditionally render something or pass the state to children\n return (\n <>\n {subscriber ? (\n <Subscribe classNames={classNames} handleSubscribe={handleSubscribe} />\n ) : (\n <RequestMagicLink\n classNames={classNames}\n handleMagicLinkRequested={handleMagicLinkRequested}\n />\n )}\n {/* <div>subscriber = {JSON.stringify(subscriber)}</div> */}\n </>\n )\n}\n"],"names":["useSubscriber","RequestMagicLink","Subscribe","RequestOrSubscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleMagicLinkRequested","handleSubscribe","subscriber"],"mappings":"AAAA;;AAEA,SAASA,aAAa,QAAQ,uCAAsC;AACpE,SACEC,gBAAgB,EAEhBC,SAAS,QAEJ,sBAAqB;AAe5B,OAAO,SAASC,mBAAmB,EACjCC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACxBC,eAAe,EAKhB;IACC,MAAM,EAAEC,UAAU,EAAE,GAAGf;IAEvB,wEAAwE;IACxE,qBACE;kBACGe,2BACC,KAACb;YAAUE,YAAYA;YAAYU,iBAAiBA;2BAEpD,KAACb;YACCG,YAAYA;YACZS,0BAA0BA;;;AAMpC"}
1
+ {"version":3,"sources":["../../../src/components/app/RequestOrSubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useState } from 'react'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport {\n RequestMagicLink,\n type RequestMagicLinkResponse,\n Subscribe,\n type SubscribeResponse,\n} from '../../exports/ui.js'\n\nexport type { RequestMagicLinkResponse, SubscribeResponse }\n\nexport type RequestOrSubscribeClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n section?: string\n}\n\nexport function RequestOrSubscribe({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleMagicLinkRequested,\n handleSubscribe,\n verifyUrl,\n}: {\n classNames?: RequestOrSubscribeClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleSubscribe?: (result: SubscribeResponse) => void\n verifyUrl?: URL\n}) {\n const { subscriber } = useSubscriber()\n\n // Example: Conditionally render something or pass the state to children\n return (\n <>\n {subscriber ? (\n <Subscribe\n classNames={classNames}\n handleSubscribe={handleSubscribe}\n verifyUrl={verifyUrl}\n />\n ) : (\n <RequestMagicLink\n classNames={classNames}\n handleMagicLinkRequested={handleMagicLinkRequested}\n verifyUrl={verifyUrl}\n />\n )}\n {/* <div>subscriber = {JSON.stringify(subscriber)}</div> */}\n </>\n )\n}\n"],"names":["useSubscriber","RequestMagicLink","Subscribe","RequestOrSubscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleMagicLinkRequested","handleSubscribe","verifyUrl","subscriber"],"mappings":"AAAA;;AAIA,SAASA,aAAa,QAAQ,uCAAsC;AACpE,SACEC,gBAAgB,EAEhBC,SAAS,QAEJ,sBAAqB;AAe5B,OAAO,SAASC,mBAAmB,EACjCC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACxBC,eAAe,EACfC,SAAS,EAMV;IACC,MAAM,EAAEC,UAAU,EAAE,GAAGhB;IAEvB,wEAAwE;IACxE,qBACE;kBACGgB,2BACC,KAACd;YACCE,YAAYA;YACZU,iBAAiBA;YACjBC,WAAWA;2BAGb,KAACd;YACCG,YAAYA;YACZS,0BAA0BA;YAC1BE,WAAWA;;;AAMrB"}
@@ -4,6 +4,7 @@ export interface ISubscribe {
4
4
  classNames?: SubscribeClasses;
5
5
  handleSubscribe?: (result: SubscribeResponse) => void;
6
6
  props?: any;
7
+ verifyUrl?: URL;
7
8
  }
8
9
  export type SubscribeClasses = {
9
10
  button?: string;
@@ -15,4 +16,4 @@ export type SubscribeClasses = {
15
16
  message?: string;
16
17
  section?: string;
17
18
  };
18
- export declare const Subscribe: ({ classNames, handleSubscribe, }: ISubscribe) => import("react").JSX.Element;
19
+ export declare const Subscribe: ({ classNames, handleSubscribe, verifyUrl, }: ISubscribe) => import("react").JSX.Element;
@@ -16,7 +16,7 @@ export const Subscribe = ({ classNames = {
16
16
  loading: '',
17
17
  message: '',
18
18
  section: ''
19
- }, handleSubscribe })=>{
19
+ }, handleSubscribe, verifyUrl })=>{
20
20
  const { refreshSubscriber, subscriber } = useSubscriber();
21
21
  const { serverURL } = useServerUrl();
22
22
  const sdk = new PayloadSDK({
@@ -44,9 +44,9 @@ export const Subscribe = ({ classNames = {
44
44
  const handleSubmit = async ()=>{
45
45
  const subscribeResult = await sdk.request({
46
46
  json: {
47
- afterVerifyUrl: window.location.pathname + '?now=' + new Date().toISOString(),
48
47
  email,
49
- optIns: selectedChannelIDs
48
+ optIns: selectedChannelIDs,
49
+ verifyUrl: verifyUrl?.href
50
50
  },
51
51
  method: 'POST',
52
52
  path: '/api/subscribe'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/Subscribe.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ChangeEvent, useEffect, useState } from 'react'\n\nimport type { Config, OptInChannel } from '../../copied/payload-types.js'\nimport type { SubscribeResponse } from '../../endpoints/subscribe.js'\n\nexport { SubscribeResponse }\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport { SelectOptInChannels } from './SelectOptInChannels.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 ISubscribe {\n classNames?: SubscribeClasses\n handleSubscribe?: (result: SubscribeResponse) => void\n props?: any\n}\n\nexport type SubscribeClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n section?: string\n}\n\ntype statusValues = 'default' | 'error' | 'sent' | 'updated'\n\nexport const Subscribe = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleSubscribe,\n}: ISubscribe) => {\n const { refreshSubscriber, subscriber } = useSubscriber()\n\n const { serverURL } = useServerUrl()\n\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const flattenChannels = (channels: (OptInChannel | string)[] | null | undefined) => {\n if (!channels) {\n return []\n }\n return channels.map((channel: OptInChannel | string) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n\n const [status, setStatus] = useState<statusValues>('default')\n const [result, setResult] = useState<string>()\n const [email, setEmail] = useState(subscriber ? subscriber.email : '')\n const [selectedChannelIDs, setSelectedChannelIDs] = useState<string[]>(() =>\n flattenChannels(subscriber?.optIns),\n )\n\n useEffect(() => {\n setEmail(subscriber?.email || '')\n setSelectedChannelIDs(flattenChannels(subscriber?.optIns))\n }, [subscriber])\n\n const handleOptInChannelsSelected = (result: OptInChannel[]) => {\n setSelectedChannelIDs(result.map((channel) => channel.id))\n }\n\n const handleSubmit = async () => {\n const subscribeResult = await sdk.request({\n json: {\n afterVerifyUrl: window.location.pathname + '?now=' + new Date().toISOString(),\n email,\n optIns: selectedChannelIDs,\n },\n method: 'POST',\n path: '/api/subscribe',\n })\n if (subscribeResult.ok) {\n const resultJson: SubscribeResponse = await subscribeResult.json()\n // // When subscriber optIns are updated...\n // | {\n // email: string\n // now: string\n // optIns: string[]\n // }\n // // When a verify link is emailed...\n // | {\n // emailResult: any\n // now: string\n // }\n // // When any error occurs...\n // | {\n // error: string\n // now: string\n // }\n // @ts-expect-error Silly type confusion\n const { emailResult, error } = resultJson\n if (error) {\n setStatus('error')\n setResult(`An error occured. Please try again. \\n ${error}`)\n } else if (emailResult) {\n setStatus('sent')\n setResult('An email has been sent containing your magic link.')\n } else if (email) {\n setStatus('updated')\n setResult(`You're subscriptions have been updated.`)\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n\n refreshSubscriber()\n\n if (handleSubscribe) {\n handleSubscribe(resultJson)\n }\n } else {\n // const resultText = await subscribeResult.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n }\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n <h2>Subscribe</h2>\n <div className={mergeClassNames([styles.section, classNames.section])}>\n <SelectOptInChannels\n handleOptInChannelsSelected={handleOptInChannelsSelected}\n selectedOptInChannelIDs={selectedChannelIDs}\n />\n </div>\n <form\n className={mergeClassNames([styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={async (e) => {\n e.preventDefault()\n await handleSubmit()\n }}\n >\n <div className={mergeClassNames([styles.section, classNames.section])}>\n {!subscriber && (\n <input\n aria-label=\"enter your email\"\n className={mergeClassNames([styles.emailInput, classNames.emailInput])}\n onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}\n placeholder=\"enter your email\"\n type=\"email\"\n value={email}\n />\n )}\n <button className={mergeClassNames([styles.button, classNames.button])} type=\"submit\">\n Save choices\n </button>\n </div>\n </form>\n {!!result && (\n <p\n className={mergeClassNames([\n styles.message,\n classNames.message,\n status == 'error' ? [styles.error, classNames.error] : [],\n ])}\n >\n {result}\n </p>\n )}\n </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useSubscriber","useServerUrl","mergeClassNames","SelectOptInChannels","styles","Subscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleSubscribe","refreshSubscriber","subscriber","serverURL","sdk","baseURL","flattenChannels","channels","map","channel","id","status","setStatus","result","setResult","email","setEmail","selectedChannelIDs","setSelectedChannelIDs","optIns","handleOptInChannelsSelected","handleSubmit","subscribeResult","request","json","afterVerifyUrl","window","location","pathname","Date","toISOString","method","path","ok","resultJson","emailResult","div","className","h2","selectedOptInChannelIDs","onSubmit","e","preventDefault","input","aria-label","onChange","target","value","placeholder","type","p"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/app/Subscribe.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ChangeEvent, useEffect, useState } from 'react'\n\nimport type { Config, OptInChannel } from '../../copied/payload-types.js'\nimport type { SubscribeResponse } from '../../endpoints/subscribe.js'\n\nexport { SubscribeResponse }\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport { SelectOptInChannels } from './SelectOptInChannels.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 ISubscribe {\n classNames?: SubscribeClasses\n handleSubscribe?: (result: SubscribeResponse) => void\n props?: any\n verifyUrl?: URL\n}\n\nexport type SubscribeClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n section?: string\n}\n\ntype statusValues = 'default' | 'error' | 'sent' | 'updated'\n\nexport const Subscribe = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleSubscribe,\n verifyUrl,\n}: ISubscribe) => {\n const { refreshSubscriber, subscriber } = useSubscriber()\n\n const { serverURL } = useServerUrl()\n\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const flattenChannels = (channels: (OptInChannel | string)[] | null | undefined) => {\n if (!channels) {\n return []\n }\n return channels.map((channel: OptInChannel | string) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n\n const [status, setStatus] = useState<statusValues>('default')\n const [result, setResult] = useState<string>()\n const [email, setEmail] = useState(subscriber ? subscriber.email : '')\n const [selectedChannelIDs, setSelectedChannelIDs] = useState<string[]>(() =>\n flattenChannels(subscriber?.optIns),\n )\n\n useEffect(() => {\n setEmail(subscriber?.email || '')\n setSelectedChannelIDs(flattenChannels(subscriber?.optIns))\n }, [subscriber])\n\n const handleOptInChannelsSelected = (result: OptInChannel[]) => {\n setSelectedChannelIDs(result.map((channel) => channel.id))\n }\n\n const handleSubmit = async () => {\n const subscribeResult = await sdk.request({\n json: {\n email,\n optIns: selectedChannelIDs,\n verifyUrl: verifyUrl?.href,\n },\n method: 'POST',\n path: '/api/subscribe',\n })\n if (subscribeResult.ok) {\n const resultJson: SubscribeResponse = await subscribeResult.json()\n // // When subscriber optIns are updated...\n // | {\n // email: string\n // now: string\n // optIns: string[]\n // }\n // // When a verify link is emailed...\n // | {\n // emailResult: any\n // now: string\n // }\n // // When any error occurs...\n // | {\n // error: string\n // now: string\n // }\n // @ts-expect-error Silly type confusion\n const { emailResult, error } = resultJson\n if (error) {\n setStatus('error')\n setResult(`An error occured. Please try again. \\n ${error}`)\n } else if (emailResult) {\n setStatus('sent')\n setResult('An email has been sent containing your magic link.')\n } else if (email) {\n setStatus('updated')\n setResult(`You're subscriptions have been updated.`)\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n\n refreshSubscriber()\n\n if (handleSubscribe) {\n handleSubscribe(resultJson)\n }\n } else {\n // const resultText = await subscribeResult.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n }\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n <h2>Subscribe</h2>\n <div className={mergeClassNames([styles.section, classNames.section])}>\n <SelectOptInChannels\n handleOptInChannelsSelected={handleOptInChannelsSelected}\n selectedOptInChannelIDs={selectedChannelIDs}\n />\n </div>\n <form\n className={mergeClassNames([styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={async (e) => {\n e.preventDefault()\n await handleSubmit()\n }}\n >\n <div className={mergeClassNames([styles.section, classNames.section])}>\n {!subscriber && (\n <input\n aria-label=\"enter your email\"\n className={mergeClassNames([styles.emailInput, classNames.emailInput])}\n onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}\n placeholder=\"enter your email\"\n type=\"email\"\n value={email}\n />\n )}\n <button className={mergeClassNames([styles.button, classNames.button])} type=\"submit\">\n Save choices\n </button>\n </div>\n </form>\n {!!result && (\n <p\n className={mergeClassNames([\n styles.message,\n classNames.message,\n status == 'error' ? [styles.error, classNames.error] : [],\n ])}\n >\n {result}\n </p>\n )}\n </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useSubscriber","useServerUrl","mergeClassNames","SelectOptInChannels","styles","Subscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleSubscribe","verifyUrl","refreshSubscriber","subscriber","serverURL","sdk","baseURL","flattenChannels","channels","map","channel","id","status","setStatus","result","setResult","email","setEmail","selectedChannelIDs","setSelectedChannelIDs","optIns","handleOptInChannelsSelected","handleSubmit","subscribeResult","request","json","href","method","path","ok","resultJson","emailResult","div","className","h2","selectedOptInChannelIDs","onSubmit","e","preventDefault","input","aria-label","onChange","target","value","placeholder","type","p"],"mappings":"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"}
@@ -2,22 +2,24 @@ import type { RequestMagicLinkResponse } from '../..//endpoints/requestMagicLink
2
2
  import type { VerifyMagicLinkResponse } from '../../endpoints/verifyMagicLink.js';
3
3
  export { VerifyMagicLinkResponse };
4
4
  export interface IVerifyMagicLink {
5
+ children?: React.ReactNode;
5
6
  classNames?: VerifyMagicLinkClasses;
6
7
  handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void;
7
8
  handleMagicLinkVerified?: (result: VerifyMagicLinkResponse) => void;
8
9
  renderButton?: (props: {
9
- forwardUrl?: string;
10
10
  name?: string;
11
11
  onClick?: () => any;
12
12
  text?: string;
13
13
  }) => React.ReactNode;
14
+ verifyUrl?: URL;
14
15
  }
15
16
  export type VerifyMagicLinkClasses = {
16
17
  button?: string;
17
18
  container?: string;
19
+ emailInput?: string;
18
20
  error?: string;
19
21
  form?: string;
20
22
  loading?: string;
21
23
  message?: string;
22
24
  };
23
- export declare const VerifyMagicLink: ({ classNames, handleMagicLinkRequested, handleMagicLinkVerified, renderButton, }: IVerifyMagicLink) => import("react").JSX.Element;
25
+ export declare const VerifyMagicLink: ({ children, classNames, handleMagicLinkRequested, handleMagicLinkVerified, renderButton, verifyUrl, }: IVerifyMagicLink) => import("react").JSX.Element;
@@ -7,25 +7,15 @@ 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';
10
- export const VerifyMagicLink = ({ classNames = {
10
+ export const VerifyMagicLink = ({ children, classNames = {
11
11
  button: '',
12
12
  container: '',
13
+ emailInput: '',
13
14
  error: '',
14
15
  form: '',
15
16
  loading: '',
16
17
  message: ''
17
- }, handleMagicLinkRequested, handleMagicLinkVerified, renderButton = ({ name, forwardUrl, onClick, text })=>forwardUrl ? /*#__PURE__*/ _jsx("a", {
18
- href: forwardUrl,
19
- children: /*#__PURE__*/ _jsx("button", {
20
- className: mergeClassNames([
21
- styles.button,
22
- classNames.button
23
- ]),
24
- name: name,
25
- type: "button",
26
- children: text
27
- })
28
- }) : /*#__PURE__*/ _jsx("button", {
18
+ }, handleMagicLinkRequested, handleMagicLinkVerified, renderButton = ({ name, onClick, text })=>/*#__PURE__*/ _jsx("button", {
29
19
  className: mergeClassNames([
30
20
  styles.button,
31
21
  classNames.button
@@ -34,13 +24,12 @@ export const VerifyMagicLink = ({ classNames = {
34
24
  onClick: onClick,
35
25
  type: "button",
36
26
  children: text
37
- }) })=>{
27
+ }), verifyUrl })=>{
38
28
  const { serverURL } = useServerUrl();
39
29
  const { // refreshSubscriber,
40
30
  subscriber } = useSubscriber();
41
31
  const searchParams = useSearchParams();
42
32
  const email = searchParams.get('email');
43
- const forwardUrl = searchParams.get('forwardUrl');
44
33
  const token = searchParams.get('token');
45
34
  const [result, setResult] = useState();
46
35
  const [isError, setIsError] = useState(false);
@@ -122,7 +111,7 @@ export const VerifyMagicLink = ({ classNames = {
122
111
  const emailResult = await sdk.request({
123
112
  json: {
124
113
  email,
125
- forwardUrl
114
+ verifyUrl: verifyUrl?.href
126
115
  },
127
116
  method: 'POST',
128
117
  path: '/api/emailToken'
@@ -175,16 +164,12 @@ export const VerifyMagicLink = ({ classNames = {
175
164
  classNames.form
176
165
  ]),
177
166
  children: [
178
- result && isError && renderButton({
167
+ result && isError && renderButton && renderButton({
179
168
  name: 'request',
180
169
  onClick: handleRequestAnother,
181
170
  text: 'Request another magic link'
182
171
  }),
183
- result && forwardUrl && renderButton({
184
- name: 'continue',
185
- forwardUrl,
186
- text: 'Continue'
187
- })
172
+ result && children
188
173
  ]
189
174
  })
190
175
  ]
@@ -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 { 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
+ {"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 children?: React.ReactNode\n classNames?: VerifyMagicLinkClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleMagicLinkVerified?: (result: VerifyMagicLinkResponse) => void\n renderButton?: (props: { name?: string; onClick?: () => any; text?: string }) => React.ReactNode\n verifyUrl?: URL\n}\n\nexport type VerifyMagicLinkClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n}\n\nexport const VerifyMagicLink = ({\n children,\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n },\n handleMagicLinkRequested,\n handleMagicLinkVerified,\n renderButton = ({ name, onClick, text }) => (\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 verifyUrl,\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 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 verifyUrl: verifyUrl?.href,\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 renderButton({\n name: 'request',\n onClick: handleRequestAnother,\n text: 'Request another magic link',\n })}\n {result && children}\n </div>\n </div>\n )}\n </>\n )\n}\n"],"names":["PayloadSDK","useSearchParams","useCallback","useEffect","useState","RequestMagicLink","useSubscriber","useServerUrl","mergeClassNames","styles","VerifyMagicLink","children","classNames","button","container","emailInput","error","form","loading","message","handleMagicLinkRequested","handleMagicLinkVerified","renderButton","name","onClick","text","className","type","verifyUrl","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","href","path","ok","div","p"],"mappings":"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"}
@@ -12,15 +12,15 @@ import { getTokenAndHash } from '../helpers/token.js';
12
12
  /**
13
13
  * requestMagicLink Endpoint Handler
14
14
  * @param req
15
- * @data { email }
15
+ * @data { email, verifyUrl }
16
16
  * @returns { status: 200, json: {message: string, now: date} }
17
17
  * @returns { status: 400, json: {error: ('Bad data' | 'Unknown email result'), now: date} }
18
18
  */ const requestMagicLinkHandler = async (req)=>{
19
19
  const data = req?.json ? await req.json() : {};
20
- const { email, forwardUrl } = data // if by POST data
20
+ const { email, verifyUrl } = data // if by POST data
21
21
  ;
22
22
  // const { email } = req.routeParams // if by path
23
- if (!email) {
23
+ if (!email || !verifyUrl) {
24
24
  return Response.json({
25
25
  error: 'Bad data',
26
26
  now: new Date().toISOString()
@@ -80,8 +80,7 @@ import { getTokenAndHash } from '../helpers/token.js';
80
80
  }
81
81
  });
82
82
  // Send email
83
- const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : '';
84
- const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`;
83
+ const magicLink = `${verifyUrl}${verifyUrl.search ? '&' : '?'}token=${token}&email=${email}`;
85
84
  const subject = data.subject || 'Your Magic Login Link';
86
85
  const message = `
87
86
  ${data.message || '<p>Use this link to log in:</p>'}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/requestMagicLink.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint, PayloadHandler, PayloadRequest, TypedUser } from 'payload'\n\nimport crypto from 'crypto'\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\n\nimport { getTokenAndHash } from '../helpers/token.js'\n\nexport type RequestMagicLinkResponse =\n | {\n emailResult: any\n now: string\n }\n | {\n error: string\n now: string\n }\n\n/**\n * createEndpointRequestMagicLink\n * @param options\n * @returns\n *\n * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug\n *\n */\nfunction createEndpointRequestMagicLink({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * requestMagicLink Endpoint Handler\n * @param req\n * @data { email }\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('Bad data' | 'Unknown email result'), now: date} }\n */\n const requestMagicLinkHandler: PayloadHandler = async (req: PayloadRequest) => {\n const data = req?.json ? await req.json() : {}\n const { email, forwardUrl } = data // if by POST data\n // const { email } = req.routeParams // if by path\n\n if (!email) {\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as RequestMagicLinkResponse,\n { status: 400 },\n )\n }\n\n const userResults = await req.payload.find({\n collection: subscribersCollectionSlug,\n where: {\n email: { equals: email },\n },\n })\n const user = userResults.docs[0] as TypedUser\n\n if (!user) {\n //\n // Create subscriber with status 'pending',\n // and an invisible unknowable password,\n //\n const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable\n const createResult = await req.payload.create({\n collection: subscribersCollectionSlug,\n data: {\n email,\n password: tokenHash2,\n status: 'pending',\n },\n draft: false,\n })\n if (!createResult) {\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as RequestMagicLinkResponse,\n { status: 400 },\n )\n }\n }\n\n // Update user with verificationToken\n const token = crypto.randomBytes(32).toString('hex')\n const tokenHash = crypto.createHash('sha256').update(token).digest('hex')\n const expiresAt = new Date(Date.now() + 15 * 60 * 1000) // 15 mins\n await req.payload.update({\n collection: subscribersCollectionSlug,\n data: {\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt.toISOString(),\n },\n where: {\n email: { equals: user.email },\n },\n })\n\n // Send email\n const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : ''\n const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`\n const subject = data.subject || 'Your Magic Login Link'\n const message = `\n ${data.message || '<p>Use this link to log in:</p>'}\n <p><a href=\"${magicLink}\"><b>Login</b></a></p>\n `\n const emailResult = await req.payload.sendEmail({\n html: message,\n subject,\n to: user.email,\n })\n // req.payload.logger.info(`email result: ${JSON.stringify(emailResult)}`)\n // return data; // Return data to allow normal submission if needed\n if (!emailResult) {\n return Response.json(\n {\n error: 'Unknown email result',\n now: new Date().toISOString(),\n } as RequestMagicLinkResponse,\n { status: 400 },\n )\n }\n req.payload.logger.info(`requestMagicLinkHandler email sent \\n ${magicLink}`)\n return Response.json({\n emailResult,\n now: new Date().toISOString(),\n } as RequestMagicLinkResponse)\n }\n\n /**\n * requestMagicLink Endpoint Config\n */\n const requestMagicLinkEndpoint: Endpoint = {\n handler: requestMagicLinkHandler,\n method: 'post',\n path: '/emailToken',\n }\n\n return requestMagicLinkEndpoint\n}\n\nexport default createEndpointRequestMagicLink\n"],"names":["crypto","defaultCollectionSlug","getTokenAndHash","createEndpointRequestMagicLink","subscribersCollectionSlug","requestMagicLinkHandler","req","data","json","email","forwardUrl","Response","error","now","Date","toISOString","status","userResults","payload","find","collection","where","equals","user","docs","tokenHash","tokenHash2","createResult","create","password","draft","token","randomBytes","toString","createHash","update","digest","expiresAt","verificationToken","verificationTokenExpires","forwardUrlParam","encodeURI","magicLink","config","serverURL","subject","message","emailResult","sendEmail","html","to","logger","info","requestMagicLinkEndpoint","handler","method","path"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/endpoints/requestMagicLink.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint, PayloadHandler, PayloadRequest, TypedUser } from 'payload'\n\nimport crypto from 'crypto'\n\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\nimport { getTokenAndHash } from '../helpers/token.js'\n\nexport type RequestMagicLinkResponse =\n | {\n emailResult: any\n now: string\n }\n | {\n error: string\n now: string\n }\n\n/**\n * createEndpointRequestMagicLink\n * @param options\n * @returns\n *\n * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug\n *\n */\nfunction createEndpointRequestMagicLink({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * requestMagicLink Endpoint Handler\n * @param req\n * @data { email, verifyUrl }\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('Bad data' | 'Unknown email result'), now: date} }\n */\n const requestMagicLinkHandler: PayloadHandler = async (req: PayloadRequest) => {\n const data = req?.json ? await req.json() : {}\n const { email, verifyUrl } = data // if by POST data\n // const { email } = req.routeParams // if by path\n\n if (!email || !verifyUrl) {\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as RequestMagicLinkResponse,\n { status: 400 },\n )\n }\n\n const userResults = await req.payload.find({\n collection: subscribersCollectionSlug,\n where: {\n email: { equals: email },\n },\n })\n const user = userResults.docs[0] as TypedUser\n\n if (!user) {\n //\n // Create subscriber with status 'pending',\n // and an invisible unknowable password,\n //\n const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable\n const createResult = await req.payload.create({\n collection: subscribersCollectionSlug,\n data: {\n email,\n password: tokenHash2,\n status: 'pending',\n },\n draft: false,\n })\n if (!createResult) {\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as RequestMagicLinkResponse,\n { status: 400 },\n )\n }\n }\n\n // Update user with verificationToken\n const token = crypto.randomBytes(32).toString('hex')\n const tokenHash = crypto.createHash('sha256').update(token).digest('hex')\n const expiresAt = new Date(Date.now() + 15 * 60 * 1000) // 15 mins\n await req.payload.update({\n collection: subscribersCollectionSlug,\n data: {\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt.toISOString(),\n },\n where: {\n email: { equals: user.email },\n },\n })\n\n // Send email\n const magicLink = `${verifyUrl}${verifyUrl.search ? '&' : '?'}token=${token}&email=${email}`\n const subject = data.subject || 'Your Magic Login Link'\n const message = `\n ${data.message || '<p>Use this link to log in:</p>'}\n <p><a href=\"${magicLink}\"><b>Login</b></a></p>\n `\n const emailResult = await req.payload.sendEmail({\n html: message,\n subject,\n to: user.email,\n })\n // req.payload.logger.info(`email result: ${JSON.stringify(emailResult)}`)\n // return data; // Return data to allow normal submission if needed\n if (!emailResult) {\n return Response.json(\n {\n error: 'Unknown email result',\n now: new Date().toISOString(),\n } as RequestMagicLinkResponse,\n { status: 400 },\n )\n }\n req.payload.logger.info(`requestMagicLinkHandler email sent \\n ${magicLink}`)\n return Response.json({\n emailResult,\n now: new Date().toISOString(),\n } as RequestMagicLinkResponse)\n }\n\n /**\n * requestMagicLink Endpoint Config\n */\n const requestMagicLinkEndpoint: Endpoint = {\n handler: requestMagicLinkHandler,\n method: 'post',\n path: '/emailToken',\n }\n\n return requestMagicLinkEndpoint\n}\n\nexport default createEndpointRequestMagicLink\n"],"names":["crypto","defaultCollectionSlug","getTokenAndHash","createEndpointRequestMagicLink","subscribersCollectionSlug","requestMagicLinkHandler","req","data","json","email","verifyUrl","Response","error","now","Date","toISOString","status","userResults","payload","find","collection","where","equals","user","docs","tokenHash","tokenHash2","createResult","create","password","draft","token","randomBytes","toString","createHash","update","digest","expiresAt","verificationToken","verificationTokenExpires","magicLink","search","subject","message","emailResult","sendEmail","html","to","logger","info","requestMagicLinkEndpoint","handler","method","path"],"mappings":"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"}
@@ -17,7 +17,7 @@ import { verifyOptIns } from '../helpers/verifyOptIns.js';
17
17
  * @returns { status: 400, json: {error: ('Bad data' | 'Already subscribed' | 'Unknown email result'), now: date} }
18
18
  */ const subscribeHandler = async (req)=>{
19
19
  const data = req?.json ? await req.json() : {};
20
- const { afterVerifyUrl, email, optIns } = data // if by POST data
20
+ const { email, optIns, verifyUrl } = data // if by POST data
21
21
  ;
22
22
  // const { email } = req.routeParams // if by path
23
23
  //
@@ -54,9 +54,8 @@ import { verifyOptIns } from '../helpers/verifyOptIns.js';
54
54
  });
55
55
  return updateResults;
56
56
  };
57
- const sendVerifyEmail = async ({ email, forwardUrl, linkText, message, subject, token })=>{
58
- const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : '';
59
- const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`;
57
+ const sendVerifyEmail = async ({ email, linkText, message, subject, token, verifyUrl })=>{
58
+ const magicLink = `${verifyUrl}${verifyUrl?.search ? '&' : '?'}token=${token}&email=${email}`;
60
59
  const html = message + `<p><a href="${magicLink}">${linkText}</a></p>`;
61
60
  const emailResult = await req.payload.sendEmail({
62
61
  html,
@@ -155,11 +154,11 @@ import { verifyOptIns } from '../helpers/verifyOptIns.js';
155
154
  // Send email
156
155
  const emailResult = await sendVerifyEmail({
157
156
  email,
158
- forwardUrl: afterVerifyUrl,
159
157
  linkText: '<b>Verify</b>',
160
158
  message: data.message || `<p>Click here to verify your subscription:</p>`,
161
159
  subject: data.subject || 'Please verify your subscription',
162
- token
160
+ token,
161
+ verifyUrl
163
162
  });
164
163
  if (!emailResult) {
165
164
  req.payload.logger.error(JSON.stringify({
@@ -211,11 +210,11 @@ import { verifyOptIns } from '../helpers/verifyOptIns.js';
211
210
  // Send email
212
211
  const emailResult = await sendVerifyEmail({
213
212
  email,
214
- forwardUrl: afterVerifyUrl,
215
213
  linkText: 'Verify',
216
214
  message: data.message || `<h1>Click here to verify your subscription:</h1>`,
217
215
  subject: data.subject || 'Please verify your subscription',
218
- token
216
+ token,
217
+ verifyUrl
219
218
  });
220
219
  if (!emailResult) {
221
220
  req.payload.logger.error(JSON.stringify({
@@ -264,11 +263,11 @@ import { verifyOptIns } from '../helpers/verifyOptIns.js';
264
263
  }
265
264
  const emailResult = await sendVerifyEmail({
266
265
  email,
267
- forwardUrl: afterVerifyUrl,
268
266
  linkText: 'Verify',
269
267
  message: data.message || `<h1>Click here to verify your email:</h1>`,
270
268
  subject: data.subject || 'Please verify your subscription',
271
- token
269
+ token,
270
+ verifyUrl
272
271
  });
273
272
  if (!emailResult) {
274
273
  req.payload.logger.error(JSON.stringify({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/subscribe.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint, PayloadHandler } from 'payload'\nimport type { Subscriber } from 'src/copied/payload-types.js'\n\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\n\nimport { getTokenAndHash } from '../helpers/token.js'\nimport { verifyOptIns } from '../helpers/verifyOptIns.js'\n\nexport type SubscribeResponse =\n // When subscriber optIns are updated...\n | {\n email: string\n now: string\n optIns: string[]\n }\n // When a verify link is emailed...\n | {\n emailResult: any\n now: string\n }\n // When any error occurs...\n | {\n error: string\n now: string\n }\n\n/**\n * createEndpointLogout\n * @param options\n * @returns\n *\n * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug\n *\n */\nfunction createEndpointSubscribe({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * subscribe Endpoint Handler\n * @param req\n * @data { email }\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('Bad data' | 'Already subscribed' | 'Unknown email result'), now: date} }\n */\n const subscribeHandler: PayloadHandler = async (req) => {\n const data = req?.json ? await req.json() : {}\n const {\n afterVerifyUrl,\n email,\n optIns,\n }: { afterVerifyUrl: string; email: string; optIns: string[] } = data // if by POST data\n // const { email } = req.routeParams // if by path\n\n //\n // HELPERS\n // Some of these functions make use of the scope within handler,\n // and would have to be refactored if moved out.\n //\n const createSubscriber = async ({\n optIns,\n password,\n status,\n verificationToken,\n verificationTokenExpires,\n }: {\n email: string\n optIns?: string[]\n password?: string\n status?: 'pending' | 'subscribed' | 'unsubscribed'\n verificationToken?: string\n verificationTokenExpires?: Date\n }) => {\n await req.payload.create({\n collection: subscribersCollectionSlug,\n data: {\n email,\n optIns,\n password,\n status: status || 'pending',\n verificationToken,\n verificationTokenExpires: verificationTokenExpires?.toISOString(),\n },\n draft: false,\n })\n }\n const updateSubscriber = async ({\n id,\n optIns,\n password,\n status,\n verificationToken,\n verificationTokenExpires,\n }: {\n id: string\n optIns?: string[]\n password?: string\n status?: 'pending' | 'subscribed' | 'unsubscribed'\n verificationToken?: string\n verificationTokenExpires?: Date | null\n }) => {\n const updateResults = await req.payload.update({\n id,\n collection: subscribersCollectionSlug,\n data: {\n optIns,\n password,\n status,\n verificationToken,\n verificationTokenExpires: verificationTokenExpires?.toISOString() || null,\n },\n depth: 0,\n })\n return updateResults\n }\n const sendVerifyEmail = async ({\n email,\n forwardUrl,\n linkText,\n message,\n subject,\n token,\n }: {\n email: string\n forwardUrl?: string\n linkText: string\n message: string\n subject: string\n token: string\n }) => {\n const forwardUrlParam = forwardUrl ? `&forwardUrl=${encodeURI(forwardUrl)}` : ''\n const magicLink = `${req.payload.config.serverURL}/verify?token=${token}&email=${email}${forwardUrlParam}`\n const html = message + `<p><a href=\"${magicLink}\">${linkText}</a></p>`\n const emailResult = await req.payload.sendEmail({\n html,\n subject,\n to: email,\n })\n req.payload.logger.info(`subscribe email sent \\n ${magicLink}`)\n return emailResult\n }\n\n //\n // VALIDATE INPUT\n //\n // Require email\n if (!email) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Bad data', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // Validate OptInChannels\n const { invalidOptInsInput, verifiedOptInIDs } = await verifyOptIns(req.payload, optIns)\n\n if (invalidOptInsInput) {\n req.payload.logger.error(\n JSON.stringify(\n {\n error: 'Invalid input: ' + JSON.stringify(optIns),\n now: new Date().toISOString(),\n } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n {\n error: 'Invalid input: ' + JSON.stringify(optIns),\n now: new Date().toISOString(),\n } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // Verify subscriber exists\n const userResults = await req.payload.find({\n collection: subscribersCollectionSlug,\n where: {\n email: { equals: email },\n },\n })\n const subscriber = userResults.docs[0] as Subscriber\n\n //\n // Now we have a subscriber and validatedOptIns\n // Handle scenarios\n //\n // ********************************************************\n //\n if (req.user && req.user.email != email) {\n //\n // Error: Auth-ed user doesn't match subscriber email\n //\n req.payload.logger.error(\n JSON.stringify(\n {\n error: 'Unauthorized: ' + email,\n now: new Date().toISOString(),\n } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n {\n error: 'Unauthorized: ' + email,\n now: new Date().toISOString(),\n } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // ********************************************************\n //\n if (!subscriber) {\n //\n // Create subscriber with status 'pending',\n // and an invisible unknowable password,\n // and send a verify email\n // Pass all optIns through verify link\n //\n const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link\n const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable\n await createSubscriber({\n email,\n optIns,\n password: tokenHash2,\n status: 'pending',\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt,\n })\n\n //\n // Send email\n const emailResult = await sendVerifyEmail({\n email,\n forwardUrl: afterVerifyUrl,\n linkText: '<b>Verify</b>',\n message: data.message || `<p>Click here to verify your subscription:</p>`,\n subject: data.subject || 'Please verify your subscription',\n token,\n })\n if (!emailResult) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)\n //\n }\n //\n // ********************************************************\n //\n if (!req.user && subscriber) {\n //\n // Send magic link to log the user in\n // Pass all optIns through verify link\n //\n const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link\n // Update subscriber with token for pending email\n const updateResults = await updateSubscriber({\n id: subscriber.id,\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt,\n })\n if (!updateResults) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // Send email\n const emailResult = await sendVerifyEmail({\n email,\n forwardUrl: afterVerifyUrl,\n linkText: 'Verify',\n message: data.message || `<h1>Click here to verify your subscription:</h1>`,\n subject: data.subject || 'Please verify your subscription',\n token,\n })\n if (!emailResult) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)\n }\n //\n // ********************************************************\n //\n if (req.user && subscriber && subscriber.status == 'pending') {\n //\n // Send magic link to verify the email and log the user in\n // Pass all optIns through verify link\n //\n const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link\n // Create subscriber with token for pending email\n const updateResults = await updateSubscriber({\n id: subscriber.id,\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt,\n })\n if (!updateResults) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n const emailResult = await sendVerifyEmail({\n email,\n forwardUrl: afterVerifyUrl,\n linkText: 'Verify',\n message: data.message || `<h1>Click here to verify your email:</h1>`,\n subject: data.subject || 'Please verify your subscription',\n token,\n })\n if (!emailResult) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)\n }\n\n //\n // ********************************************************\n //\n if (req.user && subscriber && subscriber.status != 'pending') {\n //\n // Update subscriber with status 'subscribed',\n // an invisible unknowable password,\n // and if any optIns input exists, set subscriber optIns\n // to EXACTLY verifiedOptInIDs (potentially unsubscribing from any not in verifiedOptInIDs)\n //\n const { tokenHash } = getTokenAndHash() // Use for magic link\n // Update subscriber with optIns\n const updateResults = (await updateSubscriber({\n id: subscriber.id,\n optIns: verifiedOptInIDs,\n password: tokenHash,\n status: 'subscribed',\n verificationToken: '',\n verificationTokenExpires: null,\n })) as Subscriber\n\n // Return results, including the verified optIns\n return Response.json({\n email: updateResults.email,\n now: new Date().toISOString(),\n optIns: updateResults.optIns,\n } as SubscribeResponse)\n }\n //\n // Uncaught case\n //\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n /**\n * subscribe Endpoint Config\n */\n const subscribeEndpoint: Endpoint = {\n handler: subscribeHandler,\n method: 'post',\n path: '/subscribe',\n }\n\n return subscribeEndpoint\n}\n\nexport default createEndpointSubscribe\n"],"names":["defaultCollectionSlug","getTokenAndHash","verifyOptIns","createEndpointSubscribe","subscribersCollectionSlug","subscribeHandler","req","data","json","afterVerifyUrl","email","optIns","createSubscriber","password","status","verificationToken","verificationTokenExpires","payload","create","collection","toISOString","draft","updateSubscriber","id","updateResults","update","depth","sendVerifyEmail","forwardUrl","linkText","message","subject","token","forwardUrlParam","encodeURI","magicLink","config","serverURL","html","emailResult","sendEmail","to","logger","info","error","JSON","stringify","now","Date","undefined","Response","invalidOptInsInput","verifiedOptInIDs","userResults","find","where","equals","subscriber","docs","user","expiresAt","tokenHash","tokenHash2","subscribeEndpoint","handler","method","path"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/endpoints/subscribe.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint, PayloadHandler } from 'payload'\nimport type { Subscriber } from 'src/copied/payload-types.js'\n\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\nimport { getTokenAndHash } from '../helpers/token.js'\nimport { verifyOptIns } from '../helpers/verifyOptIns.js'\n\nexport type SubscribeResponse =\n // When subscriber optIns are updated...\n | {\n email: string\n now: string\n optIns: string[]\n }\n // When a verify link is emailed...\n | {\n emailResult: any\n now: string\n }\n // When any error occurs...\n | {\n error: string\n now: string\n }\n\n/**\n * createEndpointLogout\n * @param options\n * @returns\n *\n * Factory to generate the endpoint config with handler based on input option for subscribersCollectionSlug\n *\n */\nfunction createEndpointSubscribe({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * subscribe Endpoint Handler\n * @param req\n * @data { email }\n * @returns { status: 200, json: {message: string, now: date} }\n * @returns { status: 400, json: {error: ('Bad data' | 'Already subscribed' | 'Unknown email result'), now: date} }\n */\n const subscribeHandler: PayloadHandler = async (req) => {\n const data = req?.json ? await req.json() : {}\n const { email, optIns, verifyUrl }: { email: string; optIns: string[]; verifyUrl: string } =\n data // if by POST data\n // const { email } = req.routeParams // if by path\n\n //\n // HELPERS\n // Some of these functions make use of the scope within handler,\n // and would have to be refactored if moved out.\n //\n const createSubscriber = async ({\n optIns,\n password,\n status,\n verificationToken,\n verificationTokenExpires,\n }: {\n email: string\n optIns?: string[]\n password?: string\n status?: 'pending' | 'subscribed' | 'unsubscribed'\n verificationToken?: string\n verificationTokenExpires?: Date\n }) => {\n await req.payload.create({\n collection: subscribersCollectionSlug,\n data: {\n email,\n optIns,\n password,\n status: status || 'pending',\n verificationToken,\n verificationTokenExpires: verificationTokenExpires?.toISOString(),\n },\n draft: false,\n })\n }\n const updateSubscriber = async ({\n id,\n optIns,\n password,\n status,\n verificationToken,\n verificationTokenExpires,\n }: {\n id: string\n optIns?: string[]\n password?: string\n status?: 'pending' | 'subscribed' | 'unsubscribed'\n verificationToken?: string\n verificationTokenExpires?: Date | null\n }) => {\n const updateResults = await req.payload.update({\n id,\n collection: subscribersCollectionSlug,\n data: {\n optIns,\n password,\n status,\n verificationToken,\n verificationTokenExpires: verificationTokenExpires?.toISOString() || null,\n },\n depth: 0,\n })\n return updateResults\n }\n const sendVerifyEmail = async ({\n email,\n linkText,\n message,\n subject,\n token,\n verifyUrl,\n }: {\n email: string\n linkText: string\n message: string\n subject: string\n token: string\n verifyUrl?: string\n }) => {\n const magicLink = `${verifyUrl}${verifyUrl?.search ? '&' : '?'}token=${token}&email=${email}`\n const html = message + `<p><a href=\"${magicLink}\">${linkText}</a></p>`\n const emailResult = await req.payload.sendEmail({\n html,\n subject,\n to: email,\n })\n req.payload.logger.info(`subscribe email sent \\n ${magicLink}`)\n return emailResult\n }\n\n //\n // VALIDATE INPUT\n //\n // Require email\n if (!email) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Bad data', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Bad data', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // Validate OptInChannels\n const { invalidOptInsInput, verifiedOptInIDs } = await verifyOptIns(req.payload, optIns)\n\n if (invalidOptInsInput) {\n req.payload.logger.error(\n JSON.stringify(\n {\n error: 'Invalid input: ' + JSON.stringify(optIns),\n now: new Date().toISOString(),\n } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n {\n error: 'Invalid input: ' + JSON.stringify(optIns),\n now: new Date().toISOString(),\n } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // Verify subscriber exists\n const userResults = await req.payload.find({\n collection: subscribersCollectionSlug,\n where: {\n email: { equals: email },\n },\n })\n const subscriber = userResults.docs[0] as Subscriber\n\n //\n // Now we have a subscriber and validatedOptIns\n // Handle scenarios\n //\n // ********************************************************\n //\n if (req.user && req.user.email != email) {\n //\n // Error: Auth-ed user doesn't match subscriber email\n //\n req.payload.logger.error(\n JSON.stringify(\n {\n error: 'Unauthorized: ' + email,\n now: new Date().toISOString(),\n } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n {\n error: 'Unauthorized: ' + email,\n now: new Date().toISOString(),\n } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // ********************************************************\n //\n if (!subscriber) {\n //\n // Create subscriber with status 'pending',\n // and an invisible unknowable password,\n // and send a verify email\n // Pass all optIns through verify link\n //\n const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link\n const { tokenHash: tokenHash2 } = getTokenAndHash() // Unknowable\n await createSubscriber({\n email,\n optIns,\n password: tokenHash2,\n status: 'pending',\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt,\n })\n\n //\n // Send email\n const emailResult = await sendVerifyEmail({\n email,\n linkText: '<b>Verify</b>',\n message: data.message || `<p>Click here to verify your subscription:</p>`,\n subject: data.subject || 'Please verify your subscription',\n token,\n verifyUrl,\n })\n if (!emailResult) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)\n //\n }\n //\n // ********************************************************\n //\n if (!req.user && subscriber) {\n //\n // Send magic link to log the user in\n // Pass all optIns through verify link\n //\n const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link\n // Update subscriber with token for pending email\n const updateResults = await updateSubscriber({\n id: subscriber.id,\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt,\n })\n if (!updateResults) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n //\n // Send email\n const emailResult = await sendVerifyEmail({\n email,\n linkText: 'Verify',\n message: data.message || `<h1>Click here to verify your subscription:</h1>`,\n subject: data.subject || 'Please verify your subscription',\n token,\n verifyUrl,\n })\n if (!emailResult) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)\n }\n //\n // ********************************************************\n //\n if (req.user && subscriber && subscriber.status == 'pending') {\n //\n // Send magic link to verify the email and log the user in\n // Pass all optIns through verify link\n //\n const { expiresAt, token, tokenHash } = getTokenAndHash(15 * 60 * 1000) // Use for magic link\n // Create subscriber with token for pending email\n const updateResults = await updateSubscriber({\n id: subscriber.id,\n verificationToken: tokenHash,\n verificationTokenExpires: expiresAt,\n })\n if (!updateResults) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n const emailResult = await sendVerifyEmail({\n email,\n linkText: 'Verify',\n message: data.message || `<h1>Click here to verify your email:</h1>`,\n subject: data.subject || 'Please verify your subscription',\n token,\n verifyUrl,\n })\n if (!emailResult) {\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown email result', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n return Response.json({ emailResult, now: new Date().toISOString() } as SubscribeResponse)\n }\n\n //\n // ********************************************************\n //\n if (req.user && subscriber && subscriber.status != 'pending') {\n //\n // Update subscriber with status 'subscribed',\n // an invisible unknowable password,\n // and if any optIns input exists, set subscriber optIns\n // to EXACTLY verifiedOptInIDs (potentially unsubscribing from any not in verifiedOptInIDs)\n //\n const { tokenHash } = getTokenAndHash() // Use for magic link\n // Update subscriber with optIns\n const updateResults = (await updateSubscriber({\n id: subscriber.id,\n optIns: verifiedOptInIDs,\n password: tokenHash,\n status: 'subscribed',\n verificationToken: '',\n verificationTokenExpires: null,\n })) as Subscriber\n\n // Return results, including the verified optIns\n return Response.json({\n email: updateResults.email,\n now: new Date().toISOString(),\n optIns: updateResults.optIns,\n } as SubscribeResponse)\n }\n //\n // Uncaught case\n //\n req.payload.logger.error(\n JSON.stringify(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n undefined,\n 2,\n ),\n )\n return Response.json(\n { error: 'Unknown error', now: new Date().toISOString() } as SubscribeResponse,\n { status: 400 },\n )\n }\n\n /**\n * subscribe Endpoint Config\n */\n const subscribeEndpoint: Endpoint = {\n handler: subscribeHandler,\n method: 'post',\n path: '/subscribe',\n }\n\n return subscribeEndpoint\n}\n\nexport default createEndpointSubscribe\n"],"names":["defaultCollectionSlug","getTokenAndHash","verifyOptIns","createEndpointSubscribe","subscribersCollectionSlug","subscribeHandler","req","data","json","email","optIns","verifyUrl","createSubscriber","password","status","verificationToken","verificationTokenExpires","payload","create","collection","toISOString","draft","updateSubscriber","id","updateResults","update","depth","sendVerifyEmail","linkText","message","subject","token","magicLink","search","html","emailResult","sendEmail","to","logger","info","error","JSON","stringify","now","Date","undefined","Response","invalidOptInsInput","verifiedOptInIDs","userResults","find","where","equals","subscriber","docs","user","expiresAt","tokenHash","tokenHash2","subscribeEndpoint","handler","method","path"],"mappings":"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"}
package/package.json CHANGED
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "registry": "https://registry.npmjs.org/",
71
71
  "dependencies": {},
72
- "version": "0.0.6",
72
+ "version": "0.0.7",
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",