strapi-plugin-magic-mail 2.2.4 → 2.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +0 -2
  2. package/dist/server/index.js +1 -1
  3. package/dist/server/index.mjs +1 -1
  4. package/package.json +1 -3
  5. package/admin/jsconfig.json +0 -10
  6. package/admin/src/components/AddAccountModal.jsx +0 -1943
  7. package/admin/src/components/Initializer.jsx +0 -14
  8. package/admin/src/components/LicenseGuard.jsx +0 -475
  9. package/admin/src/components/PluginIcon.jsx +0 -5
  10. package/admin/src/hooks/useAuthRefresh.js +0 -44
  11. package/admin/src/hooks/useLicense.js +0 -158
  12. package/admin/src/index.js +0 -87
  13. package/admin/src/pages/Analytics.jsx +0 -762
  14. package/admin/src/pages/App.jsx +0 -111
  15. package/admin/src/pages/EmailDesigner/EditorPage.jsx +0 -1424
  16. package/admin/src/pages/EmailDesigner/TemplateList.jsx +0 -1807
  17. package/admin/src/pages/HomePage.jsx +0 -1170
  18. package/admin/src/pages/LicensePage.jsx +0 -430
  19. package/admin/src/pages/RoutingRules.jsx +0 -1141
  20. package/admin/src/pages/Settings.jsx +0 -603
  21. package/admin/src/pluginId.js +0 -3
  22. package/admin/src/translations/de.json +0 -71
  23. package/admin/src/translations/en.json +0 -70
  24. package/admin/src/translations/es.json +0 -71
  25. package/admin/src/translations/fr.json +0 -71
  26. package/admin/src/translations/pt.json +0 -71
  27. package/admin/src/utils/fetchWithRetry.js +0 -123
  28. package/admin/src/utils/getTranslation.js +0 -5
  29. package/admin/src/utils/theme.js +0 -85
  30. package/server/jsconfig.json +0 -10
  31. package/server/src/bootstrap.js +0 -157
  32. package/server/src/config/features.js +0 -260
  33. package/server/src/config/index.js +0 -9
  34. package/server/src/content-types/email-account/schema.json +0 -93
  35. package/server/src/content-types/email-event/index.js +0 -8
  36. package/server/src/content-types/email-event/schema.json +0 -57
  37. package/server/src/content-types/email-link/index.js +0 -8
  38. package/server/src/content-types/email-link/schema.json +0 -49
  39. package/server/src/content-types/email-log/index.js +0 -8
  40. package/server/src/content-types/email-log/schema.json +0 -106
  41. package/server/src/content-types/email-template/schema.json +0 -74
  42. package/server/src/content-types/email-template-version/schema.json +0 -60
  43. package/server/src/content-types/index.js +0 -33
  44. package/server/src/content-types/routing-rule/schema.json +0 -59
  45. package/server/src/controllers/accounts.js +0 -229
  46. package/server/src/controllers/analytics.js +0 -361
  47. package/server/src/controllers/controller.js +0 -26
  48. package/server/src/controllers/email-designer.js +0 -474
  49. package/server/src/controllers/index.js +0 -21
  50. package/server/src/controllers/license.js +0 -269
  51. package/server/src/controllers/oauth.js +0 -474
  52. package/server/src/controllers/routing-rules.js +0 -129
  53. package/server/src/controllers/test.js +0 -301
  54. package/server/src/destroy.js +0 -27
  55. package/server/src/index.js +0 -25
  56. package/server/src/middlewares/index.js +0 -3
  57. package/server/src/policies/index.js +0 -3
  58. package/server/src/register.js +0 -5
  59. package/server/src/routes/admin.js +0 -469
  60. package/server/src/routes/content-api.js +0 -37
  61. package/server/src/routes/index.js +0 -9
  62. package/server/src/services/account-manager.js +0 -329
  63. package/server/src/services/analytics.js +0 -512
  64. package/server/src/services/email-designer.js +0 -717
  65. package/server/src/services/email-router.js +0 -1446
  66. package/server/src/services/index.js +0 -17
  67. package/server/src/services/license-guard.js +0 -423
  68. package/server/src/services/oauth.js +0 -515
  69. package/server/src/services/service.js +0 -7
  70. package/server/src/utils/encryption.js +0 -81
  71. package/server/src/utils/logger.js +0 -84
