strapi-plugin-magic-mail 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -17
- package/admin/src/index.js +26 -25
- package/admin/src/pages/Analytics.jsx +19 -19
- package/admin/src/pages/EmailDesigner/EditorPage.jsx +33 -33
- package/admin/src/pages/EmailDesigner/TemplateList.jsx +36 -36
- package/admin/src/pages/HomePage.jsx +25 -88
- package/admin/src/pages/LicensePage.jsx +12 -6
- package/admin/src/pages/RoutingRules.jsx +21 -21
- package/admin/src/pages/Settings.jsx +3 -3
- package/admin/src/utils/theme.js +85 -0
- package/dist/_chunks/{App-DYNCyt54.mjs → App-BZaHrE0R.mjs} +184 -200
- package/dist/_chunks/{App-DF9vAGXX.js → App-Bze8Ixs_.js} +184 -200
- package/dist/_chunks/{LicensePage-CJXwPnEe.js → LicensePage-Bg72gy8w.js} +12 -6
- package/dist/_chunks/{LicensePage-Bl02myMx.mjs → LicensePage-ndUhjynY.mjs} +12 -6
- package/dist/_chunks/{Settings-zuFQ3pnn.js → Settings-BSFLpt0H.js} +3 -7
- package/dist/_chunks/{Settings-C_TmKwcz.mjs → Settings-Ca5UE3c1.mjs} +3 -7
- package/dist/admin/index.js +41 -24
- package/dist/admin/index.mjs +41 -24
- package/dist/server/index.js +602 -665
- package/dist/server/index.mjs +602 -665
- package/package.json +1 -1
- package/server/src/bootstrap.js +16 -16
- package/server/src/config/features.js +7 -7
- package/server/src/controllers/accounts.js +15 -6
- package/server/src/controllers/analytics.js +56 -42
- package/server/src/controllers/license.js +9 -7
- package/server/src/controllers/oauth.js +9 -9
- package/server/src/controllers/routing-rules.js +18 -11
- package/server/src/controllers/test.js +111 -193
- package/server/src/services/account-manager.js +73 -21
- package/server/src/services/analytics.js +88 -72
- package/server/src/services/email-designer.js +131 -284
- package/server/src/services/email-router.js +69 -43
- package/server/src/services/license-guard.js +24 -24
- package/server/src/services/oauth.js +11 -11
- package/server/src/services/service.js +1 -1
- package/server/src/utils/encryption.js +1 -1
package/dist/server/index.js
CHANGED
|
@@ -31,14 +31,14 @@ function requireBootstrap() {
|
|
|
31
31
|
if (hasRequiredBootstrap) return bootstrap;
|
|
32
32
|
hasRequiredBootstrap = 1;
|
|
33
33
|
bootstrap = async ({ strapi: strapi2 }) => {
|
|
34
|
-
strapi2.log.info("
|
|
34
|
+
strapi2.log.info("[BOOTSTRAP] [magic-mail] Starting...");
|
|
35
35
|
try {
|
|
36
36
|
const licenseGuardService = strapi2.plugin("magic-mail").service("license-guard");
|
|
37
37
|
setTimeout(async () => {
|
|
38
38
|
const licenseStatus = await licenseGuardService.initialize();
|
|
39
39
|
if (!licenseStatus.valid && licenseStatus.demo) {
|
|
40
40
|
strapi2.log.error("╔════════════════════════════════════════════════════════════════╗");
|
|
41
|
-
strapi2.log.error("║
|
|
41
|
+
strapi2.log.error("║ [ERROR] MAGICMAIL - NO VALID LICENSE ║");
|
|
42
42
|
strapi2.log.error("║ ║");
|
|
43
43
|
strapi2.log.error("║ This plugin requires a valid license to operate. ║");
|
|
44
44
|
strapi2.log.error("║ Please activate your license via Admin UI: ║");
|
|
@@ -47,7 +47,7 @@ function requireBootstrap() {
|
|
|
47
47
|
strapi2.log.error('║ Click "Generate Free License" to get started! ║');
|
|
48
48
|
strapi2.log.error("╚════════════════════════════════════════════════════════════════╝");
|
|
49
49
|
} else if (licenseStatus.gracePeriod) {
|
|
50
|
-
strapi2.log.warn("
|
|
50
|
+
strapi2.log.warn("[WARNING] Running on grace period (license server unreachable)");
|
|
51
51
|
}
|
|
52
52
|
}, 2e3);
|
|
53
53
|
const accountManager2 = strapi2.plugin("magic-mail").service("account-manager");
|
|
@@ -56,7 +56,7 @@ function requireBootstrap() {
|
|
|
56
56
|
if (originalEmailService && originalEmailService.send) {
|
|
57
57
|
const originalSend = originalEmailService.send.bind(originalEmailService);
|
|
58
58
|
originalEmailService.send = async (emailData) => {
|
|
59
|
-
strapi2.log.info("[magic-mail]
|
|
59
|
+
strapi2.log.info("[magic-mail] [EMAIL] Intercepted from native Strapi service");
|
|
60
60
|
strapi2.log.debug("[magic-mail] Email data:", {
|
|
61
61
|
to: emailData.to,
|
|
62
62
|
subject: emailData.subject,
|
|
@@ -69,19 +69,19 @@ function requireBootstrap() {
|
|
|
69
69
|
emailData.templateData = emailData.data;
|
|
70
70
|
}
|
|
71
71
|
const result = await emailRouter2.send(emailData);
|
|
72
|
-
strapi2.log.info("[magic-mail]
|
|
72
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email routed successfully through MagicMail");
|
|
73
73
|
return result;
|
|
74
74
|
} catch (magicMailError) {
|
|
75
|
-
strapi2.log.warn("[magic-mail]
|
|
75
|
+
strapi2.log.warn("[magic-mail] [WARNING] MagicMail routing failed, falling back to original service");
|
|
76
76
|
strapi2.log.error("[magic-mail] Error:", magicMailError.message);
|
|
77
77
|
return await originalSend(emailData);
|
|
78
78
|
}
|
|
79
79
|
};
|
|
80
|
-
strapi2.log.info("[magic-mail]
|
|
81
|
-
strapi2.log.info("[magic-mail]
|
|
80
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Native email service overridden!");
|
|
81
|
+
strapi2.log.info("[magic-mail] [INFO] All strapi.plugins.email.services.email.send() calls will route through MagicMail");
|
|
82
82
|
} else {
|
|
83
|
-
strapi2.log.warn("[magic-mail]
|
|
84
|
-
strapi2.log.warn("[magic-mail]
|
|
83
|
+
strapi2.log.warn("[magic-mail] [WARNING] Native email service not found - MagicMail will work standalone");
|
|
84
|
+
strapi2.log.warn("[magic-mail] [INFO] Make sure @strapi/plugin-email is installed");
|
|
85
85
|
}
|
|
86
86
|
const hourlyResetInterval = setInterval(async () => {
|
|
87
87
|
try {
|
|
@@ -91,7 +91,7 @@ function requireBootstrap() {
|
|
|
91
91
|
}
|
|
92
92
|
const accountMgr = strapi2.plugin("magic-mail").service("account-manager");
|
|
93
93
|
await accountMgr.resetCounters("hourly");
|
|
94
|
-
strapi2.log.info("[magic-mail]
|
|
94
|
+
strapi2.log.info("[magic-mail] [RESET] Hourly counters reset");
|
|
95
95
|
} catch (err) {
|
|
96
96
|
console.error("[magic-mail] Hourly reset error:", err.message);
|
|
97
97
|
}
|
|
@@ -109,7 +109,7 @@ function requireBootstrap() {
|
|
|
109
109
|
}
|
|
110
110
|
const accountMgr = strapi2.plugin("magic-mail").service("account-manager");
|
|
111
111
|
await accountMgr.resetCounters("daily");
|
|
112
|
-
strapi2.log.info("[magic-mail]
|
|
112
|
+
strapi2.log.info("[magic-mail] [RESET] Daily counters reset");
|
|
113
113
|
const dailyResetInterval = setInterval(async () => {
|
|
114
114
|
try {
|
|
115
115
|
if (!strapi2 || !strapi2.plugin) {
|
|
@@ -118,7 +118,7 @@ function requireBootstrap() {
|
|
|
118
118
|
}
|
|
119
119
|
const accountMgr2 = strapi2.plugin("magic-mail").service("account-manager");
|
|
120
120
|
await accountMgr2.resetCounters("daily");
|
|
121
|
-
strapi2.log.info("[magic-mail]
|
|
121
|
+
strapi2.log.info("[magic-mail] [RESET] Daily counters reset");
|
|
122
122
|
} catch (err) {
|
|
123
123
|
console.error("[magic-mail] Daily reset error:", err.message);
|
|
124
124
|
}
|
|
@@ -128,10 +128,10 @@ function requireBootstrap() {
|
|
|
128
128
|
console.error("[magic-mail] Initial daily reset error:", err.message);
|
|
129
129
|
}
|
|
130
130
|
}, msUntilMidnight);
|
|
131
|
-
strapi2.log.info("[magic-mail]
|
|
132
|
-
strapi2.log.info("[magic-mail]
|
|
131
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Counter reset schedules initialized");
|
|
132
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Bootstrap complete");
|
|
133
133
|
} catch (err) {
|
|
134
|
-
strapi2.log.error("[magic-mail]
|
|
134
|
+
strapi2.log.error("[magic-mail] [ERROR] Bootstrap error:", err);
|
|
135
135
|
}
|
|
136
136
|
};
|
|
137
137
|
return bootstrap;
|
|
@@ -366,7 +366,7 @@ function requireAccounts() {
|
|
|
366
366
|
ctx.throw(403, `Provider "${accountData.provider}" requires a Premium license or higher. Please upgrade your license.`);
|
|
367
367
|
return;
|
|
368
368
|
}
|
|
369
|
-
const currentAccounts = await strapi.
|
|
369
|
+
const currentAccounts = await strapi.documents("plugin::magic-mail.email-account").count();
|
|
370
370
|
const maxAccounts = await licenseGuard2.getMaxAccounts();
|
|
371
371
|
if (maxAccounts !== -1 && currentAccounts >= maxAccounts) {
|
|
372
372
|
ctx.throw(403, `Account limit reached (${maxAccounts}). Upgrade your license to add more accounts.`);
|
|
@@ -447,9 +447,9 @@ function requireAccounts() {
|
|
|
447
447
|
ctx.throw(400, "testEmail is required");
|
|
448
448
|
}
|
|
449
449
|
strapi.log.info("[magic-mail] 🧪 Testing Strapi Email Service integration...");
|
|
450
|
-
strapi.log.info('[magic-mail]
|
|
450
|
+
strapi.log.info('[magic-mail] [EMAIL] Calling strapi.plugin("email").service("email").send()');
|
|
451
451
|
if (accountName) {
|
|
452
|
-
strapi.log.info(`[magic-mail]
|
|
452
|
+
strapi.log.info(`[magic-mail] [FORCE] Forcing specific account: ${accountName}`);
|
|
453
453
|
}
|
|
454
454
|
const result = await strapi.plugin("email").service("email").send({
|
|
455
455
|
to: testEmail,
|
|
@@ -476,6 +476,15 @@ function requireAccounts() {
|
|
|
476
476
|
<li>Statistics tracking</li>
|
|
477
477
|
</ul>
|
|
478
478
|
</div>
|
|
479
|
+
<div style="background: #DCFCE7; border: 1px solid #22C55E; border-radius: 8px; padding: 15px; margin: 20px 0;">
|
|
480
|
+
<h3 style="margin-top: 0; color: #15803D;">Security Features Active</h3>
|
|
481
|
+
<ul style="margin: 10px 0; padding-left: 20px;">
|
|
482
|
+
<li>TLS/SSL Encryption enforced</li>
|
|
483
|
+
<li>Email content validated</li>
|
|
484
|
+
<li>Proper headers included</li>
|
|
485
|
+
<li>Message-ID generated</li>
|
|
486
|
+
</ul>
|
|
487
|
+
</div>
|
|
479
488
|
<p style="color: #6B7280; font-size: 14px; margin-top: 30px;">
|
|
480
489
|
Sent at: ${(/* @__PURE__ */ new Date()).toLocaleString()}<br>
|
|
481
490
|
Via: MagicMail Email Router
|
|
@@ -486,7 +495,7 @@ function requireAccounts() {
|
|
|
486
495
|
accountName: accountName || null
|
|
487
496
|
// Force specific account if provided
|
|
488
497
|
});
|
|
489
|
-
strapi.log.info("[magic-mail]
|
|
498
|
+
strapi.log.info("[magic-mail] [SUCCESS] Strapi Email Service test completed");
|
|
490
499
|
ctx.body = {
|
|
491
500
|
success: true,
|
|
492
501
|
message: "Email sent via Strapi Email Service (intercepted by MagicMail)",
|
|
@@ -498,7 +507,7 @@ function requireAccounts() {
|
|
|
498
507
|
}
|
|
499
508
|
};
|
|
500
509
|
} catch (err) {
|
|
501
|
-
strapi.log.error("[magic-mail]
|
|
510
|
+
strapi.log.error("[magic-mail] [ERROR] Strapi Email Service test failed:", err);
|
|
502
511
|
ctx.body = {
|
|
503
512
|
success: false,
|
|
504
513
|
message: "Failed to send test email",
|
|
@@ -575,7 +584,7 @@ function requireOauth$1() {
|
|
|
575
584
|
</style>
|
|
576
585
|
</head>
|
|
577
586
|
<body>
|
|
578
|
-
<div class="error"
|
|
587
|
+
<div class="error">[ERROR] OAuth Authorization Failed</div>
|
|
579
588
|
<p>Error: ${error}</p>
|
|
580
589
|
<p>You can close this window and try again.</p>
|
|
581
590
|
<script>
|
|
@@ -609,7 +618,7 @@ function requireOauth$1() {
|
|
|
609
618
|
</style>
|
|
610
619
|
</head>
|
|
611
620
|
<body>
|
|
612
|
-
<div class="success"
|
|
621
|
+
<div class="success">[SUCCESS]</div>
|
|
613
622
|
<div class="message">Gmail OAuth Authorized!</div>
|
|
614
623
|
<div class="note">Closing window...</div>
|
|
615
624
|
<script>
|
|
@@ -684,7 +693,7 @@ function requireOauth$1() {
|
|
|
684
693
|
</style>
|
|
685
694
|
</head>
|
|
686
695
|
<body>
|
|
687
|
-
<div class="error"
|
|
696
|
+
<div class="error">[ERROR] OAuth Authorization Failed</div>
|
|
688
697
|
<p>Error: ${error}</p>
|
|
689
698
|
<p>You can close this window and try again.</p>
|
|
690
699
|
<script>
|
|
@@ -718,7 +727,7 @@ function requireOauth$1() {
|
|
|
718
727
|
</style>
|
|
719
728
|
</head>
|
|
720
729
|
<body>
|
|
721
|
-
<div class="success"
|
|
730
|
+
<div class="success">[SUCCESS]</div>
|
|
722
731
|
<div class="message">Microsoft OAuth Authorized!</div>
|
|
723
732
|
<div class="note">Closing window...</div>
|
|
724
733
|
<script>
|
|
@@ -789,7 +798,7 @@ function requireOauth$1() {
|
|
|
789
798
|
</style>
|
|
790
799
|
</head>
|
|
791
800
|
<body>
|
|
792
|
-
<div class="error"
|
|
801
|
+
<div class="error">[ERROR] OAuth Authorization Failed</div>
|
|
793
802
|
<p>Error: ${error}</p>
|
|
794
803
|
<p>You can close this window and try again.</p>
|
|
795
804
|
<script>
|
|
@@ -823,7 +832,7 @@ function requireOauth$1() {
|
|
|
823
832
|
</style>
|
|
824
833
|
</head>
|
|
825
834
|
<body>
|
|
826
|
-
<div class="success"
|
|
835
|
+
<div class="success">[SUCCESS]</div>
|
|
827
836
|
<div class="message">Yahoo Mail OAuth Authorized!</div>
|
|
828
837
|
<div class="note">Closing window...</div>
|
|
829
838
|
<script>
|
|
@@ -873,7 +882,7 @@ function requireOauth$1() {
|
|
|
873
882
|
ctx.throw(403, `OAuth provider "${provider}" requires a Premium license or higher. Please upgrade your license.`);
|
|
874
883
|
return;
|
|
875
884
|
}
|
|
876
|
-
const currentAccounts = await strapi.
|
|
885
|
+
const currentAccounts = await strapi.documents("plugin::magic-mail.email-account").count();
|
|
877
886
|
const maxAccounts = await licenseGuard2.getMaxAccounts();
|
|
878
887
|
if (maxAccounts !== -1 && currentAccounts >= maxAccounts) {
|
|
879
888
|
ctx.throw(403, `Account limit reached (${maxAccounts}). Upgrade your license to add more accounts.`);
|
|
@@ -928,7 +937,7 @@ function requireOauth$1() {
|
|
|
928
937
|
accountDetails.config
|
|
929
938
|
// contains clientId and clientSecret
|
|
930
939
|
);
|
|
931
|
-
strapi.log.info("[magic-mail]
|
|
940
|
+
strapi.log.info("[magic-mail] [SUCCESS] OAuth account created successfully");
|
|
932
941
|
ctx.body = {
|
|
933
942
|
success: true,
|
|
934
943
|
data: account,
|
|
@@ -948,14 +957,15 @@ var hasRequiredRoutingRules;
|
|
|
948
957
|
function requireRoutingRules() {
|
|
949
958
|
if (hasRequiredRoutingRules) return routingRules;
|
|
950
959
|
hasRequiredRoutingRules = 1;
|
|
960
|
+
const ROUTING_RULE_UID = "plugin::magic-mail.routing-rule";
|
|
951
961
|
routingRules = {
|
|
952
962
|
/**
|
|
953
963
|
* Get all routing rules
|
|
954
964
|
*/
|
|
955
965
|
async getAll(ctx) {
|
|
956
966
|
try {
|
|
957
|
-
const rules = await strapi.
|
|
958
|
-
sort: { priority: "desc" }
|
|
967
|
+
const rules = await strapi.documents(ROUTING_RULE_UID).findMany({
|
|
968
|
+
sort: [{ priority: "desc" }]
|
|
959
969
|
});
|
|
960
970
|
ctx.body = {
|
|
961
971
|
data: rules,
|
|
@@ -972,7 +982,9 @@ function requireRoutingRules() {
|
|
|
972
982
|
async getOne(ctx) {
|
|
973
983
|
try {
|
|
974
984
|
const { ruleId } = ctx.params;
|
|
975
|
-
const rule = await strapi.
|
|
985
|
+
const rule = await strapi.documents(ROUTING_RULE_UID).findOne({
|
|
986
|
+
documentId: ruleId
|
|
987
|
+
});
|
|
976
988
|
if (!rule) {
|
|
977
989
|
ctx.throw(404, "Routing rule not found");
|
|
978
990
|
}
|
|
@@ -990,20 +1002,20 @@ function requireRoutingRules() {
|
|
|
990
1002
|
async create(ctx) {
|
|
991
1003
|
try {
|
|
992
1004
|
const licenseGuard2 = strapi.plugin("magic-mail").service("license-guard");
|
|
993
|
-
const currentRules = await strapi.
|
|
1005
|
+
const currentRules = await strapi.documents(ROUTING_RULE_UID).count();
|
|
994
1006
|
const maxRules = await licenseGuard2.getMaxRoutingRules();
|
|
995
1007
|
if (maxRules !== -1 && currentRules >= maxRules) {
|
|
996
1008
|
ctx.throw(403, `Routing rule limit reached (${maxRules}). Upgrade to Advanced license for unlimited rules.`);
|
|
997
1009
|
return;
|
|
998
1010
|
}
|
|
999
|
-
const rule = await strapi.
|
|
1011
|
+
const rule = await strapi.documents(ROUTING_RULE_UID).create({
|
|
1000
1012
|
data: ctx.request.body
|
|
1001
1013
|
});
|
|
1002
1014
|
ctx.body = {
|
|
1003
1015
|
data: rule,
|
|
1004
1016
|
message: "Routing rule created successfully"
|
|
1005
1017
|
};
|
|
1006
|
-
strapi.log.info(`[magic-mail]
|
|
1018
|
+
strapi.log.info(`[magic-mail] [SUCCESS] Routing rule created: ${rule.name}`);
|
|
1007
1019
|
} catch (err) {
|
|
1008
1020
|
strapi.log.error("[magic-mail] Error creating routing rule:", err);
|
|
1009
1021
|
ctx.throw(err.status || 500, err.message || "Error creating routing rule");
|
|
@@ -1015,14 +1027,15 @@ function requireRoutingRules() {
|
|
|
1015
1027
|
async update(ctx) {
|
|
1016
1028
|
try {
|
|
1017
1029
|
const { ruleId } = ctx.params;
|
|
1018
|
-
const rule = await strapi.
|
|
1030
|
+
const rule = await strapi.documents(ROUTING_RULE_UID).update({
|
|
1031
|
+
documentId: ruleId,
|
|
1019
1032
|
data: ctx.request.body
|
|
1020
1033
|
});
|
|
1021
1034
|
ctx.body = {
|
|
1022
1035
|
data: rule,
|
|
1023
1036
|
message: "Routing rule updated successfully"
|
|
1024
1037
|
};
|
|
1025
|
-
strapi.log.info(`[magic-mail]
|
|
1038
|
+
strapi.log.info(`[magic-mail] [SUCCESS] Routing rule updated: ${rule.name}`);
|
|
1026
1039
|
} catch (err) {
|
|
1027
1040
|
strapi.log.error("[magic-mail] Error updating routing rule:", err);
|
|
1028
1041
|
ctx.throw(500, err.message || "Error updating routing rule");
|
|
@@ -1034,7 +1047,9 @@ function requireRoutingRules() {
|
|
|
1034
1047
|
async delete(ctx) {
|
|
1035
1048
|
try {
|
|
1036
1049
|
const { ruleId } = ctx.params;
|
|
1037
|
-
await strapi.
|
|
1050
|
+
await strapi.documents(ROUTING_RULE_UID).delete({
|
|
1051
|
+
documentId: ruleId
|
|
1052
|
+
});
|
|
1038
1053
|
ctx.body = {
|
|
1039
1054
|
message: "Routing rule deleted successfully"
|
|
1040
1055
|
};
|
|
@@ -1190,7 +1205,7 @@ function requireFeatures() {
|
|
|
1190
1205
|
*/
|
|
1191
1206
|
hasFeature(licenseData, featureName) {
|
|
1192
1207
|
if (!licenseData) {
|
|
1193
|
-
console.log(`[features.js]
|
|
1208
|
+
console.log(`[features.js] [WARNING] No license data → using FREE tier`);
|
|
1194
1209
|
return this.free.features.includes(featureName);
|
|
1195
1210
|
}
|
|
1196
1211
|
let isEnterprise = false;
|
|
@@ -1216,19 +1231,19 @@ function requireFeatures() {
|
|
|
1216
1231
|
isPremium = true;
|
|
1217
1232
|
}
|
|
1218
1233
|
if (isEnterprise && this.enterprise.features.includes(featureName)) {
|
|
1219
|
-
console.log(`[features.js]
|
|
1234
|
+
console.log(`[features.js] [SUCCESS] ENTERPRISE tier has feature "${featureName}"`);
|
|
1220
1235
|
return true;
|
|
1221
1236
|
}
|
|
1222
1237
|
if (isAdvanced && this.advanced.features.includes(featureName)) {
|
|
1223
|
-
console.log(`[features.js]
|
|
1238
|
+
console.log(`[features.js] [SUCCESS] ADVANCED tier has feature "${featureName}"`);
|
|
1224
1239
|
return true;
|
|
1225
1240
|
}
|
|
1226
1241
|
if (isPremium && this.premium.features.includes(featureName)) {
|
|
1227
|
-
console.log(`[features.js]
|
|
1242
|
+
console.log(`[features.js] [SUCCESS] PREMIUM tier has feature "${featureName}"`);
|
|
1228
1243
|
return true;
|
|
1229
1244
|
}
|
|
1230
1245
|
const inFree = this.free.features.includes(featureName);
|
|
1231
|
-
console.log(`[features.js] ${inFree ? "
|
|
1246
|
+
console.log(`[features.js] ${inFree ? "[SUCCESS]" : "[ERROR]"} FREE tier check for "${featureName}": ${inFree}`);
|
|
1232
1247
|
return inFree;
|
|
1233
1248
|
},
|
|
1234
1249
|
/**
|
|
@@ -1386,7 +1401,7 @@ function requireLicense() {
|
|
|
1386
1401
|
const licenseGuard2 = strapi2.plugin("magic-mail").service("license-guard");
|
|
1387
1402
|
const verification = await licenseGuard2.verifyLicense(trimmedKey);
|
|
1388
1403
|
if (!verification.valid) {
|
|
1389
|
-
strapi2.log.warn(`[magic-mail]
|
|
1404
|
+
strapi2.log.warn(`[magic-mail] [WARNING] Invalid license key attempted: ${trimmedKey.substring(0, 8)}...`);
|
|
1390
1405
|
return ctx.badRequest("Invalid or expired license key");
|
|
1391
1406
|
}
|
|
1392
1407
|
const license2 = await licenseGuard2.getLicenseByKey(trimmedKey);
|
|
@@ -1394,7 +1409,7 @@ function requireLicense() {
|
|
|
1394
1409
|
return ctx.badRequest("License not found");
|
|
1395
1410
|
}
|
|
1396
1411
|
if (license2.email.toLowerCase() !== trimmedEmail) {
|
|
1397
|
-
strapi2.log.warn(`[magic-mail]
|
|
1412
|
+
strapi2.log.warn(`[magic-mail] [WARNING] Email mismatch for license key`);
|
|
1398
1413
|
return ctx.badRequest("Email address does not match this license key");
|
|
1399
1414
|
}
|
|
1400
1415
|
await licenseGuard2.storeLicenseKey(trimmedKey);
|
|
@@ -1404,7 +1419,7 @@ function requireLicense() {
|
|
|
1404
1419
|
pingInterval,
|
|
1405
1420
|
data: verification.data
|
|
1406
1421
|
};
|
|
1407
|
-
strapi2.log.info(`[magic-mail]
|
|
1422
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] License validated and stored`);
|
|
1408
1423
|
return ctx.send({
|
|
1409
1424
|
success: true,
|
|
1410
1425
|
message: "License activated successfully",
|
|
@@ -1448,9 +1463,11 @@ function requireLicense() {
|
|
|
1448
1463
|
const maxAccounts = await licenseGuard2.getMaxAccounts();
|
|
1449
1464
|
const maxRules = await licenseGuard2.getMaxRoutingRules();
|
|
1450
1465
|
const maxTemplates = await licenseGuard2.getMaxEmailTemplates();
|
|
1451
|
-
const currentAccounts = await
|
|
1452
|
-
|
|
1453
|
-
|
|
1466
|
+
const [currentAccounts, currentRules, currentTemplates] = await Promise.all([
|
|
1467
|
+
strapi2.documents("plugin::magic-mail.email-account").count(),
|
|
1468
|
+
strapi2.documents("plugin::magic-mail.routing-rule").count(),
|
|
1469
|
+
strapi2.documents("plugin::magic-mail.email-template").count()
|
|
1470
|
+
]);
|
|
1454
1471
|
let tier = "free";
|
|
1455
1472
|
if (license2?.featureEnterprise === true || license2?.features?.enterprise === true) tier = "enterprise";
|
|
1456
1473
|
else if (license2?.featureAdvanced === true || license2?.features?.advanced === true) tier = "advanced";
|
|
@@ -1859,6 +1876,9 @@ var hasRequiredAnalytics$1;
|
|
|
1859
1876
|
function requireAnalytics$1() {
|
|
1860
1877
|
if (hasRequiredAnalytics$1) return analytics$1;
|
|
1861
1878
|
hasRequiredAnalytics$1 = 1;
|
|
1879
|
+
const EMAIL_LOG_UID = "plugin::magic-mail.email-log";
|
|
1880
|
+
const EMAIL_EVENT_UID = "plugin::magic-mail.email-event";
|
|
1881
|
+
const EMAIL_ACCOUNT_UID = "plugin::magic-mail.email-account";
|
|
1862
1882
|
analytics$1 = ({ strapi: strapi2 }) => ({
|
|
1863
1883
|
/**
|
|
1864
1884
|
* Tracking pixel endpoint
|
|
@@ -1898,7 +1918,8 @@ function requireAnalytics$1() {
|
|
|
1898
1918
|
async getStats(ctx) {
|
|
1899
1919
|
try {
|
|
1900
1920
|
const filters = {
|
|
1901
|
-
|
|
1921
|
+
// userId is documentId (string) in Strapi v5, NOT parseInt!
|
|
1922
|
+
userId: ctx.query.userId || null,
|
|
1902
1923
|
templateId: ctx.query.templateId ? parseInt(ctx.query.templateId) : null,
|
|
1903
1924
|
accountId: ctx.query.accountId ? parseInt(ctx.query.accountId) : null,
|
|
1904
1925
|
dateFrom: ctx.query.dateFrom || null,
|
|
@@ -1921,7 +1942,8 @@ function requireAnalytics$1() {
|
|
|
1921
1942
|
async getEmailLogs(ctx) {
|
|
1922
1943
|
try {
|
|
1923
1944
|
const filters = {
|
|
1924
|
-
|
|
1945
|
+
// userId is documentId (string) in Strapi v5, NOT parseInt!
|
|
1946
|
+
userId: ctx.query.userId || null,
|
|
1925
1947
|
templateId: ctx.query.templateId ? parseInt(ctx.query.templateId) : null,
|
|
1926
1948
|
search: ctx.query.search || null
|
|
1927
1949
|
};
|
|
@@ -1961,11 +1983,12 @@ function requireAnalytics$1() {
|
|
|
1961
1983
|
/**
|
|
1962
1984
|
* Get user email activity
|
|
1963
1985
|
* GET /magic-mail/analytics/users/:userId
|
|
1986
|
+
* Note: userId is documentId (string) in Strapi v5
|
|
1964
1987
|
*/
|
|
1965
1988
|
async getUserActivity(ctx) {
|
|
1966
1989
|
try {
|
|
1967
1990
|
const { userId } = ctx.params;
|
|
1968
|
-
const activity = await strapi2.plugin("magic-mail").service("analytics").getUserActivity(
|
|
1991
|
+
const activity = await strapi2.plugin("magic-mail").service("analytics").getUserActivity(userId);
|
|
1969
1992
|
return ctx.send({
|
|
1970
1993
|
success: true,
|
|
1971
1994
|
data: activity
|
|
@@ -1980,19 +2003,19 @@ function requireAnalytics$1() {
|
|
|
1980
2003
|
*/
|
|
1981
2004
|
async debug(ctx) {
|
|
1982
2005
|
try {
|
|
1983
|
-
strapi2.log.info("[magic-mail]
|
|
1984
|
-
const emailLogs = await strapi2.
|
|
2006
|
+
strapi2.log.info("[magic-mail] [CHECK] Running Analytics Debug...");
|
|
2007
|
+
const emailLogs = await strapi2.documents(EMAIL_LOG_UID).findMany({
|
|
1985
2008
|
limit: 10,
|
|
1986
|
-
|
|
2009
|
+
sort: [{ sentAt: "desc" }]
|
|
1987
2010
|
});
|
|
1988
|
-
const emailEvents = await strapi2.
|
|
2011
|
+
const emailEvents = await strapi2.documents(EMAIL_EVENT_UID).findMany({
|
|
1989
2012
|
limit: 20,
|
|
1990
|
-
|
|
2013
|
+
sort: [{ timestamp: "desc" }],
|
|
1991
2014
|
populate: ["emailLog"]
|
|
1992
2015
|
});
|
|
1993
2016
|
const analyticsService = strapi2.plugin("magic-mail").service("analytics");
|
|
1994
2017
|
const stats = await analyticsService.getStats();
|
|
1995
|
-
const accounts2 = await strapi2.
|
|
2018
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
1996
2019
|
filters: { isActive: true },
|
|
1997
2020
|
fields: ["id", "name", "provider", "fromEmail", "emailsSentToday", "totalEmailsSent"]
|
|
1998
2021
|
});
|
|
@@ -2056,19 +2079,20 @@ function requireAnalytics$1() {
|
|
|
2056
2079
|
async deleteEmailLog(ctx) {
|
|
2057
2080
|
try {
|
|
2058
2081
|
const { emailId } = ctx.params;
|
|
2059
|
-
const emailLog = await strapi2.
|
|
2060
|
-
|
|
2082
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
2083
|
+
filters: { emailId }
|
|
2061
2084
|
});
|
|
2062
2085
|
if (!emailLog) {
|
|
2063
2086
|
return ctx.notFound("Email log not found");
|
|
2064
2087
|
}
|
|
2065
|
-
await strapi2.
|
|
2066
|
-
|
|
2067
|
-
});
|
|
2068
|
-
await strapi2.db.query("plugin::magic-mail.email-log").delete({
|
|
2069
|
-
where: { id: emailLog.id }
|
|
2088
|
+
const events = await strapi2.documents(EMAIL_EVENT_UID).findMany({
|
|
2089
|
+
filters: { emailLog: { documentId: emailLog.documentId } }
|
|
2070
2090
|
});
|
|
2071
|
-
|
|
2091
|
+
for (const event of events) {
|
|
2092
|
+
await strapi2.documents(EMAIL_EVENT_UID).delete({ documentId: event.documentId });
|
|
2093
|
+
}
|
|
2094
|
+
await strapi2.documents(EMAIL_LOG_UID).delete({ documentId: emailLog.documentId });
|
|
2095
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleted email log: ${emailId}`);
|
|
2072
2096
|
return ctx.send({
|
|
2073
2097
|
success: true,
|
|
2074
2098
|
message: "Email log deleted successfully"
|
|
@@ -2085,33 +2109,36 @@ function requireAnalytics$1() {
|
|
|
2085
2109
|
async clearAllEmailLogs(ctx) {
|
|
2086
2110
|
try {
|
|
2087
2111
|
const { olderThan } = ctx.query;
|
|
2088
|
-
const
|
|
2112
|
+
const filters = {};
|
|
2089
2113
|
if (olderThan) {
|
|
2090
|
-
|
|
2114
|
+
filters.sentAt = { $lt: new Date(olderThan) };
|
|
2091
2115
|
}
|
|
2092
|
-
const emailLogs = await strapi2.
|
|
2093
|
-
|
|
2094
|
-
|
|
2116
|
+
const emailLogs = await strapi2.documents(EMAIL_LOG_UID).findMany({
|
|
2117
|
+
filters,
|
|
2118
|
+
fields: ["id", "documentId"],
|
|
2119
|
+
limit: 1e5
|
|
2095
2120
|
});
|
|
2096
|
-
|
|
2097
|
-
if (emailLogIds.length === 0) {
|
|
2121
|
+
if (emailLogs.length === 0) {
|
|
2098
2122
|
return ctx.send({
|
|
2099
2123
|
success: true,
|
|
2100
2124
|
message: "No email logs to delete",
|
|
2101
2125
|
deletedCount: 0
|
|
2102
2126
|
});
|
|
2103
2127
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2128
|
+
for (const log of emailLogs) {
|
|
2129
|
+
const events = await strapi2.documents(EMAIL_EVENT_UID).findMany({
|
|
2130
|
+
filters: { emailLog: { documentId: log.documentId } }
|
|
2131
|
+
});
|
|
2132
|
+
for (const event of events) {
|
|
2133
|
+
await strapi2.documents(EMAIL_EVENT_UID).delete({ documentId: event.documentId });
|
|
2134
|
+
}
|
|
2135
|
+
await strapi2.documents(EMAIL_LOG_UID).delete({ documentId: log.documentId });
|
|
2136
|
+
}
|
|
2137
|
+
strapi2.log.info(`[magic-mail] [DELETE] Cleared ${emailLogs.length} email logs`);
|
|
2111
2138
|
return ctx.send({
|
|
2112
2139
|
success: true,
|
|
2113
|
-
message: `Successfully deleted ${
|
|
2114
|
-
deletedCount:
|
|
2140
|
+
message: `Successfully deleted ${emailLogs.length} email log(s)`,
|
|
2141
|
+
deletedCount: emailLogs.length
|
|
2115
2142
|
});
|
|
2116
2143
|
} catch (error) {
|
|
2117
2144
|
strapi2.log.error("[magic-mail] Error clearing email logs:", error);
|
|
@@ -2126,18 +2153,16 @@ var hasRequiredTest;
|
|
|
2126
2153
|
function requireTest() {
|
|
2127
2154
|
if (hasRequiredTest) return test;
|
|
2128
2155
|
hasRequiredTest = 1;
|
|
2156
|
+
const EMAIL_TEMPLATE_UID = "plugin::magic-mail.email-template";
|
|
2157
|
+
const EMAIL_TEMPLATE_VERSION_UID = "plugin::magic-mail.email-template-version";
|
|
2129
2158
|
test = {
|
|
2130
2159
|
/**
|
|
2131
2160
|
* Test Template-Version Relations
|
|
2132
|
-
*
|
|
2133
|
-
* Tests beide Richtungen:
|
|
2134
|
-
* 1. Version → Template beim Erstellen
|
|
2135
|
-
* 2. Template → Version nachträglich via connect
|
|
2136
2161
|
*/
|
|
2137
2162
|
async testRelations(ctx) {
|
|
2138
2163
|
try {
|
|
2139
2164
|
console.log("\n" + "=".repeat(60));
|
|
2140
|
-
console.log("🧪 TEST: Template ↔ Version Relations");
|
|
2165
|
+
console.log("🧪 TEST: Template ↔ Version Relations (Document Service API)");
|
|
2141
2166
|
console.log("=".repeat(60));
|
|
2142
2167
|
let test1Success = false;
|
|
2143
2168
|
let test1ReverseSuccess = false;
|
|
@@ -2147,257 +2172,192 @@ function requireTest() {
|
|
|
2147
2172
|
let test3a_hasTemplate = false;
|
|
2148
2173
|
let test3b_twoVersions = false;
|
|
2149
2174
|
let test3b_allHaveTemplate = false;
|
|
2150
|
-
console.log("\n
|
|
2151
|
-
const testTemplate = await strapi.
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
category: "custom",
|
|
2161
|
-
isActive: true
|
|
2162
|
-
}
|
|
2163
|
-
}
|
|
2164
|
-
);
|
|
2165
|
-
console.log(`✅ Template erstellt: ID ${testTemplate.id}`);
|
|
2166
|
-
const version1 = await strapi.entityService.create(
|
|
2167
|
-
"plugin::magic-mail.email-template-version",
|
|
2168
|
-
{
|
|
2169
|
-
data: {
|
|
2170
|
-
template: testTemplate.id,
|
|
2171
|
-
// 👈 Direkte Verbindung beim Erstellen
|
|
2172
|
-
versionNumber: 1,
|
|
2173
|
-
name: "Version 1 von Test",
|
|
2174
|
-
subject: "Test Subject V1",
|
|
2175
|
-
bodyHtml: "<p>Version 1 HTML</p>",
|
|
2176
|
-
bodyText: "Version 1 Text"
|
|
2177
|
-
}
|
|
2175
|
+
console.log("\n[TEST] TEST 1: Version → Template Verbindung\n");
|
|
2176
|
+
const testTemplate = await strapi.documents(EMAIL_TEMPLATE_UID).create({
|
|
2177
|
+
data: {
|
|
2178
|
+
templateReferenceId: Math.floor(Math.random() * 1e6),
|
|
2179
|
+
name: "Test Template Relations",
|
|
2180
|
+
subject: "Test Subject",
|
|
2181
|
+
bodyHtml: "<p>Test HTML</p>",
|
|
2182
|
+
bodyText: "Test Text",
|
|
2183
|
+
category: "custom",
|
|
2184
|
+
isActive: true
|
|
2178
2185
|
}
|
|
2179
|
-
);
|
|
2180
|
-
console.log(
|
|
2181
|
-
const
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
+
});
|
|
2187
|
+
console.log(`[SUCCESS] Template erstellt: documentId ${testTemplate.documentId}`);
|
|
2188
|
+
const version1 = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).create({
|
|
2189
|
+
data: {
|
|
2190
|
+
template: testTemplate.documentId,
|
|
2191
|
+
versionNumber: 1,
|
|
2192
|
+
name: "Version 1 von Test",
|
|
2193
|
+
subject: "Test Subject V1",
|
|
2194
|
+
bodyHtml: "<p>Version 1 HTML</p>",
|
|
2195
|
+
bodyText: "Version 1 Text"
|
|
2186
2196
|
}
|
|
2187
|
-
);
|
|
2188
|
-
console.log(
|
|
2197
|
+
});
|
|
2198
|
+
console.log(`[SUCCESS] Version erstellt: documentId ${version1.documentId}, versionNumber: ${version1.versionNumber}`);
|
|
2199
|
+
const versionCheck = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
2200
|
+
documentId: version1.documentId,
|
|
2201
|
+
populate: ["template"]
|
|
2202
|
+
});
|
|
2203
|
+
console.log("\n[CHECK] Prüfung Version → Template:");
|
|
2189
2204
|
test1Success = !!versionCheck.template;
|
|
2190
2205
|
if (test1Success) {
|
|
2191
|
-
console.log(`
|
|
2192
|
-
console.log(` 📋 Template: "${versionCheck.template.name}"`);
|
|
2206
|
+
console.log(` [SUCCESS] SUCCESS: Version → Template ${versionCheck.template.documentId}`);
|
|
2193
2207
|
} else {
|
|
2194
|
-
console.log(`
|
|
2208
|
+
console.log(` [ERROR] FEHLER: Version hat KEINE Template-Verbindung!`);
|
|
2195
2209
|
}
|
|
2196
|
-
const templateCheck1 = await strapi.
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
}
|
|
2202
|
-
);
|
|
2203
|
-
console.log("\n🔍 Prüfung Template → Versions:");
|
|
2210
|
+
const templateCheck1 = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2211
|
+
documentId: testTemplate.documentId,
|
|
2212
|
+
populate: ["versions"]
|
|
2213
|
+
});
|
|
2214
|
+
console.log("\n[CHECK] Prüfung Template → Versions:");
|
|
2204
2215
|
test1ReverseSuccess = templateCheck1.versions && templateCheck1.versions.length > 0;
|
|
2205
2216
|
if (test1ReverseSuccess) {
|
|
2206
|
-
console.log(`
|
|
2207
|
-
templateCheck1.versions.forEach((v) => {
|
|
2208
|
-
console.log(` 📋 Version ${v.id}: versionNumber ${v.versionNumber}`);
|
|
2209
|
-
});
|
|
2217
|
+
console.log(` [SUCCESS] SUCCESS: Template hat ${templateCheck1.versions.length} Version(en)`);
|
|
2210
2218
|
} else {
|
|
2211
|
-
console.log(`
|
|
2212
|
-
}
|
|
2213
|
-
console.log("\n\n
|
|
2214
|
-
const version2 = await strapi.
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
bodyHtml: "<p>Version 2 HTML</p>",
|
|
2222
|
-
bodyText: "Version 2 Text"
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
);
|
|
2226
|
-
console.log(`✅ Version 2 erstellt: ID ${version2.id} (ohne Template)`);
|
|
2227
|
-
await strapi.entityService.update(
|
|
2228
|
-
"plugin::magic-mail.email-template",
|
|
2229
|
-
testTemplate.id,
|
|
2230
|
-
{
|
|
2231
|
-
data: {
|
|
2232
|
-
versions: {
|
|
2233
|
-
connect: [version2.id]
|
|
2234
|
-
// 👈 Nachträgliche Verbindung
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2219
|
+
console.log(` [ERROR] FEHLER: Template hat KEINE Versionen!`);
|
|
2220
|
+
}
|
|
2221
|
+
console.log("\n\n[TEST] TEST 2: Nachträgliche Verbindung\n");
|
|
2222
|
+
const version2 = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).create({
|
|
2223
|
+
data: {
|
|
2224
|
+
versionNumber: 2,
|
|
2225
|
+
name: "Version 2 ohne Template",
|
|
2226
|
+
subject: "Test Subject V2",
|
|
2227
|
+
bodyHtml: "<p>Version 2 HTML</p>",
|
|
2228
|
+
bodyText: "Version 2 Text"
|
|
2237
2229
|
}
|
|
2238
|
-
);
|
|
2239
|
-
console.log(
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
populate: ["versions"]
|
|
2230
|
+
});
|
|
2231
|
+
console.log(`[SUCCESS] Version 2 erstellt: documentId ${version2.documentId} (ohne Template)`);
|
|
2232
|
+
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).update({
|
|
2233
|
+
documentId: version2.documentId,
|
|
2234
|
+
data: {
|
|
2235
|
+
template: testTemplate.documentId
|
|
2245
2236
|
}
|
|
2246
|
-
);
|
|
2247
|
-
console.log(
|
|
2237
|
+
});
|
|
2238
|
+
console.log(`[SUCCESS] Version 2 mit Template verbunden`);
|
|
2239
|
+
const templateCheck2 = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2240
|
+
documentId: testTemplate.documentId,
|
|
2241
|
+
populate: ["versions"]
|
|
2242
|
+
});
|
|
2243
|
+
console.log("\n[CHECK] Prüfung nach Update:");
|
|
2248
2244
|
test2Success = templateCheck2.versions && templateCheck2.versions.length >= 2;
|
|
2249
2245
|
if (test2Success) {
|
|
2250
|
-
console.log(`
|
|
2251
|
-
templateCheck2.versions.forEach((v) => {
|
|
2252
|
-
console.log(` 📋 Version ${v.id}: versionNumber ${v.versionNumber}, "${v.name}"`);
|
|
2253
|
-
});
|
|
2246
|
+
console.log(` [SUCCESS] SUCCESS: Template hat jetzt ${templateCheck2.versions.length} Versionen`);
|
|
2254
2247
|
} else {
|
|
2255
|
-
console.log(`
|
|
2248
|
+
console.log(` [ERROR] FEHLER: Template hat nur ${templateCheck2.versions?.length || 0} Version(en)!`);
|
|
2256
2249
|
}
|
|
2257
|
-
const version2Check = await strapi.
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
populate: ["template"]
|
|
2262
|
-
}
|
|
2263
|
-
);
|
|
2264
|
-
console.log("\n🔍 Prüfung Version 2 → Template:");
|
|
2250
|
+
const version2Check = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
2251
|
+
documentId: version2.documentId,
|
|
2252
|
+
populate: ["template"]
|
|
2253
|
+
});
|
|
2265
2254
|
test2ReverseSuccess = !!version2Check.template;
|
|
2266
2255
|
if (test2ReverseSuccess) {
|
|
2267
|
-
console.log(`
|
|
2268
|
-
console.log(` 📋 Template: "${version2Check.template.name}"`);
|
|
2256
|
+
console.log(` [SUCCESS] SUCCESS: Version 2 → Template verbunden`);
|
|
2269
2257
|
} else {
|
|
2270
|
-
console.log(`
|
|
2271
|
-
}
|
|
2272
|
-
console.log("\n\n
|
|
2273
|
-
const autoTemplate = await strapi.
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
category: "custom",
|
|
2283
|
-
isActive: true
|
|
2284
|
-
}
|
|
2258
|
+
console.log(` [ERROR] FEHLER: Version 2 hat KEINE Template-Verbindung!`);
|
|
2259
|
+
}
|
|
2260
|
+
console.log("\n\n[TEST] TEST 3: Template Update (Auto-Versionierung)\n");
|
|
2261
|
+
const autoTemplate = await strapi.documents(EMAIL_TEMPLATE_UID).create({
|
|
2262
|
+
data: {
|
|
2263
|
+
templateReferenceId: Math.floor(Math.random() * 1e6),
|
|
2264
|
+
name: "Auto Version Test",
|
|
2265
|
+
subject: "Original Subject",
|
|
2266
|
+
bodyHtml: "<p>Original HTML</p>",
|
|
2267
|
+
bodyText: "Original Text",
|
|
2268
|
+
category: "custom",
|
|
2269
|
+
isActive: true
|
|
2285
2270
|
}
|
|
2286
|
-
);
|
|
2287
|
-
console.log(
|
|
2288
|
-
const beforeUpdate = await strapi.entityService.findOne(
|
|
2289
|
-
"plugin::magic-mail.email-template",
|
|
2290
|
-
autoTemplate.id,
|
|
2291
|
-
{ populate: ["versions"] }
|
|
2292
|
-
);
|
|
2293
|
-
console.log(` 📊 Versionen vor Update: ${beforeUpdate.versions?.length || 0}`);
|
|
2294
|
-
console.log("\n🔄 Führe Template-Update aus...");
|
|
2271
|
+
});
|
|
2272
|
+
console.log(`[SUCCESS] Template erstellt: documentId ${autoTemplate.documentId}`);
|
|
2295
2273
|
const emailDesignerService = strapi.plugin("magic-mail").service("email-designer");
|
|
2296
|
-
await emailDesignerService.update(autoTemplate.
|
|
2274
|
+
await emailDesignerService.update(autoTemplate.documentId, {
|
|
2297
2275
|
subject: "Updated Subject V1",
|
|
2298
2276
|
bodyHtml: "<p>Updated HTML V1</p>",
|
|
2299
2277
|
bodyText: "Updated Text V1"
|
|
2300
2278
|
});
|
|
2301
|
-
console.log("
|
|
2302
|
-
const afterFirstUpdate = await strapi.
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
);
|
|
2307
|
-
console.log("\n🔍 Prüfung nach 1. Update:");
|
|
2279
|
+
console.log("[SUCCESS] Template updated");
|
|
2280
|
+
const afterFirstUpdate = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2281
|
+
documentId: autoTemplate.documentId,
|
|
2282
|
+
populate: ["versions"]
|
|
2283
|
+
});
|
|
2284
|
+
console.log("\n[CHECK] Prüfung nach 1. Update:");
|
|
2308
2285
|
test3a_versionCreated = afterFirstUpdate.versions && afterFirstUpdate.versions.length === 1;
|
|
2309
2286
|
if (test3a_versionCreated) {
|
|
2310
|
-
console.log(`
|
|
2311
|
-
const autoVersion1 = await strapi.
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
);
|
|
2287
|
+
console.log(` [SUCCESS] SUCCESS: Automatisch 1 Version erstellt`);
|
|
2288
|
+
const autoVersion1 = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
2289
|
+
documentId: afterFirstUpdate.versions[0].documentId,
|
|
2290
|
+
populate: ["template"]
|
|
2291
|
+
});
|
|
2316
2292
|
test3a_hasTemplate = !!autoVersion1.template;
|
|
2317
2293
|
if (test3a_hasTemplate) {
|
|
2318
|
-
console.log(`
|
|
2319
|
-
console.log(` 📋 Version: versionNumber ${autoVersion1.versionNumber}, subject: "${autoVersion1.subject}"`);
|
|
2294
|
+
console.log(` [SUCCESS] SUCCESS: Version hat Template-Verbindung`);
|
|
2320
2295
|
} else {
|
|
2321
|
-
console.log(`
|
|
2296
|
+
console.log(` [ERROR] FEHLER: Version hat KEINE Template-Verbindung!`);
|
|
2322
2297
|
}
|
|
2323
2298
|
} else {
|
|
2324
|
-
console.log(`
|
|
2299
|
+
console.log(` [ERROR] FEHLER: Keine Version erstellt!`);
|
|
2325
2300
|
}
|
|
2326
|
-
|
|
2327
|
-
await emailDesignerService.update(autoTemplate.id, {
|
|
2301
|
+
await emailDesignerService.update(autoTemplate.documentId, {
|
|
2328
2302
|
subject: "Updated Subject V2",
|
|
2329
2303
|
bodyHtml: "<p>Updated HTML V2</p>",
|
|
2330
2304
|
bodyText: "Updated Text V2"
|
|
2331
2305
|
});
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
"
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
);
|
|
2338
|
-
console.log("\n🔍 Prüfung nach 2. Update:");
|
|
2306
|
+
const afterSecondUpdate = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2307
|
+
documentId: autoTemplate.documentId,
|
|
2308
|
+
populate: ["versions"]
|
|
2309
|
+
});
|
|
2310
|
+
console.log("\n[CHECK] Prüfung nach 2. Update:");
|
|
2339
2311
|
test3b_twoVersions = afterSecondUpdate.versions && afterSecondUpdate.versions.length === 2;
|
|
2340
2312
|
if (test3b_twoVersions) {
|
|
2341
|
-
console.log(`
|
|
2313
|
+
console.log(` [SUCCESS] SUCCESS: Jetzt 2 Versionen vorhanden`);
|
|
2342
2314
|
let allVersionsHaveTemplate = true;
|
|
2343
2315
|
for (const version3 of afterSecondUpdate.versions) {
|
|
2344
|
-
const fullVersion = await strapi.
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
)
|
|
2349
|
-
if (fullVersion.template) {
|
|
2350
|
-
console.log(` ✅ Version ${fullVersion.id} (v${fullVersion.versionNumber}): Template-Verbindung OK`);
|
|
2351
|
-
} else {
|
|
2352
|
-
console.log(` ❌ Version ${fullVersion.id} (v${fullVersion.versionNumber}): KEINE Template-Verbindung!`);
|
|
2316
|
+
const fullVersion = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
2317
|
+
documentId: version3.documentId,
|
|
2318
|
+
populate: ["template"]
|
|
2319
|
+
});
|
|
2320
|
+
if (!fullVersion.template) {
|
|
2353
2321
|
allVersionsHaveTemplate = false;
|
|
2354
2322
|
}
|
|
2355
2323
|
}
|
|
2356
2324
|
test3b_allHaveTemplate = allVersionsHaveTemplate;
|
|
2357
2325
|
if (allVersionsHaveTemplate) {
|
|
2358
|
-
console.log(`
|
|
2326
|
+
console.log(` [SUCCESS] SUCCESS: Alle Versionen haben Template-Verbindung!`);
|
|
2327
|
+
} else {
|
|
2328
|
+
console.log(` [ERROR] FEHLER: Nicht alle Versionen haben Template-Verbindung!`);
|
|
2359
2329
|
}
|
|
2360
2330
|
} else {
|
|
2361
|
-
console.log(`
|
|
2331
|
+
console.log(` [ERROR] FEHLER: Falsche Anzahl Versionen!`);
|
|
2362
2332
|
}
|
|
2363
2333
|
console.log("\n🧹 Cleanup Test 3...");
|
|
2364
2334
|
if (afterSecondUpdate.versions) {
|
|
2365
2335
|
for (const version3 of afterSecondUpdate.versions) {
|
|
2366
|
-
await strapi.
|
|
2336
|
+
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version3.documentId });
|
|
2367
2337
|
}
|
|
2368
2338
|
}
|
|
2369
|
-
await strapi.
|
|
2370
|
-
console.log("
|
|
2339
|
+
await strapi.documents(EMAIL_TEMPLATE_UID).delete({ documentId: autoTemplate.documentId });
|
|
2340
|
+
console.log(" [SUCCESS] Test 3 Daten gelöscht");
|
|
2371
2341
|
console.log("\n\n" + "=".repeat(60));
|
|
2372
|
-
console.log("
|
|
2342
|
+
console.log("[STATS] ZUSAMMENFASSUNG");
|
|
2373
2343
|
console.log("=".repeat(60));
|
|
2374
|
-
const finalTemplate = await strapi.
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
populate: ["versions"]
|
|
2379
|
-
}
|
|
2380
|
-
);
|
|
2344
|
+
const finalTemplate = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2345
|
+
documentId: testTemplate.documentId,
|
|
2346
|
+
populate: ["versions"]
|
|
2347
|
+
});
|
|
2381
2348
|
console.log(`
|
|
2382
|
-
|
|
2383
|
-
console.log(` Anzahl Versionen
|
|
2384
|
-
if (finalTemplate.versions && finalTemplate.versions.length > 0) {
|
|
2385
|
-
finalTemplate.versions.forEach((v) => {
|
|
2386
|
-
console.log(` - Version ${v.id}: versionNumber ${v.versionNumber}`);
|
|
2387
|
-
});
|
|
2388
|
-
}
|
|
2349
|
+
[INFO] Template: "${finalTemplate.name}" (documentId: ${finalTemplate.documentId})`);
|
|
2350
|
+
console.log(` Anzahl Versionen: ${finalTemplate.versions?.length || 0}`);
|
|
2389
2351
|
console.log("\n🧹 Aufräumen...");
|
|
2390
|
-
await strapi.
|
|
2391
|
-
|
|
2392
|
-
await strapi.
|
|
2393
|
-
console.log(
|
|
2394
|
-
|
|
2395
|
-
console.log(` ✅ Template ${testTemplate.id} gelöscht`);
|
|
2396
|
-
console.log("\n✅ Test abgeschlossen!\n");
|
|
2352
|
+
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version1.documentId });
|
|
2353
|
+
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version2.documentId });
|
|
2354
|
+
await strapi.documents(EMAIL_TEMPLATE_UID).delete({ documentId: testTemplate.documentId });
|
|
2355
|
+
console.log(" [SUCCESS] Alle Test-Daten gelöscht");
|
|
2356
|
+
console.log("\n[SUCCESS] Test abgeschlossen!\n");
|
|
2397
2357
|
const allSuccess = test1Success && test1ReverseSuccess && test2Success && test2ReverseSuccess && test3a_versionCreated && test3a_hasTemplate && test3b_twoVersions && test3b_allHaveTemplate;
|
|
2398
2358
|
ctx.body = {
|
|
2399
2359
|
success: allSuccess,
|
|
2400
|
-
message: allSuccess ? "Alle Tests erfolgreich!
|
|
2360
|
+
message: allSuccess ? "Alle Tests erfolgreich! [SUCCESS]" : "Einige Tests fehlgeschlagen [ERROR]",
|
|
2401
2361
|
tests: {
|
|
2402
2362
|
test1_version_to_template: test1Success,
|
|
2403
2363
|
test1_template_to_version: test1ReverseSuccess,
|
|
@@ -2407,15 +2367,10 @@ function requireTest() {
|
|
|
2407
2367
|
test3_auto_version_has_template: test3a_hasTemplate,
|
|
2408
2368
|
test3_two_auto_versions: test3b_twoVersions,
|
|
2409
2369
|
test3_all_auto_versions_have_template: test3b_allHaveTemplate
|
|
2410
|
-
},
|
|
2411
|
-
template: {
|
|
2412
|
-
id: testTemplate.id,
|
|
2413
|
-
name: testTemplate.name,
|
|
2414
|
-
versionsCount: finalTemplate.versions?.length || 0
|
|
2415
2370
|
}
|
|
2416
2371
|
};
|
|
2417
2372
|
} catch (error) {
|
|
2418
|
-
console.error("\n
|
|
2373
|
+
console.error("\n[ERROR] FEHLER:", error.message);
|
|
2419
2374
|
console.error(error.stack);
|
|
2420
2375
|
ctx.throw(500, error);
|
|
2421
2376
|
}
|
|
@@ -3009,7 +2964,7 @@ function requireEncryption() {
|
|
|
3009
2964
|
if (envKey) {
|
|
3010
2965
|
return crypto.createHash("sha256").update(envKey).digest();
|
|
3011
2966
|
}
|
|
3012
|
-
console.warn("[magic-mail]
|
|
2967
|
+
console.warn("[magic-mail] [WARNING] No MAGIC_MAIL_ENCRYPTION_KEY found. Using fallback.");
|
|
3013
2968
|
return crypto.createHash("sha256").update("magic-mail-default-key").digest();
|
|
3014
2969
|
}
|
|
3015
2970
|
function encryptCredentials(data) {
|
|
@@ -3088,12 +3043,17 @@ function requireEmailRouter() {
|
|
|
3088
3043
|
// Template Reference ID
|
|
3089
3044
|
templateData,
|
|
3090
3045
|
// Data for template rendering
|
|
3091
|
-
data
|
|
3046
|
+
data,
|
|
3092
3047
|
// Alias for templateData (for native Strapi compatibility)
|
|
3048
|
+
skipLinkTracking = false
|
|
3049
|
+
// Skip link rewriting for sensitive URLs (e.g., Magic Links)
|
|
3093
3050
|
} = emailData;
|
|
3094
3051
|
if (!templateData && data) {
|
|
3095
3052
|
templateData = data;
|
|
3096
3053
|
}
|
|
3054
|
+
if (skipLinkTracking) {
|
|
3055
|
+
strapi2.log.info(`[magic-mail] [SKIP-TRACK] skipLinkTracking=true received for email to: ${to}`);
|
|
3056
|
+
}
|
|
3097
3057
|
let renderedTemplate = null;
|
|
3098
3058
|
if (templateId || emailData.templateReferenceId) {
|
|
3099
3059
|
try {
|
|
@@ -3106,10 +3066,10 @@ function requireEmailRouter() {
|
|
|
3106
3066
|
if (!resolvedTemplateReferenceId && templateId) {
|
|
3107
3067
|
const numericTemplateId = Number(templateId);
|
|
3108
3068
|
if (!Number.isNaN(numericTemplateId) && Number.isInteger(numericTemplateId)) {
|
|
3109
|
-
strapi2.log.info(`[magic-mail]
|
|
3069
|
+
strapi2.log.info(`[magic-mail] [CHECK] Looking up template by ID: ${numericTemplateId}`);
|
|
3110
3070
|
templateRecord = await strapi2.plugin("magic-mail").service("email-designer").findOne(numericTemplateId);
|
|
3111
3071
|
if (!templateRecord) {
|
|
3112
|
-
strapi2.log.error(`[magic-mail]
|
|
3072
|
+
strapi2.log.error(`[magic-mail] [ERROR] Template with ID ${numericTemplateId} not found in database`);
|
|
3113
3073
|
throw new Error(`Template with ID ${numericTemplateId} not found`);
|
|
3114
3074
|
}
|
|
3115
3075
|
if (!templateRecord.templateReferenceId) {
|
|
@@ -3117,7 +3077,7 @@ function requireEmailRouter() {
|
|
|
3117
3077
|
}
|
|
3118
3078
|
resolvedTemplateReferenceId = String(templateRecord.templateReferenceId).trim();
|
|
3119
3079
|
strapi2.log.info(
|
|
3120
|
-
`[magic-mail]
|
|
3080
|
+
`[magic-mail] [SUCCESS] Found template: ID=${templateRecord.id}, referenceId="${resolvedTemplateReferenceId}", name="${templateRecord.name}"`
|
|
3121
3081
|
);
|
|
3122
3082
|
} else {
|
|
3123
3083
|
resolvedTemplateReferenceId = String(templateId).trim();
|
|
@@ -3133,14 +3093,14 @@ function requireEmailRouter() {
|
|
|
3133
3093
|
subject = subject || renderedTemplate.subject;
|
|
3134
3094
|
type = type || renderedTemplate.category;
|
|
3135
3095
|
strapi2.log.info(
|
|
3136
|
-
`[magic-mail]
|
|
3096
|
+
`[magic-mail] [EMAIL] Rendered template reference "${resolvedTemplateReferenceId}" (requested ID: ${templateId ?? "n/a"}): ${renderedTemplate.templateName}`
|
|
3137
3097
|
);
|
|
3138
3098
|
emailData.templateReferenceId = resolvedTemplateReferenceId;
|
|
3139
3099
|
if (!emailData.templateName) {
|
|
3140
3100
|
emailData.templateName = templateRecord?.name || renderedTemplate.templateName;
|
|
3141
3101
|
}
|
|
3142
3102
|
} catch (error) {
|
|
3143
|
-
strapi2.log.error(`[magic-mail]
|
|
3103
|
+
strapi2.log.error(`[magic-mail] [ERROR] Template rendering failed: ${error.message}`);
|
|
3144
3104
|
throw new Error(`Template rendering failed: ${error.message}`);
|
|
3145
3105
|
}
|
|
3146
3106
|
}
|
|
@@ -3169,10 +3129,14 @@ function requireEmailRouter() {
|
|
|
3169
3129
|
});
|
|
3170
3130
|
recipientHash = analyticsService.generateRecipientHash(emailLog.emailId, to);
|
|
3171
3131
|
html = analyticsService.injectTrackingPixel(html, emailLog.emailId, recipientHash);
|
|
3172
|
-
|
|
3173
|
-
|
|
3132
|
+
if (!skipLinkTracking) {
|
|
3133
|
+
html = await analyticsService.rewriteLinksForTracking(html, emailLog.emailId, recipientHash);
|
|
3134
|
+
strapi2.log.info(`[magic-mail] [STATS] Full tracking enabled for email: ${emailLog.emailId}`);
|
|
3135
|
+
} else {
|
|
3136
|
+
strapi2.log.info(`[magic-mail] [STATS] Open tracking enabled, link tracking DISABLED for email: ${emailLog.emailId}`);
|
|
3137
|
+
}
|
|
3174
3138
|
} catch (error) {
|
|
3175
|
-
strapi2.log.error(`[magic-mail]
|
|
3139
|
+
strapi2.log.error(`[magic-mail] [WARNING] Tracking setup failed (continuing without tracking):`, error.message);
|
|
3176
3140
|
}
|
|
3177
3141
|
}
|
|
3178
3142
|
emailData.html = html;
|
|
@@ -3183,7 +3147,7 @@ function requireEmailRouter() {
|
|
|
3183
3147
|
if (priority === "high") {
|
|
3184
3148
|
const hasFeature = await licenseGuard2.hasFeature("priority-headers");
|
|
3185
3149
|
if (!hasFeature) {
|
|
3186
|
-
strapi2.log.warn("[magic-mail]
|
|
3150
|
+
strapi2.log.warn("[magic-mail] [WARNING] High priority emails require Advanced license - using normal priority");
|
|
3187
3151
|
emailData.priority = "normal";
|
|
3188
3152
|
}
|
|
3189
3153
|
}
|
|
@@ -3207,8 +3171,8 @@ function requireEmailRouter() {
|
|
|
3207
3171
|
const result = await this.sendViaAccount(account, emailData);
|
|
3208
3172
|
if (emailLog) {
|
|
3209
3173
|
try {
|
|
3210
|
-
await strapi2.
|
|
3211
|
-
|
|
3174
|
+
await strapi2.documents("plugin::magic-mail.email-log").update({
|
|
3175
|
+
documentId: emailLog.documentId,
|
|
3212
3176
|
data: {
|
|
3213
3177
|
accountId: account.id,
|
|
3214
3178
|
accountName: account.name,
|
|
@@ -3219,15 +3183,15 @@ function requireEmailRouter() {
|
|
|
3219
3183
|
strapi2.log.error("[magic-mail] Failed to update email log:", error.message);
|
|
3220
3184
|
}
|
|
3221
3185
|
}
|
|
3222
|
-
await this.updateAccountStats(account.
|
|
3223
|
-
strapi2.log.info(`[magic-mail]
|
|
3186
|
+
await this.updateAccountStats(account.documentId);
|
|
3187
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Email sent to ${to} via ${account.name}`);
|
|
3224
3188
|
return {
|
|
3225
3189
|
success: true,
|
|
3226
3190
|
accountUsed: account.name,
|
|
3227
3191
|
messageId: result.messageId
|
|
3228
3192
|
};
|
|
3229
3193
|
} catch (error) {
|
|
3230
|
-
strapi2.log.error("[magic-mail]
|
|
3194
|
+
strapi2.log.error("[magic-mail] [ERROR] Email send failed:", error);
|
|
3231
3195
|
throw error;
|
|
3232
3196
|
}
|
|
3233
3197
|
},
|
|
@@ -3235,21 +3199,21 @@ function requireEmailRouter() {
|
|
|
3235
3199
|
* Select best account based on rules
|
|
3236
3200
|
*/
|
|
3237
3201
|
async selectAccount(type, priority, excludeIds = [], emailData = {}) {
|
|
3238
|
-
const accounts2 = await strapi2.
|
|
3202
|
+
const accounts2 = await strapi2.documents("plugin::magic-mail.email-account").findMany({
|
|
3239
3203
|
filters: {
|
|
3240
3204
|
isActive: true,
|
|
3241
3205
|
id: { $notIn: excludeIds }
|
|
3242
3206
|
},
|
|
3243
|
-
sort: { priority: "desc" }
|
|
3207
|
+
sort: [{ priority: "desc" }]
|
|
3244
3208
|
});
|
|
3245
3209
|
if (!accounts2 || accounts2.length === 0) {
|
|
3246
3210
|
return null;
|
|
3247
3211
|
}
|
|
3248
|
-
const allRules = await strapi2.
|
|
3212
|
+
const allRules = await strapi2.documents("plugin::magic-mail.routing-rule").findMany({
|
|
3249
3213
|
filters: {
|
|
3250
3214
|
isActive: true
|
|
3251
3215
|
},
|
|
3252
|
-
sort: { priority: "desc" }
|
|
3216
|
+
sort: [{ priority: "desc" }]
|
|
3253
3217
|
});
|
|
3254
3218
|
for (const rule of allRules) {
|
|
3255
3219
|
let matches = false;
|
|
@@ -3273,13 +3237,13 @@ function requireEmailRouter() {
|
|
|
3273
3237
|
if (matches) {
|
|
3274
3238
|
const account = accounts2.find((a) => a.name === rule.accountName);
|
|
3275
3239
|
if (account) {
|
|
3276
|
-
strapi2.log.info(`[magic-mail]
|
|
3240
|
+
strapi2.log.info(`[magic-mail] [ROUTE] Routing rule matched: ${rule.name} -> ${account.name}`);
|
|
3277
3241
|
return account;
|
|
3278
3242
|
}
|
|
3279
3243
|
if (rule.fallbackAccountName) {
|
|
3280
3244
|
const fallbackAccount = accounts2.find((a) => a.name === rule.fallbackAccountName);
|
|
3281
3245
|
if (fallbackAccount) {
|
|
3282
|
-
strapi2.log.info(`[magic-mail]
|
|
3246
|
+
strapi2.log.info(`[magic-mail] [FALLBACK] Using fallback account: ${fallbackAccount.name}`);
|
|
3283
3247
|
return fallbackAccount;
|
|
3284
3248
|
}
|
|
3285
3249
|
}
|
|
@@ -3383,7 +3347,7 @@ function requireEmailRouter() {
|
|
|
3383
3347
|
mailOptions.headers["List-Unsubscribe-Post"] = "List-Unsubscribe=One-Click";
|
|
3384
3348
|
mailOptions.headers["Precedence"] = "bulk";
|
|
3385
3349
|
} else {
|
|
3386
|
-
strapi2.log.warn("[magic-mail]
|
|
3350
|
+
strapi2.log.warn("[magic-mail] [WARNING] Marketing email without unsubscribe URL - may violate GDPR/CAN-SPAM");
|
|
3387
3351
|
}
|
|
3388
3352
|
}
|
|
3389
3353
|
if (emailData.headers && typeof emailData.headers === "object") {
|
|
@@ -3429,14 +3393,15 @@ function requireEmailRouter() {
|
|
|
3429
3393
|
config2.clientSecret
|
|
3430
3394
|
);
|
|
3431
3395
|
currentAccessToken = newTokens.accessToken;
|
|
3432
|
-
strapi2.log.info("[magic-mail]
|
|
3396
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Token refreshed successfully");
|
|
3433
3397
|
const { encryptCredentials } = requireEncryption();
|
|
3434
3398
|
const updatedOAuth = encryptCredentials({
|
|
3435
3399
|
...oauth2,
|
|
3436
3400
|
accessToken: newTokens.accessToken,
|
|
3437
3401
|
expiresAt: newTokens.expiresAt
|
|
3438
3402
|
});
|
|
3439
|
-
await strapi2.
|
|
3403
|
+
await strapi2.documents("plugin::magic-mail.email-account").update({
|
|
3404
|
+
documentId: account.documentId,
|
|
3440
3405
|
data: { oauth: updatedOAuth }
|
|
3441
3406
|
});
|
|
3442
3407
|
} catch (refreshErr) {
|
|
@@ -3555,13 +3520,19 @@ function requireEmailRouter() {
|
|
|
3555
3520
|
throw new Error(`Gmail API error: ${errorData.error?.message || response.statusText}`);
|
|
3556
3521
|
}
|
|
3557
3522
|
const result = await response.json();
|
|
3558
|
-
strapi2.log.info("[magic-mail]
|
|
3523
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via Gmail API");
|
|
3559
3524
|
return {
|
|
3560
3525
|
messageId: result.id,
|
|
3561
3526
|
response: "OK"
|
|
3562
3527
|
};
|
|
3563
3528
|
} catch (err) {
|
|
3564
|
-
strapi2.log.error("[magic-mail] Gmail API send failed:", err);
|
|
3529
|
+
strapi2.log.error("[magic-mail] Gmail API send failed:", err.message || err);
|
|
3530
|
+
strapi2.log.error("[magic-mail] Error details:", {
|
|
3531
|
+
name: err.name,
|
|
3532
|
+
code: err.code,
|
|
3533
|
+
cause: err.cause?.message || err.cause,
|
|
3534
|
+
stack: err.stack?.split("\n").slice(0, 3).join("\n")
|
|
3535
|
+
});
|
|
3565
3536
|
throw err;
|
|
3566
3537
|
}
|
|
3567
3538
|
},
|
|
@@ -3604,14 +3575,15 @@ function requireEmailRouter() {
|
|
|
3604
3575
|
config2.tenantId
|
|
3605
3576
|
);
|
|
3606
3577
|
currentAccessToken = newTokens.accessToken;
|
|
3607
|
-
strapi2.log.info("[magic-mail]
|
|
3578
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Microsoft token refreshed successfully");
|
|
3608
3579
|
const { encryptCredentials } = requireEncryption();
|
|
3609
3580
|
const updatedOAuth = encryptCredentials({
|
|
3610
3581
|
...oauth2,
|
|
3611
3582
|
accessToken: newTokens.accessToken,
|
|
3612
3583
|
expiresAt: newTokens.expiresAt
|
|
3613
3584
|
});
|
|
3614
|
-
await strapi2.
|
|
3585
|
+
await strapi2.documents("plugin::magic-mail.email-account").update({
|
|
3586
|
+
documentId: account.documentId,
|
|
3615
3587
|
data: { oauth: updatedOAuth }
|
|
3616
3588
|
});
|
|
3617
3589
|
} catch (refreshErr) {
|
|
@@ -3721,7 +3693,7 @@ function requireEmailRouter() {
|
|
|
3721
3693
|
strapi2.log.error("[magic-mail] Response status:", response.status);
|
|
3722
3694
|
throw new Error(`Microsoft Graph API error: ${response.status} - ${response.statusText}`);
|
|
3723
3695
|
}
|
|
3724
|
-
strapi2.log.info("[magic-mail]
|
|
3696
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via Microsoft Graph API with MIME + custom headers");
|
|
3725
3697
|
strapi2.log.info("[magic-mail] Microsoft adds From/DKIM automatically for DMARC compliance");
|
|
3726
3698
|
return {
|
|
3727
3699
|
messageId: `microsoft-${Date.now()}`,
|
|
@@ -3763,14 +3735,15 @@ function requireEmailRouter() {
|
|
|
3763
3735
|
config2.clientSecret
|
|
3764
3736
|
);
|
|
3765
3737
|
currentAccessToken = newTokens.accessToken;
|
|
3766
|
-
strapi2.log.info("[magic-mail]
|
|
3738
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Token refreshed successfully");
|
|
3767
3739
|
const { encryptCredentials } = requireEncryption();
|
|
3768
3740
|
const updatedOAuth = encryptCredentials({
|
|
3769
3741
|
...oauth2,
|
|
3770
3742
|
accessToken: newTokens.accessToken,
|
|
3771
3743
|
expiresAt: newTokens.expiresAt
|
|
3772
3744
|
});
|
|
3773
|
-
await strapi2.
|
|
3745
|
+
await strapi2.documents("plugin::magic-mail.email-account").update({
|
|
3746
|
+
documentId: account.documentId,
|
|
3774
3747
|
data: { oauth: updatedOAuth }
|
|
3775
3748
|
});
|
|
3776
3749
|
} catch (refreshErr) {
|
|
@@ -3816,7 +3789,7 @@ function requireEmailRouter() {
|
|
|
3816
3789
|
strapi2.log.info(`[magic-mail] Sending email with ${mailOptions.attachments.length} attachment(s)`);
|
|
3817
3790
|
}
|
|
3818
3791
|
const result = await transporter.sendMail(mailOptions);
|
|
3819
|
-
strapi2.log.info("[magic-mail]
|
|
3792
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via Yahoo OAuth");
|
|
3820
3793
|
return {
|
|
3821
3794
|
messageId: result.messageId,
|
|
3822
3795
|
response: result.response
|
|
@@ -3911,7 +3884,7 @@ function requireEmailRouter() {
|
|
|
3911
3884
|
strapi2.log.error("[magic-mail] SendGrid API error:", errorText);
|
|
3912
3885
|
throw new Error(`SendGrid API error: ${response.statusText}`);
|
|
3913
3886
|
}
|
|
3914
|
-
strapi2.log.info("[magic-mail]
|
|
3887
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via SendGrid API");
|
|
3915
3888
|
return {
|
|
3916
3889
|
messageId: response.headers.get("x-message-id") || `sendgrid-${Date.now()}`,
|
|
3917
3890
|
response: "Accepted"
|
|
@@ -3994,7 +3967,7 @@ function requireEmailRouter() {
|
|
|
3994
3967
|
throw new Error(`Mailgun API error: ${response.statusText}`);
|
|
3995
3968
|
}
|
|
3996
3969
|
const result = await response.json();
|
|
3997
|
-
strapi2.log.info("[magic-mail]
|
|
3970
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via Mailgun API");
|
|
3998
3971
|
return {
|
|
3999
3972
|
messageId: result.id || `mailgun-${Date.now()}`,
|
|
4000
3973
|
response: result.message || "Queued"
|
|
@@ -4018,10 +3991,15 @@ function requireEmailRouter() {
|
|
|
4018
3991
|
},
|
|
4019
3992
|
/**
|
|
4020
3993
|
* Update account statistics
|
|
3994
|
+
* Note: This function now expects documentId
|
|
4021
3995
|
*/
|
|
4022
|
-
async updateAccountStats(
|
|
4023
|
-
const account = await strapi2.
|
|
4024
|
-
|
|
3996
|
+
async updateAccountStats(documentId) {
|
|
3997
|
+
const account = await strapi2.documents("plugin::magic-mail.email-account").findOne({
|
|
3998
|
+
documentId
|
|
3999
|
+
});
|
|
4000
|
+
if (!account) return;
|
|
4001
|
+
await strapi2.documents("plugin::magic-mail.email-account").update({
|
|
4002
|
+
documentId,
|
|
4025
4003
|
data: {
|
|
4026
4004
|
emailsSentToday: (account.emailsSentToday || 0) + 1,
|
|
4027
4005
|
emailsSentThisHour: (account.emailsSentThisHour || 0) + 1,
|
|
@@ -4042,7 +4020,7 @@ function requireEmailRouter() {
|
|
|
4042
4020
|
* Get account by name
|
|
4043
4021
|
*/
|
|
4044
4022
|
async getAccountByName(name) {
|
|
4045
|
-
const accounts2 = await strapi2.
|
|
4023
|
+
const accounts2 = await strapi2.documents("plugin::magic-mail.email-account").findMany({
|
|
4046
4024
|
filters: { name, isActive: true },
|
|
4047
4025
|
limit: 1
|
|
4048
4026
|
});
|
|
@@ -4092,7 +4070,7 @@ function requireEmailRouter() {
|
|
|
4092
4070
|
if (html && !text) {
|
|
4093
4071
|
strapi2.log.warn("[magic-mail] Email has HTML but no text alternative - may reduce deliverability");
|
|
4094
4072
|
}
|
|
4095
|
-
strapi2.log.info("[magic-mail]
|
|
4073
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email security validation passed");
|
|
4096
4074
|
},
|
|
4097
4075
|
/**
|
|
4098
4076
|
* Add security headers to email data
|
|
@@ -4132,7 +4110,24 @@ function requireAccountManager() {
|
|
|
4132
4110
|
if (hasRequiredAccountManager) return accountManager;
|
|
4133
4111
|
hasRequiredAccountManager = 1;
|
|
4134
4112
|
const { encryptCredentials, decryptCredentials } = requireEncryption();
|
|
4113
|
+
const EMAIL_ACCOUNT_UID = "plugin::magic-mail.email-account";
|
|
4135
4114
|
accountManager = ({ strapi: strapi2 }) => ({
|
|
4115
|
+
/**
|
|
4116
|
+
* Resolves account ID to documentId (handles both numeric id and documentId)
|
|
4117
|
+
* @param {string|number} idOrDocumentId - Either numeric id or documentId
|
|
4118
|
+
* @returns {Promise<string|null>} The documentId or null if not found
|
|
4119
|
+
*/
|
|
4120
|
+
async resolveDocumentId(idOrDocumentId) {
|
|
4121
|
+
if (idOrDocumentId && !/^\d+$/.test(String(idOrDocumentId))) {
|
|
4122
|
+
return String(idOrDocumentId);
|
|
4123
|
+
}
|
|
4124
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
4125
|
+
filters: { id: Number(idOrDocumentId) },
|
|
4126
|
+
fields: ["documentId"],
|
|
4127
|
+
limit: 1
|
|
4128
|
+
});
|
|
4129
|
+
return accounts2.length > 0 ? accounts2[0].documentId : null;
|
|
4130
|
+
},
|
|
4136
4131
|
/**
|
|
4137
4132
|
* Create new email account
|
|
4138
4133
|
*/
|
|
@@ -4154,7 +4149,7 @@ function requireAccountManager() {
|
|
|
4154
4149
|
if (isPrimary) {
|
|
4155
4150
|
await this.unsetAllPrimary();
|
|
4156
4151
|
}
|
|
4157
|
-
const account = await strapi2.
|
|
4152
|
+
const account = await strapi2.documents(EMAIL_ACCOUNT_UID).create({
|
|
4158
4153
|
data: {
|
|
4159
4154
|
name,
|
|
4160
4155
|
provider,
|
|
@@ -4172,14 +4167,20 @@ function requireAccountManager() {
|
|
|
4172
4167
|
totalEmailsSent: 0
|
|
4173
4168
|
}
|
|
4174
4169
|
});
|
|
4175
|
-
strapi2.log.info(`[magic-mail]
|
|
4170
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Email account created: ${name}`);
|
|
4176
4171
|
return account;
|
|
4177
4172
|
},
|
|
4178
4173
|
/**
|
|
4179
4174
|
* Update email account
|
|
4180
4175
|
*/
|
|
4181
|
-
async updateAccount(
|
|
4182
|
-
const
|
|
4176
|
+
async updateAccount(idOrDocumentId, accountData) {
|
|
4177
|
+
const documentId = await this.resolveDocumentId(idOrDocumentId);
|
|
4178
|
+
if (!documentId) {
|
|
4179
|
+
throw new Error("Account not found");
|
|
4180
|
+
}
|
|
4181
|
+
const existingAccount = await strapi2.documents(EMAIL_ACCOUNT_UID).findOne({
|
|
4182
|
+
documentId
|
|
4183
|
+
});
|
|
4183
4184
|
if (!existingAccount) {
|
|
4184
4185
|
throw new Error("Account not found");
|
|
4185
4186
|
}
|
|
@@ -4201,7 +4202,8 @@ function requireAccountManager() {
|
|
|
4201
4202
|
if (isPrimary && !existingAccount.isPrimary) {
|
|
4202
4203
|
await this.unsetAllPrimary();
|
|
4203
4204
|
}
|
|
4204
|
-
const updatedAccount = await strapi2.
|
|
4205
|
+
const updatedAccount = await strapi2.documents(EMAIL_ACCOUNT_UID).update({
|
|
4206
|
+
documentId,
|
|
4205
4207
|
data: {
|
|
4206
4208
|
name,
|
|
4207
4209
|
description,
|
|
@@ -4217,14 +4219,20 @@ function requireAccountManager() {
|
|
|
4217
4219
|
hourlyLimit
|
|
4218
4220
|
}
|
|
4219
4221
|
});
|
|
4220
|
-
strapi2.log.info(`[magic-mail]
|
|
4222
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Email account updated: ${name} (Active: ${isActive})`);
|
|
4221
4223
|
return updatedAccount;
|
|
4222
4224
|
},
|
|
4223
4225
|
/**
|
|
4224
4226
|
* Test email account
|
|
4225
4227
|
*/
|
|
4226
|
-
async testAccount(
|
|
4227
|
-
const
|
|
4228
|
+
async testAccount(idOrDocumentId, testEmail, testOptions = {}) {
|
|
4229
|
+
const documentId = await this.resolveDocumentId(idOrDocumentId);
|
|
4230
|
+
if (!documentId) {
|
|
4231
|
+
throw new Error("Account not found");
|
|
4232
|
+
}
|
|
4233
|
+
const account = await strapi2.documents(EMAIL_ACCOUNT_UID).findOne({
|
|
4234
|
+
documentId
|
|
4235
|
+
});
|
|
4228
4236
|
if (!account) {
|
|
4229
4237
|
throw new Error("Account not found");
|
|
4230
4238
|
}
|
|
@@ -4303,8 +4311,8 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4303
4311
|
* Get all accounts
|
|
4304
4312
|
*/
|
|
4305
4313
|
async getAllAccounts() {
|
|
4306
|
-
const accounts2 = await strapi2.
|
|
4307
|
-
sort: { priority: "desc" }
|
|
4314
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
4315
|
+
sort: [{ priority: "desc" }]
|
|
4308
4316
|
});
|
|
4309
4317
|
return accounts2.map((account) => ({
|
|
4310
4318
|
...account,
|
|
@@ -4314,8 +4322,14 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4314
4322
|
/**
|
|
4315
4323
|
* Get single account with decrypted config (for editing)
|
|
4316
4324
|
*/
|
|
4317
|
-
async getAccountWithDecryptedConfig(
|
|
4318
|
-
const
|
|
4325
|
+
async getAccountWithDecryptedConfig(idOrDocumentId) {
|
|
4326
|
+
const documentId = await this.resolveDocumentId(idOrDocumentId);
|
|
4327
|
+
if (!documentId) {
|
|
4328
|
+
throw new Error("Account not found");
|
|
4329
|
+
}
|
|
4330
|
+
const account = await strapi2.documents(EMAIL_ACCOUNT_UID).findOne({
|
|
4331
|
+
documentId
|
|
4332
|
+
});
|
|
4319
4333
|
if (!account) {
|
|
4320
4334
|
throw new Error("Account not found");
|
|
4321
4335
|
}
|
|
@@ -4328,19 +4342,24 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4328
4342
|
/**
|
|
4329
4343
|
* Delete account
|
|
4330
4344
|
*/
|
|
4331
|
-
async deleteAccount(
|
|
4332
|
-
await
|
|
4333
|
-
|
|
4345
|
+
async deleteAccount(idOrDocumentId) {
|
|
4346
|
+
const documentId = await this.resolveDocumentId(idOrDocumentId);
|
|
4347
|
+
if (!documentId) {
|
|
4348
|
+
throw new Error("Account not found");
|
|
4349
|
+
}
|
|
4350
|
+
await strapi2.documents(EMAIL_ACCOUNT_UID).delete({ documentId });
|
|
4351
|
+
strapi2.log.info(`[magic-mail] Account deleted: ${documentId}`);
|
|
4334
4352
|
},
|
|
4335
4353
|
/**
|
|
4336
4354
|
* Unset all primary flags
|
|
4337
4355
|
*/
|
|
4338
4356
|
async unsetAllPrimary() {
|
|
4339
|
-
const accounts2 = await strapi2.
|
|
4357
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
4340
4358
|
filters: { isPrimary: true }
|
|
4341
4359
|
});
|
|
4342
4360
|
for (const account of accounts2) {
|
|
4343
|
-
await strapi2.
|
|
4361
|
+
await strapi2.documents(EMAIL_ACCOUNT_UID).update({
|
|
4362
|
+
documentId: account.documentId,
|
|
4344
4363
|
data: { isPrimary: false }
|
|
4345
4364
|
});
|
|
4346
4365
|
}
|
|
@@ -4349,7 +4368,7 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4349
4368
|
* Reset daily/hourly counters (called by cron)
|
|
4350
4369
|
*/
|
|
4351
4370
|
async resetCounters(type = "daily") {
|
|
4352
|
-
const accounts2 = await strapi2.
|
|
4371
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({});
|
|
4353
4372
|
for (const account of accounts2) {
|
|
4354
4373
|
const updateData = {};
|
|
4355
4374
|
if (type === "daily") {
|
|
@@ -4357,11 +4376,12 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4357
4376
|
} else if (type === "hourly") {
|
|
4358
4377
|
updateData.emailsSentThisHour = 0;
|
|
4359
4378
|
}
|
|
4360
|
-
await strapi2.
|
|
4379
|
+
await strapi2.documents(EMAIL_ACCOUNT_UID).update({
|
|
4380
|
+
documentId: account.documentId,
|
|
4361
4381
|
data: updateData
|
|
4362
4382
|
});
|
|
4363
4383
|
}
|
|
4364
|
-
strapi2.log.info(`[magic-mail]
|
|
4384
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] ${type} counters reset`);
|
|
4365
4385
|
}
|
|
4366
4386
|
});
|
|
4367
4387
|
return accountManager;
|
|
@@ -4419,7 +4439,7 @@ function requireOauth() {
|
|
|
4419
4439
|
throw new Error(`Failed to exchange code for tokens: ${response.status}`);
|
|
4420
4440
|
}
|
|
4421
4441
|
const tokens = await response.json();
|
|
4422
|
-
strapi2.log.info("[magic-mail]
|
|
4442
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Tokens received from Google");
|
|
4423
4443
|
if (!tokens.access_token) {
|
|
4424
4444
|
throw new Error("No access token received from Google");
|
|
4425
4445
|
}
|
|
@@ -4433,7 +4453,7 @@ function requireOauth() {
|
|
|
4433
4453
|
throw new Error("Failed to get user email from Google");
|
|
4434
4454
|
}
|
|
4435
4455
|
const userInfo = await userInfoResponse.json();
|
|
4436
|
-
strapi2.log.info(`[magic-mail]
|
|
4456
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Got user email from Google: ${userInfo.email}`);
|
|
4437
4457
|
if (!userInfo.email) {
|
|
4438
4458
|
strapi2.log.error("[magic-mail] userInfo:", userInfo);
|
|
4439
4459
|
throw new Error("Google did not provide email address");
|
|
@@ -4537,7 +4557,7 @@ function requireOauth() {
|
|
|
4537
4557
|
throw new Error(`Failed to exchange code for tokens: ${response.status} - ${errorData}`);
|
|
4538
4558
|
}
|
|
4539
4559
|
const tokens = await response.json();
|
|
4540
|
-
strapi2.log.info("[magic-mail]
|
|
4560
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Tokens received from Microsoft");
|
|
4541
4561
|
strapi2.log.info("[magic-mail] Has access_token:", !!tokens.access_token);
|
|
4542
4562
|
strapi2.log.info("[magic-mail] Has refresh_token:", !!tokens.refresh_token);
|
|
4543
4563
|
strapi2.log.info("[magic-mail] Has id_token:", !!tokens.id_token);
|
|
@@ -4550,7 +4570,7 @@ function requireOauth() {
|
|
|
4550
4570
|
const payloadBase64 = tokens.id_token.split(".")[1];
|
|
4551
4571
|
const payload = JSON.parse(Buffer.from(payloadBase64, "base64").toString());
|
|
4552
4572
|
email = payload.email || payload.preferred_username || payload.upn;
|
|
4553
|
-
strapi2.log.info(`[magic-mail]
|
|
4573
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Got email from Microsoft ID token: ${email}`);
|
|
4554
4574
|
} catch (jwtErr) {
|
|
4555
4575
|
strapi2.log.warn("[magic-mail] Could not decode ID token:", jwtErr.message);
|
|
4556
4576
|
}
|
|
@@ -4572,7 +4592,7 @@ function requireOauth() {
|
|
|
4572
4592
|
const userInfo = await userInfoResponse.json();
|
|
4573
4593
|
strapi2.log.info("[magic-mail] User info from Graph:", JSON.stringify(userInfo, null, 2));
|
|
4574
4594
|
email = userInfo.mail || userInfo.userPrincipalName;
|
|
4575
|
-
strapi2.log.info(`[magic-mail]
|
|
4595
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Got email from Microsoft Graph: ${email}`);
|
|
4576
4596
|
}
|
|
4577
4597
|
if (!email) {
|
|
4578
4598
|
strapi2.log.error("[magic-mail] Microsoft did not provide email - ID token and Graph API both failed");
|
|
@@ -4616,7 +4636,7 @@ function requireOauth() {
|
|
|
4616
4636
|
throw new Error(`Failed to refresh Microsoft tokens: ${response.status}`);
|
|
4617
4637
|
}
|
|
4618
4638
|
const tokens = await response.json();
|
|
4619
|
-
strapi2.log.info("[magic-mail]
|
|
4639
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Microsoft tokens refreshed successfully");
|
|
4620
4640
|
return {
|
|
4621
4641
|
accessToken: tokens.access_token,
|
|
4622
4642
|
expiresAt: new Date(Date.now() + (tokens.expires_in || 3600) * 1e3)
|
|
@@ -4671,7 +4691,7 @@ function requireOauth() {
|
|
|
4671
4691
|
throw new Error(`Failed to exchange code for tokens: ${response.status}`);
|
|
4672
4692
|
}
|
|
4673
4693
|
const tokens = await response.json();
|
|
4674
|
-
strapi2.log.info("[magic-mail]
|
|
4694
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Tokens received from Yahoo");
|
|
4675
4695
|
if (!tokens.access_token) {
|
|
4676
4696
|
throw new Error("No access token received from Yahoo");
|
|
4677
4697
|
}
|
|
@@ -4686,7 +4706,7 @@ function requireOauth() {
|
|
|
4686
4706
|
}
|
|
4687
4707
|
const userInfo = await userInfoResponse.json();
|
|
4688
4708
|
const email = userInfo.email;
|
|
4689
|
-
strapi2.log.info(`[magic-mail]
|
|
4709
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Got email from Yahoo: ${email}`);
|
|
4690
4710
|
if (!email) {
|
|
4691
4711
|
throw new Error("Yahoo did not provide email address");
|
|
4692
4712
|
}
|
|
@@ -4747,7 +4767,7 @@ function requireOauth() {
|
|
|
4747
4767
|
refreshToken: tokenData.refreshToken,
|
|
4748
4768
|
expiresAt: tokenData.expiresAt
|
|
4749
4769
|
});
|
|
4750
|
-
const account = await strapi2.
|
|
4770
|
+
const account = await strapi2.documents("plugin::magic-mail.email-account").create({
|
|
4751
4771
|
data: {
|
|
4752
4772
|
name: accountDetails.name,
|
|
4753
4773
|
description: accountDetails.description || "",
|
|
@@ -4757,7 +4777,7 @@ function requireOauth() {
|
|
|
4757
4777
|
oauth: encryptedOAuth,
|
|
4758
4778
|
// OAuth tokens
|
|
4759
4779
|
fromEmail: tokenData.email,
|
|
4760
|
-
//
|
|
4780
|
+
// [SUCCESS] Use email from Google, not from accountDetails
|
|
4761
4781
|
fromName: accountDetails.fromName || tokenData.email.split("@")[0],
|
|
4762
4782
|
replyTo: accountDetails.replyTo || tokenData.email,
|
|
4763
4783
|
isActive: true,
|
|
@@ -4770,13 +4790,13 @@ function requireOauth() {
|
|
|
4770
4790
|
totalEmailsSent: 0
|
|
4771
4791
|
}
|
|
4772
4792
|
});
|
|
4773
|
-
strapi2.log.info(`[magic-mail]
|
|
4793
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] OAuth account created: ${accountDetails.name} (${tokenData.email})`);
|
|
4774
4794
|
return account;
|
|
4775
4795
|
}
|
|
4776
4796
|
});
|
|
4777
4797
|
return oauth;
|
|
4778
4798
|
}
|
|
4779
|
-
const version = "2.0.
|
|
4799
|
+
const version = "2.0.3";
|
|
4780
4800
|
const require$$2 = {
|
|
4781
4801
|
version
|
|
4782
4802
|
};
|
|
@@ -4870,14 +4890,14 @@ function requireLicenseGuard() {
|
|
|
4870
4890
|
});
|
|
4871
4891
|
const data = await response.json();
|
|
4872
4892
|
if (data.success) {
|
|
4873
|
-
strapi2.log.info("[magic-mail]
|
|
4893
|
+
strapi2.log.info("[magic-mail] [SUCCESS] License created:", data.data.licenseKey);
|
|
4874
4894
|
return data.data;
|
|
4875
4895
|
} else {
|
|
4876
|
-
strapi2.log.error("[magic-mail]
|
|
4896
|
+
strapi2.log.error("[magic-mail] [ERROR] License creation failed:", data);
|
|
4877
4897
|
return null;
|
|
4878
4898
|
}
|
|
4879
4899
|
} catch (error) {
|
|
4880
|
-
strapi2.log.error("[magic-mail]
|
|
4900
|
+
strapi2.log.error("[magic-mail] [ERROR] Error creating license:", error);
|
|
4881
4901
|
return null;
|
|
4882
4902
|
}
|
|
4883
4903
|
},
|
|
@@ -4905,10 +4925,10 @@ function requireLicenseGuard() {
|
|
|
4905
4925
|
}
|
|
4906
4926
|
} catch (error) {
|
|
4907
4927
|
if (allowGracePeriod) {
|
|
4908
|
-
strapi2.log.warn("[magic-mail]
|
|
4928
|
+
strapi2.log.warn("[magic-mail] [WARNING] License verification timeout - grace period active");
|
|
4909
4929
|
return { valid: true, data: null, gracePeriod: true };
|
|
4910
4930
|
}
|
|
4911
|
-
strapi2.log.error("[magic-mail]
|
|
4931
|
+
strapi2.log.error("[magic-mail] [ERROR] License verification error:", error.message);
|
|
4912
4932
|
return { valid: false, data: null };
|
|
4913
4933
|
}
|
|
4914
4934
|
},
|
|
@@ -4961,7 +4981,7 @@ function requireLicenseGuard() {
|
|
|
4961
4981
|
name: "magic-mail"
|
|
4962
4982
|
});
|
|
4963
4983
|
await pluginStore.set({ key: "licenseKey", value: licenseKey });
|
|
4964
|
-
strapi2.log.info(`[magic-mail]
|
|
4984
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] License key stored: ${licenseKey.substring(0, 8)}...`);
|
|
4965
4985
|
},
|
|
4966
4986
|
startPinging(licenseKey, intervalMinutes = 15) {
|
|
4967
4987
|
this.pingLicense(licenseKey);
|
|
@@ -4990,7 +5010,7 @@ function requireLicenseGuard() {
|
|
|
4990
5010
|
const license2 = await this.getLicenseByKey(licenseKey);
|
|
4991
5011
|
return license2;
|
|
4992
5012
|
} catch (error) {
|
|
4993
|
-
strapi2.log.error(`[magic-mail]
|
|
5013
|
+
strapi2.log.error(`[magic-mail] [ERROR] Error loading license:`, error);
|
|
4994
5014
|
return null;
|
|
4995
5015
|
}
|
|
4996
5016
|
},
|
|
@@ -5040,7 +5060,7 @@ function requireLicenseGuard() {
|
|
|
5040
5060
|
*/
|
|
5041
5061
|
async initialize() {
|
|
5042
5062
|
try {
|
|
5043
|
-
strapi2.log.info("
|
|
5063
|
+
strapi2.log.info("[INIT] Initializing License Guard...");
|
|
5044
5064
|
const pluginStore = strapi2.store({
|
|
5045
5065
|
type: "plugin",
|
|
5046
5066
|
name: "magic-mail"
|
|
@@ -5058,53 +5078,53 @@ function requireLicenseGuard() {
|
|
|
5058
5078
|
strapi2.log.info("──────────────────────────────────────────────────────────");
|
|
5059
5079
|
strapi2.log.info(`📦 Plugin Store Check:`);
|
|
5060
5080
|
if (licenseKey) {
|
|
5061
|
-
strapi2.log.info(`
|
|
5062
|
-
strapi2.log.info(`
|
|
5081
|
+
strapi2.log.info(` [SUCCESS] License Key found: ${licenseKey}`);
|
|
5082
|
+
strapi2.log.info(` [LICENSE] Key (short): ${licenseKey.substring(0, 10)}...`);
|
|
5063
5083
|
if (lastValidated) {
|
|
5064
5084
|
const lastValidatedDate = new Date(lastValidated);
|
|
5065
5085
|
const hoursAgo = Math.floor((now.getTime() - lastValidatedDate.getTime()) / (1e3 * 60 * 60));
|
|
5066
|
-
strapi2.log.info(`
|
|
5086
|
+
strapi2.log.info(` [TIME] Last validated: ${hoursAgo}h ago (Grace: ${withinGracePeriod ? "ACTIVE" : "EXPIRED"})`);
|
|
5067
5087
|
} else {
|
|
5068
|
-
strapi2.log.info(`
|
|
5088
|
+
strapi2.log.info(` [TIME] Last validated: Never (Grace: ACTIVE for first ${gracePeriodHours}h)`);
|
|
5069
5089
|
}
|
|
5070
5090
|
} else {
|
|
5071
|
-
strapi2.log.info(`
|
|
5091
|
+
strapi2.log.info(` [ERROR] No license key stored`);
|
|
5072
5092
|
}
|
|
5073
5093
|
strapi2.log.info("──────────────────────────────────────────────────────────");
|
|
5074
5094
|
if (!licenseKey) {
|
|
5075
|
-
strapi2.log.info("
|
|
5076
|
-
strapi2.log.info("
|
|
5095
|
+
strapi2.log.info("[DEMO] No license found - Running in demo mode");
|
|
5096
|
+
strapi2.log.info("[INFO] Create a license in the admin panel to activate full features");
|
|
5077
5097
|
return {
|
|
5078
5098
|
valid: false,
|
|
5079
5099
|
demo: true,
|
|
5080
5100
|
data: null
|
|
5081
5101
|
};
|
|
5082
5102
|
}
|
|
5083
|
-
strapi2.log.info("
|
|
5103
|
+
strapi2.log.info("[VERIFY] Verifying stored license key...");
|
|
5084
5104
|
const verification = await this.verifyLicense(licenseKey, withinGracePeriod);
|
|
5085
5105
|
if (verification.valid) {
|
|
5086
5106
|
const license2 = await this.getLicenseByKey(licenseKey);
|
|
5087
|
-
strapi2.log.info(
|
|
5107
|
+
strapi2.log.info(`[SUCCESS] License verified online: ACTIVE (Key: ${licenseKey.substring(0, 10)}...)`);
|
|
5088
5108
|
await pluginStore.set({
|
|
5089
5109
|
key: "lastValidated",
|
|
5090
5110
|
value: now.toISOString()
|
|
5091
5111
|
});
|
|
5092
|
-
strapi2.log.info("
|
|
5112
|
+
strapi2.log.info("[SUCCESS] License is valid and active");
|
|
5093
5113
|
const pingInterval = this.startPinging(licenseKey, 15);
|
|
5094
|
-
strapi2.log.info("
|
|
5114
|
+
strapi2.log.info("[PING] Started pinging license every 15 minutes");
|
|
5095
5115
|
strapi2.licenseGuardMagicMail = {
|
|
5096
5116
|
licenseKey,
|
|
5097
5117
|
pingInterval,
|
|
5098
5118
|
data: verification.data
|
|
5099
5119
|
};
|
|
5100
5120
|
strapi2.log.info("╔════════════════════════════════════════════════════════════════╗");
|
|
5101
|
-
strapi2.log.info("║
|
|
5121
|
+
strapi2.log.info("║ [SUCCESS] MAGIC MAIL PLUGIN LICENSE ACTIVE ║");
|
|
5102
5122
|
strapi2.log.info("║ ║");
|
|
5103
5123
|
strapi2.log.info(`║ License: ${licenseKey.padEnd(38, " ")}║`);
|
|
5104
5124
|
strapi2.log.info(`║ User: ${(license2?.firstName + " " + license2?.lastName).padEnd(41, " ")}║`);
|
|
5105
5125
|
strapi2.log.info(`║ Email: ${(license2?.email || "N/A").padEnd(40, " ")}║`);
|
|
5106
5126
|
strapi2.log.info("║ ║");
|
|
5107
|
-
strapi2.log.info("║
|
|
5127
|
+
strapi2.log.info("║ [AUTO] Pinging every 15 minutes ║");
|
|
5108
5128
|
strapi2.log.info("╚════════════════════════════════════════════════════════════════╝");
|
|
5109
5129
|
return {
|
|
5110
5130
|
valid: true,
|
|
@@ -5113,9 +5133,9 @@ function requireLicenseGuard() {
|
|
|
5113
5133
|
gracePeriod: verification.gracePeriod || false
|
|
5114
5134
|
};
|
|
5115
5135
|
} else {
|
|
5116
|
-
strapi2.log.error(
|
|
5136
|
+
strapi2.log.error(`[ERROR] License validation failed (Key: ${licenseKey.substring(0, 10)}...)`);
|
|
5117
5137
|
strapi2.log.info("──────────────────────────────────────────────────────────");
|
|
5118
|
-
strapi2.log.info("
|
|
5138
|
+
strapi2.log.info("[WARNING] Running in demo mode with limited features");
|
|
5119
5139
|
return {
|
|
5120
5140
|
valid: false,
|
|
5121
5141
|
demo: true,
|
|
@@ -5124,7 +5144,7 @@ function requireLicenseGuard() {
|
|
|
5124
5144
|
};
|
|
5125
5145
|
}
|
|
5126
5146
|
} catch (error) {
|
|
5127
|
-
strapi2.log.error("
|
|
5147
|
+
strapi2.log.error("[ERROR] Error initializing License Guard:", error);
|
|
5128
5148
|
return {
|
|
5129
5149
|
valid: false,
|
|
5130
5150
|
demo: true,
|
|
@@ -5144,6 +5164,8 @@ function requireEmailDesigner() {
|
|
|
5144
5164
|
const Mustache = require$$0__default$2.default;
|
|
5145
5165
|
const htmlToTextLib = require$$1__default$1.default;
|
|
5146
5166
|
const decode = require$$2__default.default;
|
|
5167
|
+
const EMAIL_TEMPLATE_UID = "plugin::magic-mail.email-template";
|
|
5168
|
+
const EMAIL_TEMPLATE_VERSION_UID = "plugin::magic-mail.email-template-version";
|
|
5147
5169
|
const convertHtmlToText = (html, options2 = { wordwrap: 130 }) => {
|
|
5148
5170
|
try {
|
|
5149
5171
|
if (!html || typeof html !== "string") {
|
|
@@ -5181,24 +5203,36 @@ function requireEmailDesigner() {
|
|
|
5181
5203
|
* Get all templates
|
|
5182
5204
|
*/
|
|
5183
5205
|
async findAll(filters = {}) {
|
|
5184
|
-
return strapi2.
|
|
5206
|
+
return strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5185
5207
|
filters,
|
|
5186
|
-
sort: { createdAt: "desc" }
|
|
5208
|
+
sort: [{ createdAt: "desc" }]
|
|
5187
5209
|
});
|
|
5188
5210
|
},
|
|
5189
5211
|
/**
|
|
5190
|
-
* Get template by ID with populated versions
|
|
5212
|
+
* Get template by ID (documentId) with populated versions
|
|
5191
5213
|
*/
|
|
5192
|
-
async findOne(
|
|
5193
|
-
return strapi2.
|
|
5214
|
+
async findOne(documentId) {
|
|
5215
|
+
return strapi2.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
5216
|
+
documentId,
|
|
5194
5217
|
populate: ["versions"]
|
|
5195
5218
|
});
|
|
5196
5219
|
},
|
|
5220
|
+
/**
|
|
5221
|
+
* Get template by numeric ID (for backward compatibility)
|
|
5222
|
+
*/
|
|
5223
|
+
async findById(id) {
|
|
5224
|
+
const results = await strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5225
|
+
filters: { id },
|
|
5226
|
+
limit: 1,
|
|
5227
|
+
populate: ["versions"]
|
|
5228
|
+
});
|
|
5229
|
+
return results.length > 0 ? results[0] : null;
|
|
5230
|
+
},
|
|
5197
5231
|
/**
|
|
5198
5232
|
* Get template by reference ID
|
|
5199
5233
|
*/
|
|
5200
5234
|
async findByReferenceId(templateReferenceId) {
|
|
5201
|
-
const results = await strapi2.
|
|
5235
|
+
const results = await strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5202
5236
|
filters: { templateReferenceId },
|
|
5203
5237
|
limit: 1
|
|
5204
5238
|
});
|
|
@@ -5206,17 +5240,11 @@ function requireEmailDesigner() {
|
|
|
5206
5240
|
},
|
|
5207
5241
|
/**
|
|
5208
5242
|
* Create new template with automatic initial version
|
|
5209
|
-
*
|
|
5210
|
-
* Steps:
|
|
5211
|
-
* 1. Check license limits
|
|
5212
|
-
* 2. Validate reference ID is unique
|
|
5213
|
-
* 3. Create template
|
|
5214
|
-
* 4. Create initial version (if versioning enabled)
|
|
5215
5243
|
*/
|
|
5216
5244
|
async create(data) {
|
|
5217
|
-
strapi2.log.info("[magic-mail]
|
|
5245
|
+
strapi2.log.info("[magic-mail] [TEST] Creating new template...");
|
|
5218
5246
|
const maxTemplates = await strapi2.plugin("magic-mail").service("license-guard").getMaxEmailTemplates();
|
|
5219
|
-
const currentCount = await strapi2.
|
|
5247
|
+
const currentCount = await strapi2.documents(EMAIL_TEMPLATE_UID).count();
|
|
5220
5248
|
if (maxTemplates !== -1 && currentCount >= maxTemplates) {
|
|
5221
5249
|
throw new Error(
|
|
5222
5250
|
`Template limit reached (${maxTemplates}). Upgrade your license to create more templates.`
|
|
@@ -5228,17 +5256,17 @@ function requireEmailDesigner() {
|
|
|
5228
5256
|
throw new Error(`Template with reference ID ${data.templateReferenceId} already exists`);
|
|
5229
5257
|
}
|
|
5230
5258
|
}
|
|
5231
|
-
const template = await strapi2.
|
|
5259
|
+
const template = await strapi2.documents(EMAIL_TEMPLATE_UID).create({
|
|
5232
5260
|
data: {
|
|
5233
5261
|
...data,
|
|
5234
5262
|
isActive: data.isActive !== void 0 ? data.isActive : true
|
|
5235
5263
|
}
|
|
5236
5264
|
});
|
|
5237
|
-
strapi2.log.info(`[magic-mail]
|
|
5265
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template created: documentId=${template.documentId}, name="${template.name}"`);
|
|
5238
5266
|
const hasVersioning = await strapi2.plugin("magic-mail").service("license-guard").hasFeature("email-designer-versioning");
|
|
5239
5267
|
if (hasVersioning) {
|
|
5240
|
-
strapi2.log.info("[magic-mail]
|
|
5241
|
-
await this.createVersion(template.
|
|
5268
|
+
strapi2.log.info("[magic-mail] [SAVE] Creating initial version...");
|
|
5269
|
+
await this.createVersion(template.documentId, {
|
|
5242
5270
|
name: data.name,
|
|
5243
5271
|
subject: data.subject,
|
|
5244
5272
|
design: data.design,
|
|
@@ -5246,33 +5274,26 @@ function requireEmailDesigner() {
|
|
|
5246
5274
|
bodyText: data.bodyText,
|
|
5247
5275
|
tags: data.tags
|
|
5248
5276
|
});
|
|
5249
|
-
strapi2.log.info("[magic-mail]
|
|
5277
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Initial version created");
|
|
5250
5278
|
} else {
|
|
5251
|
-
strapi2.log.info("[magic-mail]
|
|
5279
|
+
strapi2.log.info("[magic-mail] [SKIP] Versioning not enabled, skipping initial version");
|
|
5252
5280
|
}
|
|
5253
5281
|
return template;
|
|
5254
5282
|
},
|
|
5255
5283
|
/**
|
|
5256
5284
|
* Update template with automatic version snapshot
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
* 3. Update template with new data
|
|
5262
|
-
*
|
|
5263
|
-
* IMPORTANT: Version is created BEFORE update to preserve old state!
|
|
5264
|
-
*/
|
|
5265
|
-
async update(id, data) {
|
|
5266
|
-
strapi2.log.info(`[magic-mail] 🔄 Updating template ID: ${id}`);
|
|
5267
|
-
const template = await this.findOne(id);
|
|
5285
|
+
*/
|
|
5286
|
+
async update(documentId, data) {
|
|
5287
|
+
strapi2.log.info(`[magic-mail] [UPDATE] Updating template documentId: ${documentId}`);
|
|
5288
|
+
const template = await this.findOne(documentId);
|
|
5268
5289
|
if (!template) {
|
|
5269
5290
|
throw new Error("Template not found");
|
|
5270
5291
|
}
|
|
5271
|
-
strapi2.log.info(`[magic-mail]
|
|
5292
|
+
strapi2.log.info(`[magic-mail] [INFO] Found template: documentId=${template.documentId}, name="${template.name}"`);
|
|
5272
5293
|
const hasVersioning = await strapi2.plugin("magic-mail").service("license-guard").hasFeature("email-designer-versioning");
|
|
5273
5294
|
if (hasVersioning) {
|
|
5274
|
-
strapi2.log.info("[magic-mail]
|
|
5275
|
-
await this.createVersion(template.
|
|
5295
|
+
strapi2.log.info("[magic-mail] [SAVE] Creating version snapshot before update...");
|
|
5296
|
+
await this.createVersion(template.documentId, {
|
|
5276
5297
|
name: template.name,
|
|
5277
5298
|
subject: template.subject,
|
|
5278
5299
|
design: template.design,
|
|
@@ -5280,66 +5301,62 @@ function requireEmailDesigner() {
|
|
|
5280
5301
|
bodyText: template.bodyText,
|
|
5281
5302
|
tags: template.tags
|
|
5282
5303
|
});
|
|
5283
|
-
strapi2.log.info("[magic-mail]
|
|
5284
|
-
} else {
|
|
5285
|
-
strapi2.log.info("[magic-mail] ⏭️ Versioning not enabled, skipping version snapshot");
|
|
5304
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Version snapshot created");
|
|
5286
5305
|
}
|
|
5287
5306
|
const updateData = { ...data };
|
|
5288
5307
|
if ("versions" in updateData) {
|
|
5289
5308
|
delete updateData.versions;
|
|
5290
|
-
strapi2.log.warn("[magic-mail]
|
|
5309
|
+
strapi2.log.warn("[magic-mail] [WARNING] Removed versions field from update data");
|
|
5291
5310
|
}
|
|
5292
|
-
const updated = await strapi2.
|
|
5311
|
+
const updated = await strapi2.documents(EMAIL_TEMPLATE_UID).update({
|
|
5312
|
+
documentId,
|
|
5293
5313
|
data: updateData
|
|
5294
5314
|
});
|
|
5295
|
-
strapi2.log.info(`[magic-mail]
|
|
5315
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template updated: documentId=${updated.documentId}`);
|
|
5296
5316
|
return updated;
|
|
5297
5317
|
},
|
|
5298
5318
|
/**
|
|
5299
5319
|
* Delete template and all its versions
|
|
5300
|
-
*
|
|
5301
|
-
* Uses Document Service for version deletion to ensure cascading delete
|
|
5302
5320
|
*/
|
|
5303
|
-
async delete(
|
|
5304
|
-
strapi2.log.info(`[magic-mail]
|
|
5305
|
-
const template = await this.findOne(
|
|
5321
|
+
async delete(documentId) {
|
|
5322
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleting template documentId: ${documentId}`);
|
|
5323
|
+
const template = await this.findOne(documentId);
|
|
5306
5324
|
if (!template) {
|
|
5307
5325
|
throw new Error("Template not found");
|
|
5308
5326
|
}
|
|
5309
|
-
strapi2.log.info(`[magic-mail]
|
|
5310
|
-
const allVersions = await strapi2.documents(
|
|
5327
|
+
strapi2.log.info(`[magic-mail] [DELETE] Template: documentId=${template.documentId}, name="${template.name}"`);
|
|
5328
|
+
const allVersions = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findMany({
|
|
5311
5329
|
filters: {
|
|
5312
5330
|
template: {
|
|
5313
5331
|
documentId: template.documentId
|
|
5314
5332
|
}
|
|
5315
5333
|
}
|
|
5316
5334
|
});
|
|
5317
|
-
strapi2.log.info(`[magic-mail]
|
|
5335
|
+
strapi2.log.info(`[magic-mail] [DELETE] Found ${allVersions.length} versions to delete`);
|
|
5318
5336
|
for (const version2 of allVersions) {
|
|
5319
5337
|
try {
|
|
5320
|
-
await strapi2.documents(
|
|
5321
|
-
|
|
5338
|
+
await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).delete({
|
|
5339
|
+
documentId: version2.documentId
|
|
5340
|
+
});
|
|
5341
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleted version #${version2.versionNumber}`);
|
|
5322
5342
|
} catch (versionError) {
|
|
5323
|
-
strapi2.log.warn(`[magic-mail]
|
|
5343
|
+
strapi2.log.warn(`[magic-mail] [WARNING] Failed to delete version: ${versionError.message}`);
|
|
5324
5344
|
}
|
|
5325
5345
|
}
|
|
5326
|
-
const result = await strapi2.
|
|
5327
|
-
strapi2.log.info(`[magic-mail]
|
|
5346
|
+
const result = await strapi2.documents(EMAIL_TEMPLATE_UID).delete({ documentId });
|
|
5347
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template "${template.name}" and ${allVersions.length} versions deleted`);
|
|
5328
5348
|
return result;
|
|
5329
5349
|
},
|
|
5330
5350
|
/**
|
|
5331
5351
|
* Duplicate template
|
|
5332
|
-
*
|
|
5333
|
-
* Creates a copy of an existing template with " copy" suffix
|
|
5334
|
-
* Does NOT copy versions, starts fresh
|
|
5335
5352
|
*/
|
|
5336
|
-
async duplicate(
|
|
5337
|
-
strapi2.log.info(`[magic-mail]
|
|
5338
|
-
const original = await this.findOne(
|
|
5353
|
+
async duplicate(documentId) {
|
|
5354
|
+
strapi2.log.info(`[magic-mail] [INFO] Duplicating template documentId: ${documentId}`);
|
|
5355
|
+
const original = await this.findOne(documentId);
|
|
5339
5356
|
if (!original) {
|
|
5340
5357
|
throw new Error("Template not found");
|
|
5341
5358
|
}
|
|
5342
|
-
strapi2.log.info(`[magic-mail]
|
|
5359
|
+
strapi2.log.info(`[magic-mail] [PACKAGE] Original template: documentId=${original.documentId}, name="${original.name}"`);
|
|
5343
5360
|
const duplicateData = {
|
|
5344
5361
|
name: `${original.name} copy`,
|
|
5345
5362
|
subject: original.subject,
|
|
@@ -5349,11 +5366,10 @@ function requireEmailDesigner() {
|
|
|
5349
5366
|
category: original.category,
|
|
5350
5367
|
tags: original.tags,
|
|
5351
5368
|
isActive: original.isActive,
|
|
5352
|
-
// Generate new templateReferenceId (unique ID)
|
|
5353
5369
|
templateReferenceId: Date.now() + Math.floor(Math.random() * 1e3)
|
|
5354
5370
|
};
|
|
5355
5371
|
const duplicated = await this.create(duplicateData);
|
|
5356
|
-
strapi2.log.info(`[magic-mail]
|
|
5372
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template duplicated: documentId=${duplicated.documentId}`);
|
|
5357
5373
|
return duplicated;
|
|
5358
5374
|
},
|
|
5359
5375
|
// ============================================================
|
|
@@ -5361,134 +5377,81 @@ function requireEmailDesigner() {
|
|
|
5361
5377
|
// ============================================================
|
|
5362
5378
|
/**
|
|
5363
5379
|
* Create a new version for a template
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
* 3. Create version WITH template relation
|
|
5371
|
-
*
|
|
5372
|
-
* @param {number} templateId - Numeric ID of template
|
|
5373
|
-
* @param {object} data - Version data (name, subject, bodyHtml, etc)
|
|
5374
|
-
* @returns {object} Created version
|
|
5375
|
-
*/
|
|
5376
|
-
async createVersion(templateId, data) {
|
|
5377
|
-
strapi2.log.info(`[magic-mail] 📸 Creating version for template ID: ${templateId}`);
|
|
5378
|
-
const template = await strapi2.entityService.findOne("plugin::magic-mail.email-template", templateId);
|
|
5380
|
+
*/
|
|
5381
|
+
async createVersion(templateDocumentId, data) {
|
|
5382
|
+
strapi2.log.info(`[magic-mail] [SNAPSHOT] Creating version for template documentId: ${templateDocumentId}`);
|
|
5383
|
+
const template = await strapi2.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
5384
|
+
documentId: templateDocumentId
|
|
5385
|
+
});
|
|
5379
5386
|
if (!template) {
|
|
5380
|
-
throw new Error(`Template ${
|
|
5387
|
+
throw new Error(`Template ${templateDocumentId} not found`);
|
|
5381
5388
|
}
|
|
5382
|
-
strapi2.log.info(`[magic-mail]
|
|
5383
|
-
const existingVersions = await strapi2.
|
|
5389
|
+
strapi2.log.info(`[magic-mail] [PACKAGE] Template found: documentId=${template.documentId}, name="${template.name}"`);
|
|
5390
|
+
const existingVersions = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findMany({
|
|
5384
5391
|
filters: {
|
|
5385
5392
|
template: {
|
|
5386
|
-
|
|
5387
|
-
// Use numeric ID in filter
|
|
5393
|
+
documentId: templateDocumentId
|
|
5388
5394
|
}
|
|
5389
5395
|
},
|
|
5390
|
-
sort: { versionNumber: "desc" }
|
|
5396
|
+
sort: [{ versionNumber: "desc" }]
|
|
5391
5397
|
});
|
|
5392
5398
|
const versionNumber = existingVersions.length > 0 ? Math.max(...existingVersions.map((v) => v.versionNumber || 0)) + 1 : 1;
|
|
5393
|
-
strapi2.log.info(`[magic-mail]
|
|
5394
|
-
const createdVersion = await strapi2.
|
|
5399
|
+
strapi2.log.info(`[magic-mail] [STATS] Existing versions: ${existingVersions.length} → Next version: #${versionNumber}`);
|
|
5400
|
+
const createdVersion = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).create({
|
|
5395
5401
|
data: {
|
|
5396
5402
|
versionNumber,
|
|
5397
5403
|
...data,
|
|
5398
|
-
template:
|
|
5399
|
-
|
|
5400
|
-
// ✅ Use connect array for Strapi v5!
|
|
5401
|
-
}
|
|
5404
|
+
template: templateDocumentId
|
|
5405
|
+
// Document Service handles relations with documentId
|
|
5402
5406
|
}
|
|
5403
5407
|
});
|
|
5404
|
-
strapi2.log.info(`[magic-mail]
|
|
5405
|
-
|
|
5406
|
-
"plugin::magic-mail.email-template-version",
|
|
5407
|
-
createdVersion.id,
|
|
5408
|
-
{
|
|
5409
|
-
populate: ["template"]
|
|
5410
|
-
}
|
|
5411
|
-
);
|
|
5412
|
-
strapi2.log.info(
|
|
5413
|
-
`[magic-mail] ✅ Version created successfully:
|
|
5414
|
-
- Version ID: ${createdVersion.id}
|
|
5415
|
-
- Version #: ${versionNumber}
|
|
5416
|
-
- Template ID: ${templateId}
|
|
5417
|
-
- Has template relation: ${!!verifiedVersion.template}
|
|
5418
|
-
- Template in DB: ${verifiedVersion.template?.id || "NULL"}`
|
|
5419
|
-
);
|
|
5420
|
-
if (!verifiedVersion.template) {
|
|
5421
|
-
strapi2.log.error(
|
|
5422
|
-
`[magic-mail] ❌ CRITICAL: Version ${createdVersion.id} was created but template relation was NOT set!
|
|
5423
|
-
This is a Strapi v5 relation bug. Investigating...`
|
|
5424
|
-
);
|
|
5425
|
-
}
|
|
5426
|
-
return verifiedVersion;
|
|
5408
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Version created: documentId=${createdVersion.documentId}, v${versionNumber}`);
|
|
5409
|
+
return createdVersion;
|
|
5427
5410
|
},
|
|
5428
5411
|
/**
|
|
5429
5412
|
* Get all versions for a template
|
|
5430
5413
|
*/
|
|
5431
|
-
async getVersions(
|
|
5432
|
-
strapi2.log.info(`[magic-mail] 📜 Fetching versions for template
|
|
5433
|
-
const template = await strapi2.
|
|
5414
|
+
async getVersions(templateDocumentId) {
|
|
5415
|
+
strapi2.log.info(`[magic-mail] 📜 Fetching versions for template documentId: ${templateDocumentId}`);
|
|
5416
|
+
const template = await strapi2.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
5417
|
+
documentId: templateDocumentId,
|
|
5434
5418
|
populate: ["versions"]
|
|
5435
5419
|
});
|
|
5436
5420
|
if (!template) {
|
|
5437
5421
|
throw new Error("Template not found");
|
|
5438
5422
|
}
|
|
5439
|
-
strapi2.log.info(`[magic-mail]
|
|
5423
|
+
strapi2.log.info(`[magic-mail] [PACKAGE] Template has ${template.versions?.length || 0} versions`);
|
|
5440
5424
|
if (template.versions && template.versions.length > 0) {
|
|
5441
5425
|
const sortedVersions = [...template.versions].sort((a, b) => b.versionNumber - a.versionNumber);
|
|
5442
|
-
strapi2.log.info(`[magic-mail] ✅ Returning ${sortedVersions.length} versions from template populate`);
|
|
5443
5426
|
return sortedVersions;
|
|
5444
5427
|
}
|
|
5445
|
-
|
|
5446
|
-
const versions = await strapi2.entityService.findMany("plugin::magic-mail.email-template-version", {
|
|
5428
|
+
const versions = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findMany({
|
|
5447
5429
|
filters: {
|
|
5448
5430
|
template: {
|
|
5449
|
-
|
|
5431
|
+
documentId: templateDocumentId
|
|
5450
5432
|
}
|
|
5451
5433
|
},
|
|
5452
|
-
sort: { versionNumber: "desc" }
|
|
5453
|
-
populate: ["template"]
|
|
5434
|
+
sort: [{ versionNumber: "desc" }]
|
|
5454
5435
|
});
|
|
5455
|
-
strapi2.log.info(`[magic-mail]
|
|
5436
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Found ${versions.length} versions`);
|
|
5456
5437
|
return versions;
|
|
5457
5438
|
},
|
|
5458
5439
|
/**
|
|
5459
5440
|
* Restore template from a specific version
|
|
5460
|
-
*
|
|
5461
|
-
* Updates the template with data from the version
|
|
5462
|
-
* This will create a NEW version snapshot (via update logic)
|
|
5463
5441
|
*/
|
|
5464
|
-
async restoreVersion(
|
|
5465
|
-
strapi2.log.info(`[magic-mail]
|
|
5466
|
-
const version2 = await strapi2.
|
|
5442
|
+
async restoreVersion(templateDocumentId, versionDocumentId) {
|
|
5443
|
+
strapi2.log.info(`[magic-mail] [RESTORE] Restoring template ${templateDocumentId} from version ${versionDocumentId}`);
|
|
5444
|
+
const version2 = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
5445
|
+
documentId: versionDocumentId,
|
|
5467
5446
|
populate: ["template"]
|
|
5468
5447
|
});
|
|
5469
5448
|
if (!version2) {
|
|
5470
5449
|
throw new Error("Version not found");
|
|
5471
5450
|
}
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
if (version2.template.id !== parseInt(templateId)) {
|
|
5475
|
-
strapi2.log.error(`[magic-mail] ❌ Version ${versionId} belongs to template ${version2.template.id}, not ${templateId}`);
|
|
5476
|
-
throw new Error("Version does not belong to this template");
|
|
5477
|
-
}
|
|
5478
|
-
strapi2.log.info(`[magic-mail] ✅ Version has correct template relation`);
|
|
5479
|
-
} else {
|
|
5480
|
-
strapi2.log.warn(`[magic-mail] ⚠️ Version ${versionId} has no template relation, checking template's versions array...`);
|
|
5481
|
-
const template = await strapi2.entityService.findOne("plugin::magic-mail.email-template", templateId, {
|
|
5482
|
-
populate: ["versions"]
|
|
5483
|
-
});
|
|
5484
|
-
const versionExists = template.versions && template.versions.some((v) => v.id === parseInt(versionId));
|
|
5485
|
-
if (!versionExists) {
|
|
5486
|
-
strapi2.log.error(`[magic-mail] ❌ Version ${versionId} not found in template ${templateId} versions array`);
|
|
5487
|
-
throw new Error("Version does not belong to this template");
|
|
5488
|
-
}
|
|
5489
|
-
strapi2.log.info(`[magic-mail] ✅ Version ${versionId} found in template's versions array (old version without relation)`);
|
|
5451
|
+
if (version2.template?.documentId !== templateDocumentId) {
|
|
5452
|
+
throw new Error("Version does not belong to this template");
|
|
5490
5453
|
}
|
|
5491
|
-
const restored = await this.update(
|
|
5454
|
+
const restored = await this.update(templateDocumentId, {
|
|
5492
5455
|
name: version2.name,
|
|
5493
5456
|
subject: version2.subject,
|
|
5494
5457
|
design: version2.design,
|
|
@@ -5496,93 +5459,65 @@ function requireEmailDesigner() {
|
|
|
5496
5459
|
bodyText: version2.bodyText,
|
|
5497
5460
|
tags: version2.tags
|
|
5498
5461
|
});
|
|
5499
|
-
strapi2.log.info(`[magic-mail]
|
|
5462
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template restored from version #${version2.versionNumber}`);
|
|
5500
5463
|
return restored;
|
|
5501
5464
|
},
|
|
5502
5465
|
/**
|
|
5503
5466
|
* Delete a single version
|
|
5504
|
-
*
|
|
5505
|
-
* @param {number} templateId - Template ID (for verification)
|
|
5506
|
-
* @param {number} versionId - Version ID to delete
|
|
5507
5467
|
*/
|
|
5508
|
-
async deleteVersion(
|
|
5509
|
-
strapi2.log.info(`[magic-mail]
|
|
5510
|
-
const version2 = await strapi2.
|
|
5468
|
+
async deleteVersion(templateDocumentId, versionDocumentId) {
|
|
5469
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleting version ${versionDocumentId}`);
|
|
5470
|
+
const version2 = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
5471
|
+
documentId: versionDocumentId,
|
|
5511
5472
|
populate: ["template"]
|
|
5512
5473
|
});
|
|
5513
5474
|
if (!version2) {
|
|
5514
5475
|
throw new Error("Version not found");
|
|
5515
5476
|
}
|
|
5516
|
-
if (version2.template?.
|
|
5517
|
-
|
|
5518
|
-
strapi2.log.error(`[magic-mail] ❌ Version ${versionId} belongs to template ${version2.template.id}, not ${templateId}`);
|
|
5519
|
-
throw new Error("Version does not belong to this template");
|
|
5520
|
-
}
|
|
5521
|
-
strapi2.log.info(`[magic-mail] ✅ Version has correct template relation`);
|
|
5522
|
-
} else {
|
|
5523
|
-
strapi2.log.warn(`[magic-mail] ⚠️ Version ${versionId} has no template relation, checking template's versions array...`);
|
|
5524
|
-
const template = await strapi2.entityService.findOne("plugin::magic-mail.email-template", templateId, {
|
|
5525
|
-
populate: ["versions"]
|
|
5526
|
-
});
|
|
5527
|
-
const versionExists = template.versions && template.versions.some((v) => v.id === parseInt(versionId));
|
|
5528
|
-
if (!versionExists) {
|
|
5529
|
-
strapi2.log.error(`[magic-mail] ❌ Version ${versionId} not found in template ${templateId} versions array`);
|
|
5530
|
-
throw new Error("Version does not belong to this template");
|
|
5531
|
-
}
|
|
5532
|
-
strapi2.log.info(`[magic-mail] ✅ Version ${versionId} found in template's versions array`);
|
|
5477
|
+
if (version2.template?.documentId !== templateDocumentId) {
|
|
5478
|
+
throw new Error("Version does not belong to this template");
|
|
5533
5479
|
}
|
|
5534
|
-
await strapi2.
|
|
5535
|
-
|
|
5480
|
+
await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).delete({
|
|
5481
|
+
documentId: versionDocumentId
|
|
5482
|
+
});
|
|
5483
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Version v${version2.versionNumber} deleted`);
|
|
5536
5484
|
return { success: true, message: "Version deleted" };
|
|
5537
5485
|
},
|
|
5538
5486
|
/**
|
|
5539
5487
|
* Delete all versions for a template
|
|
5540
|
-
*
|
|
5541
|
-
* @param {number} templateId - Template ID
|
|
5542
5488
|
*/
|
|
5543
|
-
async deleteAllVersions(
|
|
5544
|
-
strapi2.log.info(`[magic-mail]
|
|
5545
|
-
const template = await strapi2.
|
|
5489
|
+
async deleteAllVersions(templateDocumentId) {
|
|
5490
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleting all versions for template ${templateDocumentId}`);
|
|
5491
|
+
const template = await strapi2.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
5492
|
+
documentId: templateDocumentId,
|
|
5546
5493
|
populate: ["versions"]
|
|
5547
5494
|
});
|
|
5548
5495
|
if (!template) {
|
|
5549
5496
|
throw new Error("Template not found");
|
|
5550
5497
|
}
|
|
5551
5498
|
const versionCount = template.versions?.length || 0;
|
|
5552
|
-
strapi2.log.info(`[magic-mail] 📊 Found ${versionCount} versions to delete`);
|
|
5553
5499
|
if (versionCount === 0) {
|
|
5554
5500
|
return { success: true, message: "No versions to delete", deletedCount: 0 };
|
|
5555
5501
|
}
|
|
5556
5502
|
let deletedCount = 0;
|
|
5557
|
-
const errors = [];
|
|
5558
5503
|
for (const version2 of template.versions) {
|
|
5559
5504
|
try {
|
|
5560
|
-
await strapi2.
|
|
5505
|
+
await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).delete({
|
|
5506
|
+
documentId: version2.documentId
|
|
5507
|
+
});
|
|
5561
5508
|
deletedCount++;
|
|
5562
|
-
strapi2.log.info(`[magic-mail] 🗑️ Deleted version #${version2.versionNumber} (ID: ${version2.id})`);
|
|
5563
5509
|
} catch (error) {
|
|
5564
|
-
strapi2.log.error(`[magic-mail]
|
|
5565
|
-
errors.push({ versionId: version2.id, error: error.message });
|
|
5510
|
+
strapi2.log.error(`[magic-mail] [ERROR] Failed to delete version: ${error.message}`);
|
|
5566
5511
|
}
|
|
5567
5512
|
}
|
|
5568
|
-
strapi2.log.info(`[magic-mail]
|
|
5569
|
-
return {
|
|
5570
|
-
success: true,
|
|
5571
|
-
message: `Deleted ${deletedCount} of ${versionCount} versions`,
|
|
5572
|
-
deletedCount,
|
|
5573
|
-
failedCount: versionCount - deletedCount,
|
|
5574
|
-
errors: errors.length > 0 ? errors : void 0
|
|
5575
|
-
};
|
|
5513
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Deleted ${deletedCount}/${versionCount} versions`);
|
|
5514
|
+
return { success: true, deletedCount };
|
|
5576
5515
|
},
|
|
5577
5516
|
// ============================================================
|
|
5578
5517
|
// RENDERING
|
|
5579
5518
|
// ============================================================
|
|
5580
5519
|
/**
|
|
5581
5520
|
* Render template with dynamic data using Mustache
|
|
5582
|
-
*
|
|
5583
|
-
* @param {number} templateReferenceId - Template reference ID
|
|
5584
|
-
* @param {object} data - Dynamic data for Mustache
|
|
5585
|
-
* @returns {object} Rendered HTML, text, and subject
|
|
5586
5521
|
*/
|
|
5587
5522
|
async renderTemplate(templateReferenceId, data = {}) {
|
|
5588
5523
|
const template = await this.findByReferenceId(templateReferenceId);
|
|
@@ -5614,17 +5549,17 @@ function requireEmailDesigner() {
|
|
|
5614
5549
|
};
|
|
5615
5550
|
},
|
|
5616
5551
|
// ============================================================
|
|
5617
|
-
// IMPORT/EXPORT
|
|
5552
|
+
// IMPORT/EXPORT
|
|
5618
5553
|
// ============================================================
|
|
5619
5554
|
/**
|
|
5620
5555
|
* Export templates as JSON
|
|
5621
5556
|
*/
|
|
5622
|
-
async exportTemplates(
|
|
5623
|
-
strapi2.log.info("[magic-mail]
|
|
5557
|
+
async exportTemplates(templateDocumentIds = []) {
|
|
5558
|
+
strapi2.log.info("[magic-mail] [EXPORT] Exporting templates...");
|
|
5624
5559
|
let templates;
|
|
5625
|
-
if (
|
|
5626
|
-
templates = await strapi2.
|
|
5627
|
-
filters: {
|
|
5560
|
+
if (templateDocumentIds.length > 0) {
|
|
5561
|
+
templates = await strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5562
|
+
filters: { documentId: { $in: templateDocumentIds } }
|
|
5628
5563
|
});
|
|
5629
5564
|
} else {
|
|
5630
5565
|
templates = await this.findAll();
|
|
@@ -5639,26 +5574,24 @@ function requireEmailDesigner() {
|
|
|
5639
5574
|
category: template.category,
|
|
5640
5575
|
tags: template.tags
|
|
5641
5576
|
}));
|
|
5642
|
-
strapi2.log.info(`[magic-mail]
|
|
5577
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Exported ${exported.length} templates`);
|
|
5643
5578
|
return exported;
|
|
5644
5579
|
},
|
|
5645
5580
|
/**
|
|
5646
5581
|
* Import templates from JSON
|
|
5647
5582
|
*/
|
|
5648
5583
|
async importTemplates(templates) {
|
|
5649
|
-
strapi2.log.info(`[magic-mail]
|
|
5584
|
+
strapi2.log.info(`[magic-mail] [IMPORT] Importing ${templates.length} templates...`);
|
|
5650
5585
|
const results = [];
|
|
5651
5586
|
for (const templateData of templates) {
|
|
5652
5587
|
try {
|
|
5653
5588
|
const existing = await this.findByReferenceId(templateData.templateReferenceId);
|
|
5654
5589
|
if (existing) {
|
|
5655
|
-
const updated = await this.update(existing.
|
|
5590
|
+
const updated = await this.update(existing.documentId, templateData);
|
|
5656
5591
|
results.push({ success: true, action: "updated", template: updated });
|
|
5657
|
-
strapi2.log.info(`[magic-mail] ✅ Updated template: ${templateData.name}`);
|
|
5658
5592
|
} else {
|
|
5659
5593
|
const created = await this.create(templateData);
|
|
5660
5594
|
results.push({ success: true, action: "created", template: created });
|
|
5661
|
-
strapi2.log.info(`[magic-mail] ✅ Created template: ${templateData.name}`);
|
|
5662
5595
|
}
|
|
5663
5596
|
} catch (error) {
|
|
5664
5597
|
results.push({
|
|
@@ -5667,11 +5600,8 @@ function requireEmailDesigner() {
|
|
|
5667
5600
|
error: error.message,
|
|
5668
5601
|
templateName: templateData.name
|
|
5669
5602
|
});
|
|
5670
|
-
strapi2.log.error(`[magic-mail] ❌ Failed to import ${templateData.name}: ${error.message}`);
|
|
5671
5603
|
}
|
|
5672
5604
|
}
|
|
5673
|
-
const successful = results.filter((r) => r.success).length;
|
|
5674
|
-
strapi2.log.info(`[magic-mail] ✅ Import completed: ${successful}/${templates.length} templates`);
|
|
5675
5605
|
return results;
|
|
5676
5606
|
},
|
|
5677
5607
|
// ============================================================
|
|
@@ -5681,7 +5611,7 @@ function requireEmailDesigner() {
|
|
|
5681
5611
|
* Get template statistics
|
|
5682
5612
|
*/
|
|
5683
5613
|
async getStats() {
|
|
5684
|
-
const allTemplates = await strapi2.
|
|
5614
|
+
const allTemplates = await strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5685
5615
|
fields: ["isActive", "category"]
|
|
5686
5616
|
});
|
|
5687
5617
|
const total = allTemplates.length;
|
|
@@ -5706,9 +5636,7 @@ function requireEmailDesigner() {
|
|
|
5706
5636
|
// STRAPI CORE EMAIL TEMPLATES
|
|
5707
5637
|
// ============================================================
|
|
5708
5638
|
/**
|
|
5709
|
-
* Get Strapi core email template
|
|
5710
|
-
*
|
|
5711
|
-
* These are stored in users-permissions plugin store
|
|
5639
|
+
* Get Strapi core email template
|
|
5712
5640
|
*/
|
|
5713
5641
|
async getCoreTemplate(coreEmailType) {
|
|
5714
5642
|
if (!["reset-password", "email-confirmation"].includes(coreEmailType)) {
|
|
@@ -5725,8 +5653,8 @@ function requireEmailDesigner() {
|
|
|
5725
5653
|
if (emailConfig && emailConfig[pluginStoreEmailKey]) {
|
|
5726
5654
|
data = emailConfig[pluginStoreEmailKey];
|
|
5727
5655
|
}
|
|
5728
|
-
const messageConverted = data
|
|
5729
|
-
const subjectConverted = data
|
|
5656
|
+
const messageConverted = data?.options?.message ? data.options.message.replace(/<%|<%/g, "{{").replace(/%>|%>/g, "}}") : "";
|
|
5657
|
+
const subjectConverted = data?.options?.object ? data.options.object.replace(/<%|<%/g, "{{").replace(/%>|%>/g, "}}") : "";
|
|
5730
5658
|
return {
|
|
5731
5659
|
from: data?.options?.from || null,
|
|
5732
5660
|
message: messageConverted || "",
|
|
@@ -5754,14 +5682,14 @@ function requireEmailDesigner() {
|
|
|
5754
5682
|
emailsConfig[pluginStoreEmailKey] = {
|
|
5755
5683
|
...emailsConfig[pluginStoreEmailKey],
|
|
5756
5684
|
options: {
|
|
5757
|
-
...emailsConfig[pluginStoreEmailKey]
|
|
5685
|
+
...emailsConfig[pluginStoreEmailKey]?.options || {},
|
|
5758
5686
|
message: data.message.replace(/{{/g, "<%").replace(/}}/g, "%>"),
|
|
5759
5687
|
object: data.subject.replace(/{{/g, "<%").replace(/}}/g, "%>")
|
|
5760
5688
|
},
|
|
5761
5689
|
design: data.design
|
|
5762
5690
|
};
|
|
5763
5691
|
await pluginStore.set({ key: "email", value: emailsConfig });
|
|
5764
|
-
strapi2.log.info(`[magic-mail]
|
|
5692
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Core email template updated: ${pluginStoreEmailKey}`);
|
|
5765
5693
|
return { message: "Saved" };
|
|
5766
5694
|
}
|
|
5767
5695
|
});
|
|
@@ -5773,6 +5701,9 @@ function requireAnalytics() {
|
|
|
5773
5701
|
if (hasRequiredAnalytics) return analytics;
|
|
5774
5702
|
hasRequiredAnalytics = 1;
|
|
5775
5703
|
const crypto = require$$0__default.default;
|
|
5704
|
+
const EMAIL_LOG_UID = "plugin::magic-mail.email-log";
|
|
5705
|
+
const EMAIL_EVENT_UID = "plugin::magic-mail.email-event";
|
|
5706
|
+
const EMAIL_LINK_UID = "plugin::magic-mail.email-link";
|
|
5776
5707
|
analytics = ({ strapi: strapi2 }) => ({
|
|
5777
5708
|
/**
|
|
5778
5709
|
* Generate unique email ID for tracking
|
|
@@ -5791,7 +5722,7 @@ function requireAnalytics() {
|
|
|
5791
5722
|
*/
|
|
5792
5723
|
async createEmailLog(data) {
|
|
5793
5724
|
const emailId = this.generateEmailId();
|
|
5794
|
-
const logEntry = await strapi2.
|
|
5725
|
+
const logEntry = await strapi2.documents(EMAIL_LOG_UID).create({
|
|
5795
5726
|
data: {
|
|
5796
5727
|
emailId,
|
|
5797
5728
|
user: data.userId || null,
|
|
@@ -5806,9 +5737,9 @@ function requireAnalytics() {
|
|
|
5806
5737
|
metadata: data.metadata || {}
|
|
5807
5738
|
}
|
|
5808
5739
|
});
|
|
5809
|
-
strapi2.log.info(`[magic-mail]
|
|
5740
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Email log created: ${emailId}`);
|
|
5810
5741
|
if (data.templateId) {
|
|
5811
|
-
strapi2.log.info(`[magic-mail]
|
|
5742
|
+
strapi2.log.info(`[magic-mail] [INFO] Template tracked: ${data.templateName || "Unknown"} (ID: ${data.templateId})`);
|
|
5812
5743
|
}
|
|
5813
5744
|
return logEntry;
|
|
5814
5745
|
},
|
|
@@ -5817,8 +5748,8 @@ function requireAnalytics() {
|
|
|
5817
5748
|
*/
|
|
5818
5749
|
async recordOpen(emailId, recipientHash, req) {
|
|
5819
5750
|
try {
|
|
5820
|
-
const emailLog = await strapi2.
|
|
5821
|
-
|
|
5751
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
5752
|
+
filters: { emailId }
|
|
5822
5753
|
});
|
|
5823
5754
|
if (!emailLog) {
|
|
5824
5755
|
strapi2.log.warn(`[magic-mail] Email log not found: ${emailId}`);
|
|
@@ -5830,17 +5761,17 @@ function requireAnalytics() {
|
|
|
5830
5761
|
return null;
|
|
5831
5762
|
}
|
|
5832
5763
|
const now = /* @__PURE__ */ new Date();
|
|
5833
|
-
await strapi2.
|
|
5834
|
-
|
|
5764
|
+
await strapi2.documents(EMAIL_LOG_UID).update({
|
|
5765
|
+
documentId: emailLog.documentId,
|
|
5835
5766
|
data: {
|
|
5836
|
-
openCount: emailLog.openCount + 1,
|
|
5767
|
+
openCount: (emailLog.openCount || 0) + 1,
|
|
5837
5768
|
firstOpenedAt: emailLog.firstOpenedAt || now,
|
|
5838
5769
|
lastOpenedAt: now
|
|
5839
5770
|
}
|
|
5840
5771
|
});
|
|
5841
|
-
const event = await strapi2.
|
|
5772
|
+
const event = await strapi2.documents(EMAIL_EVENT_UID).create({
|
|
5842
5773
|
data: {
|
|
5843
|
-
emailLog: emailLog.
|
|
5774
|
+
emailLog: emailLog.documentId,
|
|
5844
5775
|
type: "open",
|
|
5845
5776
|
timestamp: now,
|
|
5846
5777
|
ipAddress: req.ip || req.headers["x-forwarded-for"] || null,
|
|
@@ -5848,7 +5779,7 @@ function requireAnalytics() {
|
|
|
5848
5779
|
location: this.parseLocation(req)
|
|
5849
5780
|
}
|
|
5850
5781
|
});
|
|
5851
|
-
strapi2.log.info(`[magic-mail]
|
|
5782
|
+
strapi2.log.info(`[magic-mail] [EMAIL] Email opened: ${emailId} (count: ${(emailLog.openCount || 0) + 1})`);
|
|
5852
5783
|
return event;
|
|
5853
5784
|
} catch (error) {
|
|
5854
5785
|
strapi2.log.error("[magic-mail] Error recording open:", error);
|
|
@@ -5860,8 +5791,8 @@ function requireAnalytics() {
|
|
|
5860
5791
|
*/
|
|
5861
5792
|
async recordClick(emailId, linkHash, recipientHash, targetUrl, req) {
|
|
5862
5793
|
try {
|
|
5863
|
-
const emailLog = await strapi2.
|
|
5864
|
-
|
|
5794
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
5795
|
+
filters: { emailId }
|
|
5865
5796
|
});
|
|
5866
5797
|
if (!emailLog) {
|
|
5867
5798
|
return null;
|
|
@@ -5871,15 +5802,15 @@ function requireAnalytics() {
|
|
|
5871
5802
|
return null;
|
|
5872
5803
|
}
|
|
5873
5804
|
const now = /* @__PURE__ */ new Date();
|
|
5874
|
-
await strapi2.
|
|
5875
|
-
|
|
5805
|
+
await strapi2.documents(EMAIL_LOG_UID).update({
|
|
5806
|
+
documentId: emailLog.documentId,
|
|
5876
5807
|
data: {
|
|
5877
|
-
clickCount: emailLog.clickCount + 1
|
|
5808
|
+
clickCount: (emailLog.clickCount || 0) + 1
|
|
5878
5809
|
}
|
|
5879
5810
|
});
|
|
5880
|
-
const event = await strapi2.
|
|
5811
|
+
const event = await strapi2.documents(EMAIL_EVENT_UID).create({
|
|
5881
5812
|
data: {
|
|
5882
|
-
emailLog: emailLog.
|
|
5813
|
+
emailLog: emailLog.documentId,
|
|
5883
5814
|
type: "click",
|
|
5884
5815
|
timestamp: now,
|
|
5885
5816
|
ipAddress: req.ip || req.headers["x-forwarded-for"] || null,
|
|
@@ -5888,7 +5819,7 @@ function requireAnalytics() {
|
|
|
5888
5819
|
linkUrl: targetUrl
|
|
5889
5820
|
}
|
|
5890
5821
|
});
|
|
5891
|
-
strapi2.log.info(`[magic-mail]
|
|
5822
|
+
strapi2.log.info(`[magic-mail] [CLICK] Link clicked: ${emailId} -> ${targetUrl}`);
|
|
5892
5823
|
return event;
|
|
5893
5824
|
} catch (error) {
|
|
5894
5825
|
strapi2.log.error("[magic-mail] Error recording click:", error);
|
|
@@ -5897,34 +5828,37 @@ function requireAnalytics() {
|
|
|
5897
5828
|
},
|
|
5898
5829
|
/**
|
|
5899
5830
|
* Get analytics statistics
|
|
5831
|
+
* Note: Document Service doesn't have count() - using findMany for counting
|
|
5900
5832
|
*/
|
|
5901
5833
|
async getStats(filters = {}) {
|
|
5902
|
-
const
|
|
5834
|
+
const baseFilters = {};
|
|
5903
5835
|
if (filters.userId) {
|
|
5904
|
-
|
|
5836
|
+
baseFilters.user = { documentId: filters.userId };
|
|
5905
5837
|
}
|
|
5906
5838
|
if (filters.templateId) {
|
|
5907
|
-
|
|
5839
|
+
baseFilters.templateId = filters.templateId;
|
|
5908
5840
|
}
|
|
5909
5841
|
if (filters.accountId) {
|
|
5910
|
-
|
|
5842
|
+
baseFilters.accountId = filters.accountId;
|
|
5911
5843
|
}
|
|
5912
5844
|
if (filters.dateFrom) {
|
|
5913
|
-
|
|
5845
|
+
baseFilters.sentAt = { $gte: new Date(filters.dateFrom) };
|
|
5914
5846
|
}
|
|
5915
5847
|
if (filters.dateTo) {
|
|
5916
|
-
|
|
5848
|
+
baseFilters.sentAt = { ...baseFilters.sentAt, $lte: new Date(filters.dateTo) };
|
|
5917
5849
|
}
|
|
5918
5850
|
const [totalSent, totalOpened, totalClicked, totalBounced] = await Promise.all([
|
|
5919
|
-
strapi2.
|
|
5920
|
-
|
|
5921
|
-
where: { ...where, openCount: { $gt: 0 } }
|
|
5851
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5852
|
+
filters: baseFilters
|
|
5922
5853
|
}),
|
|
5923
|
-
strapi2.
|
|
5924
|
-
|
|
5854
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5855
|
+
filters: { ...baseFilters, openCount: { $gt: 0 } }
|
|
5925
5856
|
}),
|
|
5926
|
-
strapi2.
|
|
5927
|
-
|
|
5857
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5858
|
+
filters: { ...baseFilters, clickCount: { $gt: 0 } }
|
|
5859
|
+
}),
|
|
5860
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5861
|
+
filters: { ...baseFilters, bounced: true }
|
|
5928
5862
|
})
|
|
5929
5863
|
]);
|
|
5930
5864
|
const openRate = totalSent > 0 ? totalOpened / totalSent * 100 : 0;
|
|
@@ -5946,7 +5880,7 @@ function requireAnalytics() {
|
|
|
5946
5880
|
async getEmailLogs(filters = {}, pagination = {}) {
|
|
5947
5881
|
const where = {};
|
|
5948
5882
|
if (filters.userId) {
|
|
5949
|
-
where.user = filters.userId;
|
|
5883
|
+
where.user = { documentId: filters.userId };
|
|
5950
5884
|
}
|
|
5951
5885
|
if (filters.templateId) {
|
|
5952
5886
|
where.templateId = filters.templateId;
|
|
@@ -5961,14 +5895,17 @@ function requireAnalytics() {
|
|
|
5961
5895
|
const page = pagination.page || 1;
|
|
5962
5896
|
const pageSize = pagination.pageSize || 25;
|
|
5963
5897
|
const [logs, total] = await Promise.all([
|
|
5964
|
-
strapi2.
|
|
5965
|
-
where,
|
|
5966
|
-
|
|
5898
|
+
strapi2.documents(EMAIL_LOG_UID).findMany({
|
|
5899
|
+
filters: where,
|
|
5900
|
+
sort: [{ sentAt: "desc" }],
|
|
5967
5901
|
limit: pageSize,
|
|
5968
5902
|
offset: (page - 1) * pageSize,
|
|
5969
5903
|
populate: ["user"]
|
|
5970
5904
|
}),
|
|
5971
|
-
|
|
5905
|
+
// Get total count using native count() method
|
|
5906
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5907
|
+
filters: where
|
|
5908
|
+
})
|
|
5972
5909
|
]);
|
|
5973
5910
|
return {
|
|
5974
5911
|
data: logs,
|
|
@@ -5984,8 +5921,8 @@ function requireAnalytics() {
|
|
|
5984
5921
|
* Get email log details with events
|
|
5985
5922
|
*/
|
|
5986
5923
|
async getEmailLogDetails(emailId) {
|
|
5987
|
-
const emailLog = await strapi2.
|
|
5988
|
-
|
|
5924
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
5925
|
+
filters: { emailId },
|
|
5989
5926
|
populate: ["user", "events"]
|
|
5990
5927
|
});
|
|
5991
5928
|
return emailLog;
|
|
@@ -5994,9 +5931,9 @@ function requireAnalytics() {
|
|
|
5994
5931
|
* Get user email activity
|
|
5995
5932
|
*/
|
|
5996
5933
|
async getUserActivity(userId) {
|
|
5997
|
-
const emailLogs = await strapi2.
|
|
5998
|
-
|
|
5999
|
-
|
|
5934
|
+
const emailLogs = await strapi2.documents(EMAIL_LOG_UID).findMany({
|
|
5935
|
+
filters: { user: { documentId: userId } },
|
|
5936
|
+
sort: [{ sentAt: "desc" }],
|
|
6000
5937
|
limit: 50
|
|
6001
5938
|
});
|
|
6002
5939
|
const stats = await this.getStats({ userId });
|
|
@@ -6034,8 +5971,8 @@ function requireAnalytics() {
|
|
|
6034
5971
|
*/
|
|
6035
5972
|
async rewriteLinksForTracking(html, emailId, recipientHash) {
|
|
6036
5973
|
const baseUrl = strapi2.config.get("server.url") || "http://localhost:1337";
|
|
6037
|
-
const emailLog = await strapi2.
|
|
6038
|
-
|
|
5974
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
5975
|
+
filters: { emailId }
|
|
6039
5976
|
});
|
|
6040
5977
|
if (!emailLog) {
|
|
6041
5978
|
strapi2.log.error(`[magic-mail] Cannot rewrite links: Email log not found for ${emailId}`);
|
|
@@ -6049,13 +5986,13 @@ function requireAnalytics() {
|
|
|
6049
5986
|
while ((match = linkRegex.exec(html)) !== null) {
|
|
6050
5987
|
match[0];
|
|
6051
5988
|
const originalUrl = match[1];
|
|
6052
|
-
strapi2.log.debug(`[magic-mail]
|
|
5989
|
+
strapi2.log.debug(`[magic-mail] [CHECK] Found link: ${originalUrl.substring(0, 100)}${originalUrl.length > 100 ? "..." : ""}`);
|
|
6053
5990
|
if (originalUrl.startsWith("#") || originalUrl.includes("/track/click/")) {
|
|
6054
|
-
strapi2.log.debug(`[magic-mail]
|
|
5991
|
+
strapi2.log.debug(`[magic-mail] [SKIP] Skipping (anchor or already tracked)`);
|
|
6055
5992
|
continue;
|
|
6056
5993
|
}
|
|
6057
5994
|
if (!originalUrl.match(/^https?:\/\//i) && !originalUrl.startsWith("/")) {
|
|
6058
|
-
strapi2.log.debug(`[magic-mail]
|
|
5995
|
+
strapi2.log.debug(`[magic-mail] [SKIP] Skipping relative URL: ${originalUrl}`);
|
|
6059
5996
|
continue;
|
|
6060
5997
|
}
|
|
6061
5998
|
const linkHash = crypto.createHash("md5").update(originalUrl).digest("hex").substring(0, 8);
|
|
@@ -6065,7 +6002,7 @@ function requireAnalytics() {
|
|
|
6065
6002
|
});
|
|
6066
6003
|
const trackingUrl = `${baseUrl}/api/magic-mail/track/click/${emailId}/${linkHash}/${recipientHash}`;
|
|
6067
6004
|
linkCount++;
|
|
6068
|
-
strapi2.log.info(`[magic-mail]
|
|
6005
|
+
strapi2.log.info(`[magic-mail] [LINK] Link ${linkCount}: ${originalUrl} → ${trackingUrl}`);
|
|
6069
6006
|
replacements.push({
|
|
6070
6007
|
from: originalUrl,
|
|
6071
6008
|
to: trackingUrl
|
|
@@ -6073,7 +6010,7 @@ function requireAnalytics() {
|
|
|
6073
6010
|
}
|
|
6074
6011
|
for (const mapping of linkMappings) {
|
|
6075
6012
|
try {
|
|
6076
|
-
await this.storeLinkMapping(emailLog.
|
|
6013
|
+
await this.storeLinkMapping(emailLog.documentId, mapping.linkHash, mapping.originalUrl);
|
|
6077
6014
|
} catch (err) {
|
|
6078
6015
|
strapi2.log.error("[magic-mail] Error storing link mapping:", err);
|
|
6079
6016
|
}
|
|
@@ -6083,20 +6020,20 @@ function requireAnalytics() {
|
|
|
6083
6020
|
result = result.replace(replacement.from, replacement.to);
|
|
6084
6021
|
}
|
|
6085
6022
|
if (linkCount > 0) {
|
|
6086
|
-
strapi2.log.info(`[magic-mail]
|
|
6023
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Rewrote ${linkCount} links for click tracking`);
|
|
6087
6024
|
} else {
|
|
6088
|
-
strapi2.log.warn(`[magic-mail]
|
|
6025
|
+
strapi2.log.warn(`[magic-mail] [WARNING] No links found in email HTML for tracking!`);
|
|
6089
6026
|
}
|
|
6090
6027
|
return result;
|
|
6091
6028
|
},
|
|
6092
6029
|
/**
|
|
6093
6030
|
* Store link mapping in database
|
|
6094
6031
|
*/
|
|
6095
|
-
async storeLinkMapping(
|
|
6032
|
+
async storeLinkMapping(emailLogDocId, linkHash, originalUrl) {
|
|
6096
6033
|
try {
|
|
6097
|
-
const existing = await strapi2.
|
|
6098
|
-
|
|
6099
|
-
emailLog:
|
|
6034
|
+
const existing = await strapi2.documents(EMAIL_LINK_UID).findFirst({
|
|
6035
|
+
filters: {
|
|
6036
|
+
emailLog: { documentId: emailLogDocId },
|
|
6100
6037
|
linkHash
|
|
6101
6038
|
}
|
|
6102
6039
|
});
|
|
@@ -6104,15 +6041,15 @@ function requireAnalytics() {
|
|
|
6104
6041
|
strapi2.log.debug(`[magic-mail] Link mapping already exists for ${linkHash}`);
|
|
6105
6042
|
return existing;
|
|
6106
6043
|
}
|
|
6107
|
-
const linkMapping = await strapi2.
|
|
6044
|
+
const linkMapping = await strapi2.documents(EMAIL_LINK_UID).create({
|
|
6108
6045
|
data: {
|
|
6109
|
-
emailLog:
|
|
6046
|
+
emailLog: emailLogDocId,
|
|
6110
6047
|
linkHash,
|
|
6111
6048
|
originalUrl,
|
|
6112
6049
|
clickCount: 0
|
|
6113
6050
|
}
|
|
6114
6051
|
});
|
|
6115
|
-
strapi2.log.debug(`[magic-mail]
|
|
6052
|
+
strapi2.log.debug(`[magic-mail] [SAVE] Stored link mapping: ${linkHash} → ${originalUrl}`);
|
|
6116
6053
|
return linkMapping;
|
|
6117
6054
|
} catch (error) {
|
|
6118
6055
|
strapi2.log.error("[magic-mail] Error storing link mapping:", error);
|
|
@@ -6124,16 +6061,16 @@ function requireAnalytics() {
|
|
|
6124
6061
|
*/
|
|
6125
6062
|
async getOriginalUrlFromHash(emailId, linkHash) {
|
|
6126
6063
|
try {
|
|
6127
|
-
const emailLog = await strapi2.
|
|
6128
|
-
|
|
6064
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
6065
|
+
filters: { emailId }
|
|
6129
6066
|
});
|
|
6130
6067
|
if (!emailLog) {
|
|
6131
6068
|
strapi2.log.warn(`[magic-mail] Email log not found: ${emailId}`);
|
|
6132
6069
|
return null;
|
|
6133
6070
|
}
|
|
6134
|
-
const linkMapping = await strapi2.
|
|
6135
|
-
|
|
6136
|
-
emailLog: emailLog.
|
|
6071
|
+
const linkMapping = await strapi2.documents(EMAIL_LINK_UID).findFirst({
|
|
6072
|
+
filters: {
|
|
6073
|
+
emailLog: { documentId: emailLog.documentId },
|
|
6137
6074
|
linkHash
|
|
6138
6075
|
}
|
|
6139
6076
|
});
|
|
@@ -6142,10 +6079,10 @@ function requireAnalytics() {
|
|
|
6142
6079
|
return null;
|
|
6143
6080
|
}
|
|
6144
6081
|
const now = /* @__PURE__ */ new Date();
|
|
6145
|
-
await strapi2.
|
|
6146
|
-
|
|
6082
|
+
await strapi2.documents(EMAIL_LINK_UID).update({
|
|
6083
|
+
documentId: linkMapping.documentId,
|
|
6147
6084
|
data: {
|
|
6148
|
-
clickCount: linkMapping.clickCount + 1,
|
|
6085
|
+
clickCount: (linkMapping.clickCount || 0) + 1,
|
|
6149
6086
|
firstClickedAt: linkMapping.firstClickedAt || now,
|
|
6150
6087
|
lastClickedAt: now
|
|
6151
6088
|
}
|