strapi-plugin-magic-mail 2.3.0 → 2.3.2

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.
@@ -606,7 +606,7 @@ function requireAccounts() {
606
606
  if (!testEmail) {
607
607
  ctx.throw(400, "testEmail is required");
608
608
  }
609
- strapi.log.info("[magic-mail] 🧪 Testing Strapi Email Service integration...");
609
+ strapi.log.info("[magic-mail] [TEST] Testing Strapi Email Service integration...");
610
610
  strapi.log.info('[magic-mail] [EMAIL] Calling strapi.plugin("email").service("email").send()');
611
611
  if (accountName) {
612
612
  strapi.log.info(`[magic-mail] [FORCE] Forcing specific account: ${accountName}`);
@@ -2322,7 +2322,7 @@ function requireTest() {
2322
2322
  async testRelations(ctx) {
2323
2323
  try {
2324
2324
  console.log("\n" + "=".repeat(60));
2325
- console.log("🧪 TEST: Template Version Relations (Document Service API)");
2325
+ console.log("[TEST] Template - Version Relations (Document Service API)");
2326
2326
  console.log("=".repeat(60));
2327
2327
  let test1Success = false;
2328
2328
  let test1ReverseSuccess = false;
@@ -2490,7 +2490,7 @@ function requireTest() {
2490
2490
  } else {
2491
2491
  console.log(` [ERROR] FEHLER: Falsche Anzahl Versionen!`);
2492
2492
  }
2493
- console.log("\n🧹 Cleanup Test 3...");
2493
+ console.log("\n[CLEANUP] Cleanup Test 3...");
2494
2494
  if (afterSecondUpdate.versions) {
2495
2495
  for (const version3 of afterSecondUpdate.versions) {
2496
2496
  await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version3.documentId });
@@ -2508,7 +2508,7 @@ function requireTest() {
2508
2508
  console.log(`
2509
2509
  [INFO] Template: "${finalTemplate.name}" (documentId: ${finalTemplate.documentId})`);
2510
2510
  console.log(` Anzahl Versionen: ${finalTemplate.versions?.length || 0}`);
2511
- console.log("\n🧹 Aufräumen...");
2511
+ console.log("\n[CLEANUP] Aufraumen...");
2512
2512
  await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version1.documentId });
2513
2513
  await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version2.documentId });
2514
2514
  await strapi.documents(EMAIL_TEMPLATE_UID).delete({ documentId: testTemplate.documentId });
@@ -3491,7 +3491,7 @@ function requireEncryption() {
3491
3491
  let encrypted = cipher.update(jsonData, "utf8", "hex");
3492
3492
  encrypted += cipher.final("hex");
3493
3493
  const authTag = cipher.getAuthTag();
3494
- return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}`;
3494
+ return { encrypted: `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}` };
3495
3495
  } catch (err) {
3496
3496
  console.error("[magic-mail] Encryption failed:", err);
3497
3497
  throw new Error("Failed to encrypt credentials");
@@ -3501,7 +3501,8 @@ function requireEncryption() {
3501
3501
  if (!encryptedData) return null;
3502
3502
  try {
3503
3503
  const key = getEncryptionKey();
3504
- const parts = encryptedData.split(":");
3504
+ const encryptedString = typeof encryptedData === "object" && encryptedData.encrypted ? encryptedData.encrypted : encryptedData;
3505
+ const parts = encryptedString.split(":");
3505
3506
  if (parts.length !== 3) {
3506
3507
  throw new Error("Invalid encrypted data format");
3507
3508
  }
@@ -3575,7 +3576,7 @@ function requireEmailRouter() {
3575
3576
  let templateRecord = null;
3576
3577
  if (emailData.templateReferenceId) {
3577
3578
  resolvedTemplateReferenceId = String(emailData.templateReferenceId).trim();
3578
- strapi2.log.info(`[magic-mail] 🧩 Using provided templateReferenceId="${resolvedTemplateReferenceId}"`);
3579
+ strapi2.log.info(`[magic-mail] [TEMPLATE] Using provided templateReferenceId="${resolvedTemplateReferenceId}"`);
3579
3580
  }
3580
3581
  if (!resolvedTemplateReferenceId && templateId) {
3581
3582
  const numericTemplateId = Number(templateId);
@@ -3595,7 +3596,7 @@ function requireEmailRouter() {
3595
3596
  );
3596
3597
  } else {
3597
3598
  resolvedTemplateReferenceId = String(templateId).trim();
3598
- strapi2.log.info(`[magic-mail] 🧩 Treating templateId value as referenceId="${resolvedTemplateReferenceId}"`);
3599
+ strapi2.log.info(`[magic-mail] [TEMPLATE] Treating templateId value as referenceId="${resolvedTemplateReferenceId}"`);
3599
3600
  }
3600
3601
  }
3601
3602
  if (!resolvedTemplateReferenceId) {
@@ -3708,7 +3709,7 @@ function requireEmailRouter() {
3708
3709
  }
3709
3710
  const canSend = await this.checkRateLimits(account);
3710
3711
  if (!canSend) {
3711
- const fallbackAccount = await this.selectAccount(type, priority, [account.id], emailData);
3712
+ const fallbackAccount = await this.selectAccount(type, priority, [account.documentId], emailData);
3712
3713
  if (fallbackAccount) {
3713
3714
  strapi2.log.info(`[magic-mail] Rate limit hit on ${account.name}, using fallback: ${fallbackAccount.name}`);
3714
3715
  return await this.sendViaAccount(fallbackAccount, emailData);
@@ -3776,13 +3777,19 @@ ${text || "Email delivery failed. Please check your email settings."}`;
3776
3777
  },
