@blackcode_sa/metaestetics-api 1.5.49 → 1.5.51

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.
@@ -1506,18 +1506,29 @@ declare class PatientAggregationService {
1506
1506
  cancelUpcomingCalendarEventsForPatient(patientId: string): Promise<void>;
1507
1507
  }
1508
1508
 
1509
+ /**
1510
+ * Mailgun client type for compatibility with new mailgun.js library
1511
+ * This allows us to accept both older mailgun-js and newer mailgun.js clients
1512
+ */
1513
+ interface MailgunClientCompat {
1514
+ messages: {
1515
+ create?: (domain: string, data: any) => Promise<any>;
1516
+ send?: (data: any, callback: (error: any, body: any) => void) => void;
1517
+ };
1518
+ }
1509
1519
  /**
1510
1520
  * Base mailing service class that provides common functionality for all mailing services
1511
1521
  */
1512
1522
  declare class BaseMailingService {
1513
1523
  protected db: FirebaseFirestore.Firestore;
1514
- protected mailgunClient: mailgun.Mailgun;
1524
+ protected mailgunClient: mailgun.Mailgun | MailgunClientCompat;
1525
+ protected isNewMailgunClient: boolean;
1515
1526
  /**
1516
1527
  * Constructor for BaseMailingService
1517
1528
  * @param firestore Firestore instance provided by the caller
1518
1529
  * @param mailgunClient Mailgun client instance provided by the caller
1519
1530
  */
1520
- constructor(firestore: FirebaseFirestore.Firestore, mailgunClient: mailgun.Mailgun);
1531
+ constructor(firestore: FirebaseFirestore.Firestore, mailgunClient: mailgun.Mailgun | MailgunClientCompat);
1521
1532
  /**
1522
1533
  * Validates that the Mailgun client is configured correctly
1523
1534
  * Particularly checks for EU region configuration
@@ -1528,7 +1539,18 @@ declare class BaseMailingService {
1528
1539
  * @param data Email data to send, including the 'from' address
1529
1540
  * @returns Promise with the sending result
1530
1541
  */
1531
- protected sendEmail(data: mailgun.messages.SendData): Promise<mailgun.messages.SendResponse>;
1542
+ protected sendEmail(data: mailgun.messages.SendData): Promise<any>;
1543
+ /**
1544
+ * Tries to get domain from mailgun client options
1545
+ * @returns Domain string or undefined
1546
+ */
1547
+ private getDomainFromOptions;
1548
+ /**
1549
+ * Extracts domain from an email address
1550
+ * @param email Email address
1551
+ * @returns Domain part or undefined
1552
+ */
1553
+ private getDomainFromEmail;
1532
1554
  /**
1533
1555
  * Logs email sending attempt to Firestore for tracking
1534
1556
  * @param emailData Email data that was sent
@@ -1506,18 +1506,29 @@ declare class PatientAggregationService {
1506
1506
  cancelUpcomingCalendarEventsForPatient(patientId: string): Promise<void>;
1507
1507
  }
1508
1508
 
1509
+ /**
1510
+ * Mailgun client type for compatibility with new mailgun.js library
1511
+ * This allows us to accept both older mailgun-js and newer mailgun.js clients
1512
+ */
1513
+ interface MailgunClientCompat {
1514
+ messages: {
1515
+ create?: (domain: string, data: any) => Promise<any>;
1516
+ send?: (data: any, callback: (error: any, body: any) => void) => void;
1517
+ };
1518
+ }
1509
1519
  /**
1510
1520
  * Base mailing service class that provides common functionality for all mailing services
1511
1521
  */
1512
1522
  declare class BaseMailingService {
1513
1523
  protected db: FirebaseFirestore.Firestore;
1514
- protected mailgunClient: mailgun.Mailgun;
1524
+ protected mailgunClient: mailgun.Mailgun | MailgunClientCompat;
1525
+ protected isNewMailgunClient: boolean;
1515
1526
  /**
1516
1527
  * Constructor for BaseMailingService
1517
1528
  * @param firestore Firestore instance provided by the caller
1518
1529
  * @param mailgunClient Mailgun client instance provided by the caller
1519
1530
  */
1520
- constructor(firestore: FirebaseFirestore.Firestore, mailgunClient: mailgun.Mailgun);
1531
+ constructor(firestore: FirebaseFirestore.Firestore, mailgunClient: mailgun.Mailgun | MailgunClientCompat);
1521
1532
  /**
1522
1533
  * Validates that the Mailgun client is configured correctly
1523
1534
  * Particularly checks for EU region configuration
@@ -1528,7 +1539,18 @@ declare class BaseMailingService {
1528
1539
  * @param data Email data to send, including the 'from' address
1529
1540
  * @returns Promise with the sending result
1530
1541
  */
1531
- protected sendEmail(data: mailgun.messages.SendData): Promise<mailgun.messages.SendResponse>;
1542
+ protected sendEmail(data: mailgun.messages.SendData): Promise<any>;
1543
+ /**
1544
+ * Tries to get domain from mailgun client options
1545
+ * @returns Domain string or undefined
1546
+ */
1547
+ private getDomainFromOptions;
1548
+ /**
1549
+ * Extracts domain from an email address
1550
+ * @param email Email address
1551
+ * @returns Domain part or undefined
1552
+ */
1553
+ private getDomainFromEmail;
1532
1554
  /**
1533
1555
  * Logs email sending attempt to Firestore for tracking
1534
1556
  * @param emailData Email data that was sent
@@ -1640,6 +1640,7 @@ var BaseMailingService = class {
1640
1640
  * @param mailgunClient Mailgun client instance provided by the caller
1641
1641
  */
1642
1642
  constructor(firestore8, mailgunClient) {
1643
+ this.isNewMailgunClient = false;
1643
1644
  this.db = firestore8;
1644
1645
  this.mailgunClient = mailgunClient;
1645
1646
  if (!this.db) {
@@ -1650,6 +1651,10 @@ var BaseMailingService = class {
1650
1651
  Logger.error("[BaseMailingService] No Mailgun client provided");
1651
1652
  throw new Error("Mailgun client is required");
1652
1653
  }
1654
+ this.isNewMailgunClient = typeof this.mailgunClient.messages.create === "function";
1655
+ Logger.info(
1656
+ `[BaseMailingService] Using ${this.isNewMailgunClient ? "new" : "legacy"} Mailgun client`
1657
+ );
1653
1658
  this.validateMailgunClient();
1654
1659
  Logger.info("[BaseMailingService] Service initialized successfully");
1655
1660
  }
@@ -1659,18 +1664,25 @@ var BaseMailingService = class {
1659
1664
  */
1660
1665
  validateMailgunClient() {
1661
1666
  try {
1662
- const clientOptions = this.mailgunClient.options || {};
1663
- const host = clientOptions.host || "";
1664
- const isEuRegion = host.includes("eu.mailgun.net");
1665
- Logger.info("[BaseMailingService] Mailgun client configuration:", {
1666
- host: host || "default",
1667
- isEuRegion,
1668
- domain: clientOptions.domain || "unknown"
1669
- });
1670
- if (clientOptions.domain && clientOptions.domain.endsWith(".eu") && !isEuRegion) {
1671
- Logger.warn(
1672
- "[BaseMailingService] Domain appears to be in EU region but not using EU endpoint. If you're getting 401 Forbidden errors, ensure the host is set to 'api.eu.mailgun.net'"
1673
- );
1667
+ if (!this.isNewMailgunClient) {
1668
+ const clientOptions = this.mailgunClient.options || {};
1669
+ const host = clientOptions.host || "";
1670
+ const isEuRegion = host.includes("eu.mailgun.net");
1671
+ Logger.info("[BaseMailingService] Mailgun client configuration:", {
1672
+ host: host || "default",
1673
+ isEuRegion,
1674
+ domain: clientOptions.domain || "unknown",
1675
+ clientType: "legacy"
1676
+ });
1677
+ if (clientOptions.domain && clientOptions.domain.endsWith(".eu") && !isEuRegion) {
1678
+ Logger.warn(
1679
+ "[BaseMailingService] Domain appears to be in EU region but not using EU endpoint. If you're getting 401 Forbidden errors, ensure the host is set to 'api.eu.mailgun.net'"
1680
+ );
1681
+ }
1682
+ } else {
1683
+ Logger.info("[BaseMailingService] Using new Mailgun client", {
1684
+ clientType: "mailgun.js"
1685
+ });
1674
1686
  }
1675
1687
  } catch (error) {
1676
1688
  Logger.warn("[BaseMailingService] Could not validate Mailgun client:", {
@@ -1707,73 +1719,93 @@ var BaseMailingService = class {
1707
1719
  from: data.from,
1708
1720
  subject: data.subject,
1709
1721
  hasHtml: !!data.html,
1710
- hasText: !!data.text
1722
+ hasText: !!data.text,
1723
+ clientType: this.isNewMailgunClient ? "mailgun.js" : "legacy"
1711
1724
  });
1712
- return await new Promise(
1713
- (resolve, reject) => {
1714
- try {
1715
- const messagesApi = this.mailgunClient.messages();
1716
- if (!messagesApi) {
1717
- throw new Error("Could not get Mailgun messages API");
1725
+ if (this.isNewMailgunClient) {
1726
+ try {
1727
+ const createMethod = this.mailgunClient.messages.create;
1728
+ const domain = this.getDomainFromOptions() || this.getDomainFromEmail(data.from);
1729
+ if (!domain) {
1730
+ throw new Error("Could not determine domain for Mailgun API call");
1731
+ }
1732
+ Logger.info(
1733
+ "[BaseMailingService] Using domain for new Mailgun client:",
1734
+ { domain }
1735
+ );
1736
+ const result = await createMethod(domain, data);
1737
+ Logger.info(
1738
+ "[BaseMailingService] Email sent successfully with new client"
1739
+ );
1740
+ return result;
1741
+ } catch (error) {
1742
+ Logger.error(
1743
+ "[BaseMailingService] Error sending with new Mailgun client:",
1744
+ {
1745
+ error: error instanceof Error ? error.message : String(error),
1746
+ stack: error instanceof Error ? error.stack : void 0
1718
1747
  }
1719
- messagesApi.send(data, (error, body) => {
1720
- var _a, _b, _c;
1721
- if (error) {
1722
- if (error.statusCode === 401 || error.statusCode === 403) {
1723
- const clientOptions = this.mailgunClient.options || {};
1724
- Logger.error(
1725
- "[BaseMailingService] Mailgun authentication error - possible region mismatch:",
1726
- {
1748
+ );
1749
+ throw error;
1750
+ }
1751
+ } else {
1752
+ return await new Promise(
1753
+ (resolve, reject) => {
1754
+ try {
1755
+ const messagesApi = this.mailgunClient.messages;
1756
+ if (!messagesApi || !messagesApi.send) {
1757
+ throw new Error("Could not get Mailgun messages API");
1758
+ }
1759
+ messagesApi.send(data, (error, body) => {
1760
+ var _a, _b, _c;
1761
+ if (error) {
1762
+ if (error.statusCode === 401 || error.statusCode === 403) {
1763
+ const clientOptions = this.mailgunClient.options || {};
1764
+ Logger.error(
1765
+ "[BaseMailingService] Mailgun authentication error - possible region mismatch:",
1766
+ {
1767
+ error: error instanceof Error ? error.message : error,
1768
+ statusCode: error.statusCode,
1769
+ domain: clientOptions.domain || "unknown",
1770
+ host: clientOptions.host || "default api.mailgun.net",
1771
+ suggestion: "If using EU region domains, ensure host is set to 'api.eu.mailgun.net'",
1772
+ response: error.response ? JSON.stringify(error.response) : "No response details",
1773
+ request: error.request ? {
1774
+ method: (_a = error.request) == null ? void 0 : _a.method,
1775
+ path: (_b = error.request) == null ? void 0 : _b.path,
1776
+ host: (_c = error.request) == null ? void 0 : _c.host
1777
+ } : "No request details"
1778
+ }
1779
+ );
1780
+ } else {
1781
+ Logger.error("[BaseMailingService] Mailgun API error:", {
1727
1782
  error: error instanceof Error ? error.message : error,
1728
1783
  statusCode: error.statusCode,
1729
- domain: clientOptions.domain || "unknown",
1730
- host: clientOptions.host || "default api.mailgun.net",
1731
- suggestion: "If using EU region domains, ensure host is set to 'api.eu.mailgun.net'",
1732
- response: error.response ? JSON.stringify(error.response) : "No response details",
1733
- request: error.request ? {
1734
- method: (_a = error.request) == null ? void 0 : _a.method,
1735
- path: (_b = error.request) == null ? void 0 : _b.path,
1736
- host: (_c = error.request) == null ? void 0 : _c.host
1737
- } : "No request details"
1738
- }
1739
- );
1740
- const enhancedError = new Error(
1741
- `Mailgun Authentication Error (${error.statusCode}): Possible EU region misconfiguration. Set host to 'api.eu.mailgun.net' for EU domains.`
1742
- );
1743
- enhancedError.originalError = error;
1744
- enhancedError.mailgunConfig = {
1745
- domain: clientOptions.domain,
1746
- host: clientOptions.host
1747
- };
1748
- reject(enhancedError);
1784
+ stack: error instanceof Error ? error.stack : void 0
1785
+ });
1786
+ }
1787
+ reject(error);
1749
1788
  } else {
1750
- Logger.error("[BaseMailingService] Mailgun API error:", {
1751
- error: error instanceof Error ? error.message : error,
1752
- statusCode: error.statusCode,
1753
- stack: error instanceof Error ? error.stack : void 0
1754
- });
1789
+ Logger.info(
1790
+ "[BaseMailingService] Email sent successfully:",
1791
+ body
1792
+ );
1793
+ resolve(body);
1755
1794
  }
1756
- reject(error);
1757
- } else {
1758
- Logger.info(
1759
- "[BaseMailingService] Email sent successfully:",
1760
- body
1761
- );
1762
- resolve(body);
1763
- }
1764
- });
1765
- } catch (sendError) {
1766
- Logger.error(
1767
- "[BaseMailingService] Error in mailgun.messages().send():",
1768
- {
1769
- error: sendError instanceof Error ? sendError.message : sendError,
1770
- stack: sendError instanceof Error ? sendError.stack : void 0
1771
- }
1772
- );
1773
- reject(sendError);
1795
+ });
1796
+ } catch (sendError) {
1797
+ Logger.error(
1798
+ "[BaseMailingService] Error in mailgun.messages().send():",
1799
+ {
1800
+ error: sendError instanceof Error ? sendError.message : sendError,
1801
+ stack: sendError instanceof Error ? sendError.stack : void 0
1802
+ }
1803
+ );
1804
+ reject(sendError);
1805
+ }
1774
1806
  }
1775
- }
1776
- );
1807
+ );
1808
+ }
1777
1809
  } catch (error) {
1778
1810
  Logger.error("[BaseMailingService] Error in sendEmail:", {
1779
1811
  error: error instanceof Error ? error.message : error,
@@ -1782,6 +1814,40 @@ var BaseMailingService = class {
1782
1814
  throw error;
1783
1815
  }
1784
1816
  }
1817
+ /**
1818
+ * Tries to get domain from mailgun client options
1819
+ * @returns Domain string or undefined
1820
+ */
1821
+ getDomainFromOptions() {
1822
+ try {
1823
+ if (this.isNewMailgunClient) {
1824
+ return this.mailgunClient.domain;
1825
+ } else {
1826
+ const options = this.mailgunClient.options;
1827
+ return options == null ? void 0 : options.domain;
1828
+ }
1829
+ } catch (e) {
1830
+ return void 0;
1831
+ }
1832
+ }
1833
+ /**
1834
+ * Extracts domain from an email address
1835
+ * @param email Email address
1836
+ * @returns Domain part or undefined
1837
+ */
1838
+ getDomainFromEmail(email) {
1839
+ try {
1840
+ const match = email.match(/<([^>]+)>/) || [null, email];
1841
+ const emailPart = match[1];
1842
+ const domainMatch = emailPart.match(/@([^@]+)$/);
1843
+ if (domainMatch && domainMatch[1]) {
1844
+ return domainMatch[1];
1845
+ }
1846
+ return void 0;
1847
+ } catch (e) {
1848
+ return void 0;
1849
+ }
1850
+ }
1785
1851
  /**
1786
1852
  * Logs email sending attempt to Firestore for tracking
1787
1853
  * @param emailData Email data that was sent
@@ -1948,7 +2014,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
1948
2014
  super(firestore8, mailgunClient);
1949
2015
  this.DEFAULT_REGISTRATION_URL = "https://metaestetics.net/register";
1950
2016
  this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
1951
- this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaestetics.net>";
2017
+ this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaesthetics.net>";
1952
2018
  }
1953
2019
  /**
1954
2020
  * Sends a practitioner invitation email
@@ -1599,6 +1599,7 @@ var BaseMailingService = class {
1599
1599
  * @param mailgunClient Mailgun client instance provided by the caller
1600
1600
  */
1601
1601
  constructor(firestore8, mailgunClient) {
1602
+ this.isNewMailgunClient = false;
1602
1603
  this.db = firestore8;
1603
1604
  this.mailgunClient = mailgunClient;
1604
1605
  if (!this.db) {
@@ -1609,6 +1610,10 @@ var BaseMailingService = class {
1609
1610
  Logger.error("[BaseMailingService] No Mailgun client provided");
1610
1611
  throw new Error("Mailgun client is required");
1611
1612
  }
1613
+ this.isNewMailgunClient = typeof this.mailgunClient.messages.create === "function";
1614
+ Logger.info(
1615
+ `[BaseMailingService] Using ${this.isNewMailgunClient ? "new" : "legacy"} Mailgun client`
1616
+ );
1612
1617
  this.validateMailgunClient();
1613
1618
  Logger.info("[BaseMailingService] Service initialized successfully");
1614
1619
  }
@@ -1618,18 +1623,25 @@ var BaseMailingService = class {
1618
1623
  */
1619
1624
  validateMailgunClient() {
1620
1625
  try {
1621
- const clientOptions = this.mailgunClient.options || {};
1622
- const host = clientOptions.host || "";
1623
- const isEuRegion = host.includes("eu.mailgun.net");
1624
- Logger.info("[BaseMailingService] Mailgun client configuration:", {
1625
- host: host || "default",
1626
- isEuRegion,
1627
- domain: clientOptions.domain || "unknown"
1628
- });
1629
- if (clientOptions.domain && clientOptions.domain.endsWith(".eu") && !isEuRegion) {
1630
- Logger.warn(
1631
- "[BaseMailingService] Domain appears to be in EU region but not using EU endpoint. If you're getting 401 Forbidden errors, ensure the host is set to 'api.eu.mailgun.net'"
1632
- );
1626
+ if (!this.isNewMailgunClient) {
1627
+ const clientOptions = this.mailgunClient.options || {};
1628
+ const host = clientOptions.host || "";
1629
+ const isEuRegion = host.includes("eu.mailgun.net");
1630
+ Logger.info("[BaseMailingService] Mailgun client configuration:", {
1631
+ host: host || "default",
1632
+ isEuRegion,
1633
+ domain: clientOptions.domain || "unknown",
1634
+ clientType: "legacy"
1635
+ });
1636
+ if (clientOptions.domain && clientOptions.domain.endsWith(".eu") && !isEuRegion) {
1637
+ Logger.warn(
1638
+ "[BaseMailingService] Domain appears to be in EU region but not using EU endpoint. If you're getting 401 Forbidden errors, ensure the host is set to 'api.eu.mailgun.net'"
1639
+ );
1640
+ }
1641
+ } else {
1642
+ Logger.info("[BaseMailingService] Using new Mailgun client", {
1643
+ clientType: "mailgun.js"
1644
+ });
1633
1645
  }
1634
1646
  } catch (error) {
1635
1647
  Logger.warn("[BaseMailingService] Could not validate Mailgun client:", {
@@ -1666,73 +1678,93 @@ var BaseMailingService = class {
1666
1678
  from: data.from,
1667
1679
  subject: data.subject,
1668
1680
  hasHtml: !!data.html,
1669
- hasText: !!data.text
1681
+ hasText: !!data.text,
1682
+ clientType: this.isNewMailgunClient ? "mailgun.js" : "legacy"
1670
1683
  });
1671
- return await new Promise(
1672
- (resolve, reject) => {
1673
- try {
1674
- const messagesApi = this.mailgunClient.messages();
1675
- if (!messagesApi) {
1676
- throw new Error("Could not get Mailgun messages API");
1684
+ if (this.isNewMailgunClient) {
1685
+ try {
1686
+ const createMethod = this.mailgunClient.messages.create;
1687
+ const domain = this.getDomainFromOptions() || this.getDomainFromEmail(data.from);
1688
+ if (!domain) {
1689
+ throw new Error("Could not determine domain for Mailgun API call");
1690
+ }
1691
+ Logger.info(
1692
+ "[BaseMailingService] Using domain for new Mailgun client:",
1693
+ { domain }
1694
+ );
1695
+ const result = await createMethod(domain, data);
1696
+ Logger.info(
1697
+ "[BaseMailingService] Email sent successfully with new client"
1698
+ );
1699
+ return result;
1700
+ } catch (error) {
1701
+ Logger.error(
1702
+ "[BaseMailingService] Error sending with new Mailgun client:",
1703
+ {
1704
+ error: error instanceof Error ? error.message : String(error),
1705
+ stack: error instanceof Error ? error.stack : void 0
1677
1706
  }
1678
- messagesApi.send(data, (error, body) => {
1679
- var _a, _b, _c;
1680
- if (error) {
1681
- if (error.statusCode === 401 || error.statusCode === 403) {
1682
- const clientOptions = this.mailgunClient.options || {};
1683
- Logger.error(
1684
- "[BaseMailingService] Mailgun authentication error - possible region mismatch:",
1685
- {
1707
+ );
1708
+ throw error;
1709
+ }
1710
+ } else {
1711
+ return await new Promise(
1712
+ (resolve, reject) => {
1713
+ try {
1714
+ const messagesApi = this.mailgunClient.messages;
1715
+ if (!messagesApi || !messagesApi.send) {
1716
+ throw new Error("Could not get Mailgun messages API");
1717
+ }
1718
+ messagesApi.send(data, (error, body) => {
1719
+ var _a, _b, _c;
1720
+ if (error) {
1721
+ if (error.statusCode === 401 || error.statusCode === 403) {
1722
+ const clientOptions = this.mailgunClient.options || {};
1723
+ Logger.error(
1724
+ "[BaseMailingService] Mailgun authentication error - possible region mismatch:",
1725
+ {
1726
+ error: error instanceof Error ? error.message : error,
1727
+ statusCode: error.statusCode,
1728
+ domain: clientOptions.domain || "unknown",
1729
+ host: clientOptions.host || "default api.mailgun.net",
1730
+ suggestion: "If using EU region domains, ensure host is set to 'api.eu.mailgun.net'",
1731
+ response: error.response ? JSON.stringify(error.response) : "No response details",
1732
+ request: error.request ? {
1733
+ method: (_a = error.request) == null ? void 0 : _a.method,
1734
+ path: (_b = error.request) == null ? void 0 : _b.path,
1735
+ host: (_c = error.request) == null ? void 0 : _c.host
1736
+ } : "No request details"
1737
+ }
1738
+ );
1739
+ } else {
1740
+ Logger.error("[BaseMailingService] Mailgun API error:", {
1686
1741
  error: error instanceof Error ? error.message : error,
1687
1742
  statusCode: error.statusCode,
1688
- domain: clientOptions.domain || "unknown",
1689
- host: clientOptions.host || "default api.mailgun.net",
1690
- suggestion: "If using EU region domains, ensure host is set to 'api.eu.mailgun.net'",
1691
- response: error.response ? JSON.stringify(error.response) : "No response details",
1692
- request: error.request ? {
1693
- method: (_a = error.request) == null ? void 0 : _a.method,
1694
- path: (_b = error.request) == null ? void 0 : _b.path,
1695
- host: (_c = error.request) == null ? void 0 : _c.host
1696
- } : "No request details"
1697
- }
1698
- );
1699
- const enhancedError = new Error(
1700
- `Mailgun Authentication Error (${error.statusCode}): Possible EU region misconfiguration. Set host to 'api.eu.mailgun.net' for EU domains.`
1701
- );
1702
- enhancedError.originalError = error;
1703
- enhancedError.mailgunConfig = {
1704
- domain: clientOptions.domain,
1705
- host: clientOptions.host
1706
- };
1707
- reject(enhancedError);
1743
+ stack: error instanceof Error ? error.stack : void 0
1744
+ });
1745
+ }
1746
+ reject(error);
1708
1747
  } else {
1709
- Logger.error("[BaseMailingService] Mailgun API error:", {
1710
- error: error instanceof Error ? error.message : error,
1711
- statusCode: error.statusCode,
1712
- stack: error instanceof Error ? error.stack : void 0
1713
- });
1748
+ Logger.info(
1749
+ "[BaseMailingService] Email sent successfully:",
1750
+ body
1751
+ );
1752
+ resolve(body);
1714
1753
  }
1715
- reject(error);
1716
- } else {
1717
- Logger.info(
1718
- "[BaseMailingService] Email sent successfully:",
1719
- body
1720
- );
1721
- resolve(body);
1722
- }
1723
- });
1724
- } catch (sendError) {
1725
- Logger.error(
1726
- "[BaseMailingService] Error in mailgun.messages().send():",
1727
- {
1728
- error: sendError instanceof Error ? sendError.message : sendError,
1729
- stack: sendError instanceof Error ? sendError.stack : void 0
1730
- }
1731
- );
1732
- reject(sendError);
1754
+ });
1755
+ } catch (sendError) {
1756
+ Logger.error(
1757
+ "[BaseMailingService] Error in mailgun.messages().send():",
1758
+ {
1759
+ error: sendError instanceof Error ? sendError.message : sendError,
1760
+ stack: sendError instanceof Error ? sendError.stack : void 0
1761
+ }
1762
+ );
1763
+ reject(sendError);
1764
+ }
1733
1765
  }
1734
- }
1735
- );
1766
+ );
1767
+ }
1736
1768
  } catch (error) {
1737
1769
  Logger.error("[BaseMailingService] Error in sendEmail:", {
1738
1770
  error: error instanceof Error ? error.message : error,
@@ -1741,6 +1773,40 @@ var BaseMailingService = class {
1741
1773
  throw error;
1742
1774
  }
1743
1775
  }
1776
+ /**
1777
+ * Tries to get domain from mailgun client options
1778
+ * @returns Domain string or undefined
1779
+ */
1780
+ getDomainFromOptions() {
1781
+ try {
1782
+ if (this.isNewMailgunClient) {
1783
+ return this.mailgunClient.domain;
1784
+ } else {
1785
+ const options = this.mailgunClient.options;
1786
+ return options == null ? void 0 : options.domain;
1787
+ }
1788
+ } catch (e) {
1789
+ return void 0;
1790
+ }
1791
+ }
1792
+ /**
1793
+ * Extracts domain from an email address
1794
+ * @param email Email address
1795
+ * @returns Domain part or undefined
1796
+ */
1797
+ getDomainFromEmail(email) {
1798
+ try {
1799
+ const match = email.match(/<([^>]+)>/) || [null, email];
1800
+ const emailPart = match[1];
1801
+ const domainMatch = emailPart.match(/@([^@]+)$/);
1802
+ if (domainMatch && domainMatch[1]) {
1803
+ return domainMatch[1];
1804
+ }
1805
+ return void 0;
1806
+ } catch (e) {
1807
+ return void 0;
1808
+ }
1809
+ }
1744
1810
  /**
1745
1811
  * Logs email sending attempt to Firestore for tracking
1746
1812
  * @param emailData Email data that was sent
@@ -1907,7 +1973,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
1907
1973
  super(firestore8, mailgunClient);
1908
1974
  this.DEFAULT_REGISTRATION_URL = "https://metaestetics.net/register";
1909
1975
  this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
1910
- this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaestetics.net>";
1976
+ this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaesthetics.net>";
1911
1977
  }
1912
1978
  /**
1913
1979
  * Sends a practitioner invitation email
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.5.49",
4
+ "version": "1.5.51",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -2,12 +2,24 @@ import * as mailgun from "mailgun-js";
2
2
  import * as admin from "firebase-admin";
3
3
  import { Logger } from "../logger";
4
4
 
5
+ /**
6
+ * Mailgun client type for compatibility with new mailgun.js library
7
+ * This allows us to accept both older mailgun-js and newer mailgun.js clients
8
+ */
9
+ interface MailgunClientCompat {
10
+ messages: {
11
+ create?: (domain: string, data: any) => Promise<any>;
12
+ send?: (data: any, callback: (error: any, body: any) => void) => void;
13
+ };
14
+ }
15
+
5
16
  /**
6
17
  * Base mailing service class that provides common functionality for all mailing services
7
18
  */
8
19
  export class BaseMailingService {
9
20
  protected db: FirebaseFirestore.Firestore;
10
- protected mailgunClient: mailgun.Mailgun;
21
+ protected mailgunClient: mailgun.Mailgun | MailgunClientCompat;
22
+ protected isNewMailgunClient: boolean = false;
11
23
  // Removed config property as it's no longer managed here
12
24
  // import {
13
25
  // getMailgunConfig,
@@ -22,8 +34,7 @@ export class BaseMailingService {
22
34
  */
23
35
  constructor(
24
36
  firestore: FirebaseFirestore.Firestore,
25
- mailgunClient: mailgun.Mailgun // Mailgun client is now required
26
- // mailgunConfig?: MailgunConfig // Removed config parameter
37
+ mailgunClient: mailgun.Mailgun | MailgunClientCompat // Accept either old or new client
27
38
  ) {
28
39
  // Use provided instances
29
40
  this.db = firestore;
@@ -40,6 +51,15 @@ export class BaseMailingService {
40
51
  throw new Error("Mailgun client is required");
41
52
  }
42
53
 
54
+ // Detect which type of mailgun client we're using
55
+ this.isNewMailgunClient =
56
+ typeof (this.mailgunClient.messages as any).create === "function";
57
+ Logger.info(
58
+ `[BaseMailingService] Using ${
59
+ this.isNewMailgunClient ? "new" : "legacy"
60
+ } Mailgun client`
61
+ );
62
+
43
63
  // Validate the Mailgun client configuration
44
64
  this.validateMailgunClient();
45
65
 
@@ -53,27 +73,36 @@ export class BaseMailingService {
53
73
  */
54
74
  private validateMailgunClient(): void {
55
75
  try {
56
- // Access the host property to check if it's configured for EU region
57
- const clientOptions = (this.mailgunClient as any).options || {};
58
- const host = clientOptions.host || "";
59
- const isEuRegion = host.includes("eu.mailgun.net");
76
+ // For the legacy client, check EU region configuration
77
+ if (!this.isNewMailgunClient) {
78
+ const clientOptions = (this.mailgunClient as any).options || {};
79
+ const host = clientOptions.host || "";
80
+ const isEuRegion = host.includes("eu.mailgun.net");
60
81
 
61
- Logger.info("[BaseMailingService] Mailgun client configuration:", {
62
- host: host || "default",
63
- isEuRegion,
64
- domain: clientOptions.domain || "unknown",
65
- });
82
+ Logger.info("[BaseMailingService] Mailgun client configuration:", {
83
+ host: host || "default",
84
+ isEuRegion,
85
+ domain: clientOptions.domain || "unknown",
86
+ clientType: "legacy",
87
+ });
66
88
 
67
- // Check if this appears to be an EU domain but not using EU endpoint
68
- if (
69
- clientOptions.domain &&
70
- clientOptions.domain.endsWith(".eu") &&
71
- !isEuRegion
72
- ) {
73
- Logger.warn(
74
- "[BaseMailingService] Domain appears to be in EU region but not using EU endpoint. " +
75
- "If you're getting 401 Forbidden errors, ensure the host is set to 'api.eu.mailgun.net'"
76
- );
89
+ // Check if this appears to be an EU domain but not using EU endpoint
90
+ if (
91
+ clientOptions.domain &&
92
+ clientOptions.domain.endsWith(".eu") &&
93
+ !isEuRegion
94
+ ) {
95
+ Logger.warn(
96
+ "[BaseMailingService] Domain appears to be in EU region but not using EU endpoint. " +
97
+ "If you're getting 401 Forbidden errors, ensure the host is set to 'api.eu.mailgun.net'"
98
+ );
99
+ }
100
+ } else {
101
+ // For the new client, we can't easily check the configuration
102
+ // but it should be properly configured with the URL parameter
103
+ Logger.info("[BaseMailingService] Using new Mailgun client", {
104
+ clientType: "mailgun.js",
105
+ });
77
106
  }
78
107
  } catch (error) {
79
108
  // Just log the error, don't throw
@@ -90,7 +119,7 @@ export class BaseMailingService {
90
119
  */
91
120
  protected async sendEmail(
92
121
  data: mailgun.messages.SendData // Caller must provide 'from'
93
- ): Promise<mailgun.messages.SendResponse> {
122
+ ): Promise<any> {
94
123
  try {
95
124
  // Validate email data fields
96
125
  if (!data) {
@@ -123,84 +152,111 @@ export class BaseMailingService {
123
152
  subject: data.subject,
124
153
  hasHtml: !!data.html,
125
154
  hasText: !!data.text,
155
+ clientType: this.isNewMailgunClient ? "mailgun.js" : "legacy",
126
156
  });
127
157
 
128
- // Send the email
129
- return await new Promise<mailgun.messages.SendResponse>(
130
- (resolve, reject) => {
131
- try {
132
- const messagesApi = this.mailgunClient.messages();
133
- if (!messagesApi) {
134
- throw new Error("Could not get Mailgun messages API");
158
+ // Use the appropriate API based on the client type
159
+ if (this.isNewMailgunClient) {
160
+ // New mailgun.js client
161
+ try {
162
+ const createMethod = (this.mailgunClient.messages as any).create;
163
+ // Get domain from client if possible, or from the from address as fallback
164
+ const domain =
165
+ this.getDomainFromOptions() || this.getDomainFromEmail(data.from);
166
+
167
+ if (!domain) {
168
+ throw new Error("Could not determine domain for Mailgun API call");
169
+ }
170
+
171
+ Logger.info(
172
+ "[BaseMailingService] Using domain for new Mailgun client:",
173
+ { domain }
174
+ );
175
+
176
+ // Call the create method with domain and data
177
+ const result = await createMethod(domain, data);
178
+ Logger.info(
179
+ "[BaseMailingService] Email sent successfully with new client"
180
+ );
181
+ return result;
182
+ } catch (error) {
183
+ Logger.error(
184
+ "[BaseMailingService] Error sending with new Mailgun client:",
185
+ {
186
+ error: error instanceof Error ? error.message : String(error),
187
+ stack: error instanceof Error ? error.stack : undefined,
135
188
  }
189
+ );
190
+ throw error;
191
+ }
192
+ } else {
193
+ // Legacy mailgun-js client
194
+ return await new Promise<mailgun.messages.SendResponse>(
195
+ (resolve, reject) => {
196
+ try {
197
+ const messagesApi = this.mailgunClient.messages as any;
198
+ if (!messagesApi || !messagesApi.send) {
199
+ throw new Error("Could not get Mailgun messages API");
200
+ }
136
201
 
137
- messagesApi.send(data, (error, body) => {
138
- if (error) {
139
- // Enhanced error logging for auth/region issues
140
- if (error.statusCode === 401 || error.statusCode === 403) {
141
- const clientOptions =
142
- (this.mailgunClient as any).options || {};
143
- Logger.error(
144
- "[BaseMailingService] Mailgun authentication error - possible region mismatch:",
145
- {
202
+ messagesApi.send(data, (error: any, body: any) => {
203
+ if (error) {
204
+ // Enhanced error logging for auth/region issues
205
+ if (error.statusCode === 401 || error.statusCode === 403) {
206
+ const clientOptions =
207
+ (this.mailgunClient as any).options || {};
208
+ Logger.error(
209
+ "[BaseMailingService] Mailgun authentication error - possible region mismatch:",
210
+ {
211
+ error: error instanceof Error ? error.message : error,
212
+ statusCode: error.statusCode,
213
+ domain: clientOptions.domain || "unknown",
214
+ host: clientOptions.host || "default api.mailgun.net",
215
+ suggestion:
216
+ "If using EU region domains, ensure host is set to 'api.eu.mailgun.net'",
217
+ response: (error as any).response
218
+ ? JSON.stringify((error as any).response)
219
+ : "No response details",
220
+ request: (error as any).request
221
+ ? {
222
+ method: (error as any).request?.method,
223
+ path: (error as any).request?.path,
224
+ host: (error as any).request?.host,
225
+ }
226
+ : "No request details",
227
+ }
228
+ );
229
+ } else {
230
+ Logger.error("[BaseMailingService] Mailgun API error:", {
146
231
  error: error instanceof Error ? error.message : error,
147
232
  statusCode: error.statusCode,
148
- domain: clientOptions.domain || "unknown",
149
- host: clientOptions.host || "default api.mailgun.net",
150
- suggestion:
151
- "If using EU region domains, ensure host is set to 'api.eu.mailgun.net'",
152
- response: (error as any).response
153
- ? JSON.stringify((error as any).response)
154
- : "No response details",
155
- request: (error as any).request
156
- ? {
157
- method: (error as any).request?.method,
158
- path: (error as any).request?.path,
159
- host: (error as any).request?.host,
160
- }
161
- : "No request details",
162
- }
163
- );
164
-
165
- // Provide a more specific error object
166
- const enhancedError = new Error(
167
- `Mailgun Authentication Error (${error.statusCode}): Possible EU region misconfiguration. Set host to 'api.eu.mailgun.net' for EU domains.`
168
- );
169
- (enhancedError as any).originalError = error;
170
- (enhancedError as any).mailgunConfig = {
171
- domain: clientOptions.domain,
172
- host: clientOptions.host,
173
- };
174
- reject(enhancedError);
233
+ stack: error instanceof Error ? error.stack : undefined,
234
+ });
235
+ }
236
+ reject(error);
175
237
  } else {
176
- Logger.error("[BaseMailingService] Mailgun API error:", {
177
- error: error instanceof Error ? error.message : error,
178
- statusCode: error.statusCode,
179
- stack: error instanceof Error ? error.stack : undefined,
180
- });
238
+ Logger.info(
239
+ "[BaseMailingService] Email sent successfully:",
240
+ body
241
+ );
242
+ resolve(body);
181
243
  }
182
- reject(error);
183
- } else {
184
- Logger.info(
185
- "[BaseMailingService] Email sent successfully:",
186
- body
187
- );
188
- resolve(body);
189
- }
190
- });
191
- } catch (sendError) {
192
- Logger.error(
193
- "[BaseMailingService] Error in mailgun.messages().send():",
194
- {
195
- error:
196
- sendError instanceof Error ? sendError.message : sendError,
197
- stack: sendError instanceof Error ? sendError.stack : undefined,
198
- }
199
- );
200
- reject(sendError);
244
+ });
245
+ } catch (sendError) {
246
+ Logger.error(
247
+ "[BaseMailingService] Error in mailgun.messages().send():",
248
+ {
249
+ error:
250
+ sendError instanceof Error ? sendError.message : sendError,
251
+ stack:
252
+ sendError instanceof Error ? sendError.stack : undefined,
253
+ }
254
+ );
255
+ reject(sendError);
256
+ }
201
257
  }
202
- }
203
- );
258
+ );
259
+ }
204
260
  } catch (error) {
205
261
  Logger.error("[BaseMailingService] Error in sendEmail:", {
206
262
  error: error instanceof Error ? error.message : error,
@@ -210,6 +266,46 @@ export class BaseMailingService {
210
266
  }
211
267
  }
212
268
 
269
+ /**
270
+ * Tries to get domain from mailgun client options
271
+ * @returns Domain string or undefined
272
+ */
273
+ private getDomainFromOptions(): string | undefined {
274
+ try {
275
+ // Different ways to get domain depending on client type
276
+ if (this.isNewMailgunClient) {
277
+ return (this.mailgunClient as any).domain;
278
+ } else {
279
+ const options = (this.mailgunClient as any).options;
280
+ return options?.domain;
281
+ }
282
+ } catch (e) {
283
+ return undefined;
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Extracts domain from an email address
289
+ * @param email Email address
290
+ * @returns Domain part or undefined
291
+ */
292
+ private getDomainFromEmail(email: string): string | undefined {
293
+ try {
294
+ // Extract from "Name <email@domain.com>" format
295
+ const match = email.match(/<([^>]+)>/) || [null, email];
296
+ const emailPart = match[1];
297
+
298
+ // Get domain part
299
+ const domainMatch = emailPart.match(/@([^@]+)$/);
300
+ if (domainMatch && domainMatch[1]) {
301
+ return domainMatch[1];
302
+ }
303
+ return undefined;
304
+ } catch (e) {
305
+ return undefined;
306
+ }
307
+ }
308
+
213
309
  /**
214
310
  * Logs email sending attempt to Firestore for tracking
215
311
  * @param emailData Email data that was sent
@@ -55,7 +55,7 @@ export class PractitionerInviteMailingService extends BaseMailingService {
55
55
  private readonly DEFAULT_SUBJECT =
56
56
  "You've Been Invited to Join as a Practitioner";
57
57
  private readonly DEFAULT_FROM_ADDRESS =
58
- "MedClinic <no-reply@mg.metaestetics.net>";
58
+ "MedClinic <no-reply@mg.metaesthetics.net>";
59
59
 
60
60
  /**
61
61
  * Constructor for PractitionerInviteMailingService