payload-plugin-newsletter 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/ANNOUNCEMENT.md +66 -0
  2. package/CHANGELOG.md +49 -1
  3. package/README.md +46 -15
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/collections/NewsletterSettings.d.ts +4 -0
  6. package/dist/collections/NewsletterSettings.d.ts.map +1 -0
  7. package/dist/collections/Subscribers.d.ts.map +1 -1
  8. package/dist/endpoints/preferences.d.ts.map +1 -1
  9. package/dist/endpoints/subscribe.d.ts.map +1 -1
  10. package/dist/endpoints/unsubscribe.d.ts.map +1 -1
  11. package/dist/endpoints/verify-magic-link.d.ts.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/src/collections/NewsletterSettings.js +389 -0
  14. package/dist/src/collections/NewsletterSettings.js.map +1 -0
  15. package/dist/src/collections/Subscribers.js.map +1 -1
  16. package/dist/src/components/MagicLinkVerify.js +1 -1
  17. package/dist/src/components/MagicLinkVerify.js.map +1 -1
  18. package/dist/src/endpoints/preferences.js +16 -4
  19. package/dist/src/endpoints/preferences.js.map +1 -1
  20. package/dist/src/endpoints/subscribe.js +14 -3
  21. package/dist/src/endpoints/subscribe.js.map +1 -1
  22. package/dist/src/endpoints/unsubscribe.js +10 -2
  23. package/dist/src/endpoints/unsubscribe.js.map +1 -1
  24. package/dist/src/endpoints/verify-magic-link.js +13 -3
  25. package/dist/src/endpoints/verify-magic-link.js.map +1 -1
  26. package/dist/src/index.js +18 -12
  27. package/dist/src/index.js.map +1 -1
  28. package/dist/src/templates/NewsletterTemplate.js.map +1 -1
  29. package/dist/src/templates/WelcomeTemplate.js.map +1 -1
  30. package/dist/src/types/index.js.map +1 -1
  31. package/dist/templates/NewsletterTemplate.d.ts.map +1 -1
  32. package/dist/templates/WelcomeTemplate.d.ts.map +1 -1
  33. package/dist/types/index.d.ts +5 -0
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/package.json +9 -28
  36. package/CLAUDE.md +0 -110
  37. package/dist/globals/EmailSettings.d.ts +0 -4
  38. package/dist/globals/EmailSettings.d.ts.map +0 -1
  39. package/dist/src/globals/EmailSettings.js +0 -252
  40. package/dist/src/globals/EmailSettings.js.map +0 -1
@@ -24,10 +24,16 @@ export const createPreferencesEndpoint = (config)=>{
24
24
  error: error instanceof Error ? error.message : 'Invalid token'
25
25
  });
26
26
  }
27
- // Get subscriber
27
+ // Get subscriber - use synthetic user to ensure access control
28
28
  const subscriber = await req.payload.findByID({
29
29
  collection: config.subscribersSlug || 'subscribers',
30
- id: payload.subscriberId
30
+ id: payload.subscriberId,
31
+ overrideAccess: false,
32
+ user: {
33
+ collection: 'subscribers',
34
+ id: payload.subscriberId,
35
+ email: payload.email
36
+ }
31
37
  });
