@blackcode_sa/metaestetics-api 1.5.51 → 1.6.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.
@@ -1628,224 +1628,92 @@ var Logger = class {
1628
1628
 
1629
1629
  // src/admin/mailing/base.mailing.service.ts
1630
1630
  var BaseMailingService = class {
1631
- // Removed config property as it's no longer managed here
1632
- // import {
1633
- // getMailgunConfig,
1634
- // createMailgunClient,
1635
- // MailgunConfig,
1636
- // } from "./mailgun.config";
1631
+ // Expecting the new mailgun.js client
1637
1632
  /**
1638
1633
  * Constructor for BaseMailingService
1639
1634
  * @param firestore Firestore instance provided by the caller
1640
- * @param mailgunClient Mailgun client instance provided by the caller
1635
+ * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
1641
1636
  */
1642
1637
  constructor(firestore8, mailgunClient) {
1643
- this.isNewMailgunClient = false;
1638
+ var _a;
1644
1639
  this.db = firestore8;
1645
1640
  this.mailgunClient = mailgunClient;
1646
1641
  if (!this.db) {
1647
1642
  Logger.error("[BaseMailingService] No Firestore instance provided");
1648
1643
  throw new Error("Firestore instance is required");
1649
1644
  }
1650
- if (!this.mailgunClient) {
1651
- Logger.error("[BaseMailingService] No Mailgun client provided");
1652
- throw new Error("Mailgun client is required");
1645
+ if (!this.mailgunClient || typeof ((_a = this.mailgunClient.messages) == null ? void 0 : _a.create) !== "function") {
1646
+ Logger.error(
1647
+ "[BaseMailingService] Invalid Mailgun client provided (must be mailgun.js v10+ client)"
1648
+ );
1649
+ throw new Error("A valid mailgun.js v10+ client is required");
1653
1650
  }
1654
- this.isNewMailgunClient = typeof this.mailgunClient.messages.create === "function";
1655
1651
  Logger.info(
1656
- `[BaseMailingService] Using ${this.isNewMailgunClient ? "new" : "legacy"} Mailgun client`
1652
+ "[BaseMailingService] Service initialized with mailgun.js client."
1657
1653
  );
1658
- this.validateMailgunClient();
1659
- Logger.info("[BaseMailingService] Service initialized successfully");
1660
1654
  }
1661
1655
  /**
1662
- * Validates that the Mailgun client is configured correctly
1663
- * Particularly checks for EU region configuration
1664
- */
1665
- validateMailgunClient() {
1666
- try {
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
- });
1686
- }
1687
- } catch (error) {
1688
- Logger.warn("[BaseMailingService] Could not validate Mailgun client:", {
1689
- error: error instanceof Error ? error.message : String(error)
1690
- });
1691
- }
1692
- }
1693
- /**
1694
- * Sends an email using Mailgun
1695
- * @param data Email data to send, including the 'from' address
1656
+ * Sends an email using the new Mailgun client API.
1657
+ * @param domain The Mailgun domain to send from (e.g., mg.metaesthetics.net)
1658
+ * @param data Email data to send, compatible with mailgun.js messages.create API
1696
1659
  * @returns Promise with the sending result
1697
1660
  */
