strapi-plugin-magic-mail 2.3.11 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -250,13 +250,27 @@ function requireConfig() {
250
250
  };
251
251
  return config;
252
252
  }
253
+ const kind$7 = "collectionType";
254
+ const collectionName$7 = "magic_mail_accounts";
255
+ const info$7 = { "singularName": "email-account", "pluralName": "email-accounts", "displayName": "Email Account", "description": "Email provider accounts for multi-account email sending" };
256
+ const options$7 = { "draftAndPublish": false };
257
+ const pluginOptions$7 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
258
+ const attributes$7 = { "name": { "type": "string", "required": true, "unique": true }, "description": { "type": "text" }, "provider": { "type": "enumeration", "enum": ["smtp", "gmail-oauth", "microsoft-oauth", "yahoo-oauth", "sendgrid", "mailgun", "ses", "nodemailer"], "required": true, "default": "smtp" }, "config": { "type": "json", "required": true }, "oauth": { "type": "json" }, "fromEmail": { "type": "email", "required": true }, "fromName": { "type": "string" }, "replyTo": { "type": "email" }, "isActive": { "type": "boolean", "default": true, "required": true }, "isPrimary": { "type": "boolean", "default": false }, "priority": { "type": "integer", "default": 1, "min": 1, "max": 10 }, "dailyLimit": { "type": "integer", "default": 0 }, "hourlyLimit": { "type": "integer", "default": 0 }, "emailsSentToday": { "type": "integer", "default": 0 }, "emailsSentThisHour": { "type": "integer", "default": 0 }, "totalEmailsSent": { "type": "integer", "default": 0 }, "lastUsed": { "type": "datetime" } };
259
+ const require$$0 = {
260
+ kind: kind$7,
261
+ collectionName: collectionName$7,
262
+ info: info$7,
263
+ options: options$7,
264
+ pluginOptions: pluginOptions$7,
265
+ attributes: attributes$7
266
+ };
253
267
  const kind$6 = "collectionType";
254
- const collectionName$6 = "magic_mail_accounts";
255
- const info$6 = { "singularName": "email-account", "pluralName": "email-accounts", "displayName": "Email Account", "description": "Email provider accounts for multi-account email sending" };
268
+ const collectionName$6 = "magic_mail_routing_rules";
269
+ const info$6 = { "singularName": "routing-rule", "pluralName": "routing-rules", "displayName": "Email Routing Rule", "description": "Rules for routing emails to specific accounts" };
256
270
  const options$6 = { "draftAndPublish": false };
257
271
  const pluginOptions$6 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