@@ -1,474 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * OAuth Controller
5
- * Handles OAuth authentication flows
6
- */
7
-
8
- module.exports = {
9
- /**
10
- * Initiate Gmail OAuth flow
11
- */
12
- async gmailAuth(ctx) {
13
- try {
14
- const { clientId } = ctx.query;
15
-
16
- if (!clientId) {
17
- return ctx.badRequest('Client ID is required');
18
- }
19
-
20
- const oauthService = strapi.plugin('magic-mail').service('oauth');
21
- const state = Buffer.from(JSON.stringify({
22
- timestamp: Date.now(),
23
- clientId,
24
- })).toString('base64');
25
-
26
- const authUrl = oauthService.getGmailAuthUrl(clientId, state);
27
-
28
- ctx.body = {
29
- authUrl,
30
- message: 'Redirect user to this URL to authorize',
31
- };
32
- } catch (err) {
33
- strapi.log.error('[magic-mail] Gmail OAuth init error:', err);
34
- ctx.throw(500, err.message);
35
- }
36
- },
37
-
38
- /**
39
- * Handle Gmail OAuth callback
40
- */
41
- async gmailCallback(ctx) {
42
- try {
43
- const { code, state, error } = ctx.query;
44
-
45
- if (error) {
46
- // OAuth was denied or failed
47
- ctx.type = 'html';
48
- ctx.body = `
49
- <!DOCTYPE html>
50
- <html>
51
- <head>
52
- <title>OAuth Failed</title>
53
- <style>
54
- body { font-family: system-ui; text-align: center; padding: 50px; }
55
- .error { color: #ef4444; font-size: 24px; margin: 20px 0; }
56
- </style>
57
- </head>
58
- <body>
59
- <div class="error">[ERROR] OAuth Authorization Failed</div>
60
- <p>Error: ${error}</p>
61
- <p>You can close this window and try again.</p>
62
- <script>
63
- setTimeout(() => window.close(), 3000);
64
- </script>
65
- </body>
66
- </html>
67
- `;
68
- return;
69
- }
70
-
71
- if (!code) {
72
- return ctx.badRequest('No authorization code received');
73
- }
74
-
75
- // Success - send code to parent window and close popup
76
- ctx.type = 'html';
77
- ctx.body = `
78
- <!DOCTYPE html>
79
- <html>
80
- <head>
81
- <title>OAuth Success</title>
82
- <style>
83
- body {
84
- font-family: system-ui;
85
- text-align: center;
86
- padding: 50px;
87
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
88
- color: white;
89
- }
90
- .success { font-size: 72px; margin: 20px 0; }
91
- .message { font-size: 24px; font-weight: 600; }
92
- .note { font-size: 14px; opacity: 0.9; margin-top: 20px; }
93
- </style>
94
- </head>
95
- <body>
96
- <div class="success">[SUCCESS]</div>
97
- <div class="message">Gmail OAuth Authorized!</div>
98
- <div class="note">Closing window...</div>
99
- <script>
100
- if (window.opener) {
101
- // Send data to parent window
102
- window.opener.postMessage({
103
- type: 'gmail-oauth-success',
104
- code: '${code}',
105
- state: '${state}'
106
- }, window.location.origin);
107
-
108
- setTimeout(() => window.close(), 1500);
109
- } else {
110
- // Fallback: redirect to admin panel
111
- setTimeout(() => {
112
- window.location.href = '/admin/plugins/magic-mail?oauth_code=${code}&oauth_state=${state}';
113
- }, 2000);
114
- }
115
- </script>
116
- </body>
117
- </html>
118
- `;
119
- } catch (err) {
120
- strapi.log.error('[magic-mail] Gmail OAuth callback error:', err);
121
- ctx.throw(500, err.message);
122
- }
123
- },
124
-
125
- /**
126
- * Initiate Microsoft OAuth flow
127
- */
128
- async microsoftAuth(ctx) {
129
- try {
130
- const { clientId, tenantId } = ctx.query;
131
-
132
- if (!clientId) {
133
- return ctx.badRequest('Client ID is required');
134
- }
135
-
136
- if (!tenantId) {
137
- return ctx.badRequest('Tenant ID is required');
138
- }
139
-
140
- const oauthService = strapi.plugin('magic-mail').service('oauth');
141
- const state = Buffer.from(JSON.stringify({
142
- timestamp: Date.now(),
143
- clientId,
144
- tenantId,
145
- })).toString('base64');
146
-
147
- const authUrl = oauthService.getMicrosoftAuthUrl(clientId, tenantId, state);
148
-
149
- ctx.body = {
150
- authUrl,
151
- message: 'Redirect user to this URL to authorize',
152
- };
153
- } catch (err) {
154
- strapi.log.error('[magic-mail] Microsoft OAuth init error:', err);
155
- ctx.throw(500, err.message);
156
- }
157
- },
158
-
159
- /**
160
- * Handle Microsoft OAuth callback
161
- */
162
- async microsoftCallback(ctx) {
163
- try {
164
- const { code, state, error } = ctx.query;
165
-
166
- if (error) {
167
- // OAuth was denied or failed
168
- ctx.type = 'html';
169
- ctx.body = `
170
- <!DOCTYPE html>
171
- <html>
172
- <head>
173
- <title>OAuth Failed</title>
174
- <style>
175
- body { font-family: system-ui; text-align: center; padding: 50px; }
176
- .error { color: #ef4444; font-size: 24px; margin: 20px 0; }
177
- </style>
178
- </head>
179
- <body>
180
- <div class="error">[ERROR] OAuth Authorization Failed</div>
181
- <p>Error: ${error}</p>
182
- <p>You can close this window and try again.</p>
183
- <script>
184
- setTimeout(() => window.close(), 3000);
185
- </script>
186
- </body>
187
- </html>
188
- `;
189
- return;
190
- }
191
-
192
- if (!code) {
193
- return ctx.badRequest('No authorization code received');
194
- }
195
-
196
- // Success - send code to parent window and close popup
197
- ctx.type = 'html';
198
- ctx.body = `
199
- <!DOCTYPE html>
200
- <html>
201
- <head>
202
- <title>OAuth Success</title>
203
- <style>
204
- body {
205
- font-family: system-ui;
206
- text-align: center;
207
- padding: 50px;
208
- background: linear-gradient(135deg, #00A4EF 0%, #0078D4 100%);
209
- color: white;
210
- }
211
- .success { font-size: 72px; margin: 20px 0; }
212
- .message { font-size: 24px; font-weight: 600; }
213
- .note { font-size: 14px; opacity: 0.9; margin-top: 20px; }
214
- </style>
215
- </head>
216
- <body>
217
- <div class="success">[SUCCESS]</div>
218
- <div class="message">Microsoft OAuth Authorized!</div>
219
- <div class="note">Closing window...</div>
220
- <script>
221
- if (window.opener) {
222
- // Send data to parent window
223
- window.opener.postMessage({
224
- type: 'microsoft-oauth-success',
225
- code: '${code}',
226
- state: '${state}'
227
- }, window.location.origin);
228
-
229
- setTimeout(() => window.close(), 1500);
230
- } else {
231
- // Fallback: redirect to admin panel
232
- setTimeout(() => {
233
- window.location.href = '/admin/plugins/magic-mail?oauth_code=${code}&oauth_state=${state}';
234
- }, 2000);
235
- }
236
- </script>
237
- </body>
238
- </html>
239
- `;
240
- } catch (err) {
241
- strapi.log.error('[magic-mail] Microsoft OAuth callback error:', err);
242
- ctx.throw(500, err.message);
243
- }
244
- },
245
-
246
- /**
247
- * Initiate Yahoo OAuth flow
248
- */
249
- async yahooAuth(ctx) {
250
- try {
251
- const { clientId } = ctx.query;
252
-
253
- if (!clientId) {
254
- return ctx.badRequest('Client ID is required');
255
- }
256
-
257
- const oauthService = strapi.plugin('magic-mail').service('oauth');
258
- const state = Buffer.from(JSON.stringify({
259
- timestamp: Date.now(),
260
- clientId,
261
- })).toString('base64');
262
-
263
- const authUrl = oauthService.getYahooAuthUrl(clientId, state);
264
-
265
- ctx.body = {
266
- authUrl,
267
- message: 'Redirect user to this URL to authorize',
268
- };
269
- } catch (err) {
270
- strapi.log.error('[magic-mail] Yahoo OAuth init error:', err);
271
- ctx.throw(500, err.message);
272
- }
273
- },
274
-
275
- /**
276
- * Handle Yahoo OAuth callback
277
- */
278
- async yahooCallback(ctx) {
279
- try {
280
- const { code, state, error } = ctx.query;
281
-
282
- if (error) {
283
- // OAuth was denied or failed
284
- ctx.type = 'html';
285
- ctx.body = `
286
- <!DOCTYPE html>
287
- <html>
288
- <head>
289
- <title>OAuth Failed</title>
290
- <style>
291
- body { font-family: system-ui; text-align: center; padding: 50px; }
292
- .error { color: #ef4444; font-size: 24px; margin: 20px 0; }
293
- </style>
294
- </head>
295
- <body>
296
- <div class="error">[ERROR] OAuth Authorization Failed</div>
297
- <p>Error: ${error}</p>
298
- <p>You can close this window and try again.</p>
299
- <script>
300
- setTimeout(() => window.close(), 3000);
301
- </script>
302
- </body>
303
- </html>
304
- `;
305
- return;
306
- }
307
-
308
- if (!code) {
309
- return ctx.badRequest('No authorization code received');
310
- }
311
-
312
- // Success - send code to parent window and close popup
313
- ctx.type = 'html';
314
- ctx.body = `
315
- <!DOCTYPE html>
316
- <html>
317
- <head>
318
- <title>OAuth Success</title>
319
- <style>
320
- body {
321
- font-family: system-ui;
322
- text-align: center;
323
- padding: 50px;
324
- background: linear-gradient(135deg, #6001D2 0%, #410096 100%);
325
- color: white;
326
- }
327
- .success { font-size: 72px; margin: 20px 0; }
328
- .message { font-size: 24px; font-weight: 600; }
329
- .note { font-size: 14px; opacity: 0.9; margin-top: 20px; }
330
- </style>
331
- </head>
332
- <body>
333
- <div class="success">[SUCCESS]</div>
334
- <div class="message">Yahoo Mail OAuth Authorized!</div>
335
- <div class="note">Closing window...</div>
336
- <script>
337
- if (window.opener) {
338
- // Send data to parent window
339
- window.opener.postMessage({
340
- type: 'yahoo-oauth-success',
341
- code: '${code}',
342
- state: '${state}'
343
- }, window.location.origin);
344
-
345
- setTimeout(() => window.close(), 1500);
346
- } else {
347
- // Fallback: redirect to admin panel
348
- setTimeout(() => {
349
- window.location.href = '/admin/plugins/magic-mail?oauth_code=${code}&oauth_state=${state}';
350
- }, 2000);
351
- }
352
- </script>
353
- </body>
354
- </html>
355
- `;
356
- } catch (err) {
357
- strapi.log.error('[magic-mail] Yahoo OAuth callback error:', err);
358
- ctx.throw(500, err.message);
359
- }
360
- },
361
-
362
- /**
363
- * Create account from OAuth tokens
364
- */
365
- async createOAuthAccount(ctx) {
366
- try {
367
- const { provider, code, state, accountDetails } = ctx.request.body;
368
-
369
- strapi.log.info('[magic-mail] Creating OAuth account...');
370
- strapi.log.info('[magic-mail] Provider:', provider);
371
- strapi.log.info('[magic-mail] Account name:', accountDetails?.name);
372
-
373
- if (provider !== 'gmail' && provider !== 'microsoft' && provider !== 'yahoo') {
374
- return ctx.badRequest('Only Gmail, Microsoft and Yahoo OAuth supported');
375
- }
376
-
377
- if (!code) {
378
- return ctx.badRequest('OAuth code is required');
379
- }
380
-
381
- // License check for OAuth provider
382
- const licenseGuard = strapi.plugin('magic-mail').service('license-guard');
383
- const providerKey = `${provider}-oauth`;
384
- const providerAllowed = await licenseGuard.isProviderAllowed(providerKey);
385
-
386
- if (!providerAllowed) {
387
- ctx.throw(403, `OAuth provider "${provider}" requires a Premium license or higher. Please upgrade your license.`);
388
- return;
389
- }
390
-
391
- // Check account limit using Document Service count()
392
- const currentAccounts = await strapi.documents('plugin::magic-mail.email-account').count();
393
- const maxAccounts = await licenseGuard.getMaxAccounts();
394
-
395
- if (maxAccounts !== -1 && currentAccounts >= maxAccounts) {
396
- ctx.throw(403, `Account limit reached (${maxAccounts}). Upgrade your license to add more accounts.`);
397
- return;
398
- }
399
-
400
- // Decode state to get clientId
401
- const stateData = JSON.parse(Buffer.from(state, 'base64').toString());
402
-
403
- if (!accountDetails.config?.clientId || !accountDetails.config?.clientSecret) {
404
- return ctx.badRequest('Client ID and Secret are required');
405
- }
406
-
407
- const oauthService = strapi.plugin('magic-mail').service('oauth');
408
-
409
- // Exchange code for tokens
410
- let tokenData;
411
- if (provider === 'gmail') {
412
- strapi.log.info('[magic-mail] Calling exchangeGoogleCode...');
413
- tokenData = await oauthService.exchangeGoogleCode(
414
- code,
415
- accountDetails.config.clientId,
416
- accountDetails.config.clientSecret
417
- );
418
- } else if (provider === 'microsoft') {
419
- strapi.log.info('[magic-mail] Calling exchangeMicrosoftCode...');
420
-
421
- if (!accountDetails.config.tenantId) {
422
- throw new Error('Tenant ID is required for Microsoft OAuth');
423
- }
424
-
425
- tokenData = await oauthService.exchangeMicrosoftCode(
426
- code,
427
- accountDetails.config.clientId,
428
- accountDetails.config.clientSecret,
429
- accountDetails.config.tenantId
430
- );
431
- } else if (provider === 'yahoo') {
432
- strapi.log.info('[magic-mail] Calling exchangeYahooCode...');
433
- tokenData = await oauthService.exchangeYahooCode(
434
- code,
435
- accountDetails.config.clientId,
436
- accountDetails.config.clientSecret
437
- );
438
- }
439
-
440
- strapi.log.info('[magic-mail] Token data received:', {
441
- email: tokenData.email,
442
- hasAccessToken: !!tokenData.accessToken,
443
- hasRefreshToken: !!tokenData.refreshToken,
444
- });
445
-
446
- if (!tokenData.email) {
447
- strapi.log.error('[magic-mail] No email in tokenData!');
448
- throw new Error(`Failed to get email from ${provider} OAuth`);
449
- }
450
-
451
- // Store account
452
- strapi.log.info('[magic-mail] Calling storeOAuthAccount...');
453
- const account = await oauthService.storeOAuthAccount(
454
- provider,
455
- tokenData,
456
- accountDetails,
457
- accountDetails.config // contains clientId and clientSecret
458
- );
459
-
460
- strapi.log.info('[magic-mail] [SUCCESS] OAuth account created successfully');
461
-
462
- ctx.body = {
463
- success: true,
464
- data: account,
465
- message: 'OAuth account created successfully',
466
- };
467
- } catch (err) {
468
- strapi.log.error('[magic-mail] Create OAuth account error:', err);
469
- strapi.log.error('[magic-mail] Error stack:', err.stack);
470
- ctx.throw(500, err.message);
471
- }
472
- },
473
- };
474
-
@@ -1,129 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Routing Rules Controller
5
- * Manages email routing rules CRUD operations
6
- * [SUCCESS] Migrated to strapi.documents() API (Strapi v5 Best Practice)
7
- */
8
-
9
- const ROUTING_RULE_UID = 'plugin::magic-mail.routing-rule';
10
-
11
- module.exports = {
12
- /**
13
- * Get all routing rules
14
- */
15
- async getAll(ctx) {
16
- try {
17
- const rules = await strapi.documents(ROUTING_RULE_UID).findMany({
18
- sort: [{ priority: 'desc' }],
19
- });
20
-
21
- ctx.body = {
22
- data: rules,
23
- meta: { count: rules.length },
24
- };
25
- } catch (err) {
26
- strapi.log.error('[magic-mail] Error getting routing rules:', err);
27
- ctx.throw(500, 'Error fetching routing rules');
28
- }
29
- },
30
-
31
- /**
32
- * Get single routing rule
33
- */
34
- async getOne(ctx) {
35
- try {
36
- const { ruleId } = ctx.params;
37
- const rule = await strapi.documents(ROUTING_RULE_UID).findOne({
38
- documentId: ruleId,
39
- });
40
-
41
- if (!rule) {
42
- ctx.throw(404, 'Routing rule not found');
43
- }
44
-
45
- ctx.body = {
46
- data: rule,
47
- };
48
- } catch (err) {
49
- strapi.log.error('[magic-mail] Error getting routing rule:', err);
50
- ctx.throw(500, 'Error fetching routing rule');
51
- }
52
- },
53
-
54
- /**
55
- * Create new routing rule
56
- */
57
- async create(ctx) {
58
- try {
59
- const licenseGuard = strapi.plugin('magic-mail').service('license-guard');
60
-
61
- // Check routing rule limit using Document Service count()
62
- const currentRules = await strapi.documents(ROUTING_RULE_UID).count();
63
- const maxRules = await licenseGuard.getMaxRoutingRules();
64
-
65
- if (maxRules !== -1 && currentRules >= maxRules) {
66
- ctx.throw(403, `Routing rule limit reached (${maxRules}). Upgrade to Advanced license for unlimited rules.`);
67
- return;
68
- }
69
-
70
- const rule = await strapi.documents(ROUTING_RULE_UID).create({
71
- data: ctx.request.body,
72
- });
73
-
74
- ctx.body = {
75
- data: rule,
76
- message: 'Routing rule created successfully',
77
- };
78
-
79
- strapi.log.info(`[magic-mail] [SUCCESS] Routing rule created: ${rule.name}`);
80
- } catch (err) {
81
- strapi.log.error('[magic-mail] Error creating routing rule:', err);
82
- ctx.throw(err.status || 500, err.message || 'Error creating routing rule');
83
- }
84
- },
85
-
86
- /**
87
- * Update routing rule
88
- */
89
- async update(ctx) {
90
- try {
91
- const { ruleId } = ctx.params;
92
- const rule = await strapi.documents(ROUTING_RULE_UID).update({
93
- documentId: ruleId,
94
- data: ctx.request.body,
95
- });
96
-
97
- ctx.body = {
98
- data: rule,
99
- message: 'Routing rule updated successfully',
100
- };
101
-
102
- strapi.log.info(`[magic-mail] [SUCCESS] Routing rule updated: ${rule.name}`);
103
- } catch (err) {
104
- strapi.log.error('[magic-mail] Error updating routing rule:', err);
105
- ctx.throw(500, err.message || 'Error updating routing rule');
106
- }
107
- },
108
-
109
- /**
110
- * Delete routing rule
111
- */
112
- async delete(ctx) {
113
- try {
114
- const { ruleId } = ctx.params;
115
- await strapi.documents(ROUTING_RULE_UID).delete({
116
- documentId: ruleId,
117
- });
118
-
119
- ctx.body = {
120
- message: 'Routing rule deleted successfully',
121
- };
122
-
123
- strapi.log.info(`[magic-mail] Routing rule deleted: ${ruleId}`);
124
- } catch (err) {
125
- strapi.log.error('[magic-mail] Error deleting routing rule:', err);
126
- ctx.throw(500, 'Error deleting routing rule');
127
- }
128
- },
129
- };