3777
3778
  /**
3778
3779
  * Select best account based on rules
3780
+ * @param {string} type - Email type (transactional, marketing, notification)
3781
+ * @param {string} priority - Priority level (high, normal, low)
3782
+ * @param {Array<string>} excludeDocumentIds - Array of documentIds to exclude from selection
3783
+ * @param {Object} emailData - Email data for routing rule matching
3784
+ * @returns {Promise<Object|null>} Selected account or null
3779
3785
  */
3780
- async selectAccount(type, priority, excludeIds = [], emailData = {}) {
3786
+ async selectAccount(type, priority, excludeDocumentIds = [], emailData = {}) {
3787
+ const filters = { isActive: true };
3788
+ if (excludeDocumentIds.length > 0) {
3789
+ filters.documentId = { $notIn: excludeDocumentIds };
3790
+ }
3781
3791
  const accounts2 = await strapi2.documents("plugin::magic-mail.email-account").findMany({
3782
- filters: {
3783
- isActive: true,
3784
- id: { $notIn: excludeIds }
3785
- },
3792
+ filters,
3786
3793
  sort: [{ priority: "desc" }]
3787
3794
  });
3788
3795
  if (!accounts2 || accounts2.length === 0) {
@@ -5537,7 +5544,7 @@ function requireOauth() {
5537
5544
  });
5538
5545
  return oauth;
5539
5546
  }
5540
- const version = "2.4.0";
5547
+ const version = "2.3.1";
5541
5548
  const require$$2 = {
5542
5549
  version
5543
5550
  };
@@ -6324,31 +6331,71 @@ function requireEmailDesigner() {
6324
6331
  },
6325
6332
  /**
6326
6333
  * Import templates from JSON
6334
+ * Supports both magic-mail export format and strapi-plugin-email-designer-5 format
6327
6335
  */
6328
6336
  async importTemplates(templates) {
6329
6337
  strapi2.log.info(`[magic-mail] [IMPORT] Importing ${templates.length} templates...`);
6330
6338
  const results = [];
6331
- for (const templateData of templates) {
6339
+ for (const rawData of templates) {
6332
6340
  try {
6341
+ const templateData = this.normalizeImportData(rawData);
6342
+ strapi2.log.info(`[magic-mail] [IMPORT] Processing: "${templateData.name}" (ref: ${templateData.templateReferenceId})`);
6333
6343
  const existing = await this.findByReferenceId(templateData.templateReferenceId);
6334
6344
  if (existing) {
6335
6345
  const updated = await this.update(existing.documentId, templateData);
6336
6346
  results.push({ success: true, action: "updated", template: updated });
6347
+ strapi2.log.info(`[magic-mail] [SUCCESS] Updated: "${templateData.name}"`);
6337
6348
  } else {
6338
6349
  const created = await this.create(templateData);
6339
6350
  results.push({ success: true, action: "created", template: created });
6351
+ strapi2.log.info(`[magic-mail] [SUCCESS] Created: "${templateData.name}"`);
6340
6352
  }
6341
6353
  } catch (error) {
6354
+ strapi2.log.error(`[magic-mail] [ERROR] Import failed for "${rawData.name}": ${error.message}`);
6342
6355
  results.push({
6343
6356
  success: false,
6344
6357
  action: "failed",
6345
6358
  error: error.message,
6346
- templateName: templateData.name
6359
+ templateName: rawData.name
6347
6360
  });
6348
6361
  }
6349
6362
  }
6363
+ const successful = results.filter((r) => r.success).length;
6364
+ const failed = results.filter((r) => !r.success).length;
6365
+ strapi2.log.info(`[magic-mail] [IMPORT] Complete: ${successful} successful, ${failed} failed`);
6350
6366
  return results;
6351
6367
  },
6368
+ /**
6369
+ * Normalize import data from different export formats
6370
+ * Supports: magic-mail, strapi-plugin-email-designer-5, and generic formats
6371
+ */
6372
+ normalizeImportData(rawData) {
6373
+ const isActive = rawData.isActive !== void 0 ? rawData.isActive : rawData.enabled !== void 0 ? rawData.enabled : true;
6374
+ const templateReferenceId = rawData.templateReferenceId || rawData.referenceId || Date.now() + Math.floor(Math.random() * 1e3);
6375
+ const category = rawData.category || "custom";
6376
+ let tags = rawData.tags;
6377
+ if (typeof tags === "string") {
6378
+ try {
6379
+ tags = JSON.parse(tags);
6380
+ } catch (e) {
6381
+ tags = tags.split(",").map((t) => t.trim()).filter(Boolean);
6382
+ }
6383
+ }
6384
+ if (!Array.isArray(tags)) {
6385
+ tags = [];
6386
+ }
6387
+ return {
6388
+ templateReferenceId,
6389
+ name: rawData.name || "Imported Template",
6390
+ subject: rawData.subject || "",
6391
+ design: rawData.design || null,
6392
+ bodyHtml: rawData.bodyHtml || rawData.message || "",
6393
+ bodyText: rawData.bodyText || "",
6394
+ category,
6395
+ tags,
6396
+ isActive
6397
+ };
6398
+ },
6352
6399
  // ============================================================
6353
6400
  // STATISTICS
6354
6401
  // ============================================================
@@ -6705,7 +6752,7 @@ function requireAnalytics() {
6705
6752
  const randomToken = crypto.randomBytes(8).toString("hex");
6706
6753
  const trackingUrl = `${baseUrl}/api/magic-mail/track/open/${emailId}/${recipientHash}?r=${randomToken}`;
6707
6754
  const trackingPixel = `<img src="${trackingUrl}" width="1" height="1" style="display:none;" alt="" />`;
6708
- strapi2.log.info(`[magic-mail] 📍 Tracking pixel URL: ${trackingUrl}`);
6755
+ strapi2.log.info(`[magic-mail] [PIXEL] Tracking pixel URL: ${trackingUrl}`);
6709
6756
  if (html.includes("</body>")) {
6710
6757
  return html.replace("</body>", `${trackingPixel}</body>`);
6711
6758
  }
@@ -596,7 +596,7 @@ function requireAccounts() {
596
596
  if (!testEmail) {
597
597
  ctx.throw(400, "testEmail is required");
598
598
  }
599
- strapi.log.info("[magic-mail] 🧪 Testing Strapi Email Service integration...");
599
+ strapi.log.info("[magic-mail] [TEST] Testing Strapi Email Service integration...");
600
600
  strapi.log.info('[magic-mail] [EMAIL] Calling strapi.plugin("email").service("email").send()');
601
601
  if (accountName) {
602
602
  strapi.log.info(`[magic-mail] [FORCE] Forcing specific account: ${accountName}`);
@@ -2312,7 +2312,7 @@ function requireTest() {
2312
2312
  async testRelations(ctx) {
2313
2313
  try {
2314
2314
  console.log("\n" + "=".repeat(60));
2315
- console.log("🧪 TEST: Template Version Relations (Document Service API)");
2315
+ console.log("[TEST] Template - Version Relations (Document Service API)");
2316
2316
  console.log("=".repeat(60));
2317
2317
  let test1Success = false;
2318
2318
  let test1ReverseSuccess = false;
@@ -2480,7 +2480,7 @@ function requireTest() {
2480
2480
  } else {
2481
2481
  console.log(` [ERROR] FEHLER: Falsche Anzahl Versionen!`);
2482
2482
  }
2483
- console.log("\n🧹 Cleanup Test 3...");
2483
+ console.log("\n[CLEANUP] Cleanup Test 3...");
2484
2484
  if (afterSecondUpdate.versions) {
2485
2485
  for (const version3 of afterSecondUpdate.versions) {
2486
2486
  await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version3.documentId });
@@ -2498,7 +2498,7 @@ function requireTest() {
2498
2498
  console.log(`
2499
2499
  [INFO] Template: "${finalTemplate.name}" (documentId: ${finalTemplate.documentId})`);
2500
2500
  console.log(` Anzahl Versionen: ${finalTemplate.versions?.length || 0}`);
2501
- console.log("\n🧹 Aufräumen...");
2501
+ console.log("\n[CLEANUP] Aufraumen...");
2502
2502
  await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version1.documentId });
2503
2503
  await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version2.documentId });
2504
2504
  await strapi.documents(EMAIL_TEMPLATE_UID).delete({ documentId: testTemplate.documentId });
@@ -3481,7 +3481,7 @@ function requireEncryption() {
3481
3481
  let encrypted = cipher.update(jsonData, "utf8", "hex");
3482
3482
  encrypted += cipher.final("hex");
3483
3483
  const authTag = cipher.getAuthTag();
3484
- return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}`;
3484
+ return { encrypted: `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}` };
3485
3485
  } catch (err) {
3486
3486
  console.error("[magic-mail] Encryption failed:", err);
3487
3487
  throw new Error("Failed to encrypt credentials");
@@ -3491,7 +3491,8 @@ function requireEncryption() {
3491
3491
  if (!encryptedData) return null;
3492
3492
  try {
3493
3493
  const key = getEncryptionKey();
3494
- const parts = encryptedData.split(":");
3494
+ const encryptedString = typeof encryptedData === "object" && encryptedData.encrypted ? encryptedData.encrypted : encryptedData;
3495
+ const parts = encryptedString.split(":");
3495
3496
  if (parts.length !== 3) {
3496
3497
  throw new Error("Invalid encrypted data format");
3497
3498
  }
@@ -3565,7 +3566,7 @@ function requireEmailRouter() {
3565
3566
  let templateRecord = null;
3566
3567
  if (emailData.templateReferenceId) {
3567
3568
  resolvedTemplateReferenceId = String(emailData.templateReferenceId).trim();
3568
- strapi2.log.info(`[magic-mail] 🧩 Using provided templateReferenceId="${resolvedTemplateReferenceId}"`);
3569
+ strapi2.log.info(`[magic-mail] [TEMPLATE] Using provided templateReferenceId="${resolvedTemplateReferenceId}"`);
3569
3570
  }
3570
3571
  if (!resolvedTemplateReferenceId && templateId) {
3571
3572
  const numericTemplateId = Number(templateId);
@@ -3585,7 +3586,7 @@ function requireEmailRouter() {
3585
3586
  );
3586
3587
  } else {
3587
3588
  resolvedTemplateReferenceId = String(templateId).trim();
3588
- strapi2.log.info(`[magic-mail] 🧩 Treating templateId value as referenceId="${resolvedTemplateReferenceId}"`);
3589
+ strapi2.log.info(`[magic-mail] [TEMPLATE] Treating templateId value as referenceId="${resolvedTemplateReferenceId}"`);
3589
3590
  }
3590
3591
  }
3591
3592
  if (!resolvedTemplateReferenceId) {
@@ -3698,7 +3699,7 @@ function requireEmailRouter() {
3698
3699
  }
3699
3700
  const canSend = await this.checkRateLimits(account);
3700
3701
  if (!canSend) {
3701
- const fallbackAccount = await this.selectAccount(type, priority, [account.id], emailData);
3702
+ const fallbackAccount = await this.selectAccount(type, priority, [account.documentId], emailData);
3702
3703
  if (fallbackAccount) {
3703
3704
  strapi2.log.info(`[magic-mail] Rate limit hit on ${account.name}, using fallback: ${fallbackAccount.name}`);
3704
3705
  return await this.sendViaAccount(fallbackAccount, emailData);
@@ -3766,13 +3767,19 @@ ${text || "Email delivery failed. Please check your email settings."}`;
3766
3767
  },
3767
3768
  /**
3768
3769
  * Select best account based on rules
3770
+ * @param {string} type - Email type (transactional, marketing, notification)
3771
+ * @param {string} priority - Priority level (high, normal, low)
3772
+ * @param {Array<string>} excludeDocumentIds - Array of documentIds to exclude from selection
3773
+ * @param {Object} emailData - Email data for routing rule matching
3774
+ * @returns {Promise<Object|null>} Selected account or null
3769
3775
  */
3770
- async selectAccount(type, priority, excludeIds = [], emailData = {}) {
3776
+ async selectAccount(type, priority, excludeDocumentIds = [], emailData = {}) {
3777
+ const filters = { isActive: true };
3778
+ if (excludeDocumentIds.length > 0) {
3779
+ filters.documentId = { $notIn: excludeDocumentIds };
3780
+ }
3771
3781
  const accounts2 = await strapi2.documents("plugin::magic-mail.email-account").findMany({
3772
- filters: {
3773
- isActive: true,
3774
- id: { $notIn: excludeIds }
3775
- },
3782
+ filters,
3776
3783
  sort: [{ priority: "desc" }]
3777
3784
  });
3778
3785
  if (!accounts2 || accounts2.length === 0) {
@@ -5527,7 +5534,7 @@ function requireOauth() {
5527
5534
  });
5528
5535
  return oauth;
5529
5536
  }
5530
- const version = "2.4.0";
5537
+ const version = "2.3.1";
5531
5538
  const require$$2 = {
5532
5539
  version
5533
5540
  };
@@ -6314,31 +6321,71 @@ function requireEmailDesigner() {
6314
6321
  },
6315
6322
  /**
6316
6323
  * Import templates from JSON
6324
+ * Supports both magic-mail export format and strapi-plugin-email-designer-5 format
6317
6325
  */
6318
6326
  async importTemplates(templates) {
6319
6327
  strapi2.log.info(`[magic-mail] [IMPORT] Importing ${templates.length} templates...`);
6320
6328
  const results = [];
6321
- for (const templateData of templates) {
6329
+ for (const rawData of templates) {
6322
6330
  try {
6331
+ const templateData = this.normalizeImportData(rawData);
6332
+ strapi2.log.info(`[magic-mail] [IMPORT] Processing: "${templateData.name}" (ref: ${templateData.templateReferenceId})`);
6323
6333
  const existing = await this.findByReferenceId(templateData.templateReferenceId);
6324
6334
  if (existing) {
6325
6335
  const updated = await this.update(existing.documentId, templateData);
6326
6336
  results.push({ success: true, action: "updated", template: updated });
6337
+ strapi2.log.info(`[magic-mail] [SUCCESS] Updated: "${templateData.name}"`);
6327
6338
  } else {
6328
6339
  const created = await this.create(templateData);
6329
6340
  results.push({ success: true, action: "created", template: created });
6341
+ strapi2.log.info(`[magic-mail] [SUCCESS] Created: "${templateData.name}"`);
6330
6342
  }
6331
6343
  } catch (error) {
6344
+ strapi2.log.error(`[magic-mail] [ERROR] Import failed for "${rawData.name}": ${error.message}`);
6332
6345
  results.push({
6333
6346
  success: false,
6334
6347
  action: "failed",
6335
6348
  error: error.message,
6336
- templateName: templateData.name
6349
+ templateName: rawData.name
6337
6350
  });
6338
6351
  }
6339
6352
  }
6353
+ const successful = results.filter((r) => r.success).length;
6354
+ const failed = results.filter((r) => !r.success).length;
6355
+ strapi2.log.info(`[magic-mail] [IMPORT] Complete: ${successful} successful, ${failed} failed`);
6340
6356
  return results;
6341
6357
  },
6358
+ /**
6359
+ * Normalize import data from different export formats
6360
+ * Supports: magic-mail, strapi-plugin-email-designer-5, and generic formats
6361
+ */
6362
+ normalizeImportData(rawData) {
6363
+ const isActive = rawData.isActive !== void 0 ? rawData.isActive : rawData.enabled !== void 0 ? rawData.enabled : true;
6364
+ const templateReferenceId = rawData.templateReferenceId || rawData.referenceId || Date.now() + Math.floor(Math.random() * 1e3);
6365
+ const category = rawData.category || "custom";
6366
+ let tags = rawData.tags;
6367
+ if (typeof tags === "string") {
6368
+ try {
6369
+ tags = JSON.parse(tags);
6370
+ } catch (e) {
6371
+ tags = tags.split(",").map((t) => t.trim()).filter(Boolean);
6372
+ }
6373
+ }
6374
+ if (!Array.isArray(tags)) {
6375
+ tags = [];
6376
+ }
6377
+ return {
6378
+ templateReferenceId,
6379
+ name: rawData.name || "Imported Template",
6380
+ subject: rawData.subject || "",
6381
+ design: rawData.design || null,
6382
+ bodyHtml: rawData.bodyHtml || rawData.message || "",
6383
+ bodyText: rawData.bodyText || "",
6384
+ category,
6385
+ tags,
6386
+ isActive
6387
+ };
6388
+ },
6342
6389
  // ============================================================
6343
6390
  // STATISTICS
6344
6391
  // ============================================================
@@ -6695,7 +6742,7 @@ function requireAnalytics() {
6695
6742
  const randomToken = crypto.randomBytes(8).toString("hex");
6696
6743
  const trackingUrl = `${baseUrl}/api/magic-mail/track/open/${emailId}/${recipientHash}?r=${randomToken}`;
6697
6744
  const trackingPixel = `<img src="${trackingUrl}" width="1" height="1" style="display:none;" alt="" />`;
6698
- strapi2.log.info(`[magic-mail] 📍 Tracking pixel URL: ${trackingUrl}`);
6745
+ strapi2.log.info(`[magic-mail] [PIXEL] Tracking pixel URL: ${trackingUrl}`);
6699
6746
  if (html.includes("</body>")) {
6700
6747
  return html.replace("</body>", `${trackingPixel}</body>`);
6701
6748
  }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.3.0",
2
+ "version": "2.3.2",
3
3
  "keywords": [
4
4
  "strapi",
5
5
  "strapi-plugin",