1698
- async sendEmail(data) {
1699
- try {
1700
- if (!data) {
1701
- throw new Error("Email data object is required");
1702
- }
1703
- if (!data.to) {
1704
- throw new Error("Email 'to' address is required");
1705
- }
1706
- if (!data.from) {
1707
- throw new Error(
1708
- "Email 'from' address must be provided in sendEmail data."
1709
- );
1710
- }
1711
- if (!data.subject) {
1712
- throw new Error("Email 'subject' is required");
1713
- }
1714
- if (!data.html && !data.text) {
1715
- throw new Error("Email must have either 'html' or 'text' content");
1716
- }
1717
- Logger.info("[BaseMailingService] Sending email via Mailgun", {
1661
+ async sendEmail(domain, data) {
1662
+ if (!domain) {
1663
+ throw new Error("Mailgun domain is required for sending email.");
1664
+ }
1665
+ if (!data) {
1666
+ throw new Error("Email data object is required");
1667
+ }
1668
+ if (!data.to) {
1669
+ throw new Error("Email 'to' address is required");
1670
+ }
1671
+ if (!data.from) {
1672
+ throw new Error("Email 'from' address is required");
1673
+ }
1674
+ if (!data.subject) {
1675
+ throw new Error("Email 'subject' is required");
1676
+ }
1677
+ if (!data.html && !data.text) {
1678
+ throw new Error("Email must have either 'html' or 'text' content");
1679
+ }
1680
+ Logger.info(
1681
+ "[BaseMailingService] Sending email via Mailgun (mailgun.js API)",
1682
+ {
1683
+ domain,
1718
1684
  to: data.to,
1719
1685
  from: data.from,
1720
1686
  subject: data.subject,
1721
1687
  hasHtml: !!data.html,
1722
- hasText: !!data.text,
1723
- clientType: this.isNewMailgunClient ? "mailgun.js" : "legacy"
1724
- });
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
1747
- }
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:", {
1782
- error: error instanceof Error ? error.message : error,
1783
- statusCode: error.statusCode,
1784
- stack: error instanceof Error ? error.stack : void 0
1785
- });
1786
- }
1787
- reject(error);
1788
- } else {
1789
- Logger.info(
1790
- "[BaseMailingService] Email sent successfully:",
1791
- body
1792
- );
1793
- resolve(body);
1794
- }
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
- }
1806
- }
1807
- );
1688
+ hasText: !!data.text
1808
1689
  }
1809
- } catch (error) {
1810
- Logger.error("[BaseMailingService] Error in sendEmail:", {
1811
- error: error instanceof Error ? error.message : error,
1812
- stack: error instanceof Error ? error.stack : void 0
1813
- });
1814
- throw error;
1815
- }
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) {
1690
+ );
1839
1691
  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;
1692
+ const result = await this.mailgunClient.messages.create(domain, data);
1693
+ Logger.info(
1694
+ "[BaseMailingService] Email sent successfully with mailgun.js client",
1695
+ result
1696
+ );
1697
+ return result;
1698
+ } catch (error) {
1699
+ Logger.error(
1700
+ "[BaseMailingService] Error sending email with mailgun.js client",
1701
+ {
1702
+ errorMessage: error == null ? void 0 : error.message,
1703
+ errorDetails: error == null ? void 0 : error.details,
1704
+ // mailgun.js errors often have a details field
1705
+ errorStatusCode: error == null ? void 0 : error.status,
1706
+ // and a status field for HTTP status code
1707
+ stack: error == null ? void 0 : error.stack,
1708
+ domain,
1709
+ to: data.to
1710
+ }
1711
+ );
1712
+ const mailgunError = new Error(
1713
+ `Mailgun API Error (${(error == null ? void 0 : error.status) || "unknown"}): ${(error == null ? void 0 : error.details) || (error == null ? void 0 : error.message) || "Failed to send email"}`
1714
+ );
1715
+ mailgunError.originalError = error;
1716
+ throw mailgunError;
1849
1717
  }
1850
1718
  }
1851
1719
  /**
@@ -1862,7 +1730,12 @@ var BaseMailingService = class {
1862
1730
  subject: emailData.subject,
1863
1731
  templateName: emailData.templateName,
1864
1732
  success,
1865
- error: error ? error instanceof Error ? { message: error.message, stack: error.stack } : JSON.stringify(error) : null,
1733
+ error: error ? {
1734
+ message: error.message,
1735
+ details: error.details,
1736
+ status: error.status,
1737
+ stack: error.stack
1738
+ } : null,
1866
1739
  sentAt: admin6.firestore.FieldValue.serverTimestamp()
1867
1740
  });
1868
1741
  Logger.info(
@@ -1870,8 +1743,8 @@ var BaseMailingService = class {
1870
1743
  );
1871
1744
  } catch (logError) {
1872
1745
  Logger.error("[BaseMailingService] Error logging email attempt:", {
1873
- error: logError instanceof Error ? logError.message : logError,
1874
- stack: logError instanceof Error ? logError.stack : void 0
1746
+ errorMessage: logError.message,
1747
+ stack: logError.stack
1875
1748
  });
1876
1749
  }
1877
1750
  }
@@ -1888,16 +1761,16 @@ var BaseMailingService = class {
1888
1761
  try {
1889
1762
  let rendered = template;
1890
1763
  Object.entries(variables).forEach(([key, value]) => {
1891
- const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
1764
+ const regex = new RegExp(`{{s*${key}s*}}`, "g");
1892
1765
  rendered = rendered.replace(regex, value || "");
1893
1766
  });
1894
1767
  return rendered;
1895
1768
  } catch (renderError) {
1896
1769
  Logger.error("[BaseMailingService] Error rendering template:", {
1897
- error: renderError instanceof Error ? renderError.message : renderError
1770
+ errorMessage: renderError.message
1898
1771
  });
1899
1772
  throw new Error(
1900
- `Template rendering failed: ${renderError instanceof Error ? renderError.message : "Unknown error"}`
1773
+ `Template rendering failed: ${renderError.message || "Unknown error"}`
1901
1774
  );
1902
1775
  }
1903
1776
  }
@@ -2008,13 +1881,13 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2008
1881
  /**
2009
1882
  * Constructor for PractitionerInviteMailingService
2010
1883
  * @param firestore Firestore instance provided by the caller
2011
- * @param mailgunClient Mailgun client instance provided by the caller
1884
+ * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
2012
1885
  */