258
- const attributes$6 = { "name": { "type": "string", "required": true, "unique": true }, "description": { "type": "text" }, "provider": { "type": "enumeration", "enum": ["smtp", "gmail-oauth", "microsoft-oauth", "yahoo-oauth", "sendgrid", "mailgun", "ses", "nodemailer"], "required": true, "default": "smtp" }, "config": { "type": "json", "required": true }, "oauth": { "type": "json" }, "fromEmail": { "type": "email", "required": true }, "fromName": { "type": "string" }, "replyTo": { "type": "email" }, "isActive": { "type": "boolean", "default": true, "required": true }, "isPrimary": { "type": "boolean", "default": false }, "priority": { "type": "integer", "default": 1, "min": 1, "max": 10 }, "dailyLimit": { "type": "integer", "default": 0 }, "hourlyLimit": { "type": "integer", "default": 0 }, "emailsSentToday": { "type": "integer", "default": 0 }, "emailsSentThisHour": { "type": "integer", "default": 0 }, "totalEmailsSent": { "type": "integer", "default": 0 }, "lastUsed": { "type": "datetime" } };
259
- const require$$0 = {
272
+ const attributes$6 = { "name": { "type": "string", "required": true, "unique": true }, "description": { "type": "text" }, "isActive": { "type": "boolean", "default": true, "required": true }, "priority": { "type": "integer", "default": 1, "min": 1 }, "matchType": { "type": "enumeration", "enum": ["emailType", "subject", "recipient", "template", "custom"], "required": true, "default": "emailType" }, "matchValue": { "type": "text", "required": true }, "accountName": { "type": "string", "required": true }, "fallbackAccountName": { "type": "string" }, "whatsappFallback": { "type": "boolean", "default": false }, "whatsappPhoneField": { "type": "string" } };
273
+ const require$$1 = {
260
274
  kind: kind$6,
261
275
  collectionName: collectionName$6,
262
276
  info: info$6,
@@ -265,12 +279,12 @@ const require$$0 = {
265
279
  attributes: attributes$6
266
280
  };
267
281
  const kind$5 = "collectionType";
268
- const collectionName$5 = "magic_mail_routing_rules";
269
- const info$5 = { "singularName": "routing-rule", "pluralName": "routing-rules", "displayName": "Email Routing Rule", "description": "Rules for routing emails to specific accounts" };
282
+ const collectionName$5 = "magic_mail_email_logs";
283
+ const info$5 = { "singularName": "email-log", "pluralName": "email-logs", "displayName": "Email Log", "description": "Tracks all sent emails with user association" };
270
284
  const options$5 = { "draftAndPublish": false };
271
285
  const pluginOptions$5 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
272
- const attributes$5 = { "name": { "type": "string", "required": true, "unique": true }, "description": { "type": "text" }, "isActive": { "type": "boolean", "default": true, "required": true }, "priority": { "type": "integer", "default": 1, "min": 1 }, "matchType": { "type": "enumeration", "enum": ["emailType", "subject", "recipient", "template", "custom"], "required": true, "default": "emailType" }, "matchValue": { "type": "text", "required": true }, "accountName": { "type": "string", "required": true }, "fallbackAccountName": { "type": "string" }, "whatsappFallback": { "type": "boolean", "default": false }, "whatsappPhoneField": { "type": "string" } };
273
- const require$$1 = {
286
+ const attributes$5 = { "emailId": { "type": "string", "required": true, "unique": true }, "user": { "type": "relation", "relation": "manyToOne", "target": "plugin::users-permissions.user" }, "recipient": { "type": "email", "required": true }, "recipientName": { "type": "string" }, "subject": { "type": "string", "required": true }, "templateId": { "type": "integer" }, "templateName": { "type": "string" }, "accountId": { "type": "integer" }, "accountName": { "type": "string" }, "sentAt": { "type": "datetime", "required": true }, "deliveredAt": { "type": "datetime" }, "firstOpenedAt": { "type": "datetime" }, "lastOpenedAt": { "type": "datetime" }, "openCount": { "type": "integer", "default": 0 }, "clickCount": { "type": "integer", "default": 0 }, "bounced": { "type": "boolean", "default": false }, "bounceReason": { "type": "text" }, "unsubscribed": { "type": "boolean", "default": false }, "unsubscribedAt": { "type": "datetime" }, "metadata": { "type": "json" }, "events": { "type": "relation", "relation": "oneToMany", "target": "plugin::magic-mail.email-event", "mappedBy": "emailLog" }, "links": { "type": "relation", "relation": "oneToMany", "target": "plugin::magic-mail.email-link", "mappedBy": "emailLog" } };
287
+ const require$$2$1 = {
274
288
  kind: kind$5,
275
289
  collectionName: collectionName$5,
276
290
  info: info$5,
@@ -279,12 +293,12 @@ const require$$1 = {
279
293
  attributes: attributes$5
280
294
  };
281
295
  const kind$4 = "collectionType";
282
- const collectionName$4 = "magic_mail_email_logs";
283
- const info$4 = { "singularName": "email-log", "pluralName": "email-logs", "displayName": "Email Log", "description": "Tracks all sent emails with user association" };
296
+ const collectionName$4 = "magic_mail_email_events";
297
+ const info$4 = { "singularName": "email-event", "pluralName": "email-events", "displayName": "Email Event", "description": "Individual email tracking events (opens, clicks, bounces)" };
284
298
  const options$4 = { "draftAndPublish": false };
285
299
  const pluginOptions$4 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
286
- const attributes$4 = { "emailId": { "type": "string", "required": true, "unique": true }, "user": { "type": "relation", "relation": "manyToOne", "target": "plugin::users-permissions.user" }, "recipient": { "type": "email", "required": true }, "recipientName": { "type": "string" }, "subject": { "type": "string", "required": true }, "templateId": { "type": "integer" }, "templateName": { "type": "string" }, "accountId": { "type": "integer" }, "accountName": { "type": "string" }, "sentAt": { "type": "datetime", "required": true }, "deliveredAt": { "type": "datetime" }, "firstOpenedAt": { "type": "datetime" }, "lastOpenedAt": { "type": "datetime" }, "openCount": { "type": "integer", "default": 0 }, "clickCount": { "type": "integer", "default": 0 }, "bounced": { "type": "boolean", "default": false }, "bounceReason": { "type": "text" }, "unsubscribed": { "type": "boolean", "default": false }, "unsubscribedAt": { "type": "datetime" }, "metadata": { "type": "json" }, "events": { "type": "relation", "relation": "oneToMany", "target": "plugin::magic-mail.email-event", "mappedBy": "emailLog" }, "links": { "type": "relation", "relation": "oneToMany", "target": "plugin::magic-mail.email-link", "mappedBy": "emailLog" } };
287
- const require$$2$1 = {
300
+ const attributes$4 = { "emailLog": { "type": "relation", "relation": "manyToOne", "target": "plugin::magic-mail.email-log", "inversedBy": "events" }, "type": { "type": "enumeration", "enum": ["open", "click", "bounce", "complaint", "unsubscribe"], "required": true }, "timestamp": { "type": "datetime", "required": true }, "ipAddress": { "type": "string" }, "userAgent": { "type": "text" }, "location": { "type": "json" }, "linkUrl": { "type": "text" }, "linkText": { "type": "string" }, "metadata": { "type": "json" } };
301
+ const require$$3 = {
288
302
  kind: kind$4,
289
303
  collectionName: collectionName$4,
290
304
  info: info$4,
@@ -293,12 +307,12 @@ const require$$2$1 = {
293
307
  attributes: attributes$4
294
308
  };
295
309
  const kind$3 = "collectionType";
296
- const collectionName$3 = "magic_mail_email_events";
297
- const info$3 = { "singularName": "email-event", "pluralName": "email-events", "displayName": "Email Event", "description": "Individual email tracking events (opens, clicks, bounces)" };
310
+ const collectionName$3 = "magic_mail_email_links";
311
+ const info$3 = { "singularName": "email-link", "pluralName": "email-links", "displayName": "Email Link", "description": "Stores click tracking links for emails" };
298
312
  const options$3 = { "draftAndPublish": false };
299
313
  const pluginOptions$3 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
300
- const attributes$3 = { "emailLog": { "type": "relation", "relation": "manyToOne", "target": "plugin::magic-mail.email-log", "inversedBy": "events" }, "type": { "type": "enumeration", "enum": ["open", "click", "bounce", "complaint", "unsubscribe"], "required": true }, "timestamp": { "type": "datetime", "required": true }, "ipAddress": { "type": "string" }, "userAgent": { "type": "text" }, "location": { "type": "json" }, "linkUrl": { "type": "text" }, "linkText": { "type": "string" }, "metadata": { "type": "json" } };
301
- const require$$3 = {
314
+ const attributes$3 = { "emailLog": { "type": "relation", "relation": "manyToOne", "target": "plugin::magic-mail.email-log", "inversedBy": "links" }, "linkHash": { "type": "string", "required": true, "unique": false }, "originalUrl": { "type": "text", "required": true }, "clickCount": { "type": "integer", "default": 0 }, "firstClickedAt": { "type": "datetime" }, "lastClickedAt": { "type": "datetime" } };
315
+ const require$$4 = {
302
316
  kind: kind$3,
303
317
  collectionName: collectionName$3,
304
318
  info: info$3,
@@ -307,12 +321,12 @@ const require$$3 = {
307
321
  attributes: attributes$3
308
322
  };
309
323
  const kind$2 = "collectionType";
310
- const collectionName$2 = "magic_mail_email_links";
311
- const info$2 = { "singularName": "email-link", "pluralName": "email-links", "displayName": "Email Link", "description": "Stores click tracking links for emails" };
312
- const options$2 = { "draftAndPublish": false };
324
+ const collectionName$2 = "magic_mail_email_templates";
325
+ const info$2 = { "singularName": "email-template", "pluralName": "email-templates", "displayName": "Email Template", "description": "Email templates created with the visual designer" };
326
+ const options$2 = { "draftAndPublish": false, "timestamps": true };
313
327
  const pluginOptions$2 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
314
- const attributes$2 = { "emailLog": { "type": "relation", "relation": "manyToOne", "target": "plugin::magic-mail.email-log", "inversedBy": "links" }, "linkHash": { "type": "string", "required": true, "unique": false }, "originalUrl": { "type": "text", "required": true }, "clickCount": { "type": "integer", "default": 0 }, "firstClickedAt": { "type": "datetime" }, "lastClickedAt": { "type": "datetime" } };
315
- const require$$4 = {
328
+ const attributes$2 = { "templateReferenceId": { "type": "integer", "required": true, "unique": true, "configurable": false }, "name": { "type": "string", "required": true, "configurable": false }, "subject": { "type": "string", "required": true, "configurable": false }, "design": { "type": "json", "configurable": false }, "bodyHtml": { "type": "text", "configurable": false }, "bodyText": { "type": "text", "configurable": false }, "category": { "type": "enumeration", "enum": ["transactional", "marketing", "notification", "custom"], "default": "custom", "configurable": false }, "isActive": { "type": "boolean", "default": true, "configurable": false }, "tags": { "type": "json", "configurable": false }, "versions": { "type": "relation", "relation": "oneToMany", "target": "plugin::magic-mail.email-template-version", "mappedBy": "template" } };
329
+ const require$$5 = {
316
330
  kind: kind$2,
317
331
  collectionName: collectionName$2,
318
332
  info: info$2,
@@ -321,12 +335,12 @@ const require$$4 = {
321
335
  attributes: attributes$2
322
336
  };
323
337
  const kind$1 = "collectionType";
324
- const collectionName$1 = "magic_mail_email_templates";
325
- const info$1 = { "singularName": "email-template", "pluralName": "email-templates", "displayName": "Email Template", "description": "Email templates created with the visual designer" };
338
+ const collectionName$1 = "magic_mail_email_template_versions";
339
+ const info$1 = { "singularName": "email-template-version", "pluralName": "email-template-versions", "displayName": "Email Template Version", "description": "Version history for email templates" };
326
340
  const options$1 = { "draftAndPublish": false, "timestamps": true };
327
341
  const pluginOptions$1 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
328
- const attributes$1 = { "templateReferenceId": { "type": "integer", "required": true, "unique": true, "configurable": false }, "name": { "type": "string", "required": true, "configurable": false }, "subject": { "type": "string", "required": true, "configurable": false }, "design": { "type": "json", "configurable": false }, "bodyHtml": { "type": "text", "configurable": false }, "bodyText": { "type": "text", "configurable": false }, "category": { "type": "enumeration", "enum": ["transactional", "marketing", "notification", "custom"], "default": "custom", "configurable": false }, "isActive": { "type": "boolean", "default": true, "configurable": false }, "tags": { "type": "json", "configurable": false }, "versions": { "type": "relation", "relation": "oneToMany", "target": "plugin::magic-mail.email-template-version", "mappedBy": "template" } };
329
- const require$$5 = {
342
+ const attributes$1 = { "template": { "type": "relation", "relation": "manyToOne", "target": "plugin::magic-mail.email-template", "inversedBy": "versions" }, "versionNumber": { "type": "integer", "required": true, "configurable": false }, "name": { "type": "string", "configurable": false }, "subject": { "type": "string", "configurable": false }, "design": { "type": "json", "configurable": false }, "bodyHtml": { "type": "text", "configurable": false }, "bodyText": { "type": "text", "configurable": false }, "tags": { "type": "json", "configurable": false } };
343
+ const require$$6 = {
330
344
  kind: kind$1,
331
345
  collectionName: collectionName$1,
332
346
  info: info$1,
@@ -334,13 +348,13 @@ const require$$5 = {
334
348
  pluginOptions: pluginOptions$1,
335
349
  attributes: attributes$1
336
350
  };
337
- const kind = "collectionType";
338
- const collectionName = "magic_mail_email_template_versions";
339
- const info = { "singularName": "email-template-version", "pluralName": "email-template-versions", "displayName": "Email Template Version", "description": "Version history for email templates" };
340
- const options = { "draftAndPublish": false, "timestamps": true };
351
+ const kind = "singleType";
352
+ const collectionName = "magic_mail_settings";
353
+ const info = { "singularName": "plugin-settings", "pluralName": "plugin-settings", "displayName": "MagicMail Settings" };
354
+ const options = { "draftAndPublish": false, "comment": "Global settings for MagicMail plugin" };
341
355
  const pluginOptions = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
342
- const attributes = { "template": { "type": "relation", "relation": "manyToOne", "target": "plugin::magic-mail.email-template", "inversedBy": "versions" }, "versionNumber": { "type": "integer", "required": true, "configurable": false }, "name": { "type": "string", "configurable": false }, "subject": { "type": "string", "configurable": false }, "design": { "type": "json", "configurable": false }, "bodyHtml": { "type": "text", "configurable": false }, "bodyText": { "type": "text", "configurable": false }, "tags": { "type": "json", "configurable": false } };
343
- const require$$6 = {
356
+ const attributes = { "enableLinkTracking": { "type": "boolean", "default": true, "configurable": false }, "enableOpenTracking": { "type": "boolean", "default": true, "configurable": false }, "trackingBaseUrl": { "type": "string", "configurable": false }, "defaultFromName": { "type": "string", "configurable": false }, "defaultFromEmail": { "type": "string", "configurable": false }, "unsubscribeUrl": { "type": "string", "configurable": false }, "enableUnsubscribeHeader": { "type": "boolean", "default": true, "configurable": false } };
357
+ const require$$7 = {
344
358
  kind,
345
359
  collectionName,
346
360
  info,
@@ -360,6 +374,7 @@ function requireContentTypes() {
360
374
  const emailLink = require$$4;
361
375
  const emailTemplate = require$$5;
362
376
  const emailTemplateVersion = require$$6;
377
+ const pluginSettings2 = require$$7;
363
378
  contentTypes = {
364
379
  "email-account": {
365
380
  schema: emailAccount
@@ -381,6 +396,9 @@ function requireContentTypes() {
381
396
  },
382
397
  "email-template-version": {
383
398
  schema: emailTemplateVersion
399
+ },
400
+ "plugin-settings": {
401
+ schema: pluginSettings2
384
402
  }
385
403
  };
386
404
  return contentTypes;
@@ -2555,7 +2573,7 @@ function requireWhatsapp$1() {
2555
2573
  success: true,
2556
2574
  data: {
2557
2575
  available,
2558
- message: available ? "WhatsApp integration is available" : "Baileys not installed. Run: npm install @whiskeysockets/baileys pino qrcode"
2576
+ message: available ? "WhatsApp integration is available" : "Baileys not installed. Run: npm install baileys pino qrcode"
2559
2577
  }
2560
2578
  };
2561
2579
  } catch (error) {
@@ -2750,6 +2768,62 @@ function requireWhatsapp$1() {
2750
2768
  };
2751
2769
  return whatsapp$1;
2752
2770
  }
2771
+ var pluginSettings$1;
2772
+ var hasRequiredPluginSettings$1;
2773
+ function requirePluginSettings$1() {
2774
+ if (hasRequiredPluginSettings$1) return pluginSettings$1;
2775
+ hasRequiredPluginSettings$1 = 1;
2776
+ pluginSettings$1 = ({ strapi: strapi2 }) => ({
2777
+ /**
2778
+ * GET /magic-mail/settings
2779
+ * Get current plugin settings
2780
+ */
2781
+ async getSettings(ctx) {
2782
+ try {
2783
+ const settings = await strapi2.plugin("magic-mail").service("plugin-settings").getSettings();
2784
+ ctx.body = {
2785
+ data: settings
2786
+ };
2787
+ } catch (error) {
2788
+ strapi2.log.error("[magic-mail] [SETTINGS] Error getting settings:", error);
2789
+ ctx.throw(500, "Failed to get settings");
2790
+ }
2791
+ },
2792
+ /**
2793
+ * PUT /magic-mail/settings
2794
+ * Update plugin settings
2795
+ */
2796
+ async updateSettings(ctx) {
2797
+ try {
2798
+ const { body } = ctx.request;
2799
+ const allowedFields = [
2800
+ "enableLinkTracking",
2801
+ "enableOpenTracking",
2802
+ "trackingBaseUrl",
2803
+ "defaultFromName",
2804
+ "defaultFromEmail",
2805
+ "unsubscribeUrl",
2806
+ "enableUnsubscribeHeader"
2807
+ ];
2808
+ const data = {};
2809
+ for (const field of allowedFields) {
2810
+ if (body[field] !== void 0) {
2811
+ data[field] = body[field];
2812
+ }
2813
+ }
2814
+ const settings = await strapi2.plugin("magic-mail").service("plugin-settings").updateSettings(data);
2815
+ ctx.body = {
2816
+ data: settings,
2817
+ message: "Settings updated successfully"
2818
+ };
2819
+ } catch (error) {
2820
+ strapi2.log.error("[magic-mail] [SETTINGS] Error updating settings:", error);
2821
+ ctx.throw(500, "Failed to update settings");
2822
+ }
2823
+ }
2824
+ });
2825
+ return pluginSettings$1;
2826
+ }
2753
2827
  var controllers;
2754
2828
  var hasRequiredControllers;
2755
2829
  function requireControllers() {
@@ -2764,6 +2838,7 @@ function requireControllers() {
2764
2838
  const analytics2 = requireAnalytics$1();
2765
2839
  const test2 = requireTest();
2766
2840
  const whatsapp2 = requireWhatsapp$1();
2841
+ const pluginSettings2 = requirePluginSettings$1();
2767
2842
  controllers = {
2768
2843
  controller: controller2,
2769
2844
  accounts: accounts2,
@@ -2773,7 +2848,8 @@ function requireControllers() {
2773
2848
  emailDesigner: emailDesigner2,
2774
2849
  analytics: analytics2,
2775
2850
  test: test2,
2776
- whatsapp: whatsapp2
2851
+ whatsapp: whatsapp2,
2852
+ pluginSettings: pluginSettings2
2777
2853
  };
2778
2854
  return controllers;
2779
2855
  }
@@ -3348,6 +3424,25 @@ function requireAdmin() {
3348
3424
  policies: ["admin::isAuthenticatedAdmin"],
3349
3425
  description: "Get WhatsApp session info"
3350
3426
  }
3427
+ },
3428
+ // Plugin Settings Routes
3429
+ {
3430
+ method: "GET",
3431
+ path: "/settings",
3432
+ handler: "pluginSettings.getSettings",
3433
+ config: {
3434
+ policies: ["admin::isAuthenticatedAdmin"],
3435
+ description: "Get plugin settings"
3436
+ }
3437
+ },
3438
+ {
3439
+ method: "PUT",
3440
+ path: "/settings",
3441
+ handler: "pluginSettings.updateSettings",
3442
+ config: {
3443
+ policies: ["admin::isAuthenticatedAdmin"],
3444
+ description: "Update plugin settings"
3445
+ }
3351
3446
  }
3352
3447
  ]
3353
3448
  };
@@ -3624,6 +3719,10 @@ function requireEmailRouter() {
3624
3719
  if (enableTracking && html) {
3625
3720
  try {
3626
3721
  const analyticsService = strapi2.plugin("magic-mail").service("analytics");
3722
+ const settingsService = strapi2.plugin("magic-mail").service("plugin-settings");
3723
+ const pluginSettings2 = await settingsService.getSettings();
3724
+ const globalLinkTrackingEnabled = pluginSettings2.enableLinkTracking !== false;
3725
+ const globalOpenTrackingEnabled = pluginSettings2.enableOpenTracking !== false;
3627
3726
  emailLog = await analyticsService.createEmailLog({
3628
3727
  to,
3629
3728
  userId: emailData.userId || null,
@@ -3642,12 +3741,19 @@ function requireEmailRouter() {
3642
3741
  }
3643
3742
  });
3644
3743
  recipientHash = analyticsService.generateRecipientHash(emailLog.emailId, to);
3645
- html = analyticsService.injectTrackingPixel(html, emailLog.emailId, recipientHash);
3646
- if (!skipLinkTracking) {
3744
+ if (globalOpenTrackingEnabled) {
3745
+ html = analyticsService.injectTrackingPixel(html, emailLog.emailId, recipientHash);
3746
+ } else {
3747
+ strapi2.log.info(`[magic-mail] [STATS] Open tracking DISABLED globally`);
3748
+ }
3749
+ const shouldTrackLinks = globalLinkTrackingEnabled && !skipLinkTracking;
3750
+ if (shouldTrackLinks) {
3647
3751
  html = await analyticsService.rewriteLinksForTracking(html, emailLog.emailId, recipientHash);
3648
- strapi2.log.info(`[magic-mail] [STATS] Full tracking enabled for email: ${emailLog.emailId}`);
3752
+ strapi2.log.info(`[magic-mail] [STATS] Link tracking enabled for email: ${emailLog.emailId}`);
3753
+ } else if (!globalLinkTrackingEnabled) {
3754
+ strapi2.log.info(`[magic-mail] [STATS] Link tracking DISABLED globally for email: ${emailLog.emailId}`);
3649
3755
  } else {
3650
- strapi2.log.info(`[magic-mail] [STATS] Open tracking enabled, link tracking DISABLED for email: ${emailLog.emailId}`);
3756
+ strapi2.log.info(`[magic-mail] [STATS] Link tracking DISABLED per-email (skipLinkTracking=true) for email: ${emailLog.emailId}`);
3651
3757
  }
3652
3758
  } catch (error) {
3653
3759
  strapi2.log.error(`[magic-mail] [WARNING] Tracking setup failed (continuing without tracking):`, error.message);
@@ -3721,7 +3827,7 @@ function requireEmailRouter() {
3721
3827
  await strapi2.documents("plugin::magic-mail.email-log").update({
3722
3828
  documentId: emailLog.documentId,
3723
3829
  data: {
3724
- accountId: account.id,
3830
+ accountId: account.documentId,
3725
3831
  accountName: account.name,
3726
3832
  deliveredAt: /* @__PURE__ */ new Date()
3727
3833
  }
@@ -5543,7 +5649,7 @@ function requireOauth() {
5543
5649
  });
5544
5650
  return oauth;
5545
5651
  }
5546
- const version = "2.3.10";
5652
+ const version = "2.4.0";
5547
5653
  const require$$2 = {
5548
5654
  version
5549
5655
  };
@@ -6827,7 +6933,10 @@ function requireAnalytics() {
6827
6933
  * Rewrite links for click tracking
6828
6934
  */
6829
6935
  async rewriteLinksForTracking(html, emailId, recipientHash) {
6830
- const baseUrl = strapi2.config.get("server.url") || "http://localhost:1337";
6936
+ const settingsService = strapi2.plugin("magic-mail").service("plugin-settings");
6937
+ const pluginSettings2 = await settingsService.getSettings();
6938
+ const baseUrl = pluginSettings2.trackingBaseUrl || strapi2.config.get("server.url") || "http://localhost:1337";
6939
+ strapi2.log.debug(`[magic-mail] [LINK-TRACK] Using base URL: ${baseUrl}`);
6831
6940
  const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
6832
6941
  filters: { emailId }
6833
6942
  });
@@ -6835,23 +6944,27 @@ function requireAnalytics() {
6835
6944
  strapi2.log.error(`[magic-mail] Cannot rewrite links: Email log not found for ${emailId}`);
6836
6945
  return html;
6837
6946
  }
6838
- const linkRegex = /<a\s+[^>]*href\s*=\s*["']([^"']+)["'][^>]*>/gis;
6947
+ let processedHtml = html.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"');
6948
+ const linkRegex = /<a\s+(?:[^>]*?\s+)?href\s*=\s*["']([^"']+)["'][^>]*>/gis;
6949
+ const simpleLinkRegex = /href\s*=\s*["'](https?:\/\/[^"']+)["']/gi;
6839
6950
  const linkMappings = [];
6840
6951
  const replacements = [];
6952
+ const processedUrls = /* @__PURE__ */ new Set();
6841
6953
  let linkCount = 0;
6842
6954
  let match;
6843
- while ((match = linkRegex.exec(html)) !== null) {
6844
- match[0];
6845
- const originalUrl = match[1];
6955
+ while ((match = linkRegex.exec(processedHtml)) !== null) {
6956
+ const originalUrl = match[1].trim();
6957
+ if (processedUrls.has(originalUrl)) continue;
6846
6958
  strapi2.log.debug(`[magic-mail] [CHECK] Found link: ${originalUrl.substring(0, 100)}${originalUrl.length > 100 ? "..." : ""}`);
6847
- if (originalUrl.startsWith("#") || originalUrl.includes("/track/click/")) {
6848
- strapi2.log.debug(`[magic-mail] [SKIP] Skipping (anchor or already tracked)`);
6959
+ if (originalUrl.startsWith("#") || originalUrl.includes("/track/click/") || originalUrl.startsWith("mailto:") || originalUrl.startsWith("tel:") || originalUrl.startsWith("javascript:")) {
6960
+ strapi2.log.debug(`[magic-mail] [SKIP] Skipping special URL: ${originalUrl.substring(0, 50)}`);
6849
6961
  continue;
6850
6962
  }
6851
- if (!originalUrl.match(/^https?:\/\//i) && !originalUrl.startsWith("/")) {
6852
- strapi2.log.debug(`[magic-mail] [SKIP] Skipping relative URL: ${originalUrl}`);
6963
+ if (!originalUrl.match(/^https?:\/\//i)) {
6964
+ strapi2.log.debug(`[magic-mail] [SKIP] Skipping non-http URL: ${originalUrl.substring(0, 50)}`);
6853
6965
  continue;
6854
6966
  }
6967
+ processedUrls.add(originalUrl);
6855
6968
  const linkHash = crypto.createHash("md5").update(originalUrl).digest("hex").substring(0, 8);
6856
6969
  linkMappings.push({
6857
6970
  linkHash,
@@ -6859,12 +6972,27 @@ function requireAnalytics() {
6859
6972
  });
6860
6973
  const trackingUrl = `${baseUrl}/api/magic-mail/track/click/${emailId}/${linkHash}/${recipientHash}`;
6861
6974
  linkCount++;
6862
- strapi2.log.info(`[magic-mail] [LINK] Link ${linkCount}: ${originalUrl} ${trackingUrl}`);
6975
+ strapi2.log.info(`[magic-mail] [LINK] Link ${linkCount}: ${originalUrl.substring(0, 80)}${originalUrl.length > 80 ? "..." : ""}`);
6863
6976
  replacements.push({
6864
6977
  from: originalUrl,
6865
6978
  to: trackingUrl
6866
6979
  });
6867
6980
  }
6981
+ if (linkCount === 0) {
6982
+ strapi2.log.debug(`[magic-mail] [LINK-TRACK] Primary regex found no links, trying simple pattern...`);
6983
+ while ((match = simpleLinkRegex.exec(processedHtml)) !== null) {
6984
+ const originalUrl = match[1].trim();
6985
+ if (processedUrls.has(originalUrl)) continue;
6986
+ if (originalUrl.includes("/track/click/")) continue;
6987
+ processedUrls.add(originalUrl);
6988
+ const linkHash = crypto.createHash("md5").update(originalUrl).digest("hex").substring(0, 8);
6989
+ linkMappings.push({ linkHash, originalUrl });
6990
+ const trackingUrl = `${baseUrl}/api/magic-mail/track/click/${emailId}/${linkHash}/${recipientHash}`;
6991
+ linkCount++;
6992
+ strapi2.log.info(`[magic-mail] [LINK] Link ${linkCount} (simple): ${originalUrl.substring(0, 80)}`);
6993
+ replacements.push({ from: originalUrl, to: trackingUrl });
6994
+ }
6995
+ }
6868
6996
  for (const mapping of linkMappings) {
6869
6997
  try {
6870
6998
  await this.storeLinkMapping(emailLog.documentId, mapping.linkHash, mapping.originalUrl);
@@ -6875,13 +7003,17 @@ function requireAnalytics() {
6875
7003
  let result = html;
6876
7004
  for (const replacement of replacements) {
6877
7005
  const escapedFrom = replacement.from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7006
+ const escapedFromEncoded = replacement.from.replace(/&/g, "&amp;").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6878
7007
  const hrefRegex = new RegExp(`(href\\s*=\\s*["'])${escapedFrom}(["'])`, "gi");
7008
+ const hrefRegexEncoded = new RegExp(`(href\\s*=\\s*["'])${escapedFromEncoded}(["'])`, "gi");
6879
7009
  result = result.replace(hrefRegex, `$1${replacement.to}$2`);
7010
+ result = result.replace(hrefRegexEncoded, `$1${replacement.to}$2`);
6880
7011
  }
6881
7012
  if (linkCount > 0) {
6882
7013
  strapi2.log.info(`[magic-mail] [SUCCESS] Rewrote ${linkCount} links for click tracking`);
6883
7014
  } else {
6884
- strapi2.log.warn(`[magic-mail] [WARNING] No links found in email HTML for tracking!`);
7015
+ strapi2.log.warn(`[magic-mail] [WARNING] No links found in email HTML for tracking!`);
7016
+ strapi2.log.debug(`[magic-mail] [DEBUG] HTML preview: ${html.substring(0, 500)}...`);
6885
7017
  }
6886
7018
  return result;
6887
7019
  },
@@ -6966,14 +7098,14 @@ function requireWhatsapp() {
6966
7098
  const loadBaileys = async () => {
6967
7099
  if (!baileys) {
6968
7100
  try {
6969
- baileys = require("@whiskeysockets/baileys");
7101
+ baileys = require("baileys");
6970
7102
  if (process.env.DEBUG) {
6971
7103
  console.log("[MagicMail WhatsApp] Baileys loaded successfully");
6972
7104
  }
6973
7105
  return true;
6974
7106
  } catch (error) {
6975
7107
  console.warn("[MagicMail WhatsApp] Baileys not installed. WhatsApp features disabled.");
6976
- console.warn("[MagicMail WhatsApp] Install with: npm install @whiskeysockets/baileys pino qrcode");
7108
+ console.warn("[MagicMail WhatsApp] Install with: npm install baileys pino qrcode");
6977
7109
  return false;
6978
7110
  }
6979
7111
  }
@@ -7053,7 +7185,7 @@ function requireWhatsapp() {
7053
7185
  async connect() {
7054
7186
  const available = await loadBaileys();
7055
7187
  if (!available) {
7056
- lastError = "Baileys not installed. Run: npm install @whiskeysockets/baileys pino qrcode";
7188
+ lastError = "Baileys not installed. Run: npm install baileys pino qrcode";
7057
7189
  strapi2.log.error("[MagicMail WhatsApp] [ERROR] Baileys library not available");
7058
7190
  return { success: false, error: lastError };
7059
7191
  }
@@ -7383,6 +7515,116 @@ function requireWhatsapp() {
7383
7515
  };
7384
7516
  return whatsapp;
7385
7517
  }
7518
+ var pluginSettings;
7519
+ var hasRequiredPluginSettings;
7520
+ function requirePluginSettings() {
7521
+ if (hasRequiredPluginSettings) return pluginSettings;
7522
+ hasRequiredPluginSettings = 1;
7523
+ const SETTINGS_UID = "plugin::magic-mail.plugin-settings";
7524
+ pluginSettings = ({ strapi: strapi2 }) => ({
7525
+ /**
7526
+ * Get plugin settings (creates default if not exists)
7527
+ * @returns {Promise<Object>} Plugin settings
7528
+ */
7529
+ async getSettings() {
7530
+ try {
7531
+ let settings = await strapi2.documents(SETTINGS_UID).findFirst({});
7532
+ if (!settings) {
7533
+ settings = await strapi2.documents(SETTINGS_UID).create({
7534
+ data: {
7535
+ enableLinkTracking: true,
7536
+ enableOpenTracking: true,
7537
+ trackingBaseUrl: null,
7538
+ defaultFromName: null,
7539
+ defaultFromEmail: null,
7540
+ unsubscribeUrl: null,
7541
+ enableUnsubscribeHeader: true
7542
+ }
7543
+ });
7544
+ strapi2.log.info("[magic-mail] [SETTINGS] Created default plugin settings");
7545
+ }
7546
+ return settings;
7547
+ } catch (error) {
7548
+ strapi2.log.error("[magic-mail] [SETTINGS] Error getting settings:", error);
7549
+ return {
7550
+ enableLinkTracking: true,
7551
+ enableOpenTracking: true,
7552
+ trackingBaseUrl: null,
7553
+ defaultFromName: null,
7554
+ defaultFromEmail: null,
7555
+ unsubscribeUrl: null,
7556
+ enableUnsubscribeHeader: true
7557
+ };
7558
+ }
7559
+ },
7560
+ /**
7561
+ * Update plugin settings
7562
+ * @param {Object} data - Settings to update
7563
+ * @returns {Promise<Object>} Updated settings
7564
+ */
7565
+ async updateSettings(data) {
7566
+ try {
7567
+ const sanitizedData = {
7568
+ ...data,
7569
+ trackingBaseUrl: data.trackingBaseUrl?.trim() || null,
7570
+ defaultFromName: data.defaultFromName?.trim() || null,
7571
+ defaultFromEmail: data.defaultFromEmail?.trim() || null,
7572
+ unsubscribeUrl: data.unsubscribeUrl?.trim() || null
7573
+ };
7574
+ let settings = await strapi2.documents(SETTINGS_UID).findFirst({});
7575
+ if (settings) {
7576
+ settings = await strapi2.documents(SETTINGS_UID).update({
7577
+ documentId: settings.documentId,
7578
+ data: sanitizedData
7579
+ });
7580
+ strapi2.log.info("[magic-mail] [SETTINGS] Updated plugin settings");
7581
+ } else {
7582
+ settings = await strapi2.documents(SETTINGS_UID).create({
7583
+ data: {
7584
+ enableLinkTracking: sanitizedData.enableLinkTracking ?? true,
7585
+ enableOpenTracking: sanitizedData.enableOpenTracking ?? true,
7586
+ trackingBaseUrl: sanitizedData.trackingBaseUrl,
7587
+ defaultFromName: sanitizedData.defaultFromName,
7588
+ defaultFromEmail: sanitizedData.defaultFromEmail,
7589
+ unsubscribeUrl: sanitizedData.unsubscribeUrl,
7590
+ enableUnsubscribeHeader: sanitizedData.enableUnsubscribeHeader ?? true
7591
+ }
7592
+ });
7593
+ strapi2.log.info("[magic-mail] [SETTINGS] Created plugin settings");
7594
+ }
7595
+ return settings;
7596
+ } catch (error) {
7597
+ strapi2.log.error("[magic-mail] [SETTINGS] Error updating settings:", error);
7598
+ throw error;
7599
+ }
7600
+ },
7601
+ /**
7602
+ * Check if link tracking is enabled
7603
+ * @returns {Promise<boolean>}
7604
+ */
7605
+ async isLinkTrackingEnabled() {
7606
+ const settings = await this.getSettings();
7607
+ return settings.enableLinkTracking !== false;
7608
+ },
7609
+ /**
7610
+ * Check if open tracking is enabled
7611
+ * @returns {Promise<boolean>}
7612
+ */
7613
+ async isOpenTrackingEnabled() {
7614
+ const settings = await this.getSettings();
7615
+ return settings.enableOpenTracking !== false;
7616
+ },
7617
+ /**
7618
+ * Get tracking base URL
7619
+ * @returns {Promise<string|null>}
7620
+ */
7621
+ async getTrackingBaseUrl() {
7622
+ const settings = await this.getSettings();
7623
+ return settings.trackingBaseUrl || null;
7624
+ }
7625
+ });
7626
+ return pluginSettings;
7627
+ }
7386
7628
  var services;
7387
7629
  var hasRequiredServices;
7388
7630
  function requireServices() {
@@ -7395,6 +7637,7 @@ function requireServices() {
7395
7637
  const emailDesigner2 = requireEmailDesigner();
7396
7638
  const analytics2 = requireAnalytics();
7397
7639
  const whatsapp2 = requireWhatsapp();
7640
+ const pluginSettings2 = requirePluginSettings();
7398
7641
  services = {
7399
7642
  "email-router": emailRouter2,
7400
7643
  "account-manager": accountManager2,
@@ -7402,7 +7645,8 @@ function requireServices() {
7402
7645
  "license-guard": licenseGuard2,
7403
7646
  "email-designer": emailDesigner2,
7404
7647
  analytics: analytics2,
7405
- whatsapp: whatsapp2
7648
+ whatsapp: whatsapp2,
7649
+ "plugin-settings": pluginSettings2
7406
7650
  };
7407
7651
  return services;
7408
7652
  }