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.
- package/dist/server/index.js +222 -162
- package/dist/server/index.mjs +222 -162
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -89,19 +89,23 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
89
89
|
try {
|
|
90
90
|
const licenseGuardService = strapi2.plugin("magic-mail").service("license-guard");
|
|
91
91
|
setTimeout(async () => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
try {
|
|
93
|
+
const licenseStatus = await licenseGuardService.initialize();
|
|
94
|
+
if (!licenseStatus.valid && licenseStatus.demo) {
|
|
95
|
+
log.error("╔════════════════════════════════════════════════════════════════╗");
|
|
96
|
+
log.error("║ [ERROR] MAGICMAIL - NO VALID LICENSE ║");
|
|
97
|
+
log.error("║ ║");
|
|
98
|
+
log.error("║ This plugin requires a valid license to operate. ║");
|
|
99
|
+
log.error("║ Please activate your license via Admin UI: ║");
|
|
100
|
+
log.error("║ Go to MagicMail -> License tab ║");
|
|
101
|
+
log.error("║ ║");
|
|
102
|
+
log.error('║ Click "Generate Free License" to get started! ║');
|
|
103
|
+
log.error("╚════════════════════════════════════════════════════════════════╝");
|
|
104
|
+
} else if (licenseStatus.gracePeriod) {
|
|
105
|
+
log.warn("[WARNING] Running on grace period (license server unreachable)");
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
log.error("[ERROR] License initialization failed:", err.message);
|
|
105
109
|
}
|
|
106
110
|
}, 2e3);
|
|
107
111
|
const accountManager2 = strapi2.plugin("magic-mail").service("account-manager");
|
|
@@ -140,14 +144,14 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
140
144
|
const hourlyResetInterval = setInterval(async () => {
|
|
141
145
|
try {
|
|
142
146
|
if (!strapi2 || !strapi2.plugin) {
|
|
143
|
-
|
|
147
|
+
strapi2.log.warn("[magic-mail] Strapi not available for hourly reset");
|
|
144
148
|
return;
|
|
145
149
|
}
|
|
146
150
|
const accountMgr = strapi2.plugin("magic-mail").service("account-manager");
|
|
147
151
|
await accountMgr.resetCounters("hourly");
|
|
148
152
|
log.info("[RESET] Hourly counters reset");
|
|
149
153
|
} catch (err) {
|
|
150
|
-
|
|
154
|
+
strapi2.log.error("[magic-mail] Hourly reset error:", err.message);
|
|
151
155
|
}
|
|
152
156
|
}, 60 * 60 * 1e3);
|
|
153
157
|
if (!commonjsGlobal.magicMailIntervals) commonjsGlobal.magicMailIntervals = {};
|
|
@@ -158,7 +162,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
158
162
|
setTimeout(async () => {
|
|
159
163
|
try {
|
|
160
164
|
if (!strapi2 || !strapi2.plugin) {
|
|
161
|
-
|
|
165
|
+
strapi2.log.warn("[magic-mail] Strapi not available for daily reset");
|
|
162
166
|
return;
|
|
163
167
|
}
|
|
164
168
|
const accountMgr = strapi2.plugin("magic-mail").service("account-manager");
|
|
@@ -167,19 +171,19 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
167
171
|
const dailyResetInterval = setInterval(async () => {
|
|
168
172
|
try {
|
|
169
173
|
if (!strapi2 || !strapi2.plugin) {
|
|
170
|
-
|
|
174
|
+
strapi2.log.warn("[magic-mail] Strapi not available for daily reset");
|
|
171
175
|
return;
|
|
172
176
|
}
|
|
173
177
|
const accountMgr2 = strapi2.plugin("magic-mail").service("account-manager");
|
|
174
178
|
await accountMgr2.resetCounters("daily");
|
|
175
179
|
log.info("[RESET] Daily counters reset");
|
|
176
180
|
} catch (err) {
|
|
177
|
-
|
|
181
|
+
strapi2.log.error("[magic-mail] Daily reset error:", err.message);
|
|
178
182
|
}
|
|
179
183
|
}, 24 * 60 * 60 * 1e3);
|
|
180
184
|
commonjsGlobal.magicMailIntervals.daily = dailyResetInterval;
|
|
181
185
|
} catch (err) {
|
|
182
|
-
|
|
186
|
+
strapi2.log.error("[magic-mail] Initial daily reset error:", err.message);
|
|
183
187
|
}
|
|
184
188
|
}, msUntilMidnight);
|
|
185
189
|
log.info("[SUCCESS] Counter reset schedules initialized");
|
|
@@ -447,7 +451,7 @@ const attributes$5 = {
|
|
|
447
451
|
type: "string"
|
|
448
452
|
},
|
|
449
453
|
accountId: {
|
|
450
|
-
type: "
|
|
454
|
+
type: "string"
|
|
451
455
|
},
|
|
452
456
|
accountName: {
|
|
453
457
|
type: "string"
|
|
@@ -876,62 +880,75 @@ var contentTypes$1 = {
|
|
|
876
880
|
schema: pluginSettings$4
|
|
877
881
|
}
|
|
878
882
|
};
|
|
883
|
+
function stripAttachmentPaths(body) {
|
|
884
|
+
if (body.attachments && Array.isArray(body.attachments)) {
|
|
885
|
+
body.attachments = body.attachments.map(({ path: path2, ...safe }) => safe);
|
|
886
|
+
}
|
|
887
|
+
return body;
|
|
888
|
+
}
|
|
879
889
|
var controller$1 = {
|
|
880
890
|
/**
|
|
881
891
|
* Send email via MagicMail router
|
|
882
892
|
*/
|
|
883
893
|
async send(ctx) {
|
|
884
894
|
try {
|
|
895
|
+
const body = stripAttachmentPaths({ ...ctx.request.body });
|
|
896
|
+
if (!body || !body.to) {
|
|
897
|
+
return ctx.badRequest("Recipient (to) is required");
|
|
898
|
+
}
|
|
885
899
|
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
886
|
-
const result = await emailRouter2.send(
|
|
900
|
+
const result = await emailRouter2.send(body);
|
|
887
901
|
ctx.body = {
|
|
888
902
|
success: true,
|
|
889
903
|
...result
|
|
890
904
|
};
|
|
891
905
|
} catch (err) {
|
|
892
|
-
strapi.log.error("[magic-mail] Error sending email:", err);
|
|
893
|
-
ctx.throw(500, err.message || "Failed to send email");
|
|
906
|
+
strapi.log.error("[magic-mail] Error sending email:", err.message);
|
|
907
|
+
ctx.throw(err.status || 500, err.message || "Failed to send email");
|
|
894
908
|
}
|
|
895
909
|
},
|
|
896
910
|
/**
|
|
897
911
|
* Send message via Email or WhatsApp (unified API)
|
|
898
|
-
* POST /api/magic-mail/send-message
|
|
899
|
-
* Body: { channel: 'email' | 'whatsapp' | 'auto', to, phoneNumber, subject, message, ... }
|
|
900
912
|
*/
|
|
901
913
|
async sendMessage(ctx) {
|
|
902
914
|
try {
|
|
915
|
+
const body = stripAttachmentPaths({ ...ctx.request.body });
|
|
916
|
+
if (!body || !body.to && !body.phoneNumber) {
|
|
917
|
+
return ctx.badRequest("Recipient (to or phoneNumber) is required");
|
|
918
|
+
}
|
|
903
919
|
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
904
|
-
const result = await emailRouter2.sendMessage(
|
|
920
|
+
const result = await emailRouter2.sendMessage(body);
|
|
905
921
|
ctx.body = {
|
|
906
922
|
success: true,
|
|
907
923
|
...result
|
|
908
924
|
};
|
|
909
925
|
} catch (err) {
|
|
910
|
-
strapi.log.error("[magic-mail] Error sending message:", err);
|
|
911
|
-
ctx.throw(500, err.message || "Failed to send message");
|
|
926
|
+
strapi.log.error("[magic-mail] Error sending message:", err.message);
|
|
927
|
+
ctx.throw(err.status || 500, err.message || "Failed to send message");
|
|
912
928
|
}
|
|
913
929
|
},
|
|
914
930
|
/**
|
|
915
931
|
* Send WhatsApp message
|
|
916
|
-
* POST /api/magic-mail/send-whatsapp
|
|
917
|
-
* Body: { phoneNumber, message, templateId?, templateData? }
|
|
918
932
|
*/
|
|
919
933
|
async sendWhatsApp(ctx) {
|
|
920
934
|
try {
|
|
935
|
+
const body = stripAttachmentPaths({ ...ctx.request.body });
|
|
936
|
+
if (!body || !body.phoneNumber) {
|
|
937
|
+
return ctx.badRequest("Phone number is required");
|
|
938
|
+
}
|
|
921
939
|
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
922
|
-
const result = await emailRouter2.sendWhatsApp(
|
|
940
|
+
const result = await emailRouter2.sendWhatsApp(body);
|
|
923
941
|
ctx.body = {
|
|
924
942
|
success: true,
|
|
925
943
|
...result
|
|
926
944
|
};
|
|
927
945
|
} catch (err) {
|
|
928
|
-
strapi.log.error("[magic-mail] Error sending WhatsApp:", err);
|
|
929
|
-
ctx.throw(500, err.message || "Failed to send WhatsApp message");
|
|
946
|
+
strapi.log.error("[magic-mail] Error sending WhatsApp:", err.message);
|
|
947
|
+
ctx.throw(err.status || 500, err.message || "Failed to send WhatsApp message");
|
|
930
948
|
}
|
|
931
949
|
},
|
|
932
950
|
/**
|
|
933
951
|
* Get WhatsApp connection status
|
|
934
|
-
* GET /api/magic-mail/whatsapp/status
|
|
935
952
|
*/
|
|
936
953
|
async getWhatsAppStatus(ctx) {
|
|
937
954
|
try {
|
|
@@ -942,7 +959,8 @@ var controller$1 = {
|
|
|
942
959
|
data: status
|
|
943
960
|
};
|
|
944
961
|
} catch (err) {
|
|
945
|
-
strapi.log.error("[magic-mail] Error getting WhatsApp status:", err);
|
|
962
|
+
strapi.log.error("[magic-mail] Error getting WhatsApp status:", err.message);
|
|
963
|
+
ctx.status = 503;
|
|
946
964
|
ctx.body = {
|
|
947
965
|
success: false,
|
|
948
966
|
data: {
|
|
@@ -955,14 +973,12 @@ var controller$1 = {
|
|
|
955
973
|
},
|
|
956
974
|
/**
|
|
957
975
|
* Check if phone number is on WhatsApp
|
|
958
|
-
* GET /api/magic-mail/whatsapp/check/:phoneNumber
|
|
959
976
|
*/
|
|
960
977
|
async checkWhatsAppNumber(ctx) {
|
|
961
978
|
try {
|
|
962
979
|
const { phoneNumber } = ctx.params;
|
|
963
980
|
if (!phoneNumber) {
|
|
964
|
-
ctx.
|
|
965
|
-
return;
|
|
981
|
+
return ctx.badRequest("Phone number is required");
|
|
966
982
|
}
|
|
967
983
|
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
968
984
|
const result = await emailRouter2.checkWhatsAppNumber(phoneNumber);
|
|
@@ -971,8 +987,8 @@ var controller$1 = {
|
|
|
971
987
|
data: result
|
|
972
988
|
};
|
|
973
989
|
} catch (err) {
|
|
974
|
-
strapi.log.error("[magic-mail] Error checking WhatsApp number:", err);
|
|
975
|
-
ctx.throw(500, err.message || "Failed to check phone number");
|
|
990
|
+
strapi.log.error("[magic-mail] Error checking WhatsApp number:", err.message);
|
|
991
|
+
ctx.throw(err.status || 500, err.message || "Failed to check phone number");
|
|
976
992
|
}
|
|
977
993
|
}
|
|
978
994
|
};
|
|
@@ -1030,12 +1046,15 @@ var accounts$1 = {
|
|
|
1030
1046
|
const { accountId } = ctx.params;
|
|
1031
1047
|
const accountManager2 = strapi.plugin("magic-mail").service("account-manager");
|
|
1032
1048
|
const account = await accountManager2.getAccountWithDecryptedConfig(accountId);
|
|
1049
|
+
if (!account) {
|
|
1050
|
+
return ctx.notFound("Email account not found");
|
|
1051
|
+
}
|
|
1033
1052
|
ctx.body = {
|
|
1034
1053
|
data: account
|
|
1035
1054
|
};
|
|
1036
1055
|
} catch (err) {
|
|
1037
|
-
strapi.log.error("[magic-mail] Error getting account:", err);
|
|
1038
|
-
ctx.throw(500, "Error fetching email account");
|
|
1056
|
+
strapi.log.error("[magic-mail] Error getting account:", err.message);
|
|
1057
|
+
ctx.throw(err.status || 500, err.message || "Error fetching email account");
|
|
1039
1058
|
}
|
|
1040
1059
|
},
|
|
1041
1060
|
/**
|
|
@@ -1046,33 +1065,40 @@ var accounts$1 = {
|
|
|
1046
1065
|
const { accountId } = ctx.params;
|
|
1047
1066
|
const accountManager2 = strapi.plugin("magic-mail").service("account-manager");
|
|
1048
1067
|
const account = await accountManager2.updateAccount(accountId, ctx.request.body);
|
|
1068
|
+
if (!account) {
|
|
1069
|
+
return ctx.notFound("Email account not found");
|
|
1070
|
+
}
|
|
1049
1071
|
ctx.body = {
|
|
1050
1072
|
data: account,
|
|
1051
1073
|
message: "Email account updated successfully"
|
|
1052
1074
|
};
|
|
1053
1075
|
} catch (err) {
|
|
1054
|
-
strapi.log.error("[magic-mail] Error updating account:", err);
|
|
1055
|
-
ctx.throw(500, err.message || "Error updating email account");
|
|
1076
|
+
strapi.log.error("[magic-mail] Error updating account:", err.message);
|
|
1077
|
+
ctx.throw(err.status || 500, err.message || "Error updating email account");
|
|
1056
1078
|
}
|
|
1057
1079
|
},
|
|
1058
1080
|
/**
|
|
1059
|
-
* Test email account
|
|
1081
|
+
* Test email account with validation
|
|
1060
1082
|
*/
|
|
1061
1083
|
async test(ctx) {
|
|
1062
1084
|
try {
|
|
1063
1085
|
const { accountId } = ctx.params;
|
|
1064
|
-
const { testEmail, priority, type, unsubscribeUrl } = ctx.request.body;
|
|
1086
|
+
const { testEmail, to, priority, type, unsubscribeUrl } = ctx.request.body;
|
|
1087
|
+
const recipientEmail = testEmail || to;
|
|
1088
|
+
if (!recipientEmail) {
|
|
1089
|
+
return ctx.badRequest("testEmail is required");
|
|
1090
|
+
}
|
|
1065
1091
|
const testOptions = {
|
|
1066
1092
|
priority: priority || "normal",
|
|
1067
1093
|
type: type || "transactional",
|
|
1068
1094
|
unsubscribeUrl: unsubscribeUrl || null
|
|
1069
1095
|
};
|
|
1070
1096
|
const accountManager2 = strapi.plugin("magic-mail").service("account-manager");
|
|
1071
|
-
const result = await accountManager2.testAccount(accountId,
|
|
1097
|
+
const result = await accountManager2.testAccount(accountId, recipientEmail, testOptions);
|
|
1072
1098
|
ctx.body = result;
|
|
1073
1099
|
} catch (err) {
|
|
1074
|
-
strapi.log.error("[magic-mail] Error testing account:", err);
|
|
1075
|
-
ctx.throw(500, "Error testing email account");
|
|
1100
|
+
strapi.log.error("[magic-mail] Error testing account:", err.message);
|
|
1101
|
+
ctx.throw(err.status || 500, err.message || "Error testing email account");
|
|
1076
1102
|
}
|
|
1077
1103
|
},
|
|
1078
1104
|
/**
|
|
@@ -1172,6 +1198,12 @@ var accounts$1 = {
|
|
|
1172
1198
|
}
|
|
1173
1199
|
}
|
|
1174
1200
|
};
|
|
1201
|
+
function escapeHtml(str) {
|
|
1202
|
+
return String(str || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1203
|
+
}
|
|
1204
|
+
function escapeJs(str) {
|
|
1205
|
+
return String(str || "").replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
1206
|
+
}
|
|
1175
1207
|
var oauth$3 = {
|
|
1176
1208
|
/**
|
|
1177
1209
|
* Initiate Gmail OAuth flow
|
|
@@ -1217,7 +1249,7 @@ var oauth$3 = {
|
|
|
1217
1249
|
</head>
|
|
1218
1250
|
<body>
|
|
1219
1251
|
<div class="error">[ERROR] OAuth Authorization Failed</div>
|
|
1220
|
-
<p>Error: ${error}</p>
|
|
1252
|
+
<p>Error: ${escapeHtml(error)}</p>
|
|
1221
1253
|
<p>You can close this window and try again.</p>
|
|
1222
1254
|
<script>
|
|
1223
1255
|
setTimeout(() => window.close(), 3000);
|
|
@@ -1258,15 +1290,15 @@ var oauth$3 = {
|
|
|
1258
1290
|
// Send data to parent window
|
|
1259
1291
|
window.opener.postMessage({
|
|
1260
1292
|
type: 'gmail-oauth-success',
|
|
1261
|
-
code: '${code}',
|
|
1262
|
-
state: '${state}'
|
|
1293
|
+
code: '${escapeJs(code)}',
|
|
1294
|
+
state: '${escapeJs(state)}'
|
|
1263
1295
|
}, window.location.origin);
|
|
1264
1296
|
|
|
1265
1297
|
setTimeout(() => window.close(), 1500);
|
|
1266
1298
|
} else {
|
|
1267
1299
|
// Fallback: redirect to admin panel
|
|
1268
1300
|
setTimeout(() => {
|
|
1269
|
-
window.location.href = '/admin/plugins/magic-mail?oauth_code
|
|
1301
|
+
window.location.href = '/admin/plugins/magic-mail?oauth_code=' + encodeURIComponent('${escapeJs(code)}') + '&oauth_state=' + encodeURIComponent('${escapeJs(state)}');
|
|
1270
1302
|
}, 2000);
|
|
1271
1303
|
}
|
|
1272
1304
|
<\/script>
|
|
@@ -1326,7 +1358,7 @@ var oauth$3 = {
|
|
|
1326
1358
|
</head>
|
|
1327
1359
|
<body>
|
|
1328
1360
|
<div class="error">[ERROR] OAuth Authorization Failed</div>
|
|
1329
|
-
<p>Error: ${error}</p>
|
|
1361
|
+
<p>Error: ${escapeHtml(error)}</p>
|
|
1330
1362
|
<p>You can close this window and try again.</p>
|
|
1331
1363
|
<script>
|
|
1332
1364
|
setTimeout(() => window.close(), 3000);
|
|
@@ -1367,15 +1399,15 @@ var oauth$3 = {
|
|
|
1367
1399
|
// Send data to parent window
|
|
1368
1400
|
window.opener.postMessage({
|
|
1369
1401
|
type: 'microsoft-oauth-success',
|
|
1370
|
-
code: '${code}',
|
|
1371
|
-
state: '${state}'
|
|
1402
|
+
code: '${escapeJs(code)}',
|
|
1403
|
+
state: '${escapeJs(state)}'
|
|
1372
1404
|
}, window.location.origin);
|
|
1373
1405
|
|
|
1374
1406
|
setTimeout(() => window.close(), 1500);
|
|
1375
1407
|
} else {
|
|
1376
1408
|
// Fallback: redirect to admin panel
|
|
1377
1409
|
setTimeout(() => {
|
|
1378
|
-
window.location.href = '/admin/plugins/magic-mail?oauth_code
|
|
1410
|
+
window.location.href = '/admin/plugins/magic-mail?oauth_code=' + encodeURIComponent('${escapeJs(code)}') + '&oauth_state=' + encodeURIComponent('${escapeJs(state)}');
|
|
1379
1411
|
}, 2000);
|
|
1380
1412
|
}
|
|
1381
1413
|
<\/script>
|
|
@@ -1431,7 +1463,7 @@ var oauth$3 = {
|
|
|
1431
1463
|
</head>
|
|
1432
1464
|
<body>
|
|
1433
1465
|
<div class="error">[ERROR] OAuth Authorization Failed</div>
|
|
1434
|
-
<p>Error: ${error}</p>
|
|
1466
|
+
<p>Error: ${escapeHtml(error)}</p>
|
|
1435
1467
|
<p>You can close this window and try again.</p>
|
|
1436
1468
|
<script>
|
|
1437
1469
|
setTimeout(() => window.close(), 3000);
|
|
@@ -1472,15 +1504,15 @@ var oauth$3 = {
|
|
|
1472
1504
|
// Send data to parent window
|
|
1473
1505
|
window.opener.postMessage({
|
|
1474
1506
|
type: 'yahoo-oauth-success',
|
|
1475
|
-
code: '${code}',
|
|
1476
|
-
state: '${state}'
|
|
1507
|
+
code: '${escapeJs(code)}',
|
|
1508
|
+
state: '${escapeJs(state)}'
|
|
1477
1509
|
}, window.location.origin);
|
|
1478
1510
|
|
|
1479
1511
|
setTimeout(() => window.close(), 1500);
|
|
1480
1512
|
} else {
|
|
1481
1513
|
// Fallback: redirect to admin panel
|
|
1482
1514
|
setTimeout(() => {
|
|
1483
|
-
window.location.href = '/admin/plugins/magic-mail?oauth_code
|
|
1515
|
+
window.location.href = '/admin/plugins/magic-mail?oauth_code=' + encodeURIComponent('${escapeJs(code)}') + '&oauth_state=' + encodeURIComponent('${escapeJs(state)}');
|
|
1484
1516
|
}, 2000);
|
|
1485
1517
|
}
|
|
1486
1518
|
<\/script>
|
|
@@ -1583,6 +1615,16 @@ var oauth$3 = {
|
|
|
1583
1615
|
}
|
|
1584
1616
|
};
|
|
1585
1617
|
const ROUTING_RULE_UID = "plugin::magic-mail.routing-rule";
|
|
1618
|
+
const ALLOWED_RULE_FIELDS = ["name", "conditions", "accountName", "priority", "isActive", "matchType", "matchField", "matchValue", "description"];
|
|
1619
|
+
function sanitizeRuleData(body) {
|
|
1620
|
+
const data = {};
|
|
1621
|
+
for (const field of ALLOWED_RULE_FIELDS) {
|
|
1622
|
+
if (body[field] !== void 0) {
|
|
1623
|
+
data[field] = body[field];
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
return data;
|
|
1627
|
+
}
|
|
1586
1628
|
var routingRules$1 = {
|
|
1587
1629
|
/**
|
|
1588
1630
|
* Get all routing rules
|
|
@@ -1590,15 +1632,14 @@ var routingRules$1 = {
|
|
|
1590
1632
|
async getAll(ctx) {
|
|
1591
1633
|
try {
|
|
1592
1634
|
const rules = await strapi.documents(ROUTING_RULE_UID).findMany({
|
|
1593
|
-
sort: [{ priority: "
|
|
1635
|
+
sort: [{ priority: "asc" }]
|
|
1594
1636
|
});
|
|
1595
1637
|
ctx.body = {
|
|
1596
|
-
data: rules
|
|
1597
|
-
meta: { count: rules.length }
|
|
1638
|
+
data: rules
|
|
1598
1639
|
};
|
|
1599
1640
|
} catch (err) {
|
|
1600
|
-
strapi.log.error("[magic-mail] Error getting routing rules:", err);
|
|
1601
|
-
ctx.throw(500, "Error fetching routing rules");
|
|
1641
|
+
strapi.log.error("[magic-mail] Error getting routing rules:", err.message);
|
|
1642
|
+
ctx.throw(err.status || 500, err.message || "Error fetching routing rules");
|
|
1602
1643
|
}
|
|
1603
1644
|
},
|
|
1604
1645
|
/**
|
|
@@ -1611,18 +1652,18 @@ var routingRules$1 = {
|
|
|
1611
1652
|
documentId: ruleId
|
|
1612
1653
|
});
|
|
1613
1654
|
if (!rule) {
|
|
1614
|
-
ctx.
|
|
1655
|
+
return ctx.notFound("Routing rule not found");
|
|
1615
1656
|
}
|
|
1616
1657
|
ctx.body = {
|
|
1617
1658
|
data: rule
|
|
1618
1659
|
};
|
|
1619
1660
|
} catch (err) {
|
|
1620
|
-
strapi.log.error("[magic-mail] Error getting routing rule:", err);
|
|
1621
|
-
ctx.throw(500, "Error fetching routing rule");
|
|
1661
|
+
strapi.log.error("[magic-mail] Error getting routing rule:", err.message);
|
|
1662
|
+
ctx.throw(err.status || 500, err.message || "Error fetching routing rule");
|
|
1622
1663
|
}
|
|
1623
1664
|
},
|
|
1624
1665
|
/**
|
|
1625
|
-
* Create new routing rule
|
|
1666
|
+
* Create new routing rule with input sanitization
|
|
1626
1667
|
*/
|
|
1627
1668
|
async create(ctx) {
|
|
1628
1669
|
try {
|
|
@@ -1630,31 +1671,34 @@ var routingRules$1 = {
|
|
|
1630
1671
|
const currentRules = await strapi.documents(ROUTING_RULE_UID).count();
|
|
1631
1672
|
const maxRules = await licenseGuard2.getMaxRoutingRules();
|
|
1632
1673
|
if (maxRules !== -1 && currentRules >= maxRules) {
|
|
1633
|
-
ctx.
|
|
1634
|
-
return;
|
|
1674
|
+
return ctx.forbidden(`Routing rule limit reached (${maxRules}). Upgrade to Advanced license for unlimited rules.`);
|
|
1635
1675
|
}
|
|
1636
|
-
const
|
|
1637
|
-
|
|
1638
|
-
});
|
|
1676
|
+
const data = sanitizeRuleData(ctx.request.body);
|
|
1677
|
+
const rule = await strapi.documents(ROUTING_RULE_UID).create({ data });
|
|
1639
1678
|
ctx.body = {
|
|
1640
1679
|
data: rule,
|
|
1641
1680
|
message: "Routing rule created successfully"
|
|
1642
1681
|
};
|
|
1643
1682
|
strapi.log.info(`[magic-mail] [SUCCESS] Routing rule created: ${rule.name}`);
|
|
1644
1683
|
} catch (err) {
|
|
1645
|
-
strapi.log.error("[magic-mail] Error creating routing rule:", err);
|
|
1684
|
+
strapi.log.error("[magic-mail] Error creating routing rule:", err.message);
|
|
1646
1685
|
ctx.throw(err.status || 500, err.message || "Error creating routing rule");
|
|
1647
1686
|
}
|
|
1648
1687
|
},
|
|
1649
1688
|
/**
|
|
1650
|
-
* Update routing rule
|
|
1689
|
+
* Update routing rule with existence check and input sanitization
|
|
1651
1690
|
*/
|
|
1652
1691
|
async update(ctx) {
|
|
1653
1692
|
try {
|
|
1654
1693
|
const { ruleId } = ctx.params;
|
|
1694
|
+
const existing = await strapi.documents(ROUTING_RULE_UID).findOne({ documentId: ruleId });
|
|
1695
|
+
if (!existing) {
|
|
1696
|
+
return ctx.notFound("Routing rule not found");
|
|
1697
|
+
}
|
|
1698
|
+
const data = sanitizeRuleData(ctx.request.body);
|
|
1655
1699
|
const rule = await strapi.documents(ROUTING_RULE_UID).update({
|
|
1656
1700
|
documentId: ruleId,
|
|
1657
|
-
data
|
|
1701
|
+
data
|
|
1658
1702
|
});
|
|
1659
1703
|
ctx.body = {
|
|
1660
1704
|
data: rule,
|
|
@@ -1662,16 +1706,20 @@ var routingRules$1 = {
|
|
|
1662
1706
|
};
|
|
1663
1707
|
strapi.log.info(`[magic-mail] [SUCCESS] Routing rule updated: ${rule.name}`);
|
|
1664
1708
|
} catch (err) {
|
|
1665
|
-
strapi.log.error("[magic-mail] Error updating routing rule:", err);
|
|
1666
|
-
ctx.throw(500, err.message || "Error updating routing rule");
|
|
1709
|
+
strapi.log.error("[magic-mail] Error updating routing rule:", err.message);
|
|
1710
|
+
ctx.throw(err.status || 500, err.message || "Error updating routing rule");
|
|
1667
1711
|
}
|
|
1668
1712
|
},
|
|
1669
1713
|
/**
|
|
1670
|
-
* Delete routing rule
|
|
1714
|
+
* Delete routing rule with existence check
|
|
1671
1715
|
*/
|
|
1672
1716
|
async delete(ctx) {
|
|
1673
1717
|
try {
|
|
1674
1718
|
const { ruleId } = ctx.params;
|
|
1719
|
+
const existing = await strapi.documents(ROUTING_RULE_UID).findOne({ documentId: ruleId });
|
|
1720
|
+
if (!existing) {
|
|
1721
|
+
return ctx.notFound("Routing rule not found");
|
|
1722
|
+
}
|
|
1675
1723
|
await strapi.documents(ROUTING_RULE_UID).delete({
|
|
1676
1724
|
documentId: ruleId
|
|
1677
1725
|
});
|
|
@@ -1680,8 +1728,8 @@ var routingRules$1 = {
|
|
|
1680
1728
|
};
|
|
1681
1729
|
strapi.log.info(`[magic-mail] Routing rule deleted: ${ruleId}`);
|
|
1682
1730
|
} catch (err) {
|
|
1683
|
-
strapi.log.error("[magic-mail] Error deleting routing rule:", err);
|
|
1684
|
-
ctx.throw(500, "Error deleting routing rule");
|
|
1731
|
+
strapi.log.error("[magic-mail] Error deleting routing rule:", err.message);
|
|
1732
|
+
ctx.throw(err.status || 500, err.message || "Error deleting routing rule");
|
|
1685
1733
|
}
|
|
1686
1734
|
}
|
|
1687
1735
|
};
|
|
@@ -1828,7 +1876,6 @@ function requireFeatures() {
|
|
|
1828
1876
|
*/
|
|
1829
1877
|
hasFeature(licenseData, featureName) {
|
|
1830
1878
|
if (!licenseData) {
|
|
1831
|
-
console.log(`[features.js] [WARNING] No license data → using FREE tier`);
|
|
1832
1879
|
return this.free.features.includes(featureName);
|
|
1833
1880
|
}
|
|
1834
1881
|
let isEnterprise = false;
|
|
@@ -1854,20 +1901,15 @@ function requireFeatures() {
|
|
|
1854
1901
|
isPremium = true;
|
|
1855
1902
|
}
|
|
1856
1903
|
if (isEnterprise && this.enterprise.features.includes(featureName)) {
|
|
1857
|
-
console.log(`[features.js] [SUCCESS] ENTERPRISE tier has feature "${featureName}"`);
|
|
1858
1904
|
return true;
|
|
1859
1905
|
}
|
|
1860
1906
|
if (isAdvanced && this.advanced.features.includes(featureName)) {
|
|
1861
|
-
console.log(`[features.js] [SUCCESS] ADVANCED tier has feature "${featureName}"`);
|
|
1862
1907
|
return true;
|
|
1863
1908
|
}
|
|
1864
1909
|
if (isPremium && this.premium.features.includes(featureName)) {
|
|
1865
|
-
console.log(`[features.js] [SUCCESS] PREMIUM tier has feature "${featureName}"`);
|
|
1866
1910
|
return true;
|
|
1867
1911
|
}
|
|
1868
|
-
|
|
1869
|
-
console.log(`[features.js] ${inFree ? "[SUCCESS]" : "[ERROR]"} FREE tier check for "${featureName}": ${inFree}`);
|
|
1870
|
-
return inFree;
|
|
1912
|
+
return this.free.features.includes(featureName);
|
|
1871
1913
|
},
|
|
1872
1914
|
/**
|
|
1873
1915
|
* Get max allowed accounts for license tier
|
|
@@ -1976,7 +2018,7 @@ var license$1 = ({ strapi: strapi2 }) => ({
|
|
|
1976
2018
|
valid: verification.valid,
|
|
1977
2019
|
demo: false,
|
|
1978
2020
|
data: {
|
|
1979
|
-
licenseKey,
|
|
2021
|
+
licenseKey: licenseKey ? licenseKey.substring(0, 8) + "..." + licenseKey.substring(licenseKey.length - 4) : null,
|
|
1980
2022
|
email: license2?.email || null,
|
|
1981
2023
|
firstName: license2?.firstName || null,
|
|
1982
2024
|
lastName: license2?.lastName || null,
|
|
@@ -2057,7 +2099,6 @@ var license$1 = ({ strapi: strapi2 }) => ({
|
|
|
2057
2099
|
const license2 = await licenseGuard2.getCurrentLicense();
|
|
2058
2100
|
ctx.body = {
|
|
2059
2101
|
success: true,
|
|
2060
|
-
rawLicense: license2,
|
|
2061
2102
|
detectedFlags: {
|
|
2062
2103
|
featurePremium: license2?.featurePremium,
|
|
2063
2104
|
featureAdvanced: license2?.featureAdvanced,
|
|
@@ -2138,7 +2179,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2138
2179
|
data: templates
|
|
2139
2180
|
});
|
|
2140
2181
|
} catch (error) {
|
|
2141
|
-
ctx.throw(500, error);
|
|
2182
|
+
ctx.throw(500, error.message);
|
|
2142
2183
|
}
|
|
2143
2184
|
},
|
|
2144
2185
|
/**
|
|
@@ -2156,7 +2197,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2156
2197
|
data: template
|
|
2157
2198
|
});
|
|
2158
2199
|
} catch (error) {
|
|
2159
|
-
ctx.throw(500, error);
|
|
2200
|
+
ctx.throw(500, error.message);
|
|
2160
2201
|
}
|
|
2161
2202
|
},
|
|
2162
2203
|
/**
|
|
@@ -2173,7 +2214,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2173
2214
|
if (error.message.includes("limit reached") || error.message.includes("already exists")) {
|
|
2174
2215
|
return ctx.badRequest(error.message);
|
|
2175
2216
|
}
|
|
2176
|
-
ctx.throw(500, error);
|
|
2217
|
+
ctx.throw(500, error.message);
|
|
2177
2218
|
}
|
|
2178
2219
|
},
|
|
2179
2220
|
/**
|
|
@@ -2191,7 +2232,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2191
2232
|
if (error.message.includes("not found")) {
|
|
2192
2233
|
return ctx.notFound(error.message);
|
|
2193
2234
|
}
|
|
2194
|
-
ctx.throw(500, error);
|
|
2235
|
+
ctx.throw(500, error.message);
|
|
2195
2236
|
}
|
|
2196
2237
|
},
|
|
2197
2238
|
/**
|
|
@@ -2206,7 +2247,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2206
2247
|
message: "Template deleted successfully"
|
|
2207
2248
|
});
|
|
2208
2249
|
} catch (error) {
|
|
2209
|
-
ctx.throw(500, error);
|
|
2250
|
+
ctx.throw(500, error.message);
|
|
2210
2251
|
}
|
|
2211
2252
|
},
|
|
2212
2253
|
/**
|
|
@@ -2221,7 +2262,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2221
2262
|
data: versions
|
|
2222
2263
|
});
|
|
2223
2264
|
} catch (error) {
|
|
2224
|
-
ctx.throw(500, error);
|
|
2265
|
+
ctx.throw(500, error.message);
|
|
2225
2266
|
}
|
|
2226
2267
|
},
|
|
2227
2268
|
/**
|
|
@@ -2239,7 +2280,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2239
2280
|
if (error.message.includes("not found")) {
|
|
2240
2281
|
return ctx.notFound(error.message);
|
|
2241
2282
|
}
|
|
2242
|
-
ctx.throw(500, error);
|
|
2283
|
+
ctx.throw(500, error.message);
|
|
2243
2284
|
}
|
|
2244
2285
|
},
|
|
2245
2286
|
/**
|
|
@@ -2260,7 +2301,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2260
2301
|
if (error.message.includes("does not belong")) {
|
|
2261
2302
|
return ctx.badRequest(error.message);
|
|
2262
2303
|
}
|
|
2263
|
-
ctx.throw(500, error);
|
|
2304
|
+
ctx.throw(500, error.message);
|
|
2264
2305
|
}
|
|
2265
2306
|
},
|
|
2266
2307
|
/**
|
|
@@ -2278,7 +2319,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2278
2319
|
if (error.message.includes("not found")) {
|
|
2279
2320
|
return ctx.notFound(error.message);
|
|
2280
2321
|
}
|
|
2281
|
-
ctx.throw(500, error);
|
|
2322
|
+
ctx.throw(500, error.message);
|
|
2282
2323
|
}
|
|
2283
2324
|
},
|
|
2284
2325
|
/**
|
|
@@ -2297,7 +2338,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2297
2338
|
if (error.message.includes("not found")) {
|
|
2298
2339
|
return ctx.notFound(error.message);
|
|
2299
2340
|
}
|
|
2300
|
-
ctx.throw(500, error);
|
|
2341
|
+
ctx.throw(500, error.message);
|
|
2301
2342
|
}
|
|
2302
2343
|
},
|
|
2303
2344
|
/**
|
|
@@ -2315,7 +2356,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2315
2356
|
if (error.message.includes("requires")) {
|
|
2316
2357
|
return ctx.forbidden(error.message);
|
|
2317
2358
|
}
|
|
2318
|
-
ctx.throw(500, error);
|
|
2359
|
+
ctx.throw(500, error.message);
|
|
2319
2360
|
}
|
|
2320
2361
|
},
|
|
2321
2362
|
/**
|
|
@@ -2336,7 +2377,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2336
2377
|
if (error.message.includes("requires")) {
|
|
2337
2378
|
return ctx.forbidden(error.message);
|
|
2338
2379
|
}
|
|
2339
|
-
ctx.throw(500, error);
|
|
2380
|
+
ctx.throw(500, error.message);
|
|
2340
2381
|
}
|
|
2341
2382
|
},
|
|
2342
2383
|
/**
|
|
@@ -2350,7 +2391,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2350
2391
|
data: stats
|
|
2351
2392
|
});
|
|
2352
2393
|
} catch (error) {
|
|
2353
|
-
ctx.throw(500, error);
|
|
2394
|
+
ctx.throw(500, error.message);
|
|
2354
2395
|
}
|
|
2355
2396
|
},
|
|
2356
2397
|
/**
|
|
@@ -2365,7 +2406,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2365
2406
|
data: template
|
|
2366
2407
|
});
|
|
2367
2408
|
} catch (error) {
|
|
2368
|
-
ctx.throw(500, error);
|
|
2409
|
+
ctx.throw(500, error.message);
|
|
2369
2410
|
}
|
|
2370
2411
|
},
|
|
2371
2412
|
/**
|
|
@@ -2380,7 +2421,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2380
2421
|
data: template
|
|
2381
2422
|
});
|
|
2382
2423
|
} catch (error) {
|
|
2383
|
-
ctx.throw(500, error);
|
|
2424
|
+
ctx.throw(500, error.message);
|
|
2384
2425
|
}
|
|
2385
2426
|
},
|
|
2386
2427
|
/**
|
|
@@ -2409,8 +2450,8 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2409
2450
|
ctx.set("Content-Disposition", `attachment; filename="${fileName}"`);
|
|
2410
2451
|
ctx.send(fileContent);
|
|
2411
2452
|
} catch (error) {
|
|
2412
|
-
strapi2.log.error("[magic-mail] Error downloading template:", error);
|
|
2413
|
-
ctx.throw(500, error);
|
|
2453
|
+
strapi2.log.error("[magic-mail] Error downloading template:", error.message);
|
|
2454
|
+
ctx.throw(500, error.message);
|
|
2414
2455
|
}
|
|
2415
2456
|
},
|
|
2416
2457
|
/**
|
|
@@ -2428,7 +2469,7 @@ var emailDesigner$3 = ({ strapi: strapi2 }) => ({
|
|
|
2428
2469
|
if (error.message.includes("not found")) {
|
|
2429
2470
|
return ctx.notFound(error.message);
|
|
2430
2471
|
}
|
|
2431
|
-
ctx.throw(500, error);
|
|
2472
|
+
ctx.throw(500, error.message);
|
|
2432
2473
|
}
|
|
2433
2474
|
},
|
|
2434
2475
|
/**
|
|
@@ -2489,7 +2530,11 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
|
|
|
2489
2530
|
*/
|
|
2490
2531
|
async trackOpen(ctx) {
|
|
2491
2532
|
const { emailId, recipientHash } = ctx.params;
|
|
2492
|
-
|
|
2533
|
+
try {
|
|
2534
|
+
await strapi2.plugin("magic-mail").service("analytics").recordOpen(emailId, recipientHash, ctx.request);
|
|
2535
|
+
} catch (err) {
|
|
2536
|
+
strapi2.log.error("[magic-mail] Error recording open event:", err.message);
|
|
2537
|
+
}
|
|
2493
2538
|
const pixel = Buffer.from(
|
|
2494
2539
|
"R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
|
2495
2540
|
"base64"
|
|
@@ -2498,20 +2543,34 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
|
|
|
2498
2543
|
ctx.body = pixel;
|
|
2499
2544
|
},
|
|
2500
2545
|
/**
|
|
2501
|
-
* Click tracking endpoint
|
|
2546
|
+
* Click tracking endpoint with open-redirect protection
|
|
2502
2547
|
* GET /magic-mail/track/click/:emailId/:linkHash/:recipientHash
|
|
2503
2548
|
*/
|
|
2504
2549
|
async trackClick(ctx) {
|
|
2505
2550
|
const { emailId, linkHash, recipientHash } = ctx.params;
|
|
2506
|
-
let
|
|
2507
|
-
|
|
2551
|
+
let url;
|
|
2552
|
+
try {
|
|
2508
2553
|
const analyticsService = strapi2.plugin("magic-mail").service("analytics");
|
|
2509
2554
|
url = await analyticsService.getOriginalUrlFromHash(emailId, linkHash);
|
|
2555
|
+
} catch (err) {
|
|
2556
|
+
strapi2.log.error("[magic-mail] Error getting original URL:", err.message);
|
|
2510
2557
|
}
|
|
2511
2558
|
if (!url) {
|
|
2512
|
-
return ctx.badRequest("
|
|
2559
|
+
return ctx.badRequest("Invalid or expired tracking link");
|
|
2560
|
+
}
|
|
2561
|
+
try {
|
|
2562
|
+
const parsed = new URL(url);
|
|
2563
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
2564
|
+
return ctx.badRequest("Invalid URL protocol");
|
|
2565
|
+
}
|
|
2566
|
+
} catch {
|
|
2567
|
+
return ctx.badRequest("Invalid URL format");
|
|
2568
|
+
}
|
|
2569
|
+
try {
|
|
2570
|
+
await strapi2.plugin("magic-mail").service("analytics").recordClick(emailId, linkHash, recipientHash, url, ctx.request);
|
|
2571
|
+
} catch (err) {
|
|
2572
|
+
strapi2.log.error("[magic-mail] Error recording click event:", err.message);
|
|
2513
2573
|
}
|
|
2514
|
-
await strapi2.plugin("magic-mail").service("analytics").recordClick(emailId, linkHash, recipientHash, url, ctx.request);
|
|
2515
2574
|
ctx.redirect(url);
|
|
2516
2575
|
},
|
|
2517
2576
|
/**
|
|
@@ -2535,7 +2594,7 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
|
|
|
2535
2594
|
data: stats
|
|
2536
2595
|
});
|
|
2537
2596
|
} catch (error) {
|
|
2538
|
-
ctx.throw(500, error);
|
|
2597
|
+
ctx.throw(500, error.message);
|
|
2539
2598
|
}
|
|
2540
2599
|
},
|
|
2541
2600
|
/**
|
|
@@ -2561,7 +2620,7 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
|
|
|
2561
2620
|
...result
|
|
2562
2621
|
});
|
|
2563
2622
|
} catch (error) {
|
|
2564
|
-
ctx.throw(500, error);
|
|
2623
|
+
ctx.throw(500, error.message);
|
|
2565
2624
|
}
|
|
2566
2625
|
},
|
|
2567
2626
|
/**
|
|
@@ -2580,7 +2639,7 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
|
|
|
2580
2639
|
data: emailLog2
|
|
2581
2640
|
});
|
|
2582
2641
|
} catch (error) {
|
|
2583
|
-
ctx.throw(500, error);
|
|
2642
|
+
ctx.throw(500, error.message);
|
|
2584
2643
|
}
|
|
2585
2644
|
},
|
|
2586
2645
|
/**
|
|
@@ -2597,7 +2656,7 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
|
|
|
2597
2656
|
data: activity
|
|
2598
2657
|
});
|
|
2599
2658
|
} catch (error) {
|
|
2600
|
-
ctx.throw(500, error);
|
|
2659
|
+
ctx.throw(500, error.message);
|
|
2601
2660
|
}
|
|
2602
2661
|
},
|
|
2603
2662
|
/**
|
|
@@ -2671,8 +2730,8 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
|
|
|
2671
2730
|
}
|
|
2672
2731
|
});
|
|
2673
2732
|
} catch (error) {
|
|
2674
|
-
strapi2.log.error("[magic-mail] Debug error:", error);
|
|
2675
|
-
ctx.throw(500, error);
|
|
2733
|
+
strapi2.log.error("[magic-mail] Debug error:", error.message);
|
|
2734
|
+
ctx.throw(500, error.message);
|
|
2676
2735
|
}
|
|
2677
2736
|
},
|
|
2678
2737
|
/**
|
|
@@ -2701,8 +2760,8 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
|
|
|
2701
2760
|
message: "Email log deleted successfully"
|
|
2702
2761
|
});
|
|
2703
2762
|
} catch (error) {
|
|
2704
|
-
strapi2.log.error("[magic-mail] Error deleting email log:", error);
|
|
2705
|
-
ctx.throw(500, error);
|
|
2763
|
+
strapi2.log.error("[magic-mail] Error deleting email log:", error.message);
|
|
2764
|
+
ctx.throw(500, error.message);
|
|
2706
2765
|
}
|
|
2707
2766
|
},
|
|
2708
2767
|
/**
|
|
@@ -2744,8 +2803,8 @@ var analytics$3 = ({ strapi: strapi2 }) => ({
|
|
|
2744
2803
|
deletedCount: emailLogs.length
|
|
2745
2804
|
});
|
|
2746
2805
|
} catch (error) {
|
|
2747
|
-
strapi2.log.error("[magic-mail] Error clearing email logs:", error);
|
|
2748
|
-
ctx.throw(500, error);
|
|
2806
|
+
strapi2.log.error("[magic-mail] Error clearing email logs:", error.message);
|
|
2807
|
+
ctx.throw(500, error.message);
|
|
2749
2808
|
}
|
|
2750
2809
|
}
|
|
2751
2810
|
});
|
|
@@ -2968,7 +3027,7 @@ var test$1 = {
|
|
|
2968
3027
|
} catch (error) {
|
|
2969
3028
|
console.error("\n[ERROR] FEHLER:", error.message);
|
|
2970
3029
|
console.error(error.stack);
|
|
2971
|
-
ctx.throw(500, error);
|
|
3030
|
+
ctx.throw(500, error.message);
|
|
2972
3031
|
}
|
|
2973
3032
|
}
|
|
2974
3033
|
};
|
|
@@ -3276,8 +3335,7 @@ var admin$1 = {
|
|
|
3276
3335
|
path: "/accounts",
|
|
3277
3336
|
handler: "accounts.create",
|
|
3278
3337
|
config: {
|
|
3279
|
-
policies: [],
|
|
3280
|
-
auth: false,
|
|
3338
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3281
3339
|
description: "Create email account"
|
|
3282
3340
|
}
|
|
3283
3341
|
},
|
|
@@ -3466,7 +3524,7 @@ var admin$1 = {
|
|
|
3466
3524
|
path: "/license/limits",
|
|
3467
3525
|
handler: "license.getLimits",
|
|
3468
3526
|
config: {
|
|
3469
|
-
policies: [],
|
|
3527
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3470
3528
|
description: "Get license limits and available features"
|
|
3471
3529
|
}
|
|
3472
3530
|
},
|
|
@@ -3475,7 +3533,7 @@ var admin$1 = {
|
|
|
3475
3533
|
path: "/license/debug",
|
|
3476
3534
|
handler: "license.debugLicense",
|
|
3477
3535
|
config: {
|
|
3478
|
-
policies: [],
|
|
3536
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3479
3537
|
description: "Debug license data"
|
|
3480
3538
|
}
|
|
3481
3539
|
},
|
|
@@ -3846,9 +3904,7 @@ var contentApi$1 = {
|
|
|
3846
3904
|
path: "/send",
|
|
3847
3905
|
handler: "controller.send",
|
|
3848
3906
|
config: {
|
|
3849
|
-
|
|
3850
|
-
// Can be called from anywhere
|
|
3851
|
-
description: "Send email via MagicMail router"
|
|
3907
|
+
description: "Send email via MagicMail router (requires API token)"
|
|
3852
3908
|
}
|
|
3853
3909
|
},
|
|
3854
3910
|
// ============= UNIFIED MESSAGE ROUTE =============
|
|
@@ -3857,8 +3913,7 @@ var contentApi$1 = {
|
|
|
3857
3913
|
path: "/send-message",
|
|
3858
3914
|
handler: "controller.sendMessage",
|
|
3859
3915
|
config: {
|
|
3860
|
-
|
|
3861
|
-
description: "Send message via Email or WhatsApp (unified API)"
|
|
3916
|
+
description: "Send message via Email or WhatsApp (requires API token)"
|
|
3862
3917
|
}
|
|
3863
3918
|
},
|
|
3864
3919
|
// ============= WHATSAPP ROUTES =============
|
|
@@ -3867,8 +3922,7 @@ var contentApi$1 = {
|
|
|
3867
3922
|
path: "/send-whatsapp",
|
|
3868
3923
|
handler: "controller.sendWhatsApp",
|
|
3869
3924
|
config: {
|
|
3870
|
-
|
|
3871
|
-
description: "Send WhatsApp message"
|
|
3925
|
+
description: "Send WhatsApp message (requires API token)"
|
|
3872
3926
|
}
|
|
3873
3927
|
},
|
|
3874
3928
|
{
|
|
@@ -3876,8 +3930,7 @@ var contentApi$1 = {
|
|
|
3876
3930
|
path: "/whatsapp/status",
|
|
3877
3931
|
handler: "controller.getWhatsAppStatus",
|
|
3878
3932
|
config: {
|
|
3879
|
-
|
|
3880
|
-
description: "Get WhatsApp connection status"
|
|
3933
|
+
description: "Get WhatsApp connection status (requires API token)"
|
|
3881
3934
|
}
|
|
3882
3935
|
},
|
|
3883
3936
|
{
|
|
@@ -3885,8 +3938,7 @@ var contentApi$1 = {
|
|
|
3885
3938
|
path: "/whatsapp/check/:phoneNumber",
|
|
3886
3939
|
handler: "controller.checkWhatsAppNumber",
|
|
3887
3940
|
config: {
|
|
3888
|
-
|
|
3889
|
-
description: "Check if phone number is on WhatsApp"
|
|
3941
|
+
description: "Check if phone number is on WhatsApp (requires API token)"
|
|
3890
3942
|
}
|
|
3891
3943
|
},
|
|
3892
3944
|
// ============= TRACKING ROUTES =============
|
|
@@ -3925,11 +3977,12 @@ const ALGORITHM = "aes-256-gcm";
|
|
|
3925
3977
|
const IV_LENGTH = 16;
|
|
3926
3978
|
function getEncryptionKey() {
|
|
3927
3979
|
const envKey = process.env.MAGIC_MAIL_ENCRYPTION_KEY || process.env.APP_KEYS;
|
|
3928
|
-
if (envKey) {
|
|
3929
|
-
|
|
3980
|
+
if (!envKey) {
|
|
3981
|
+
throw new Error(
|
|
3982
|
+
"[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."
|
|
3983
|
+
);
|
|
3930
3984
|
}
|
|
3931
|
-
|
|
3932
|
-
return crypto$2.createHash("sha256").update("magic-mail-default-key").digest();
|
|
3985
|
+
return crypto$2.createHash("sha256").update(envKey).digest();
|
|
3933
3986
|
}
|
|
3934
3987
|
function encryptCredentials$2(data) {
|
|
3935
3988
|
if (!data) return null;
|
|
@@ -3943,8 +3996,7 @@ function encryptCredentials$2(data) {
|
|
|
3943
3996
|
const authTag = cipher.getAuthTag();
|
|
3944
3997
|
return { encrypted: `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}` };
|
|
3945
3998
|
} catch (err) {
|
|
3946
|
-
|
|
3947
|
-
throw new Error("Failed to encrypt credentials");
|
|
3999
|
+
throw new Error(`Failed to encrypt credentials: ${err.message}`);
|
|
3948
4000
|
}
|
|
3949
4001
|
}
|
|
3950
4002
|
function decryptCredentials$2(encryptedData) {
|
|
@@ -3965,7 +4017,9 @@ function decryptCredentials$2(encryptedData) {
|
|
|
3965
4017
|
decrypted += decipher.final("utf8");
|
|
3966
4018
|
return JSON.parse(decrypted);
|
|
3967
4019
|
} catch (err) {
|
|
3968
|
-
|
|
4020
|
+
if (typeof strapi !== "undefined" && strapi.log) {
|
|
4021
|
+
strapi.log.error("[magic-mail] Decryption failed:", err.message);
|
|
4022
|
+
}
|
|
3969
4023
|
return null;
|
|
3970
4024
|
}
|
|
3971
4025
|
}
|
|
@@ -5390,8 +5444,8 @@ var accountManager$1 = ({ strapi: strapi2 }) => ({
|
|
|
5390
5444
|
dailyLimit = 0,
|
|
5391
5445
|
hourlyLimit = 0
|
|
5392
5446
|
} = accountData;
|
|
5393
|
-
|
|
5394
|
-
const encryptedConfig = encryptCredentials$1(config2);
|
|
5447
|
+
strapi2.log.info(`[magic-mail] Creating account: ${name} (${provider})`);
|
|
5448
|
+
const encryptedConfig = config2 ? encryptCredentials$1(config2) : null;
|
|
5395
5449
|
if (isPrimary) {
|
|
5396
5450
|
await this.unsetAllPrimary();
|
|
5397
5451
|
}
|
|
@@ -5444,7 +5498,7 @@ var accountManager$1 = ({ strapi: strapi2 }) => ({
|
|
|
5444
5498
|
dailyLimit,
|
|
5445
5499
|
hourlyLimit
|
|
5446
5500
|
} = accountData;
|
|
5447
|
-
const encryptedConfig = encryptCredentials$1(config2);
|
|
5501
|
+
const encryptedConfig = config2 ? encryptCredentials$1(config2) : existingAccount.config;
|
|
5448
5502
|
if (isPrimary && !existingAccount.isPrimary) {
|
|
5449
5503
|
await this.unsetAllPrimary();
|
|
5450
5504
|
}
|
|
@@ -5647,7 +5701,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
|
|
|
5647
5701
|
"https://www.googleapis.com/auth/userinfo.email",
|
|
5648
5702
|
"openid"
|
|
5649
5703
|
].join(" ");
|
|
5650
|
-
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}`;
|
|
5704
|
+
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)}`;
|
|
5651
5705
|
return authUrl;
|
|
5652
5706
|
},
|
|
5653
5707
|
/**
|
|
@@ -5727,7 +5781,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
|
|
|
5727
5781
|
const tokens = await response.json();
|
|
5728
5782
|
return {
|
|
5729
5783
|
accessToken: tokens.access_token,
|
|
5730
|
-
expiresAt: new Date(Date.now() + tokens.expires_in * 1e3)
|
|
5784
|
+
expiresAt: new Date(Date.now() + (tokens.expires_in || 3600) * 1e3)
|
|
5731
5785
|
};
|
|
5732
5786
|
},
|
|
5733
5787
|
/**
|
|
@@ -5756,7 +5810,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
|
|
|
5756
5810
|
"email"
|
|
5757
5811
|
// Email address
|
|
5758
5812
|
].join(" ");
|
|
5759
|
-
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}`;
|
|
5813
|
+
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)}`;
|
|
5760
5814
|
strapi2.log.info(`[magic-mail] Microsoft OAuth URL: Using tenant ${tenantId}`);
|
|
5761
5815
|
return authUrl;
|
|
5762
5816
|
},
|
|
@@ -5897,7 +5951,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
|
|
|
5897
5951
|
"sdps-r"
|
|
5898
5952
|
// Read profile
|
|
5899
5953
|
].join(" ");
|
|
5900
|
-
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}`;
|
|
5954
|
+
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)}`;
|
|
5901
5955
|
return authUrl;
|
|
5902
5956
|
},
|
|
5903
5957
|
/**
|
|
@@ -5981,7 +6035,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
|
|
|
5981
6035
|
const tokens = await response.json();
|
|
5982
6036
|
return {
|
|
5983
6037
|
accessToken: tokens.access_token,
|
|
5984
|
-
expiresAt: new Date(Date.now() + tokens.expires_in * 1e3)
|
|
6038
|
+
expiresAt: new Date(Date.now() + (tokens.expires_in || 3600) * 1e3)
|
|
5985
6039
|
};
|
|
5986
6040
|
},
|
|
5987
6041
|
/**
|
|
@@ -6032,7 +6086,7 @@ var oauth$1 = ({ strapi: strapi2 }) => ({
|
|
|
6032
6086
|
return account;
|
|
6033
6087
|
}
|
|
6034
6088
|
});
|
|
6035
|
-
const version = "2.6.
|
|
6089
|
+
const version = "2.6.8";
|
|
6036
6090
|
const require$$2 = {
|
|
6037
6091
|
version
|
|
6038
6092
|
};
|
|
@@ -6312,8 +6366,8 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => {
|
|
|
6312
6366
|
log.info("──────────────────────────────────────────────────────────");
|
|
6313
6367
|
log.info(`📦 Plugin Store Check:`);
|
|
6314
6368
|
if (licenseKey) {
|
|
6315
|
-
|
|
6316
|
-
log.info(` [
|
|
6369
|
+
const maskedKey = licenseKey.substring(0, 8) + "..." + licenseKey.substring(licenseKey.length - 4);
|
|
6370
|
+
log.info(` [SUCCESS] License Key found: ${maskedKey}`);
|
|
6317
6371
|
if (lastValidated) {
|
|
6318
6372
|
const lastValidatedDate = new Date(lastValidated);
|
|
6319
6373
|
const hoursAgo = Math.floor((now.getTime() - lastValidatedDate.getTime()) / (1e3 * 60 * 60));
|
|
@@ -6419,7 +6473,7 @@ const convertHtmlToText = (html, options2 = { wordwrap: 130 }) => {
|
|
|
6419
6473
|
}
|
|
6420
6474
|
return html.replace(/<[^>]*>/g, "");
|
|
6421
6475
|
} catch (error) {
|
|
6422
|
-
|
|
6476
|
+
console.error("[magic-mail] Error converting HTML to text:", error.message);
|
|
6423
6477
|
return (html || "").replace(/<[^>]*>/g, "");
|
|
6424
6478
|
}
|
|
6425
6479
|
};
|
|
@@ -7042,8 +7096,14 @@ var analytics$1 = ({ strapi: strapi2 }) => ({
|
|
|
7042
7096
|
* Generate secure hash for recipient (for tracking URLs)
|
|
7043
7097
|
*/
|
|
7044
7098
|
generateRecipientHash(emailId, recipient) {
|
|
7099
|
+
const secret = process.env.APP_KEYS || process.env.MAGIC_MAIL_ENCRYPTION_KEY;
|
|
7100
|
+
if (!secret) {
|
|
7101
|
+
throw new Error(
|
|
7102
|
+
"[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."
|
|
7103
|
+
);
|
|
7104
|
+
}
|
|
7045
7105
|
const normalized = (recipient || "").trim().toLowerCase();
|
|
7046
|
-
return crypto.
|
|
7106
|
+
return crypto.createHmac("sha256", secret).update(`${emailId}-${normalized}`).digest("hex").substring(0, 32);
|
|
7047
7107
|
},
|
|
7048
7108
|
/**
|
|
7049
7109
|
* Create email log entry
|