strapi-plugin-magic-mail 2.3.10 → 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.
@@ -240,13 +240,27 @@ function requireConfig() {
240
240
  };
241
241
  return config;
242
242
  }
243
+ const kind$7 = "collectionType";
244
+ const collectionName$7 = "magic_mail_accounts";
245
+ const info$7 = { "singularName": "email-account", "pluralName": "email-accounts", "displayName": "Email Account", "description": "Email provider accounts for multi-account email sending" };
246
+ const options$7 = { "draftAndPublish": false };
247
+ const pluginOptions$7 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
248
+ 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" } };
249
+ const require$$0 = {
250
+ kind: kind$7,
251
+ collectionName: collectionName$7,
252
+ info: info$7,
253
+ options: options$7,
254
+ pluginOptions: pluginOptions$7,
255
+ attributes: attributes$7
256
+ };
243
257
  const kind$6 = "collectionType";
244
- const collectionName$6 = "magic_mail_accounts";
245
- const info$6 = { "singularName": "email-account", "pluralName": "email-accounts", "displayName": "Email Account", "description": "Email provider accounts for multi-account email sending" };
258
+ const collectionName$6 = "magic_mail_routing_rules";
259
+ const info$6 = { "singularName": "routing-rule", "pluralName": "routing-rules", "displayName": "Email Routing Rule", "description": "Rules for routing emails to specific accounts" };
246
260
  const options$6 = { "draftAndPublish": false };
247
261
  const pluginOptions$6 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
