@blackcode_sa/metaestetics-api 1.5.48 → 1.5.50

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,57 +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
- 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
- {
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:", {
1726
1782
  error: error instanceof Error ? error.message : error,
1727
1783
  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
- }
1732
- );
1784
+ stack: error instanceof Error ? error.stack : void 0
1785
+ });
1786
+ }
1787
+ reject(error);
1733
1788
  } else {
1734
- Logger.error("[BaseMailingService] Mailgun API error:", {
1735
- error: error instanceof Error ? error.message : error,
1736
- statusCode: error.statusCode,
1737
- stack: error instanceof Error ? error.stack : void 0
1738
- });
1789
+ Logger.info(
1790
+ "[BaseMailingService] Email sent successfully:",
1791
+ body
1792
+ );
1793
+ resolve(body);
1739
1794
  }
1740
- reject(error);
1741
- } else {
1742
- Logger.info(
1743
- "[BaseMailingService] Email sent successfully:",
1744
- body
1745
- );
1746
- resolve(body);
1747
- }
1748
- });
1749
- } catch (sendError) {
1750
- Logger.error(
1751
- "[BaseMailingService] Error in mailgun.messages().send():",
1752
- {
1753
- error: sendError instanceof Error ? sendError.message : sendError,
1754
- stack: sendError instanceof Error ? sendError.stack : void 0
1755
- }
1756
- );
1757
- 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
+ }
1758
1806
  }
1759
- }
1760
- );
1807
+ );
1808
+ }
1761
1809
  } catch (error) {
1762
1810
  Logger.error("[BaseMailingService] Error in sendEmail:", {
1763
1811
  error: error instanceof Error ? error.message : error,
@@ -1766,6 +1814,40 @@ var BaseMailingService = class {
1766
1814
  throw error;
1767
1815
  }
1768
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
+ }
1769
1851
  /**
1770
1852
  * Logs email sending attempt to Firestore for tracking
1771
1853
  * @param emailData Email data that was sent
@@ -1932,7 +2014,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
1932
2014
  super(firestore8, mailgunClient);
1933
2015
  this.DEFAULT_REGISTRATION_URL = "https://metaestetics.net/register";
1934
2016
  this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
1935
- this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaestetics.net>";
2017
+ this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaesthetics.net>";
1936
2018
  }
1937
2019
  /**
1938
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,57 +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
- if (error) {
1680
- if (error.statusCode === 401 || error.statusCode === 403) {
1681
- const clientOptions = this.mailgunClient.options || {};
1682
- Logger.error(
1683
- "[BaseMailingService] Mailgun authentication error - possible region mismatch:",
1684
- {
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:", {
1685
1741
  error: error instanceof Error ? error.message : error,
1686
1742
  statusCode: error.statusCode,
1687
- domain: clientOptions.domain || "unknown",
1688
- host: clientOptions.host || "default api.mailgun.net",
1689
- suggestion: "If using EU region domains, ensure host is set to 'api.eu.mailgun.net'"
1690
- }
1691
- );
1743
+ stack: error instanceof Error ? error.stack : void 0
1744
+ });
1745
+ }
1746
+ reject(error);
1692
1747
  } else {
1693
- Logger.error("[BaseMailingService] Mailgun API error:", {
1694
- error: error instanceof Error ? error.message : error,
1695
- statusCode: error.statusCode,
1696
- stack: error instanceof Error ? error.stack : void 0
1697
- });
1748
+ Logger.info(
1749
+ "[BaseMailingService] Email sent successfully:",
1750
+ body
1751
+ );
1752
+ resolve(body);
1698
1753
  }
1699
- reject(error);
1700
- } else {
1701
- Logger.info(
1702
- "[BaseMailingService] Email sent successfully:",
1703
- body
1704
- );
1705
- resolve(body);
1706
- }
1707
- });
1708
- } catch (sendError) {
1709
- Logger.error(
1710
- "[BaseMailingService] Error in mailgun.messages().send():",
1711
- {
1712
- error: sendError instanceof Error ? sendError.message : sendError,
1713
- stack: sendError instanceof Error ? sendError.stack : void 0
1714
- }
1715
- );
1716
- 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
+ }
1717
1765
  }
1718
- }
1719
- );
1766
+ );
1767
+ }
1720
1768
  } catch (error) {
1721
1769
  Logger.error("[BaseMailingService] Error in sendEmail:", {
1722
1770
  error: error instanceof Error ? error.message : error,
@@ -1725,6 +1773,40 @@ var BaseMailingService = class {
1725
1773
  throw error;
1726
1774
  }
1727
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
+ }
1728
1810
  /**
1729
1811
  * Logs email sending attempt to Firestore for tracking
1730
1812
  * @param emailData Email data that was sent
@@ -1891,7 +1973,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
1891
1973
  super(firestore8, mailgunClient);
1892
1974
  this.DEFAULT_REGISTRATION_URL = "https://metaestetics.net/register";
1893
1975
  this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
1894
- this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaestetics.net>";
1976
+ this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaesthetics.net>";
1895
1977
  }
1896
1978
  /**
1897
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.48",
4
+ "version": "1.5.50",
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,63 +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
- }
153
- );
233
+ stack: error instanceof Error ? error.stack : undefined,
234
+ });
235
+ }
236
+ reject(error);
154
237
  } else {
155
- Logger.error("[BaseMailingService] Mailgun API error:", {
156
- error: error instanceof Error ? error.message : error,
157
- statusCode: error.statusCode,
158
- stack: error instanceof Error ? error.stack : undefined,
159
- });
238
+ Logger.info(
239
+ "[BaseMailingService] Email sent successfully:",
240
+ body
241
+ );
242
+ resolve(body);
160
243
  }
161
- reject(error);
162
- } else {
163
- Logger.info(
164
- "[BaseMailingService] Email sent successfully:",
165
- body
166
- );
167
- resolve(body);
168
- }
169
- });
170
- } catch (sendError) {
171
- Logger.error(
172
- "[BaseMailingService] Error in mailgun.messages().send():",
173
- {
174
- error:
175
- sendError instanceof Error ? sendError.message : sendError,
176
- stack: sendError instanceof Error ? sendError.stack : undefined,
177
- }
178
- );
179
- 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
+ }
180
257
  }
181
- }
182
- );
258
+ );
259
+ }
183
260
  } catch (error) {
184
261
  Logger.error("[BaseMailingService] Error in sendEmail:", {
185
262
  error: error instanceof Error ? error.message : error,
@@ -189,6 +266,46 @@ export class BaseMailingService {
189
266
  }
190
267
  }
191
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
+
192
309
  /**
193
310
  * Logs email sending attempt to Firestore for tracking
194
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