32
38
  if (!subscriber) {
33
39
  return res.status(404).json({
@@ -93,11 +99,17 @@ export const createUpdatePreferencesEndpoint = (config)=>{
93
99
  if (emailPreferences !== undefined) {
94
100
  updateData.emailPreferences = emailPreferences;
95
101
  }
96
- // Update subscriber
102
+ // Update subscriber - use synthetic user to ensure only updating own data
97
103
  const subscriber = await req.payload.update({
98
104
  collection: config.subscribersSlug || 'subscribers',
99
105
  id: payload.subscriberId,
100
- data: updateData
106
+ data: updateData,
107
+ overrideAccess: false,
108
+ user: {
109
+ collection: 'subscribers',
110
+ id: payload.subscriberId,
111
+ email: payload.email
112
+ }
101
113
  });
102
114
  res.json({
103
115
  success: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/endpoints/preferences.ts"],"sourcesContent":["import type { Endpoint, PayloadHandler } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { verifySessionToken } from '../utils/jwt'\n\nexport const createPreferencesEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/preferences',\n method: 'get',\n handler: (async (req: any, res: any) => {\n try {\n // Get token from Authorization header\n const authHeader = req.headers.authorization\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\n return res.status(401).json({\n success: false,\n error: 'Authorization required',\n })\n }\n\n const token = authHeader.substring(7)\n\n // Verify session token\n let payload\n try {\n payload = verifySessionToken(token)\n } catch (error: unknown) {\n return res.status(401).json({\n success: false,\n error: error instanceof Error ? error.message : 'Invalid token',\n })\n }\n\n // Get subscriber\n const subscriber = await req.payload.findByID({\n collection: config.subscribersSlug || 'subscribers',\n id: payload.subscriberId,\n })\n\n if (!subscriber) {\n return res.status(404).json({\n success: false,\n error: 'Subscriber not found',\n })\n }\n\n res.json({\n success: true,\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n name: subscriber.name,\n locale: subscriber.locale,\n emailPreferences: subscriber.emailPreferences,\n subscriptionStatus: subscriber.subscriptionStatus,\n },\n })\n } catch (error: unknown) {\n console.error('Get preferences error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to get preferences',\n })\n }\n }) as PayloadHandler,\n }\n}\n\nexport const createUpdatePreferencesEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/preferences',\n method: 'post',\n handler: (async (req: any, res: any) => {\n try {\n // Get token from Authorization header\n const authHeader = req.headers.authorization\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\n return res.status(401).json({\n success: false,\n error: 'Authorization required',\n })\n }\n\n const token = authHeader.substring(7)\n\n // Verify session token\n let payload\n try {\n payload = verifySessionToken(token)\n } catch (error: unknown) {\n return res.status(401).json({\n success: false,\n error: error instanceof Error ? error.message : 'Invalid token',\n })\n }\n\n const { name, locale, emailPreferences } = req.body\n\n // Prepare update data\n const updateData: any = {}\n \n if (name !== undefined) {\n updateData.name = name\n }\n \n if (locale !== undefined) {\n updateData.locale = locale\n }\n \n if (emailPreferences !== undefined) {\n updateData.emailPreferences = emailPreferences\n }\n\n // Update subscriber\n const subscriber = await req.payload.update({\n collection: config.subscribersSlug || 'subscribers',\n id: payload.subscriberId,\n data: updateData,\n })\n\n res.json({\n success: true,\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n name: subscriber.name,\n locale: subscriber.locale,\n emailPreferences: subscriber.emailPreferences,\n subscriptionStatus: subscriber.subscriptionStatus,\n },\n })\n } catch (error: unknown) {\n console.error('Update preferences error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to update preferences',\n })\n }\n }) as PayloadHandler,\n }\n}"],"names":["verifySessionToken","createPreferencesEndpoint","config","path","method","handler","req","res","authHeader","headers","authorization","startsWith","status","json","success","error","token","substring","payload","Error","message","subscriber","findByID","collection","subscribersSlug","id","subscriberId","email","name","locale","emailPreferences","subscriptionStatus","console","createUpdatePreferencesEndpoint","body","updateData","undefined","update","data"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/endpoints/preferences.ts"],"sourcesContent":["import type { Endpoint, PayloadHandler } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { verifySessionToken } from '../utils/jwt'\n\nexport const createPreferencesEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/preferences',\n method: 'get',\n handler: (async (req: any, res: any) => {\n try {\n // Get token from Authorization header\n const authHeader = req.headers.authorization\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\n return res.status(401).json({\n success: false,\n error: 'Authorization required',\n })\n }\n\n const token = authHeader.substring(7)\n\n // Verify session token\n let payload\n try {\n payload = verifySessionToken(token)\n } catch (error: unknown) {\n return res.status(401).json({\n success: false,\n error: error instanceof Error ? error.message : 'Invalid token',\n })\n }\n\n // Get subscriber - use synthetic user to ensure access control\n const subscriber = await req.payload.findByID({\n collection: config.subscribersSlug || 'subscribers',\n id: payload.subscriberId,\n overrideAccess: false,\n user: {\n collection: 'subscribers',\n id: payload.subscriberId,\n email: payload.email,\n },\n })\n\n if (!subscriber) {\n return res.status(404).json({\n success: false,\n error: 'Subscriber not found',\n })\n }\n\n res.json({\n success: true,\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n name: subscriber.name,\n locale: subscriber.locale,\n emailPreferences: subscriber.emailPreferences,\n subscriptionStatus: subscriber.subscriptionStatus,\n },\n })\n } catch (error: unknown) {\n console.error('Get preferences error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to get preferences',\n })\n }\n }) as PayloadHandler,\n }\n}\n\nexport const createUpdatePreferencesEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/preferences',\n method: 'post',\n handler: (async (req: any, res: any) => {\n try {\n // Get token from Authorization header\n const authHeader = req.headers.authorization\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\n return res.status(401).json({\n success: false,\n error: 'Authorization required',\n })\n }\n\n const token = authHeader.substring(7)\n\n // Verify session token\n let payload\n try {\n payload = verifySessionToken(token)\n } catch (error: unknown) {\n return res.status(401).json({\n success: false,\n error: error instanceof Error ? error.message : 'Invalid token',\n })\n }\n\n const { name, locale, emailPreferences } = req.body\n\n // Prepare update data\n const updateData: any = {}\n \n if (name !== undefined) {\n updateData.name = name\n }\n \n if (locale !== undefined) {\n updateData.locale = locale\n }\n \n if (emailPreferences !== undefined) {\n updateData.emailPreferences = emailPreferences\n }\n\n // Update subscriber - use synthetic user to ensure only updating own data\n const subscriber = await req.payload.update({\n collection: config.subscribersSlug || 'subscribers',\n id: payload.subscriberId,\n data: updateData,\n overrideAccess: false,\n user: {\n collection: 'subscribers',\n id: payload.subscriberId,\n email: payload.email,\n },\n })\n\n res.json({\n success: true,\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n name: subscriber.name,\n locale: subscriber.locale,\n emailPreferences: subscriber.emailPreferences,\n subscriptionStatus: subscriber.subscriptionStatus,\n },\n })\n } catch (error: unknown) {\n console.error('Update preferences error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to update preferences',\n })\n }\n }) as PayloadHandler,\n }\n}"],"names":["verifySessionToken","createPreferencesEndpoint","config","path","method","handler","req","res","authHeader","headers","authorization","startsWith","status","json","success","error","token","substring","payload","Error","message","subscriber","findByID","collection","subscribersSlug","id","subscriberId","overrideAccess","user","email","name","locale","emailPreferences","subscriptionStatus","console","createUpdatePreferencesEndpoint","body","updateData","undefined","update","data"],"mappings":"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"}
@@ -18,10 +18,19 @@ export const createSubscribeEndpoint = (config)=>{
18
18
  errors: validation.errors
19
19
  });
20
20
  }
21
- // Check domain restrictions from settings
22
- const settings = await req.payload.findGlobal({
23
- slug: 'newsletter-settings'
21
+ // Check domain restrictions from active settings
22
+ // Settings are public info needed for validation, but we can still respect access control
23
+ const settingsResult = await req.payload.find({
24
+ collection: config.settingsSlug || 'newsletter-settings',
25
+ where: {
26
+ active: {
27
+ equals: true
28
+ }
29
+ },
30
+ limit: 1,
31
+ overrideAccess: false
24
32
  });
33
+ const settings = settingsResult.docs[0];
25
34
  const allowedDomains = settings?.allowedDomains?.map((d)=>d.domain) || [];
26
35
  if (!isDomainAllowed(email, allowedDomains)) {
27
36
  return res.status(400).json({
@@ -30,6 +39,7 @@ export const createSubscribeEndpoint = (config)=>{
30
39
  });
31
40
  }
32
41
  // Check if already subscribed
42
+ // This needs admin access to check for existing email
33
43
  const existing = await req.payload.find({
34
44
  collection: config.subscribersSlug || 'subscribers',
35
45
  where: {
@@ -105,6 +115,7 @@ export const createSubscribeEndpoint = (config)=>{
105
115
  subscriberData.leadMagnet = leadMagnet;
106
116
  }
107
117
  // Create subscriber
118
+ // Public endpoint needs to create subscribers
108
119
  const subscriber = await req.payload.create({
109
120
  collection: config.subscribersSlug || 'subscribers',
110
121
  data: subscriberData
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/endpoints/subscribe.ts"],"sourcesContent":["import type { Endpoint, PayloadHandler } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { \n isValidEmail, \n isDomainAllowed, \n sanitizeInput, \n validateSubscriberData,\n extractUTMParams \n} from '../utils/validation'\n\nexport const createSubscribeEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/subscribe',\n method: 'post',\n handler: (async (req: any, res: any) => {\n try {\n const { \n email, \n name, \n source,\n preferences,\n leadMagnet,\n surveyResponses,\n metadata = {}\n } = req.body\n\n // Validate input\n const validation = validateSubscriberData({ email, name, source })\n if (!validation.valid) {\n return res.status(400).json({\n success: false,\n errors: validation.errors,\n })\n }\n\n // Check domain restrictions from settings\n const settings = await req.payload.findGlobal({\n slug: 'newsletter-settings',\n })\n\n const allowedDomains = settings?.allowedDomains?.map((d: any) => d.domain) || []\n if (!isDomainAllowed(email, allowedDomains)) {\n return res.status(400).json({\n success: false,\n error: 'Email domain not allowed',\n })\n }\n\n // Check if already subscribed\n const existing = await req.payload.find({\n collection: config.subscribersSlug || 'subscribers',\n where: {\n email: {\n equals: email.toLowerCase(),\n },\n },\n })\n\n if (existing.docs.length > 0) {\n const subscriber = existing.docs[0]\n \n // If unsubscribed, don't allow resubscription via API\n if (subscriber.subscriptionStatus === 'unsubscribed') {\n return res.status(400).json({\n success: false,\n error: 'This email has been unsubscribed. Please contact support to resubscribe.',\n })\n }\n\n return res.status(400).json({\n success: false,\n error: 'Already subscribed',\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n subscriptionStatus: subscriber.subscriptionStatus,\n },\n })\n }\n\n // Check IP rate limiting\n const ipAddress = req.ip || req.connection.remoteAddress\n const maxPerIP = settings?.maxSubscribersPerIP || 10\n\n const ipSubscribers = await req.payload.find({\n collection: config.subscribersSlug || 'subscribers',\n where: {\n 'signupMetadata.ipAddress': {\n equals: ipAddress,\n },\n },\n })\n\n if (ipSubscribers.docs.length >= maxPerIP) {\n return res.status(429).json({\n success: false,\n error: 'Too many subscriptions from this IP address',\n })\n }\n\n // Extract UTM parameters\n const referer = req.headers.referer || req.headers.referrer || ''\n const utmParams = extractUTMParams(new URL(referer).searchParams)\n\n // Prepare subscriber data\n const subscriberData: any = {\n email: email.toLowerCase(),\n name: name ? sanitizeInput(name) : undefined,\n locale: metadata.locale || config.i18n?.defaultLocale || 'en',\n subscriptionStatus: settings?.requireDoubleOptIn ? 'pending' : 'active',\n source: source || 'api',\n emailPreferences: {\n newsletter: true,\n announcements: true,\n ...(preferences || {}),\n },\n signupMetadata: {\n ipAddress,\n userAgent: req.headers['user-agent'],\n referrer: referer,\n signupPage: metadata.signupPage || referer,\n },\n }\n\n // Add UTM parameters if tracking is enabled\n if (config.features?.utmTracking?.enabled && Object.keys(utmParams).length > 0) {\n subscriberData.utmParameters = utmParams\n }\n\n // Add lead magnet if provided\n if (config.features?.leadMagnets?.enabled && leadMagnet) {\n subscriberData.leadMagnet = leadMagnet\n }\n\n // Create subscriber\n const subscriber = await req.payload.create({\n collection: config.subscribersSlug || 'subscribers',\n data: subscriberData,\n })\n\n // Handle survey responses if provided\n if (config.features?.surveys?.enabled && surveyResponses) {\n // TODO: Store survey responses\n }\n\n // Send confirmation email if double opt-in\n if (settings?.requireDoubleOptIn) {\n // TODO: Send confirmation email with magic link\n }\n\n res.json({\n success: true,\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n subscriptionStatus: subscriber.subscriptionStatus,\n },\n message: settings?.requireDoubleOptIn \n ? 'Please check your email to confirm your subscription'\n : 'Successfully subscribed',\n })\n } catch (error) {\n console.error('Subscribe endpoint error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to subscribe. Please try again.',\n })\n }\n }) as PayloadHandler,\n }\n}"],"names":["isDomainAllowed","sanitizeInput","validateSubscriberData","extractUTMParams","createSubscribeEndpoint","config","path","method","handler","req","res","email","name","source","preferences","leadMagnet","surveyResponses","metadata","body","validation","valid","status","json","success","errors","settings","payload","findGlobal","slug","allowedDomains","map","d","domain","error","existing","find","collection","subscribersSlug","where","equals","toLowerCase","docs","length","subscriber","subscriptionStatus","id","ipAddress","ip","connection","remoteAddress","maxPerIP","maxSubscribersPerIP","ipSubscribers","referer","headers","referrer","utmParams","URL","searchParams","subscriberData","undefined","locale","i18n","defaultLocale","requireDoubleOptIn","emailPreferences","newsletter","announcements","signupMetadata","userAgent","signupPage","features","utmTracking","enabled","Object","keys","utmParameters","leadMagnets","create","data","surveys","message","console"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/endpoints/subscribe.ts"],"sourcesContent":["import type { Endpoint, PayloadHandler } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { \n isDomainAllowed, \n sanitizeInput, \n validateSubscriberData,\n extractUTMParams \n} from '../utils/validation'\n\nexport const createSubscribeEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/subscribe',\n method: 'post',\n handler: (async (req: any, res: any) => {\n try {\n const { \n email, \n name, \n source,\n preferences,\n leadMagnet,\n surveyResponses,\n metadata = {}\n } = req.body\n\n // Validate input\n const validation = validateSubscriberData({ email, name, source })\n if (!validation.valid) {\n return res.status(400).json({\n success: false,\n errors: validation.errors,\n })\n }\n\n // Check domain restrictions from active settings\n // Settings are public info needed for validation, but we can still respect access control\n const settingsResult = await req.payload.find({\n collection: config.settingsSlug || 'newsletter-settings',\n where: {\n active: {\n equals: true,\n },\n },\n limit: 1,\n overrideAccess: false,\n // No user context for public endpoint\n })\n \n const settings = settingsResult.docs[0]\n\n const allowedDomains = settings?.allowedDomains?.map((d: any) => d.domain) || []\n if (!isDomainAllowed(email, allowedDomains)) {\n return res.status(400).json({\n success: false,\n error: 'Email domain not allowed',\n })\n }\n\n // Check if already subscribed\n // This needs admin access to check for existing email\n const existing = await req.payload.find({\n collection: config.subscribersSlug || 'subscribers',\n where: {\n email: {\n equals: email.toLowerCase(),\n },\n },\n // Keep overrideAccess: true for public subscription check\n })\n\n if (existing.docs.length > 0) {\n const subscriber = existing.docs[0]\n \n // If unsubscribed, don't allow resubscription via API\n if (subscriber.subscriptionStatus === 'unsubscribed') {\n return res.status(400).json({\n success: false,\n error: 'This email has been unsubscribed. Please contact support to resubscribe.',\n })\n }\n\n return res.status(400).json({\n success: false,\n error: 'Already subscribed',\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n subscriptionStatus: subscriber.subscriptionStatus,\n },\n })\n }\n\n // Check IP rate limiting\n const ipAddress = req.ip || req.connection.remoteAddress\n const maxPerIP = settings?.maxSubscribersPerIP || 10\n\n const ipSubscribers = await req.payload.find({\n collection: config.subscribersSlug || 'subscribers',\n where: {\n 'signupMetadata.ipAddress': {\n equals: ipAddress,\n },\n },\n // Keep overrideAccess: true for rate limiting check\n })\n\n if (ipSubscribers.docs.length >= maxPerIP) {\n return res.status(429).json({\n success: false,\n error: 'Too many subscriptions from this IP address',\n })\n }\n\n // Extract UTM parameters\n const referer = req.headers.referer || req.headers.referrer || ''\n const utmParams = extractUTMParams(new URL(referer).searchParams)\n\n // Prepare subscriber data\n const subscriberData: any = {\n email: email.toLowerCase(),\n name: name ? sanitizeInput(name) : undefined,\n locale: metadata.locale || config.i18n?.defaultLocale || 'en',\n subscriptionStatus: settings?.requireDoubleOptIn ? 'pending' : 'active',\n source: source || 'api',\n emailPreferences: {\n newsletter: true,\n announcements: true,\n ...(preferences || {}),\n },\n signupMetadata: {\n ipAddress,\n userAgent: req.headers['user-agent'],\n referrer: referer,\n signupPage: metadata.signupPage || referer,\n },\n }\n\n // Add UTM parameters if tracking is enabled\n if (config.features?.utmTracking?.enabled && Object.keys(utmParams).length > 0) {\n subscriberData.utmParameters = utmParams\n }\n\n // Add lead magnet if provided\n if (config.features?.leadMagnets?.enabled && leadMagnet) {\n subscriberData.leadMagnet = leadMagnet\n }\n\n // Create subscriber\n // Public endpoint needs to create subscribers\n const subscriber = await req.payload.create({\n collection: config.subscribersSlug || 'subscribers',\n data: subscriberData,\n // Keep overrideAccess: true for public subscription\n })\n\n // Handle survey responses if provided\n if (config.features?.surveys?.enabled && surveyResponses) {\n // TODO: Store survey responses\n }\n\n // Send confirmation email if double opt-in\n if (settings?.requireDoubleOptIn) {\n // TODO: Send confirmation email with magic link\n }\n\n res.json({\n success: true,\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n subscriptionStatus: subscriber.subscriptionStatus,\n },\n message: settings?.requireDoubleOptIn \n ? 'Please check your email to confirm your subscription'\n : 'Successfully subscribed',\n })\n } catch (error) {\n console.error('Subscribe endpoint error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to subscribe. Please try again.',\n })\n }\n }) as PayloadHandler,\n }\n}"],"names":["isDomainAllowed","sanitizeInput","validateSubscriberData","extractUTMParams","createSubscribeEndpoint","config","path","method","handler","req","res","email","name","source","preferences","leadMagnet","surveyResponses","metadata","body","validation","valid","status","json","success","errors","settingsResult","payload","find","collection","settingsSlug","where","active","equals","limit","overrideAccess","settings","docs","allowedDomains","map","d","domain","error","existing","subscribersSlug","toLowerCase","length","subscriber","subscriptionStatus","id","ipAddress","ip","connection","remoteAddress","maxPerIP","maxSubscribersPerIP","ipSubscribers","referer","headers","referrer","utmParams","URL","searchParams","subscriberData","undefined","locale","i18n","defaultLocale","requireDoubleOptIn","emailPreferences","newsletter","announcements","signupMetadata","userAgent","signupPage","features","utmTracking","enabled","Object","keys","utmParameters","leadMagnets","create","data","surveys","message","console"],"mappings":"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"}
@@ -22,11 +22,13 @@ export const createUnsubscribeEndpoint = (config)=>{
22
22
  if (payload.type !== 'unsubscribe') {
23
23
  throw new Error('Invalid token type');
24
24
  }
25
+ // Token verified, so we can look up the subscriber
26
+ // Using overrideAccess: true here is OK since we verified the token
25
27
  subscriber = await req.payload.findByID({
26
28
  collection: config.subscribersSlug || 'subscribers',
27
29
  id: payload.subscriberId
28
30
  });
29
- } catch (error) {
31
+ } catch {
30
32
  return res.status(401).json({
31
33
  success: false,
32
34
  error: 'Invalid or expired unsubscribe link'
@@ -70,13 +72,19 @@ export const createUnsubscribeEndpoint = (config)=>{
70
72
  message: 'Already unsubscribed'
71
73
  });
72
74
  }
73
- // Update subscription status
75
+ // Update subscription status - use synthetic user to ensure proper access
74
76
  await req.payload.update({
75
77
  collection: config.subscribersSlug || 'subscribers',
76
78
  id: subscriber.id,
77
79
  data: {
78
80
  subscriptionStatus: 'unsubscribed',
79
81
  unsubscribedAt: new Date().toISOString()
82
+ },
83
+ overrideAccess: false,
84
+ user: {
85
+ collection: 'subscribers',
86
+ id: subscriber.id,
87
+ email: subscriber.email
80
88
  }
81
89
  });
82
90
  res.json({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/endpoints/unsubscribe.ts"],"sourcesContent":["import type { Endpoint, PayloadHandler } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { isValidEmail } from '../utils/validation'\n\nexport const createUnsubscribeEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/unsubscribe',\n method: 'post',\n handler: (async (req: any, res: any) => {\n try {\n const { email, token } = req.body\n\n // Two methods: email or token\n if (!email && !token) {\n return res.status(400).json({\n success: false,\n error: 'Email or token is required',\n })\n }\n\n let subscriber\n\n if (token) {\n // Token-based unsubscribe (from email link)\n try {\n const jwt = await import('jsonwebtoken')\n const payload = jwt.verify(\n token,\n process.env.JWT_SECRET || process.env.PAYLOAD_SECRET || ''\n ) as any\n\n if (payload.type !== 'unsubscribe') {\n throw new Error('Invalid token type')\n }\n\n subscriber = await req.payload.findByID({\n collection: config.subscribersSlug || 'subscribers',\n id: payload.subscriberId,\n })\n } catch (error) {\n return res.status(401).json({\n success: false,\n error: 'Invalid or expired unsubscribe link',\n })\n }\n } else {\n // Email-based unsubscribe\n if (!isValidEmail(email)) {\n return res.status(400).json({\n success: false,\n error: 'Invalid email format',\n })\n }\n\n const result = await req.payload.find({\n collection: config.subscribersSlug || 'subscribers',\n where: {\n email: {\n equals: email.toLowerCase(),\n },\n },\n })\n\n if (result.docs.length === 0) {\n // Don't reveal if email exists or not\n return res.json({\n success: true,\n message: 'If this email was subscribed, it has been unsubscribed.',\n })\n }\n\n subscriber = result.docs[0]\n }\n\n if (!subscriber) {\n return res.json({\n success: true,\n message: 'If this email was subscribed, it has been unsubscribed.',\n })\n }\n\n // Check if already unsubscribed\n if (subscriber.subscriptionStatus === 'unsubscribed') {\n return res.json({\n success: true,\n message: 'Already unsubscribed',\n })\n }\n\n // Update subscription status\n await req.payload.update({\n collection: config.subscribersSlug || 'subscribers',\n id: subscriber.id,\n data: {\n subscriptionStatus: 'unsubscribed',\n unsubscribedAt: new Date().toISOString(),\n },\n })\n\n res.json({\n success: true,\n message: 'Successfully unsubscribed',\n })\n } catch (error: unknown) {\n console.error('Unsubscribe error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to unsubscribe. Please try again.',\n })\n }\n }) as PayloadHandler,\n }\n}"],"names":["isValidEmail","createUnsubscribeEndpoint","config","path","method","handler","req","res","email","token","body","status","json","success","error","subscriber","jwt","payload","verify","process","env","JWT_SECRET","PAYLOAD_SECRET","type","Error","findByID","collection","subscribersSlug","id","subscriberId","result","find","where","equals","toLowerCase","docs","length","message","subscriptionStatus","update","data","unsubscribedAt","Date","toISOString","console"],"mappings":"AAEA,SAASA,YAAY,QAAQ,sBAAqB;AAElD,OAAO,MAAMC,4BAA4B,CACvCC;IAEA,OAAO;QACLC,MAAM;QACNC,QAAQ;QACRC,SAAU,OAAOC,KAAUC;YACzB,IAAI;gBACF,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAE,GAAGH,IAAII,IAAI;gBAEjC,8BAA8B;gBAC9B,IAAI,CAACF,SAAS,CAACC,OAAO;oBACpB,OAAOF,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,IAAIC;gBAEJ,IAAIN,OAAO;oBACT,4CAA4C;oBAC5C,IAAI;wBACF,MAAMO,MAAM,MAAM,MAAM,CAAC;wBACzB,MAAMC,UAAUD,IAAIE,MAAM,CACxBT,OACAU,QAAQC,GAAG,CAACC,UAAU,IAAIF,QAAQC,GAAG,CAACE,cAAc,IAAI;wBAG1D,IAAIL,QAAQM,IAAI,KAAK,eAAe;4BAClC,MAAM,IAAIC,MAAM;wBAClB;wBAEAT,aAAa,MAAMT,IAAIW,OAAO,CAACQ,QAAQ,CAAC;4BACtCC,YAAYxB,OAAOyB,eAAe,IAAI;4BACtCC,IAAIX,QAAQY,YAAY;wBAC1B;oBACF,EAAE,OAAOf,OAAO;wBACd,OAAOP,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;4BAC1BC,SAAS;4BACTC,OAAO;wBACT;oBACF;gBACF,OAAO;oBACL,0BAA0B;oBAC1B,IAAI,CAACd,aAAaQ,QAAQ;wBACxB,OAAOD,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;4BAC1BC,SAAS;4BACTC,OAAO;wBACT;oBACF;oBAEA,MAAMgB,SAAS,MAAMxB,IAAIW,OAAO,CAACc,IAAI,CAAC;wBACpCL,YAAYxB,OAAOyB,eAAe,IAAI;wBACtCK,OAAO;4BACLxB,OAAO;gCACLyB,QAAQzB,MAAM0B,WAAW;4BAC3B;wBACF;oBACF;oBAEA,IAAIJ,OAAOK,IAAI,CAACC,MAAM,KAAK,GAAG;wBAC5B,sCAAsC;wBACtC,OAAO7B,IAAIK,IAAI,CAAC;4BACdC,SAAS;4BACTwB,SAAS;wBACX;oBACF;oBAEAtB,aAAae,OAAOK,IAAI,CAAC,EAAE;gBAC7B;gBAEA,IAAI,CAACpB,YAAY;oBACf,OAAOR,IAAIK,IAAI,CAAC;wBACdC,SAAS;wBACTwB,SAAS;oBACX;gBACF;gBAEA,gCAAgC;gBAChC,IAAItB,WAAWuB,kBAAkB,KAAK,gBAAgB;oBACpD,OAAO/B,IAAIK,IAAI,CAAC;wBACdC,SAAS;wBACTwB,SAAS;oBACX;gBACF;gBAEA,6BAA6B;gBAC7B,MAAM/B,IAAIW,OAAO,CAACsB,MAAM,CAAC;oBACvBb,YAAYxB,OAAOyB,eAAe,IAAI;oBACtCC,IAAIb,WAAWa,EAAE;oBACjBY,MAAM;wBACJF,oBAAoB;wBACpBG,gBAAgB,IAAIC,OAAOC,WAAW;oBACxC;gBACF;gBAEApC,IAAIK,IAAI,CAAC;oBACPC,SAAS;oBACTwB,SAAS;gBACX;YACF,EAAE,OAAOvB,OAAgB;gBACvB8B,QAAQ9B,KAAK,CAAC,sBAAsBA;gBACpCP,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;oBACnBC,SAAS;oBACTC,OAAO;gBACT;YACF;QACF;IACF;AACF,EAAC"}
1
+ {"version":3,"sources":["../../../src/endpoints/unsubscribe.ts"],"sourcesContent":["import type { Endpoint, PayloadHandler } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { isValidEmail } from '../utils/validation'\n\nexport const createUnsubscribeEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/unsubscribe',\n method: 'post',\n handler: (async (req: any, res: any) => {\n try {\n const { email, token } = req.body\n\n // Two methods: email or token\n if (!email && !token) {\n return res.status(400).json({\n success: false,\n error: 'Email or token is required',\n })\n }\n\n let subscriber\n\n if (token) {\n // Token-based unsubscribe (from email link)\n try {\n const jwt = await import('jsonwebtoken')\n const payload = jwt.verify(\n token,\n process.env.JWT_SECRET || process.env.PAYLOAD_SECRET || ''\n ) as any\n\n if (payload.type !== 'unsubscribe') {\n throw new Error('Invalid token type')\n }\n\n // Token verified, so we can look up the subscriber\n // Using overrideAccess: true here is OK since we verified the token\n subscriber = await req.payload.findByID({\n collection: config.subscribersSlug || 'subscribers',\n id: payload.subscriberId,\n })\n } catch {\n return res.status(401).json({\n success: false,\n error: 'Invalid or expired unsubscribe link',\n })\n }\n } else {\n // Email-based unsubscribe\n if (!isValidEmail(email)) {\n return res.status(400).json({\n success: false,\n error: 'Invalid email format',\n })\n }\n\n const result = await req.payload.find({\n collection: config.subscribersSlug || 'subscribers',\n where: {\n email: {\n equals: email.toLowerCase(),\n },\n },\n })\n\n if (result.docs.length === 0) {\n // Don't reveal if email exists or not\n return res.json({\n success: true,\n message: 'If this email was subscribed, it has been unsubscribed.',\n })\n }\n\n subscriber = result.docs[0]\n }\n\n if (!subscriber) {\n return res.json({\n success: true,\n message: 'If this email was subscribed, it has been unsubscribed.',\n })\n }\n\n // Check if already unsubscribed\n if (subscriber.subscriptionStatus === 'unsubscribed') {\n return res.json({\n success: true,\n message: 'Already unsubscribed',\n })\n }\n\n // Update subscription status - use synthetic user to ensure proper access\n await req.payload.update({\n collection: config.subscribersSlug || 'subscribers',\n id: subscriber.id,\n data: {\n subscriptionStatus: 'unsubscribed',\n unsubscribedAt: new Date().toISOString(),\n },\n overrideAccess: false,\n user: {\n collection: 'subscribers',\n id: subscriber.id,\n email: subscriber.email,\n },\n })\n\n res.json({\n success: true,\n message: 'Successfully unsubscribed',\n })\n } catch (error: unknown) {\n console.error('Unsubscribe error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to unsubscribe. Please try again.',\n })\n }\n }) as PayloadHandler,\n }\n}"],"names":["isValidEmail","createUnsubscribeEndpoint","config","path","method","handler","req","res","email","token","body","status","json","success","error","subscriber","jwt","payload","verify","process","env","JWT_SECRET","PAYLOAD_SECRET","type","Error","findByID","collection","subscribersSlug","id","subscriberId","result","find","where","equals","toLowerCase","docs","length","message","subscriptionStatus","update","data","unsubscribedAt","Date","toISOString","overrideAccess","user","console"],"mappings":"AAEA,SAASA,YAAY,QAAQ,sBAAqB;AAElD,OAAO,MAAMC,4BAA4B,CACvCC;IAEA,OAAO;QACLC,MAAM;QACNC,QAAQ;QACRC,SAAU,OAAOC,KAAUC;YACzB,IAAI;gBACF,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAE,GAAGH,IAAII,IAAI;gBAEjC,8BAA8B;gBAC9B,IAAI,CAACF,SAAS,CAACC,OAAO;oBACpB,OAAOF,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,IAAIC;gBAEJ,IAAIN,OAAO;oBACT,4CAA4C;oBAC5C,IAAI;wBACF,MAAMO,MAAM,MAAM,MAAM,CAAC;wBACzB,MAAMC,UAAUD,IAAIE,MAAM,CACxBT,OACAU,QAAQC,GAAG,CAACC,UAAU,IAAIF,QAAQC,GAAG,CAACE,cAAc,IAAI;wBAG1D,IAAIL,QAAQM,IAAI,KAAK,eAAe;4BAClC,MAAM,IAAIC,MAAM;wBAClB;wBAEA,mDAAmD;wBACnD,oEAAoE;wBACpET,aAAa,MAAMT,IAAIW,OAAO,CAACQ,QAAQ,CAAC;4BACtCC,YAAYxB,OAAOyB,eAAe,IAAI;4BACtCC,IAAIX,QAAQY,YAAY;wBAC1B;oBACF,EAAE,OAAM;wBACN,OAAOtB,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;4BAC1BC,SAAS;4BACTC,OAAO;wBACT;oBACF;gBACF,OAAO;oBACL,0BAA0B;oBAC1B,IAAI,CAACd,aAAaQ,QAAQ;wBACxB,OAAOD,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;4BAC1BC,SAAS;4BACTC,OAAO;wBACT;oBACF;oBAEA,MAAMgB,SAAS,MAAMxB,IAAIW,OAAO,CAACc,IAAI,CAAC;wBACpCL,YAAYxB,OAAOyB,eAAe,IAAI;wBACtCK,OAAO;4BACLxB,OAAO;gCACLyB,QAAQzB,MAAM0B,WAAW;4BAC3B;wBACF;oBACF;oBAEA,IAAIJ,OAAOK,IAAI,CAACC,MAAM,KAAK,GAAG;wBAC5B,sCAAsC;wBACtC,OAAO7B,IAAIK,IAAI,CAAC;4BACdC,SAAS;4BACTwB,SAAS;wBACX;oBACF;oBAEAtB,aAAae,OAAOK,IAAI,CAAC,EAAE;gBAC7B;gBAEA,IAAI,CAACpB,YAAY;oBACf,OAAOR,IAAIK,IAAI,CAAC;wBACdC,SAAS;wBACTwB,SAAS;oBACX;gBACF;gBAEA,gCAAgC;gBAChC,IAAItB,WAAWuB,kBAAkB,KAAK,gBAAgB;oBACpD,OAAO/B,IAAIK,IAAI,CAAC;wBACdC,SAAS;wBACTwB,SAAS;oBACX;gBACF;gBAEA,0EAA0E;gBAC1E,MAAM/B,IAAIW,OAAO,CAACsB,MAAM,CAAC;oBACvBb,YAAYxB,OAAOyB,eAAe,IAAI;oBACtCC,IAAIb,WAAWa,EAAE;oBACjBY,MAAM;wBACJF,oBAAoB;wBACpBG,gBAAgB,IAAIC,OAAOC,WAAW;oBACxC;oBACAC,gBAAgB;oBAChBC,MAAM;wBACJnB,YAAY;wBACZE,IAAIb,WAAWa,EAAE;wBACjBpB,OAAOO,WAAWP,KAAK;oBACzB;gBACF;gBAEAD,IAAIK,IAAI,CAAC;oBACPC,SAAS;oBACTwB,SAAS;gBACX;YACF,EAAE,OAAOvB,OAAgB;gBACvBgC,QAAQhC,KAAK,CAAC,sBAAsBA;gBACpCP,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;oBACnBC,SAAS;oBACTC,OAAO;gBACT;YACF;QACF;IACF;AACF,EAAC"}
@@ -22,7 +22,7 @@ export const createVerifyMagicLinkEndpoint = (config)=>{
22
22
  error: error instanceof Error ? error.message : 'Invalid token'
23
23
  });
24
24
  }
25
- // Find the subscriber
25
+ // Find the subscriber - token verified so we can use admin access for initial lookup
26
26
  const subscriber = await req.payload.findByID({
27
27
  collection: config.subscribersSlug || 'subscribers',
28
28
  id: payload.subscriberId
@@ -47,6 +47,12 @@ export const createVerifyMagicLinkEndpoint = (config)=>{
47
47
  error: 'This email has been unsubscribed'
48
48
  });
49
49
  }
50
+ // Create synthetic user for subscriber operations
51
+ const syntheticUser = {
52
+ collection: 'subscribers',
53
+ id: subscriber.id,
54
+ email: subscriber.email
55
+ };
50
56
  // Update subscription status if pending
51
57
  if (subscriber.subscriptionStatus === 'pending') {
52
58
  await req.payload.update({
@@ -54,7 +60,9 @@ export const createVerifyMagicLinkEndpoint = (config)=>{
54
60
  id: subscriber.id,
55
61
  data: {
56
62
  subscriptionStatus: 'active'
57
- }
63
+ },
64
+ overrideAccess: false,
65
+ user: syntheticUser
58
66
  });
59
67
  }
60
68
  // Clear the magic link token
@@ -64,7 +72,9 @@ export const createVerifyMagicLinkEndpoint = (config)=>{
64
72
  data: {
65
73
  magicLinkToken: null,
66
74
  magicLinkTokenExpiry: null
67
- }
75
+ },
76
+ overrideAccess: false,
77
+ user: syntheticUser
68
78
  });
69
79
  // Generate session token
70
80
  const sessionToken = generateSessionToken(String(subscriber.id), subscriber.email);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/endpoints/verify-magic-link.ts"],"sourcesContent":["import type { Endpoint, PayloadHandler } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { \n verifyMagicLinkToken, \n generateSessionToken \n} from '../utils/jwt'\n\nexport const createVerifyMagicLinkEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/verify-magic-link',\n method: 'post',\n handler: (async (req: any, res: any) => {\n try {\n const { token } = req.body\n\n if (!token) {\n return res.status(400).json({\n success: false,\n error: 'Token is required',\n })\n }\n\n // Verify the magic link token\n let payload\n try {\n payload = verifyMagicLinkToken(token)\n } catch (error: unknown) {\n return res.status(401).json({\n success: false,\n error: error instanceof Error ? error.message : 'Invalid token',\n })\n }\n\n // Find the subscriber\n const subscriber = await req.payload.findByID({\n collection: config.subscribersSlug || 'subscribers',\n id: payload.subscriberId,\n })\n\n if (!subscriber) {\n return res.status(404).json({\n success: false,\n error: 'Subscriber not found',\n })\n }\n\n // Check if email matches\n if (subscriber.email !== payload.email) {\n return res.status(401).json({\n success: false,\n error: 'Invalid token',\n })\n }\n\n // Check if subscriber is active\n if (subscriber.subscriptionStatus === 'unsubscribed') {\n return res.status(403).json({\n success: false,\n error: 'This email has been unsubscribed',\n })\n }\n\n // Update subscription status if pending\n if (subscriber.subscriptionStatus === 'pending') {\n await req.payload.update({\n collection: config.subscribersSlug || 'subscribers',\n id: subscriber.id,\n data: {\n subscriptionStatus: 'active',\n },\n })\n }\n\n // Clear the magic link token\n await req.payload.update({\n collection: config.subscribersSlug || 'subscribers',\n id: subscriber.id,\n data: {\n magicLinkToken: null,\n magicLinkTokenExpiry: null,\n },\n })\n\n // Generate session token\n const sessionToken = generateSessionToken(\n String(subscriber.id),\n subscriber.email\n )\n\n res.json({\n success: true,\n sessionToken,\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n name: subscriber.name,\n locale: subscriber.locale,\n emailPreferences: subscriber.emailPreferences,\n },\n })\n } catch (error: unknown) {\n console.error('Verify magic link error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to verify magic link',\n })\n }\n }) as PayloadHandler,\n }\n}"],"names":["verifyMagicLinkToken","generateSessionToken","createVerifyMagicLinkEndpoint","config","path","method","handler","req","res","token","body","status","json","success","error","payload","Error","message","subscriber","findByID","collection","subscribersSlug","id","subscriberId","email","subscriptionStatus","update","data","magicLinkToken","magicLinkTokenExpiry","sessionToken","String","name","locale","emailPreferences","console"],"mappings":"AAEA,SACEA,oBAAoB,EACpBC,oBAAoB,QACf,eAAc;AAErB,OAAO,MAAMC,gCAAgC,CAC3CC;IAEA,OAAO;QACLC,MAAM;QACNC,QAAQ;QACRC,SAAU,OAAOC,KAAUC;YACzB,IAAI;gBACF,MAAM,EAAEC,KAAK,EAAE,GAAGF,IAAIG,IAAI;gBAE1B,IAAI,CAACD,OAAO;oBACV,OAAOD,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,8BAA8B;gBAC9B,IAAIC;gBACJ,IAAI;oBACFA,UAAUf,qBAAqBS;gBACjC,EAAE,OAAOK,OAAgB;oBACvB,OAAON,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAOA,iBAAiBE,QAAQF,MAAMG,OAAO,GAAG;oBAClD;gBACF;gBAEA,sBAAsB;gBACtB,MAAMC,aAAa,MAAMX,IAAIQ,OAAO,CAACI,QAAQ,CAAC;oBAC5CC,YAAYjB,OAAOkB,eAAe,IAAI;oBACtCC,IAAIP,QAAQQ,YAAY;gBAC1B;gBAEA,IAAI,CAACL,YAAY;oBACf,OAAOV,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,yBAAyB;gBACzB,IAAII,WAAWM,KAAK,KAAKT,QAAQS,KAAK,EAAE;oBACtC,OAAOhB,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,gCAAgC;gBAChC,IAAII,WAAWO,kBAAkB,KAAK,gBAAgB;oBACpD,OAAOjB,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,wCAAwC;gBACxC,IAAII,WAAWO,kBAAkB,KAAK,WAAW;oBAC/C,MAAMlB,IAAIQ,OAAO,CAACW,MAAM,CAAC;wBACvBN,YAAYjB,OAAOkB,eAAe,IAAI;wBACtCC,IAAIJ,WAAWI,EAAE;wBACjBK,MAAM;4BACJF,oBAAoB;wBACtB;oBACF;gBACF;gBAEA,6BAA6B;gBAC7B,MAAMlB,IAAIQ,OAAO,CAACW,MAAM,CAAC;oBACvBN,YAAYjB,OAAOkB,eAAe,IAAI;oBACtCC,IAAIJ,WAAWI,EAAE;oBACjBK,MAAM;wBACJC,gBAAgB;wBAChBC,sBAAsB;oBACxB;gBACF;gBAEA,yBAAyB;gBACzB,MAAMC,eAAe7B,qBACnB8B,OAAOb,WAAWI,EAAE,GACpBJ,WAAWM,KAAK;gBAGlBhB,IAAII,IAAI,CAAC;oBACPC,SAAS;oBACTiB;oBACAZ,YAAY;wBACVI,IAAIJ,WAAWI,EAAE;wBACjBE,OAAON,WAAWM,KAAK;wBACvBQ,MAAMd,WAAWc,IAAI;wBACrBC,QAAQf,WAAWe,MAAM;wBACzBC,kBAAkBhB,WAAWgB,gBAAgB;oBAC/C;gBACF;YACF,EAAE,OAAOpB,OAAgB;gBACvBqB,QAAQrB,KAAK,CAAC,4BAA4BA;gBAC1CN,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;oBACnBC,SAAS;oBACTC,OAAO;gBACT;YACF;QACF;IACF;AACF,EAAC"}
1
+ {"version":3,"sources":["../../../src/endpoints/verify-magic-link.ts"],"sourcesContent":["import type { Endpoint, PayloadHandler } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { \n verifyMagicLinkToken, \n generateSessionToken \n} from '../utils/jwt'\n\nexport const createVerifyMagicLinkEndpoint = (\n config: NewsletterPluginConfig\n): Endpoint => {\n return {\n path: '/newsletter/verify-magic-link',\n method: 'post',\n handler: (async (req: any, res: any) => {\n try {\n const { token } = req.body\n\n if (!token) {\n return res.status(400).json({\n success: false,\n error: 'Token is required',\n })\n }\n\n // Verify the magic link token\n let payload\n try {\n payload = verifyMagicLinkToken(token)\n } catch (error: unknown) {\n return res.status(401).json({\n success: false,\n error: error instanceof Error ? error.message : 'Invalid token',\n })\n }\n\n // Find the subscriber - token verified so we can use admin access for initial lookup\n const subscriber = await req.payload.findByID({\n collection: config.subscribersSlug || 'subscribers',\n id: payload.subscriberId,\n // Keep overrideAccess: true for token verification\n })\n\n if (!subscriber) {\n return res.status(404).json({\n success: false,\n error: 'Subscriber not found',\n })\n }\n\n // Check if email matches\n if (subscriber.email !== payload.email) {\n return res.status(401).json({\n success: false,\n error: 'Invalid token',\n })\n }\n\n // Check if subscriber is active\n if (subscriber.subscriptionStatus === 'unsubscribed') {\n return res.status(403).json({\n success: false,\n error: 'This email has been unsubscribed',\n })\n }\n\n // Create synthetic user for subscriber operations\n const syntheticUser = {\n collection: 'subscribers',\n id: subscriber.id,\n email: subscriber.email,\n }\n\n // Update subscription status if pending\n if (subscriber.subscriptionStatus === 'pending') {\n await req.payload.update({\n collection: config.subscribersSlug || 'subscribers',\n id: subscriber.id,\n data: {\n subscriptionStatus: 'active',\n },\n overrideAccess: false,\n user: syntheticUser,\n })\n }\n\n // Clear the magic link token\n await req.payload.update({\n collection: config.subscribersSlug || 'subscribers',\n id: subscriber.id,\n data: {\n magicLinkToken: null,\n magicLinkTokenExpiry: null,\n },\n overrideAccess: false,\n user: syntheticUser,\n })\n\n // Generate session token\n const sessionToken = generateSessionToken(\n String(subscriber.id),\n subscriber.email\n )\n\n res.json({\n success: true,\n sessionToken,\n subscriber: {\n id: subscriber.id,\n email: subscriber.email,\n name: subscriber.name,\n locale: subscriber.locale,\n emailPreferences: subscriber.emailPreferences,\n },\n })\n } catch (error: unknown) {\n console.error('Verify magic link error:', error)\n res.status(500).json({\n success: false,\n error: 'Failed to verify magic link',\n })\n }\n }) as PayloadHandler,\n }\n}"],"names":["verifyMagicLinkToken","generateSessionToken","createVerifyMagicLinkEndpoint","config","path","method","handler","req","res","token","body","status","json","success","error","payload","Error","message","subscriber","findByID","collection","subscribersSlug","id","subscriberId","email","subscriptionStatus","syntheticUser","update","data","overrideAccess","user","magicLinkToken","magicLinkTokenExpiry","sessionToken","String","name","locale","emailPreferences","console"],"mappings":"AAEA,SACEA,oBAAoB,EACpBC,oBAAoB,QACf,eAAc;AAErB,OAAO,MAAMC,gCAAgC,CAC3CC;IAEA,OAAO;QACLC,MAAM;QACNC,QAAQ;QACRC,SAAU,OAAOC,KAAUC;YACzB,IAAI;gBACF,MAAM,EAAEC,KAAK,EAAE,GAAGF,IAAIG,IAAI;gBAE1B,IAAI,CAACD,OAAO;oBACV,OAAOD,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,8BAA8B;gBAC9B,IAAIC;gBACJ,IAAI;oBACFA,UAAUf,qBAAqBS;gBACjC,EAAE,OAAOK,OAAgB;oBACvB,OAAON,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAOA,iBAAiBE,QAAQF,MAAMG,OAAO,GAAG;oBAClD;gBACF;gBAEA,qFAAqF;gBACrF,MAAMC,aAAa,MAAMX,IAAIQ,OAAO,CAACI,QAAQ,CAAC;oBAC5CC,YAAYjB,OAAOkB,eAAe,IAAI;oBACtCC,IAAIP,QAAQQ,YAAY;gBAE1B;gBAEA,IAAI,CAACL,YAAY;oBACf,OAAOV,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,yBAAyB;gBACzB,IAAII,WAAWM,KAAK,KAAKT,QAAQS,KAAK,EAAE;oBACtC,OAAOhB,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,gCAAgC;gBAChC,IAAII,WAAWO,kBAAkB,KAAK,gBAAgB;oBACpD,OAAOjB,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAC1BC,SAAS;wBACTC,OAAO;oBACT;gBACF;gBAEA,kDAAkD;gBAClD,MAAMY,gBAAgB;oBACpBN,YAAY;oBACZE,IAAIJ,WAAWI,EAAE;oBACjBE,OAAON,WAAWM,KAAK;gBACzB;gBAEA,wCAAwC;gBACxC,IAAIN,WAAWO,kBAAkB,KAAK,WAAW;oBAC/C,MAAMlB,IAAIQ,OAAO,CAACY,MAAM,CAAC;wBACvBP,YAAYjB,OAAOkB,eAAe,IAAI;wBACtCC,IAAIJ,WAAWI,EAAE;wBACjBM,MAAM;4BACJH,oBAAoB;wBACtB;wBACAI,gBAAgB;wBAChBC,MAAMJ;oBACR;gBACF;gBAEA,6BAA6B;gBAC7B,MAAMnB,IAAIQ,OAAO,CAACY,MAAM,CAAC;oBACvBP,YAAYjB,OAAOkB,eAAe,IAAI;oBACtCC,IAAIJ,WAAWI,EAAE;oBACjBM,MAAM;wBACJG,gBAAgB;wBAChBC,sBAAsB;oBACxB;oBACAH,gBAAgB;oBAChBC,MAAMJ;gBACR;gBAEA,yBAAyB;gBACzB,MAAMO,eAAehC,qBACnBiC,OAAOhB,WAAWI,EAAE,GACpBJ,WAAWM,KAAK;gBAGlBhB,IAAII,IAAI,CAAC;oBACPC,SAAS;oBACToB;oBACAf,YAAY;wBACVI,IAAIJ,WAAWI,EAAE;wBACjBE,OAAON,WAAWM,KAAK;wBACvBW,MAAMjB,WAAWiB,IAAI;wBACrBC,QAAQlB,WAAWkB,MAAM;wBACzBC,kBAAkBnB,WAAWmB,gBAAgB;oBAC/C;gBACF;YACF,EAAE,OAAOvB,OAAgB;gBACvBwB,QAAQxB,KAAK,CAAC,4BAA4BA;gBAC1CN,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;oBACnBC,SAAS;oBACTC,OAAO;gBACT;YACF;QACF;IACF;AACF,EAAC"}
package/dist/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createSubscribersCollection } from './collections/Subscribers';
2
- import { createEmailSettingsGlobal } from './globals/EmailSettings';
2
+ import { createNewsletterSettingsCollection } from './collections/NewsletterSettings';
3
3
  import { createEmailService } from './providers';
4
4
  import { createNewsletterEndpoints } from './endpoints';
5
5
  import { createNewsletterSchedulingFields } from './fields/newsletterScheduling';
@@ -8,6 +8,7 @@ export const newsletterPlugin = (pluginConfig)=>(incomingConfig)=>{
8
8
  const config = {
9
9
  enabled: true,
10
10
  subscribersSlug: 'subscribers',
11
+ settingsSlug: 'newsletter-settings',
11
12
  auth: {
12
13
  enabled: true,
13
14
  tokenExpiration: '7d',
@@ -20,14 +21,14 @@ export const newsletterPlugin = (pluginConfig)=>(incomingConfig)=>{
20
21
  if (!config.enabled) {
21
22
  return incomingConfig;
22
23
  }
23
- // Create subscribers collection
24
+ // Create plugin collections
24
25
  const subscribersCollection = createSubscribersCollection(config);
25
- // Create email settings global
26
- const emailSettingsGlobal = createEmailSettingsGlobal(config);
26
+ const settingsCollection = createNewsletterSettingsCollection(config);
27
27
  // Build collections array
28
28
  let collections = [
29
29
  ...incomingConfig.collections || [],
30
- subscribersCollection
30
+ subscribersCollection,
31
+ settingsCollection
31
32
  ];
32
33
  // Extend collections with newsletter scheduling fields if enabled
33
34
  if (config.features?.newsletterScheduling?.enabled) {
@@ -56,8 +57,7 @@ export const newsletterPlugin = (pluginConfig)=>(incomingConfig)=>{
56
57
  ...incomingConfig,
57
58
  collections,
58
59
  globals: [
59
- ...incomingConfig.globals || [],
60
- emailSettingsGlobal
60
+ ...incomingConfig.globals || []
61
61
  ],
62
62
  endpoints: [
63
63
  ...incomingConfig.endpoints || [],
@@ -66,11 +66,17 @@ export const newsletterPlugin = (pluginConfig)=>(incomingConfig)=>{
66
66
  onInit: async (payload)=>{
67
67
  // Initialize email service
68
68
  try {
69
- // Get settings from global
70
- const settings = await payload.findGlobal({
71
- slug: 'newsletter-settings',
72
- depth: 0
69
+ // Get active settings from collection
70
+ const settingsResult = await payload.find({
71
+ collection: config.settingsSlug || 'newsletter-settings',
72
+ where: {
73
+ active: {
74
+ equals: true
75
+ }
76
+ },
77
+ limit: 1
73
78
  });
79
+ const settings = settingsResult.docs[0];
74
80
  let emailServiceConfig;
75
81
  if (settings) {
76
82
  emailServiceConfig = {
@@ -107,7 +113,7 @@ export const newsletterPlugin = (pluginConfig)=>(incomingConfig)=>{
107
113
  };
108
114
  }
109
115
  payload.newsletterEmailService = createEmailService(emailServiceConfig);
110
- console.log('Newsletter plugin initialized with', payload.newsletterEmailService.getProvider(), 'provider');
116
+ console.warn('Newsletter plugin initialized with', payload.newsletterEmailService.getProvider(), 'provider');
111
117
  } catch (error) {
112
118
  console.error('Failed to initialize newsletter email service:', error);
113
119
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\nimport type { NewsletterPluginConfig } from './types'\nimport { createSubscribersCollection } from './collections/Subscribers'\nimport { createEmailSettingsGlobal } from './globals/EmailSettings'\nimport { createEmailService } from './providers'\nimport { createNewsletterEndpoints } from './endpoints'\nimport { createNewsletterSchedulingFields } from './fields/newsletterScheduling'\n\n// Extend Payload type to include our email service\ndeclare module 'payload' {\n interface BasePayload {\n newsletterEmailService?: any\n }\n}\n\nexport const newsletterPlugin = (pluginConfig: NewsletterPluginConfig) => (incomingConfig: Config): Config => {\n // Validate and set defaults\n const config: NewsletterPluginConfig = {\n enabled: true,\n subscribersSlug: 'subscribers',\n auth: {\n enabled: true,\n tokenExpiration: '7d',\n magicLinkPath: '/newsletter/verify',\n ...pluginConfig.auth,\n },\n ...pluginConfig,\n }\n\n // If plugin is disabled, return config unchanged\n if (!config.enabled) {\n return incomingConfig\n }\n\n // Create subscribers collection\n const subscribersCollection = createSubscribersCollection(config)\n\n // Create email settings global\n const emailSettingsGlobal = createEmailSettingsGlobal(config)\n\n // Build collections array\n let collections = [...(incomingConfig.collections || []), subscribersCollection]\n\n // Extend collections with newsletter scheduling fields if enabled\n if (config.features?.newsletterScheduling?.enabled) {\n const targetCollections = config.features.newsletterScheduling.collections || 'articles'\n const collectionsToExtend = Array.isArray(targetCollections) ? targetCollections : [targetCollections]\n const schedulingFields = createNewsletterSchedulingFields(config)\n \n collections = collections.map(collection => {\n if (collectionsToExtend.includes(collection.slug)) {\n return {\n ...collection,\n fields: [\n ...collection.fields,\n ...schedulingFields,\n ],\n }\n }\n return collection\n })\n }\n\n // Create API endpoints\n const endpoints = createNewsletterEndpoints(config)\n\n // Build the modified config\n const modifiedConfig: Config = {\n ...incomingConfig,\n collections,\n globals: [\n ...(incomingConfig.globals || []),\n emailSettingsGlobal,\n ],\n endpoints: [\n ...(incomingConfig.endpoints || []),\n ...endpoints,\n ],\n onInit: async (payload) => {\n // Initialize email service\n try {\n // Get settings from global\n const settings = await (payload as any).findGlobal({\n slug: 'newsletter-settings',\n depth: 0,\n })\n\n let emailServiceConfig: any\n \n if (settings) {\n emailServiceConfig = {\n provider: settings.provider || config.providers.default,\n fromAddress: settings.fromAddress || config.providers.resend?.fromAddress || config.providers.broadcast?.fromAddress || 'noreply@example.com',\n fromName: settings.fromName || config.providers.resend?.fromName || config.providers.broadcast?.fromName || 'Newsletter',\n replyTo: settings.replyTo,\n resend: settings.provider === 'resend' ? {\n apiKey: settings.resendSettings?.apiKey || config.providers.resend?.apiKey || '',\n audienceIds: settings.resendSettings?.audienceIds?.reduce((acc: any, item: any) => {\n acc[item.locale] = {\n production: item.production,\n development: item.development,\n }\n return acc\n }, {}) || config.providers.resend?.audienceIds,\n } : config.providers.resend,\n broadcast: settings.provider === 'broadcast' ? {\n apiUrl: settings.broadcastSettings?.apiUrl || config.providers.broadcast?.apiUrl || '',\n tokens: {\n production: settings.broadcastSettings?.productionToken || config.providers.broadcast?.tokens.production,\n development: settings.broadcastSettings?.developmentToken || config.providers.broadcast?.tokens.development,\n },\n } : config.providers.broadcast,\n }\n } else {\n // Use config defaults\n emailServiceConfig = {\n provider: config.providers.default,\n fromAddress: config.providers.resend?.fromAddress || config.providers.broadcast?.fromAddress || 'noreply@example.com',\n fromName: config.providers.resend?.fromName || config.providers.broadcast?.fromName || 'Newsletter',\n resend: config.providers.resend,\n broadcast: config.providers.broadcast,\n }\n }\n\n (payload as any).newsletterEmailService = createEmailService(emailServiceConfig)\n\n console.log('Newsletter plugin initialized with', (payload as any).newsletterEmailService.getProvider(), 'provider')\n } catch (error) {\n console.error('Failed to initialize newsletter email service:', error)\n }\n\n // Call original onInit if it exists\n if (incomingConfig.onInit) {\n await incomingConfig.onInit(payload)\n }\n },\n }\n\n return modifiedConfig\n}\n\nexport { newsletterPlugin as default }"],"names":["createSubscribersCollection","createEmailSettingsGlobal","createEmailService","createNewsletterEndpoints","createNewsletterSchedulingFields","newsletterPlugin","pluginConfig","incomingConfig","config","enabled","subscribersSlug","auth","tokenExpiration","magicLinkPath","subscribersCollection","emailSettingsGlobal","collections","features","newsletterScheduling","targetCollections","collectionsToExtend","Array","isArray","schedulingFields","map","collection","includes","slug","fields","endpoints","modifiedConfig","globals","onInit","payload","settings","findGlobal","depth","emailServiceConfig","provider","providers","default","fromAddress","resend","broadcast","fromName","replyTo","apiKey","resendSettings","audienceIds","reduce","acc","item","locale","production","development","apiUrl","broadcastSettings","tokens","productionToken","developmentToken","newsletterEmailService","console","log","getProvider","error"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\nimport type { NewsletterPluginConfig } from './types'\nimport { createSubscribersCollection } from './collections/Subscribers'\nimport { createNewsletterSettingsCollection } from './collections/NewsletterSettings'\nimport { createEmailService } from './providers'\nimport { createNewsletterEndpoints } from './endpoints'\nimport { createNewsletterSchedulingFields } from './fields/newsletterScheduling'\n\n// Extend Payload type to include our email service\ndeclare module 'payload' {\n interface BasePayload {\n newsletterEmailService?: any\n }\n}\n\nexport const newsletterPlugin = (pluginConfig: NewsletterPluginConfig) => (incomingConfig: Config): Config => {\n // Validate and set defaults\n const config: NewsletterPluginConfig = {\n enabled: true,\n subscribersSlug: 'subscribers',\n settingsSlug: 'newsletter-settings',\n auth: {\n enabled: true,\n tokenExpiration: '7d',\n magicLinkPath: '/newsletter/verify',\n ...pluginConfig.auth,\n },\n ...pluginConfig,\n }\n\n // If plugin is disabled, return config unchanged\n if (!config.enabled) {\n return incomingConfig\n }\n\n // Create plugin collections\n const subscribersCollection = createSubscribersCollection(config)\n const settingsCollection = createNewsletterSettingsCollection(config)\n\n // Build collections array\n let collections = [...(incomingConfig.collections || []), subscribersCollection, settingsCollection]\n\n // Extend collections with newsletter scheduling fields if enabled\n if (config.features?.newsletterScheduling?.enabled) {\n const targetCollections = config.features.newsletterScheduling.collections || 'articles'\n const collectionsToExtend = Array.isArray(targetCollections) ? targetCollections : [targetCollections]\n const schedulingFields = createNewsletterSchedulingFields(config)\n \n collections = collections.map(collection => {\n if (collectionsToExtend.includes(collection.slug)) {\n return {\n ...collection,\n fields: [\n ...collection.fields,\n ...schedulingFields,\n ],\n }\n }\n return collection\n })\n }\n\n // Create API endpoints\n const endpoints = createNewsletterEndpoints(config)\n\n // Build the modified config\n const modifiedConfig: Config = {\n ...incomingConfig,\n collections,\n globals: [\n ...(incomingConfig.globals || []),\n ],\n endpoints: [\n ...(incomingConfig.endpoints || []),\n ...endpoints,\n ],\n onInit: async (payload) => {\n // Initialize email service\n try {\n // Get active settings from collection\n const settingsResult = await payload.find({\n collection: config.settingsSlug || 'newsletter-settings',\n where: {\n active: {\n equals: true,\n },\n },\n limit: 1,\n })\n \n const settings = settingsResult.docs[0]\n\n let emailServiceConfig: any\n \n if (settings) {\n emailServiceConfig = {\n provider: settings.provider || config.providers.default,\n fromAddress: settings.fromAddress || config.providers.resend?.fromAddress || config.providers.broadcast?.fromAddress || 'noreply@example.com',\n fromName: settings.fromName || config.providers.resend?.fromName || config.providers.broadcast?.fromName || 'Newsletter',\n replyTo: settings.replyTo,\n resend: settings.provider === 'resend' ? {\n apiKey: settings.resendSettings?.apiKey || config.providers.resend?.apiKey || '',\n audienceIds: settings.resendSettings?.audienceIds?.reduce((acc: any, item: any) => {\n acc[item.locale] = {\n production: item.production,\n development: item.development,\n }\n return acc\n }, {}) || config.providers.resend?.audienceIds,\n } : config.providers.resend,\n broadcast: settings.provider === 'broadcast' ? {\n apiUrl: settings.broadcastSettings?.apiUrl || config.providers.broadcast?.apiUrl || '',\n tokens: {\n production: settings.broadcastSettings?.productionToken || config.providers.broadcast?.tokens.production,\n development: settings.broadcastSettings?.developmentToken || config.providers.broadcast?.tokens.development,\n },\n } : config.providers.broadcast,\n }\n } else {\n // Use config defaults\n emailServiceConfig = {\n provider: config.providers.default,\n fromAddress: config.providers.resend?.fromAddress || config.providers.broadcast?.fromAddress || 'noreply@example.com',\n fromName: config.providers.resend?.fromName || config.providers.broadcast?.fromName || 'Newsletter',\n resend: config.providers.resend,\n broadcast: config.providers.broadcast,\n }\n }\n\n (payload as any).newsletterEmailService = createEmailService(emailServiceConfig)\n\n console.warn('Newsletter plugin initialized with', (payload as any).newsletterEmailService.getProvider(), 'provider')\n } catch (error) {\n console.error('Failed to initialize newsletter email service:', error)\n }\n\n // Call original onInit if it exists\n if (incomingConfig.onInit) {\n await incomingConfig.onInit(payload)\n }\n },\n }\n\n return modifiedConfig\n}\n\nexport { newsletterPlugin as default }"],"names":["createSubscribersCollection","createNewsletterSettingsCollection","createEmailService","createNewsletterEndpoints","createNewsletterSchedulingFields","newsletterPlugin","pluginConfig","incomingConfig","config","enabled","subscribersSlug","settingsSlug","auth","tokenExpiration","magicLinkPath","subscribersCollection","settingsCollection","collections","features","newsletterScheduling","targetCollections","collectionsToExtend","Array","isArray","schedulingFields","map","collection","includes","slug","fields","endpoints","modifiedConfig","globals","onInit","payload","settingsResult","find","where","active","equals","limit","settings","docs","emailServiceConfig","provider","providers","default","fromAddress","resend","broadcast","fromName","replyTo","apiKey","resendSettings","audienceIds","reduce","acc","item","locale","production","development","apiUrl","broadcastSettings","tokens","productionToken","developmentToken","newsletterEmailService","console","warn","getProvider","error"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/templates/NewsletterTemplate.tsx"],"sourcesContent":["import React from 'react'\nimport {\n Heading,\n Text,\n Link,\n Img,\n Button,\n Markdown,\n} from '@react-email/components'\nimport { BaseTemplate, baseStyles } from './BaseTemplate'\n\nexport interface NewsletterTemplateProps {\n subject: string\n preheader?: string\n title?: string\n content: string // Markdown content\n ctaButton?: {\n text: string\n href: string\n }\n footer?: {\n unsubscribeUrl?: string\n preferencesUrl?: string\n address?: string\n copyright?: string\n }\n}\n\nconst newsletterStyles = {\n heading: {\n fontSize: '32px',\n fontWeight: '700',\n color: '#1a1a1a',\n margin: '0 0 24px 0',\n },\n markdown: {\n h1: {\n fontSize: '28px',\n fontWeight: '700',\n color: '#1a1a1a',\n margin: '32px 0 16px 0',\n },\n h2: {\n fontSize: '24px',\n fontWeight: '600',\n color: '#1a1a1a',\n margin: '24px 0 12px 0',\n },\n h3: {\n fontSize: '20px',\n fontWeight: '600',\n color: '#1a1a1a',\n margin: '20px 0 8px 0',\n },\n p: {\n fontSize: '16px',\n lineHeight: '24px',\n color: '#4a4a4a',\n margin: '0 0 16px 0',\n },\n a: {\n color: '#0066cc',\n textDecoration: 'underline',\n },\n ul: {\n paddingLeft: '20px',\n margin: '0 0 16px 0',\n },\n ol: {\n paddingLeft: '20px',\n margin: '0 0 16px 0',\n },\n li: {\n fontSize: '16px',\n lineHeight: '24px',\n color: '#4a4a4a',\n marginBottom: '8px',\n },\n img: {\n maxWidth: '100%',\n height: 'auto',\n margin: '16px 0',\n },\n blockquote: {\n borderLeft: '4px solid #e6ebf1',\n paddingLeft: '16px',\n margin: '0 0 16px 0',\n fontStyle: 'italic',\n color: '#6a6a6a',\n },\n code: {\n backgroundColor: '#f6f8fa',\n padding: '2px 4px',\n borderRadius: '3px',\n fontFamily: 'monospace',\n fontSize: '14px',\n },\n pre: {\n backgroundColor: '#f6f8fa',\n padding: '16px',\n borderRadius: '6px',\n overflow: 'auto',\n margin: '0 0 16px 0',\n },\n },\n button: {\n backgroundColor: '#0066cc',\n borderRadius: '6px',\n color: '#ffffff',\n fontSize: '16px',\n fontWeight: '600',\n textDecoration: 'none',\n textAlign: 'center' as const,\n display: 'block',\n width: '100%',\n padding: '12px 20px',\n margin: '32px 0',\n },\n}\n\nexport const NewsletterTemplate: React.FC<NewsletterTemplateProps> = ({\n subject,\n preheader,\n title,\n content,\n ctaButton,\n footer,\n}) => {\n return (\n <BaseTemplate\n preview={preheader || subject}\n footer={footer}\n >\n {title && (\n <Heading style={newsletterStyles.heading}>\n {title}\n </Heading>\n )}\n \n <Markdown\n markdownCustomStyles={newsletterStyles.markdown}\n markdownContainerStyles={{\n padding: '0',\n }}\n >\n {content}\n </Markdown>\n \n {ctaButton && (\n <Button\n href={ctaButton.href}\n style={newsletterStyles.button}\n >\n {ctaButton.text}\n </Button>\n )}\n </BaseTemplate>\n )\n}\n\n// Export a function that creates a custom newsletter template\nexport function createNewsletterTemplate(\n customStyles?: Partial<typeof newsletterStyles>\n): React.FC<NewsletterTemplateProps> {\n const mergedStyles = {\n ...newsletterStyles,\n ...customStyles,\n }\n \n return (props: NewsletterTemplateProps) => (\n <BaseTemplate\n preview={props.preheader || props.subject}\n footer={props.footer}\n >\n {props.title && (\n <Heading style={mergedStyles.heading}>\n {props.title}\n </Heading>\n )}\n \n <Markdown\n markdownCustomStyles={mergedStyles.markdown}\n markdownContainerStyles={{\n padding: '0',\n }}\n >\n {props.content}\n </Markdown>\n \n {props.ctaButton && (\n <Button\n href={props.ctaButton.href}\n style={mergedStyles.button}\n >\n {props.ctaButton.text}\n </Button>\n )}\n </BaseTemplate>\n )\n}"],"names":["React","Heading","Button","Markdown","BaseTemplate","newsletterStyles","heading","fontSize","fontWeight","color","margin","markdown","h1","h2","h3","p","lineHeight","a","textDecoration","ul","paddingLeft","ol","li","marginBottom","img","maxWidth","height","blockquote","borderLeft","fontStyle","code","backgroundColor","padding","borderRadius","fontFamily","pre","overflow","button","textAlign","display","width","NewsletterTemplate","subject","preheader","title","content","ctaButton","footer","preview","style","markdownCustomStyles","markdownContainerStyles","href","text","createNewsletterTemplate","customStyles","mergedStyles","props"],"mappings":";AAAA,OAAOA,WAAW,QAAO;AACzB,SACEC,OAAO,EAIPC,MAAM,EACNC,QAAQ,QACH,0BAAyB;AAChC,SAASC,YAAY,QAAoB,iBAAgB;AAmBzD,MAAMC,mBAAmB;IACvBC,SAAS;QACPC,UAAU;QACVC,YAAY;QACZC,OAAO;QACPC,QAAQ;IACV;IACAC,UAAU;QACRC,IAAI;YACFL,UAAU;YACVC,YAAY;YACZC,OAAO;YACPC,QAAQ;QACV;QACAG,IAAI;YACFN,UAAU;YACVC,YAAY;YACZC,OAAO;YACPC,QAAQ;QACV;QACAI,IAAI;YACFP,UAAU;YACVC,YAAY;YACZC,OAAO;YACPC,QAAQ;QACV;QACAK,GAAG;YACDR,UAAU;YACVS,YAAY;YACZP,OAAO;YACPC,QAAQ;QACV;QACAO,GAAG;YACDR,OAAO;YACPS,gBAAgB;QAClB;QACAC,IAAI;YACFC,aAAa;YACbV,QAAQ;QACV;QACAW,IAAI;YACFD,aAAa;YACbV,QAAQ;QACV;QACAY,IAAI;YACFf,UAAU;YACVS,YAAY;YACZP,OAAO;YACPc,cAAc;QAChB;QACAC,KAAK;YACHC,UAAU;YACVC,QAAQ;YACRhB,QAAQ;QACV;QACAiB,YAAY;YACVC,YAAY;YACZR,aAAa;YACbV,QAAQ;YACRmB,WAAW;YACXpB,OAAO;QACT;QACAqB,MAAM;YACJC,iBAAiB;YACjBC,SAAS;YACTC,cAAc;YACdC,YAAY;YACZ3B,UAAU;QACZ;QACA4B,KAAK;YACHJ,iBAAiB;YACjBC,SAAS;YACTC,cAAc;YACdG,UAAU;YACV1B,QAAQ;QACV;IACF;IACA2B,QAAQ;QACNN,iBAAiB;QACjBE,cAAc;QACdxB,OAAO;QACPF,UAAU;QACVC,YAAY;QACZU,gBAAgB;QAChBoB,WAAW;QACXC,SAAS;QACTC,OAAO;QACPR,SAAS;QACTtB,QAAQ;IACV;AACF;AAEA,OAAO,MAAM+B,qBAAwD,CAAC,EACpEC,OAAO,EACPC,SAAS,EACTC,KAAK,EACLC,OAAO,EACPC,SAAS,EACTC,MAAM,EACP;IACC,qBACE,MAAC3C;QACC4C,SAASL,aAAaD;QACtBK,QAAQA;;YAEPH,uBACC,KAAC3C;gBAAQgD,OAAO5C,iBAAiBC,OAAO;0BACrCsC;;0BAIL,KAACzC;gBACC+C,sBAAsB7C,iBAAiBM,QAAQ;gBAC/CwC,yBAAyB;oBACvBnB,SAAS;gBACX;0BAECa;;YAGFC,2BACC,KAAC5C;gBACCkD,MAAMN,UAAUM,IAAI;gBACpBH,OAAO5C,iBAAiBgC,MAAM;0BAE7BS,UAAUO,IAAI;;;;AAKzB,EAAC;AAED,8DAA8D;AAC9D,OAAO,SAASC,yBACdC,YAA+C;IAE/C,MAAMC,eAAe;QACnB,GAAGnD,gBAAgB;QACnB,GAAGkD,YAAY;IACjB;IAEA,OAAO,CAACE,sBACN,MAACrD;YACC4C,SAASS,MAAMd,SAAS,IAAIc,MAAMf,OAAO;YACzCK,QAAQU,MAAMV,MAAM;;gBAEnBU,MAAMb,KAAK,kBACV,KAAC3C;oBAAQgD,OAAOO,aAAalD,OAAO;8BACjCmD,MAAMb,KAAK;;8BAIhB,KAACzC;oBACC+C,sBAAsBM,aAAa7C,QAAQ;oBAC3CwC,yBAAyB;wBACvBnB,SAAS;oBACX;8BAECyB,MAAMZ,OAAO;;gBAGfY,MAAMX,SAAS,kBACd,KAAC5C;oBACCkD,MAAMK,MAAMX,SAAS,CAACM,IAAI;oBAC1BH,OAAOO,aAAanB,MAAM;8BAEzBoB,MAAMX,SAAS,CAACO,IAAI;;;;AAK/B"}
1
+ {"version":3,"sources":["../../../src/templates/NewsletterTemplate.tsx"],"sourcesContent":["import React from 'react'\nimport {\n Heading,\n Button,\n Markdown,\n} from '@react-email/components'\nimport { BaseTemplate } from './BaseTemplate'\n\nexport interface NewsletterTemplateProps {\n subject: string\n preheader?: string\n title?: string\n content: string // Markdown content\n ctaButton?: {\n text: string\n href: string\n }\n footer?: {\n unsubscribeUrl?: string\n preferencesUrl?: string\n address?: string\n copyright?: string\n }\n}\n\nconst newsletterStyles = {\n heading: {\n fontSize: '32px',\n fontWeight: '700',\n color: '#1a1a1a',\n margin: '0 0 24px 0',\n },\n markdown: {\n h1: {\n fontSize: '28px',\n fontWeight: '700',\n color: '#1a1a1a',\n margin: '32px 0 16px 0',\n },\n h2: {\n fontSize: '24px',\n fontWeight: '600',\n color: '#1a1a1a',\n margin: '24px 0 12px 0',\n },\n h3: {\n fontSize: '20px',\n fontWeight: '600',\n color: '#1a1a1a',\n margin: '20px 0 8px 0',\n },\n p: {\n fontSize: '16px',\n lineHeight: '24px',\n color: '#4a4a4a',\n margin: '0 0 16px 0',\n },\n a: {\n color: '#0066cc',\n textDecoration: 'underline',\n },\n ul: {\n paddingLeft: '20px',\n margin: '0 0 16px 0',\n },\n ol: {\n paddingLeft: '20px',\n margin: '0 0 16px 0',\n },\n li: {\n fontSize: '16px',\n lineHeight: '24px',\n color: '#4a4a4a',\n marginBottom: '8px',\n },\n img: {\n maxWidth: '100%',\n height: 'auto',\n margin: '16px 0',\n },\n blockquote: {\n borderLeft: '4px solid #e6ebf1',\n paddingLeft: '16px',\n margin: '0 0 16px 0',\n fontStyle: 'italic',\n color: '#6a6a6a',\n },\n code: {\n backgroundColor: '#f6f8fa',\n padding: '2px 4px',\n borderRadius: '3px',\n fontFamily: 'monospace',\n fontSize: '14px',\n },\n pre: {\n backgroundColor: '#f6f8fa',\n padding: '16px',\n borderRadius: '6px',\n overflow: 'auto',\n margin: '0 0 16px 0',\n },\n },\n button: {\n backgroundColor: '#0066cc',\n borderRadius: '6px',\n color: '#ffffff',\n fontSize: '16px',\n fontWeight: '600',\n textDecoration: 'none',\n textAlign: 'center' as const,\n display: 'block',\n width: '100%',\n padding: '12px 20px',\n margin: '32px 0',\n },\n}\n\nexport const NewsletterTemplate: React.FC<NewsletterTemplateProps> = ({\n subject,\n preheader,\n title,\n content,\n ctaButton,\n footer,\n}) => {\n return (\n <BaseTemplate\n preview={preheader || subject}\n footer={footer}\n >\n {title && (\n <Heading style={newsletterStyles.heading}>\n {title}\n </Heading>\n )}\n \n <Markdown\n markdownCustomStyles={newsletterStyles.markdown}\n markdownContainerStyles={{\n padding: '0',\n }}\n >\n {content}\n </Markdown>\n \n {ctaButton && (\n <Button\n href={ctaButton.href}\n style={newsletterStyles.button}\n >\n {ctaButton.text}\n </Button>\n )}\n </BaseTemplate>\n )\n}\n\n// Export a function that creates a custom newsletter template\nexport function createNewsletterTemplate(\n customStyles?: Partial<typeof newsletterStyles>\n): React.FC<NewsletterTemplateProps> {\n const mergedStyles = {\n ...newsletterStyles,\n ...customStyles,\n }\n \n return (props: NewsletterTemplateProps) => (\n <BaseTemplate\n preview={props.preheader || props.subject}\n footer={props.footer}\n >\n {props.title && (\n <Heading style={mergedStyles.heading}>\n {props.title}\n </Heading>\n )}\n \n <Markdown\n markdownCustomStyles={mergedStyles.markdown}\n markdownContainerStyles={{\n padding: '0',\n }}\n >\n {props.content}\n </Markdown>\n \n {props.ctaButton && (\n <Button\n href={props.ctaButton.href}\n style={mergedStyles.button}\n >\n {props.ctaButton.text}\n </Button>\n )}\n </BaseTemplate>\n )\n}"],"names":["React","Heading","Button","Markdown","BaseTemplate","newsletterStyles","heading","fontSize","fontWeight","color","margin","markdown","h1","h2","h3","p","lineHeight","a","textDecoration","ul","paddingLeft","ol","li","marginBottom","img","maxWidth","height","blockquote","borderLeft","fontStyle","code","backgroundColor","padding","borderRadius","fontFamily","pre","overflow","button","textAlign","display","width","NewsletterTemplate","subject","preheader","title","content","ctaButton","footer","preview","style","markdownCustomStyles","markdownContainerStyles","href","text","createNewsletterTemplate","customStyles","mergedStyles","props"],"mappings":";AAAA,OAAOA,WAAW,QAAO;AACzB,SACEC,OAAO,EACPC,MAAM,EACNC,QAAQ,QACH,0BAAyB;AAChC,SAASC,YAAY,QAAQ,iBAAgB;AAmB7C,MAAMC,mBAAmB;IACvBC,SAAS;QACPC,UAAU;QACVC,YAAY;QACZC,OAAO;QACPC,QAAQ;IACV;IACAC,UAAU;QACRC,IAAI;YACFL,UAAU;YACVC,YAAY;YACZC,OAAO;YACPC,QAAQ;QACV;QACAG,IAAI;YACFN,UAAU;YACVC,YAAY;YACZC,OAAO;YACPC,QAAQ;QACV;QACAI,IAAI;YACFP,UAAU;YACVC,YAAY;YACZC,OAAO;YACPC,QAAQ;QACV;QACAK,GAAG;YACDR,UAAU;YACVS,YAAY;YACZP,OAAO;YACPC,QAAQ;QACV;QACAO,GAAG;YACDR,OAAO;YACPS,gBAAgB;QAClB;QACAC,IAAI;YACFC,aAAa;YACbV,QAAQ;QACV;QACAW,IAAI;YACFD,aAAa;YACbV,QAAQ;QACV;QACAY,IAAI;YACFf,UAAU;YACVS,YAAY;YACZP,OAAO;YACPc,cAAc;QAChB;QACAC,KAAK;YACHC,UAAU;YACVC,QAAQ;YACRhB,QAAQ;QACV;QACAiB,YAAY;YACVC,YAAY;YACZR,aAAa;YACbV,QAAQ;YACRmB,WAAW;YACXpB,OAAO;QACT;QACAqB,MAAM;YACJC,iBAAiB;YACjBC,SAAS;YACTC,cAAc;YACdC,YAAY;YACZ3B,UAAU;QACZ;QACA4B,KAAK;YACHJ,iBAAiB;YACjBC,SAAS;YACTC,cAAc;YACdG,UAAU;YACV1B,QAAQ;QACV;IACF;IACA2B,QAAQ;QACNN,iBAAiB;QACjBE,cAAc;QACdxB,OAAO;QACPF,UAAU;QACVC,YAAY;QACZU,gBAAgB;QAChBoB,WAAW;QACXC,SAAS;QACTC,OAAO;QACPR,SAAS;QACTtB,QAAQ;IACV;AACF;AAEA,OAAO,MAAM+B,qBAAwD,CAAC,EACpEC,OAAO,EACPC,SAAS,EACTC,KAAK,EACLC,OAAO,EACPC,SAAS,EACTC,MAAM,EACP;IACC,qBACE,MAAC3C;QACC4C,SAASL,aAAaD;QACtBK,QAAQA;;YAEPH,uBACC,KAAC3C;gBAAQgD,OAAO5C,iBAAiBC,OAAO;0BACrCsC;;0BAIL,KAACzC;gBACC+C,sBAAsB7C,iBAAiBM,QAAQ;gBAC/CwC,yBAAyB;oBACvBnB,SAAS;gBACX;0BAECa;;YAGFC,2BACC,KAAC5C;gBACCkD,MAAMN,UAAUM,IAAI;gBACpBH,OAAO5C,iBAAiBgC,MAAM;0BAE7BS,UAAUO,IAAI;;;;AAKzB,EAAC;AAED,8DAA8D;AAC9D,OAAO,SAASC,yBACdC,YAA+C;IAE/C,MAAMC,eAAe;QACnB,GAAGnD,gBAAgB;QACnB,GAAGkD,YAAY;IACjB;IAEA,OAAO,CAACE,sBACN,MAACrD;YACC4C,SAASS,MAAMd,SAAS,IAAIc,MAAMf,OAAO;YACzCK,QAAQU,MAAMV,MAAM;;gBAEnBU,MAAMb,KAAK,kBACV,KAAC3C;oBAAQgD,OAAOO,aAAalD,OAAO;8BACjCmD,MAAMb,KAAK;;8BAIhB,KAACzC;oBACC+C,sBAAsBM,aAAa7C,QAAQ;oBAC3CwC,yBAAyB;wBACvBnB,SAAS;oBACX;8BAECyB,MAAMZ,OAAO;;gBAGfY,MAAMX,SAAS,kBACd,KAAC5C;oBACCkD,MAAMK,MAAMX,SAAS,CAACM,IAAI;oBAC1BH,OAAOO,aAAanB,MAAM;8BAEzBoB,MAAMX,SAAS,CAACO,IAAI;;;;AAK/B"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/templates/WelcomeTemplate.tsx"],"sourcesContent":["import React from 'react'\nimport {\n Heading,\n Text,\n Link,\n Button,\n} from '@react-email/components'\nimport { BaseTemplate } from './BaseTemplate'\nimport type { WelcomeEmailProps } from '../types'\n\nconst welcomeStyles = {\n heading: {\n fontSize: '28px',\n fontWeight: '700',\n color: '#1a1a1a',\n margin: '0 0 16px 0',\n },\n subheading: {\n fontSize: '20px',\n fontWeight: '600',\n color: '#4a4a4a',\n margin: '24px 0 12px 0',\n },\n text: {\n fontSize: '16px',\n lineHeight: '24px',\n color: '#4a4a4a',\n margin: '0 0 16px 0',\n },\n button: {\n backgroundColor: '#0066cc',\n borderRadius: '6px',\n color: '#ffffff',\n fontSize: '16px',\n fontWeight: '600',\n textDecoration: 'none',\n textAlign: 'center' as const,\n display: 'block',\n width: '200px',\n padding: '12px 20px',\n margin: '24px 0',\n },\n list: {\n paddingLeft: '20px',\n margin: '0 0 16px 0',\n },\n listItem: {\n fontSize: '16px',\n lineHeight: '24px',\n color: '#4a4a4a',\n marginBottom: '8px',\n },\n}\n\nexport const WelcomeTemplate: React.FC<WelcomeEmailProps> = ({\n subscriber,\n unsubscribeUrl,\n preferencesUrl,\n}) => {\n return (\n <BaseTemplate\n preview={`Welcome to our newsletter, ${subscriber.name || 'there'}!`}\n footer={{\n unsubscribeUrl,\n preferencesUrl,\n }}\n >\n <Heading style={welcomeStyles.heading}>\n Welcome{subscriber.name ? `, ${subscriber.name}` : ''}!\n </Heading>\n \n <Text style={welcomeStyles.text}>\n Thank you for subscribing to our newsletter. We're excited to have you on board!\n </Text>\n \n <Text style={welcomeStyles.text}>\n Here's what you can expect from us:\n </Text>\n \n <ul style={welcomeStyles.list}>\n <li style={welcomeStyles.listItem}>\n Weekly updates with our latest content\n </li>\n <li style={welcomeStyles.listItem}>\n Exclusive insights and tips\n </li>\n <li style={welcomeStyles.listItem}>\n Early access to new features and announcements\n </li>\n </ul>\n \n {preferencesUrl && (\n <>\n <Heading as=\"h2\" style={welcomeStyles.subheading}>\n Customize Your Experience\n </Heading>\n \n <Text style={welcomeStyles.text}>\n Want to control what types of emails you receive? Visit your preferences page to customize your subscription.\n </Text>\n \n <Button\n href={preferencesUrl}\n style={welcomeStyles.button}\n >\n Manage Preferences\n </Button>\n </>\n )}\n \n <Text style={welcomeStyles.text}>\n If you have any questions or feedback, feel free to reply to this email. We'd love to hear from you!\n </Text>\n \n <Text style={welcomeStyles.text}>\n Best regards,<br />\n The Team\n </Text>\n </BaseTemplate>\n )\n}\n\n// Export a function that creates a custom welcome template\nexport function createWelcomeTemplate(\n customContent?: {\n heading?: string\n intro?: string\n features?: string[]\n ctaText?: string\n closing?: string\n },\n customStyles?: Partial<typeof welcomeStyles>\n): React.FC<WelcomeEmailProps> {\n const mergedStyles = {\n ...welcomeStyles,\n ...customStyles,\n }\n \n return (props: WelcomeEmailProps) => (\n <BaseTemplate\n preview={customContent?.heading || `Welcome to our newsletter, ${props.subscriber.name || 'there'}!`}\n footer={{\n unsubscribeUrl: props.unsubscribeUrl,\n preferencesUrl: props.preferencesUrl,\n }}\n >\n <Heading style={mergedStyles.heading}>\n {customContent?.heading || `Welcome${props.subscriber.name ? `, ${props.subscriber.name}` : ''}!`}\n </Heading>\n \n <Text style={mergedStyles.text}>\n {customContent?.intro || 'Thank you for subscribing to our newsletter. We\\'re excited to have you on board!'}\n </Text>\n \n {customContent?.features && customContent.features.length > 0 && (\n <>\n <Text style={mergedStyles.text}>\n Here's what you can expect from us:\n </Text>\n \n <ul style={mergedStyles.list}>\n {customContent.features.map((feature, index) => (\n <li key={index} style={mergedStyles.listItem}>\n {feature}\n </li>\n ))}\n </ul>\n </>\n )}\n \n {props.preferencesUrl && (\n <>\n <Heading as=\"h2\" style={mergedStyles.subheading}>\n Customize Your Experience\n </Heading>\n \n <Text style={mergedStyles.text}>\n Want to control what types of emails you receive? Visit your preferences page to customize your subscription.\n </Text>\n \n <Button\n href={props.preferencesUrl}\n style={mergedStyles.button}\n >\n {customContent?.ctaText || 'Manage Preferences'}\n </Button>\n </>\n )}\n \n <Text style={mergedStyles.text}>\n {customContent?.closing || 'If you have any questions or feedback, feel free to reply to this email. We\\'d love to hear from you!'}\n </Text>\n \n <Text style={mergedStyles.text}>\n Best regards,<br />\n The Team\n </Text>\n </BaseTemplate>\n )\n}"],"names":["React","Heading","Text","Button","BaseTemplate","welcomeStyles","heading","fontSize","fontWeight","color","margin","subheading","text","lineHeight","button","backgroundColor","borderRadius","textDecoration","textAlign","display","width","padding","list","paddingLeft","listItem","marginBottom","WelcomeTemplate","subscriber","unsubscribeUrl","preferencesUrl","preview","name","footer","style","ul","li","as","href","br","createWelcomeTemplate","customContent","customStyles","mergedStyles","props","intro","features","length","map","feature","index","ctaText","closing"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/templates/WelcomeTemplate.tsx"],"sourcesContent":["import React from 'react'\nimport {\n Heading,\n Text,\n Button,\n} from '@react-email/components'\nimport { BaseTemplate } from './BaseTemplate'\nimport type { WelcomeEmailProps } from '../types'\n\nconst welcomeStyles = {\n heading: {\n fontSize: '28px',\n fontWeight: '700',\n color: '#1a1a1a',\n margin: '0 0 16px 0',\n },\n subheading: {\n fontSize: '20px',\n fontWeight: '600',\n color: '#4a4a4a',\n margin: '24px 0 12px 0',\n },\n text: {\n fontSize: '16px',\n lineHeight: '24px',\n color: '#4a4a4a',\n margin: '0 0 16px 0',\n },\n button: {\n backgroundColor: '#0066cc',\n borderRadius: '6px',\n color: '#ffffff',\n fontSize: '16px',\n fontWeight: '600',\n textDecoration: 'none',\n textAlign: 'center' as const,\n display: 'block',\n width: '200px',\n padding: '12px 20px',\n margin: '24px 0',\n },\n list: {\n paddingLeft: '20px',\n margin: '0 0 16px 0',\n },\n listItem: {\n fontSize: '16px',\n lineHeight: '24px',\n color: '#4a4a4a',\n marginBottom: '8px',\n },\n}\n\nexport const WelcomeTemplate: React.FC<WelcomeEmailProps> = ({\n subscriber,\n unsubscribeUrl,\n preferencesUrl,\n}) => {\n return (\n <BaseTemplate\n preview={`Welcome to our newsletter, ${subscriber.name || 'there'}!`}\n footer={{\n unsubscribeUrl,\n preferencesUrl,\n }}\n >\n <Heading style={welcomeStyles.heading}>\n Welcome{subscriber.name ? `, ${subscriber.name}` : ''}!\n </Heading>\n \n <Text style={welcomeStyles.text}>\n Thank you for subscribing to our newsletter. We're excited to have you on board!\n </Text>\n \n <Text style={welcomeStyles.text}>\n Here's what you can expect from us:\n </Text>\n \n <ul style={welcomeStyles.list}>\n <li style={welcomeStyles.listItem}>\n Weekly updates with our latest content\n </li>\n <li style={welcomeStyles.listItem}>\n Exclusive insights and tips\n </li>\n <li style={welcomeStyles.listItem}>\n Early access to new features and announcements\n </li>\n </ul>\n \n {preferencesUrl && (\n <>\n <Heading as=\"h2\" style={welcomeStyles.subheading}>\n Customize Your Experience\n </Heading>\n \n <Text style={welcomeStyles.text}>\n Want to control what types of emails you receive? Visit your preferences page to customize your subscription.\n </Text>\n \n <Button\n href={preferencesUrl}\n style={welcomeStyles.button}\n >\n Manage Preferences\n </Button>\n </>\n )}\n \n <Text style={welcomeStyles.text}>\n If you have any questions or feedback, feel free to reply to this email. We'd love to hear from you!\n </Text>\n \n <Text style={welcomeStyles.text}>\n Best regards,<br />\n The Team\n </Text>\n </BaseTemplate>\n )\n}\n\n// Export a function that creates a custom welcome template\nexport function createWelcomeTemplate(\n customContent?: {\n heading?: string\n intro?: string\n features?: string[]\n ctaText?: string\n closing?: string\n },\n customStyles?: Partial<typeof welcomeStyles>\n): React.FC<WelcomeEmailProps> {\n const mergedStyles = {\n ...welcomeStyles,\n ...customStyles,\n }\n \n return (props: WelcomeEmailProps) => (\n <BaseTemplate\n preview={customContent?.heading || `Welcome to our newsletter, ${props.subscriber.name || 'there'}!`}\n footer={{\n unsubscribeUrl: props.unsubscribeUrl,\n preferencesUrl: props.preferencesUrl,\n }}\n >\n <Heading style={mergedStyles.heading}>\n {customContent?.heading || `Welcome${props.subscriber.name ? `, ${props.subscriber.name}` : ''}!`}\n </Heading>\n \n <Text style={mergedStyles.text}>\n {customContent?.intro || 'Thank you for subscribing to our newsletter. We\\'re excited to have you on board!'}\n </Text>\n \n {customContent?.features && customContent.features.length > 0 && (\n <>\n <Text style={mergedStyles.text}>\n Here's what you can expect from us:\n </Text>\n \n <ul style={mergedStyles.list}>\n {customContent.features.map((feature, index) => (\n <li key={index} style={mergedStyles.listItem}>\n {feature}\n </li>\n ))}\n </ul>\n </>\n )}\n \n {props.preferencesUrl && (\n <>\n <Heading as=\"h2\" style={mergedStyles.subheading}>\n Customize Your Experience\n </Heading>\n \n <Text style={mergedStyles.text}>\n Want to control what types of emails you receive? Visit your preferences page to customize your subscription.\n </Text>\n \n <Button\n href={props.preferencesUrl}\n style={mergedStyles.button}\n >\n {customContent?.ctaText || 'Manage Preferences'}\n </Button>\n </>\n )}\n \n <Text style={mergedStyles.text}>\n {customContent?.closing || 'If you have any questions or feedback, feel free to reply to this email. We\\'d love to hear from you!'}\n </Text>\n \n <Text style={mergedStyles.text}>\n Best regards,<br />\n The Team\n </Text>\n </BaseTemplate>\n )\n}"],"names":["React","Heading","Text","Button","BaseTemplate","welcomeStyles","heading","fontSize","fontWeight","color","margin","subheading","text","lineHeight","button","backgroundColor","borderRadius","textDecoration","textAlign","display","width","padding","list","paddingLeft","listItem","marginBottom","WelcomeTemplate","subscriber","unsubscribeUrl","preferencesUrl","preview","name","footer","style","ul","li","as","href","br","createWelcomeTemplate","customContent","customStyles","mergedStyles","props","intro","features","length","map","feature","index","ctaText","closing"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/types/index.ts"],"sourcesContent":["import type { Config, CollectionConfig, GlobalConfig, Field, Endpoint } from 'payload'\n\nexport interface NewsletterPluginConfig {\n /**\n * Enable or disable the plugin\n * @default true\n */\n enabled?: boolean\n\n /**\n * Slug for the subscribers collection\n * @default 'subscribers'\n */\n subscribersSlug?: string\n\n /**\n * Authentication configuration for magic links\n */\n auth?: {\n /**\n * Enable magic link authentication\n * @default true\n */\n enabled?: boolean\n \n /**\n * Token expiration time\n * @default '7d'\n */\n tokenExpiration?: string\n \n /**\n * Path where magic link redirects\n * @default '/newsletter/verify'\n */\n magicLinkPath?: string\n }\n\n /**\n * Email provider configuration\n */\n providers: {\n /**\n * Default provider to use\n */\n default: 'resend' | 'broadcast' | string\n \n /**\n * Resend provider configuration\n */\n resend?: ResendProviderConfig\n \n /**\n * Broadcast provider configuration\n */\n broadcast?: BroadcastProviderConfig\n }\n\n /**\n * Field customization options\n */\n fields?: {\n /**\n * Override default fields\n */\n overrides?: (args: { defaultFields: Field[] }) => Field[]\n \n /**\n * Additional custom fields\n */\n additional?: Field[]\n }\n\n /**\n * Email template components\n */\n templates?: {\n /**\n * Welcome email template\n */\n welcome?: React.ComponentType<WelcomeEmailProps>\n \n /**\n * Magic link email template\n */\n magicLink?: React.ComponentType<MagicLinkEmailProps>\n }\n\n /**\n * Plugin hooks\n */\n hooks?: {\n beforeSubscribe?: (args: BeforeSubscribeArgs) => void | Promise<void>\n afterSubscribe?: (args: AfterSubscribeArgs) => void | Promise<void>\n beforeUnsubscribe?: (args: BeforeUnsubscribeArgs) => void | Promise<void>\n afterUnsubscribe?: (args: AfterUnsubscribeArgs) => void | Promise<void>\n }\n\n /**\n * UI component overrides\n */\n components?: {\n signupForm?: React.ComponentType<SignupFormProps>\n preferencesForm?: React.ComponentType<PreferencesFormProps>\n }\n\n /**\n * Feature flags\n */\n features?: {\n /**\n * Lead magnet configuration\n */\n leadMagnets?: {\n enabled?: boolean\n collection?: string\n }\n \n /**\n * Post-signup survey configuration\n */\n surveys?: {\n enabled?: boolean\n questions?: SurveyQuestion[]\n }\n \n /**\n * UTM tracking configuration\n */\n utmTracking?: {\n enabled?: boolean\n fields?: string[]\n }\n \n /**\n * Newsletter scheduling configuration\n */\n newsletterScheduling?: {\n enabled?: boolean\n /**\n * Collections to add newsletter fields to\n * Can be a string for single collection or array for multiple\n * @example 'articles' or ['articles', 'posts', 'updates']\n */\n collections?: string | string[]\n /**\n * Field configuration\n */\n fields?: {\n /**\n * Group name for newsletter fields\n * @default 'newsletterScheduling'\n */\n groupName?: string\n /**\n * Rich text field name to use for content\n * @default 'content'\n */\n contentField?: string\n /**\n * Whether to create a markdown companion field\n * @default true\n */\n createMarkdownField?: boolean\n }\n }\n }\n\n /**\n * Internationalization configuration\n */\n i18n?: {\n defaultLocale?: string\n locales?: string[]\n }\n}\n\nexport interface ResendProviderConfig {\n apiKey: string\n fromAddress?: string\n fromName?: string\n audienceIds?: {\n [locale: string]: {\n production?: string\n development?: string\n }\n }\n}\n\nexport interface BroadcastProviderConfig {\n apiUrl: string\n tokens: {\n production?: string\n development?: string\n }\n fromAddress?: string\n fromName?: string\n}\n\nexport interface EmailProvider {\n send(params: SendEmailParams): Promise<void>\n addContact(contact: Subscriber): Promise<void>\n updateContact(contact: Subscriber): Promise<void>\n removeContact(email: string): Promise<void>\n}\n\nexport interface SendEmailParams {\n to: string | string[]\n subject: string\n html?: string\n text?: string\n react?: React.ReactElement\n}\n\nexport interface Subscriber {\n id: string\n email: string\n name?: string\n locale?: string\n subscriptionStatus: 'active' | 'unsubscribed' | 'pending'\n emailPreferences?: {\n newsletter?: boolean\n announcements?: boolean\n [key: string]: boolean | undefined\n }\n source?: string\n utmParameters?: {\n source?: string\n medium?: string\n campaign?: string\n content?: string\n term?: string\n }\n createdAt: string\n updatedAt: string\n}\n\nexport interface WelcomeEmailProps {\n subscriber: Subscriber\n unsubscribeUrl: string\n preferencesUrl: string\n}\n\nexport interface MagicLinkEmailProps {\n magicLinkUrl: string\n subscriber: Subscriber\n}\n\nexport interface SignupFormProps {\n onSuccess?: (subscriber: Subscriber) => void\n onError?: (error: Error) => void\n showName?: boolean\n showPreferences?: boolean\n leadMagnet?: {\n id: string\n title: string\n description?: string\n }\n className?: string\n styles?: {\n form?: React.CSSProperties\n inputGroup?: React.CSSProperties\n label?: React.CSSProperties\n input?: React.CSSProperties\n button?: React.CSSProperties\n buttonDisabled?: React.CSSProperties\n error?: React.CSSProperties\n success?: React.CSSProperties\n checkbox?: React.CSSProperties\n checkboxInput?: React.CSSProperties\n checkboxLabel?: React.CSSProperties\n }\n apiEndpoint?: string\n buttonText?: string\n loadingText?: string\n successMessage?: string\n placeholders?: {\n email?: string\n name?: string\n }\n labels?: {\n email?: string\n name?: string\n newsletter?: string\n announcements?: string\n }\n}\n\nexport interface PreferencesFormProps {\n subscriber?: Subscriber\n onSuccess?: (subscriber: Subscriber) => void\n onError?: (error: Error) => void\n className?: string\n styles?: {\n container?: React.CSSProperties\n heading?: React.CSSProperties\n form?: React.CSSProperties\n section?: React.CSSProperties\n sectionTitle?: React.CSSProperties\n inputGroup?: React.CSSProperties\n label?: React.CSSProperties\n input?: React.CSSProperties\n select?: React.CSSProperties\n checkbox?: React.CSSProperties\n checkboxInput?: React.CSSProperties\n checkboxLabel?: React.CSSProperties\n buttonGroup?: React.CSSProperties\n button?: React.CSSProperties\n primaryButton?: React.CSSProperties\n secondaryButton?: React.CSSProperties\n dangerButton?: React.CSSProperties\n error?: React.CSSProperties\n success?: React.CSSProperties\n info?: React.CSSProperties\n }\n sessionToken?: string\n apiEndpoint?: string\n showUnsubscribe?: boolean\n locales?: string[]\n labels?: {\n title?: string\n personalInfo?: string\n emailPreferences?: string\n name?: string\n language?: string\n newsletter?: string\n announcements?: string\n saveButton?: string\n unsubscribeButton?: string\n saving?: string\n saved?: string\n unsubscribeConfirm?: string\n }\n}\n\nexport interface BeforeSubscribeArgs {\n data: Partial<Subscriber>\n req: any\n}\n\nexport interface AfterSubscribeArgs {\n doc: Subscriber\n req: any\n}\n\nexport interface BeforeUnsubscribeArgs {\n email: string\n req: any\n}\n\nexport interface AfterUnsubscribeArgs {\n doc: Subscriber\n req: any\n}\n\nexport interface SurveyQuestion {\n id: string\n question: string\n type: 'text' | 'select' | 'multiselect' | 'radio'\n options?: string[]\n required?: boolean\n}"],"names":[],"mappings":"AAmWA,WAMC"}
1
+ {"version":3,"sources":["../../../src/types/index.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nexport interface NewsletterPluginConfig {\n /**\n * Enable or disable the plugin\n * @default true\n */\n enabled?: boolean\n\n /**\n * Slug for the subscribers collection\n * @default 'subscribers'\n */\n subscribersSlug?: string\n \n /**\n * Slug for the newsletter settings collection\n * @default 'newsletter-settings'\n */\n settingsSlug?: string\n\n /**\n * Authentication configuration for magic links\n */\n auth?: {\n /**\n * Enable magic link authentication\n * @default true\n */\n enabled?: boolean\n \n /**\n * Token expiration time\n * @default '7d'\n */\n tokenExpiration?: string\n \n /**\n * Path where magic link redirects\n * @default '/newsletter/verify'\n */\n magicLinkPath?: string\n }\n\n /**\n * Email provider configuration\n */\n providers: {\n /**\n * Default provider to use\n */\n default: 'resend' | 'broadcast' | string\n \n /**\n * Resend provider configuration\n */\n resend?: ResendProviderConfig\n \n /**\n * Broadcast provider configuration\n */\n broadcast?: BroadcastProviderConfig\n }\n\n /**\n * Field customization options\n */\n fields?: {\n /**\n * Override default fields\n */\n overrides?: (args: { defaultFields: Field[] }) => Field[]\n \n /**\n * Additional custom fields\n */\n additional?: Field[]\n }\n\n /**\n * Email template components\n */\n templates?: {\n /**\n * Welcome email template\n */\n welcome?: React.ComponentType<WelcomeEmailProps>\n \n /**\n * Magic link email template\n */\n magicLink?: React.ComponentType<MagicLinkEmailProps>\n }\n\n /**\n * Plugin hooks\n */\n hooks?: {\n beforeSubscribe?: (args: BeforeSubscribeArgs) => void | Promise<void>\n afterSubscribe?: (args: AfterSubscribeArgs) => void | Promise<void>\n beforeUnsubscribe?: (args: BeforeUnsubscribeArgs) => void | Promise<void>\n afterUnsubscribe?: (args: AfterUnsubscribeArgs) => void | Promise<void>\n }\n\n /**\n * UI component overrides\n */\n components?: {\n signupForm?: React.ComponentType<SignupFormProps>\n preferencesForm?: React.ComponentType<PreferencesFormProps>\n }\n\n /**\n * Feature flags\n */\n features?: {\n /**\n * Lead magnet configuration\n */\n leadMagnets?: {\n enabled?: boolean\n collection?: string\n }\n \n /**\n * Post-signup survey configuration\n */\n surveys?: {\n enabled?: boolean\n questions?: SurveyQuestion[]\n }\n \n /**\n * UTM tracking configuration\n */\n utmTracking?: {\n enabled?: boolean\n fields?: string[]\n }\n \n /**\n * Newsletter scheduling configuration\n */\n newsletterScheduling?: {\n enabled?: boolean\n /**\n * Collections to add newsletter fields to\n * Can be a string for single collection or array for multiple\n * @example 'articles' or ['articles', 'posts', 'updates']\n */\n collections?: string | string[]\n /**\n * Field configuration\n */\n fields?: {\n /**\n * Group name for newsletter fields\n * @default 'newsletterScheduling'\n */\n groupName?: string\n /**\n * Rich text field name to use for content\n * @default 'content'\n */\n contentField?: string\n /**\n * Whether to create a markdown companion field\n * @default true\n */\n createMarkdownField?: boolean\n }\n }\n }\n\n /**\n * Internationalization configuration\n */\n i18n?: {\n defaultLocale?: string\n locales?: string[]\n }\n}\n\nexport interface ResendProviderConfig {\n apiKey: string\n fromAddress?: string\n fromName?: string\n audienceIds?: {\n [locale: string]: {\n production?: string\n development?: string\n }\n }\n}\n\nexport interface BroadcastProviderConfig {\n apiUrl: string\n tokens: {\n production?: string\n development?: string\n }\n fromAddress?: string\n fromName?: string\n}\n\nexport interface EmailProvider {\n send(params: SendEmailParams): Promise<void>\n addContact(contact: Subscriber): Promise<void>\n updateContact(contact: Subscriber): Promise<void>\n removeContact(email: string): Promise<void>\n}\n\nexport interface SendEmailParams {\n to: string | string[]\n subject: string\n html?: string\n text?: string\n react?: React.ReactElement\n}\n\nexport interface Subscriber {\n id: string\n email: string\n name?: string\n locale?: string\n subscriptionStatus: 'active' | 'unsubscribed' | 'pending'\n emailPreferences?: {\n newsletter?: boolean\n announcements?: boolean\n [key: string]: boolean | undefined\n }\n source?: string\n utmParameters?: {\n source?: string\n medium?: string\n campaign?: string\n content?: string\n term?: string\n }\n createdAt: string\n updatedAt: string\n}\n\nexport interface WelcomeEmailProps {\n subscriber: Subscriber\n unsubscribeUrl: string\n preferencesUrl: string\n}\n\nexport interface MagicLinkEmailProps {\n magicLinkUrl: string\n subscriber: Subscriber\n}\n\nexport interface SignupFormProps {\n onSuccess?: (subscriber: Subscriber) => void\n onError?: (error: Error) => void\n showName?: boolean\n showPreferences?: boolean\n leadMagnet?: {\n id: string\n title: string\n description?: string\n }\n className?: string\n styles?: {\n form?: React.CSSProperties\n inputGroup?: React.CSSProperties\n label?: React.CSSProperties\n input?: React.CSSProperties\n button?: React.CSSProperties\n buttonDisabled?: React.CSSProperties\n error?: React.CSSProperties\n success?: React.CSSProperties\n checkbox?: React.CSSProperties\n checkboxInput?: React.CSSProperties\n checkboxLabel?: React.CSSProperties\n }\n apiEndpoint?: string\n buttonText?: string\n loadingText?: string\n successMessage?: string\n placeholders?: {\n email?: string\n name?: string\n }\n labels?: {\n email?: string\n name?: string\n newsletter?: string\n announcements?: string\n }\n}\n\nexport interface PreferencesFormProps {\n subscriber?: Subscriber\n onSuccess?: (subscriber: Subscriber) => void\n onError?: (error: Error) => void\n className?: string\n styles?: {\n container?: React.CSSProperties\n heading?: React.CSSProperties\n form?: React.CSSProperties\n section?: React.CSSProperties\n sectionTitle?: React.CSSProperties\n inputGroup?: React.CSSProperties\n label?: React.CSSProperties\n input?: React.CSSProperties\n select?: React.CSSProperties\n checkbox?: React.CSSProperties\n checkboxInput?: React.CSSProperties\n checkboxLabel?: React.CSSProperties\n buttonGroup?: React.CSSProperties\n button?: React.CSSProperties\n primaryButton?: React.CSSProperties\n secondaryButton?: React.CSSProperties\n dangerButton?: React.CSSProperties\n error?: React.CSSProperties\n success?: React.CSSProperties\n info?: React.CSSProperties\n }\n sessionToken?: string\n apiEndpoint?: string\n showUnsubscribe?: boolean\n locales?: string[]\n labels?: {\n title?: string\n personalInfo?: string\n emailPreferences?: string\n name?: string\n language?: string\n newsletter?: string\n announcements?: string\n saveButton?: string\n unsubscribeButton?: string\n saving?: string\n saved?: string\n unsubscribeConfirm?: string\n }\n}\n\nexport interface BeforeSubscribeArgs {\n data: Partial<Subscriber>\n req: any\n}\n\nexport interface AfterSubscribeArgs {\n doc: Subscriber\n req: any\n}\n\nexport interface BeforeUnsubscribeArgs {\n email: string\n req: any\n}\n\nexport interface AfterUnsubscribeArgs {\n doc: Subscriber\n req: any\n}\n\nexport interface SurveyQuestion {\n id: string\n question: string\n type: 'text' | 'select' | 'multiselect' | 'radio'\n options?: string[]\n required?: boolean\n}"],"names":[],"mappings":"AAyWA,WAMC"}