@jhits/plugin-newsletter 0.0.8 → 0.0.10

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 (75) hide show
  1. package/data/locales/en/common.json +12 -0
  2. package/data/locales/nl/common.json +12 -0
  3. package/data/locales/sv/common.json +12 -0
  4. package/dist/api/email-utils.d.ts +6 -0
  5. package/dist/api/email-utils.d.ts.map +1 -0
  6. package/dist/api/email-utils.js +134 -0
  7. package/dist/api/handler.d.ts +2 -47
  8. package/dist/api/handler.d.ts.map +1 -1
  9. package/dist/api/handler.js +2 -523
  10. package/dist/api/handlers/index.d.ts +12 -0
  11. package/dist/api/handlers/index.d.ts.map +1 -0
  12. package/dist/api/handlers/index.js +11 -0
  13. package/dist/api/handlers/newsletters.d.ts +11 -0
  14. package/dist/api/handlers/newsletters.d.ts.map +1 -0
  15. package/dist/api/handlers/newsletters.js +250 -0
  16. package/dist/api/handlers/send-newsletter.d.ts +8 -0
  17. package/dist/api/handlers/send-newsletter.d.ts.map +1 -0
  18. package/dist/api/handlers/send-newsletter.js +206 -0
  19. package/dist/api/handlers/settings.d.ts +11 -0
  20. package/dist/api/handlers/settings.d.ts.map +1 -0
  21. package/dist/api/handlers/settings.js +304 -0
  22. package/dist/api/handlers/subscribers.d.ts +10 -0
  23. package/dist/api/handlers/subscribers.d.ts.map +1 -0
  24. package/dist/api/handlers/subscribers.js +96 -0
  25. package/dist/api/handlers/upload.d.ts +7 -0
  26. package/dist/api/handlers/upload.d.ts.map +1 -0
  27. package/dist/api/handlers/upload.js +32 -0
  28. package/dist/api/handlers/welcome-email.d.ts +13 -0
  29. package/dist/api/handlers/welcome-email.d.ts.map +1 -0
  30. package/dist/api/handlers/welcome-email.js +142 -0
  31. package/dist/api/router.d.ts.map +1 -1
  32. package/dist/api/router.js +45 -6
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +42 -16
  35. package/dist/lib/email/EmailRenderer.d.ts.map +1 -1
  36. package/dist/lib/email/EmailRenderer.js +3 -7
  37. package/dist/lib/i18n.d.ts +16 -0
  38. package/dist/lib/i18n.d.ts.map +1 -0
  39. package/dist/lib/i18n.js +60 -0
  40. package/dist/state/EditorContext.d.ts +3 -1
  41. package/dist/state/EditorContext.d.ts.map +1 -1
  42. package/dist/state/EditorContext.js +1 -5
  43. package/dist/state/reducer.js +5 -5
  44. package/dist/types/newsletter.d.ts +1 -0
  45. package/dist/types/newsletter.d.ts.map +1 -1
  46. package/dist/views/CanvasEditor/CanvasEditorView.d.ts +4 -2
  47. package/dist/views/CanvasEditor/CanvasEditorView.d.ts.map +1 -1
  48. package/dist/views/CanvasEditor/CanvasEditorView.js +73 -10
  49. package/dist/views/CanvasEditor/EditorHeader.d.ts +6 -1
  50. package/dist/views/CanvasEditor/EditorHeader.d.ts.map +1 -1
  51. package/dist/views/CanvasEditor/EditorHeader.js +66 -11
  52. package/dist/views/CanvasEditor/components/EditorSidebar.d.ts +6 -2
  53. package/dist/views/CanvasEditor/components/EditorSidebar.d.ts.map +1 -1
  54. package/dist/views/CanvasEditor/components/EditorSidebar.js +3 -3
  55. package/dist/views/CanvasEditor/hooks/useNewsletterLoader.d.ts +1 -1
  56. package/dist/views/CanvasEditor/hooks/useNewsletterLoader.d.ts.map +1 -1
  57. package/dist/views/CanvasEditor/hooks/useNewsletterLoader.js +41 -5
  58. package/dist/views/NewsletterEditor.d.ts +4 -2
  59. package/dist/views/NewsletterEditor.d.ts.map +1 -1
  60. package/dist/views/NewsletterEditor.js +2 -2
  61. package/dist/views/NewsletterManager.d.ts.map +1 -1
  62. package/dist/views/NewsletterManager.js +137 -8
  63. package/dist/views/components/SendNewsletterModal.d.ts +18 -0
  64. package/dist/views/components/SendNewsletterModal.d.ts.map +1 -0
  65. package/dist/views/components/SendNewsletterModal.js +107 -0
  66. package/dist/views/components/SmtpSettingsModal.d.ts +10 -0
  67. package/dist/views/components/SmtpSettingsModal.d.ts.map +1 -0
  68. package/dist/views/components/SmtpSettingsModal.js +145 -0
  69. package/dist/views/components/TestEmailModal.d.ts +10 -0
  70. package/dist/views/components/TestEmailModal.d.ts.map +1 -0
  71. package/dist/views/components/TestEmailModal.js +99 -0
  72. package/package.json +20 -9
  73. package/templates/logo.png +0 -0
  74. package/templates/test-email.hbs +221 -0
  75. package/templates/welcome-email-legacy.hbs +35 -0
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Settings API Handler
3
+ */
4
+ 'use server';
5
+ import { NextResponse } from 'next/server';
6
+ import nodemailer from 'nodemailer';
7
+ import path from 'path';
8
+ import fs from 'fs';
9
+ import handlebars from 'handlebars';
10
+ import { getTestEmailTranslations } from '../../lib/i18n';
11
+ async function getWelcomeEmail(config) {
12
+ const dbConnection = await config.getDb();
13
+ const db = dbConnection.db();
14
+ const newsletters = db.collection('newsletters');
15
+ const welcomeEmail = await newsletters.findOne({
16
+ type: 'welcome_email',
17
+ id: 'welcome_automation'
18
+ });
19
+ return welcomeEmail || null;
20
+ }
21
+ export async function GET_SETTINGS(req, config) {
22
+ try {
23
+ const userId = await config.getUserId?.(req);
24
+ if (!userId) {
25
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
26
+ }
27
+ const dbConnection = await config.getDb();
28
+ const db = dbConnection.db();
29
+ const newsletters = db.collection('newsletters');
30
+ const settings = await newsletters.findOne({ id: 'welcome_automation' });
31
+ return NextResponse.json(settings || {
32
+ id: 'welcome_automation',
33
+ languages: {
34
+ nl: { title: '', message: '' },
35
+ en: { title: '', message: '' },
36
+ sv: { title: '', message: '' },
37
+ },
38
+ });
39
+ }
40
+ catch (error) {
41
+ console.error('[NewsletterAPI] GET_SETTINGS error:', error);
42
+ return NextResponse.json({ error: 'Failed to fetch settings', detail: error.message }, { status: 500 });
43
+ }
44
+ }
45
+ export async function POST_SETTINGS(req, config) {
46
+ try {
47
+ const userId = await config.getUserId?.(req);
48
+ if (!userId) {
49
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
50
+ }
51
+ const body = await req.json();
52
+ const dbConnection = await config.getDb();
53
+ const db = dbConnection.db();
54
+ const newsletters = db.collection('newsletters');
55
+ await newsletters.updateOne({ id: 'welcome_automation' }, {
56
+ $set: {
57
+ id: 'welcome_automation',
58
+ languages: body.languages || {},
59
+ updatedAt: new Date(),
60
+ },
61
+ }, { upsert: true });
62
+ return NextResponse.json({ success: true, message: 'Settings updated successfully' });
63
+ }
64
+ catch (error) {
65
+ console.error('[NewsletterAPI] POST_SETTINGS error:', error);
66
+ return NextResponse.json({ error: 'Failed to update settings', detail: error.message }, { status: 500 });
67
+ }
68
+ }
69
+ export async function GET_SMTP_SETTINGS(req, config) {
70
+ try {
71
+ const userId = await config.getUserId?.(req);
72
+ if (!userId) {
73
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
74
+ }
75
+ const dbConnection = await config.getDb();
76
+ const db = dbConnection.db();
77
+ const settings = db.collection('settings');
78
+ const smtpConfig = await settings.findOne({ key: 'smtp' });
79
+ if (!smtpConfig) {
80
+ return NextResponse.json({
81
+ host: '',
82
+ port: 465,
83
+ user: '',
84
+ password: '',
85
+ from: '',
86
+ fromName: '',
87
+ primaryLanguage: 'en',
88
+ logoUrl: '',
89
+ });
90
+ }
91
+ return NextResponse.json({
92
+ host: smtpConfig.host || '',
93
+ port: smtpConfig.port || 465,
94
+ user: smtpConfig.user || '',
95
+ password: smtpConfig.password || '',
96
+ from: smtpConfig.from || '',
97
+ fromName: smtpConfig.fromName || '',
98
+ primaryLanguage: smtpConfig.primaryLanguage || 'en',
99
+ logoUrl: smtpConfig.logoUrl || '',
100
+ });
101
+ }
102
+ catch (error) {
103
+ console.error('[NewsletterAPI] GET_SMTP_SETTINGS error:', error);
104
+ return NextResponse.json({ error: 'Failed to fetch SMTP settings', detail: error.message }, { status: 500 });
105
+ }
106
+ }
107
+ export async function POST_SMTP_SETTINGS(req, config) {
108
+ try {
109
+ const userId = await config.getUserId?.(req);
110
+ if (!userId) {
111
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
112
+ }
113
+ const body = await req.json();
114
+ if (!body.host || !body.user || !body.from) {
115
+ return NextResponse.json({ error: 'Host, user, and from address are required' }, { status: 400 });
116
+ }
117
+ const dbConnection = await config.getDb();
118
+ const db = dbConnection.db();
119
+ const settings = db.collection('settings');
120
+ await settings.updateOne({ key: 'smtp' }, {
121
+ $set: {
122
+ key: 'smtp',
123
+ host: body.host,
124
+ port: body.port || 465,
125
+ user: body.user,
126
+ password: body.password,
127
+ from: body.from,
128
+ fromName: body.fromName || '',
129
+ primaryLanguage: body.primaryLanguage || 'en',
130
+ logoUrl: body.logoUrl || '',
131
+ updatedAt: new Date(),
132
+ updatedBy: userId,
133
+ },
134
+ }, { upsert: true });
135
+ return NextResponse.json({ success: true, message: 'SMTP settings saved successfully' });
136
+ }
137
+ catch (error) {
138
+ console.error('[NewsletterAPI] POST_SMTP_SETTINGS error:', error);
139
+ return NextResponse.json({ error: 'Failed to save SMTP settings', detail: error.message }, { status: 500 });
140
+ }
141
+ }
142
+ function getSmtpConfigFromDb(config) {
143
+ return (async () => {
144
+ const dbConnection = await config.getDb();
145
+ const db = dbConnection.db();
146
+ const settings = db.collection('settings');
147
+ const smtpConfig = await settings.findOne({ key: 'smtp' });
148
+ if (smtpConfig && smtpConfig.host) {
149
+ return {
150
+ host: smtpConfig.host,
151
+ port: smtpConfig.port || 465,
152
+ user: smtpConfig.user,
153
+ password: smtpConfig.password,
154
+ from: smtpConfig.from,
155
+ fromName: smtpConfig.fromName || '',
156
+ logoUrl: smtpConfig.logoUrl || '',
157
+ };
158
+ }
159
+ return null;
160
+ })();
161
+ }
162
+ export async function POST_TEST_EMAIL(req, config) {
163
+ try {
164
+ const userId = await config.getUserId?.(req);
165
+ if (!userId) {
166
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
167
+ }
168
+ const body = await req.json();
169
+ const { email, language = 'en', emailType = 'test' } = body;
170
+ if (!email) {
171
+ return NextResponse.json({ error: 'Email address is required' }, { status: 400 });
172
+ }
173
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
174
+ if (!emailRegex.test(email)) {
175
+ return NextResponse.json({ error: 'Invalid email address' }, { status: 400 });
176
+ }
177
+ let smtpConfig = await getSmtpConfigFromDb(config);
178
+ if (!smtpConfig || !smtpConfig.host || !smtpConfig.user || !smtpConfig.from) {
179
+ return NextResponse.json({ error: 'SMTP not configured. Please configure SMTP settings first.' }, { status: 400 });
180
+ }
181
+ const transporter = nodemailer.createTransport({
182
+ host: smtpConfig.host,
183
+ port: smtpConfig.port,
184
+ secure: smtpConfig.port === 465,
185
+ auth: {
186
+ user: smtpConfig.user,
187
+ pass: smtpConfig.password,
188
+ },
189
+ connectionTimeout: 10000,
190
+ });
191
+ const logoPath = path.join(process.cwd(), 'node_modules', '@jhits', 'plugin-newsletter', 'templates', 'logo.png');
192
+ let logoExists = fs.existsSync(logoPath);
193
+ if (!logoExists) {
194
+ const altPath = path.join(__dirname, '..', '..', '..', '..', 'templates', 'logo.png');
195
+ logoExists = fs.existsSync(altPath);
196
+ }
197
+ const baseUrl = config.baseUrl || 'http://localhost:3001';
198
+ let logoAttachment = undefined;
199
+ let logoSrc = smtpConfig.logoUrl || `${baseUrl}/logo_black.svg`;
200
+ if (smtpConfig.logoUrl && smtpConfig.logoUrl.startsWith('data:')) {
201
+ const matches = smtpConfig.logoUrl.match(/^data:([^;]+);base64,(.+)$/);
202
+ if (matches) {
203
+ const mimeType = matches[1];
204
+ const base64Data = matches[2];
205
+ const ext = mimeType === 'image/png' ? 'png' : 'jpg';
206
+ logoAttachment = {
207
+ filename: `logo.${ext}`,
208
+ content: Buffer.from(base64Data, 'base64'),
209
+ cid: 'logo',
210
+ contentType: mimeType,
211
+ };
212
+ logoSrc = 'cid:logo';
213
+ }
214
+ }
215
+ let html;
216
+ let subject;
217
+ if (emailType === 'welcome') {
218
+ const dbConnection = await config.getDb();
219
+ const db = dbConnection.db();
220
+ const newsletters = db.collection('newsletters');
221
+ const welcomeEmail = await newsletters.findOne({
222
+ type: 'welcome_email',
223
+ id: 'welcome_automation'
224
+ });
225
+ const languages = welcomeEmail?.languages;
226
+ const langContent = languages?.[language];
227
+ const defaultContent = languages?.en || { blocks: [], metadata: { subject: '' } };
228
+ const content = langContent || defaultContent;
229
+ const blocks = content.blocks;
230
+ const metadata = content.metadata;
231
+ if (!blocks || blocks.length === 0) {
232
+ return NextResponse.json({ error: 'Welcome email not configured for this language. Please configure it first.' }, { status: 400 });
233
+ }
234
+ const { generateNewsletterEmailHtml } = await import('../../lib/email/EmailRenderer');
235
+ const isDutch = language === 'nl';
236
+ const isSwedish = language === 'sv';
237
+ const slugs = {
238
+ sv: '/avmälla',
239
+ nl: '/afmelden',
240
+ en: '/unsubscribe',
241
+ };
242
+ const slug = slugs[language] || slugs.en;
243
+ const unsubscribeUrl = `${baseUrl}${slug}?email=${encodeURIComponent(email)}`;
244
+ html = generateNewsletterEmailHtml(blocks, { subject: metadata?.subject || '' }, {
245
+ baseUrl,
246
+ locale: language,
247
+ logoUrl: logoSrc,
248
+ unsubscribeUrl,
249
+ footerText: `© ${new Date().getFullYear()} ${smtpConfig.fromName || 'Botanics & You'}`,
250
+ });
251
+ subject = metadata?.subject || (isDutch ? 'Welkom!' : isSwedish ? 'Välkommen!' : 'Welcome!');
252
+ }
253
+ else {
254
+ const t = getTestEmailTranslations(language);
255
+ const templatePath = path.join(process.cwd(), 'node_modules', '@jhits', 'plugin-newsletter', 'templates', 'test-email.hbs');
256
+ let templateContent;
257
+ if (fs.existsSync(templatePath)) {
258
+ templateContent = fs.readFileSync(templatePath, 'utf-8');
259
+ }
260
+ else {
261
+ const altPath = path.join(__dirname, '..', '..', '..', '..', 'templates', 'test-email.hbs');
262
+ if (fs.existsSync(altPath)) {
263
+ templateContent = fs.readFileSync(altPath, 'utf-8');
264
+ }
265
+ else {
266
+ return NextResponse.json({ error: 'Email template not found' }, { status: 500 });
267
+ }
268
+ }
269
+ const template = handlebars.compile(templateContent);
270
+ html = template({
271
+ recipientEmail: email,
272
+ fromName: smtpConfig.fromName || 'JHITS Newsletter',
273
+ currentYear: new Date().getFullYear(),
274
+ ...t,
275
+ });
276
+ subject = language === 'nl' ? 'Test e-mail - SMTP configuratie' : language === 'sv' ? 'Test e-post - SMTP-konfiguration' : 'Test Email - SMTP Configuration';
277
+ }
278
+ await transporter.sendMail({
279
+ from: smtpConfig.fromName
280
+ ? `"${smtpConfig.fromName}" <${smtpConfig.from}>`
281
+ : smtpConfig.from,
282
+ to: email,
283
+ subject,
284
+ html,
285
+ ...(emailType === 'test' && logoExists && {
286
+ attachments: [
287
+ {
288
+ filename: 'logo.png',
289
+ path: logoPath,
290
+ cid: 'logo',
291
+ },
292
+ ],
293
+ }),
294
+ ...(emailType === 'welcome' && logoAttachment && {
295
+ attachments: [logoAttachment],
296
+ }),
297
+ });
298
+ return NextResponse.json({ success: true, message: `Test email sent to ${email}` });
299
+ }
300
+ catch (error) {
301
+ console.error('[NewsletterAPI] POST_TEST_EMAIL error:', error);
302
+ return NextResponse.json({ error: 'Failed to send test email', detail: error.message }, { status: 500 });
303
+ }
304
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Subscribers API Handler
3
+ */
4
+ import { NextRequest, NextResponse } from 'next/server';
5
+ import { NewsletterApiConfig } from '../../types/newsletter';
6
+ export declare function GET_SUBSCRIBERS(req: NextRequest, config: NewsletterApiConfig): Promise<NextResponse>;
7
+ export declare function POST_SUBSCRIBE(req: NextRequest, config: NewsletterApiConfig): Promise<NextResponse>;
8
+ export declare function GET_SUBSCRIBER(req: NextRequest, email: string, config: NewsletterApiConfig): Promise<NextResponse>;
9
+ export declare function DELETE_SUBSCRIBER(req: NextRequest, email: string, config: NewsletterApiConfig): Promise<NextResponse>;
10
+ //# sourceMappingURL=subscribers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscribers.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/subscribers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAG7D,wBAAsB,eAAe,CACjC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAwBvB;AAED,wBAAsB,cAAc,CAChC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA2CvB;AAED,wBAAsB,cAAc,CAChC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA2BvB;AAED,wBAAsB,iBAAiB,CACnC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA2BvB"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Subscribers API Handler
3
+ */
4
+ 'use server';
5
+ import { NextResponse } from 'next/server';
6
+ import { sendWelcomeEmail } from '../email-utils';
7
+ export async function GET_SUBSCRIBERS(req, config) {
8
+ try {
9
+ const userId = await config.getUserId?.(req);
10
+ if (!userId) {
11
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
12
+ }
13
+ const dbConnection = await config.getDb();
14
+ const db = dbConnection.db();
15
+ const subscribers = db.collection('subscribers');
16
+ const subscriberList = await subscribers
17
+ .find({})
18
+ .sort({ subscribedAt: -1 })
19
+ .toArray();
20
+ return NextResponse.json(subscriberList);
21
+ }
22
+ catch (error) {
23
+ console.error('[NewsletterAPI] GET_SUBSCRIBERS error:', error);
24
+ return NextResponse.json({ error: 'Failed to fetch subscribers', detail: error.message }, { status: 500 });
25
+ }
26
+ }
27
+ export async function POST_SUBSCRIBE(req, config) {
28
+ try {
29
+ const body = await req.json();
30
+ const { email, language } = body;
31
+ if (!email || !email.includes('@')) {
32
+ return NextResponse.json({ error: 'Invalid email address' }, { status: 400 });
33
+ }
34
+ const dbConnection = await config.getDb();
35
+ const db = dbConnection.db();
36
+ const subscribers = db.collection('subscribers');
37
+ const existing = await subscribers.findOne({ email: email.toLowerCase() });
38
+ if (existing) {
39
+ return NextResponse.json({ error: 'You are already subscribed!' }, { status: 409 });
40
+ }
41
+ await subscribers.insertOne({
42
+ email: email.toLowerCase(),
43
+ language: language || 'en',
44
+ subscribedAt: new Date(),
45
+ status: 'active',
46
+ });
47
+ const headers = await req.headers;
48
+ const host = headers.get('host') || undefined;
49
+ await sendWelcomeEmail(config, email, language || 'en', host);
50
+ return NextResponse.json({ message: 'Successfully subscribed' }, { status: 201 });
51
+ }
52
+ catch (error) {
53
+ console.error('[NewsletterAPI] POST_SUBSCRIBE error:', error);
54
+ return NextResponse.json({ error: 'Failed to subscribe', detail: error.message }, { status: 500 });
55
+ }
56
+ }
57
+ export async function GET_SUBSCRIBER(req, email, config) {
58
+ try {
59
+ const userId = await config.getUserId?.(req);
60
+ if (!userId) {
61
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
62
+ }
63
+ const dbConnection = await config.getDb();
64
+ const db = dbConnection.db();
65
+ const subscribers = db.collection('subscribers');
66
+ const subscriber = await subscribers.findOne({ email: email.toLowerCase() });
67
+ if (!subscriber) {
68
+ return NextResponse.json({ error: 'Subscriber not found' }, { status: 404 });
69
+ }
70
+ return NextResponse.json(subscriber);
71
+ }
72
+ catch (error) {
73
+ console.error('[NewsletterAPI] GET_SUBSCRIBER error:', error);
74
+ return NextResponse.json({ error: 'Failed to fetch subscriber', detail: error.message }, { status: 500 });
75
+ }
76
+ }
77
+ export async function DELETE_SUBSCRIBER(req, email, config) {
78
+ try {
79
+ const userId = await config.getUserId?.(req);
80
+ if (!userId) {
81
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
82
+ }
83
+ const dbConnection = await config.getDb();
84
+ const db = dbConnection.db();
85
+ const subscribers = db.collection('subscribers');
86
+ const result = await subscribers.deleteOne({ email: email.toLowerCase() });
87
+ if (result.deletedCount === 0) {
88
+ return NextResponse.json({ error: 'Subscriber not found' }, { status: 404 });
89
+ }
90
+ return NextResponse.json({ message: 'Subscriber successfully removed' });
91
+ }
92
+ catch (error) {
93
+ console.error('[NewsletterAPI] DELETE_SUBSCRIBER error:', error);
94
+ return NextResponse.json({ error: 'Failed to delete subscriber', detail: error.message }, { status: 500 });
95
+ }
96
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Logo Upload Handler
3
+ */
4
+ import { NextRequest, NextResponse } from 'next/server';
5
+ import { NewsletterApiConfig } from '../../types/newsletter';
6
+ export declare function POST_LOGO_UPLOAD(req: NextRequest, config: NewsletterApiConfig): Promise<NextResponse>;
7
+ //# sourceMappingURL=upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/upload.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,wBAAsB,gBAAgB,CAClC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAkCvB"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Logo Upload Handler
3
+ */
4
+ 'use server';
5
+ import { NextResponse } from 'next/server';
6
+ export async function POST_LOGO_UPLOAD(req, config) {
7
+ try {
8
+ const userId = await config.getUserId?.(req);
9
+ if (!userId) {
10
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
11
+ }
12
+ const formData = await req.formData();
13
+ const file = formData.get('file');
14
+ if (!file) {
15
+ return NextResponse.json({ error: 'No file provided' }, { status: 400 });
16
+ }
17
+ const allowedTypes = ['image/png', 'image/jpeg'];
18
+ if (!allowedTypes.includes(file.type)) {
19
+ return NextResponse.json({ error: 'Invalid file type. Only PNG and JPEG are allowed.' }, { status: 400 });
20
+ }
21
+ const bytes = await file.arrayBuffer();
22
+ const buffer = Buffer.from(bytes);
23
+ const base64 = buffer.toString('base64');
24
+ const mimeType = file.type;
25
+ const dataUrl = `data:${mimeType};base64,${base64}`;
26
+ return NextResponse.json({ url: dataUrl, success: true });
27
+ }
28
+ catch (error) {
29
+ console.error('[NewsletterAPI] POST_LOGO_UPLOAD error:', error);
30
+ return NextResponse.json({ error: 'Failed to upload logo', detail: error.message }, { status: 500 });
31
+ }
32
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Welcome Email API Handler
3
+ */
4
+ import { NextRequest, NextResponse } from 'next/server';
5
+ import { NewsletterApiConfig, NewsletterMetadata } from '../../types/newsletter';
6
+ export declare function GET_WELCOME_EMAIL_STATUS(req: NextRequest, config: NewsletterApiConfig): Promise<NextResponse>;
7
+ export declare function GET_WELCOME_EMAIL(req: NextRequest, config: NewsletterApiConfig): Promise<NextResponse>;
8
+ export declare function POST_WELCOME_EMAIL(req: NextRequest, config: NewsletterApiConfig): Promise<NextResponse>;
9
+ export declare function GET_WELCOME_EMAIL_CONTENT(req: NextRequest, config: NewsletterApiConfig, language: string): Promise<{
10
+ blocks: any[];
11
+ metadata: NewsletterMetadata;
12
+ }>;
13
+ //# sourceMappingURL=welcome-email.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"welcome-email.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/welcome-email.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAajF,wBAAsB,wBAAwB,CAC1C,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAsCvB;AAED,wBAAsB,iBAAiB,CACnC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA6DvB;AAED,wBAAsB,kBAAkB,CACpC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAyDvB;AAED,wBAAsB,yBAAyB,CAC3C,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAAC,QAAQ,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAc1D"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Welcome Email API Handler
3
+ */
4
+ 'use server';
5
+ import { NextResponse } from 'next/server';
6
+ export async function GET_WELCOME_EMAIL_STATUS(req, config) {
7
+ try {
8
+ const userId = await config.getUserId?.(req);
9
+ if (!userId) {
10
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
11
+ }
12
+ const dbConnection = await config.getDb();
13
+ const db = dbConnection.db();
14
+ const newsletters = db.collection('newsletters');
15
+ const settings = db.collection('settings');
16
+ const welcomeEmail = await newsletters.findOne({
17
+ type: 'welcome_email',
18
+ id: 'welcome_automation'
19
+ });
20
+ const smtpConfig = await settings.findOne({ key: 'smtp' });
21
+ const primaryLanguage = smtpConfig?.primaryLanguage || 'en';
22
+ const languages = welcomeEmail?.languages || {};
23
+ const availableLanguages = Object.keys(languages).filter(lang => languages[lang]?.blocks?.length);
24
+ return NextResponse.json({
25
+ configured: availableLanguages.length > 0,
26
+ lastUpdated: welcomeEmail?.updatedAt || null,
27
+ availableLanguages,
28
+ primaryLanguage,
29
+ });
30
+ }
31
+ catch (error) {
32
+ console.error('[NewsletterAPI] GET_WELCOME_EMAIL_STATUS error:', error);
33
+ return NextResponse.json({ error: 'Failed to fetch welcome email status', detail: error.message }, { status: 500 });
34
+ }
35
+ }
36
+ export async function GET_WELCOME_EMAIL(req, config) {
37
+ try {
38
+ const userId = await config.getUserId?.(req);
39
+ if (!userId) {
40
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
41
+ }
42
+ const { searchParams } = new URL(req.url);
43
+ const language = searchParams.get('language') || 'en';
44
+ const dbConnection = await config.getDb();
45
+ const db = dbConnection.db();
46
+ const newsletters = db.collection('newsletters');
47
+ const settings = db.collection('settings');
48
+ const welcomeEmail = await newsletters.findOne({
49
+ type: 'welcome_email',
50
+ id: 'welcome_automation'
51
+ });
52
+ const smtpConfig = await settings.findOne({ key: 'smtp' });
53
+ const primaryLanguage = smtpConfig?.primaryLanguage || 'en';
54
+ const languages = welcomeEmail?.languages || {};
55
+ // Get primary language content as fallback
56
+ const primaryContent = languages[primaryLanguage] || languages.en || { blocks: [], metadata: { subject: '', previewText: '' } };
57
+ // If requested language doesn't exist, copy from primary language
58
+ if (!languages[language]) {
59
+ return NextResponse.json({
60
+ id: 'welcome_automation',
61
+ type: 'welcome_email',
62
+ title: 'Welcome Email',
63
+ language,
64
+ blocks: primaryContent.blocks,
65
+ metadata: primaryContent.metadata,
66
+ languages,
67
+ isCopy: true,
68
+ copyFrom: primaryLanguage,
69
+ });
70
+ }
71
+ return NextResponse.json({
72
+ id: 'welcome_automation',
73
+ type: 'welcome_email',
74
+ title: 'Welcome Email',
75
+ language,
76
+ blocks: languages[language]?.blocks || primaryContent.blocks,
77
+ metadata: languages[language]?.metadata || primaryContent.metadata,
78
+ languages,
79
+ isCopy: false,
80
+ });
81
+ }
82
+ catch (error) {
83
+ console.error('[NewsletterAPI] GET_WELCOME_EMAIL error:', error);
84
+ return NextResponse.json({ error: 'Failed to fetch welcome email', detail: error.message }, { status: 500 });
85
+ }
86
+ }
87
+ export async function POST_WELCOME_EMAIL(req, config) {
88
+ try {
89
+ const userId = await config.getUserId?.(req);
90
+ if (!userId) {
91
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
92
+ }
93
+ // Get language from query params first (for welcome email saves)
94
+ const { searchParams } = new URL(req.url);
95
+ const queryLanguage = searchParams.get('language');
96
+ const body = await req.json();
97
+ const { blocks, metadata } = body;
98
+ // Use query param language, fallback to body language, then 'en'
99
+ const language = queryLanguage || body.language || 'en';
100
+ const dbConnection = await config.getDb();
101
+ const db = dbConnection.db();
102
+ const newsletters = db.collection('newsletters');
103
+ const existing = await newsletters.findOne({
104
+ type: 'welcome_email',
105
+ id: 'welcome_automation'
106
+ });
107
+ const existingLanguages = existing?.languages || {};
108
+ const updatedLanguages = {
109
+ ...existingLanguages,
110
+ [language]: {
111
+ blocks: blocks || [],
112
+ metadata: metadata || { subject: '', previewText: '' },
113
+ },
114
+ };
115
+ await newsletters.updateOne({ type: 'welcome_email', id: 'welcome_automation' }, {
116
+ $set: {
117
+ type: 'welcome_email',
118
+ id: 'welcome_automation',
119
+ title: 'Welcome Email',
120
+ languages: updatedLanguages,
121
+ updatedAt: new Date().toISOString(),
122
+ }
123
+ }, { upsert: true });
124
+ return NextResponse.json({ success: true, message: 'Welcome email saved successfully' });
125
+ }
126
+ catch (error) {
127
+ console.error('[NewsletterAPI] POST_WELCOME_EMAIL error:', error);
128
+ return NextResponse.json({ error: 'Failed to save welcome email', detail: error.message }, { status: 500 });
129
+ }
130
+ }
131
+ export async function GET_WELCOME_EMAIL_CONTENT(req, config, language) {
132
+ const dbConnection = await config.getDb();
133
+ const db = dbConnection.db();
134
+ const newsletters = db.collection('newsletters');
135
+ const welcomeEmail = await newsletters.findOne({
136
+ type: 'welcome_email',
137
+ id: 'welcome_automation'
138
+ });
139
+ const languages = welcomeEmail?.languages || {};
140
+ const defaultContent = languages.en || { blocks: [], metadata: { subject: '', previewText: '' } };
141
+ return languages[language] || defaultContent;
142
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAe1D;;GAEG;AACH,wBAAsB,mBAAmB,CACrC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA+EvB"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAwB1D;;GAEG;AACH,wBAAsB,mBAAmB,CACrC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA6HvB"}