2013
1886
  constructor(firestore8, mailgunClient) {
2014
1887
  super(firestore8, mailgunClient);
2015
- this.DEFAULT_REGISTRATION_URL = "https://metaestetics.net/register";
1888
+ this.DEFAULT_REGISTRATION_URL = "https://metaesthetics.net/register";
2016
1889
  this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
2017
- this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaesthetics.net>";
1890
+ this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
2018
1891
  }
2019
1892
  /**
2020
1893
  * Sends a practitioner invitation email
@@ -2022,7 +1895,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2022
1895
  * @returns Promise resolved when email is sent
2023
1896
  */
2024
1897
  async sendInvitationEmail(data) {
2025
- var _a, _b, _c, _d;
1898
+ var _a, _b, _c, _d, _e, _f;
2026
1899
  try {
2027
1900
  Logger.info(
2028
1901
  "[PractitionerInviteMailingService] Sending invitation email to",
@@ -2038,7 +1911,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2038
1911
  const contactName = data.clinic.contactName || "Clinic Administrator";
2039
1912
  const contactEmail = data.clinic.contactEmail;
2040
1913
  const subject = ((_b = data.options) == null ? void 0 : _b.customSubject) || this.DEFAULT_SUBJECT;
2041
- const fromAddress = ((_c = data.options) == null ? void 0 : _c.fromAddress) || this.DEFAULT_FROM_ADDRESS;
1914
+ const fromAddress = ((_c = data.options) == null ? void 0 : _c.fromAddress) || `MetaEstetics <no-reply@${((_d = data.options) == null ? void 0 : _d.mailgunDomain) || this.DEFAULT_MAILGUN_DOMAIN}>`;
2042
1915
  const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
2043
1916
  const practitionerName = `${data.practitioner.firstName} ${data.practitioner.lastName}`;
2044
1917
  const templateVariables = {
@@ -2065,22 +1938,24 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2065
1938
  practitionerInvitationTemplate,
2066
1939
  templateVariables
2067
1940
  );
2068
- const emailData = {
1941
+ const mailgunSendData = {
2069
1942
  to: data.token.email,
2070
1943
  from: fromAddress,
2071
1944
  subject,
2072
1945
  html
2073
1946
  };
1947
+ const domainToSendFrom = ((_e = data.options) == null ? void 0 : _e.mailgunDomain) || this.DEFAULT_MAILGUN_DOMAIN;
2074
1948
  Logger.info(
2075
1949
  "[PractitionerInviteMailingService] Sending email with data:",
2076
1950
  {
2077
- to: emailData.to,
2078
- from: emailData.from,
2079
- subject: emailData.subject,
2080
- hasHtml: !!emailData.html
1951
+ domain: domainToSendFrom,
1952
+ to: mailgunSendData.to,
1953
+ from: mailgunSendData.from,
1954
+ subject: mailgunSendData.subject,
1955
+ hasHtml: !!mailgunSendData.html
2081
1956
  }
2082
1957
  );
2083
- const result = await this.sendEmail(emailData);
1958
+ const result = await this.sendEmail(domainToSendFrom, mailgunSendData);
2084
1959
  await this.logEmailAttempt(
2085
1960
  {
2086
1961
  to: data.token.email,
@@ -2094,14 +1969,16 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2094
1969
  Logger.error(
2095
1970
  "[PractitionerInviteMailingService] Error sending invitation email:",
2096
1971
  {
2097
- error: error instanceof Error ? error.message : error,
2098
- stack: error instanceof Error ? error.stack : void 0
1972
+ errorMessage: error.message,
1973
+ errorDetails: error.details,
1974
+ errorStatus: error.status,
1975
+ stack: error.stack
2099
1976
  }
2100
1977
  );
2101
1978
  await this.logEmailAttempt(
2102
1979
  {
2103
1980
  to: data.token.email,
2104
- subject: ((_d = data.options) == null ? void 0 : _d.customSubject) || this.DEFAULT_SUBJECT,
1981
+ subject: ((_f = data.options) == null ? void 0 : _f.customSubject) || this.DEFAULT_SUBJECT,
2105
1982
  templateName: "practitioner_invitation"
2106
1983
  },
2107
1984
  false,
@@ -2116,9 +1993,10 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2116
1993
  * and sends the invitation email.
2117
1994
  * @param tokenData The fully typed token object including its id
2118
1995
  * @param fromAddress The 'from' email address to use, obtained from config
1996
+ * @param mailgunDomain The mailgun domain to use for sending
2119
1997
  * @returns Promise resolved when the email is sent
2120
1998
  */
2121
- async handleTokenCreationEvent(tokenData, fromAddress) {
1999
+ async handleTokenCreationEvent(tokenData, mailgunConfig) {
2122
2000
  try {
2123
2001
  Logger.info(
2124
2002
  "[PractitionerInviteMailingService] Handling token creation event for token:",
@@ -2140,8 +2018,12 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2140
2018
  if (!tokenData.expiresAt) {
2141
2019
  throw new Error(`Token ${tokenData.id} is missing expiration date`);
2142
2020
  }
2143
- if (!tokenData.status) {
2144
- throw new Error(`Token ${tokenData.id} has no status defined`);
2021
+ if (tokenData.status !== "active" /* ACTIVE */ && String(tokenData.status).toLowerCase() !== "active") {
2022
+ Logger.warn(
2023
+ "[PractitionerInviteMailingService] Token is not active, skipping email.",
2024
+ { tokenId: tokenData.id, status: tokenData.status }
2025
+ );
2026
+ return;
2145
2027
  }
2146
2028
  Logger.info(
2147
2029
  `[PractitionerInviteMailingService] Token status validation:`,
@@ -2185,11 +2067,11 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2185
2067
  Logger.info(
2186
2068
  `[PractitionerInviteMailingService] Clinic found: ${clinicData.name}`
2187
2069
  );
2188
- if (!fromAddress) {
2070
+ if (!mailgunConfig.fromAddress) {
2189
2071
  Logger.warn(
2190
2072
  "[PractitionerInviteMailingService] No fromAddress provided, using default"
2191
2073
  );
2192
- fromAddress = this.DEFAULT_FROM_ADDRESS;
2074
+ mailgunConfig.fromAddress = `MetaEstetics <no-reply@${this.DEFAULT_MAILGUN_DOMAIN}>`;
2193
2075
  }
2194
2076
  const emailData = {
2195
2077
  token: {
@@ -2211,7 +2093,9 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2211
2093
  contactName: "Clinic Admin"
2212
2094
  },
2213
2095
  options: {
2214
- fromAddress
2096
+ fromAddress: mailgunConfig.fromAddress,
2097
+ mailgunDomain: mailgunConfig.domain,
2098
+ registrationUrl: mailgunConfig.registrationUrl
2215
2099
  }
2216
2100
  };
2217
2101
  Logger.info(
@@ -2225,8 +2109,11 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2225
2109
  Logger.error(
2226
2110
  "[PractitionerInviteMailingService] Error handling token creation event:",
2227
2111
  {
2228
- error: error instanceof Error ? error.message : error,
2229
- stack: error instanceof Error ? error.stack : void 0
2112
+ errorMessage: error.message,
2113
+ errorDetails: error.details,
2114
+ errorStatus: error.status,
2115
+ stack: error.stack,
2116
+ tokenId: tokenData == null ? void 0 : tokenData.id
2230
2117
  }
2231
2118
  );
2232
2119
  throw error;