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.mjs
CHANGED
|
@@ -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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
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,
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
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.
|
|
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.
|
|
1624
|
-
return;
|
|
1664
|
+
return ctx.forbidden(`Routing rule limit reached (${maxRules}). Upgrade to Advanced license for unlimited rules.`);
|
|
1625
1665
|
}
|
|
1626
|
-
const
|
|
1627
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2497
|
-
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
6306
|
-
log.info(` [
|
|
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
|
-
|
|
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.
|
|
7096
|
+
return crypto.createHmac("sha256", secret).update(`${emailId}-${normalized}`).digest("hex").substring(0, 32);
|
|
7037
7097
|
},
|
|
7038
7098
|
/**
|
|
7039
7099
|
* Create email log entry
|