strapi-plugin-magic-mail 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/index.js +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.mjs
CHANGED
|
@@ -23,14 +23,14 @@ function requireBootstrap() {
|
|
|
23
23
|
if (hasRequiredBootstrap) return bootstrap;
|
|
24
24
|
hasRequiredBootstrap = 1;
|
|
25
25
|
bootstrap = async ({ strapi: strapi2 }) => {
|
|
26
|
-
strapi2.log.info("
|
|
26
|
+
strapi2.log.info("[BOOTSTRAP] [magic-mail] Starting...");
|
|
27
27
|
try {
|
|
28
28
|
const licenseGuardService = strapi2.plugin("magic-mail").service("license-guard");
|
|
29
29
|
setTimeout(async () => {
|
|
30
30
|
const licenseStatus = await licenseGuardService.initialize();
|
|
31
31
|
if (!licenseStatus.valid && licenseStatus.demo) {
|
|
32
32
|
strapi2.log.error("╔════════════════════════════════════════════════════════════════╗");
|
|
33
|
-
strapi2.log.error("║
|
|
33
|
+
strapi2.log.error("║ [ERROR] MAGICMAIL - NO VALID LICENSE ║");
|
|
34
34
|
strapi2.log.error("║ ║");
|
|
35
35
|
strapi2.log.error("║ This plugin requires a valid license to operate. ║");
|
|
36
36
|
strapi2.log.error("║ Please activate your license via Admin UI: ║");
|
|
@@ -39,7 +39,7 @@ function requireBootstrap() {
|
|
|
39
39
|
strapi2.log.error('║ Click "Generate Free License" to get started! ║');
|
|
40
40
|
strapi2.log.error("╚════════════════════════════════════════════════════════════════╝");
|
|
41
41
|
} else if (licenseStatus.gracePeriod) {
|
|
42
|
-
strapi2.log.warn("
|
|
42
|
+
strapi2.log.warn("[WARNING] Running on grace period (license server unreachable)");
|
|
43
43
|
}
|
|
44
44
|
}, 2e3);
|
|
45
45
|
const accountManager2 = strapi2.plugin("magic-mail").service("account-manager");
|
|
@@ -48,7 +48,7 @@ function requireBootstrap() {
|
|
|
48
48
|
if (originalEmailService && originalEmailService.send) {
|
|
49
49
|
const originalSend = originalEmailService.send.bind(originalEmailService);
|
|
50
50
|
originalEmailService.send = async (emailData) => {
|
|
51
|
-
strapi2.log.info("[magic-mail]
|
|
51
|
+
strapi2.log.info("[magic-mail] [EMAIL] Intercepted from native Strapi service");
|
|
52
52
|
strapi2.log.debug("[magic-mail] Email data:", {
|
|
53
53
|
to: emailData.to,
|
|
54
54
|
subject: emailData.subject,
|
|
@@ -61,19 +61,19 @@ function requireBootstrap() {
|
|
|
61
61
|
emailData.templateData = emailData.data;
|
|
62
62
|
}
|
|
63
63
|
const result = await emailRouter2.send(emailData);
|
|
64
|
-
strapi2.log.info("[magic-mail]
|
|
64
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email routed successfully through MagicMail");
|
|
65
65
|
return result;
|
|
66
66
|
} catch (magicMailError) {
|
|
67
|
-
strapi2.log.warn("[magic-mail]
|
|
67
|
+
strapi2.log.warn("[magic-mail] [WARNING] MagicMail routing failed, falling back to original service");
|
|
68
68
|
strapi2.log.error("[magic-mail] Error:", magicMailError.message);
|
|
69
69
|
return await originalSend(emailData);
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
|
-
strapi2.log.info("[magic-mail]
|
|
73
|
-
strapi2.log.info("[magic-mail]
|
|
72
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Native email service overridden!");
|
|
73
|
+
strapi2.log.info("[magic-mail] [INFO] All strapi.plugins.email.services.email.send() calls will route through MagicMail");
|
|
74
74
|
} else {
|
|
75
|
-
strapi2.log.warn("[magic-mail]
|
|
76
|
-
strapi2.log.warn("[magic-mail]
|
|
75
|
+
strapi2.log.warn("[magic-mail] [WARNING] Native email service not found - MagicMail will work standalone");
|
|
76
|
+
strapi2.log.warn("[magic-mail] [INFO] Make sure @strapi/plugin-email is installed");
|
|
77
77
|
}
|
|
78
78
|
const hourlyResetInterval = setInterval(async () => {
|
|
79
79
|
try {
|
|
@@ -83,7 +83,7 @@ function requireBootstrap() {
|
|
|
83
83
|
}
|
|
84
84
|
const accountMgr = strapi2.plugin("magic-mail").service("account-manager");
|
|
85
85
|
await accountMgr.resetCounters("hourly");
|
|
86
|
-
strapi2.log.info("[magic-mail]
|
|
86
|
+
strapi2.log.info("[magic-mail] [RESET] Hourly counters reset");
|
|
87
87
|
} catch (err) {
|
|
88
88
|
console.error("[magic-mail] Hourly reset error:", err.message);
|
|
89
89
|
}
|
|
@@ -101,7 +101,7 @@ function requireBootstrap() {
|
|
|
101
101
|
}
|
|
102
102
|
const accountMgr = strapi2.plugin("magic-mail").service("account-manager");
|
|
103
103
|
await accountMgr.resetCounters("daily");
|
|
104
|
-
strapi2.log.info("[magic-mail]
|
|
104
|
+
strapi2.log.info("[magic-mail] [RESET] Daily counters reset");
|
|
105
105
|
const dailyResetInterval = setInterval(async () => {
|
|
106
106
|
try {
|
|
107
107
|
if (!strapi2 || !strapi2.plugin) {
|
|
@@ -110,7 +110,7 @@ function requireBootstrap() {
|
|
|
110
110
|
}
|
|
111
111
|
const accountMgr2 = strapi2.plugin("magic-mail").service("account-manager");
|
|
112
112
|
await accountMgr2.resetCounters("daily");
|
|
113
|
-
strapi2.log.info("[magic-mail]
|
|
113
|
+
strapi2.log.info("[magic-mail] [RESET] Daily counters reset");
|
|
114
114
|
} catch (err) {
|
|
115
115
|
console.error("[magic-mail] Daily reset error:", err.message);
|
|
116
116
|
}
|
|
@@ -120,10 +120,10 @@ function requireBootstrap() {
|
|
|
120
120
|
console.error("[magic-mail] Initial daily reset error:", err.message);
|
|
121
121
|
}
|
|
122
122
|
}, msUntilMidnight);
|
|
123
|
-
strapi2.log.info("[magic-mail]
|
|
124
|
-
strapi2.log.info("[magic-mail]
|
|
123
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Counter reset schedules initialized");
|
|
124
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Bootstrap complete");
|
|
125
125
|
} catch (err) {
|
|
126
|
-
strapi2.log.error("[magic-mail]
|
|
126
|
+
strapi2.log.error("[magic-mail] [ERROR] Bootstrap error:", err);
|
|
127
127
|
}
|
|
128
128
|
};
|
|
129
129
|
return bootstrap;
|
|
@@ -358,7 +358,7 @@ function requireAccounts() {
|
|
|
358
358
|
ctx.throw(403, `Provider "${accountData.provider}" requires a Premium license or higher. Please upgrade your license.`);
|
|
359
359
|
return;
|
|
360
360
|
}
|
|
361
|
-
const currentAccounts = await strapi.
|
|
361
|
+
const currentAccounts = await strapi.documents("plugin::magic-mail.email-account").count();
|
|
362
362
|
const maxAccounts = await licenseGuard2.getMaxAccounts();
|
|
363
363
|
if (maxAccounts !== -1 && currentAccounts >= maxAccounts) {
|
|
364
364
|
ctx.throw(403, `Account limit reached (${maxAccounts}). Upgrade your license to add more accounts.`);
|
|
@@ -439,9 +439,9 @@ function requireAccounts() {
|
|
|
439
439
|
ctx.throw(400, "testEmail is required");
|
|
440
440
|
}
|
|
441
441
|
strapi.log.info("[magic-mail] 🧪 Testing Strapi Email Service integration...");
|
|
442
|
-
strapi.log.info('[magic-mail]
|
|
442
|
+
strapi.log.info('[magic-mail] [EMAIL] Calling strapi.plugin("email").service("email").send()');
|
|
443
443
|
if (accountName) {
|
|
444
|
-
strapi.log.info(`[magic-mail]
|
|
444
|
+
strapi.log.info(`[magic-mail] [FORCE] Forcing specific account: ${accountName}`);
|
|
445
445
|
}
|
|
446
446
|
const result = await strapi.plugin("email").service("email").send({
|
|
447
447
|
to: testEmail,
|
|
@@ -468,6 +468,15 @@ function requireAccounts() {
|
|
|
468
468
|
<li>Statistics tracking</li>
|
|
469
469
|
</ul>
|
|
470
470
|
</div>
|
|
471
|
+
<div style="background: #DCFCE7; border: 1px solid #22C55E; border-radius: 8px; padding: 15px; margin: 20px 0;">
|
|
472
|
+
<h3 style="margin-top: 0; color: #15803D;">Security Features Active</h3>
|
|
473
|
+
<ul style="margin: 10px 0; padding-left: 20px;">
|
|
474
|
+
<li>TLS/SSL Encryption enforced</li>
|
|
475
|
+
<li>Email content validated</li>
|
|
476
|
+
<li>Proper headers included</li>
|
|
477
|
+
<li>Message-ID generated</li>
|
|
478
|
+
</ul>
|
|
479
|
+
</div>
|
|
471
480
|
<p style="color: #6B7280; font-size: 14px; margin-top: 30px;">
|
|
472
481
|
Sent at: ${(/* @__PURE__ */ new Date()).toLocaleString()}<br>
|
|
473
482
|
Via: MagicMail Email Router
|
|
@@ -478,7 +487,7 @@ function requireAccounts() {
|
|
|
478
487
|
accountName: accountName || null
|
|
479
488
|
// Force specific account if provided
|
|
480
489
|
});
|
|
481
|
-
strapi.log.info("[magic-mail]
|
|
490
|
+
strapi.log.info("[magic-mail] [SUCCESS] Strapi Email Service test completed");
|
|
482
491
|
ctx.body = {
|
|
483
492
|
success: true,
|
|
484
493
|
message: "Email sent via Strapi Email Service (intercepted by MagicMail)",
|
|
@@ -490,7 +499,7 @@ function requireAccounts() {
|
|
|
490
499
|
}
|
|
491
500
|
};
|
|
492
501
|
} catch (err) {
|
|
493
|
-
strapi.log.error("[magic-mail]
|
|
502
|
+
strapi.log.error("[magic-mail] [ERROR] Strapi Email Service test failed:", err);
|
|
494
503
|
ctx.body = {
|
|
495
504
|
success: false,
|
|
496
505
|
message: "Failed to send test email",
|
|
@@ -567,7 +576,7 @@ function requireOauth$1() {
|
|
|
567
576
|
</style>
|
|
568
577
|
</head>
|
|
569
578
|
<body>
|
|
570
|
-
<div class="error"
|
|
579
|
+
<div class="error">[ERROR] OAuth Authorization Failed</div>
|
|
571
580
|
<p>Error: ${error}</p>
|
|
572
581
|
<p>You can close this window and try again.</p>
|
|
573
582
|
<script>
|
|
@@ -601,7 +610,7 @@ function requireOauth$1() {
|
|
|
601
610
|
</style>
|
|
602
611
|
</head>
|
|
603
612
|
<body>
|
|
604
|
-
<div class="success"
|
|
613
|
+
<div class="success">[SUCCESS]</div>
|
|
605
614
|
<div class="message">Gmail OAuth Authorized!</div>
|
|
606
615
|
<div class="note">Closing window...</div>
|
|
607
616
|
<script>
|
|
@@ -676,7 +685,7 @@ function requireOauth$1() {
|
|
|
676
685
|
</style>
|
|
677
686
|
</head>
|
|
678
687
|
<body>
|
|
679
|
-
<div class="error"
|
|
688
|
+
<div class="error">[ERROR] OAuth Authorization Failed</div>
|
|
680
689
|
<p>Error: ${error}</p>
|
|
681
690
|
<p>You can close this window and try again.</p>
|
|
682
691
|
<script>
|
|
@@ -710,7 +719,7 @@ function requireOauth$1() {
|
|
|
710
719
|
</style>
|
|
711
720
|
</head>
|
|
712
721
|
<body>
|
|
713
|
-
<div class="success"
|
|
722
|
+
<div class="success">[SUCCESS]</div>
|
|
714
723
|
<div class="message">Microsoft OAuth Authorized!</div>
|
|
715
724
|
<div class="note">Closing window...</div>
|
|
716
725
|
<script>
|
|
@@ -781,7 +790,7 @@ function requireOauth$1() {
|
|
|
781
790
|
</style>
|
|
782
791
|
</head>
|
|
783
792
|
<body>
|
|
784
|
-
<div class="error"
|
|
793
|
+
<div class="error">[ERROR] OAuth Authorization Failed</div>
|
|
785
794
|
<p>Error: ${error}</p>
|
|
786
795
|
<p>You can close this window and try again.</p>
|
|
787
796
|
<script>
|
|
@@ -815,7 +824,7 @@ function requireOauth$1() {
|
|
|
815
824
|
</style>
|
|
816
825
|
</head>
|
|
817
826
|
<body>
|
|
818
|
-
<div class="success"
|
|
827
|
+
<div class="success">[SUCCESS]</div>
|
|
819
828
|
<div class="message">Yahoo Mail OAuth Authorized!</div>
|
|
820
829
|
<div class="note">Closing window...</div>
|
|
821
830
|
<script>
|
|
@@ -865,7 +874,7 @@ function requireOauth$1() {
|
|
|
865
874
|
ctx.throw(403, `OAuth provider "${provider}" requires a Premium license or higher. Please upgrade your license.`);
|
|
866
875
|
return;
|
|
867
876
|
}
|
|
868
|
-
const currentAccounts = await strapi.
|
|
877
|
+
const currentAccounts = await strapi.documents("plugin::magic-mail.email-account").count();
|
|
869
878
|
const maxAccounts = await licenseGuard2.getMaxAccounts();
|
|
870
879
|
if (maxAccounts !== -1 && currentAccounts >= maxAccounts) {
|
|
871
880
|
ctx.throw(403, `Account limit reached (${maxAccounts}). Upgrade your license to add more accounts.`);
|
|
@@ -920,7 +929,7 @@ function requireOauth$1() {
|
|
|
920
929
|
accountDetails.config
|
|
921
930
|
// contains clientId and clientSecret
|
|
922
931
|
);
|
|
923
|
-
strapi.log.info("[magic-mail]
|
|
932
|
+
strapi.log.info("[magic-mail] [SUCCESS] OAuth account created successfully");
|
|
924
933
|
ctx.body = {
|
|
925
934
|
success: true,
|
|
926
935
|
data: account,
|
|
@@ -940,14 +949,15 @@ var hasRequiredRoutingRules;
|
|
|
940
949
|
function requireRoutingRules() {
|
|
941
950
|
if (hasRequiredRoutingRules) return routingRules;
|
|
942
951
|
hasRequiredRoutingRules = 1;
|
|
952
|
+
const ROUTING_RULE_UID = "plugin::magic-mail.routing-rule";
|
|
943
953
|
routingRules = {
|
|
944
954
|
/**
|
|
945
955
|
* Get all routing rules
|
|
946
956
|
*/
|
|
947
957
|
async getAll(ctx) {
|
|
948
958
|
try {
|
|
949
|
-
const rules = await strapi.
|
|
950
|
-
sort: { priority: "desc" }
|
|
959
|
+
const rules = await strapi.documents(ROUTING_RULE_UID).findMany({
|
|
960
|
+
sort: [{ priority: "desc" }]
|
|
951
961
|
});
|
|
952
962
|
ctx.body = {
|
|
953
963
|
data: rules,
|
|
@@ -964,7 +974,9 @@ function requireRoutingRules() {
|
|
|
964
974
|
async getOne(ctx) {
|
|
965
975
|
try {
|
|
966
976
|
const { ruleId } = ctx.params;
|
|
967
|
-
const rule = await strapi.
|
|
977
|
+
const rule = await strapi.documents(ROUTING_RULE_UID).findOne({
|
|
978
|
+
documentId: ruleId
|
|
979
|
+
});
|
|
968
980
|
if (!rule) {
|
|
969
981
|
ctx.throw(404, "Routing rule not found");
|
|
970
982
|
}
|
|
@@ -982,20 +994,20 @@ function requireRoutingRules() {
|
|
|
982
994
|
async create(ctx) {
|
|
983
995
|
try {
|
|
984
996
|
const licenseGuard2 = strapi.plugin("magic-mail").service("license-guard");
|
|
985
|
-
const currentRules = await strapi.
|
|
997
|
+
const currentRules = await strapi.documents(ROUTING_RULE_UID).count();
|
|
986
998
|
const maxRules = await licenseGuard2.getMaxRoutingRules();
|
|
987
999
|
if (maxRules !== -1 && currentRules >= maxRules) {
|
|
988
1000
|
ctx.throw(403, `Routing rule limit reached (${maxRules}). Upgrade to Advanced license for unlimited rules.`);
|
|
989
1001
|
return;
|
|
990
1002
|
}
|
|
991
|
-
const rule = await strapi.
|
|
1003
|
+
const rule = await strapi.documents(ROUTING_RULE_UID).create({
|
|
992
1004
|
data: ctx.request.body
|
|
993
1005
|
});
|
|
994
1006
|
ctx.body = {
|
|
995
1007
|
data: rule,
|
|
996
1008
|
message: "Routing rule created successfully"
|
|
997
1009
|
};
|
|
998
|
-
strapi.log.info(`[magic-mail]
|
|
1010
|
+
strapi.log.info(`[magic-mail] [SUCCESS] Routing rule created: ${rule.name}`);
|
|
999
1011
|
} catch (err) {
|
|
1000
1012
|
strapi.log.error("[magic-mail] Error creating routing rule:", err);
|
|
1001
1013
|
ctx.throw(err.status || 500, err.message || "Error creating routing rule");
|
|
@@ -1007,14 +1019,15 @@ function requireRoutingRules() {
|
|
|
1007
1019
|
async update(ctx) {
|
|
1008
1020
|
try {
|
|
1009
1021
|
const { ruleId } = ctx.params;
|
|
1010
|
-
const rule = await strapi.
|
|
1022
|
+
const rule = await strapi.documents(ROUTING_RULE_UID).update({
|
|
1023
|
+
documentId: ruleId,
|
|
1011
1024
|
data: ctx.request.body
|
|
1012
1025
|
});
|
|
1013
1026
|
ctx.body = {
|
|
1014
1027
|
data: rule,
|
|
1015
1028
|
message: "Routing rule updated successfully"
|
|
1016
1029
|
};
|
|
1017
|
-
strapi.log.info(`[magic-mail]
|
|
1030
|
+
strapi.log.info(`[magic-mail] [SUCCESS] Routing rule updated: ${rule.name}`);
|
|
1018
1031
|
} catch (err) {
|
|
1019
1032
|
strapi.log.error("[magic-mail] Error updating routing rule:", err);
|
|
1020
1033
|
ctx.throw(500, err.message || "Error updating routing rule");
|
|
@@ -1026,7 +1039,9 @@ function requireRoutingRules() {
|
|
|
1026
1039
|
async delete(ctx) {
|
|
1027
1040
|
try {
|
|
1028
1041
|
const { ruleId } = ctx.params;
|
|
1029
|
-
await strapi.
|
|
1042
|
+
await strapi.documents(ROUTING_RULE_UID).delete({
|
|
1043
|
+
documentId: ruleId
|
|
1044
|
+
});
|
|
1030
1045
|
ctx.body = {
|
|
1031
1046
|
message: "Routing rule deleted successfully"
|
|
1032
1047
|
};
|
|
@@ -1182,7 +1197,7 @@ function requireFeatures() {
|
|
|
1182
1197
|
*/
|
|
1183
1198
|
hasFeature(licenseData, featureName) {
|
|
1184
1199
|
if (!licenseData) {
|
|
1185
|
-
console.log(`[features.js]
|
|
1200
|
+
console.log(`[features.js] [WARNING] No license data → using FREE tier`);
|
|
1186
1201
|
return this.free.features.includes(featureName);
|
|
1187
1202
|
}
|
|
1188
1203
|
let isEnterprise = false;
|
|
@@ -1208,19 +1223,19 @@ function requireFeatures() {
|
|
|
1208
1223
|
isPremium = true;
|
|
1209
1224
|
}
|
|
1210
1225
|
if (isEnterprise && this.enterprise.features.includes(featureName)) {
|
|
1211
|
-
console.log(`[features.js]
|
|
1226
|
+
console.log(`[features.js] [SUCCESS] ENTERPRISE tier has feature "${featureName}"`);
|
|
1212
1227
|
return true;
|
|
1213
1228
|
}
|
|
1214
1229
|
if (isAdvanced && this.advanced.features.includes(featureName)) {
|
|
1215
|
-
console.log(`[features.js]
|
|
1230
|
+
console.log(`[features.js] [SUCCESS] ADVANCED tier has feature "${featureName}"`);
|
|
1216
1231
|
return true;
|
|
1217
1232
|
}
|
|
1218
1233
|
if (isPremium && this.premium.features.includes(featureName)) {
|
|
1219
|
-
console.log(`[features.js]
|
|
1234
|
+
console.log(`[features.js] [SUCCESS] PREMIUM tier has feature "${featureName}"`);
|
|
1220
1235
|
return true;
|
|
1221
1236
|
}
|
|
1222
1237
|
const inFree = this.free.features.includes(featureName);
|
|
1223
|
-
console.log(`[features.js] ${inFree ? "
|
|
1238
|
+
console.log(`[features.js] ${inFree ? "[SUCCESS]" : "[ERROR]"} FREE tier check for "${featureName}": ${inFree}`);
|
|
1224
1239
|
return inFree;
|
|
1225
1240
|
},
|
|
1226
1241
|
/**
|
|
@@ -1378,7 +1393,7 @@ function requireLicense() {
|
|
|
1378
1393
|
const licenseGuard2 = strapi2.plugin("magic-mail").service("license-guard");
|
|
1379
1394
|
const verification = await licenseGuard2.verifyLicense(trimmedKey);
|
|
1380
1395
|
if (!verification.valid) {
|
|
1381
|
-
strapi2.log.warn(`[magic-mail]
|
|
1396
|
+
strapi2.log.warn(`[magic-mail] [WARNING] Invalid license key attempted: ${trimmedKey.substring(0, 8)}...`);
|
|
1382
1397
|
return ctx.badRequest("Invalid or expired license key");
|
|
1383
1398
|
}
|
|
1384
1399
|
const license2 = await licenseGuard2.getLicenseByKey(trimmedKey);
|
|
@@ -1386,7 +1401,7 @@ function requireLicense() {
|
|
|
1386
1401
|
return ctx.badRequest("License not found");
|
|
1387
1402
|
}
|
|
1388
1403
|
if (license2.email.toLowerCase() !== trimmedEmail) {
|
|
1389
|
-
strapi2.log.warn(`[magic-mail]
|
|
1404
|
+
strapi2.log.warn(`[magic-mail] [WARNING] Email mismatch for license key`);
|
|
1390
1405
|
return ctx.badRequest("Email address does not match this license key");
|
|
1391
1406
|
}
|
|
1392
1407
|
await licenseGuard2.storeLicenseKey(trimmedKey);
|
|
@@ -1396,7 +1411,7 @@ function requireLicense() {
|
|
|
1396
1411
|
pingInterval,
|
|
1397
1412
|
data: verification.data
|
|
1398
1413
|
};
|
|
1399
|
-
strapi2.log.info(`[magic-mail]
|
|
1414
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] License validated and stored`);
|
|
1400
1415
|
return ctx.send({
|
|
1401
1416
|
success: true,
|
|
1402
1417
|
message: "License activated successfully",
|
|
@@ -1440,9 +1455,11 @@ function requireLicense() {
|
|
|
1440
1455
|
const maxAccounts = await licenseGuard2.getMaxAccounts();
|
|
1441
1456
|
const maxRules = await licenseGuard2.getMaxRoutingRules();
|
|
1442
1457
|
const maxTemplates = await licenseGuard2.getMaxEmailTemplates();
|
|
1443
|
-
const currentAccounts = await
|
|
1444
|
-
|
|
1445
|
-
|
|
1458
|
+
const [currentAccounts, currentRules, currentTemplates] = await Promise.all([
|
|
1459
|
+
strapi2.documents("plugin::magic-mail.email-account").count(),
|
|
1460
|
+
strapi2.documents("plugin::magic-mail.routing-rule").count(),
|
|
1461
|
+
strapi2.documents("plugin::magic-mail.email-template").count()
|
|
1462
|
+
]);
|
|
1446
1463
|
let tier = "free";
|
|
1447
1464
|
if (license2?.featureEnterprise === true || license2?.features?.enterprise === true) tier = "enterprise";
|
|
1448
1465
|
else if (license2?.featureAdvanced === true || license2?.features?.advanced === true) tier = "advanced";
|
|
@@ -1851,6 +1868,9 @@ var hasRequiredAnalytics$1;
|
|
|
1851
1868
|
function requireAnalytics$1() {
|
|
1852
1869
|
if (hasRequiredAnalytics$1) return analytics$1;
|
|
1853
1870
|
hasRequiredAnalytics$1 = 1;
|
|
1871
|
+
const EMAIL_LOG_UID = "plugin::magic-mail.email-log";
|
|
1872
|
+
const EMAIL_EVENT_UID = "plugin::magic-mail.email-event";
|
|
1873
|
+
const EMAIL_ACCOUNT_UID = "plugin::magic-mail.email-account";
|
|
1854
1874
|
analytics$1 = ({ strapi: strapi2 }) => ({
|
|
1855
1875
|
/**
|
|
1856
1876
|
* Tracking pixel endpoint
|
|
@@ -1890,7 +1910,8 @@ function requireAnalytics$1() {
|
|
|
1890
1910
|
async getStats(ctx) {
|
|
1891
1911
|
try {
|
|
1892
1912
|
const filters = {
|
|
1893
|
-
|
|
1913
|
+
// userId is documentId (string) in Strapi v5, NOT parseInt!
|
|
1914
|
+
userId: ctx.query.userId || null,
|
|
1894
1915
|
templateId: ctx.query.templateId ? parseInt(ctx.query.templateId) : null,
|
|
1895
1916
|
accountId: ctx.query.accountId ? parseInt(ctx.query.accountId) : null,
|
|
1896
1917
|
dateFrom: ctx.query.dateFrom || null,
|
|
@@ -1913,7 +1934,8 @@ function requireAnalytics$1() {
|
|
|
1913
1934
|
async getEmailLogs(ctx) {
|
|
1914
1935
|
try {
|
|
1915
1936
|
const filters = {
|
|
1916
|
-
|
|
1937
|
+
// userId is documentId (string) in Strapi v5, NOT parseInt!
|
|
1938
|
+
userId: ctx.query.userId || null,
|
|
1917
1939
|
templateId: ctx.query.templateId ? parseInt(ctx.query.templateId) : null,
|
|
1918
1940
|
search: ctx.query.search || null
|
|
1919
1941
|
};
|
|
@@ -1953,11 +1975,12 @@ function requireAnalytics$1() {
|
|
|
1953
1975
|
/**
|
|
1954
1976
|
* Get user email activity
|
|
1955
1977
|
* GET /magic-mail/analytics/users/:userId
|
|
1978
|
+
* Note: userId is documentId (string) in Strapi v5
|
|
1956
1979
|
*/
|
|
1957
1980
|
async getUserActivity(ctx) {
|
|
1958
1981
|
try {
|
|
1959
1982
|
const { userId } = ctx.params;
|
|
1960
|
-
const activity = await strapi2.plugin("magic-mail").service("analytics").getUserActivity(
|
|
1983
|
+
const activity = await strapi2.plugin("magic-mail").service("analytics").getUserActivity(userId);
|
|
1961
1984
|
return ctx.send({
|
|
1962
1985
|
success: true,
|
|
1963
1986
|
data: activity
|
|
@@ -1972,19 +1995,19 @@ function requireAnalytics$1() {
|
|
|
1972
1995
|
*/
|
|
1973
1996
|
async debug(ctx) {
|
|
1974
1997
|
try {
|
|
1975
|
-
strapi2.log.info("[magic-mail]
|
|
1976
|
-
const emailLogs = await strapi2.
|
|
1998
|
+
strapi2.log.info("[magic-mail] [CHECK] Running Analytics Debug...");
|
|
1999
|
+
const emailLogs = await strapi2.documents(EMAIL_LOG_UID).findMany({
|
|
1977
2000
|
limit: 10,
|
|
1978
|
-
|
|
2001
|
+
sort: [{ sentAt: "desc" }]
|
|
1979
2002
|
});
|
|
1980
|
-
const emailEvents = await strapi2.
|
|
2003
|
+
const emailEvents = await strapi2.documents(EMAIL_EVENT_UID).findMany({
|
|
1981
2004
|
limit: 20,
|
|
1982
|
-
|
|
2005
|
+
sort: [{ timestamp: "desc" }],
|
|
1983
2006
|
populate: ["emailLog"]
|
|
1984
2007
|
});
|
|
1985
2008
|
const analyticsService = strapi2.plugin("magic-mail").service("analytics");
|
|
1986
2009
|
const stats = await analyticsService.getStats();
|
|
1987
|
-
const accounts2 = await strapi2.
|
|
2010
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
1988
2011
|
filters: { isActive: true },
|
|
1989
2012
|
fields: ["id", "name", "provider", "fromEmail", "emailsSentToday", "totalEmailsSent"]
|
|
1990
2013
|
});
|
|
@@ -2048,19 +2071,20 @@ function requireAnalytics$1() {
|
|
|
2048
2071
|
async deleteEmailLog(ctx) {
|
|
2049
2072
|
try {
|
|
2050
2073
|
const { emailId } = ctx.params;
|
|
2051
|
-
const emailLog = await strapi2.
|
|
2052
|
-
|
|
2074
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
2075
|
+
filters: { emailId }
|
|
2053
2076
|
});
|
|
2054
2077
|
if (!emailLog) {
|
|
2055
2078
|
return ctx.notFound("Email log not found");
|
|
2056
2079
|
}
|
|
2057
|
-
await strapi2.
|
|
2058
|
-
|
|
2059
|
-
});
|
|
2060
|
-
await strapi2.db.query("plugin::magic-mail.email-log").delete({
|
|
2061
|
-
where: { id: emailLog.id }
|
|
2080
|
+
const events = await strapi2.documents(EMAIL_EVENT_UID).findMany({
|
|
2081
|
+
filters: { emailLog: { documentId: emailLog.documentId } }
|
|
2062
2082
|
});
|
|
2063
|
-
|
|
2083
|
+
for (const event of events) {
|
|
2084
|
+
await strapi2.documents(EMAIL_EVENT_UID).delete({ documentId: event.documentId });
|
|
2085
|
+
}
|
|
2086
|
+
await strapi2.documents(EMAIL_LOG_UID).delete({ documentId: emailLog.documentId });
|
|
2087
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleted email log: ${emailId}`);
|
|
2064
2088
|
return ctx.send({
|
|
2065
2089
|
success: true,
|
|
2066
2090
|
message: "Email log deleted successfully"
|
|
@@ -2077,33 +2101,36 @@ function requireAnalytics$1() {
|
|
|
2077
2101
|
async clearAllEmailLogs(ctx) {
|
|
2078
2102
|
try {
|
|
2079
2103
|
const { olderThan } = ctx.query;
|
|
2080
|
-
const
|
|
2104
|
+
const filters = {};
|
|
2081
2105
|
if (olderThan) {
|
|
2082
|
-
|
|
2106
|
+
filters.sentAt = { $lt: new Date(olderThan) };
|
|
2083
2107
|
}
|
|
2084
|
-
const emailLogs = await strapi2.
|
|
2085
|
-
|
|
2086
|
-
|
|
2108
|
+
const emailLogs = await strapi2.documents(EMAIL_LOG_UID).findMany({
|
|
2109
|
+
filters,
|
|
2110
|
+
fields: ["id", "documentId"],
|
|
2111
|
+
limit: 1e5
|
|
2087
2112
|
});
|
|
2088
|
-
|
|
2089
|
-
if (emailLogIds.length === 0) {
|
|
2113
|
+
if (emailLogs.length === 0) {
|
|
2090
2114
|
return ctx.send({
|
|
2091
2115
|
success: true,
|
|
2092
2116
|
message: "No email logs to delete",
|
|
2093
2117
|
deletedCount: 0
|
|
2094
2118
|
});
|
|
2095
2119
|
}
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2120
|
+
for (const log of emailLogs) {
|
|
2121
|
+
const events = await strapi2.documents(EMAIL_EVENT_UID).findMany({
|
|
2122
|
+
filters: { emailLog: { documentId: log.documentId } }
|
|
2123
|
+
});
|
|
2124
|
+
for (const event of events) {
|
|
2125
|
+
await strapi2.documents(EMAIL_EVENT_UID).delete({ documentId: event.documentId });
|
|
2126
|
+
}
|
|
2127
|
+
await strapi2.documents(EMAIL_LOG_UID).delete({ documentId: log.documentId });
|
|
2128
|
+
}
|
|
2129
|
+
strapi2.log.info(`[magic-mail] [DELETE] Cleared ${emailLogs.length} email logs`);
|
|
2103
2130
|
return ctx.send({
|
|
2104
2131
|
success: true,
|
|
2105
|
-
message: `Successfully deleted ${
|
|
2106
|
-
deletedCount:
|
|
2132
|
+
message: `Successfully deleted ${emailLogs.length} email log(s)`,
|
|
2133
|
+
deletedCount: emailLogs.length
|
|
2107
2134
|
});
|
|
2108
2135
|
} catch (error) {
|
|
2109
2136
|
strapi2.log.error("[magic-mail] Error clearing email logs:", error);
|
|
@@ -2118,18 +2145,16 @@ var hasRequiredTest;
|
|
|
2118
2145
|
function requireTest() {
|
|
2119
2146
|
if (hasRequiredTest) return test;
|
|
2120
2147
|
hasRequiredTest = 1;
|
|
2148
|
+
const EMAIL_TEMPLATE_UID = "plugin::magic-mail.email-template";
|
|
2149
|
+
const EMAIL_TEMPLATE_VERSION_UID = "plugin::magic-mail.email-template-version";
|
|
2121
2150
|
test = {
|
|
2122
2151
|
/**
|
|
2123
2152
|
* Test Template-Version Relations
|
|
2124
|
-
*
|
|
2125
|
-
* Tests beide Richtungen:
|
|
2126
|
-
* 1. Version → Template beim Erstellen
|
|
2127
|
-
* 2. Template → Version nachträglich via connect
|
|
2128
2153
|
*/
|
|
2129
2154
|
async testRelations(ctx) {
|
|
2130
2155
|
try {
|
|
2131
2156
|
console.log("\n" + "=".repeat(60));
|
|
2132
|
-
console.log("🧪 TEST: Template ↔ Version Relations");
|
|
2157
|
+
console.log("🧪 TEST: Template ↔ Version Relations (Document Service API)");
|
|
2133
2158
|
console.log("=".repeat(60));
|
|
2134
2159
|
let test1Success = false;
|
|
2135
2160
|
let test1ReverseSuccess = false;
|
|
@@ -2139,257 +2164,192 @@ function requireTest() {
|
|
|
2139
2164
|
let test3a_hasTemplate = false;
|
|
2140
2165
|
let test3b_twoVersions = false;
|
|
2141
2166
|
let test3b_allHaveTemplate = false;
|
|
2142
|
-
console.log("\n
|
|
2143
|
-
const testTemplate = await strapi.
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
category: "custom",
|
|
2153
|
-
isActive: true
|
|
2154
|
-
}
|
|
2155
|
-
}
|
|
2156
|
-
);
|
|
2157
|
-
console.log(`✅ Template erstellt: ID ${testTemplate.id}`);
|
|
2158
|
-
const version1 = await strapi.entityService.create(
|
|
2159
|
-
"plugin::magic-mail.email-template-version",
|
|
2160
|
-
{
|
|
2161
|
-
data: {
|
|
2162
|
-
template: testTemplate.id,
|
|
2163
|
-
// 👈 Direkte Verbindung beim Erstellen
|
|
2164
|
-
versionNumber: 1,
|
|
2165
|
-
name: "Version 1 von Test",
|
|
2166
|
-
subject: "Test Subject V1",
|
|
2167
|
-
bodyHtml: "<p>Version 1 HTML</p>",
|
|
2168
|
-
bodyText: "Version 1 Text"
|
|
2169
|
-
}
|
|
2167
|
+
console.log("\n[TEST] TEST 1: Version → Template Verbindung\n");
|
|
2168
|
+
const testTemplate = await strapi.documents(EMAIL_TEMPLATE_UID).create({
|
|
2169
|
+
data: {
|
|
2170
|
+
templateReferenceId: Math.floor(Math.random() * 1e6),
|
|
2171
|
+
name: "Test Template Relations",
|
|
2172
|
+
subject: "Test Subject",
|
|
2173
|
+
bodyHtml: "<p>Test HTML</p>",
|
|
2174
|
+
bodyText: "Test Text",
|
|
2175
|
+
category: "custom",
|
|
2176
|
+
isActive: true
|
|
2170
2177
|
}
|
|
2171
|
-
);
|
|
2172
|
-
console.log(
|
|
2173
|
-
const
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
+
});
|
|
2179
|
+
console.log(`[SUCCESS] Template erstellt: documentId ${testTemplate.documentId}`);
|
|
2180
|
+
const version1 = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).create({
|
|
2181
|
+
data: {
|
|
2182
|
+
template: testTemplate.documentId,
|
|
2183
|
+
versionNumber: 1,
|
|
2184
|
+
name: "Version 1 von Test",
|
|
2185
|
+
subject: "Test Subject V1",
|
|
2186
|
+
bodyHtml: "<p>Version 1 HTML</p>",
|
|
2187
|
+
bodyText: "Version 1 Text"
|
|
2178
2188
|
}
|
|
2179
|
-
);
|
|
2180
|
-
console.log(
|
|
2189
|
+
});
|
|
2190
|
+
console.log(`[SUCCESS] Version erstellt: documentId ${version1.documentId}, versionNumber: ${version1.versionNumber}`);
|
|
2191
|
+
const versionCheck = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
2192
|
+
documentId: version1.documentId,
|
|
2193
|
+
populate: ["template"]
|
|
2194
|
+
});
|
|
2195
|
+
console.log("\n[CHECK] Prüfung Version → Template:");
|
|
2181
2196
|
test1Success = !!versionCheck.template;
|
|
2182
2197
|
if (test1Success) {
|
|
2183
|
-
console.log(`
|
|
2184
|
-
console.log(` 📋 Template: "${versionCheck.template.name}"`);
|
|
2198
|
+
console.log(` [SUCCESS] SUCCESS: Version → Template ${versionCheck.template.documentId}`);
|
|
2185
2199
|
} else {
|
|
2186
|
-
console.log(`
|
|
2200
|
+
console.log(` [ERROR] FEHLER: Version hat KEINE Template-Verbindung!`);
|
|
2187
2201
|
}
|
|
2188
|
-
const templateCheck1 = await strapi.
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
}
|
|
2194
|
-
);
|
|
2195
|
-
console.log("\n🔍 Prüfung Template → Versions:");
|
|
2202
|
+
const templateCheck1 = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2203
|
+
documentId: testTemplate.documentId,
|
|
2204
|
+
populate: ["versions"]
|
|
2205
|
+
});
|
|
2206
|
+
console.log("\n[CHECK] Prüfung Template → Versions:");
|
|
2196
2207
|
test1ReverseSuccess = templateCheck1.versions && templateCheck1.versions.length > 0;
|
|
2197
2208
|
if (test1ReverseSuccess) {
|
|
2198
|
-
console.log(`
|
|
2199
|
-
templateCheck1.versions.forEach((v) => {
|
|
2200
|
-
console.log(` 📋 Version ${v.id}: versionNumber ${v.versionNumber}`);
|
|
2201
|
-
});
|
|
2209
|
+
console.log(` [SUCCESS] SUCCESS: Template hat ${templateCheck1.versions.length} Version(en)`);
|
|
2202
2210
|
} else {
|
|
2203
|
-
console.log(`
|
|
2204
|
-
}
|
|
2205
|
-
console.log("\n\n
|
|
2206
|
-
const version2 = await strapi.
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
bodyHtml: "<p>Version 2 HTML</p>",
|
|
2214
|
-
bodyText: "Version 2 Text"
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
);
|
|
2218
|
-
console.log(`✅ Version 2 erstellt: ID ${version2.id} (ohne Template)`);
|
|
2219
|
-
await strapi.entityService.update(
|
|
2220
|
-
"plugin::magic-mail.email-template",
|
|
2221
|
-
testTemplate.id,
|
|
2222
|
-
{
|
|
2223
|
-
data: {
|
|
2224
|
-
versions: {
|
|
2225
|
-
connect: [version2.id]
|
|
2226
|
-
// 👈 Nachträgliche Verbindung
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2211
|
+
console.log(` [ERROR] FEHLER: Template hat KEINE Versionen!`);
|
|
2212
|
+
}
|
|
2213
|
+
console.log("\n\n[TEST] TEST 2: Nachträgliche Verbindung\n");
|
|
2214
|
+
const version2 = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).create({
|
|
2215
|
+
data: {
|
|
2216
|
+
versionNumber: 2,
|
|
2217
|
+
name: "Version 2 ohne Template",
|
|
2218
|
+
subject: "Test Subject V2",
|
|
2219
|
+
bodyHtml: "<p>Version 2 HTML</p>",
|
|
2220
|
+
bodyText: "Version 2 Text"
|
|
2229
2221
|
}
|
|
2230
|
-
);
|
|
2231
|
-
console.log(
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
populate: ["versions"]
|
|
2222
|
+
});
|
|
2223
|
+
console.log(`[SUCCESS] Version 2 erstellt: documentId ${version2.documentId} (ohne Template)`);
|
|
2224
|
+
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).update({
|
|
2225
|
+
documentId: version2.documentId,
|
|
2226
|
+
data: {
|
|
2227
|
+
template: testTemplate.documentId
|
|
2237
2228
|
}
|
|
2238
|
-
);
|
|
2239
|
-
console.log(
|
|
2229
|
+
});
|
|
2230
|
+
console.log(`[SUCCESS] Version 2 mit Template verbunden`);
|
|
2231
|
+
const templateCheck2 = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2232
|
+
documentId: testTemplate.documentId,
|
|
2233
|
+
populate: ["versions"]
|
|
2234
|
+
});
|
|
2235
|
+
console.log("\n[CHECK] Prüfung nach Update:");
|
|
2240
2236
|
test2Success = templateCheck2.versions && templateCheck2.versions.length >= 2;
|
|
2241
2237
|
if (test2Success) {
|
|
2242
|
-
console.log(`
|
|
2243
|
-
templateCheck2.versions.forEach((v) => {
|
|
2244
|
-
console.log(` 📋 Version ${v.id}: versionNumber ${v.versionNumber}, "${v.name}"`);
|
|
2245
|
-
});
|
|
2238
|
+
console.log(` [SUCCESS] SUCCESS: Template hat jetzt ${templateCheck2.versions.length} Versionen`);
|
|
2246
2239
|
} else {
|
|
2247
|
-
console.log(`
|
|
2240
|
+
console.log(` [ERROR] FEHLER: Template hat nur ${templateCheck2.versions?.length || 0} Version(en)!`);
|
|
2248
2241
|
}
|
|
2249
|
-
const version2Check = await strapi.
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
populate: ["template"]
|
|
2254
|
-
}
|
|
2255
|
-
);
|
|
2256
|
-
console.log("\n🔍 Prüfung Version 2 → Template:");
|
|
2242
|
+
const version2Check = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
2243
|
+
documentId: version2.documentId,
|
|
2244
|
+
populate: ["template"]
|
|
2245
|
+
});
|
|
2257
2246
|
test2ReverseSuccess = !!version2Check.template;
|
|
2258
2247
|
if (test2ReverseSuccess) {
|
|
2259
|
-
console.log(`
|
|
2260
|
-
console.log(` 📋 Template: "${version2Check.template.name}"`);
|
|
2248
|
+
console.log(` [SUCCESS] SUCCESS: Version 2 → Template verbunden`);
|
|
2261
2249
|
} else {
|
|
2262
|
-
console.log(`
|
|
2263
|
-
}
|
|
2264
|
-
console.log("\n\n
|
|
2265
|
-
const autoTemplate = await strapi.
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
category: "custom",
|
|
2275
|
-
isActive: true
|
|
2276
|
-
}
|
|
2250
|
+
console.log(` [ERROR] FEHLER: Version 2 hat KEINE Template-Verbindung!`);
|
|
2251
|
+
}
|
|
2252
|
+
console.log("\n\n[TEST] TEST 3: Template Update (Auto-Versionierung)\n");
|
|
2253
|
+
const autoTemplate = await strapi.documents(EMAIL_TEMPLATE_UID).create({
|
|
2254
|
+
data: {
|
|
2255
|
+
templateReferenceId: Math.floor(Math.random() * 1e6),
|
|
2256
|
+
name: "Auto Version Test",
|
|
2257
|
+
subject: "Original Subject",
|
|
2258
|
+
bodyHtml: "<p>Original HTML</p>",
|
|
2259
|
+
bodyText: "Original Text",
|
|
2260
|
+
category: "custom",
|
|
2261
|
+
isActive: true
|
|
2277
2262
|
}
|
|
2278
|
-
);
|
|
2279
|
-
console.log(
|
|
2280
|
-
const beforeUpdate = await strapi.entityService.findOne(
|
|
2281
|
-
"plugin::magic-mail.email-template",
|
|
2282
|
-
autoTemplate.id,
|
|
2283
|
-
{ populate: ["versions"] }
|
|
2284
|
-
);
|
|
2285
|
-
console.log(` 📊 Versionen vor Update: ${beforeUpdate.versions?.length || 0}`);
|
|
2286
|
-
console.log("\n🔄 Führe Template-Update aus...");
|
|
2263
|
+
});
|
|
2264
|
+
console.log(`[SUCCESS] Template erstellt: documentId ${autoTemplate.documentId}`);
|
|
2287
2265
|
const emailDesignerService = strapi.plugin("magic-mail").service("email-designer");
|
|
2288
|
-
await emailDesignerService.update(autoTemplate.
|
|
2266
|
+
await emailDesignerService.update(autoTemplate.documentId, {
|
|
2289
2267
|
subject: "Updated Subject V1",
|
|
2290
2268
|
bodyHtml: "<p>Updated HTML V1</p>",
|
|
2291
2269
|
bodyText: "Updated Text V1"
|
|
2292
2270
|
});
|
|
2293
|
-
console.log("
|
|
2294
|
-
const afterFirstUpdate = await strapi.
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
);
|
|
2299
|
-
console.log("\n🔍 Prüfung nach 1. Update:");
|
|
2271
|
+
console.log("[SUCCESS] Template updated");
|
|
2272
|
+
const afterFirstUpdate = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2273
|
+
documentId: autoTemplate.documentId,
|
|
2274
|
+
populate: ["versions"]
|
|
2275
|
+
});
|
|
2276
|
+
console.log("\n[CHECK] Prüfung nach 1. Update:");
|
|
2300
2277
|
test3a_versionCreated = afterFirstUpdate.versions && afterFirstUpdate.versions.length === 1;
|
|
2301
2278
|
if (test3a_versionCreated) {
|
|
2302
|
-
console.log(`
|
|
2303
|
-
const autoVersion1 = await strapi.
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
);
|
|
2279
|
+
console.log(` [SUCCESS] SUCCESS: Automatisch 1 Version erstellt`);
|
|
2280
|
+
const autoVersion1 = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
2281
|
+
documentId: afterFirstUpdate.versions[0].documentId,
|
|
2282
|
+
populate: ["template"]
|
|
2283
|
+
});
|
|
2308
2284
|
test3a_hasTemplate = !!autoVersion1.template;
|
|
2309
2285
|
if (test3a_hasTemplate) {
|
|
2310
|
-
console.log(`
|
|
2311
|
-
console.log(` 📋 Version: versionNumber ${autoVersion1.versionNumber}, subject: "${autoVersion1.subject}"`);
|
|
2286
|
+
console.log(` [SUCCESS] SUCCESS: Version hat Template-Verbindung`);
|
|
2312
2287
|
} else {
|
|
2313
|
-
console.log(`
|
|
2288
|
+
console.log(` [ERROR] FEHLER: Version hat KEINE Template-Verbindung!`);
|
|
2314
2289
|
}
|
|
2315
2290
|
} else {
|
|
2316
|
-
console.log(`
|
|
2291
|
+
console.log(` [ERROR] FEHLER: Keine Version erstellt!`);
|
|
2317
2292
|
}
|
|
2318
|
-
|
|
2319
|
-
await emailDesignerService.update(autoTemplate.id, {
|
|
2293
|
+
await emailDesignerService.update(autoTemplate.documentId, {
|
|
2320
2294
|
subject: "Updated Subject V2",
|
|
2321
2295
|
bodyHtml: "<p>Updated HTML V2</p>",
|
|
2322
2296
|
bodyText: "Updated Text V2"
|
|
2323
2297
|
});
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
"
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
);
|
|
2330
|
-
console.log("\n🔍 Prüfung nach 2. Update:");
|
|
2298
|
+
const afterSecondUpdate = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2299
|
+
documentId: autoTemplate.documentId,
|
|
2300
|
+
populate: ["versions"]
|
|
2301
|
+
});
|
|
2302
|
+
console.log("\n[CHECK] Prüfung nach 2. Update:");
|
|
2331
2303
|
test3b_twoVersions = afterSecondUpdate.versions && afterSecondUpdate.versions.length === 2;
|
|
2332
2304
|
if (test3b_twoVersions) {
|
|
2333
|
-
console.log(`
|
|
2305
|
+
console.log(` [SUCCESS] SUCCESS: Jetzt 2 Versionen vorhanden`);
|
|
2334
2306
|
let allVersionsHaveTemplate = true;
|
|
2335
2307
|
for (const version3 of afterSecondUpdate.versions) {
|
|
2336
|
-
const fullVersion = await strapi.
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
)
|
|
2341
|
-
if (fullVersion.template) {
|
|
2342
|
-
console.log(` ✅ Version ${fullVersion.id} (v${fullVersion.versionNumber}): Template-Verbindung OK`);
|
|
2343
|
-
} else {
|
|
2344
|
-
console.log(` ❌ Version ${fullVersion.id} (v${fullVersion.versionNumber}): KEINE Template-Verbindung!`);
|
|
2308
|
+
const fullVersion = await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
2309
|
+
documentId: version3.documentId,
|
|
2310
|
+
populate: ["template"]
|
|
2311
|
+
});
|
|
2312
|
+
if (!fullVersion.template) {
|
|
2345
2313
|
allVersionsHaveTemplate = false;
|
|
2346
2314
|
}
|
|
2347
2315
|
}
|
|
2348
2316
|
test3b_allHaveTemplate = allVersionsHaveTemplate;
|
|
2349
2317
|
if (allVersionsHaveTemplate) {
|
|
2350
|
-
console.log(`
|
|
2318
|
+
console.log(` [SUCCESS] SUCCESS: Alle Versionen haben Template-Verbindung!`);
|
|
2319
|
+
} else {
|
|
2320
|
+
console.log(` [ERROR] FEHLER: Nicht alle Versionen haben Template-Verbindung!`);
|
|
2351
2321
|
}
|
|
2352
2322
|
} else {
|
|
2353
|
-
console.log(`
|
|
2323
|
+
console.log(` [ERROR] FEHLER: Falsche Anzahl Versionen!`);
|
|
2354
2324
|
}
|
|
2355
2325
|
console.log("\n🧹 Cleanup Test 3...");
|
|
2356
2326
|
if (afterSecondUpdate.versions) {
|
|
2357
2327
|
for (const version3 of afterSecondUpdate.versions) {
|
|
2358
|
-
await strapi.
|
|
2328
|
+
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version3.documentId });
|
|
2359
2329
|
}
|
|
2360
2330
|
}
|
|
2361
|
-
await strapi.
|
|
2362
|
-
console.log("
|
|
2331
|
+
await strapi.documents(EMAIL_TEMPLATE_UID).delete({ documentId: autoTemplate.documentId });
|
|
2332
|
+
console.log(" [SUCCESS] Test 3 Daten gelöscht");
|
|
2363
2333
|
console.log("\n\n" + "=".repeat(60));
|
|
2364
|
-
console.log("
|
|
2334
|
+
console.log("[STATS] ZUSAMMENFASSUNG");
|
|
2365
2335
|
console.log("=".repeat(60));
|
|
2366
|
-
const finalTemplate = await strapi.
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
populate: ["versions"]
|
|
2371
|
-
}
|
|
2372
|
-
);
|
|
2336
|
+
const finalTemplate = await strapi.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
2337
|
+
documentId: testTemplate.documentId,
|
|
2338
|
+
populate: ["versions"]
|
|
2339
|
+
});
|
|
2373
2340
|
console.log(`
|
|
2374
|
-
|
|
2375
|
-
console.log(` Anzahl Versionen
|
|
2376
|
-
if (finalTemplate.versions && finalTemplate.versions.length > 0) {
|
|
2377
|
-
finalTemplate.versions.forEach((v) => {
|
|
2378
|
-
console.log(` - Version ${v.id}: versionNumber ${v.versionNumber}`);
|
|
2379
|
-
});
|
|
2380
|
-
}
|
|
2341
|
+
[INFO] Template: "${finalTemplate.name}" (documentId: ${finalTemplate.documentId})`);
|
|
2342
|
+
console.log(` Anzahl Versionen: ${finalTemplate.versions?.length || 0}`);
|
|
2381
2343
|
console.log("\n🧹 Aufräumen...");
|
|
2382
|
-
await strapi.
|
|
2383
|
-
|
|
2384
|
-
await strapi.
|
|
2385
|
-
console.log(
|
|
2386
|
-
|
|
2387
|
-
console.log(` ✅ Template ${testTemplate.id} gelöscht`);
|
|
2388
|
-
console.log("\n✅ Test abgeschlossen!\n");
|
|
2344
|
+
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version1.documentId });
|
|
2345
|
+
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version2.documentId });
|
|
2346
|
+
await strapi.documents(EMAIL_TEMPLATE_UID).delete({ documentId: testTemplate.documentId });
|
|
2347
|
+
console.log(" [SUCCESS] Alle Test-Daten gelöscht");
|
|
2348
|
+
console.log("\n[SUCCESS] Test abgeschlossen!\n");
|
|
2389
2349
|
const allSuccess = test1Success && test1ReverseSuccess && test2Success && test2ReverseSuccess && test3a_versionCreated && test3a_hasTemplate && test3b_twoVersions && test3b_allHaveTemplate;
|
|
2390
2350
|
ctx.body = {
|
|
2391
2351
|
success: allSuccess,
|
|
2392
|
-
message: allSuccess ? "Alle Tests erfolgreich!
|
|
2352
|
+
message: allSuccess ? "Alle Tests erfolgreich! [SUCCESS]" : "Einige Tests fehlgeschlagen [ERROR]",
|
|
2393
2353
|
tests: {
|
|
2394
2354
|
test1_version_to_template: test1Success,
|
|
2395
2355
|
test1_template_to_version: test1ReverseSuccess,
|
|
@@ -2399,15 +2359,10 @@ function requireTest() {
|
|
|
2399
2359
|
test3_auto_version_has_template: test3a_hasTemplate,
|
|
2400
2360
|
test3_two_auto_versions: test3b_twoVersions,
|
|
2401
2361
|
test3_all_auto_versions_have_template: test3b_allHaveTemplate
|
|
2402
|
-
},
|
|
2403
|
-
template: {
|
|
2404
|
-
id: testTemplate.id,
|
|
2405
|
-
name: testTemplate.name,
|
|
2406
|
-
versionsCount: finalTemplate.versions?.length || 0
|
|
2407
2362
|
}
|
|
2408
2363
|
};
|
|
2409
2364
|
} catch (error) {
|
|
2410
|
-
console.error("\n
|
|
2365
|
+
console.error("\n[ERROR] FEHLER:", error.message);
|
|
2411
2366
|
console.error(error.stack);
|
|
2412
2367
|
ctx.throw(500, error);
|
|
2413
2368
|
}
|
|
@@ -3001,7 +2956,7 @@ function requireEncryption() {
|
|
|
3001
2956
|
if (envKey) {
|
|
3002
2957
|
return crypto.createHash("sha256").update(envKey).digest();
|
|
3003
2958
|
}
|
|
3004
|
-
console.warn("[magic-mail]
|
|
2959
|
+
console.warn("[magic-mail] [WARNING] No MAGIC_MAIL_ENCRYPTION_KEY found. Using fallback.");
|
|
3005
2960
|
return crypto.createHash("sha256").update("magic-mail-default-key").digest();
|
|
3006
2961
|
}
|
|
3007
2962
|
function encryptCredentials(data) {
|
|
@@ -3080,12 +3035,17 @@ function requireEmailRouter() {
|
|
|
3080
3035
|
// Template Reference ID
|
|
3081
3036
|
templateData,
|
|
3082
3037
|
// Data for template rendering
|
|
3083
|
-
data
|
|
3038
|
+
data,
|
|
3084
3039
|
// Alias for templateData (for native Strapi compatibility)
|
|
3040
|
+
skipLinkTracking = false
|
|
3041
|
+
// Skip link rewriting for sensitive URLs (e.g., Magic Links)
|
|
3085
3042
|
} = emailData;
|
|
3086
3043
|
if (!templateData && data) {
|
|
3087
3044
|
templateData = data;
|
|
3088
3045
|
}
|
|
3046
|
+
if (skipLinkTracking) {
|
|
3047
|
+
strapi2.log.info(`[magic-mail] [SKIP-TRACK] skipLinkTracking=true received for email to: ${to}`);
|
|
3048
|
+
}
|
|
3089
3049
|
let renderedTemplate = null;
|
|
3090
3050
|
if (templateId || emailData.templateReferenceId) {
|
|
3091
3051
|
try {
|
|
@@ -3098,10 +3058,10 @@ function requireEmailRouter() {
|
|
|
3098
3058
|
if (!resolvedTemplateReferenceId && templateId) {
|
|
3099
3059
|
const numericTemplateId = Number(templateId);
|
|
3100
3060
|
if (!Number.isNaN(numericTemplateId) && Number.isInteger(numericTemplateId)) {
|
|
3101
|
-
strapi2.log.info(`[magic-mail]
|
|
3061
|
+
strapi2.log.info(`[magic-mail] [CHECK] Looking up template by ID: ${numericTemplateId}`);
|
|
3102
3062
|
templateRecord = await strapi2.plugin("magic-mail").service("email-designer").findOne(numericTemplateId);
|
|
3103
3063
|
if (!templateRecord) {
|
|
3104
|
-
strapi2.log.error(`[magic-mail]
|
|
3064
|
+
strapi2.log.error(`[magic-mail] [ERROR] Template with ID ${numericTemplateId} not found in database`);
|
|
3105
3065
|
throw new Error(`Template with ID ${numericTemplateId} not found`);
|
|
3106
3066
|
}
|
|
3107
3067
|
if (!templateRecord.templateReferenceId) {
|
|
@@ -3109,7 +3069,7 @@ function requireEmailRouter() {
|
|
|
3109
3069
|
}
|
|
3110
3070
|
resolvedTemplateReferenceId = String(templateRecord.templateReferenceId).trim();
|
|
3111
3071
|
strapi2.log.info(
|
|
3112
|
-
`[magic-mail]
|
|
3072
|
+
`[magic-mail] [SUCCESS] Found template: ID=${templateRecord.id}, referenceId="${resolvedTemplateReferenceId}", name="${templateRecord.name}"`
|
|
3113
3073
|
);
|
|
3114
3074
|
} else {
|
|
3115
3075
|
resolvedTemplateReferenceId = String(templateId).trim();
|
|
@@ -3125,14 +3085,14 @@ function requireEmailRouter() {
|
|
|
3125
3085
|
subject = subject || renderedTemplate.subject;
|
|
3126
3086
|
type = type || renderedTemplate.category;
|
|
3127
3087
|
strapi2.log.info(
|
|
3128
|
-
`[magic-mail]
|
|
3088
|
+
`[magic-mail] [EMAIL] Rendered template reference "${resolvedTemplateReferenceId}" (requested ID: ${templateId ?? "n/a"}): ${renderedTemplate.templateName}`
|
|
3129
3089
|
);
|
|
3130
3090
|
emailData.templateReferenceId = resolvedTemplateReferenceId;
|
|
3131
3091
|
if (!emailData.templateName) {
|
|
3132
3092
|
emailData.templateName = templateRecord?.name || renderedTemplate.templateName;
|
|
3133
3093
|
}
|
|
3134
3094
|
} catch (error) {
|
|
3135
|
-
strapi2.log.error(`[magic-mail]
|
|
3095
|
+
strapi2.log.error(`[magic-mail] [ERROR] Template rendering failed: ${error.message}`);
|
|
3136
3096
|
throw new Error(`Template rendering failed: ${error.message}`);
|
|
3137
3097
|
}
|
|
3138
3098
|
}
|
|
@@ -3161,10 +3121,14 @@ function requireEmailRouter() {
|
|
|
3161
3121
|
});
|
|
3162
3122
|
recipientHash = analyticsService.generateRecipientHash(emailLog.emailId, to);
|
|
3163
3123
|
html = analyticsService.injectTrackingPixel(html, emailLog.emailId, recipientHash);
|
|
3164
|
-
|
|
3165
|
-
|
|
3124
|
+
if (!skipLinkTracking) {
|
|
3125
|
+
html = await analyticsService.rewriteLinksForTracking(html, emailLog.emailId, recipientHash);
|
|
3126
|
+
strapi2.log.info(`[magic-mail] [STATS] Full tracking enabled for email: ${emailLog.emailId}`);
|
|
3127
|
+
} else {
|
|
3128
|
+
strapi2.log.info(`[magic-mail] [STATS] Open tracking enabled, link tracking DISABLED for email: ${emailLog.emailId}`);
|
|
3129
|
+
}
|
|
3166
3130
|
} catch (error) {
|
|
3167
|
-
strapi2.log.error(`[magic-mail]
|
|
3131
|
+
strapi2.log.error(`[magic-mail] [WARNING] Tracking setup failed (continuing without tracking):`, error.message);
|
|
3168
3132
|
}
|
|
3169
3133
|
}
|
|
3170
3134
|
emailData.html = html;
|
|
@@ -3175,7 +3139,7 @@ function requireEmailRouter() {
|
|
|
3175
3139
|
if (priority === "high") {
|
|
3176
3140
|
const hasFeature = await licenseGuard2.hasFeature("priority-headers");
|
|
3177
3141
|
if (!hasFeature) {
|
|
3178
|
-
strapi2.log.warn("[magic-mail]
|
|
3142
|
+
strapi2.log.warn("[magic-mail] [WARNING] High priority emails require Advanced license - using normal priority");
|
|
3179
3143
|
emailData.priority = "normal";
|
|
3180
3144
|
}
|
|
3181
3145
|
}
|
|
@@ -3199,8 +3163,8 @@ function requireEmailRouter() {
|
|
|
3199
3163
|
const result = await this.sendViaAccount(account, emailData);
|
|
3200
3164
|
if (emailLog) {
|
|
3201
3165
|
try {
|
|
3202
|
-
await strapi2.
|
|
3203
|
-
|
|
3166
|
+
await strapi2.documents("plugin::magic-mail.email-log").update({
|
|
3167
|
+
documentId: emailLog.documentId,
|
|
3204
3168
|
data: {
|
|
3205
3169
|
accountId: account.id,
|
|
3206
3170
|
accountName: account.name,
|
|
@@ -3211,15 +3175,15 @@ function requireEmailRouter() {
|
|
|
3211
3175
|
strapi2.log.error("[magic-mail] Failed to update email log:", error.message);
|
|
3212
3176
|
}
|
|
3213
3177
|
}
|
|
3214
|
-
await this.updateAccountStats(account.
|
|
3215
|
-
strapi2.log.info(`[magic-mail]
|
|
3178
|
+
await this.updateAccountStats(account.documentId);
|
|
3179
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Email sent to ${to} via ${account.name}`);
|
|
3216
3180
|
return {
|
|
3217
3181
|
success: true,
|
|
3218
3182
|
accountUsed: account.name,
|
|
3219
3183
|
messageId: result.messageId
|
|
3220
3184
|
};
|
|
3221
3185
|
} catch (error) {
|
|
3222
|
-
strapi2.log.error("[magic-mail]
|
|
3186
|
+
strapi2.log.error("[magic-mail] [ERROR] Email send failed:", error);
|
|
3223
3187
|
throw error;
|
|
3224
3188
|
}
|
|
3225
3189
|
},
|
|
@@ -3227,21 +3191,21 @@ function requireEmailRouter() {
|
|
|
3227
3191
|
* Select best account based on rules
|
|
3228
3192
|
*/
|
|
3229
3193
|
async selectAccount(type, priority, excludeIds = [], emailData = {}) {
|
|
3230
|
-
const accounts2 = await strapi2.
|
|
3194
|
+
const accounts2 = await strapi2.documents("plugin::magic-mail.email-account").findMany({
|
|
3231
3195
|
filters: {
|
|
3232
3196
|
isActive: true,
|
|
3233
3197
|
id: { $notIn: excludeIds }
|
|
3234
3198
|
},
|
|
3235
|
-
sort: { priority: "desc" }
|
|
3199
|
+
sort: [{ priority: "desc" }]
|
|
3236
3200
|
});
|
|
3237
3201
|
if (!accounts2 || accounts2.length === 0) {
|
|
3238
3202
|
return null;
|
|
3239
3203
|
}
|
|
3240
|
-
const allRules = await strapi2.
|
|
3204
|
+
const allRules = await strapi2.documents("plugin::magic-mail.routing-rule").findMany({
|
|
3241
3205
|
filters: {
|
|
3242
3206
|
isActive: true
|
|
3243
3207
|
},
|
|
3244
|
-
sort: { priority: "desc" }
|
|
3208
|
+
sort: [{ priority: "desc" }]
|
|
3245
3209
|
});
|
|
3246
3210
|
for (const rule of allRules) {
|
|
3247
3211
|
let matches = false;
|
|
@@ -3265,13 +3229,13 @@ function requireEmailRouter() {
|
|
|
3265
3229
|
if (matches) {
|
|
3266
3230
|
const account = accounts2.find((a) => a.name === rule.accountName);
|
|
3267
3231
|
if (account) {
|
|
3268
|
-
strapi2.log.info(`[magic-mail]
|
|
3232
|
+
strapi2.log.info(`[magic-mail] [ROUTE] Routing rule matched: ${rule.name} -> ${account.name}`);
|
|
3269
3233
|
return account;
|
|
3270
3234
|
}
|
|
3271
3235
|
if (rule.fallbackAccountName) {
|
|
3272
3236
|
const fallbackAccount = accounts2.find((a) => a.name === rule.fallbackAccountName);
|
|
3273
3237
|
if (fallbackAccount) {
|
|
3274
|
-
strapi2.log.info(`[magic-mail]
|
|
3238
|
+
strapi2.log.info(`[magic-mail] [FALLBACK] Using fallback account: ${fallbackAccount.name}`);
|
|
3275
3239
|
return fallbackAccount;
|
|
3276
3240
|
}
|
|
3277
3241
|
}
|
|
@@ -3375,7 +3339,7 @@ function requireEmailRouter() {
|
|
|
3375
3339
|
mailOptions.headers["List-Unsubscribe-Post"] = "List-Unsubscribe=One-Click";
|
|
3376
3340
|
mailOptions.headers["Precedence"] = "bulk";
|
|
3377
3341
|
} else {
|
|
3378
|
-
strapi2.log.warn("[magic-mail]
|
|
3342
|
+
strapi2.log.warn("[magic-mail] [WARNING] Marketing email without unsubscribe URL - may violate GDPR/CAN-SPAM");
|
|
3379
3343
|
}
|
|
3380
3344
|
}
|
|
3381
3345
|
if (emailData.headers && typeof emailData.headers === "object") {
|
|
@@ -3421,14 +3385,15 @@ function requireEmailRouter() {
|
|
|
3421
3385
|
config2.clientSecret
|
|
3422
3386
|
);
|
|
3423
3387
|
currentAccessToken = newTokens.accessToken;
|
|
3424
|
-
strapi2.log.info("[magic-mail]
|
|
3388
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Token refreshed successfully");
|
|
3425
3389
|
const { encryptCredentials } = requireEncryption();
|
|
3426
3390
|
const updatedOAuth = encryptCredentials({
|
|
3427
3391
|
...oauth2,
|
|
3428
3392
|
accessToken: newTokens.accessToken,
|
|
3429
3393
|
expiresAt: newTokens.expiresAt
|
|
3430
3394
|
});
|
|
3431
|
-
await strapi2.
|
|
3395
|
+
await strapi2.documents("plugin::magic-mail.email-account").update({
|
|
3396
|
+
documentId: account.documentId,
|
|
3432
3397
|
data: { oauth: updatedOAuth }
|
|
3433
3398
|
});
|
|
3434
3399
|
} catch (refreshErr) {
|
|
@@ -3547,13 +3512,19 @@ function requireEmailRouter() {
|
|
|
3547
3512
|
throw new Error(`Gmail API error: ${errorData.error?.message || response.statusText}`);
|
|
3548
3513
|
}
|
|
3549
3514
|
const result = await response.json();
|
|
3550
|
-
strapi2.log.info("[magic-mail]
|
|
3515
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via Gmail API");
|
|
3551
3516
|
return {
|
|
3552
3517
|
messageId: result.id,
|
|
3553
3518
|
response: "OK"
|
|
3554
3519
|
};
|
|
3555
3520
|
} catch (err) {
|
|
3556
|
-
strapi2.log.error("[magic-mail] Gmail API send failed:", err);
|
|
3521
|
+
strapi2.log.error("[magic-mail] Gmail API send failed:", err.message || err);
|
|
3522
|
+
strapi2.log.error("[magic-mail] Error details:", {
|
|
3523
|
+
name: err.name,
|
|
3524
|
+
code: err.code,
|
|
3525
|
+
cause: err.cause?.message || err.cause,
|
|
3526
|
+
stack: err.stack?.split("\n").slice(0, 3).join("\n")
|
|
3527
|
+
});
|
|
3557
3528
|
throw err;
|
|
3558
3529
|
}
|
|
3559
3530
|
},
|
|
@@ -3596,14 +3567,15 @@ function requireEmailRouter() {
|
|
|
3596
3567
|
config2.tenantId
|
|
3597
3568
|
);
|
|
3598
3569
|
currentAccessToken = newTokens.accessToken;
|
|
3599
|
-
strapi2.log.info("[magic-mail]
|
|
3570
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Microsoft token refreshed successfully");
|
|
3600
3571
|
const { encryptCredentials } = requireEncryption();
|
|
3601
3572
|
const updatedOAuth = encryptCredentials({
|
|
3602
3573
|
...oauth2,
|
|
3603
3574
|
accessToken: newTokens.accessToken,
|
|
3604
3575
|
expiresAt: newTokens.expiresAt
|
|
3605
3576
|
});
|
|
3606
|
-
await strapi2.
|
|
3577
|
+
await strapi2.documents("plugin::magic-mail.email-account").update({
|
|
3578
|
+
documentId: account.documentId,
|
|
3607
3579
|
data: { oauth: updatedOAuth }
|
|
3608
3580
|
});
|
|
3609
3581
|
} catch (refreshErr) {
|
|
@@ -3713,7 +3685,7 @@ function requireEmailRouter() {
|
|
|
3713
3685
|
strapi2.log.error("[magic-mail] Response status:", response.status);
|
|
3714
3686
|
throw new Error(`Microsoft Graph API error: ${response.status} - ${response.statusText}`);
|
|
3715
3687
|
}
|
|
3716
|
-
strapi2.log.info("[magic-mail]
|
|
3688
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via Microsoft Graph API with MIME + custom headers");
|
|
3717
3689
|
strapi2.log.info("[magic-mail] Microsoft adds From/DKIM automatically for DMARC compliance");
|
|
3718
3690
|
return {
|
|
3719
3691
|
messageId: `microsoft-${Date.now()}`,
|
|
@@ -3755,14 +3727,15 @@ function requireEmailRouter() {
|
|
|
3755
3727
|
config2.clientSecret
|
|
3756
3728
|
);
|
|
3757
3729
|
currentAccessToken = newTokens.accessToken;
|
|
3758
|
-
strapi2.log.info("[magic-mail]
|
|
3730
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Token refreshed successfully");
|
|
3759
3731
|
const { encryptCredentials } = requireEncryption();
|
|
3760
3732
|
const updatedOAuth = encryptCredentials({
|
|
3761
3733
|
...oauth2,
|
|
3762
3734
|
accessToken: newTokens.accessToken,
|
|
3763
3735
|
expiresAt: newTokens.expiresAt
|
|
3764
3736
|
});
|
|
3765
|
-
await strapi2.
|
|
3737
|
+
await strapi2.documents("plugin::magic-mail.email-account").update({
|
|
3738
|
+
documentId: account.documentId,
|
|
3766
3739
|
data: { oauth: updatedOAuth }
|
|
3767
3740
|
});
|
|
3768
3741
|
} catch (refreshErr) {
|
|
@@ -3808,7 +3781,7 @@ function requireEmailRouter() {
|
|
|
3808
3781
|
strapi2.log.info(`[magic-mail] Sending email with ${mailOptions.attachments.length} attachment(s)`);
|
|
3809
3782
|
}
|
|
3810
3783
|
const result = await transporter.sendMail(mailOptions);
|
|
3811
|
-
strapi2.log.info("[magic-mail]
|
|
3784
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via Yahoo OAuth");
|
|
3812
3785
|
return {
|
|
3813
3786
|
messageId: result.messageId,
|
|
3814
3787
|
response: result.response
|
|
@@ -3903,7 +3876,7 @@ function requireEmailRouter() {
|
|
|
3903
3876
|
strapi2.log.error("[magic-mail] SendGrid API error:", errorText);
|
|
3904
3877
|
throw new Error(`SendGrid API error: ${response.statusText}`);
|
|
3905
3878
|
}
|
|
3906
|
-
strapi2.log.info("[magic-mail]
|
|
3879
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via SendGrid API");
|
|
3907
3880
|
return {
|
|
3908
3881
|
messageId: response.headers.get("x-message-id") || `sendgrid-${Date.now()}`,
|
|
3909
3882
|
response: "Accepted"
|
|
@@ -3986,7 +3959,7 @@ function requireEmailRouter() {
|
|
|
3986
3959
|
throw new Error(`Mailgun API error: ${response.statusText}`);
|
|
3987
3960
|
}
|
|
3988
3961
|
const result = await response.json();
|
|
3989
|
-
strapi2.log.info("[magic-mail]
|
|
3962
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email sent via Mailgun API");
|
|
3990
3963
|
return {
|
|
3991
3964
|
messageId: result.id || `mailgun-${Date.now()}`,
|
|
3992
3965
|
response: result.message || "Queued"
|
|
@@ -4010,10 +3983,15 @@ function requireEmailRouter() {
|
|
|
4010
3983
|
},
|
|
4011
3984
|
/**
|
|
4012
3985
|
* Update account statistics
|
|
3986
|
+
* Note: This function now expects documentId
|
|
4013
3987
|
*/
|
|
4014
|
-
async updateAccountStats(
|
|
4015
|
-
const account = await strapi2.
|
|
4016
|
-
|
|
3988
|
+
async updateAccountStats(documentId) {
|
|
3989
|
+
const account = await strapi2.documents("plugin::magic-mail.email-account").findOne({
|
|
3990
|
+
documentId
|
|
3991
|
+
});
|
|
3992
|
+
if (!account) return;
|
|
3993
|
+
await strapi2.documents("plugin::magic-mail.email-account").update({
|
|
3994
|
+
documentId,
|
|
4017
3995
|
data: {
|
|
4018
3996
|
emailsSentToday: (account.emailsSentToday || 0) + 1,
|
|
4019
3997
|
emailsSentThisHour: (account.emailsSentThisHour || 0) + 1,
|
|
@@ -4034,7 +4012,7 @@ function requireEmailRouter() {
|
|
|
4034
4012
|
* Get account by name
|
|
4035
4013
|
*/
|
|
4036
4014
|
async getAccountByName(name) {
|
|
4037
|
-
const accounts2 = await strapi2.
|
|
4015
|
+
const accounts2 = await strapi2.documents("plugin::magic-mail.email-account").findMany({
|
|
4038
4016
|
filters: { name, isActive: true },
|
|
4039
4017
|
limit: 1
|
|
4040
4018
|
});
|
|
@@ -4084,7 +4062,7 @@ function requireEmailRouter() {
|
|
|
4084
4062
|
if (html && !text) {
|
|
4085
4063
|
strapi2.log.warn("[magic-mail] Email has HTML but no text alternative - may reduce deliverability");
|
|
4086
4064
|
}
|
|
4087
|
-
strapi2.log.info("[magic-mail]
|
|
4065
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Email security validation passed");
|
|
4088
4066
|
},
|
|
4089
4067
|
/**
|
|
4090
4068
|
* Add security headers to email data
|
|
@@ -4124,7 +4102,24 @@ function requireAccountManager() {
|
|
|
4124
4102
|
if (hasRequiredAccountManager) return accountManager;
|
|
4125
4103
|
hasRequiredAccountManager = 1;
|
|
4126
4104
|
const { encryptCredentials, decryptCredentials } = requireEncryption();
|
|
4105
|
+
const EMAIL_ACCOUNT_UID = "plugin::magic-mail.email-account";
|
|
4127
4106
|
accountManager = ({ strapi: strapi2 }) => ({
|
|
4107
|
+
/**
|
|
4108
|
+
* Resolves account ID to documentId (handles both numeric id and documentId)
|
|
4109
|
+
* @param {string|number} idOrDocumentId - Either numeric id or documentId
|
|
4110
|
+
* @returns {Promise<string|null>} The documentId or null if not found
|
|
4111
|
+
*/
|
|
4112
|
+
async resolveDocumentId(idOrDocumentId) {
|
|
4113
|
+
if (idOrDocumentId && !/^\d+$/.test(String(idOrDocumentId))) {
|
|
4114
|
+
return String(idOrDocumentId);
|
|
4115
|
+
}
|
|
4116
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
4117
|
+
filters: { id: Number(idOrDocumentId) },
|
|
4118
|
+
fields: ["documentId"],
|
|
4119
|
+
limit: 1
|
|
4120
|
+
});
|
|
4121
|
+
return accounts2.length > 0 ? accounts2[0].documentId : null;
|
|
4122
|
+
},
|
|
4128
4123
|
/**
|
|
4129
4124
|
* Create new email account
|
|
4130
4125
|
*/
|
|
@@ -4146,7 +4141,7 @@ function requireAccountManager() {
|
|
|
4146
4141
|
if (isPrimary) {
|
|
4147
4142
|
await this.unsetAllPrimary();
|
|
4148
4143
|
}
|
|
4149
|
-
const account = await strapi2.
|
|
4144
|
+
const account = await strapi2.documents(EMAIL_ACCOUNT_UID).create({
|
|
4150
4145
|
data: {
|
|
4151
4146
|
name,
|
|
4152
4147
|
provider,
|
|
@@ -4164,14 +4159,20 @@ function requireAccountManager() {
|
|
|
4164
4159
|
totalEmailsSent: 0
|
|
4165
4160
|
}
|
|
4166
4161
|
});
|
|
4167
|
-
strapi2.log.info(`[magic-mail]
|
|
4162
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Email account created: ${name}`);
|
|
4168
4163
|
return account;
|
|
4169
4164
|
},
|
|
4170
4165
|
/**
|
|
4171
4166
|
* Update email account
|
|
4172
4167
|
*/
|
|
4173
|
-
async updateAccount(
|
|
4174
|
-
const
|
|
4168
|
+
async updateAccount(idOrDocumentId, accountData) {
|
|
4169
|
+
const documentId = await this.resolveDocumentId(idOrDocumentId);
|
|
4170
|
+
if (!documentId) {
|
|
4171
|
+
throw new Error("Account not found");
|
|
4172
|
+
}
|
|
4173
|
+
const existingAccount = await strapi2.documents(EMAIL_ACCOUNT_UID).findOne({
|
|
4174
|
+
documentId
|
|
4175
|
+
});
|
|
4175
4176
|
if (!existingAccount) {
|
|
4176
4177
|
throw new Error("Account not found");
|
|
4177
4178
|
}
|
|
@@ -4193,7 +4194,8 @@ function requireAccountManager() {
|
|
|
4193
4194
|
if (isPrimary && !existingAccount.isPrimary) {
|
|
4194
4195
|
await this.unsetAllPrimary();
|
|
4195
4196
|
}
|
|
4196
|
-
const updatedAccount = await strapi2.
|
|
4197
|
+
const updatedAccount = await strapi2.documents(EMAIL_ACCOUNT_UID).update({
|
|
4198
|
+
documentId,
|
|
4197
4199
|
data: {
|
|
4198
4200
|
name,
|
|
4199
4201
|
description,
|
|
@@ -4209,14 +4211,20 @@ function requireAccountManager() {
|
|
|
4209
4211
|
hourlyLimit
|
|
4210
4212
|
}
|
|
4211
4213
|
});
|
|
4212
|
-
strapi2.log.info(`[magic-mail]
|
|
4214
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Email account updated: ${name} (Active: ${isActive})`);
|
|
4213
4215
|
return updatedAccount;
|
|
4214
4216
|
},
|
|
4215
4217
|
/**
|
|
4216
4218
|
* Test email account
|
|
4217
4219
|
*/
|
|
4218
|
-
async testAccount(
|
|
4219
|
-
const
|
|
4220
|
+
async testAccount(idOrDocumentId, testEmail, testOptions = {}) {
|
|
4221
|
+
const documentId = await this.resolveDocumentId(idOrDocumentId);
|
|
4222
|
+
if (!documentId) {
|
|
4223
|
+
throw new Error("Account not found");
|
|
4224
|
+
}
|
|
4225
|
+
const account = await strapi2.documents(EMAIL_ACCOUNT_UID).findOne({
|
|
4226
|
+
documentId
|
|
4227
|
+
});
|
|
4220
4228
|
if (!account) {
|
|
4221
4229
|
throw new Error("Account not found");
|
|
4222
4230
|
}
|
|
@@ -4295,8 +4303,8 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4295
4303
|
* Get all accounts
|
|
4296
4304
|
*/
|
|
4297
4305
|
async getAllAccounts() {
|
|
4298
|
-
const accounts2 = await strapi2.
|
|
4299
|
-
sort: { priority: "desc" }
|
|
4306
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
4307
|
+
sort: [{ priority: "desc" }]
|
|
4300
4308
|
});
|
|
4301
4309
|
return accounts2.map((account) => ({
|
|
4302
4310
|
...account,
|
|
@@ -4306,8 +4314,14 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4306
4314
|
/**
|
|
4307
4315
|
* Get single account with decrypted config (for editing)
|
|
4308
4316
|
*/
|
|
4309
|
-
async getAccountWithDecryptedConfig(
|
|
4310
|
-
const
|
|
4317
|
+
async getAccountWithDecryptedConfig(idOrDocumentId) {
|
|
4318
|
+
const documentId = await this.resolveDocumentId(idOrDocumentId);
|
|
4319
|
+
if (!documentId) {
|
|
4320
|
+
throw new Error("Account not found");
|
|
4321
|
+
}
|
|
4322
|
+
const account = await strapi2.documents(EMAIL_ACCOUNT_UID).findOne({
|
|
4323
|
+
documentId
|
|
4324
|
+
});
|
|
4311
4325
|
if (!account) {
|
|
4312
4326
|
throw new Error("Account not found");
|
|
4313
4327
|
}
|
|
@@ -4320,19 +4334,24 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4320
4334
|
/**
|
|
4321
4335
|
* Delete account
|
|
4322
4336
|
*/
|
|
4323
|
-
async deleteAccount(
|
|
4324
|
-
await
|
|
4325
|
-
|
|
4337
|
+
async deleteAccount(idOrDocumentId) {
|
|
4338
|
+
const documentId = await this.resolveDocumentId(idOrDocumentId);
|
|
4339
|
+
if (!documentId) {
|
|
4340
|
+
throw new Error("Account not found");
|
|
4341
|
+
}
|
|
4342
|
+
await strapi2.documents(EMAIL_ACCOUNT_UID).delete({ documentId });
|
|
4343
|
+
strapi2.log.info(`[magic-mail] Account deleted: ${documentId}`);
|
|
4326
4344
|
},
|
|
4327
4345
|
/**
|
|
4328
4346
|
* Unset all primary flags
|
|
4329
4347
|
*/
|
|
4330
4348
|
async unsetAllPrimary() {
|
|
4331
|
-
const accounts2 = await strapi2.
|
|
4349
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
4332
4350
|
filters: { isPrimary: true }
|
|
4333
4351
|
});
|
|
4334
4352
|
for (const account of accounts2) {
|
|
4335
|
-
await strapi2.
|
|
4353
|
+
await strapi2.documents(EMAIL_ACCOUNT_UID).update({
|
|
4354
|
+
documentId: account.documentId,
|
|
4336
4355
|
data: { isPrimary: false }
|
|
4337
4356
|
});
|
|
4338
4357
|
}
|
|
@@ -4341,7 +4360,7 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4341
4360
|
* Reset daily/hourly counters (called by cron)
|
|
4342
4361
|
*/
|
|
4343
4362
|
async resetCounters(type = "daily") {
|
|
4344
|
-
const accounts2 = await strapi2.
|
|
4363
|
+
const accounts2 = await strapi2.documents(EMAIL_ACCOUNT_UID).findMany({});
|
|
4345
4364
|
for (const account of accounts2) {
|
|
4346
4365
|
const updateData = {};
|
|
4347
4366
|
if (type === "daily") {
|
|
@@ -4349,11 +4368,12 @@ If you receive this, your email account is configured correctly!`,
|
|
|
4349
4368
|
} else if (type === "hourly") {
|
|
4350
4369
|
updateData.emailsSentThisHour = 0;
|
|
4351
4370
|
}
|
|
4352
|
-
await strapi2.
|
|
4371
|
+
await strapi2.documents(EMAIL_ACCOUNT_UID).update({
|
|
4372
|
+
documentId: account.documentId,
|
|
4353
4373
|
data: updateData
|
|
4354
4374
|
});
|
|
4355
4375
|
}
|
|
4356
|
-
strapi2.log.info(`[magic-mail]
|
|
4376
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] ${type} counters reset`);
|
|
4357
4377
|
}
|
|
4358
4378
|
});
|
|
4359
4379
|
return accountManager;
|
|
@@ -4411,7 +4431,7 @@ function requireOauth() {
|
|
|
4411
4431
|
throw new Error(`Failed to exchange code for tokens: ${response.status}`);
|
|
4412
4432
|
}
|
|
4413
4433
|
const tokens = await response.json();
|
|
4414
|
-
strapi2.log.info("[magic-mail]
|
|
4434
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Tokens received from Google");
|
|
4415
4435
|
if (!tokens.access_token) {
|
|
4416
4436
|
throw new Error("No access token received from Google");
|
|
4417
4437
|
}
|
|
@@ -4425,7 +4445,7 @@ function requireOauth() {
|
|
|
4425
4445
|
throw new Error("Failed to get user email from Google");
|
|
4426
4446
|
}
|
|
4427
4447
|
const userInfo = await userInfoResponse.json();
|
|
4428
|
-
strapi2.log.info(`[magic-mail]
|
|
4448
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Got user email from Google: ${userInfo.email}`);
|
|
4429
4449
|
if (!userInfo.email) {
|
|
4430
4450
|
strapi2.log.error("[magic-mail] userInfo:", userInfo);
|
|
4431
4451
|
throw new Error("Google did not provide email address");
|
|
@@ -4529,7 +4549,7 @@ function requireOauth() {
|
|
|
4529
4549
|
throw new Error(`Failed to exchange code for tokens: ${response.status} - ${errorData}`);
|
|
4530
4550
|
}
|
|
4531
4551
|
const tokens = await response.json();
|
|
4532
|
-
strapi2.log.info("[magic-mail]
|
|
4552
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Tokens received from Microsoft");
|
|
4533
4553
|
strapi2.log.info("[magic-mail] Has access_token:", !!tokens.access_token);
|
|
4534
4554
|
strapi2.log.info("[magic-mail] Has refresh_token:", !!tokens.refresh_token);
|
|
4535
4555
|
strapi2.log.info("[magic-mail] Has id_token:", !!tokens.id_token);
|
|
@@ -4542,7 +4562,7 @@ function requireOauth() {
|
|
|
4542
4562
|
const payloadBase64 = tokens.id_token.split(".")[1];
|
|
4543
4563
|
const payload = JSON.parse(Buffer.from(payloadBase64, "base64").toString());
|
|
4544
4564
|
email = payload.email || payload.preferred_username || payload.upn;
|
|
4545
|
-
strapi2.log.info(`[magic-mail]
|
|
4565
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Got email from Microsoft ID token: ${email}`);
|
|
4546
4566
|
} catch (jwtErr) {
|
|
4547
4567
|
strapi2.log.warn("[magic-mail] Could not decode ID token:", jwtErr.message);
|
|
4548
4568
|
}
|
|
@@ -4564,7 +4584,7 @@ function requireOauth() {
|
|
|
4564
4584
|
const userInfo = await userInfoResponse.json();
|
|
4565
4585
|
strapi2.log.info("[magic-mail] User info from Graph:", JSON.stringify(userInfo, null, 2));
|
|
4566
4586
|
email = userInfo.mail || userInfo.userPrincipalName;
|
|
4567
|
-
strapi2.log.info(`[magic-mail]
|
|
4587
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Got email from Microsoft Graph: ${email}`);
|
|
4568
4588
|
}
|
|
4569
4589
|
if (!email) {
|
|
4570
4590
|
strapi2.log.error("[magic-mail] Microsoft did not provide email - ID token and Graph API both failed");
|
|
@@ -4608,7 +4628,7 @@ function requireOauth() {
|
|
|
4608
4628
|
throw new Error(`Failed to refresh Microsoft tokens: ${response.status}`);
|
|
4609
4629
|
}
|
|
4610
4630
|
const tokens = await response.json();
|
|
4611
|
-
strapi2.log.info("[magic-mail]
|
|
4631
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Microsoft tokens refreshed successfully");
|
|
4612
4632
|
return {
|
|
4613
4633
|
accessToken: tokens.access_token,
|
|
4614
4634
|
expiresAt: new Date(Date.now() + (tokens.expires_in || 3600) * 1e3)
|
|
@@ -4663,7 +4683,7 @@ function requireOauth() {
|
|
|
4663
4683
|
throw new Error(`Failed to exchange code for tokens: ${response.status}`);
|
|
4664
4684
|
}
|
|
4665
4685
|
const tokens = await response.json();
|
|
4666
|
-
strapi2.log.info("[magic-mail]
|
|
4686
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Tokens received from Yahoo");
|
|
4667
4687
|
if (!tokens.access_token) {
|
|
4668
4688
|
throw new Error("No access token received from Yahoo");
|
|
4669
4689
|
}
|
|
@@ -4678,7 +4698,7 @@ function requireOauth() {
|
|
|
4678
4698
|
}
|
|
4679
4699
|
const userInfo = await userInfoResponse.json();
|
|
4680
4700
|
const email = userInfo.email;
|
|
4681
|
-
strapi2.log.info(`[magic-mail]
|
|
4701
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Got email from Yahoo: ${email}`);
|
|
4682
4702
|
if (!email) {
|
|
4683
4703
|
throw new Error("Yahoo did not provide email address");
|
|
4684
4704
|
}
|
|
@@ -4739,7 +4759,7 @@ function requireOauth() {
|
|
|
4739
4759
|
refreshToken: tokenData.refreshToken,
|
|
4740
4760
|
expiresAt: tokenData.expiresAt
|
|
4741
4761
|
});
|
|
4742
|
-
const account = await strapi2.
|
|
4762
|
+
const account = await strapi2.documents("plugin::magic-mail.email-account").create({
|
|
4743
4763
|
data: {
|
|
4744
4764
|
name: accountDetails.name,
|
|
4745
4765
|
description: accountDetails.description || "",
|
|
@@ -4749,7 +4769,7 @@ function requireOauth() {
|
|
|
4749
4769
|
oauth: encryptedOAuth,
|
|
4750
4770
|
// OAuth tokens
|
|
4751
4771
|
fromEmail: tokenData.email,
|
|
4752
|
-
//
|
|
4772
|
+
// [SUCCESS] Use email from Google, not from accountDetails
|
|
4753
4773
|
fromName: accountDetails.fromName || tokenData.email.split("@")[0],
|
|
4754
4774
|
replyTo: accountDetails.replyTo || tokenData.email,
|
|
4755
4775
|
isActive: true,
|
|
@@ -4762,13 +4782,13 @@ function requireOauth() {
|
|
|
4762
4782
|
totalEmailsSent: 0
|
|
4763
4783
|
}
|
|
4764
4784
|
});
|
|
4765
|
-
strapi2.log.info(`[magic-mail]
|
|
4785
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] OAuth account created: ${accountDetails.name} (${tokenData.email})`);
|
|
4766
4786
|
return account;
|
|
4767
4787
|
}
|
|
4768
4788
|
});
|
|
4769
4789
|
return oauth;
|
|
4770
4790
|
}
|
|
4771
|
-
const version = "2.0.
|
|
4791
|
+
const version = "2.0.2";
|
|
4772
4792
|
const require$$2 = {
|
|
4773
4793
|
version
|
|
4774
4794
|
};
|
|
@@ -4862,14 +4882,14 @@ function requireLicenseGuard() {
|
|
|
4862
4882
|
});
|
|
4863
4883
|
const data = await response.json();
|
|
4864
4884
|
if (data.success) {
|
|
4865
|
-
strapi2.log.info("[magic-mail]
|
|
4885
|
+
strapi2.log.info("[magic-mail] [SUCCESS] License created:", data.data.licenseKey);
|
|
4866
4886
|
return data.data;
|
|
4867
4887
|
} else {
|
|
4868
|
-
strapi2.log.error("[magic-mail]
|
|
4888
|
+
strapi2.log.error("[magic-mail] [ERROR] License creation failed:", data);
|
|
4869
4889
|
return null;
|
|
4870
4890
|
}
|
|
4871
4891
|
} catch (error) {
|
|
4872
|
-
strapi2.log.error("[magic-mail]
|
|
4892
|
+
strapi2.log.error("[magic-mail] [ERROR] Error creating license:", error);
|
|
4873
4893
|
return null;
|
|
4874
4894
|
}
|
|
4875
4895
|
},
|
|
@@ -4897,10 +4917,10 @@ function requireLicenseGuard() {
|
|
|
4897
4917
|
}
|
|
4898
4918
|
} catch (error) {
|
|
4899
4919
|
if (allowGracePeriod) {
|
|
4900
|
-
strapi2.log.warn("[magic-mail]
|
|
4920
|
+
strapi2.log.warn("[magic-mail] [WARNING] License verification timeout - grace period active");
|
|
4901
4921
|
return { valid: true, data: null, gracePeriod: true };
|
|
4902
4922
|
}
|
|
4903
|
-
strapi2.log.error("[magic-mail]
|
|
4923
|
+
strapi2.log.error("[magic-mail] [ERROR] License verification error:", error.message);
|
|
4904
4924
|
return { valid: false, data: null };
|
|
4905
4925
|
}
|
|
4906
4926
|
},
|
|
@@ -4953,7 +4973,7 @@ function requireLicenseGuard() {
|
|
|
4953
4973
|
name: "magic-mail"
|
|
4954
4974
|
});
|
|
4955
4975
|
await pluginStore.set({ key: "licenseKey", value: licenseKey });
|
|
4956
|
-
strapi2.log.info(`[magic-mail]
|
|
4976
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] License key stored: ${licenseKey.substring(0, 8)}...`);
|
|
4957
4977
|
},
|
|
4958
4978
|
startPinging(licenseKey, intervalMinutes = 15) {
|
|
4959
4979
|
this.pingLicense(licenseKey);
|
|
@@ -4982,7 +5002,7 @@ function requireLicenseGuard() {
|
|
|
4982
5002
|
const license2 = await this.getLicenseByKey(licenseKey);
|
|
4983
5003
|
return license2;
|
|
4984
5004
|
} catch (error) {
|
|
4985
|
-
strapi2.log.error(`[magic-mail]
|
|
5005
|
+
strapi2.log.error(`[magic-mail] [ERROR] Error loading license:`, error);
|
|
4986
5006
|
return null;
|
|
4987
5007
|
}
|
|
4988
5008
|
},
|
|
@@ -5032,7 +5052,7 @@ function requireLicenseGuard() {
|
|
|
5032
5052
|
*/
|
|
5033
5053
|
async initialize() {
|
|
5034
5054
|
try {
|
|
5035
|
-
strapi2.log.info("
|
|
5055
|
+
strapi2.log.info("[INIT] Initializing License Guard...");
|
|
5036
5056
|
const pluginStore = strapi2.store({
|
|
5037
5057
|
type: "plugin",
|
|
5038
5058
|
name: "magic-mail"
|
|
@@ -5050,53 +5070,53 @@ function requireLicenseGuard() {
|
|
|
5050
5070
|
strapi2.log.info("──────────────────────────────────────────────────────────");
|
|
5051
5071
|
strapi2.log.info(`📦 Plugin Store Check:`);
|
|
5052
5072
|
if (licenseKey) {
|
|
5053
|
-
strapi2.log.info(`
|
|
5054
|
-
strapi2.log.info(`
|
|
5073
|
+
strapi2.log.info(` [SUCCESS] License Key found: ${licenseKey}`);
|
|
5074
|
+
strapi2.log.info(` [LICENSE] Key (short): ${licenseKey.substring(0, 10)}...`);
|
|
5055
5075
|
if (lastValidated) {
|
|
5056
5076
|
const lastValidatedDate = new Date(lastValidated);
|
|
5057
5077
|
const hoursAgo = Math.floor((now.getTime() - lastValidatedDate.getTime()) / (1e3 * 60 * 60));
|
|
5058
|
-
strapi2.log.info(`
|
|
5078
|
+
strapi2.log.info(` [TIME] Last validated: ${hoursAgo}h ago (Grace: ${withinGracePeriod ? "ACTIVE" : "EXPIRED"})`);
|
|
5059
5079
|
} else {
|
|
5060
|
-
strapi2.log.info(`
|
|
5080
|
+
strapi2.log.info(` [TIME] Last validated: Never (Grace: ACTIVE for first ${gracePeriodHours}h)`);
|
|
5061
5081
|
}
|
|
5062
5082
|
} else {
|
|
5063
|
-
strapi2.log.info(`
|
|
5083
|
+
strapi2.log.info(` [ERROR] No license key stored`);
|
|
5064
5084
|
}
|
|
5065
5085
|
strapi2.log.info("──────────────────────────────────────────────────────────");
|
|
5066
5086
|
if (!licenseKey) {
|
|
5067
|
-
strapi2.log.info("
|
|
5068
|
-
strapi2.log.info("
|
|
5087
|
+
strapi2.log.info("[DEMO] No license found - Running in demo mode");
|
|
5088
|
+
strapi2.log.info("[INFO] Create a license in the admin panel to activate full features");
|
|
5069
5089
|
return {
|
|
5070
5090
|
valid: false,
|
|
5071
5091
|
demo: true,
|
|
5072
5092
|
data: null
|
|
5073
5093
|
};
|
|
5074
5094
|
}
|
|
5075
|
-
strapi2.log.info("
|
|
5095
|
+
strapi2.log.info("[VERIFY] Verifying stored license key...");
|
|
5076
5096
|
const verification = await this.verifyLicense(licenseKey, withinGracePeriod);
|
|
5077
5097
|
if (verification.valid) {
|
|
5078
5098
|
const license2 = await this.getLicenseByKey(licenseKey);
|
|
5079
|
-
strapi2.log.info(
|
|
5099
|
+
strapi2.log.info(`[SUCCESS] License verified online: ACTIVE (Key: ${licenseKey.substring(0, 10)}...)`);
|
|
5080
5100
|
await pluginStore.set({
|
|
5081
5101
|
key: "lastValidated",
|
|
5082
5102
|
value: now.toISOString()
|
|
5083
5103
|
});
|
|
5084
|
-
strapi2.log.info("
|
|
5104
|
+
strapi2.log.info("[SUCCESS] License is valid and active");
|
|
5085
5105
|
const pingInterval = this.startPinging(licenseKey, 15);
|
|
5086
|
-
strapi2.log.info("
|
|
5106
|
+
strapi2.log.info("[PING] Started pinging license every 15 minutes");
|
|
5087
5107
|
strapi2.licenseGuardMagicMail = {
|
|
5088
5108
|
licenseKey,
|
|
5089
5109
|
pingInterval,
|
|
5090
5110
|
data: verification.data
|
|
5091
5111
|
};
|
|
5092
5112
|
strapi2.log.info("╔════════════════════════════════════════════════════════════════╗");
|
|
5093
|
-
strapi2.log.info("║
|
|
5113
|
+
strapi2.log.info("║ [SUCCESS] MAGIC MAIL PLUGIN LICENSE ACTIVE ║");
|
|
5094
5114
|
strapi2.log.info("║ ║");
|
|
5095
5115
|
strapi2.log.info(`║ License: ${licenseKey.padEnd(38, " ")}║`);
|
|
5096
5116
|
strapi2.log.info(`║ User: ${(license2?.firstName + " " + license2?.lastName).padEnd(41, " ")}║`);
|
|
5097
5117
|
strapi2.log.info(`║ Email: ${(license2?.email || "N/A").padEnd(40, " ")}║`);
|
|
5098
5118
|
strapi2.log.info("║ ║");
|
|
5099
|
-
strapi2.log.info("║
|
|
5119
|
+
strapi2.log.info("║ [AUTO] Pinging every 15 minutes ║");
|
|
5100
5120
|
strapi2.log.info("╚════════════════════════════════════════════════════════════════╝");
|
|
5101
5121
|
return {
|
|
5102
5122
|
valid: true,
|
|
@@ -5105,9 +5125,9 @@ function requireLicenseGuard() {
|
|
|
5105
5125
|
gracePeriod: verification.gracePeriod || false
|
|
5106
5126
|
};
|
|
5107
5127
|
} else {
|
|
5108
|
-
strapi2.log.error(
|
|
5128
|
+
strapi2.log.error(`[ERROR] License validation failed (Key: ${licenseKey.substring(0, 10)}...)`);
|
|
5109
5129
|
strapi2.log.info("──────────────────────────────────────────────────────────");
|
|
5110
|
-
strapi2.log.info("
|
|
5130
|
+
strapi2.log.info("[WARNING] Running in demo mode with limited features");
|
|
5111
5131
|
return {
|
|
5112
5132
|
valid: false,
|
|
5113
5133
|
demo: true,
|
|
@@ -5116,7 +5136,7 @@ function requireLicenseGuard() {
|
|
|
5116
5136
|
};
|
|
5117
5137
|
}
|
|
5118
5138
|
} catch (error) {
|
|
5119
|
-
strapi2.log.error("
|
|
5139
|
+
strapi2.log.error("[ERROR] Error initializing License Guard:", error);
|
|
5120
5140
|
return {
|
|
5121
5141
|
valid: false,
|
|
5122
5142
|
demo: true,
|
|
@@ -5136,6 +5156,8 @@ function requireEmailDesigner() {
|
|
|
5136
5156
|
const Mustache = require$$0$3;
|
|
5137
5157
|
const htmlToTextLib = require$$1$2;
|
|
5138
5158
|
const decode = require$$2$2;
|
|
5159
|
+
const EMAIL_TEMPLATE_UID = "plugin::magic-mail.email-template";
|
|
5160
|
+
const EMAIL_TEMPLATE_VERSION_UID = "plugin::magic-mail.email-template-version";
|
|
5139
5161
|
const convertHtmlToText = (html, options2 = { wordwrap: 130 }) => {
|
|
5140
5162
|
try {
|
|
5141
5163
|
if (!html || typeof html !== "string") {
|
|
@@ -5173,24 +5195,36 @@ function requireEmailDesigner() {
|
|
|
5173
5195
|
* Get all templates
|
|
5174
5196
|
*/
|
|
5175
5197
|
async findAll(filters = {}) {
|
|
5176
|
-
return strapi2.
|
|
5198
|
+
return strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5177
5199
|
filters,
|
|
5178
|
-
sort: { createdAt: "desc" }
|
|
5200
|
+
sort: [{ createdAt: "desc" }]
|
|
5179
5201
|
});
|
|
5180
5202
|
},
|
|
5181
5203
|
/**
|
|
5182
|
-
* Get template by ID with populated versions
|
|
5204
|
+
* Get template by ID (documentId) with populated versions
|
|
5183
5205
|
*/
|
|
5184
|
-
async findOne(
|
|
5185
|
-
return strapi2.
|
|
5206
|
+
async findOne(documentId) {
|
|
5207
|
+
return strapi2.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
5208
|
+
documentId,
|
|
5186
5209
|
populate: ["versions"]
|
|
5187
5210
|
});
|
|
5188
5211
|
},
|
|
5212
|
+
/**
|
|
5213
|
+
* Get template by numeric ID (for backward compatibility)
|
|
5214
|
+
*/
|
|
5215
|
+
async findById(id) {
|
|
5216
|
+
const results = await strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5217
|
+
filters: { id },
|
|
5218
|
+
limit: 1,
|
|
5219
|
+
populate: ["versions"]
|
|
5220
|
+
});
|
|
5221
|
+
return results.length > 0 ? results[0] : null;
|
|
5222
|
+
},
|
|
5189
5223
|
/**
|
|
5190
5224
|
* Get template by reference ID
|
|
5191
5225
|
*/
|
|
5192
5226
|
async findByReferenceId(templateReferenceId) {
|
|
5193
|
-
const results = await strapi2.
|
|
5227
|
+
const results = await strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5194
5228
|
filters: { templateReferenceId },
|
|
5195
5229
|
limit: 1
|
|
5196
5230
|
});
|
|
@@ -5198,17 +5232,11 @@ function requireEmailDesigner() {
|
|
|
5198
5232
|
},
|
|
5199
5233
|
/**
|
|
5200
5234
|
* Create new template with automatic initial version
|
|
5201
|
-
*
|
|
5202
|
-
* Steps:
|
|
5203
|
-
* 1. Check license limits
|
|
5204
|
-
* 2. Validate reference ID is unique
|
|
5205
|
-
* 3. Create template
|
|
5206
|
-
* 4. Create initial version (if versioning enabled)
|
|
5207
5235
|
*/
|
|
5208
5236
|
async create(data) {
|
|
5209
|
-
strapi2.log.info("[magic-mail]
|
|
5237
|
+
strapi2.log.info("[magic-mail] [TEST] Creating new template...");
|
|
5210
5238
|
const maxTemplates = await strapi2.plugin("magic-mail").service("license-guard").getMaxEmailTemplates();
|
|
5211
|
-
const currentCount = await strapi2.
|
|
5239
|
+
const currentCount = await strapi2.documents(EMAIL_TEMPLATE_UID).count();
|
|
5212
5240
|
if (maxTemplates !== -1 && currentCount >= maxTemplates) {
|
|
5213
5241
|
throw new Error(
|
|
5214
5242
|
`Template limit reached (${maxTemplates}). Upgrade your license to create more templates.`
|
|
@@ -5220,17 +5248,17 @@ function requireEmailDesigner() {
|
|
|
5220
5248
|
throw new Error(`Template with reference ID ${data.templateReferenceId} already exists`);
|
|
5221
5249
|
}
|
|
5222
5250
|
}
|
|
5223
|
-
const template = await strapi2.
|
|
5251
|
+
const template = await strapi2.documents(EMAIL_TEMPLATE_UID).create({
|
|
5224
5252
|
data: {
|
|
5225
5253
|
...data,
|
|
5226
5254
|
isActive: data.isActive !== void 0 ? data.isActive : true
|
|
5227
5255
|
}
|
|
5228
5256
|
});
|
|
5229
|
-
strapi2.log.info(`[magic-mail]
|
|
5257
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template created: documentId=${template.documentId}, name="${template.name}"`);
|
|
5230
5258
|
const hasVersioning = await strapi2.plugin("magic-mail").service("license-guard").hasFeature("email-designer-versioning");
|
|
5231
5259
|
if (hasVersioning) {
|
|
5232
|
-
strapi2.log.info("[magic-mail]
|
|
5233
|
-
await this.createVersion(template.
|
|
5260
|
+
strapi2.log.info("[magic-mail] [SAVE] Creating initial version...");
|
|
5261
|
+
await this.createVersion(template.documentId, {
|
|
5234
5262
|
name: data.name,
|
|
5235
5263
|
subject: data.subject,
|
|
5236
5264
|
design: data.design,
|
|
@@ -5238,33 +5266,26 @@ function requireEmailDesigner() {
|
|
|
5238
5266
|
bodyText: data.bodyText,
|
|
5239
5267
|
tags: data.tags
|
|
5240
5268
|
});
|
|
5241
|
-
strapi2.log.info("[magic-mail]
|
|
5269
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Initial version created");
|
|
5242
5270
|
} else {
|
|
5243
|
-
strapi2.log.info("[magic-mail]
|
|
5271
|
+
strapi2.log.info("[magic-mail] [SKIP] Versioning not enabled, skipping initial version");
|
|
5244
5272
|
}
|
|
5245
5273
|
return template;
|
|
5246
5274
|
},
|
|
5247
5275
|
/**
|
|
5248
5276
|
* Update template with automatic version snapshot
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
* 3. Update template with new data
|
|
5254
|
-
*
|
|
5255
|
-
* IMPORTANT: Version is created BEFORE update to preserve old state!
|
|
5256
|
-
*/
|
|
5257
|
-
async update(id, data) {
|
|
5258
|
-
strapi2.log.info(`[magic-mail] 🔄 Updating template ID: ${id}`);
|
|
5259
|
-
const template = await this.findOne(id);
|
|
5277
|
+
*/
|
|
5278
|
+
async update(documentId, data) {
|
|
5279
|
+
strapi2.log.info(`[magic-mail] [UPDATE] Updating template documentId: ${documentId}`);
|
|
5280
|
+
const template = await this.findOne(documentId);
|
|
5260
5281
|
if (!template) {
|
|
5261
5282
|
throw new Error("Template not found");
|
|
5262
5283
|
}
|
|
5263
|
-
strapi2.log.info(`[magic-mail]
|
|
5284
|
+
strapi2.log.info(`[magic-mail] [INFO] Found template: documentId=${template.documentId}, name="${template.name}"`);
|
|
5264
5285
|
const hasVersioning = await strapi2.plugin("magic-mail").service("license-guard").hasFeature("email-designer-versioning");
|
|
5265
5286
|
if (hasVersioning) {
|
|
5266
|
-
strapi2.log.info("[magic-mail]
|
|
5267
|
-
await this.createVersion(template.
|
|
5287
|
+
strapi2.log.info("[magic-mail] [SAVE] Creating version snapshot before update...");
|
|
5288
|
+
await this.createVersion(template.documentId, {
|
|
5268
5289
|
name: template.name,
|
|
5269
5290
|
subject: template.subject,
|
|
5270
5291
|
design: template.design,
|
|
@@ -5272,66 +5293,62 @@ function requireEmailDesigner() {
|
|
|
5272
5293
|
bodyText: template.bodyText,
|
|
5273
5294
|
tags: template.tags
|
|
5274
5295
|
});
|
|
5275
|
-
strapi2.log.info("[magic-mail]
|
|
5276
|
-
} else {
|
|
5277
|
-
strapi2.log.info("[magic-mail] ⏭️ Versioning not enabled, skipping version snapshot");
|
|
5296
|
+
strapi2.log.info("[magic-mail] [SUCCESS] Version snapshot created");
|
|
5278
5297
|
}
|
|
5279
5298
|
const updateData = { ...data };
|
|
5280
5299
|
if ("versions" in updateData) {
|
|
5281
5300
|
delete updateData.versions;
|
|
5282
|
-
strapi2.log.warn("[magic-mail]
|
|
5301
|
+
strapi2.log.warn("[magic-mail] [WARNING] Removed versions field from update data");
|
|
5283
5302
|
}
|
|
5284
|
-
const updated = await strapi2.
|
|
5303
|
+
const updated = await strapi2.documents(EMAIL_TEMPLATE_UID).update({
|
|
5304
|
+
documentId,
|
|
5285
5305
|
data: updateData
|
|
5286
5306
|
});
|
|
5287
|
-
strapi2.log.info(`[magic-mail]
|
|
5307
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template updated: documentId=${updated.documentId}`);
|
|
5288
5308
|
return updated;
|
|
5289
5309
|
},
|
|
5290
5310
|
/**
|
|
5291
5311
|
* Delete template and all its versions
|
|
5292
|
-
*
|
|
5293
|
-
* Uses Document Service for version deletion to ensure cascading delete
|
|
5294
5312
|
*/
|
|
5295
|
-
async delete(
|
|
5296
|
-
strapi2.log.info(`[magic-mail]
|
|
5297
|
-
const template = await this.findOne(
|
|
5313
|
+
async delete(documentId) {
|
|
5314
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleting template documentId: ${documentId}`);
|
|
5315
|
+
const template = await this.findOne(documentId);
|
|
5298
5316
|
if (!template) {
|
|
5299
5317
|
throw new Error("Template not found");
|
|
5300
5318
|
}
|
|
5301
|
-
strapi2.log.info(`[magic-mail]
|
|
5302
|
-
const allVersions = await strapi2.documents(
|
|
5319
|
+
strapi2.log.info(`[magic-mail] [DELETE] Template: documentId=${template.documentId}, name="${template.name}"`);
|
|
5320
|
+
const allVersions = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findMany({
|
|
5303
5321
|
filters: {
|
|
5304
5322
|
template: {
|
|
5305
5323
|
documentId: template.documentId
|
|
5306
5324
|
}
|
|
5307
5325
|
}
|
|
5308
5326
|
});
|
|
5309
|
-
strapi2.log.info(`[magic-mail]
|
|
5327
|
+
strapi2.log.info(`[magic-mail] [DELETE] Found ${allVersions.length} versions to delete`);
|
|
5310
5328
|
for (const version2 of allVersions) {
|
|
5311
5329
|
try {
|
|
5312
|
-
await strapi2.documents(
|
|
5313
|
-
|
|
5330
|
+
await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).delete({
|
|
5331
|
+
documentId: version2.documentId
|
|
5332
|
+
});
|
|
5333
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleted version #${version2.versionNumber}`);
|
|
5314
5334
|
} catch (versionError) {
|
|
5315
|
-
strapi2.log.warn(`[magic-mail]
|
|
5335
|
+
strapi2.log.warn(`[magic-mail] [WARNING] Failed to delete version: ${versionError.message}`);
|
|
5316
5336
|
}
|
|
5317
5337
|
}
|
|
5318
|
-
const result = await strapi2.
|
|
5319
|
-
strapi2.log.info(`[magic-mail]
|
|
5338
|
+
const result = await strapi2.documents(EMAIL_TEMPLATE_UID).delete({ documentId });
|
|
5339
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template "${template.name}" and ${allVersions.length} versions deleted`);
|
|
5320
5340
|
return result;
|
|
5321
5341
|
},
|
|
5322
5342
|
/**
|
|
5323
5343
|
* Duplicate template
|
|
5324
|
-
*
|
|
5325
|
-
* Creates a copy of an existing template with " copy" suffix
|
|
5326
|
-
* Does NOT copy versions, starts fresh
|
|
5327
5344
|
*/
|
|
5328
|
-
async duplicate(
|
|
5329
|
-
strapi2.log.info(`[magic-mail]
|
|
5330
|
-
const original = await this.findOne(
|
|
5345
|
+
async duplicate(documentId) {
|
|
5346
|
+
strapi2.log.info(`[magic-mail] [INFO] Duplicating template documentId: ${documentId}`);
|
|
5347
|
+
const original = await this.findOne(documentId);
|
|
5331
5348
|
if (!original) {
|
|
5332
5349
|
throw new Error("Template not found");
|
|
5333
5350
|
}
|
|
5334
|
-
strapi2.log.info(`[magic-mail]
|
|
5351
|
+
strapi2.log.info(`[magic-mail] [PACKAGE] Original template: documentId=${original.documentId}, name="${original.name}"`);
|
|
5335
5352
|
const duplicateData = {
|
|
5336
5353
|
name: `${original.name} copy`,
|
|
5337
5354
|
subject: original.subject,
|
|
@@ -5341,11 +5358,10 @@ function requireEmailDesigner() {
|
|
|
5341
5358
|
category: original.category,
|
|
5342
5359
|
tags: original.tags,
|
|
5343
5360
|
isActive: original.isActive,
|
|
5344
|
-
// Generate new templateReferenceId (unique ID)
|
|
5345
5361
|
templateReferenceId: Date.now() + Math.floor(Math.random() * 1e3)
|
|
5346
5362
|
};
|
|
5347
5363
|
const duplicated = await this.create(duplicateData);
|
|
5348
|
-
strapi2.log.info(`[magic-mail]
|
|
5364
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template duplicated: documentId=${duplicated.documentId}`);
|
|
5349
5365
|
return duplicated;
|
|
5350
5366
|
},
|
|
5351
5367
|
// ============================================================
|
|
@@ -5353,134 +5369,81 @@ function requireEmailDesigner() {
|
|
|
5353
5369
|
// ============================================================
|
|
5354
5370
|
/**
|
|
5355
5371
|
* Create a new version for a template
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
* 3. Create version WITH template relation
|
|
5363
|
-
*
|
|
5364
|
-
* @param {number} templateId - Numeric ID of template
|
|
5365
|
-
* @param {object} data - Version data (name, subject, bodyHtml, etc)
|
|
5366
|
-
* @returns {object} Created version
|
|
5367
|
-
*/
|
|
5368
|
-
async createVersion(templateId, data) {
|
|
5369
|
-
strapi2.log.info(`[magic-mail] 📸 Creating version for template ID: ${templateId}`);
|
|
5370
|
-
const template = await strapi2.entityService.findOne("plugin::magic-mail.email-template", templateId);
|
|
5372
|
+
*/
|
|
5373
|
+
async createVersion(templateDocumentId, data) {
|
|
5374
|
+
strapi2.log.info(`[magic-mail] [SNAPSHOT] Creating version for template documentId: ${templateDocumentId}`);
|
|
5375
|
+
const template = await strapi2.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
5376
|
+
documentId: templateDocumentId
|
|
5377
|
+
});
|
|
5371
5378
|
if (!template) {
|
|
5372
|
-
throw new Error(`Template ${
|
|
5379
|
+
throw new Error(`Template ${templateDocumentId} not found`);
|
|
5373
5380
|
}
|
|
5374
|
-
strapi2.log.info(`[magic-mail]
|
|
5375
|
-
const existingVersions = await strapi2.
|
|
5381
|
+
strapi2.log.info(`[magic-mail] [PACKAGE] Template found: documentId=${template.documentId}, name="${template.name}"`);
|
|
5382
|
+
const existingVersions = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findMany({
|
|
5376
5383
|
filters: {
|
|
5377
5384
|
template: {
|
|
5378
|
-
|
|
5379
|
-
// Use numeric ID in filter
|
|
5385
|
+
documentId: templateDocumentId
|
|
5380
5386
|
}
|
|
5381
5387
|
},
|
|
5382
|
-
sort: { versionNumber: "desc" }
|
|
5388
|
+
sort: [{ versionNumber: "desc" }]
|
|
5383
5389
|
});
|
|
5384
5390
|
const versionNumber = existingVersions.length > 0 ? Math.max(...existingVersions.map((v) => v.versionNumber || 0)) + 1 : 1;
|
|
5385
|
-
strapi2.log.info(`[magic-mail]
|
|
5386
|
-
const createdVersion = await strapi2.
|
|
5391
|
+
strapi2.log.info(`[magic-mail] [STATS] Existing versions: ${existingVersions.length} → Next version: #${versionNumber}`);
|
|
5392
|
+
const createdVersion = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).create({
|
|
5387
5393
|
data: {
|
|
5388
5394
|
versionNumber,
|
|
5389
5395
|
...data,
|
|
5390
|
-
template:
|
|
5391
|
-
|
|
5392
|
-
// ✅ Use connect array for Strapi v5!
|
|
5393
|
-
}
|
|
5396
|
+
template: templateDocumentId
|
|
5397
|
+
// Document Service handles relations with documentId
|
|
5394
5398
|
}
|
|
5395
5399
|
});
|
|
5396
|
-
strapi2.log.info(`[magic-mail]
|
|
5397
|
-
|
|
5398
|
-
"plugin::magic-mail.email-template-version",
|
|
5399
|
-
createdVersion.id,
|
|
5400
|
-
{
|
|
5401
|
-
populate: ["template"]
|
|
5402
|
-
}
|
|
5403
|
-
);
|
|
5404
|
-
strapi2.log.info(
|
|
5405
|
-
`[magic-mail] ✅ Version created successfully:
|
|
5406
|
-
- Version ID: ${createdVersion.id}
|
|
5407
|
-
- Version #: ${versionNumber}
|
|
5408
|
-
- Template ID: ${templateId}
|
|
5409
|
-
- Has template relation: ${!!verifiedVersion.template}
|
|
5410
|
-
- Template in DB: ${verifiedVersion.template?.id || "NULL"}`
|
|
5411
|
-
);
|
|
5412
|
-
if (!verifiedVersion.template) {
|
|
5413
|
-
strapi2.log.error(
|
|
5414
|
-
`[magic-mail] ❌ CRITICAL: Version ${createdVersion.id} was created but template relation was NOT set!
|
|
5415
|
-
This is a Strapi v5 relation bug. Investigating...`
|
|
5416
|
-
);
|
|
5417
|
-
}
|
|
5418
|
-
return verifiedVersion;
|
|
5400
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Version created: documentId=${createdVersion.documentId}, v${versionNumber}`);
|
|
5401
|
+
return createdVersion;
|
|
5419
5402
|
},
|
|
5420
5403
|
/**
|
|
5421
5404
|
* Get all versions for a template
|
|
5422
5405
|
*/
|
|
5423
|
-
async getVersions(
|
|
5424
|
-
strapi2.log.info(`[magic-mail] 📜 Fetching versions for template
|
|
5425
|
-
const template = await strapi2.
|
|
5406
|
+
async getVersions(templateDocumentId) {
|
|
5407
|
+
strapi2.log.info(`[magic-mail] 📜 Fetching versions for template documentId: ${templateDocumentId}`);
|
|
5408
|
+
const template = await strapi2.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
5409
|
+
documentId: templateDocumentId,
|
|
5426
5410
|
populate: ["versions"]
|
|
5427
5411
|
});
|
|
5428
5412
|
if (!template) {
|
|
5429
5413
|
throw new Error("Template not found");
|
|
5430
5414
|
}
|
|
5431
|
-
strapi2.log.info(`[magic-mail]
|
|
5415
|
+
strapi2.log.info(`[magic-mail] [PACKAGE] Template has ${template.versions?.length || 0} versions`);
|
|
5432
5416
|
if (template.versions && template.versions.length > 0) {
|
|
5433
5417
|
const sortedVersions = [...template.versions].sort((a, b) => b.versionNumber - a.versionNumber);
|
|
5434
|
-
strapi2.log.info(`[magic-mail] ✅ Returning ${sortedVersions.length} versions from template populate`);
|
|
5435
5418
|
return sortedVersions;
|
|
5436
5419
|
}
|
|
5437
|
-
|
|
5438
|
-
const versions = await strapi2.entityService.findMany("plugin::magic-mail.email-template-version", {
|
|
5420
|
+
const versions = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findMany({
|
|
5439
5421
|
filters: {
|
|
5440
5422
|
template: {
|
|
5441
|
-
|
|
5423
|
+
documentId: templateDocumentId
|
|
5442
5424
|
}
|
|
5443
5425
|
},
|
|
5444
|
-
sort: { versionNumber: "desc" }
|
|
5445
|
-
populate: ["template"]
|
|
5426
|
+
sort: [{ versionNumber: "desc" }]
|
|
5446
5427
|
});
|
|
5447
|
-
strapi2.log.info(`[magic-mail]
|
|
5428
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Found ${versions.length} versions`);
|
|
5448
5429
|
return versions;
|
|
5449
5430
|
},
|
|
5450
5431
|
/**
|
|
5451
5432
|
* Restore template from a specific version
|
|
5452
|
-
*
|
|
5453
|
-
* Updates the template with data from the version
|
|
5454
|
-
* This will create a NEW version snapshot (via update logic)
|
|
5455
5433
|
*/
|
|
5456
|
-
async restoreVersion(
|
|
5457
|
-
strapi2.log.info(`[magic-mail]
|
|
5458
|
-
const version2 = await strapi2.
|
|
5434
|
+
async restoreVersion(templateDocumentId, versionDocumentId) {
|
|
5435
|
+
strapi2.log.info(`[magic-mail] [RESTORE] Restoring template ${templateDocumentId} from version ${versionDocumentId}`);
|
|
5436
|
+
const version2 = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
5437
|
+
documentId: versionDocumentId,
|
|
5459
5438
|
populate: ["template"]
|
|
5460
5439
|
});
|
|
5461
5440
|
if (!version2) {
|
|
5462
5441
|
throw new Error("Version not found");
|
|
5463
5442
|
}
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
if (version2.template.id !== parseInt(templateId)) {
|
|
5467
|
-
strapi2.log.error(`[magic-mail] ❌ Version ${versionId} belongs to template ${version2.template.id}, not ${templateId}`);
|
|
5468
|
-
throw new Error("Version does not belong to this template");
|
|
5469
|
-
}
|
|
5470
|
-
strapi2.log.info(`[magic-mail] ✅ Version has correct template relation`);
|
|
5471
|
-
} else {
|
|
5472
|
-
strapi2.log.warn(`[magic-mail] ⚠️ Version ${versionId} has no template relation, checking template's versions array...`);
|
|
5473
|
-
const template = await strapi2.entityService.findOne("plugin::magic-mail.email-template", templateId, {
|
|
5474
|
-
populate: ["versions"]
|
|
5475
|
-
});
|
|
5476
|
-
const versionExists = template.versions && template.versions.some((v) => v.id === parseInt(versionId));
|
|
5477
|
-
if (!versionExists) {
|
|
5478
|
-
strapi2.log.error(`[magic-mail] ❌ Version ${versionId} not found in template ${templateId} versions array`);
|
|
5479
|
-
throw new Error("Version does not belong to this template");
|
|
5480
|
-
}
|
|
5481
|
-
strapi2.log.info(`[magic-mail] ✅ Version ${versionId} found in template's versions array (old version without relation)`);
|
|
5443
|
+
if (version2.template?.documentId !== templateDocumentId) {
|
|
5444
|
+
throw new Error("Version does not belong to this template");
|
|
5482
5445
|
}
|
|
5483
|
-
const restored = await this.update(
|
|
5446
|
+
const restored = await this.update(templateDocumentId, {
|
|
5484
5447
|
name: version2.name,
|
|
5485
5448
|
subject: version2.subject,
|
|
5486
5449
|
design: version2.design,
|
|
@@ -5488,93 +5451,65 @@ function requireEmailDesigner() {
|
|
|
5488
5451
|
bodyText: version2.bodyText,
|
|
5489
5452
|
tags: version2.tags
|
|
5490
5453
|
});
|
|
5491
|
-
strapi2.log.info(`[magic-mail]
|
|
5454
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Template restored from version #${version2.versionNumber}`);
|
|
5492
5455
|
return restored;
|
|
5493
5456
|
},
|
|
5494
5457
|
/**
|
|
5495
5458
|
* Delete a single version
|
|
5496
|
-
*
|
|
5497
|
-
* @param {number} templateId - Template ID (for verification)
|
|
5498
|
-
* @param {number} versionId - Version ID to delete
|
|
5499
5459
|
*/
|
|
5500
|
-
async deleteVersion(
|
|
5501
|
-
strapi2.log.info(`[magic-mail]
|
|
5502
|
-
const version2 = await strapi2.
|
|
5460
|
+
async deleteVersion(templateDocumentId, versionDocumentId) {
|
|
5461
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleting version ${versionDocumentId}`);
|
|
5462
|
+
const version2 = await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).findOne({
|
|
5463
|
+
documentId: versionDocumentId,
|
|
5503
5464
|
populate: ["template"]
|
|
5504
5465
|
});
|
|
5505
5466
|
if (!version2) {
|
|
5506
5467
|
throw new Error("Version not found");
|
|
5507
5468
|
}
|
|
5508
|
-
if (version2.template?.
|
|
5509
|
-
|
|
5510
|
-
strapi2.log.error(`[magic-mail] ❌ Version ${versionId} belongs to template ${version2.template.id}, not ${templateId}`);
|
|
5511
|
-
throw new Error("Version does not belong to this template");
|
|
5512
|
-
}
|
|
5513
|
-
strapi2.log.info(`[magic-mail] ✅ Version has correct template relation`);
|
|
5514
|
-
} else {
|
|
5515
|
-
strapi2.log.warn(`[magic-mail] ⚠️ Version ${versionId} has no template relation, checking template's versions array...`);
|
|
5516
|
-
const template = await strapi2.entityService.findOne("plugin::magic-mail.email-template", templateId, {
|
|
5517
|
-
populate: ["versions"]
|
|
5518
|
-
});
|
|
5519
|
-
const versionExists = template.versions && template.versions.some((v) => v.id === parseInt(versionId));
|
|
5520
|
-
if (!versionExists) {
|
|
5521
|
-
strapi2.log.error(`[magic-mail] ❌ Version ${versionId} not found in template ${templateId} versions array`);
|
|
5522
|
-
throw new Error("Version does not belong to this template");
|
|
5523
|
-
}
|
|
5524
|
-
strapi2.log.info(`[magic-mail] ✅ Version ${versionId} found in template's versions array`);
|
|
5469
|
+
if (version2.template?.documentId !== templateDocumentId) {
|
|
5470
|
+
throw new Error("Version does not belong to this template");
|
|
5525
5471
|
}
|
|
5526
|
-
await strapi2.
|
|
5527
|
-
|
|
5472
|
+
await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).delete({
|
|
5473
|
+
documentId: versionDocumentId
|
|
5474
|
+
});
|
|
5475
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Version v${version2.versionNumber} deleted`);
|
|
5528
5476
|
return { success: true, message: "Version deleted" };
|
|
5529
5477
|
},
|
|
5530
5478
|
/**
|
|
5531
5479
|
* Delete all versions for a template
|
|
5532
|
-
*
|
|
5533
|
-
* @param {number} templateId - Template ID
|
|
5534
5480
|
*/
|
|
5535
|
-
async deleteAllVersions(
|
|
5536
|
-
strapi2.log.info(`[magic-mail]
|
|
5537
|
-
const template = await strapi2.
|
|
5481
|
+
async deleteAllVersions(templateDocumentId) {
|
|
5482
|
+
strapi2.log.info(`[magic-mail] [DELETE] Deleting all versions for template ${templateDocumentId}`);
|
|
5483
|
+
const template = await strapi2.documents(EMAIL_TEMPLATE_UID).findOne({
|
|
5484
|
+
documentId: templateDocumentId,
|
|
5538
5485
|
populate: ["versions"]
|
|
5539
5486
|
});
|
|
5540
5487
|
if (!template) {
|
|
5541
5488
|
throw new Error("Template not found");
|
|
5542
5489
|
}
|
|
5543
5490
|
const versionCount = template.versions?.length || 0;
|
|
5544
|
-
strapi2.log.info(`[magic-mail] 📊 Found ${versionCount} versions to delete`);
|
|
5545
5491
|
if (versionCount === 0) {
|
|
5546
5492
|
return { success: true, message: "No versions to delete", deletedCount: 0 };
|
|
5547
5493
|
}
|
|
5548
5494
|
let deletedCount = 0;
|
|
5549
|
-
const errors = [];
|
|
5550
5495
|
for (const version2 of template.versions) {
|
|
5551
5496
|
try {
|
|
5552
|
-
await strapi2.
|
|
5497
|
+
await strapi2.documents(EMAIL_TEMPLATE_VERSION_UID).delete({
|
|
5498
|
+
documentId: version2.documentId
|
|
5499
|
+
});
|
|
5553
5500
|
deletedCount++;
|
|
5554
|
-
strapi2.log.info(`[magic-mail] 🗑️ Deleted version #${version2.versionNumber} (ID: ${version2.id})`);
|
|
5555
5501
|
} catch (error) {
|
|
5556
|
-
strapi2.log.error(`[magic-mail]
|
|
5557
|
-
errors.push({ versionId: version2.id, error: error.message });
|
|
5502
|
+
strapi2.log.error(`[magic-mail] [ERROR] Failed to delete version: ${error.message}`);
|
|
5558
5503
|
}
|
|
5559
5504
|
}
|
|
5560
|
-
strapi2.log.info(`[magic-mail]
|
|
5561
|
-
return {
|
|
5562
|
-
success: true,
|
|
5563
|
-
message: `Deleted ${deletedCount} of ${versionCount} versions`,
|
|
5564
|
-
deletedCount,
|
|
5565
|
-
failedCount: versionCount - deletedCount,
|
|
5566
|
-
errors: errors.length > 0 ? errors : void 0
|
|
5567
|
-
};
|
|
5505
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Deleted ${deletedCount}/${versionCount} versions`);
|
|
5506
|
+
return { success: true, deletedCount };
|
|
5568
5507
|
},
|
|
5569
5508
|
// ============================================================
|
|
5570
5509
|
// RENDERING
|
|
5571
5510
|
// ============================================================
|
|
5572
5511
|
/**
|
|
5573
5512
|
* Render template with dynamic data using Mustache
|
|
5574
|
-
*
|
|
5575
|
-
* @param {number} templateReferenceId - Template reference ID
|
|
5576
|
-
* @param {object} data - Dynamic data for Mustache
|
|
5577
|
-
* @returns {object} Rendered HTML, text, and subject
|
|
5578
5513
|
*/
|
|
5579
5514
|
async renderTemplate(templateReferenceId, data = {}) {
|
|
5580
5515
|
const template = await this.findByReferenceId(templateReferenceId);
|
|
@@ -5606,17 +5541,17 @@ function requireEmailDesigner() {
|
|
|
5606
5541
|
};
|
|
5607
5542
|
},
|
|
5608
5543
|
// ============================================================
|
|
5609
|
-
// IMPORT/EXPORT
|
|
5544
|
+
// IMPORT/EXPORT
|
|
5610
5545
|
// ============================================================
|
|
5611
5546
|
/**
|
|
5612
5547
|
* Export templates as JSON
|
|
5613
5548
|
*/
|
|
5614
|
-
async exportTemplates(
|
|
5615
|
-
strapi2.log.info("[magic-mail]
|
|
5549
|
+
async exportTemplates(templateDocumentIds = []) {
|
|
5550
|
+
strapi2.log.info("[magic-mail] [EXPORT] Exporting templates...");
|
|
5616
5551
|
let templates;
|
|
5617
|
-
if (
|
|
5618
|
-
templates = await strapi2.
|
|
5619
|
-
filters: {
|
|
5552
|
+
if (templateDocumentIds.length > 0) {
|
|
5553
|
+
templates = await strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5554
|
+
filters: { documentId: { $in: templateDocumentIds } }
|
|
5620
5555
|
});
|
|
5621
5556
|
} else {
|
|
5622
5557
|
templates = await this.findAll();
|
|
@@ -5631,26 +5566,24 @@ function requireEmailDesigner() {
|
|
|
5631
5566
|
category: template.category,
|
|
5632
5567
|
tags: template.tags
|
|
5633
5568
|
}));
|
|
5634
|
-
strapi2.log.info(`[magic-mail]
|
|
5569
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Exported ${exported.length} templates`);
|
|
5635
5570
|
return exported;
|
|
5636
5571
|
},
|
|
5637
5572
|
/**
|
|
5638
5573
|
* Import templates from JSON
|
|
5639
5574
|
*/
|
|
5640
5575
|
async importTemplates(templates) {
|
|
5641
|
-
strapi2.log.info(`[magic-mail]
|
|
5576
|
+
strapi2.log.info(`[magic-mail] [IMPORT] Importing ${templates.length} templates...`);
|
|
5642
5577
|
const results = [];
|
|
5643
5578
|
for (const templateData of templates) {
|
|
5644
5579
|
try {
|
|
5645
5580
|
const existing = await this.findByReferenceId(templateData.templateReferenceId);
|
|
5646
5581
|
if (existing) {
|
|
5647
|
-
const updated = await this.update(existing.
|
|
5582
|
+
const updated = await this.update(existing.documentId, templateData);
|
|
5648
5583
|
results.push({ success: true, action: "updated", template: updated });
|
|
5649
|
-
strapi2.log.info(`[magic-mail] ✅ Updated template: ${templateData.name}`);
|
|
5650
5584
|
} else {
|
|
5651
5585
|
const created = await this.create(templateData);
|
|
5652
5586
|
results.push({ success: true, action: "created", template: created });
|
|
5653
|
-
strapi2.log.info(`[magic-mail] ✅ Created template: ${templateData.name}`);
|
|
5654
5587
|
}
|
|
5655
5588
|
} catch (error) {
|
|
5656
5589
|
results.push({
|
|
@@ -5659,11 +5592,8 @@ function requireEmailDesigner() {
|
|
|
5659
5592
|
error: error.message,
|
|
5660
5593
|
templateName: templateData.name
|
|
5661
5594
|
});
|
|
5662
|
-
strapi2.log.error(`[magic-mail] ❌ Failed to import ${templateData.name}: ${error.message}`);
|
|
5663
5595
|
}
|
|
5664
5596
|
}
|
|
5665
|
-
const successful = results.filter((r) => r.success).length;
|
|
5666
|
-
strapi2.log.info(`[magic-mail] ✅ Import completed: ${successful}/${templates.length} templates`);
|
|
5667
5597
|
return results;
|
|
5668
5598
|
},
|
|
5669
5599
|
// ============================================================
|
|
@@ -5673,7 +5603,7 @@ function requireEmailDesigner() {
|
|
|
5673
5603
|
* Get template statistics
|
|
5674
5604
|
*/
|
|
5675
5605
|
async getStats() {
|
|
5676
|
-
const allTemplates = await strapi2.
|
|
5606
|
+
const allTemplates = await strapi2.documents(EMAIL_TEMPLATE_UID).findMany({
|
|
5677
5607
|
fields: ["isActive", "category"]
|
|
5678
5608
|
});
|
|
5679
5609
|
const total = allTemplates.length;
|
|
@@ -5698,9 +5628,7 @@ function requireEmailDesigner() {
|
|
|
5698
5628
|
// STRAPI CORE EMAIL TEMPLATES
|
|
5699
5629
|
// ============================================================
|
|
5700
5630
|
/**
|
|
5701
|
-
* Get Strapi core email template
|
|
5702
|
-
*
|
|
5703
|
-
* These are stored in users-permissions plugin store
|
|
5631
|
+
* Get Strapi core email template
|
|
5704
5632
|
*/
|
|
5705
5633
|
async getCoreTemplate(coreEmailType) {
|
|
5706
5634
|
if (!["reset-password", "email-confirmation"].includes(coreEmailType)) {
|
|
@@ -5717,8 +5645,8 @@ function requireEmailDesigner() {
|
|
|
5717
5645
|
if (emailConfig && emailConfig[pluginStoreEmailKey]) {
|
|
5718
5646
|
data = emailConfig[pluginStoreEmailKey];
|
|
5719
5647
|
}
|
|
5720
|
-
const messageConverted = data
|
|
5721
|
-
const subjectConverted = data
|
|
5648
|
+
const messageConverted = data?.options?.message ? data.options.message.replace(/<%|<%/g, "{{").replace(/%>|%>/g, "}}") : "";
|
|
5649
|
+
const subjectConverted = data?.options?.object ? data.options.object.replace(/<%|<%/g, "{{").replace(/%>|%>/g, "}}") : "";
|
|
5722
5650
|
return {
|
|
5723
5651
|
from: data?.options?.from || null,
|
|
5724
5652
|
message: messageConverted || "",
|
|
@@ -5746,14 +5674,14 @@ function requireEmailDesigner() {
|
|
|
5746
5674
|
emailsConfig[pluginStoreEmailKey] = {
|
|
5747
5675
|
...emailsConfig[pluginStoreEmailKey],
|
|
5748
5676
|
options: {
|
|
5749
|
-
...emailsConfig[pluginStoreEmailKey]
|
|
5677
|
+
...emailsConfig[pluginStoreEmailKey]?.options || {},
|
|
5750
5678
|
message: data.message.replace(/{{/g, "<%").replace(/}}/g, "%>"),
|
|
5751
5679
|
object: data.subject.replace(/{{/g, "<%").replace(/}}/g, "%>")
|
|
5752
5680
|
},
|
|
5753
5681
|
design: data.design
|
|
5754
5682
|
};
|
|
5755
5683
|
await pluginStore.set({ key: "email", value: emailsConfig });
|
|
5756
|
-
strapi2.log.info(`[magic-mail]
|
|
5684
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Core email template updated: ${pluginStoreEmailKey}`);
|
|
5757
5685
|
return { message: "Saved" };
|
|
5758
5686
|
}
|
|
5759
5687
|
});
|
|
@@ -5765,6 +5693,9 @@ function requireAnalytics() {
|
|
|
5765
5693
|
if (hasRequiredAnalytics) return analytics;
|
|
5766
5694
|
hasRequiredAnalytics = 1;
|
|
5767
5695
|
const crypto = require$$0$1;
|
|
5696
|
+
const EMAIL_LOG_UID = "plugin::magic-mail.email-log";
|
|
5697
|
+
const EMAIL_EVENT_UID = "plugin::magic-mail.email-event";
|
|
5698
|
+
const EMAIL_LINK_UID = "plugin::magic-mail.email-link";
|
|
5768
5699
|
analytics = ({ strapi: strapi2 }) => ({
|
|
5769
5700
|
/**
|
|
5770
5701
|
* Generate unique email ID for tracking
|
|
@@ -5783,7 +5714,7 @@ function requireAnalytics() {
|
|
|
5783
5714
|
*/
|
|
5784
5715
|
async createEmailLog(data) {
|
|
5785
5716
|
const emailId = this.generateEmailId();
|
|
5786
|
-
const logEntry = await strapi2.
|
|
5717
|
+
const logEntry = await strapi2.documents(EMAIL_LOG_UID).create({
|
|
5787
5718
|
data: {
|
|
5788
5719
|
emailId,
|
|
5789
5720
|
user: data.userId || null,
|
|
@@ -5798,9 +5729,9 @@ function requireAnalytics() {
|
|
|
5798
5729
|
metadata: data.metadata || {}
|
|
5799
5730
|
}
|
|
5800
5731
|
});
|
|
5801
|
-
strapi2.log.info(`[magic-mail]
|
|
5732
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Email log created: ${emailId}`);
|
|
5802
5733
|
if (data.templateId) {
|
|
5803
|
-
strapi2.log.info(`[magic-mail]
|
|
5734
|
+
strapi2.log.info(`[magic-mail] [INFO] Template tracked: ${data.templateName || "Unknown"} (ID: ${data.templateId})`);
|
|
5804
5735
|
}
|
|
5805
5736
|
return logEntry;
|
|
5806
5737
|
},
|
|
@@ -5809,8 +5740,8 @@ function requireAnalytics() {
|
|
|
5809
5740
|
*/
|
|
5810
5741
|
async recordOpen(emailId, recipientHash, req) {
|
|
5811
5742
|
try {
|
|
5812
|
-
const emailLog = await strapi2.
|
|
5813
|
-
|
|
5743
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
5744
|
+
filters: { emailId }
|
|
5814
5745
|
});
|
|
5815
5746
|
if (!emailLog) {
|
|
5816
5747
|
strapi2.log.warn(`[magic-mail] Email log not found: ${emailId}`);
|
|
@@ -5822,17 +5753,17 @@ function requireAnalytics() {
|
|
|
5822
5753
|
return null;
|
|
5823
5754
|
}
|
|
5824
5755
|
const now = /* @__PURE__ */ new Date();
|
|
5825
|
-
await strapi2.
|
|
5826
|
-
|
|
5756
|
+
await strapi2.documents(EMAIL_LOG_UID).update({
|
|
5757
|
+
documentId: emailLog.documentId,
|
|
5827
5758
|
data: {
|
|
5828
|
-
openCount: emailLog.openCount + 1,
|
|
5759
|
+
openCount: (emailLog.openCount || 0) + 1,
|
|
5829
5760
|
firstOpenedAt: emailLog.firstOpenedAt || now,
|
|
5830
5761
|
lastOpenedAt: now
|
|
5831
5762
|
}
|
|
5832
5763
|
});
|
|
5833
|
-
const event = await strapi2.
|
|
5764
|
+
const event = await strapi2.documents(EMAIL_EVENT_UID).create({
|
|
5834
5765
|
data: {
|
|
5835
|
-
emailLog: emailLog.
|
|
5766
|
+
emailLog: emailLog.documentId,
|
|
5836
5767
|
type: "open",
|
|
5837
5768
|
timestamp: now,
|
|
5838
5769
|
ipAddress: req.ip || req.headers["x-forwarded-for"] || null,
|
|
@@ -5840,7 +5771,7 @@ function requireAnalytics() {
|
|
|
5840
5771
|
location: this.parseLocation(req)
|
|
5841
5772
|
}
|
|
5842
5773
|
});
|
|
5843
|
-
strapi2.log.info(`[magic-mail]
|
|
5774
|
+
strapi2.log.info(`[magic-mail] [EMAIL] Email opened: ${emailId} (count: ${(emailLog.openCount || 0) + 1})`);
|
|
5844
5775
|
return event;
|
|
5845
5776
|
} catch (error) {
|
|
5846
5777
|
strapi2.log.error("[magic-mail] Error recording open:", error);
|
|
@@ -5852,8 +5783,8 @@ function requireAnalytics() {
|
|
|
5852
5783
|
*/
|
|
5853
5784
|
async recordClick(emailId, linkHash, recipientHash, targetUrl, req) {
|
|
5854
5785
|
try {
|
|
5855
|
-
const emailLog = await strapi2.
|
|
5856
|
-
|
|
5786
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
5787
|
+
filters: { emailId }
|
|
5857
5788
|
});
|
|
5858
5789
|
if (!emailLog) {
|
|
5859
5790
|
return null;
|
|
@@ -5863,15 +5794,15 @@ function requireAnalytics() {
|
|
|
5863
5794
|
return null;
|
|
5864
5795
|
}
|
|
5865
5796
|
const now = /* @__PURE__ */ new Date();
|
|
5866
|
-
await strapi2.
|
|
5867
|
-
|
|
5797
|
+
await strapi2.documents(EMAIL_LOG_UID).update({
|
|
5798
|
+
documentId: emailLog.documentId,
|
|
5868
5799
|
data: {
|
|
5869
|
-
clickCount: emailLog.clickCount + 1
|
|
5800
|
+
clickCount: (emailLog.clickCount || 0) + 1
|
|
5870
5801
|
}
|
|
5871
5802
|
});
|
|
5872
|
-
const event = await strapi2.
|
|
5803
|
+
const event = await strapi2.documents(EMAIL_EVENT_UID).create({
|
|
5873
5804
|
data: {
|
|
5874
|
-
emailLog: emailLog.
|
|
5805
|
+
emailLog: emailLog.documentId,
|
|
5875
5806
|
type: "click",
|
|
5876
5807
|
timestamp: now,
|
|
5877
5808
|
ipAddress: req.ip || req.headers["x-forwarded-for"] || null,
|
|
@@ -5880,7 +5811,7 @@ function requireAnalytics() {
|
|
|
5880
5811
|
linkUrl: targetUrl
|
|
5881
5812
|
}
|
|
5882
5813
|
});
|
|
5883
|
-
strapi2.log.info(`[magic-mail]
|
|
5814
|
+
strapi2.log.info(`[magic-mail] [CLICK] Link clicked: ${emailId} -> ${targetUrl}`);
|
|
5884
5815
|
return event;
|
|
5885
5816
|
} catch (error) {
|
|
5886
5817
|
strapi2.log.error("[magic-mail] Error recording click:", error);
|
|
@@ -5889,34 +5820,37 @@ function requireAnalytics() {
|
|
|
5889
5820
|
},
|
|
5890
5821
|
/**
|
|
5891
5822
|
* Get analytics statistics
|
|
5823
|
+
* Note: Document Service doesn't have count() - using findMany for counting
|
|
5892
5824
|
*/
|
|
5893
5825
|
async getStats(filters = {}) {
|
|
5894
|
-
const
|
|
5826
|
+
const baseFilters = {};
|
|
5895
5827
|
if (filters.userId) {
|
|
5896
|
-
|
|
5828
|
+
baseFilters.user = { documentId: filters.userId };
|
|
5897
5829
|
}
|
|
5898
5830
|
if (filters.templateId) {
|
|
5899
|
-
|
|
5831
|
+
baseFilters.templateId = filters.templateId;
|
|
5900
5832
|
}
|
|
5901
5833
|
if (filters.accountId) {
|
|
5902
|
-
|
|
5834
|
+
baseFilters.accountId = filters.accountId;
|
|
5903
5835
|
}
|
|
5904
5836
|
if (filters.dateFrom) {
|
|
5905
|
-
|
|
5837
|
+
baseFilters.sentAt = { $gte: new Date(filters.dateFrom) };
|
|
5906
5838
|
}
|
|
5907
5839
|
if (filters.dateTo) {
|
|
5908
|
-
|
|
5840
|
+
baseFilters.sentAt = { ...baseFilters.sentAt, $lte: new Date(filters.dateTo) };
|
|
5909
5841
|
}
|
|
5910
5842
|
const [totalSent, totalOpened, totalClicked, totalBounced] = await Promise.all([
|
|
5911
|
-
strapi2.
|
|
5912
|
-
|
|
5913
|
-
where: { ...where, openCount: { $gt: 0 } }
|
|
5843
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5844
|
+
filters: baseFilters
|
|
5914
5845
|
}),
|
|
5915
|
-
strapi2.
|
|
5916
|
-
|
|
5846
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5847
|
+
filters: { ...baseFilters, openCount: { $gt: 0 } }
|
|
5917
5848
|
}),
|
|
5918
|
-
strapi2.
|
|
5919
|
-
|
|
5849
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5850
|
+
filters: { ...baseFilters, clickCount: { $gt: 0 } }
|
|
5851
|
+
}),
|
|
5852
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5853
|
+
filters: { ...baseFilters, bounced: true }
|
|
5920
5854
|
})
|
|
5921
5855
|
]);
|
|
5922
5856
|
const openRate = totalSent > 0 ? totalOpened / totalSent * 100 : 0;
|
|
@@ -5938,7 +5872,7 @@ function requireAnalytics() {
|
|
|
5938
5872
|
async getEmailLogs(filters = {}, pagination = {}) {
|
|
5939
5873
|
const where = {};
|
|
5940
5874
|
if (filters.userId) {
|
|
5941
|
-
where.user = filters.userId;
|
|
5875
|
+
where.user = { documentId: filters.userId };
|
|
5942
5876
|
}
|
|
5943
5877
|
if (filters.templateId) {
|
|
5944
5878
|
where.templateId = filters.templateId;
|
|
@@ -5953,14 +5887,17 @@ function requireAnalytics() {
|
|
|
5953
5887
|
const page = pagination.page || 1;
|
|
5954
5888
|
const pageSize = pagination.pageSize || 25;
|
|
5955
5889
|
const [logs, total] = await Promise.all([
|
|
5956
|
-
strapi2.
|
|
5957
|
-
where,
|
|
5958
|
-
|
|
5890
|
+
strapi2.documents(EMAIL_LOG_UID).findMany({
|
|
5891
|
+
filters: where,
|
|
5892
|
+
sort: [{ sentAt: "desc" }],
|
|
5959
5893
|
limit: pageSize,
|
|
5960
5894
|
offset: (page - 1) * pageSize,
|
|
5961
5895
|
populate: ["user"]
|
|
5962
5896
|
}),
|
|
5963
|
-
|
|
5897
|
+
// Get total count using native count() method
|
|
5898
|
+
strapi2.documents(EMAIL_LOG_UID).count({
|
|
5899
|
+
filters: where
|
|
5900
|
+
})
|
|
5964
5901
|
]);
|
|
5965
5902
|
return {
|
|
5966
5903
|
data: logs,
|
|
@@ -5976,8 +5913,8 @@ function requireAnalytics() {
|
|
|
5976
5913
|
* Get email log details with events
|
|
5977
5914
|
*/
|
|
5978
5915
|
async getEmailLogDetails(emailId) {
|
|
5979
|
-
const emailLog = await strapi2.
|
|
5980
|
-
|
|
5916
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
5917
|
+
filters: { emailId },
|
|
5981
5918
|
populate: ["user", "events"]
|
|
5982
5919
|
});
|
|
5983
5920
|
return emailLog;
|
|
@@ -5986,9 +5923,9 @@ function requireAnalytics() {
|
|
|
5986
5923
|
* Get user email activity
|
|
5987
5924
|
*/
|
|
5988
5925
|
async getUserActivity(userId) {
|
|
5989
|
-
const emailLogs = await strapi2.
|
|
5990
|
-
|
|
5991
|
-
|
|
5926
|
+
const emailLogs = await strapi2.documents(EMAIL_LOG_UID).findMany({
|
|
5927
|
+
filters: { user: { documentId: userId } },
|
|
5928
|
+
sort: [{ sentAt: "desc" }],
|
|
5992
5929
|
limit: 50
|
|
5993
5930
|
});
|
|
5994
5931
|
const stats = await this.getStats({ userId });
|
|
@@ -6026,8 +5963,8 @@ function requireAnalytics() {
|
|
|
6026
5963
|
*/
|
|
6027
5964
|
async rewriteLinksForTracking(html, emailId, recipientHash) {
|
|
6028
5965
|
const baseUrl = strapi2.config.get("server.url") || "http://localhost:1337";
|
|
6029
|
-
const emailLog = await strapi2.
|
|
6030
|
-
|
|
5966
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
5967
|
+
filters: { emailId }
|
|
6031
5968
|
});
|
|
6032
5969
|
if (!emailLog) {
|
|
6033
5970
|
strapi2.log.error(`[magic-mail] Cannot rewrite links: Email log not found for ${emailId}`);
|
|
@@ -6041,13 +5978,13 @@ function requireAnalytics() {
|
|
|
6041
5978
|
while ((match = linkRegex.exec(html)) !== null) {
|
|
6042
5979
|
match[0];
|
|
6043
5980
|
const originalUrl = match[1];
|
|
6044
|
-
strapi2.log.debug(`[magic-mail]
|
|
5981
|
+
strapi2.log.debug(`[magic-mail] [CHECK] Found link: ${originalUrl.substring(0, 100)}${originalUrl.length > 100 ? "..." : ""}`);
|
|
6045
5982
|
if (originalUrl.startsWith("#") || originalUrl.includes("/track/click/")) {
|
|
6046
|
-
strapi2.log.debug(`[magic-mail]
|
|
5983
|
+
strapi2.log.debug(`[magic-mail] [SKIP] Skipping (anchor or already tracked)`);
|
|
6047
5984
|
continue;
|
|
6048
5985
|
}
|
|
6049
5986
|
if (!originalUrl.match(/^https?:\/\//i) && !originalUrl.startsWith("/")) {
|
|
6050
|
-
strapi2.log.debug(`[magic-mail]
|
|
5987
|
+
strapi2.log.debug(`[magic-mail] [SKIP] Skipping relative URL: ${originalUrl}`);
|
|
6051
5988
|
continue;
|
|
6052
5989
|
}
|
|
6053
5990
|
const linkHash = crypto.createHash("md5").update(originalUrl).digest("hex").substring(0, 8);
|
|
@@ -6057,7 +5994,7 @@ function requireAnalytics() {
|
|
|
6057
5994
|
});
|
|
6058
5995
|
const trackingUrl = `${baseUrl}/api/magic-mail/track/click/${emailId}/${linkHash}/${recipientHash}`;
|
|
6059
5996
|
linkCount++;
|
|
6060
|
-
strapi2.log.info(`[magic-mail]
|
|
5997
|
+
strapi2.log.info(`[magic-mail] [LINK] Link ${linkCount}: ${originalUrl} → ${trackingUrl}`);
|
|
6061
5998
|
replacements.push({
|
|
6062
5999
|
from: originalUrl,
|
|
6063
6000
|
to: trackingUrl
|
|
@@ -6065,7 +6002,7 @@ function requireAnalytics() {
|
|
|
6065
6002
|
}
|
|
6066
6003
|
for (const mapping of linkMappings) {
|
|
6067
6004
|
try {
|
|
6068
|
-
await this.storeLinkMapping(emailLog.
|
|
6005
|
+
await this.storeLinkMapping(emailLog.documentId, mapping.linkHash, mapping.originalUrl);
|
|
6069
6006
|
} catch (err) {
|
|
6070
6007
|
strapi2.log.error("[magic-mail] Error storing link mapping:", err);
|
|
6071
6008
|
}
|
|
@@ -6075,20 +6012,20 @@ function requireAnalytics() {
|
|
|
6075
6012
|
result = result.replace(replacement.from, replacement.to);
|
|
6076
6013
|
}
|
|
6077
6014
|
if (linkCount > 0) {
|
|
6078
|
-
strapi2.log.info(`[magic-mail]
|
|
6015
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] Rewrote ${linkCount} links for click tracking`);
|
|
6079
6016
|
} else {
|
|
6080
|
-
strapi2.log.warn(`[magic-mail]
|
|
6017
|
+
strapi2.log.warn(`[magic-mail] [WARNING] No links found in email HTML for tracking!`);
|
|
6081
6018
|
}
|
|
6082
6019
|
return result;
|
|
6083
6020
|
},
|
|
6084
6021
|
/**
|
|
6085
6022
|
* Store link mapping in database
|
|
6086
6023
|
*/
|
|
6087
|
-
async storeLinkMapping(
|
|
6024
|
+
async storeLinkMapping(emailLogDocId, linkHash, originalUrl) {
|
|
6088
6025
|
try {
|
|
6089
|
-
const existing = await strapi2.
|
|
6090
|
-
|
|
6091
|
-
emailLog:
|
|
6026
|
+
const existing = await strapi2.documents(EMAIL_LINK_UID).findFirst({
|
|
6027
|
+
filters: {
|
|
6028
|
+
emailLog: { documentId: emailLogDocId },
|
|
6092
6029
|
linkHash
|
|
6093
6030
|
}
|
|
6094
6031
|
});
|
|
@@ -6096,15 +6033,15 @@ function requireAnalytics() {
|
|
|
6096
6033
|
strapi2.log.debug(`[magic-mail] Link mapping already exists for ${linkHash}`);
|
|
6097
6034
|
return existing;
|
|
6098
6035
|
}
|
|
6099
|
-
const linkMapping = await strapi2.
|
|
6036
|
+
const linkMapping = await strapi2.documents(EMAIL_LINK_UID).create({
|
|
6100
6037
|
data: {
|
|
6101
|
-
emailLog:
|
|
6038
|
+
emailLog: emailLogDocId,
|
|
6102
6039
|
linkHash,
|
|
6103
6040
|
originalUrl,
|
|
6104
6041
|
clickCount: 0
|
|
6105
6042
|
}
|
|
6106
6043
|
});
|
|
6107
|
-
strapi2.log.debug(`[magic-mail]
|
|
6044
|
+
strapi2.log.debug(`[magic-mail] [SAVE] Stored link mapping: ${linkHash} → ${originalUrl}`);
|
|
6108
6045
|
return linkMapping;
|
|
6109
6046
|
} catch (error) {
|
|
6110
6047
|
strapi2.log.error("[magic-mail] Error storing link mapping:", error);
|
|
@@ -6116,16 +6053,16 @@ function requireAnalytics() {
|
|
|
6116
6053
|
*/
|
|
6117
6054
|
async getOriginalUrlFromHash(emailId, linkHash) {
|
|
6118
6055
|
try {
|
|
6119
|
-
const emailLog = await strapi2.
|
|
6120
|
-
|
|
6056
|
+
const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
|
|
6057
|
+
filters: { emailId }
|
|
6121
6058
|
});
|
|
6122
6059
|
if (!emailLog) {
|
|
6123
6060
|
strapi2.log.warn(`[magic-mail] Email log not found: ${emailId}`);
|
|
6124
6061
|
return null;
|
|
6125
6062
|
}
|
|
6126
|
-
const linkMapping = await strapi2.
|
|
6127
|
-
|
|
6128
|
-
emailLog: emailLog.
|
|
6063
|
+
const linkMapping = await strapi2.documents(EMAIL_LINK_UID).findFirst({
|
|
6064
|
+
filters: {
|
|
6065
|
+
emailLog: { documentId: emailLog.documentId },
|
|
6129
6066
|
linkHash
|
|
6130
6067
|
}
|
|
6131
6068
|
});
|
|
@@ -6134,10 +6071,10 @@ function requireAnalytics() {
|
|
|
6134
6071
|
return null;
|
|
6135
6072
|
}
|
|
6136
6073
|
const now = /* @__PURE__ */ new Date();
|
|
6137
|
-
await strapi2.
|
|
6138
|
-
|
|
6074
|
+
await strapi2.documents(EMAIL_LINK_UID).update({
|
|
6075
|
+
documentId: linkMapping.documentId,
|
|
6139
6076
|
data: {
|
|
6140
|
-
clickCount: linkMapping.clickCount + 1,
|
|
6077
|
+
clickCount: (linkMapping.clickCount || 0) + 1,
|
|
6141
6078
|
firstClickedAt: linkMapping.firstClickedAt || now,
|
|
6142
6079
|
lastClickedAt: now
|
|
6143
6080
|
}
|