strapi-plugin-magic-mail 2.0.2 → 2.0.4

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 (37) hide show
  1. package/README.md +15 -17
  2. package/admin/src/index.js +26 -25
  3. package/admin/src/pages/Analytics.jsx +19 -19
  4. package/admin/src/pages/EmailDesigner/EditorPage.jsx +33 -33
  5. package/admin/src/pages/EmailDesigner/TemplateList.jsx +36 -36
  6. package/admin/src/pages/HomePage.jsx +25 -88
  7. package/admin/src/pages/LicensePage.jsx +12 -6
  8. package/admin/src/pages/RoutingRules.jsx +21 -21
  9. package/admin/src/pages/Settings.jsx +3 -3
  10. package/admin/src/utils/theme.js +85 -0
  11. package/dist/_chunks/{App-DYNCyt54.mjs → App-BZaHrE0R.mjs} +184 -200
  12. package/dist/_chunks/{App-DF9vAGXX.js → App-Bze8Ixs_.js} +184 -200
  13. package/dist/_chunks/{LicensePage-CJXwPnEe.js → LicensePage-Bg72gy8w.js} +12 -6
  14. package/dist/_chunks/{LicensePage-Bl02myMx.mjs → LicensePage-ndUhjynY.mjs} +12 -6
  15. package/dist/_chunks/{Settings-zuFQ3pnn.js → Settings-BSFLpt0H.js} +3 -7
  16. package/dist/_chunks/{Settings-C_TmKwcz.mjs → Settings-Ca5UE3c1.mjs} +3 -7
  17. package/dist/admin/index.js +41 -24
  18. package/dist/admin/index.mjs +41 -24
  19. package/dist/server/index.js +602 -665
  20. package/dist/server/index.mjs +602 -665
  21. package/package.json +1 -1
  22. package/server/src/bootstrap.js +16 -16
  23. package/server/src/config/features.js +7 -7
  24. package/server/src/controllers/accounts.js +15 -6
  25. package/server/src/controllers/analytics.js +56 -42
  26. package/server/src/controllers/license.js +9 -7
  27. package/server/src/controllers/oauth.js +9 -9
  28. package/server/src/controllers/routing-rules.js +18 -11
  29. package/server/src/controllers/test.js +111 -193
  30. package/server/src/services/account-manager.js +73 -21
  31. package/server/src/services/analytics.js +88 -72
  32. package/server/src/services/email-designer.js +131 -284
  33. package/server/src/services/email-router.js +69 -43
  34. package/server/src/services/license-guard.js +24 -24
  35. package/server/src/services/oauth.js +11 -11
  36. package/server/src/services/service.js +1 -1
  37. package/server/src/utils/encryption.js +1 -1