248
- 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" } };
249
- const require$$0 = {
262
+ 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" } };
263
+ const require$$1 = {
250
264
  kind: kind$6,
251
265
  collectionName: collectionName$6,
252
266
  info: info$6,
@@ -255,12 +269,12 @@ const require$$0 = {
255
269
  attributes: attributes$6
256
270
  };
257
271
  const kind$5 = "collectionType";
258
- const collectionName$5 = "magic_mail_routing_rules";
259
- const info$5 = { "singularName": "routing-rule", "pluralName": "routing-rules", "displayName": "Email Routing Rule", "description": "Rules for routing emails to specific accounts" };
272
+ const collectionName$5 = "magic_mail_email_logs";
273
+ const info$5 = { "singularName": "email-log", "pluralName": "email-logs", "displayName": "Email Log", "description": "Tracks all sent emails with user association" };
260
274
  const options$5 = { "draftAndPublish": false };
261
275
  const pluginOptions$5 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
262
- 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" } };
263
- const require$$1 = {
276
+ 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" } };
277
+ const require$$2$1 = {
264
278
  kind: kind$5,
265
279
  collectionName: collectionName$5,
266
280
  info: info$5,
@@ -269,12 +283,12 @@ const require$$1 = {
269
283
  attributes: attributes$5
270
284
  };
271
285
  const kind$4 = "collectionType";
272
- const collectionName$4 = "magic_mail_email_logs";
273
- const info$4 = { "singularName": "email-log", "pluralName": "email-logs", "displayName": "Email Log", "description": "Tracks all sent emails with user association" };
286
+ const collectionName$4 = "magic_mail_email_events";
287
+ const info$4 = { "singularName": "email-event", "pluralName": "email-events", "displayName": "Email Event", "description": "Individual email tracking events (opens, clicks, bounces)" };
274
288
  const options$4 = { "draftAndPublish": false };
275
289
  const pluginOptions$4 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
276
- 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" } };
277
- const require$$2$1 = {
290
+ 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" } };
291
+ const require$$3 = {
278
292
  kind: kind$4,
279
293
  collectionName: collectionName$4,
280
294
  info: info$4,
@@ -283,12 +297,12 @@ const require$$2$1 = {
283
297
  attributes: attributes$4
284
298
  };
285
299
  const kind$3 = "collectionType";
286
- const collectionName$3 = "magic_mail_email_events";
287
- const info$3 = { "singularName": "email-event", "pluralName": "email-events", "displayName": "Email Event", "description": "Individual email tracking events (opens, clicks, bounces)" };
300
+ const collectionName$3 = "magic_mail_email_links";
301
+ const info$3 = { "singularName": "email-link", "pluralName": "email-links", "displayName": "Email Link", "description": "Stores click tracking links for emails" };
288
302
  const options$3 = { "draftAndPublish": false };
289
303
  const pluginOptions$3 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
290
- 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" } };
291
- const require$$3 = {
304
+ 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" } };
305
+ const require$$4 = {
292
306
  kind: kind$3,
293
307
  collectionName: collectionName$3,
294
308
  info: info$3,
@@ -297,12 +311,12 @@ const require$$3 = {
297
311
  attributes: attributes$3
298
312
  };
299
313
  const kind$2 = "collectionType";
300
- const collectionName$2 = "magic_mail_email_links";
301
- const info$2 = { "singularName": "email-link", "pluralName": "email-links", "displayName": "Email Link", "description": "Stores click tracking links for emails" };
302
- const options$2 = { "draftAndPublish": false };
314
+ const collectionName$2 = "magic_mail_email_templates";
315
+ const info$2 = { "singularName": "email-template", "pluralName": "email-templates", "displayName": "Email Template", "description": "Email templates created with the visual designer" };
316
+ const options$2 = { "draftAndPublish": false, "timestamps": true };
303
317
  const pluginOptions$2 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
304
- 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" } };
305
- const require$$4 = {
318
+ 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" } };
319
+ const require$$5 = {
306
320
  kind: kind$2,
307
321
  collectionName: collectionName$2,
308
322
  info: info$2,
@@ -311,12 +325,12 @@ const require$$4 = {
311
325
  attributes: attributes$2
312
326
  };
313
327
  const kind$1 = "collectionType";
314
- const collectionName$1 = "magic_mail_email_templates";
315
- const info$1 = { "singularName": "email-template", "pluralName": "email-templates", "displayName": "Email Template", "description": "Email templates created with the visual designer" };
328
+ const collectionName$1 = "magic_mail_email_template_versions";
329
+ const info$1 = { "singularName": "email-template-version", "pluralName": "email-template-versions", "displayName": "Email Template Version", "description": "Version history for email templates" };
316
330
  const options$1 = { "draftAndPublish": false, "timestamps": true };
317
331
  const pluginOptions$1 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
318
- 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" } };
319
- const require$$5 = {
332
+ 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 } };
333
+ const require$$6 = {
320
334
  kind: kind$1,
321
335
  collectionName: collectionName$1,
322
336
  info: info$1,
@@ -324,13 +338,13 @@ const require$$5 = {
324
338
  pluginOptions: pluginOptions$1,
325
339
  attributes: attributes$1
326
340
  };
327
- const kind = "collectionType";
328
- const collectionName = "magic_mail_email_template_versions";
329
- const info = { "singularName": "email-template-version", "pluralName": "email-template-versions", "displayName": "Email Template Version", "description": "Version history for email templates" };
330
- const options = { "draftAndPublish": false, "timestamps": true };
341
+ const kind = "singleType";
342
+ const collectionName = "magic_mail_settings";
343
+ const info = { "singularName": "plugin-settings", "pluralName": "plugin-settings", "displayName": "MagicMail Settings" };
344
+ const options = { "draftAndPublish": false, "comment": "Global settings for MagicMail plugin" };
331
345
  const pluginOptions = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
332
- 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 } };
333
- const require$$6 = {
346
+ 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 } };
347
+ const require$$7 = {
334
348
  kind,
335
349
  collectionName,
336
350
  info,
@@ -350,6 +364,7 @@ function requireContentTypes() {
350
364
  const emailLink = require$$4;
351
365
  const emailTemplate = require$$5;
352
366
  const emailTemplateVersion = require$$6;
367
+ const pluginSettings2 = require$$7;
353
368
  contentTypes = {
354
369
  "email-account": {
355
370
  schema: emailAccount
@@ -371,6 +386,9 @@ function requireContentTypes() {
371
386
  },
372
387
  "email-template-version": {
373
388
  schema: emailTemplateVersion
389
+ },
390
+ "plugin-settings": {
391
+ schema: pluginSettings2
374
392
  }
375
393
  };
376
394
  return contentTypes;
@@ -2545,7 +2563,7 @@ function requireWhatsapp$1() {
2545
2563
  success: true,
2546
2564
  data: {
2547
2565
  available,
2548
- message: available ? "WhatsApp integration is available" : "Baileys not installed. Run: npm install @whiskeysockets/baileys pino qrcode"
2566
+ message: available ? "WhatsApp integration is available" : "Baileys not installed. Run: npm install baileys pino qrcode"
2549
2567
  }
2550
2568
  };
2551
2569
  } catch (error) {
@@ -2740,6 +2758,62 @@ function requireWhatsapp$1() {
2740
2758
  };
2741
2759
  return whatsapp$1;
2742
2760
  }
2761
+ var pluginSettings$1;
2762
+ var hasRequiredPluginSettings$1;
2763
+ function requirePluginSettings$1() {
2764
+ if (hasRequiredPluginSettings$1) return pluginSettings$1;
2765
+ hasRequiredPluginSettings$1 = 1;
2766
+ pluginSettings$1 = ({ strapi: strapi2 }) => ({
2767
+ /**
2768
+ * GET /magic-mail/settings
2769
+ * Get current plugin settings
2770
+ */
2771
+ async getSettings(ctx) {
2772
+ try {
2773
+ const settings = await strapi2.plugin("magic-mail").service("plugin-settings").getSettings();
2774
+ ctx.body = {
2775
+ data: settings
2776
+ };
2777
+ } catch (error) {
2778
+ strapi2.log.error("[magic-mail] [SETTINGS] Error getting settings:", error);
2779
+ ctx.throw(500, "Failed to get settings");
2780
+ }
2781
+ },
2782
+ /**
2783
+ * PUT /magic-mail/settings
2784
+ * Update plugin settings
2785
+ */
2786
+ async updateSettings(ctx) {
2787
+ try {
2788
+ const { body } = ctx.request;
2789
+ const allowedFields = [
2790
+ "enableLinkTracking",
2791
+ "enableOpenTracking",
2792
+ "trackingBaseUrl",
2793
+ "defaultFromName",
2794
+ "defaultFromEmail",
2795
+ "unsubscribeUrl",
2796
+ "enableUnsubscribeHeader"
2797
+ ];
2798
+ const data = {};
2799
+ for (const field of allowedFields) {
2800
+ if (body[field] !== void 0) {
2801
+ data[field] = body[field];
2802
+ }
2803
+ }
2804
+ const settings = await strapi2.plugin("magic-mail").service("plugin-settings").updateSettings(data);
2805
+ ctx.body = {
2806
+ data: settings,
2807
+ message: "Settings updated successfully"
2808
+ };
2809
+ } catch (error) {
2810
+ strapi2.log.error("[magic-mail] [SETTINGS] Error updating settings:", error);
2811
+ ctx.throw(500, "Failed to update settings");
2812
+ }
2813
+ }
2814
+ });
2815
+ return pluginSettings$1;
2816
+ }
2743
2817
  var controllers;
2744
2818
  var hasRequiredControllers;
2745
2819
  function requireControllers() {
@@ -2754,6 +2828,7 @@ function requireControllers() {
2754
2828
  const analytics2 = requireAnalytics$1();
2755
2829
  const test2 = requireTest();
2756
2830
  const whatsapp2 = requireWhatsapp$1();
2831
+ const pluginSettings2 = requirePluginSettings$1();
2757
2832
  controllers = {
2758
2833
  controller: controller2,
2759
2834
  accounts: accounts2,
@@ -2763,7 +2838,8 @@ function requireControllers() {
2763
2838
  emailDesigner: emailDesigner2,
2764
2839
  analytics: analytics2,
2765
2840
  test: test2,
2766
- whatsapp: whatsapp2
2841
+ whatsapp: whatsapp2,
2842
+ pluginSettings: pluginSettings2
2767
2843
  };
2768
2844
  return controllers;
2769
2845
  }
@@ -3338,6 +3414,25 @@ function requireAdmin() {
3338
3414
  policies: ["admin::isAuthenticatedAdmin"],
3339
3415
  description: "Get WhatsApp session info"
3340
3416
  }
3417
+ },
3418
+ // Plugin Settings Routes
3419
+ {
3420
+ method: "GET",
3421
+ path: "/settings",
3422
+ handler: "pluginSettings.getSettings",
3423
+ config: {
3424
+ policies: ["admin::isAuthenticatedAdmin"],
3425
+ description: "Get plugin settings"
3426
+ }
3427
+ },
3428
+ {
3429
+ method: "PUT",
3430
+ path: "/settings",
3431
+ handler: "pluginSettings.updateSettings",
3432
+ config: {
3433
+ policies: ["admin::isAuthenticatedAdmin"],
3434
+ description: "Update plugin settings"
3435
+ }
3341
3436
  }
3342
3437
  ]
3343
3438
  };
@@ -3614,6 +3709,10 @@ function requireEmailRouter() {
3614
3709
  if (enableTracking && html) {
3615
3710
  try {
3616
3711
  const analyticsService = strapi2.plugin("magic-mail").service("analytics");
3712
+ const settingsService = strapi2.plugin("magic-mail").service("plugin-settings");
3713
+ const pluginSettings2 = await settingsService.getSettings();
3714
+ const globalLinkTrackingEnabled = pluginSettings2.enableLinkTracking !== false;
3715
+ const globalOpenTrackingEnabled = pluginSettings2.enableOpenTracking !== false;
3617
3716
  emailLog = await analyticsService.createEmailLog({
3618
3717
  to,
3619
3718
  userId: emailData.userId || null,
@@ -3632,12 +3731,19 @@ function requireEmailRouter() {
3632
3731
  }
3633
3732
  });
3634
3733
  recipientHash = analyticsService.generateRecipientHash(emailLog.emailId, to);
3635
- html = analyticsService.injectTrackingPixel(html, emailLog.emailId, recipientHash);
3636
- if (!skipLinkTracking) {
3734
+ if (globalOpenTrackingEnabled) {
3735
+ html = analyticsService.injectTrackingPixel(html, emailLog.emailId, recipientHash);
3736
+ } else {
3737
+ strapi2.log.info(`[magic-mail] [STATS] Open tracking DISABLED globally`);
3738
+ }
3739
+ const shouldTrackLinks = globalLinkTrackingEnabled && !skipLinkTracking;
3740
+ if (shouldTrackLinks) {
3637
3741
  html = await analyticsService.rewriteLinksForTracking(html, emailLog.emailId, recipientHash);
3638
- strapi2.log.info(`[magic-mail] [STATS] Full tracking enabled for email: ${emailLog.emailId}`);
3742
+ strapi2.log.info(`[magic-mail] [STATS] Link tracking enabled for email: ${emailLog.emailId}`);
3743
+ } else if (!globalLinkTrackingEnabled) {
3744
+ strapi2.log.info(`[magic-mail] [STATS] Link tracking DISABLED globally for email: ${emailLog.emailId}`);
3639
3745
  } else {
3640
- strapi2.log.info(`[magic-mail] [STATS] Open tracking enabled, link tracking DISABLED for email: ${emailLog.emailId}`);
3746
+ strapi2.log.info(`[magic-mail] [STATS] Link tracking DISABLED per-email (skipLinkTracking=true) for email: ${emailLog.emailId}`);
3641
3747
  }
3642
3748
  } catch (error) {
3643
3749
  strapi2.log.error(`[magic-mail] [WARNING] Tracking setup failed (continuing without tracking):`, error.message);
@@ -3711,7 +3817,7 @@ function requireEmailRouter() {
3711
3817
  await strapi2.documents("plugin::magic-mail.email-log").update({
3712
3818
  documentId: emailLog.documentId,
3713
3819
  data: {
3714
- accountId: account.id,
3820
+ accountId: account.documentId,
3715
3821
  accountName: account.name,
3716
3822
  deliveredAt: /* @__PURE__ */ new Date()
3717
3823
  }
@@ -5533,7 +5639,7 @@ function requireOauth() {
5533
5639
  });
5534
5640
  return oauth;
5535
5641
  }
5536
- const version = "2.3.9";
5642
+ const version = "2.4.0";
5537
5643
  const require$$2 = {
5538
5644
  version
5539
5645
  };
@@ -6817,7 +6923,10 @@ function requireAnalytics() {
6817
6923
  * Rewrite links for click tracking
6818
6924
  */
6819
6925
  async rewriteLinksForTracking(html, emailId, recipientHash) {
6820
- const baseUrl = strapi2.config.get("server.url") || "http://localhost:1337";
6926
+ const settingsService = strapi2.plugin("magic-mail").service("plugin-settings");
6927
+ const pluginSettings2 = await settingsService.getSettings();
6928
+ const baseUrl = pluginSettings2.trackingBaseUrl || strapi2.config.get("server.url") || "http://localhost:1337";
6929
+ strapi2.log.debug(`[magic-mail] [LINK-TRACK] Using base URL: ${baseUrl}`);
6821
6930
  const emailLog = await strapi2.documents(EMAIL_LOG_UID).findFirst({
6822
6931
  filters: { emailId }
6823
6932
  });
@@ -6825,23 +6934,27 @@ function requireAnalytics() {
6825
6934
  strapi2.log.error(`[magic-mail] Cannot rewrite links: Email log not found for ${emailId}`);
6826
6935
  return html;
6827
6936
  }
6828
- const linkRegex = /<a\s+[^>]*href\s*=\s*["']([^"']+)["'][^>]*>/gis;
6937
+ let processedHtml = html.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"');
6938
+ const linkRegex = /<a\s+(?:[^>]*?\s+)?href\s*=\s*["']([^"']+)["'][^>]*>/gis;
6939
+ const simpleLinkRegex = /href\s*=\s*["'](https?:\/\/[^"']+)["']/gi;
6829
6940
  const linkMappings = [];
6830
6941
  const replacements = [];
6942
+ const processedUrls = /* @__PURE__ */ new Set();
6831
6943
  let linkCount = 0;
6832
6944
  let match;
6833
- while ((match = linkRegex.exec(html)) !== null) {
6834
- match[0];
6835
- const originalUrl = match[1];
6945
+ while ((match = linkRegex.exec(processedHtml)) !== null) {
6946
+ const originalUrl = match[1].trim();
6947
+ if (processedUrls.has(originalUrl)) continue;
6836
6948
  strapi2.log.debug(`[magic-mail] [CHECK] Found link: ${originalUrl.substring(0, 100)}${originalUrl.length > 100 ? "..." : ""}`);
6837
- if (originalUrl.startsWith("#") || originalUrl.includes("/track/click/")) {
6838
- strapi2.log.debug(`[magic-mail] [SKIP] Skipping (anchor or already tracked)`);
6949
+ if (originalUrl.startsWith("#") || originalUrl.includes("/track/click/") || originalUrl.startsWith("mailto:") || originalUrl.startsWith("tel:") || originalUrl.startsWith("javascript:")) {
6950
+ strapi2.log.debug(`[magic-mail] [SKIP] Skipping special URL: ${originalUrl.substring(0, 50)}`);
6839
6951
  continue;
6840
6952
  }
6841
- if (!originalUrl.match(/^https?:\/\//i) && !originalUrl.startsWith("/")) {
6842
- strapi2.log.debug(`[magic-mail] [SKIP] Skipping relative URL: ${originalUrl}`);
6953
+ if (!originalUrl.match(/^https?:\/\//i)) {
6954
+ strapi2.log.debug(`[magic-mail] [SKIP] Skipping non-http URL: ${originalUrl.substring(0, 50)}`);
6843
6955
  continue;
6844
6956
  }
6957
+ processedUrls.add(originalUrl);
6845
6958
  const linkHash = crypto.createHash("md5").update(originalUrl).digest("hex").substring(0, 8);
6846
6959
  linkMappings.push({
6847
6960
  linkHash,
@@ -6849,12 +6962,27 @@ function requireAnalytics() {
6849
6962
  });
6850
6963
  const trackingUrl = `${baseUrl}/api/magic-mail/track/click/${emailId}/${linkHash}/${recipientHash}`;
6851
6964
  linkCount++;
6852
- strapi2.log.info(`[magic-mail] [LINK] Link ${linkCount}: ${originalUrl} ${trackingUrl}`);
6965
+ strapi2.log.info(`[magic-mail] [LINK] Link ${linkCount}: ${originalUrl.substring(0, 80)}${originalUrl.length > 80 ? "..." : ""}`);
6853
6966
  replacements.push({
6854
6967
  from: originalUrl,
6855
6968
  to: trackingUrl
6856
6969
  });
6857
6970
  }
6971
+ if (linkCount === 0) {
6972
+ strapi2.log.debug(`[magic-mail] [LINK-TRACK] Primary regex found no links, trying simple pattern...`);
6973
+ while ((match = simpleLinkRegex.exec(processedHtml)) !== null) {
6974
+ const originalUrl = match[1].trim();
6975
+ if (processedUrls.has(originalUrl)) continue;
6976
+ if (originalUrl.includes("/track/click/")) continue;
6977
+ processedUrls.add(originalUrl);
6978
+ const linkHash = crypto.createHash("md5").update(originalUrl).digest("hex").substring(0, 8);
6979
+ linkMappings.push({ linkHash, originalUrl });
6980
+ const trackingUrl = `${baseUrl}/api/magic-mail/track/click/${emailId}/${linkHash}/${recipientHash}`;
6981
+ linkCount++;
6982
+ strapi2.log.info(`[magic-mail] [LINK] Link ${linkCount} (simple): ${originalUrl.substring(0, 80)}`);
6983
+ replacements.push({ from: originalUrl, to: trackingUrl });
6984
+ }
6985
+ }
6858
6986
  for (const mapping of linkMappings) {
6859
6987
  try {
6860
6988
  await this.storeLinkMapping(emailLog.documentId, mapping.linkHash, mapping.originalUrl);
@@ -6865,13 +6993,17 @@ function requireAnalytics() {
6865
6993
  let result = html;
6866
6994
  for (const replacement of replacements) {
6867
6995
  const escapedFrom = replacement.from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6996
+ const escapedFromEncoded = replacement.from.replace(/&/g, "&amp;").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6868
6997
  const hrefRegex = new RegExp(`(href\\s*=\\s*["'])${escapedFrom}(["'])`, "gi");
6998
+ const hrefRegexEncoded = new RegExp(`(href\\s*=\\s*["'])${escapedFromEncoded}(["'])`, "gi");
6869
6999
  result = result.replace(hrefRegex, `$1${replacement.to}$2`);
7000
+ result = result.replace(hrefRegexEncoded, `$1${replacement.to}$2`);
6870
7001
  }
6871
7002
  if (linkCount > 0) {
6872
7003
  strapi2.log.info(`[magic-mail] [SUCCESS] Rewrote ${linkCount} links for click tracking`);
6873
7004
  } else {
6874
- strapi2.log.warn(`[magic-mail] [WARNING] No links found in email HTML for tracking!`);
7005
+ strapi2.log.warn(`[magic-mail] [WARNING] No links found in email HTML for tracking!`);
7006
+ strapi2.log.debug(`[magic-mail] [DEBUG] HTML preview: ${html.substring(0, 500)}...`);
6875
7007
  }
6876
7008
  return result;
6877
7009
  },
@@ -6956,14 +7088,14 @@ function requireWhatsapp() {
6956
7088
  const loadBaileys = async () => {
6957
7089
  if (!baileys) {
6958
7090
  try {
6959
- baileys = require("@whiskeysockets/baileys");
7091
+ baileys = require("baileys");
6960
7092
  if (process.env.DEBUG) {
6961
7093
  console.log("[MagicMail WhatsApp] Baileys loaded successfully");
6962
7094
  }
6963
7095
  return true;
6964
7096
  } catch (error) {
6965
7097
  console.warn("[MagicMail WhatsApp] Baileys not installed. WhatsApp features disabled.");
6966
- console.warn("[MagicMail WhatsApp] Install with: npm install @whiskeysockets/baileys pino qrcode");
7098
+ console.warn("[MagicMail WhatsApp] Install with: npm install baileys pino qrcode");
6967
7099
  return false;
6968
7100
  }
6969
7101
  }
@@ -7043,7 +7175,7 @@ function requireWhatsapp() {
7043
7175
  async connect() {
7044
7176
  const available = await loadBaileys();
7045
7177
  if (!available) {
7046
- lastError = "Baileys not installed. Run: npm install @whiskeysockets/baileys pino qrcode";
7178
+ lastError = "Baileys not installed. Run: npm install baileys pino qrcode";
7047
7179
  strapi2.log.error("[MagicMail WhatsApp] [ERROR] Baileys library not available");
7048
7180
  return { success: false, error: lastError };
7049
7181
  }
@@ -7373,6 +7505,116 @@ function requireWhatsapp() {
7373
7505
  };
7374
7506
  return whatsapp;
7375
7507
  }
7508
+ var pluginSettings;
7509
+ var hasRequiredPluginSettings;
7510
+ function requirePluginSettings() {
7511
+ if (hasRequiredPluginSettings) return pluginSettings;
7512
+ hasRequiredPluginSettings = 1;
7513
+ const SETTINGS_UID = "plugin::magic-mail.plugin-settings";
7514
+ pluginSettings = ({ strapi: strapi2 }) => ({
7515
+ /**
7516
+ * Get plugin settings (creates default if not exists)
7517
+ * @returns {Promise<Object>} Plugin settings
7518
+ */
7519
+ async getSettings() {
7520
+ try {
7521
+ let settings = await strapi2.documents(SETTINGS_UID).findFirst({});
7522
+ if (!settings) {
7523
+ settings = await strapi2.documents(SETTINGS_UID).create({
7524
+ data: {
7525
+ enableLinkTracking: true,
7526
+ enableOpenTracking: true,
7527
+ trackingBaseUrl: null,
7528
+ defaultFromName: null,
7529
+ defaultFromEmail: null,
7530
+ unsubscribeUrl: null,
7531
+ enableUnsubscribeHeader: true
7532
+ }
7533
+ });
7534
+ strapi2.log.info("[magic-mail] [SETTINGS] Created default plugin settings");
7535
+ }
7536
+ return settings;
7537
+ } catch (error) {
7538
+ strapi2.log.error("[magic-mail] [SETTINGS] Error getting settings:", error);
7539
+ return {
7540
+ enableLinkTracking: true,
7541
+ enableOpenTracking: true,
7542
+ trackingBaseUrl: null,
7543
+ defaultFromName: null,
7544
+ defaultFromEmail: null,
7545
+ unsubscribeUrl: null,
7546
+ enableUnsubscribeHeader: true
7547
+ };
7548
+ }
7549
+ },
7550
+ /**
7551
+ * Update plugin settings
7552
+ * @param {Object} data - Settings to update
7553
+ * @returns {Promise<Object>} Updated settings
7554
+ */
7555
+ async updateSettings(data) {
7556
+ try {
7557
+ const sanitizedData = {
7558
+ ...data,
7559
+ trackingBaseUrl: data.trackingBaseUrl?.trim() || null,
7560
+ defaultFromName: data.defaultFromName?.trim() || null,
7561
+ defaultFromEmail: data.defaultFromEmail?.trim() || null,
7562
+ unsubscribeUrl: data.unsubscribeUrl?.trim() || null
7563
+ };
7564
+ let settings = await strapi2.documents(SETTINGS_UID).findFirst({});
7565
+ if (settings) {
7566
+ settings = await strapi2.documents(SETTINGS_UID).update({
7567
+ documentId: settings.documentId,
7568
+ data: sanitizedData
7569
+ });
7570
+ strapi2.log.info("[magic-mail] [SETTINGS] Updated plugin settings");
7571
+ } else {
7572
+ settings = await strapi2.documents(SETTINGS_UID).create({
7573
+ data: {
7574
+ enableLinkTracking: sanitizedData.enableLinkTracking ?? true,
7575
+ enableOpenTracking: sanitizedData.enableOpenTracking ?? true,
7576
+ trackingBaseUrl: sanitizedData.trackingBaseUrl,
7577
+ defaultFromName: sanitizedData.defaultFromName,
7578
+ defaultFromEmail: sanitizedData.defaultFromEmail,
7579
+ unsubscribeUrl: sanitizedData.unsubscribeUrl,
7580
+ enableUnsubscribeHeader: sanitizedData.enableUnsubscribeHeader ?? true
7581
+ }
7582
+ });
7583
+ strapi2.log.info("[magic-mail] [SETTINGS] Created plugin settings");
7584
+ }
7585
+ return settings;
7586
+ } catch (error) {
7587
+ strapi2.log.error("[magic-mail] [SETTINGS] Error updating settings:", error);
7588
+ throw error;
7589
+ }
7590
+ },
7591
+ /**
7592
+ * Check if link tracking is enabled
7593
+ * @returns {Promise<boolean>}
7594
+ */
7595
+ async isLinkTrackingEnabled() {
7596
+ const settings = await this.getSettings();
7597
+ return settings.enableLinkTracking !== false;
7598
+ },
7599
+ /**
7600
+ * Check if open tracking is enabled
7601
+ * @returns {Promise<boolean>}
7602
+ */
7603
+ async isOpenTrackingEnabled() {
7604
+ const settings = await this.getSettings();
7605
+ return settings.enableOpenTracking !== false;
7606
+ },
7607
+ /**
7608
+ * Get tracking base URL
7609
+ * @returns {Promise<string|null>}
7610
+ */
7611
+ async getTrackingBaseUrl() {
7612
+ const settings = await this.getSettings();
7613
+ return settings.trackingBaseUrl || null;
7614
+ }
7615
+ });
7616
+ return pluginSettings;
7617
+ }
7376
7618
  var services;
7377
7619
  var hasRequiredServices;
7378
7620
  function requireServices() {
@@ -7385,6 +7627,7 @@ function requireServices() {
7385
7627
  const emailDesigner2 = requireEmailDesigner();
7386
7628
  const analytics2 = requireAnalytics();
7387
7629
  const whatsapp2 = requireWhatsapp();
7630
+ const pluginSettings2 = requirePluginSettings();
7388
7631
  services = {
7389
7632
  "email-router": emailRouter2,
7390
7633
  "account-manager": accountManager2,
@@ -7392,7 +7635,8 @@ function requireServices() {
7392
7635
  "license-guard": licenseGuard2,
7393
7636
  "email-designer": emailDesigner2,
7394
7637
  analytics: analytics2,
7395
- whatsapp: whatsapp2
7638
+ whatsapp: whatsapp2,
7639
+ "plugin-settings": pluginSettings2
7396
7640
  };
7397
7641
  return services;
7398
7642
  }