strapi-plugin-magic-mail 2.6.7 → 2.7.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.
@@ -79,19 +79,23 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
79
79
  try {
80
80
  const licenseGuardService = strapi2.plugin("magic-mail").service("license-guard");
81
81
  setTimeout(async () => {
82
- const licenseStatus = await licenseGuardService.initialize();
83
- if (!licenseStatus.valid && licenseStatus.demo) {
84
- log.error("╔════════════════════════════════════════════════════════════════╗");
85
- log.error("║ [ERROR] MAGICMAIL - NO VALID LICENSE ║");
86
- log.error("║ ║");
87
- log.error("║ This plugin requires a valid license to operate. ║");
88
- log.error("║ Please activate your license via Admin UI: ║");
89
- log.error("║ Go to MagicMail License tab ║");
90
- log.error("║ ║");
91
- log.error(' Click "Generate Free License" to get started! ║');
92
- log.error("╚════════════════════════════════════════════════════════════════╝");
93
- } else if (licenseStatus.gracePeriod) {
94
- log.warn("[WARNING] Running on grace period (license server unreachable)");
82
+ try {
83
+ const licenseStatus = await licenseGuardService.initialize();
84
+ if (!licenseStatus.valid && licenseStatus.demo) {
85
+ log.error("╔════════════════════════════════════════════════════════════════╗");
86
+ log.error("║ [ERROR] MAGICMAIL - NO VALID LICENSE ║");
87
+ log.error("║ ║");
88
+ log.error("║ This plugin requires a valid license to operate. ║");
89
+ log.error("║ Please activate your license via Admin UI: ║");
90
+ log.error("║ Go to MagicMail -> License tab ║");
91
+ log.error("");
92
+ log.error('║ Click "Generate Free License" to get started! ║');
93
+ log.error("╚════════════════════════════════════════════════════════════════╝");
94
+ } else if (licenseStatus.gracePeriod) {
95
+ log.warn("[WARNING] Running on grace period (license server unreachable)");
96
+ }
97
+ } catch (err) {
98
+ log.error("[ERROR] License initialization failed:", err.message);
95
99
  }
96
100
  }, 2e3);
97
101
  const accountManager2 = strapi2.plugin("magic-mail").service("account-manager");
@@ -130,14 +134,14 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
130
134
  const hourlyResetInterval = setInterval(async () => {
131
135
  try {
132
136
  if (!strapi2 || !strapi2.plugin) {
133
- console.warn("Strapi not available for hourly reset");
137
+ strapi2.log.warn("[magic-mail] Strapi not available for hourly reset");
134
138
  return;
135
139
  }
136
140
  const accountMgr = strapi2.plugin("magic-mail").service("account-manager");
137
141
  await accountMgr.resetCounters("hourly");
138
142
  log.info("[RESET] Hourly counters reset");
139
143
  } catch (err) {
140
- console.error("Hourly reset error:", err.message);
144
+ strapi2.log.error("[magic-mail] Hourly reset error:", err.message);
141
145
  }
142
146
  }, 60 * 60 * 1e3);
143
147
  if (!commonjsGlobal.magicMailIntervals) commonjsGlobal.magicMailIntervals = {};
@@ -148,7 +152,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
148
152
  setTimeout(async () => {
149
153
  try {
150
154
  if (!strapi2 || !strapi2.plugin) {
151
- console.warn("Strapi not available for daily reset");
155
+ strapi2.log.warn("[magic-mail] Strapi not available for daily reset");
152
156
  return;
153
157
  }
154
158
  const accountMgr = strapi2.plugin("magic-mail").service("account-manager");
@@ -157,19 +161,19 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
157
161
  const dailyResetInterval = setInterval(async () => {
158
162
  try {
159
163
  if (!strapi2 || !strapi2.plugin) {
160
- console.warn("Strapi not available for daily reset");
164
+ strapi2.log.warn("[magic-mail] Strapi not available for daily reset");
161
165
  return;
162
166
  }
163
167
  const accountMgr2 = strapi2.plugin("magic-mail").service("account-manager");
164
168
  await accountMgr2.resetCounters("daily");
165
169
  log.info("[RESET] Daily counters reset");
166
170
  } catch (err) {
167
- console.error("Daily reset error:", err.message);
171
+ strapi2.log.error("[magic-mail] Daily reset error:", err.message);
168
172
  }
169
173
  }, 24 * 60 * 60 * 1e3);
170
174
  commonjsGlobal.magicMailIntervals.daily = dailyResetInterval;
171
175
  } catch (err) {
172
- console.error("Initial daily reset error:", err.message);
176
+ strapi2.log.error("[magic-mail] Initial daily reset error:", err.message);
173
177
  }
174
178
  }, msUntilMidnight);
175
179
  log.info("[SUCCESS] Counter reset schedules initialized");
@@ -437,7 +441,7 @@ const attributes$5 = {
437
441
  type: "string"
438
442
  },
439
443
  accountId: {
440
- type: "integer"
444
+ type: "string"
441
445
  },
442
446
  accountName: {
443
447
  type: "string"
@@ -866,62 +870,75 @@ var contentTypes$1 = {
866
870
  schema: pluginSettings$4
867
871
  }
868
872
  };
873
+ function stripAttachmentPaths(body) {
874
+ if (body.attachments && Array.isArray(body.attachments)) {
875
+ body.attachments = body.attachments.map(({ path: path2, ...safe }) => safe);
876
+ }
877
+ return body;
878
+ }
869
879
  var controller$1 = {
870
880
  /**
871
881
  * Send email via MagicMail router
872
882
  */
873
883
  async send(ctx) {
874
884
  try {
885
+ const body = stripAttachmentPaths({ ...ctx.request.body });
886
+ if (!body || !body.to) {
887
+ return ctx.badRequest("Recipient (to) is required");
888
+ }
875
889
  const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
876
- const result = await emailRouter2.send(ctx.request.body);
890
+ const result = await emailRouter2.send(body);
877
891
  ctx.body = {
878
892
  success: true,
879
893
  ...result
880
894
  };
881
895
  } catch (err) {
882
- strapi.log.error("[magic-mail] Error sending email:", err);
883
- ctx.throw(500, err.message || "Failed to send email");
896
+ strapi.log.error("[magic-mail] Error sending email:", err.message);
897
+ ctx.throw(err.status || 500, err.message || "Failed to send email");
884
898
  }
885
899
  },
886
900
  /**
887
901
  * Send message via Email or WhatsApp (unified API)
888
- * POST /api/magic-mail/send-message
889
- * Body: { channel: 'email' | 'whatsapp' | 'auto', to, phoneNumber, subject, message, ... }
890
902
  */
891
903
  async sendMessage(ctx) {
892
904
  try {
905
+ const body = stripAttachmentPaths({ ...ctx.request.body });
906
+ if (!body || !body.to && !body.phoneNumber) {
907
+ return ctx.badRequest("Recipient (to or phoneNumber) is required");
908
+ }
893
909
  const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
894
- const result = await emailRouter2.sendMessage(ctx.request.body);
910
+ const result = await emailRouter2.sendMessage(body);
895
911
  ctx.body = {
896
912
  success: true,
897
913
  ...result
898
914
  };
899
915
  } catch (err) {
900
- strapi.log.error("[magic-mail] Error sending message:", err);
901
- ctx.throw(500, err.message || "Failed to send message");
916
+ strapi.log.error("[magic-mail] Error sending message:", err.message);
917
+ ctx.throw(err.status || 500, err.message || "Failed to send message");
902
918
  }
903
919
  },
904
920
  /**
905
921
  * Send WhatsApp message
906
- * POST /api/magic-mail/send-whatsapp
907
- * Body: { phoneNumber, message, templateId?, templateData? }
908
922
  */
909
923
  async sendWhatsApp(ctx) {
910
924
  try {
925
+ const body = stripAttachmentPaths({ ...ctx.request.body });
926
+ if (!body || !body.phoneNumber) {
927
+ return ctx.badRequest("Phone number is required");
928
+ }
911
929
  const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
912
- const result = await emailRouter2.sendWhatsApp(ctx.request.body);
930
+ const result = await emailRouter2.sendWhatsApp(body);
913
931
  ctx.body = {
914
932
  success: true,
915
933
  ...result
916
934
  };
917
935
  } catch (err) {
918
- strapi.log.error("[magic-mail] Error sending WhatsApp:", err);
919
- ctx.throw(500, err.message || "Failed to send WhatsApp message");
936
+ strapi.log.error("[magic-mail] Error sending WhatsApp:", err.message);
937
+ ctx.throw(err.status || 500, err.message || "Failed to send WhatsApp message");
920
938
  }
921
939
  },
922
940
  /**
923
941
  * Get WhatsApp connection status
924
- * GET /api/magic-mail/whatsapp/status
925
942
  */
926
943
  async getWhatsAppStatus(ctx) {
927
944
  try {
@@ -932,7 +949,8 @@ var controller$1 = {
932
949
  data: status
933
950
  };
934
951
  } catch (err) {
935
- strapi.log.error("[magic-mail] Error getting WhatsApp status:", err);
952
+ strapi.log.error("[magic-mail] Error getting WhatsApp status:", err.message);
953
+ ctx.status = 503;
936
954
  ctx.body = {
937
955
  success: false,
938
956
  data: {
@@ -945,14 +963,12 @@ var controller$1 = {
945
963
  },
946
964
  /**
947
965
  * Check if phone number is on WhatsApp
948
- * GET /api/magic-mail/whatsapp/check/:phoneNumber
949
966
  */
950
967
  async checkWhatsAppNumber(ctx) {
951
968
  try {
952
969
  const { phoneNumber } = ctx.params;
953
970
  if (!phoneNumber) {
954
- ctx.throw(400, "Phone number is required");
955
- return;
971
+ return ctx.badRequest("Phone number is required");
956
972
  }
957
973
  const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
958
974
  const result = await emailRouter2.checkWhatsAppNumber(phoneNumber);
@@ -961,8 +977,8 @@ var controller$1 = {
961
977
  data: result
962
978
  };
963
979
  } catch (err) {
964
- strapi.log.error("[magic-mail] Error checking WhatsApp number:", err);
965
- ctx.throw(500, err.message || "Failed to check phone number");
980
+ strapi.log.error("[magic-mail] Error checking WhatsApp number:", err.message);
981
+ ctx.throw(err.status || 500, err.message || "Failed to check phone number");
966
982
  }
967
983
  }
968
984
  };
@@ -1020,12 +1036,15 @@ var accounts$1 = {
1020
1036
  const { accountId } = ctx.params;
1021
1037
  const accountManager2 = strapi.plugin("magic-mail").service("account-manager");
1022
1038
  const account = await accountManager2.getAccountWithDecryptedConfig(accountId);
1039
+ if (!account) {
1040
+ return ctx.notFound("Email account not found");
1041
+ }
1023
1042
  ctx.body = {
1024
1043
  data: account
1025
1044
  };
1026
1045
  } catch (err) {
1027
- strapi.log.error("[magic-mail] Error getting account:", err);
1028
- ctx.throw(500, "Error fetching email account");
1046
+ strapi.log.error("[magic-mail] Error getting account:", err.message);
1047
+ ctx.throw(err.status || 500, err.message || "Error fetching email account");
1029
1048
  }
1030
1049
  },
1031
1050
  /**
@@ -1036,33 +1055,40 @@ var accounts$1 = {
1036
1055
  const { accountId } = ctx.params;
1037
1056
  const accountManager2 = strapi.plugin("magic-mail").service("account-manager");
1038
1057
  const account = await accountManager2.updateAccount(accountId, ctx.request.body);
1058
+ if (!account) {
1059
+ return ctx.notFound("Email account not found");
1060
+ }
1039
1061
  ctx.body = {
1040
1062
  data: account,
1041
1063
  message: "Email account updated successfully"
1042
1064
  };
1043
1065
  } catch (err) {
1044
- strapi.log.error("[magic-mail] Error updating account:", err);
1045
- ctx.throw(500, err.message || "Error updating email account");
1066
+ strapi.log.error("[magic-mail] Error updating account:", err.message);
1067
+ ctx.throw(err.status || 500, err.message || "Error updating email account");
1046
1068
  }
1047
1069
  },
1048
1070
  /**
1049
- * Test email account
1071
+ * Test email account with validation
1050
1072
  */
1051
1073
  async test(ctx) {
1052
1074
  try {
1053
1075
  const { accountId } = ctx.params;
1054
- const { testEmail, priority, type, unsubscribeUrl } = ctx.request.body;
1076
+ const { testEmail, to, priority, type, unsubscribeUrl } = ctx.request.body;
1077
+ const recipientEmail = testEmail || to;
1078
+ if (!recipientEmail) {
1079
+ return ctx.badRequest("testEmail is required");
1080
+ }
1055
1081
  const testOptions = {
1056
1082
  priority: priority || "normal",
1057
1083
  type: type || "transactional",
1058
1084
  unsubscribeUrl: unsubscribeUrl || null
1059
1085
  };
1060
1086
  const accountManager2 = strapi.plugin("magic-mail").service("account-manager");
1061
- const result = await accountManager2.testAccount(accountId, testEmail, testOptions);
1087
+ const result = await accountManager2.testAccount(accountId, recipientEmail, testOptions);
1062
1088
  ctx.body = result;
1063
1089
  } catch (err) {
1064
- strapi.log.error("[magic-mail] Error testing account:", err);
1065
- ctx.throw(500, "Error testing email account");
1090
+ strapi.log.error("[magic-mail] Error testing account:", err.message);
1091
+ ctx.throw(err.status || 500, err.message || "Error testing email account");
1066
1092
  }
1067
1093
  },
1068
1094
  /**
@@ -1162,6 +1188,12 @@ var accounts$1 = {
1162
1188
  }
1163
1189
  }
1164
1190
  };
1191
+ function escapeHtml(str) {
1192
+ return String(str || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1193
+ }
1194
+ function escapeJs(str) {
1195
+ return String(str || "").replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
1196
+ }
1165
1197
  var oauth$3 = {
1166
1198
  /**
1167
1199
  * Initiate Gmail OAuth flow
@@ -1207,7 +1239,7 @@ var oauth$3 = {
1207
1239
  </head>
1208
1240
  <body>
1209
1241
  <div class="error">[ERROR] OAuth Authorization Failed</div>
1210
- <p>Error: ${error}</p>
1242
+ <p>Error: ${escapeHtml(error)}</p>
1211
1243
  <p>You can close this window and try again.</p>
1212
1244
  <script>
1213
1245
  setTimeout(() => window.close(), 3000);
@@ -1248,15 +1280,15 @@ var oauth$3 = {
1248
1280
  // Send data to parent window
1249
1281
  window.opener.postMessage({
1250
1282
  type: 'gmail-oauth-success',
1251
- code: '${code}',
1252
- state: '${state}'
1283
+ code: '${escapeJs(code)}',
1284
+ state: '${escapeJs(state)}'
1253
1285
  }, window.location.origin);
1254
1286
 
1255
1287
  setTimeout(() => window.close(), 1500);
1256
1288
  } else {
1257
1289
  // Fallback: redirect to admin panel
1258
1290
  setTimeout(() => {
1259
- window.location.href = '/admin/plugins/magic-mail?oauth_code=${code}&oauth_state=${state}';
1291
+ window.location.href = '/admin/plugins/magic-mail?oauth_code=' + encodeURIComponent('${escapeJs(code)}') + '&oauth_state=' + encodeURIComponent('${escapeJs(state)}');
1260
1292
  }, 2000);
1261
1293
  }
1262
1294
  <\/script>
@@ -1316,7 +1348,7 @@ var oauth$3 = {
1316
1348
  </head>
1317
1349
  <body>
1318
1350
  <div class="error">[ERROR] OAuth Authorization Failed</div>
1319
- <p>Error: ${error}</p>
1351
+ <p>Error: ${escapeHtml(error)}</p>
1320
1352
  <p>You can close this window and try again.</p>
1321
1353
  <script>
1322
1354
  setTimeout(() => window.close(), 3000);
@@ -1357,15 +1389,15 @@ var oauth$3 = {
1357
1389
  // Send data to parent window
1358
1390
  window.opener.postMessage({
1359
1391
  type: 'microsoft-oauth-success',
1360
- code: '${code}',
1361
- state: '${state}'
1392
+ code: '${escapeJs(code)}',
1393
+ state: '${escapeJs(state)}'
1362
1394
  }, window.location.origin);
1363
1395
 
1364
1396
  setTimeout(() => window.close(), 1500);
1365
1397
  } else {
1366
1398
  // Fallback: redirect to admin panel
1367
1399
  setTimeout(() => {
1368
- window.location.href = '/admin/plugins/magic-mail?oauth_code=${code}&oauth_state=${state}';
1400
+ window.location.href = '/admin/plugins/magic-mail?oauth_code=' + encodeURIComponent('${escapeJs(code)}') + '&oauth_state=' + encodeURIComponent('${escapeJs(state)}');
1369
1401
  }, 2000);
1370
1402
  }
1371
1403
  <\/script>
@@ -1421,7 +1453,7 @@ var oauth$3 = {
1421
1453
  </head>
1422
1454
  <body>
1423
1455
  <div class="error">[ERROR] OAuth Authorization Failed</div>
1424
- <p>Error: ${error}</p>
1456
+ <p>Error: ${escapeHtml(error)}</p>
1425
1457
  <p>You can close this window and try again.</p>
1426
1458
  <script>
1427
1459
  setTimeout(() => window.close(), 3000);
@@ -1462,15 +1494,15 @@ var oauth$3 = {
1462
1494
  // Send data to parent window
1463
1495
  window.opener.postMessage({
1464
1496
  type: 'yahoo-oauth-success',
1465
- code: '${code}',
1466
- state: '${state}'
1497
+ code: '${escapeJs(code)}',
1498
+ state: '${escapeJs(state)}'
1467
1499
  }, window.location.origin);
1468
1500
 
1469
1501
  setTimeout(() => window.close(), 1500);
1470
1502
  } else {
1471
1503
  // Fallback: redirect to admin panel
1472
1504
  setTimeout(() => {
1473
- window.location.href = '/admin/plugins/magic-mail?oauth_code=${code}&oauth_state=${state}';
1505
+ window.location.href = '/admin/plugins/magic-mail?oauth_code=' + encodeURIComponent('${escapeJs(code)}') + '&oauth_state=' + encodeURIComponent('${escapeJs(state)}');
1474
1506
  }, 2000);
1475
1507
  }
1476
1508
  <\/script>
@@ -1573,6 +1605,16 @@ var oauth$3 = {
1573
1605
  }
1574
1606
  };
1575
1607
  const ROUTING_RULE_UID = "plugin::magic-mail.routing-rule";
1608
+ const ALLOWED_RULE_FIELDS = ["name", "conditions", "accountName", "priority", "isActive", "matchType", "matchField", "matchValue", "description"];
1609
+ function sanitizeRuleData(body) {
1610
+ const data = {};
1611
+ for (const field of ALLOWED_RULE_FIELDS) {
1612
+ if (body[field] !== void 0) {
1613
+ data[field] = body[field];
1614
+ }
1615
+ }
1616
+ return data;
1617
+ }
1576
1618
  var routingRules$1 = {
1577
1619
  /**
1578
1620
  * Get all routing rules
@@ -1580,15 +1622,14 @@ var routingRules$1 = {
1580
1622
  async getAll(ctx) {
1581
1623
  try {
1582
1624
  const rules = await strapi.documents(ROUTING_RULE_UID).findMany({
1583
- sort: [{ priority: "desc" }]
1625
+ sort: [{ priority: "asc" }]
1584
1626
  });
1585
1627
  ctx.body = {
1586
- data: rules,
1587
- meta: { count: rules.length }
1628
+ data: rules
1588
1629
  };
1589
1630
  } catch (err) {
1590
- strapi.log.error("[magic-mail] Error getting routing rules:", err);
1591
- ctx.throw(500, "Error fetching routing rules");
1631
+ strapi.log.error("[magic-mail] Error getting routing rules:", err.message);
1632
+ ctx.throw(err.status || 500, err.message || "Error fetching routing rules");
1592
1633
  }
1593
1634
  },
1594
1635
  /**
@@ -1601,18 +1642,18 @@ var routingRules$1 = {
1601
1642
  documentId: ruleId
1602
1643
  });
1603
1644
  if (!rule) {
1604
- ctx.throw(404, "Routing rule not found");
1645
+ return ctx.notFound("Routing rule not found");
1605
1646
  }
1606
1647
  ctx.body = {
1607
1648
  data: rule
1608
1649
  };
1609
1650
  } catch (err) {
1610
- strapi.log.error("[magic-mail] Error getting routing rule:", err);
1611
- ctx.throw(500, "Error fetching routing rule");
1651
+ strapi.log.error("[magic-mail] Error getting routing rule:", err.message);
1652
+ ctx.throw(err.status || 500, err.message || "Error fetching routing rule");
1612
1653
  }
1613
1654
  },
1614
1655
  /**
1615
- * Create new routing rule
1656
+ * Create new routing rule with input sanitization
1616
1657
  */
1617
1658
  async create(ctx) {
1618
1659
  try {
@@ -1620,31 +1661,34 @@ var routingRules$1 = {
1620
1661
  const currentRules = await strapi.documents(ROUTING_RULE_UID).count();
1621
1662
  const maxRules = await licenseGuard2.getMaxRoutingRules();
1622
1663
  if (maxRules !== -1 && currentRules >= maxRules) {
1623
- ctx.throw(403, `Routing rule limit reached (${maxRules}). Upgrade to Advanced license for unlimited rules.`);
1624
- return;
1664
+ return ctx.forbidden(`Routing rule limit reached (${maxRules}). Upgrade to Advanced license for unlimited rules.`);
1625
1665
  }
1626
- const rule = await strapi.documents(ROUTING_RULE_UID).create({
1627
- data: ctx.request.body
1628
- });
1666
+ const data = sanitizeRuleData(ctx.request.body);
1667
+ const rule = await strapi.documents(ROUTING_RULE_UID).create({ data });
1629
1668
  ctx.body = {
1630
1669
  data: rule,
1631
1670
  message: "Routing rule created successfully"
1632
1671
  };
1633
1672
  strapi.log.info(`[magic-mail] [SUCCESS] Routing rule created: ${rule.name}`);
1634
1673
  } catch (err) {
1635
- strapi.log.error("[magic-mail] Error creating routing rule:", err);
1674
+ strapi.log.error("[magic-mail] Error creating routing rule:", err.message);
1636
1675
  ctx.throw(err.status || 500, err.message || "Error creating routing rule");
1637
1676
  }
1638
1677
  },
1639
1678
  /**
1640
- * Update routing rule
1679
+ * Update routing rule with existence check and input sanitization
1641
1680
  */
1642
1681
  async update(ctx) {
1643
1682
  try {
1644
1683
  const { ruleId } = ctx.params;
1684
+ const existing = await strapi.documents(ROUTING_RULE_UID).findOne({ documentId: ruleId });
1685
+ if (!existing) {
1686
+ return ctx.notFound("Routing rule not found");
1687
+ }
1688
+ const data = sanitizeRuleData(ctx.request.body);
1645
1689
  const rule = await strapi.documents(ROUTING_RULE_UID).update({
1646
1690
  documentId: ruleId,
1647
- data: ctx.request.body
1691
+ data
1648
1692
  });
1649
1693
  ctx.body = {
1650
1694
  data: rule,
@@ -1652,16 +1696,20 @@ var routingRules$1 = {
1652
1696
  };
1653
1697
  strapi.log.info(`[magic-mail] [SUCCESS] Routing rule updated: ${rule.name}`);
1654
1698
  } catch (err) {
1655
- strapi.log.error("[magic-mail] Error updating routing rule:", err);
1656
- ctx.throw(500, err.message || "Error updating routing rule");
1699
+ strapi.log.error("[magic-mail] Error updating routing rule:", err.message);
1700
+ ctx.throw(err.status || 500, err.message || "Error updating routing rule");
1657
1701
  }
1658
1702
  },
1659
1703
  /**
1660
- * Delete routing rule
1704
+ * Delete routing rule with existence check
1661
1705
  */
1662
1706
  async delete(ctx) {
1663
1707
  try {
1664
1708
  const { ruleId } = ctx.params;
1709
+ const existing = await strapi.documents(ROUTING_RULE_UID).findOne({ documentId: ruleId });
1710
+ if (!existing) {
1711
+ return ctx.notFound("Routing rule not found");
1712
+ }
1665
1713
  await strapi.documents(ROUTING_RULE_UID).delete({
1666
1714
  documentId: ruleId
1667
1715
  });
@@ -1670,8 +1718,8 @@ var routingRules$1 = {
1670
1718
  };
1671
1719
  strapi.log.info(`[magic-mail] Routing rule deleted: ${ruleId}`);
1672
1720
  } catch (err) {
1673
- strapi.log.error("[magic-mail] Error deleting routing rule:", err);
1674
- ctx.throw(500, "Error deleting routing rule");
1721
+ strapi.log.error("[magic-mail] Error deleting routing rule:", err.message);
1722
+ ctx.throw(err.status || 500, err.message || "Error deleting routing rule");
1675
1723
  }
1676
1724
  }
1677
1725
  };
@@ -1818,7 +1866,6 @@ function requireFeatures() {
1818
1866
  */
1819
1867
  hasFeature(licenseData, featureName) {
1820
1868
  if (!licenseData) {
1821
- console.log(`[features.js] [WARNING] No license data → using FREE tier`);
1822
1869
  return this.free.features.includes(featureName);
1823
1870
  }
1824
1871
  let isEnterprise = false;
@@ -1844,20 +1891,15 @@ function requireFeatures() {
1844
1891
  isPremium = true;
1845
1892
  }
1846
1893
  if (isEnterprise && this.enterprise.features.includes(featureName)) {
1847
- console.log(`[features.js] [SUCCESS] ENTERPRISE tier has feature "${featureName}"`);
1848
1894
  return true;
1849
1895
  }
1850
1896
  if (isAdvanced && this.advanced.features.includes(featureName)) {
1851
- console.log(`[features.js] [SUCCESS] ADVANCED tier has feature "${featureName}"`);
1852
1897
  return true;
1853
1898
  }
1854
1899
  if (isPremium && this.premium.features.includes(featureName)) {
1855
- console.log(`[features.js] [SUCCESS] PREMIUM tier has feature "${featureName}"`);
1856
1900
  return true;
1857
1901
  }
1858
- const inFree = this.free.features.includes(featureName);
1859
- console.log(`[features.js] ${inFree ? "[SUCCESS]" : "[ERROR]"} FREE tier check for "${featureName}": ${inFree}`);
1860
- return inFree;
1902
+ return this.free.features.includes(featureName);
1861
1903
  },
1862
1904
  /**
1863
1905
  * Get max allowed accounts for license tier
@@ -1966,7 +2008,7 @@ var license$1 = ({ strapi: strapi2 }) => ({
1966
2008
  valid: verification.valid,
1967
2009
  demo: false,
1968
2010
  data: {
1969
- licenseKey,
2011
+ licenseKey: licenseKey ? licenseKey.substring(0, 8) + "..." + licenseKey.substring(licenseKey.length - 4) : null,
1970
2012
  email: license2?.email || null,
1971
2013
  firstName: license2?.firstName || null,
1972
2014
  lastName: license2?.lastName || null,
@@ -2047,7 +2089,6 @@ var license$1 = ({ strapi: strapi2 }) => ({
2047
2089
  const license2 = await licenseGuard2.getCurrentLicense();
2048
2090
  ctx.body = {
2049
2091
  success: true,
2050
- rawLicense: license2,
2051
2092
  detectedFlags: {
2052
2093
  featurePremium: license2?.featurePremium,
2053
2094
  featureAdvanced: license2?.featureAdvanced,
@@ -2128,7 +2169,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2128
2169
  data: templates
2129
2170
  });
2130
2171
  } catch (error) {
2131
- ctx.throw(500, error);
2172
+ ctx.throw(500, error.message);
2132
2173
  }
2133
2174
  },
2134
2175
  /**
@@ -2146,7 +2187,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2146
2187
  data: template
2147
2188
  });
2148
2189
  } catch (error) {
2149
- ctx.throw(500, error);
2190
+ ctx.throw(500, error.message);
2150
2191
  }
2151
2192
  },
2152
2193
  /**
@@ -2163,7 +2204,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2163
2204
  if (error.message.includes("limit reached") || error.message.includes("already exists")) {
2164
2205
  return ctx.badRequest(error.message);
2165
2206
  }
2166
- ctx.throw(500, error);
2207
+ ctx.throw(500, error.message);
2167
2208
  }
2168
2209
  },
2169
2210
  /**
@@ -2181,7 +2222,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2181
2222
  if (error.message.includes("not found")) {
2182
2223
  return ctx.notFound(error.message);
2183
2224
  }
2184
- ctx.throw(500, error);
2225
+ ctx.throw(500, error.message);
2185
2226
  }
2186
2227
  },
2187
2228
  /**
@@ -2196,7 +2237,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2196
2237
  message: "Template deleted successfully"
2197
2238
  });
2198
2239
  } catch (error) {
2199
- ctx.throw(500, error);
2240
+ ctx.throw(500, error.message);
2200
2241
  }
2201
2242
  },
2202
2243
  /**
@@ -2211,7 +2252,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2211
2252
  data: versions
2212
2253
  });
2213
2254
  } catch (error) {
2214
- ctx.throw(500, error);
2255
+ ctx.throw(500, error.message);
2215
2256
  }
2216
2257
  },
2217
2258
  /**
@@ -2229,7 +2270,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2229
2270
  if (error.message.includes("not found")) {
2230
2271
  return ctx.notFound(error.message);
2231
2272
  }
2232
- ctx.throw(500, error);
2273
+ ctx.throw(500, error.message);
2233
2274
  }
2234
2275
  },
2235
2276
  /**
@@ -2250,7 +2291,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2250
2291
  if (error.message.includes("does not belong")) {
2251
2292
  return ctx.badRequest(error.message);
2252
2293
  }
2253
- ctx.throw(500, error);
2294
+ ctx.throw(500, error.message);
2254
2295
  }
2255
2296
  },
2256
2297
  /**
@@ -2268,7 +2309,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2268
2309
  if (error.message.includes("not found")) {
2269
2310
  return ctx.notFound(error.message);
2270
2311
  }
2271
- ctx.throw(500, error);
2312
+ ctx.throw(500, error.message);
2272
2313
  }
2273
2314
  },
2274
2315
  /**
@@ -2287,7 +2328,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2287
2328
  if (error.message.includes("not found")) {
2288
2329
  return ctx.notFound(error.message);
2289
2330
  }
2290
- ctx.throw(500, error);
2331
+ ctx.throw(500, error.message);
2291
2332
  }
2292
2333
  },
2293
2334
  /**
@@ -2305,7 +2346,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2305
2346
  if (error.message.includes("requires")) {
2306
2347
  return ctx.forbidden(error.message);
2307
2348
  }
2308
- ctx.throw(500, error);
2349
+ ctx.throw(500, error.message);
2309
2350
  }
2310
2351
  },
2311
2352
  /**
@@ -2326,7 +2367,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2326
2367
  if (error.message.includes("requires")) {
2327
2368
  return ctx.forbidden(error.message);
2328
2369
  }
2329
- ctx.throw(500, error);
2370
+ ctx.throw(500, error.message);
2330
2371
  }
2331
2372
  },
2332
2373
  /**
@@ -2340,7 +2381,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2340
2381
  data: stats
2341
2382
  });
2342
2383
  } catch (error) {
2343
- ctx.throw(500, error);
2384
+ ctx.throw(500, error.message);
2344
2385
  }
2345
2386
  },
2346
2387
  /**
@@ -2355,7 +2396,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2355
2396
  data: template
2356
2397
  });
2357
2398
  } catch (error) {
2358
- ctx.throw(500, error);
2399
+ ctx.throw(500, error.message);
2359
2400
  }
2360
2401
  },
2361
2402
  /**
@@ -2370,7 +2411,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2370
2411
  data: template
2371
2412
  });
2372
2413
  } catch (error) {
2373
- ctx.throw(500, error);
2414
+ ctx.throw(500, error.message);
2374
2415
  }
2375
2416
  },
2376
2417
  /**
@@ -2399,8 +2440,8 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2399
2440
  ctx.set("Content-Disposition", `attachment; filename="${fileName}"`);
2400
2441
  ctx.send(fileContent);
2401
2442
  } catch (error) {
2402
- strapi2.log.error("[magic-mail] Error downloading template:", error);
2403
- ctx.throw(500, error);
2443
+ strapi2.log.error("[magic-mail] Error downloading template:", error.message);
2444
+ ctx.throw(500, error.message);
2404
2445
  }
2405
2446
  },
2406
2447
  /**
@@ -2418,7 +2459,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
2418
2459
  if (error.message.includes("not found")) {
2419
2460
  return ctx.notFound(error.message);
2420
2461
  }
2421
- ctx.throw(500, error);
2462
+ ctx.throw(500, error.message);
2422
2463
  }
2423
2464
  },
2424
2465
  /**
@@ -2479,7 +2520,11 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
2479
2520
  */
2480
2521
  async trackOpen(ctx) {
2481
2522
  const { emailId, recipientHash } = ctx.params;
2482
- await strapi2.plugin("magic-mail").service("analytics").recordOpen(emailId, recipientHash, ctx.request);
2523
+ try {
2524
+ await strapi2.plugin("magic-mail").service("analytics").recordOpen(emailId, recipientHash, ctx.request);
2525
+ } catch (err) {
2526
+ strapi2.log.error("[magic-mail] Error recording open event:", err.message);
2527
+ }
2483
2528
  const pixel = Buffer.from(
2484
2529
  "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
2485
2530
  "base64"
@@ -2488,20 +2533,34 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
2488
2533
  ctx.body = pixel;
2489
2534
  },
2490
2535
  /**
2491
- * Click tracking endpoint
2536
+ * Click tracking endpoint with open-redirect protection
2492
2537
  * GET /magic-mail/track/click/:emailId/:linkHash/:recipientHash
2493
2538
  */
2494
2539
  async trackClick(ctx) {
2495
2540
  const { emailId, linkHash, recipientHash } = ctx.params;
2496
- let { url } = ctx.query;
2497
- if (!url) {
2541
+ let url;
2542
+ try {
2498
2543
  const analyticsService = strapi2.plugin("magic-mail").service("analytics");
2499
2544
  url = await analyticsService.getOriginalUrlFromHash(emailId, linkHash);
2545
+ } catch (err) {
2546
+ strapi2.log.error("[magic-mail] Error getting original URL:", err.message);
2500
2547
  }
2501
2548
  if (!url) {
2502
- return ctx.badRequest("Missing target URL");
2549
+ return ctx.badRequest("Invalid or expired tracking link");
2550
+ }
2551
+ try {
2552
+ const parsed = new URL(url);
2553
+ if (!["http:", "https:"].includes(parsed.protocol)) {
2554
+ return ctx.badRequest("Invalid URL protocol");
2555
+ }
2556
+ } catch {
2557
+ return ctx.badRequest("Invalid URL format");
2558
+ }
2559
+ try {
2560
+ await strapi2.plugin("magic-mail").service("analytics").recordClick(emailId, linkHash, recipientHash, url, ctx.request);
2561
+ } catch (err) {
2562
+ strapi2.log.error("[magic-mail] Error recording click event:", err.message);
2503
2563
  }
2504
- await strapi2.plugin("magic-mail").service("analytics").recordClick(emailId, linkHash, recipientHash, url, ctx.request);
2505
2564
  ctx.redirect(url);
2506
2565
  },
2507
2566
  /**
@@ -2525,7 +2584,7 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
2525
2584
  data: stats
2526
2585
  });
2527
2586
  } catch (error) {
2528
- ctx.throw(500, error);
2587
+ ctx.throw(500, error.message);
2529
2588
  }
2530
2589
  },
2531
2590
  /**
@@ -2551,7 +2610,7 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
2551
2610
  ...result
2552
2611
  });
2553
2612
  } catch (error) {
2554
- ctx.throw(500, error);
2613
+ ctx.throw(500, error.message);
2555
2614
  }
2556
2615
  },
2557
2616
  /**
@@ -2570,7 +2629,7 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
2570
2629
  data: emailLog2
2571
2630
  });
2572
2631
  } catch (error) {
2573
- ctx.throw(500, error);
2632
+ ctx.throw(500, error.message);
2574
2633
  }
2575
2634
  },
2576
2635
  /**
@@ -2587,7 +2646,7 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
2587
2646
  data: activity
2588
2647
  });
2589
2648
  } catch (error) {
2590
- ctx.throw(500, error);
2649
+ ctx.throw(500, error.message);
2591
2650
  }
2592
2651
  },
2593
2652
  /**
@@ -2661,8 +2720,8 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
2661
2720
  }
2662
2721
  });
2663
2722
  } catch (error) {
2664
- strapi2.log.error("[magic-mail] Debug error:", error);
2665
- ctx.throw(500, error);
2723
+ strapi2.log.error("[magic-mail] Debug error:", error.message);
2724
+ ctx.throw(500, error.message);
2666
2725
  }
2667
2726
  },
2668
2727
  /**
@@ -2691,8 +2750,8 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
2691
2750
  message: "Email log deleted successfully"
2692
2751
  });
2693
2752
  } catch (error) {
2694
- strapi2.log.error("[magic-mail] Error deleting email log:", error);
2695
- ctx.throw(500, error);
2753
+ strapi2.log.error("[magic-mail] Error deleting email log:", error.message);
2754
+ ctx.throw(500, error.message);
2696
2755
  }
2697
2756
  },
2698
2757
  /**
@@ -2734,8 +2793,8 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
2734
2793
  deletedCount: emailLogs.length
2735
2794
  });
2736
2795
  } catch (error) {
2737
- strapi2.log.error("[magic-mail] Error clearing email logs:", error);
2738
- ctx.throw(500, error);
2796
+ strapi2.log.error("[magic-mail] Error clearing email logs:", error.message);
2797
+ ctx.throw(500, error.message);
2739
2798
  }
2740
2799
  }
2741
2800
  });
@@ -2958,7 +3017,7 @@ var test$1 = {
2958
3017
  } catch (error) {
2959
3018
  console.error("\n[ERROR] FEHLER:", error.message);
2960
3019
  console.error(error.stack);
2961
- ctx.throw(500, error);
3020
+ ctx.throw(500, error.message);
2962
3021
  }
2963
3022
  }
2964
3023
  };
@@ -3266,8 +3325,7 @@ var admin$1 = {
3266
3325
  path: "/accounts",
3267
3326
  handler: "accounts.create",
3268
3327
  config: {
3269
- policies: [],
3270
- auth: false,
3328
+ policies: ["admin::isAuthenticatedAdmin"],
3271
3329
  description: "Create email account"
3272
3330
  }
3273
3331
  },
@@ -3456,7 +3514,7 @@ var admin$1 = {
3456
3514
  path: "/license/limits",
3457
3515
  handler: "license.getLimits",
3458
3516
  config: {
3459
- policies: [],
3517
+ policies: ["admin::isAuthenticatedAdmin"],
3460
3518
  description: "Get license limits and available features"
3461
3519
  }
3462
3520
  },
@@ -3465,7 +3523,7 @@ var admin$1 = {
3465
3523
  path: "/license/debug",
3466
3524
  handler: "license.debugLicense",
3467
3525
  config: {
3468
- policies: [],
3526
+ policies: ["admin::isAuthenticatedAdmin"],
3469
3527
  description: "Debug license data"
3470
3528
  }
3471
3529
  },
@@ -3836,9 +3894,7 @@ var contentApi$1 = {
3836
3894
  path: "/send",
3837
3895
  handler: "controller.send",
3838
3896
  config: {
3839
- auth: false,
3840
- // Can be called from anywhere
3841
- description: "Send email via MagicMail router"
3897
+ description: "Send email via MagicMail router (requires API token)"
3842
3898
  }
3843
3899
  },
3844
3900
  // ============= UNIFIED MESSAGE ROUTE =============
@@ -3847,8 +3903,7 @@ var contentApi$1 = {
3847
3903
  path: "/send-message",
3848
3904
  handler: "controller.sendMessage",
3849
3905
  config: {
3850
- auth: false,
3851
- description: "Send message via Email or WhatsApp (unified API)"
3906
+ description: "Send message via Email or WhatsApp (requires API token)"
3852
3907
  }
3853
3908
  },
3854
3909
  // ============= WHATSAPP ROUTES =============
@@ -3857,8 +3912,7 @@ var contentApi$1 = {
3857
3912
  path: "/send-whatsapp",
3858
3913
  handler: "controller.sendWhatsApp",
3859
3914
  config: {
3860
- auth: false,
3861
- description: "Send WhatsApp message"
3915
+ description: "Send WhatsApp message (requires API token)"
3862
3916
  }
3863
3917
  },
3864
3918
  {
@@ -3866,8 +3920,7 @@ var contentApi$1 = {
3866
3920
  path: "/whatsapp/status",
3867
3921
  handler: "controller.getWhatsAppStatus",
3868
3922
  config: {
3869
- auth: false,
3870
- description: "Get WhatsApp connection status"
3923
+ description: "Get WhatsApp connection status (requires API token)"
3871
3924
  }
3872
3925
  },
3873
3926
  {
@@ -3875,8 +3928,7 @@ var contentApi$1 = {
3875
3928
  path: "/whatsapp/check/:phoneNumber",
3876
3929
  handler: "controller.checkWhatsAppNumber",
3877
3930
  config: {
3878
- auth: false,
3879
- description: "Check if phone number is on WhatsApp"
3931
+ description: "Check if phone number is on WhatsApp (requires API token)"
3880
3932
  }
3881
3933
  },
3882
3934
  // ============= TRACKING ROUTES =============
@@ -3915,11 +3967,12 @@ const ALGORITHM = "aes-256-gcm";
3915
3967
  const IV_LENGTH = 16;
3916
3968
  function getEncryptionKey() {
3917
3969
  const envKey = process.env.MAGIC_MAIL_ENCRYPTION_KEY || process.env.APP_KEYS;
3918
- if (envKey) {
3919
- return crypto$2.createHash("sha256").update(envKey).digest();
3970
+ if (!envKey) {
3971
+ throw new Error(
3972
+ "[magic-mail] FATAL: No encryption key configured. Set MAGIC_MAIL_ENCRYPTION_KEY or APP_KEYS in your environment variables. Email account credentials cannot be stored securely without a proper key."
3973
+ );
3920
3974
  }
3921
- console.warn("[magic-mail] [WARNING] No MAGIC_MAIL_ENCRYPTION_KEY found. Using fallback.");
3922
- return crypto$2.createHash("sha256").update("magic-mail-default-key").digest();
3975
+ return crypto$2.createHash("sha256").update(envKey).digest();
3923
3976
  }
3924
3977
  function encryptCredentials$2(data) {
3925
3978
  if (!data) return null;
@@ -3933,8 +3986,7 @@ function encryptCredentials$2(data) {
3933
3986
  const authTag = cipher.getAuthTag();
3934
3987
  return { encrypted: `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}` };
3935
3988
  } catch (err) {
3936
- console.error("[magic-mail] Encryption failed:", err);
3937
- throw new Error("Failed to encrypt credentials");
3989
+ throw new Error(`Failed to encrypt credentials: ${err.message}`);
3938
3990
  }
3939
3991
  }
3940
3992
  function decryptCredentials$2(encryptedData) {
@@ -3955,7 +4007,9 @@ function decryptCredentials$2(encryptedData) {
3955
4007
  decrypted += decipher.final("utf8");
3956
4008
  return JSON.parse(decrypted);
3957
4009
  } catch (err) {
3958
- console.error("[magic-mail] Decryption failed:", err);
4010
+ if (typeof strapi !== "undefined" && strapi.log) {
4011
+ strapi.log.error("[magic-mail] Decryption failed:", err.message);
4012
+ }
3959
4013
  return null;
3960
4014
  }
3961
4015
  }
@@ -5380,8 +5434,8 @@ var accountManager$1 = ({ strapi: strapi2 }) => ({
5380
5434
  dailyLimit = 0,
5381
5435
  hourlyLimit = 0
5382
5436
  } = accountData;
5383
- console.log("create account", accountData);
5384
- const encryptedConfig = encryptCredentials$1(config2);
5437
+ strapi2.log.info(`[magic-mail] Creating account: ${name} (${provider})`);
5438
+ const encryptedConfig = config2 ? encryptCredentials$1(config2) : null;
5385
5439
  if (isPrimary) {
5386
5440
  await this.unsetAllPrimary();
5387
5441
  }
@@ -5434,7 +5488,7 @@ var accountManager$1 = ({ strapi: strapi2 }) => ({
5434
5488
  dailyLimit,
5435
5489
  hourlyLimit
5436
5490
  } = accountData;
5437
- const encryptedConfig = encryptCredentials$1(config2);
5491
+ const encryptedConfig = config2 ? encryptCredentials$1(config2) : existingAccount.config;
5438
5492
  if (isPrimary && !existingAccount.isPrimary) {
5439
5493
  await this.unsetAllPrimary();
5440
5494
  }
@@ -5637,7 +5691,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
5637
5691
  "https://www.googleapis.com/auth/userinfo.email",
5638
5692
  "openid"
5639
5693
  ].join(" ");
5640
- const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scopes)}&access_type=offline&prompt=consent&state=${state}`;
5694
+ const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scopes)}&access_type=offline&prompt=consent&state=${encodeURIComponent(state)}`;
5641
5695
  return authUrl;
5642
5696
  },
5643
5697
  /**
@@ -5717,7 +5771,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
5717
5771
  const tokens = await response.json();
5718
5772
  return {
5719
5773
  accessToken: tokens.access_token,
5720
- expiresAt: new Date(Date.now() + tokens.expires_in * 1e3)
5774
+ expiresAt: new Date(Date.now() + (tokens.expires_in || 3600) * 1e3)
5721
5775
  };
5722
5776
  },
5723
5777
  /**
@@ -5746,7 +5800,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
5746
5800
  "email"
5747
5801
  // Email address
5748
5802
  ].join(" ");
5749
- const authUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scopes)}&response_mode=query&prompt=consent&state=${state}`;
5803
+ const authUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scopes)}&response_mode=query&prompt=consent&state=${encodeURIComponent(state)}`;
5750
5804
  strapi2.log.info(`[magic-mail] Microsoft OAuth URL: Using tenant ${tenantId}`);
5751
5805
  return authUrl;
5752
5806
  },
@@ -5887,7 +5941,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
5887
5941
  "sdps-r"
5888
5942
  // Read profile
5889
5943
  ].join(" ");
5890
- const authUrl = `https://api.login.yahoo.com/oauth2/request_auth?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scopes)}&state=${state}`;
5944
+ const authUrl = `https://api.login.yahoo.com/oauth2/request_auth?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scopes)}&state=${encodeURIComponent(state)}`;
5891
5945
  return authUrl;
5892
5946
  },
5893
5947
  /**
@@ -5971,7 +6025,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
5971
6025
  const tokens = await response.json();
5972
6026
  return {
5973
6027
  accessToken: tokens.access_token,
5974
- expiresAt: new Date(Date.now() + tokens.expires_in * 1e3)
6028
+ expiresAt: new Date(Date.now() + (tokens.expires_in || 3600) * 1e3)
5975
6029
  };
5976
6030
  },
5977
6031
  /**
@@ -6022,7 +6076,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
6022
6076
  return account;
6023
6077
  }
6024
6078
  });
6025
- const version = "2.6.6";
6079
+ const version = "2.6.8";
6026
6080
  const require$$2 = {
6027
6081
  version
6028
6082
  };
@@ -6302,8 +6356,8 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => {
6302
6356
  log.info("──────────────────────────────────────────────────────────");
6303
6357
  log.info(`📦 Plugin Store Check:`);
6304
6358
  if (licenseKey) {
6305
- log.info(` [SUCCESS] License Key found: ${licenseKey}`);
6306
- log.info(` [LICENSE] Key (short): ${licenseKey.substring(0, 10)}...`);
6359
+ const maskedKey = licenseKey.substring(0, 8) + "..." + licenseKey.substring(licenseKey.length - 4);
6360
+ log.info(` [SUCCESS] License Key found: ${maskedKey}`);
6307
6361
  if (lastValidated) {
6308
6362
  const lastValidatedDate = new Date(lastValidated);
6309
6363
  const hoursAgo = Math.floor((now.getTime() - lastValidatedDate.getTime()) / (1e3 * 60 * 60));
@@ -6409,7 +6463,7 @@ const convertHtmlToText = (html, options2 = { wordwrap: 130 }) => {
6409
6463
  }
6410
6464
  return html.replace(/<[^>]*>/g, "");
6411
6465
  } catch (error) {
6412
- strapi.log.error("[magic-mail] Error converting HTML to text:", error);
6466
+ console.error("[magic-mail] Error converting HTML to text:", error.message);
6413
6467
  return (html || "").replace(/<[^>]*>/g, "");
6414
6468
  }
6415
6469
  };
@@ -7032,8 +7086,14 @@ var analytics$1 = ({ strapi: strapi2 }) => ({
7032
7086
  * Generate secure hash for recipient (for tracking URLs)
7033
7087
  */
7034
7088
  generateRecipientHash(emailId, recipient) {
7089
+ const secret = process.env.APP_KEYS || process.env.MAGIC_MAIL_ENCRYPTION_KEY;
7090
+ if (!secret) {
7091
+ throw new Error(
7092
+ "[magic-mail] FATAL: No HMAC secret configured. Set APP_KEYS or MAGIC_MAIL_ENCRYPTION_KEY in your environment variables. Tracking hashes cannot be generated securely without a proper key."
7093
+ );
7094
+ }
7035
7095
  const normalized = (recipient || "").trim().toLowerCase();
7036
- return crypto.createHash("sha256").update(`${emailId}-${normalized}-${process.env.APP_KEYS || "secret"}`).digest("hex").substring(0, 16);
7096
+ return crypto.createHmac("sha256", secret).update(`${emailId}-${normalized}`).digest("hex").substring(0, 32);
7037
7097
  },
7038
7098
  /**
7039
7099
  * Create email log entry