@@ -30,12 +30,18 @@ module.exports = ({ strapi }) => ({
30
30
  templateId = null, // Template Reference ID
31
31
  templateData, // Data for template rendering
32
32
  data, // Alias for templateData (for native Strapi compatibility)
33
+ skipLinkTracking = false, // Skip link rewriting for sensitive URLs (e.g., Magic Links)
33
34
  } = emailData;
34
35
 
35
36
  // Support both 'data' and 'templateData' for backward compatibility
36
37
  if (!templateData && data) {
37
38
  templateData = data;
38
39
  }
40
+
41
+ // Debug log for skipLinkTracking
42
+ if (skipLinkTracking) {
43
+ strapi.log.info(`[magic-mail] [SKIP-TRACK] skipLinkTracking=true received for email to: ${to}`);
44
+ }
39
45
 
40
46
  // NEW: If templateId/templateReferenceId provided, render template
41
47
  let renderedTemplate = null;
@@ -53,14 +59,14 @@ module.exports = ({ strapi }) => ({
53
59
  const numericTemplateId = Number(templateId);
54
60
 
55
61
  if (!Number.isNaN(numericTemplateId) && Number.isInteger(numericTemplateId)) {
56
- strapi.log.info(`[magic-mail] 🔍 Looking up template by ID: ${numericTemplateId}`);
62
+ strapi.log.info(`[magic-mail] [CHECK] Looking up template by ID: ${numericTemplateId}`);
57
63
  templateRecord = await strapi
58
64
  .plugin('magic-mail')
59
65
  .service('email-designer')
60
66
  .findOne(numericTemplateId);
61
67
 
62
68
  if (!templateRecord) {
63
- strapi.log.error(`[magic-mail] Template with ID ${numericTemplateId} not found in database`);
69
+ strapi.log.error(`[magic-mail] [ERROR] Template with ID ${numericTemplateId} not found in database`);
64
70
  throw new Error(`Template with ID ${numericTemplateId} not found`);
65
71
  }
66
72
 
@@ -70,7 +76,7 @@ module.exports = ({ strapi }) => ({
70
76
 
71
77
  resolvedTemplateReferenceId = String(templateRecord.templateReferenceId).trim();
72
78
  strapi.log.info(
73
- `[magic-mail] Found template: ID=${templateRecord.id}, referenceId="${resolvedTemplateReferenceId}", name="${templateRecord.name}"`
79
+ `[magic-mail] [SUCCESS] Found template: ID=${templateRecord.id}, referenceId="${resolvedTemplateReferenceId}", name="${templateRecord.name}"`
74
80
  );
75
81
  } else {
76
82
  // templateId was provided but not numeric; treat it directly as reference ID
@@ -96,7 +102,7 @@ module.exports = ({ strapi }) => ({
96
102
  type = type || renderedTemplate.category; // Use template category if not specified
97
103
 
98
104
  strapi.log.info(
99
- `[magic-mail] 📧 Rendered template reference "${resolvedTemplateReferenceId}" (requested ID: ${templateId ?? 'n/a'}): ${renderedTemplate.templateName}`
105
+ `[magic-mail] [EMAIL] Rendered template reference "${resolvedTemplateReferenceId}" (requested ID: ${templateId ?? 'n/a'}): ${renderedTemplate.templateName}`
100
106
  );
101
107
 
102
108
  // Ensure templateId/templateName are populated for logging/analytics
@@ -105,7 +111,7 @@ module.exports = ({ strapi }) => ({
105
111
  emailData.templateName = templateRecord?.name || renderedTemplate.templateName;
106
112
  }
107
113
  } catch (error) {
108
- strapi.log.error(`[magic-mail] Template rendering failed: ${error.message}`);
114
+ strapi.log.error(`[magic-mail] [ERROR] Template rendering failed: ${error.message}`);
109
115
  throw new Error(`Template rendering failed: ${error.message}`);
110
116
  }
111
117
  }
@@ -139,15 +145,20 @@ module.exports = ({ strapi }) => ({
139
145
 
140
146
  recipientHash = analyticsService.generateRecipientHash(emailLog.emailId, to);
141
147
 
142
- // Inject tracking pixel
148
+ // Inject tracking pixel (open tracking)
143
149
  html = analyticsService.injectTrackingPixel(html, emailLog.emailId, recipientHash);
144
150
 
145
- // Rewrite links for click tracking (now async)
146
- html = await analyticsService.rewriteLinksForTracking(html, emailLog.emailId, recipientHash);
147
-
148
- strapi.log.info(`[magic-mail] 📊 Tracking enabled for email: ${emailLog.emailId}`);
151
+ // Rewrite links for click tracking (unless explicitly disabled)
152
+ // skipLinkTracking is useful for sensitive URLs like Magic Links, password resets, etc.
153
+ // where the original URL must remain intact for security/UX reasons
154
+ if (!skipLinkTracking) {
155
+ html = await analyticsService.rewriteLinksForTracking(html, emailLog.emailId, recipientHash);
156
+ strapi.log.info(`[magic-mail] [STATS] Full tracking enabled for email: ${emailLog.emailId}`);
157
+ } else {
158
+ strapi.log.info(`[magic-mail] [STATS] Open tracking enabled, link tracking DISABLED for email: ${emailLog.emailId}`);
159
+ }
149
160
  } catch (error) {
150
- strapi.log.error(`[magic-mail] ⚠️ Tracking setup failed (continuing without tracking):`, error.message);
161
+ strapi.log.error(`[magic-mail] [WARNING] Tracking setup failed (continuing without tracking):`, error.message);
151
162
  // Continue sending email even if tracking fails
152
163
  }
153
164
  }
@@ -165,7 +176,7 @@ module.exports = ({ strapi }) => ({
165
176
  if (priority === 'high') {
166
177
  const hasFeature = await licenseGuard.hasFeature('priority-headers');
167
178
  if (!hasFeature) {
168
- strapi.log.warn('[magic-mail] ⚠️ High priority emails require Advanced license - using normal priority');
179
+ strapi.log.warn('[magic-mail] [WARNING] High priority emails require Advanced license - using normal priority');
169
180
  emailData.priority = 'normal';
170
181
  }
171
182
  }
@@ -203,8 +214,8 @@ module.exports = ({ strapi }) => ({
203
214
  // Update email log with account info (if tracking enabled)
204
215
  if (emailLog) {
205
216
  try {
206
- await strapi.db.query('plugin::magic-mail.email-log').update({
207
- where: { id: emailLog.id },
217
+ await strapi.documents('plugin::magic-mail.email-log').update({
218
+ documentId: emailLog.documentId,
208
219
  data: {
209
220
  accountId: account.id,
210
221
  accountName: account.name,
@@ -217,9 +228,9 @@ module.exports = ({ strapi }) => ({
217
228
  }
218
229
 
219
230
  // Update stats
220
- await this.updateAccountStats(account.id);
231
+ await this.updateAccountStats(account.documentId);
221
232
 
222
- strapi.log.info(`[magic-mail] Email sent to ${to} via ${account.name}`);
233
+ strapi.log.info(`[magic-mail] [SUCCESS] Email sent to ${to} via ${account.name}`);
223
234
 
224
235
  return {
225
236
  success: true,
@@ -227,7 +238,7 @@ module.exports = ({ strapi }) => ({
227
238
  messageId: result.messageId,
228
239
  };
229
240
  } catch (error) {
230
- strapi.log.error('[magic-mail] Email send failed:', error);
241
+ strapi.log.error('[magic-mail] [ERROR] Email send failed:', error);
231
242
 
232
243
  throw error;
233
244
  }
@@ -237,25 +248,25 @@ module.exports = ({ strapi }) => ({
237
248
  * Select best account based on rules
238
249
  */
239
250
  async selectAccount(type, priority, excludeIds = [], emailData = {}) {
240
- // Get all active accounts
241
- const accounts = await strapi.entityService.findMany('plugin::magic-mail.email-account', {
251
+ // Get all active accounts using Document Service
252
+ const accounts = await strapi.documents('plugin::magic-mail.email-account').findMany({
242
253
  filters: {
243
254
  isActive: true,
244
255
  id: { $notIn: excludeIds },
245
256
  },
246
- sort: { priority: 'desc' },
257
+ sort: [{ priority: 'desc' }],
247
258
  });
248
259
 
249
260
  if (!accounts || accounts.length === 0) {
250
261
  return null;
251
262
  }
252
263
 
253
- // Get all active routing rules
254
- const allRules = await strapi.entityService.findMany('plugin::magic-mail.routing-rule', {
264
+ // Get all active routing rules using Document Service
265
+ const allRules = await strapi.documents('plugin::magic-mail.routing-rule').findMany({
255
266
  filters: {
256
267
  isActive: true,
257
268
  },
258
- sort: { priority: 'desc' },
269
+ sort: [{ priority: 'desc' }],
259
270
  });
260
271
 
261
272
  // Check routing rules with different match types
@@ -292,14 +303,14 @@ module.exports = ({ strapi }) => ({
292
303
  if (matches) {
293
304
  const account = accounts.find(a => a.name === rule.accountName);
294
305
  if (account) {
295
- strapi.log.info(`[magic-mail] 🎯 Routing rule matched: ${rule.name} ${account.name}`);
306
+ strapi.log.info(`[magic-mail] [ROUTE] Routing rule matched: ${rule.name} -> ${account.name}`);
296
307
  return account;
297
308
  }
298
309
  // If primary account not found, try fallback
299
310
  if (rule.fallbackAccountName) {
300
311
  const fallbackAccount = accounts.find(a => a.name === rule.fallbackAccountName);
301
312
  if (fallbackAccount) {
302
- strapi.log.info(`[magic-mail] 🔄 Using fallback account: ${fallbackAccount.name}`);
313
+ strapi.log.info(`[magic-mail] [FALLBACK] Using fallback account: ${fallbackAccount.name}`);
303
314
  return fallbackAccount;
304
315
  }
305
316
  }
@@ -426,7 +437,7 @@ module.exports = ({ strapi }) => ({
426
437
  mailOptions.headers['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click';
427
438
  mailOptions.headers['Precedence'] = 'bulk'; // Mark as bulk mail
428
439
  } else {
429
- strapi.log.warn('[magic-mail] ⚠️ Marketing email without unsubscribe URL - may violate GDPR/CAN-SPAM');
440
+ strapi.log.warn('[magic-mail] [WARNING] Marketing email without unsubscribe URL - may violate GDPR/CAN-SPAM');
430
441
  }
431
442
  }
432
443
 
@@ -490,7 +501,7 @@ module.exports = ({ strapi }) => ({
490
501
  );
491
502
 
492
503
  currentAccessToken = newTokens.accessToken;
493
- strapi.log.info('[magic-mail] Token refreshed successfully');
504
+ strapi.log.info('[magic-mail] [SUCCESS] Token refreshed successfully');
494
505
 
495
506
  // Update stored tokens
496
507
  const { encryptCredentials } = require('../utils/encryption');
@@ -500,7 +511,8 @@ module.exports = ({ strapi }) => ({
500
511
  expiresAt: newTokens.expiresAt,
501
512
  });
502
513
 
503
- await strapi.entityService.update('plugin::magic-mail.email-account', account.id, {
514
+ await strapi.documents('plugin::magic-mail.email-account').update({
515
+ documentId: account.documentId,
504
516
  data: { oauth: updatedOAuth },
505
517
  });
506
518
  } catch (refreshErr) {
@@ -661,14 +673,20 @@ module.exports = ({ strapi }) => ({
661
673
  }
662
674
 
663
675
  const result = await response.json();
664
- strapi.log.info('[magic-mail] Email sent via Gmail API');
676
+ strapi.log.info('[magic-mail] [SUCCESS] Email sent via Gmail API');
665
677
 
666
678
  return {
667
679
  messageId: result.id,
668
680
  response: 'OK',
669
681
  };
670
682
  } catch (err) {
671
- strapi.log.error('[magic-mail] Gmail API send failed:', err);
683
+ strapi.log.error('[magic-mail] Gmail API send failed:', err.message || err);
684
+ strapi.log.error('[magic-mail] Error details:', {
685
+ name: err.name,
686
+ code: err.code,
687
+ cause: err.cause?.message || err.cause,
688
+ stack: err.stack?.split('\n').slice(0, 3).join('\n'),
689
+ });
672
690
  throw err;
673
691
  }
674
692
  },
@@ -726,7 +744,7 @@ module.exports = ({ strapi }) => ({
726
744
  );
727
745
 
728
746
  currentAccessToken = newTokens.accessToken;
729
- strapi.log.info('[magic-mail] Microsoft token refreshed successfully');
747
+ strapi.log.info('[magic-mail] [SUCCESS] Microsoft token refreshed successfully');
730
748
 
731
749
  // Update stored tokens
732
750
  const { encryptCredentials } = require('../utils/encryption');
@@ -736,7 +754,8 @@ module.exports = ({ strapi }) => ({
736
754
  expiresAt: newTokens.expiresAt,
737
755
  });
738
756
 
739
- await strapi.entityService.update('plugin::magic-mail.email-account', account.id, {
757
+ await strapi.documents('plugin::magic-mail.email-account').update({
758
+ documentId: account.documentId,
740
759
  data: { oauth: updatedOAuth },
741
760
  });
742
761
  } catch (refreshErr) {
@@ -886,7 +905,7 @@ module.exports = ({ strapi }) => ({
886
905
  throw new Error(`Microsoft Graph API error: ${response.status} - ${response.statusText}`);
887
906
  }
888
907
 
889
- strapi.log.info('[magic-mail] Email sent via Microsoft Graph API with MIME + custom headers');
908
+ strapi.log.info('[magic-mail] [SUCCESS] Email sent via Microsoft Graph API with MIME + custom headers');
890
909
  strapi.log.info('[magic-mail] Microsoft adds From/DKIM automatically for DMARC compliance');
891
910
 
892
911
  return {
@@ -942,7 +961,7 @@ module.exports = ({ strapi }) => ({
942
961
  );
943
962
 
944
963
  currentAccessToken = newTokens.accessToken;
945
- strapi.log.info('[magic-mail] Token refreshed successfully');
964
+ strapi.log.info('[magic-mail] [SUCCESS] Token refreshed successfully');
946
965
 
947
966
  // Update stored tokens
948
967
  const { encryptCredentials } = require('../utils/encryption');
@@ -952,7 +971,8 @@ module.exports = ({ strapi }) => ({
952
971
  expiresAt: newTokens.expiresAt,
953
972
  });
954
973
 
955
- await strapi.entityService.update('plugin::magic-mail.email-account', account.id, {
974
+ await strapi.documents('plugin::magic-mail.email-account').update({
975
+ documentId: account.documentId,
956
976
  data: { oauth: updatedOAuth },
957
977
  });
958
978
  } catch (refreshErr) {
@@ -1010,7 +1030,7 @@ module.exports = ({ strapi }) => ({
1010
1030
  }
1011
1031
 
1012
1032
  const result = await transporter.sendMail(mailOptions);
1013
- strapi.log.info('[magic-mail] Email sent via Yahoo OAuth');
1033
+ strapi.log.info('[magic-mail] [SUCCESS] Email sent via Yahoo OAuth');
1014
1034
 
1015
1035
  return {
1016
1036
  messageId: result.messageId,
@@ -1136,7 +1156,7 @@ module.exports = ({ strapi }) => ({
1136
1156
  throw new Error(`SendGrid API error: ${response.statusText}`);
1137
1157
  }
1138
1158
 
1139
- strapi.log.info('[magic-mail] Email sent via SendGrid API');
1159
+ strapi.log.info('[magic-mail] [SUCCESS] Email sent via SendGrid API');
1140
1160
 
1141
1161
  // SendGrid returns 202 Accepted with no body on success
1142
1162
  return {
@@ -1253,7 +1273,7 @@ module.exports = ({ strapi }) => ({
1253
1273
  }
1254
1274
 
1255
1275
  const result = await response.json();
1256
- strapi.log.info('[magic-mail] Email sent via Mailgun API');
1276
+ strapi.log.info('[magic-mail] [SUCCESS] Email sent via Mailgun API');
1257
1277
 
1258
1278
  return {
1259
1279
  messageId: result.id || `mailgun-${Date.now()}`,
@@ -1280,11 +1300,17 @@ module.exports = ({ strapi }) => ({
1280
1300
 
1281
1301
  /**
1282
1302
  * Update account statistics
1303
+ * Note: This function now expects documentId
1283
1304
  */
1284
- async updateAccountStats(accountId) {
1285
- const account = await strapi.entityService.findOne('plugin::magic-mail.email-account', accountId);
1305
+ async updateAccountStats(documentId) {
1306
+ const account = await strapi.documents('plugin::magic-mail.email-account').findOne({
1307
+ documentId,
1308
+ });
1309
+
1310
+ if (!account) return;
1286
1311
 
1287
- await strapi.entityService.update('plugin::magic-mail.email-account', accountId, {
1312
+ await strapi.documents('plugin::magic-mail.email-account').update({
1313
+ documentId,
1288
1314
  data: {
1289
1315
  emailsSentToday: (account.emailsSentToday || 0) + 1,
1290
1316
  emailsSentThisHour: (account.emailsSentThisHour || 0) + 1,
@@ -1310,7 +1336,7 @@ module.exports = ({ strapi }) => ({
1310
1336
  * Get account by name
1311
1337
  */
1312
1338
  async getAccountByName(name) {
1313
- const accounts = await strapi.entityService.findMany('plugin::magic-mail.email-account', {
1339
+ const accounts = await strapi.documents('plugin::magic-mail.email-account').findMany({
1314
1340
  filters: { name, isActive: true },
1315
1341
  limit: 1,
1316
1342
  });
@@ -1379,7 +1405,7 @@ module.exports = ({ strapi }) => ({
1379
1405
  strapi.log.warn('[magic-mail] Email has HTML but no text alternative - may reduce deliverability');
1380
1406
  }
1381
1407
 
1382
- strapi.log.info('[magic-mail] Email security validation passed');
1408
+ strapi.log.info('[magic-mail] [SUCCESS] Email security validation passed');
1383
1409
  },
1384
1410
 
1385
1411
  /**
@@ -101,14 +101,14 @@ module.exports = ({ strapi }) => ({
101
101
  const data = await response.json();
102
102
 
103
103
  if (data.success) {
104
- strapi.log.info('[magic-mail] License created:', data.data.licenseKey);
104
+ strapi.log.info('[magic-mail] [SUCCESS] License created:', data.data.licenseKey);
105
105
  return data.data;
106
106
  } else {
107
- strapi.log.error('[magic-mail] License creation failed:', data);
107
+ strapi.log.error('[magic-mail] [ERROR] License creation failed:', data);
108
108
  return null;
109
109
  }
110
110
  } catch (error) {
111
- strapi.log.error('[magic-mail] Error creating license:', error);
111
+ strapi.log.error('[magic-mail] [ERROR] Error creating license:', error);
112
112
  return null;
113
113
  }
114
114
  },
@@ -140,10 +140,10 @@ module.exports = ({ strapi }) => ({
140
140
  }
141
141
  } catch (error) {
142
142
  if (allowGracePeriod) {
143
- strapi.log.warn('[magic-mail] ⚠️ License verification timeout - grace period active');
143
+ strapi.log.warn('[magic-mail] [WARNING] License verification timeout - grace period active');
144
144
  return { valid: true, data: null, gracePeriod: true };
145
145
  }
146
- strapi.log.error('[magic-mail] License verification error:', error.message);
146
+ strapi.log.error('[magic-mail] [ERROR] License verification error:', error.message);
147
147
  return { valid: false, data: null };
148
148
  }
149
149
  },
@@ -206,7 +206,7 @@ module.exports = ({ strapi }) => ({
206
206
  name: 'magic-mail'
207
207
  });
208
208
  await pluginStore.set({ key: 'licenseKey', value: licenseKey });
209
- strapi.log.info(`[magic-mail] License key stored: ${licenseKey.substring(0, 8)}...`);
209
+ strapi.log.info(`[magic-mail] [SUCCESS] License key stored: ${licenseKey.substring(0, 8)}...`);
210
210
  },
211
211
 
212
212
  startPinging(licenseKey, intervalMinutes = 15) {
@@ -242,7 +242,7 @@ module.exports = ({ strapi }) => ({
242
242
  const license = await this.getLicenseByKey(licenseKey);
243
243
  return license;
244
244
  } catch (error) {
245
- strapi.log.error(`[magic-mail] Error loading license:`, error);
245
+ strapi.log.error(`[magic-mail] [ERROR] Error loading license:`, error);
246
246
  return null;
247
247
  }
248
248
  },
@@ -298,7 +298,7 @@ module.exports = ({ strapi }) => ({
298
298
  */
299
299
  async initialize() {
300
300
  try {
301
- strapi.log.info('🔐 Initializing License Guard...');
301
+ strapi.log.info('[INIT] Initializing License Guard...');
302
302
 
303
303
  // Check if license key exists in plugin store
304
304
  const pluginStore = strapi.store({
@@ -322,23 +322,23 @@ module.exports = ({ strapi }) => ({
322
322
  strapi.log.info('──────────────────────────────────────────────────────────');
323
323
  strapi.log.info(`📦 Plugin Store Check:`);
324
324
  if (licenseKey) {
325
- strapi.log.info(` License Key found: ${licenseKey}`);
326
- strapi.log.info(` 🔑 Key (short): ${licenseKey.substring(0, 10)}...`);
325
+ strapi.log.info(` [SUCCESS] License Key found: ${licenseKey}`);
326
+ strapi.log.info(` [LICENSE] Key (short): ${licenseKey.substring(0, 10)}...`);
327
327
  if (lastValidated) {
328
328
  const lastValidatedDate = new Date(lastValidated);
329
329
  const hoursAgo = Math.floor((now.getTime() - lastValidatedDate.getTime()) / (1000 * 60 * 60));
330
- strapi.log.info(` 🕐 Last validated: ${hoursAgo}h ago (Grace: ${withinGracePeriod ? 'ACTIVE' : 'EXPIRED'})`);
330
+ strapi.log.info(` [TIME] Last validated: ${hoursAgo}h ago (Grace: ${withinGracePeriod ? 'ACTIVE' : 'EXPIRED'})`);
331
331
  } else {
332
- strapi.log.info(` 🕐 Last validated: Never (Grace: ACTIVE for first ${gracePeriodHours}h)`);
332
+ strapi.log.info(` [TIME] Last validated: Never (Grace: ACTIVE for first ${gracePeriodHours}h)`);
333
333
  }
334
334
  } else {
335
- strapi.log.info(` No license key stored`);
335
+ strapi.log.info(` [ERROR] No license key stored`);
336
336
  }
337
337
  strapi.log.info('──────────────────────────────────────────────────────────');
338
338
 
339
339
  if (!licenseKey) {
340
- strapi.log.info('📄 No license found - Running in demo mode');
341
- strapi.log.info('💡 Create a license in the admin panel to activate full features');
340
+ strapi.log.info('[DEMO] No license found - Running in demo mode');
341
+ strapi.log.info('[INFO] Create a license in the admin panel to activate full features');
342
342
  return {
343
343
  valid: false,
344
344
  demo: true,
@@ -346,7 +346,7 @@ module.exports = ({ strapi }) => ({
346
346
  };
347
347
  }
348
348
 
349
- strapi.log.info('📄 Verifying stored license key...');
349
+ strapi.log.info('[VERIFY] Verifying stored license key...');
350
350
 
351
351
  // Verify license (allow grace period if we have a last validation)
352
352
  const verification = await this.verifyLicense(licenseKey, withinGracePeriod);
@@ -355,7 +355,7 @@ module.exports = ({ strapi }) => ({
355
355
  // Get license details for display
356
356
  const license = await this.getLicenseByKey(licenseKey);
357
357
 
358
- strapi.log.info(`✅ License verified online: ACTIVE (Key: ${licenseKey.substring(0, 10)}...)`);
358
+ strapi.log.info(`[SUCCESS] License verified online: ACTIVE (Key: ${licenseKey.substring(0, 10)}...)`);
359
359
 
360
360
  // Update last validated timestamp
361
361
  await pluginStore.set({
@@ -363,11 +363,11 @@ module.exports = ({ strapi }) => ({
363
363
  value: now.toISOString()
364
364
  });
365
365
 
366
- strapi.log.info(' License is valid and active');
366
+ strapi.log.info('[SUCCESS] License is valid and active');
367
367
 
368
368
  // Start automatic pinging
369
369
  const pingInterval = this.startPinging(licenseKey, 15);
370
- strapi.log.info('📡 Started pinging license every 15 minutes');
370
+ strapi.log.info('[PING] Started pinging license every 15 minutes');
371
371
 
372
372
  // Store interval globally so we can clean it up
373
373
  strapi.licenseGuardMagicMail = {
@@ -378,13 +378,13 @@ module.exports = ({ strapi }) => ({
378
378
 
379
379
  // Display license info box
380
380
  strapi.log.info('╔════════════════════════════════════════════════════════════════╗');
381
- strapi.log.info('║ MAGIC MAIL PLUGIN LICENSE ACTIVE ║');
381
+ strapi.log.info('║ [SUCCESS] MAGIC MAIL PLUGIN LICENSE ACTIVE ║');
382
382
  strapi.log.info('║ ║');
383
383
  strapi.log.info(`║ License: ${licenseKey.padEnd(38, ' ')}║`);
384
384
  strapi.log.info(`║ User: ${(license?.firstName + ' ' + license?.lastName).padEnd(41, ' ')}║`);
385
385
  strapi.log.info(`║ Email: ${(license?.email || 'N/A').padEnd(40, ' ')}║`);
386
386
  strapi.log.info('║ ║');
387
- strapi.log.info('║ 🔄 Auto-pinging every 15 minutes ║');
387
+ strapi.log.info('║ [AUTO] Pinging every 15 minutes ║');
388
388
  strapi.log.info('╚════════════════════════════════════════════════════════════════╝');
389
389
 
390
390
  return {
@@ -394,9 +394,9 @@ module.exports = ({ strapi }) => ({
394
394
  gracePeriod: verification.gracePeriod || false,
395
395
  };
396
396
  } else {
397
- strapi.log.error(`❌ License validation failed (Key: ${licenseKey.substring(0, 10)}...)`);
397
+ strapi.log.error(`[ERROR] License validation failed (Key: ${licenseKey.substring(0, 10)}...)`);
398
398
  strapi.log.info('──────────────────────────────────────────────────────────');
399
- strapi.log.info('⚠️ Running in demo mode with limited features');
399
+ strapi.log.info('[WARNING] Running in demo mode with limited features');
400
400
  return {
401
401
  valid: false,
402
402
  demo: true,
@@ -405,7 +405,7 @@ module.exports = ({ strapi }) => ({
405
405
  };
406
406
  }
407
407
  } catch (error) {
408
- strapi.log.error(' Error initializing License Guard:', error);
408
+ strapi.log.error('[ERROR] Error initializing License Guard:', error);
409
409
  return {
410
410
  valid: false,
411
411
  demo: true,
@@ -70,7 +70,7 @@ module.exports = ({ strapi }) => ({
70
70
  }
71
71
 
72
72
  const tokens = await response.json();
73
- strapi.log.info('[magic-mail] Tokens received from Google');
73
+ strapi.log.info('[magic-mail] [SUCCESS] Tokens received from Google');
74
74
 
75
75
  if (!tokens.access_token) {
76
76
  throw new Error('No access token received from Google');
@@ -89,7 +89,7 @@ module.exports = ({ strapi }) => ({
89
89
  }
90
90
 
91
91
  const userInfo = await userInfoResponse.json();
92
- strapi.log.info(`[magic-mail] Got user email from Google: ${userInfo.email}`);
92
+ strapi.log.info(`[magic-mail] [SUCCESS] Got user email from Google: ${userInfo.email}`);
93
93
 
94
94
  if (!userInfo.email) {
95
95
  strapi.log.error('[magic-mail] userInfo:', userInfo);
@@ -220,7 +220,7 @@ module.exports = ({ strapi }) => ({
220
220
  }
221
221
 
222
222
  const tokens = await response.json();
223
- strapi.log.info('[magic-mail] Tokens received from Microsoft');
223
+ strapi.log.info('[magic-mail] [SUCCESS] Tokens received from Microsoft');
224
224
  strapi.log.info('[magic-mail] Has access_token:', !!tokens.access_token);
225
225
  strapi.log.info('[magic-mail] Has refresh_token:', !!tokens.refresh_token);
226
226
  strapi.log.info('[magic-mail] Has id_token:', !!tokens.id_token);
@@ -237,7 +237,7 @@ module.exports = ({ strapi }) => ({
237
237
  const payloadBase64 = tokens.id_token.split('.')[1];
238
238
  const payload = JSON.parse(Buffer.from(payloadBase64, 'base64').toString());
239
239
  email = payload.email || payload.preferred_username || payload.upn;
240
- strapi.log.info(`[magic-mail] Got email from Microsoft ID token: ${email}`);
240
+ strapi.log.info(`[magic-mail] [SUCCESS] Got email from Microsoft ID token: ${email}`);
241
241
  } catch (jwtErr) {
242
242
  strapi.log.warn('[magic-mail] Could not decode ID token:', jwtErr.message);
243
243
  }
@@ -264,7 +264,7 @@ module.exports = ({ strapi }) => ({
264
264
  strapi.log.info('[magic-mail] User info from Graph:', JSON.stringify(userInfo, null, 2));
265
265
 
266
266
  email = userInfo.mail || userInfo.userPrincipalName;
267
- strapi.log.info(`[magic-mail] Got email from Microsoft Graph: ${email}`);
267
+ strapi.log.info(`[magic-mail] [SUCCESS] Got email from Microsoft Graph: ${email}`);
268
268
  }
269
269
 
270
270
  if (!email) {
@@ -317,7 +317,7 @@ module.exports = ({ strapi }) => ({
317
317
  }
318
318
 
319
319
  const tokens = await response.json();
320
- strapi.log.info('[magic-mail] Microsoft tokens refreshed successfully');
320
+ strapi.log.info('[magic-mail] [SUCCESS] Microsoft tokens refreshed successfully');
321
321
 
322
322
  return {
323
323
  accessToken: tokens.access_token,
@@ -388,7 +388,7 @@ module.exports = ({ strapi }) => ({
388
388
  }
389
389
 
390
390
  const tokens = await response.json();
391
- strapi.log.info('[magic-mail] Tokens received from Yahoo');
391
+ strapi.log.info('[magic-mail] [SUCCESS] Tokens received from Yahoo');
392
392
 
393
393
  if (!tokens.access_token) {
394
394
  throw new Error('No access token received from Yahoo');
@@ -408,7 +408,7 @@ module.exports = ({ strapi }) => ({
408
408
 
409
409
  const userInfo = await userInfoResponse.json();
410
410
  const email = userInfo.email;
411
- strapi.log.info(`[magic-mail] Got email from Yahoo: ${email}`);
411
+ strapi.log.info(`[magic-mail] [SUCCESS] Got email from Yahoo: ${email}`);
412
412
 
413
413
  if (!email) {
414
414
  throw new Error('Yahoo did not provide email address');
@@ -486,14 +486,14 @@ module.exports = ({ strapi }) => ({
486
486
  expiresAt: tokenData.expiresAt,
487
487
  });
488
488
 
489
- const account = await strapi.entityService.create('plugin::magic-mail.email-account', {
489
+ const account = await strapi.documents('plugin::magic-mail.email-account').create({
490
490
  data: {
491
491
  name: accountDetails.name,
492
492
  description: accountDetails.description || '',
493
493
  provider: `${provider}-oauth`,
494
494
  config: encryptedConfig, // OAuth app credentials
495
495
  oauth: encryptedOAuth, // OAuth tokens
496
- fromEmail: tokenData.email, // Use email from Google, not from accountDetails
496
+ fromEmail: tokenData.email, // [SUCCESS] Use email from Google, not from accountDetails
497
497
  fromName: accountDetails.fromName || tokenData.email.split('@')[0],
498
498
  replyTo: accountDetails.replyTo || tokenData.email,
499
499
  isActive: true,
@@ -507,7 +507,7 @@ module.exports = ({ strapi }) => ({
507
507
  },
508
508
  });
509
509
 
510
- strapi.log.info(`[magic-mail] OAuth account created: ${accountDetails.name} (${tokenData.email})`);
510
+ strapi.log.info(`[magic-mail] [SUCCESS] OAuth account created: ${accountDetails.name} (${tokenData.email})`);
511
511
 
512
512
  return account;
513
513
  },
@@ -2,6 +2,6 @@
2
2
 
3
3
  module.exports = ({ strapi }) => ({
4
4
  getWelcomeMessage() {
5
- return 'Welcome to Strapi 🚀';
5
+ return 'Welcome to Strapi [START]';
6
6
  },
7
7
  });
@@ -15,7 +15,7 @@ function getEncryptionKey() {
15
15
  return crypto.createHash('sha256').update(envKey).digest();
16
16
  }
17
17
 
18
- console.warn('[magic-mail] ⚠️ No MAGIC_MAIL_ENCRYPTION_KEY found. Using fallback.');
18
+ console.warn('[magic-mail] [WARNING] No MAGIC_MAIL_ENCRYPTION_KEY found. Using fallback.');
19
19
  return crypto.createHash('sha256').update('magic-mail-default-key').digest();
20
20
  }
21
21