@blackcode_sa/metaestetics-api 1.5.51 → 1.5.52

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.
@@ -1587,224 +1587,92 @@ var Logger = class {
1587
1587
 
1588
1588
  // src/admin/mailing/base.mailing.service.ts
1589
1589
  var BaseMailingService = class {
1590
- // Removed config property as it's no longer managed here
1591
- // import {
1592
- // getMailgunConfig,
1593
- // createMailgunClient,
1594
- // MailgunConfig,
1595
- // } from "./mailgun.config";
1590
+ // Expecting the new mailgun.js client
1596
1591
  /**
1597
1592
  * Constructor for BaseMailingService
1598
1593
  * @param firestore Firestore instance provided by the caller
1599
- * @param mailgunClient Mailgun client instance provided by the caller
1594
+ * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
1600
1595
  */
1601
1596
  constructor(firestore8, mailgunClient) {
1602
- this.isNewMailgunClient = false;
1597
+ var _a;
1603
1598
  this.db = firestore8;
1604
1599
  this.mailgunClient = mailgunClient;
1605
1600
  if (!this.db) {
1606
1601
  Logger.error("[BaseMailingService] No Firestore instance provided");
1607
1602
  throw new Error("Firestore instance is required");
1608
1603
  }
1609
- if (!this.mailgunClient) {
1610
- Logger.error("[BaseMailingService] No Mailgun client provided");
1611
- throw new Error("Mailgun client is required");
1604
+ if (!this.mailgunClient || typeof ((_a = this.mailgunClient.messages) == null ? void 0 : _a.create) !== "function") {
1605
+ Logger.error(
1606
+ "[BaseMailingService] Invalid Mailgun client provided (must be mailgun.js v10+ client)"
1607
+ );
1608
+ throw new Error("A valid mailgun.js v10+ client is required");
1612
1609
  }
1613
- this.isNewMailgunClient = typeof this.mailgunClient.messages.create === "function";
1614
1610
  Logger.info(
1615
- `[BaseMailingService] Using ${this.isNewMailgunClient ? "new" : "legacy"} Mailgun client`
1611
+ "[BaseMailingService] Service initialized with mailgun.js client."
1616
1612
  );
1617
- this.validateMailgunClient();
1618
- Logger.info("[BaseMailingService] Service initialized successfully");
1619
1613
  }
1620
1614
  /**
1621
- * Validates that the Mailgun client is configured correctly
1622
- * Particularly checks for EU region configuration
1623
- */
1624
- validateMailgunClient() {
1625
- try {
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
- });
1645
- }
1646
- } catch (error) {
1647
- Logger.warn("[BaseMailingService] Could not validate Mailgun client:", {
1648
- error: error instanceof Error ? error.message : String(error)
1649
- });
1650
- }
1651
- }
1652
- /**
1653
- * Sends an email using Mailgun
1654
- * @param data Email data to send, including the 'from' address
1615
+ * Sends an email using the new Mailgun client API.
1616
+ * @param domain The Mailgun domain to send from (e.g., mg.metaesthetics.net)
1617
+ * @param data Email data to send, compatible with mailgun.js messages.create API
1655
1618
  * @returns Promise with the sending result
1656
1619
  */
1657
- async sendEmail(data) {
1658
- try {
1659
- if (!data) {
1660
- throw new Error("Email data object is required");
1661
- }
1662
- if (!data.to) {
1663
- throw new Error("Email 'to' address is required");
1664
- }
1665
- if (!data.from) {
1666
- throw new Error(
1667
- "Email 'from' address must be provided in sendEmail data."
1668
- );
1669
- }
1670
- if (!data.subject) {
1671
- throw new Error("Email 'subject' is required");
1672
- }
1673
- if (!data.html && !data.text) {
1674
- throw new Error("Email must have either 'html' or 'text' content");
1675
- }
1676
- Logger.info("[BaseMailingService] Sending email via Mailgun", {
1620
+ async sendEmail(domain, data) {
1621
+ if (!domain) {
1622
+ throw new Error("Mailgun domain is required for sending email.");
1623
+ }
1624
+ if (!data) {
1625
+ throw new Error("Email data object is required");
1626
+ }
1627
+ if (!data.to) {
1628
+ throw new Error("Email 'to' address is required");
1629
+ }
1630
+ if (!data.from) {
1631
+ throw new Error("Email 'from' address is required");
1632
+ }
1633
+ if (!data.subject) {
1634
+ throw new Error("Email 'subject' is required");
1635
+ }
1636
+ if (!data.html && !data.text) {
1637
+ throw new Error("Email must have either 'html' or 'text' content");
1638
+ }
1639
+ Logger.info(
1640
+ "[BaseMailingService] Sending email via Mailgun (mailgun.js API)",
1641
+ {
1642
+ domain,
1677
1643
  to: data.to,
1678
1644
  from: data.from,
1679
1645
  subject: data.subject,
1680
1646
  hasHtml: !!data.html,
1681
- hasText: !!data.text,
1682
- clientType: this.isNewMailgunClient ? "mailgun.js" : "legacy"
1683
- });
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
1706
- }
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:", {
1741
- error: error instanceof Error ? error.message : error,
1742
- statusCode: error.statusCode,
1743
- stack: error instanceof Error ? error.stack : void 0
1744
- });
1745
- }
1746
- reject(error);
1747
- } else {
1748
- Logger.info(
1749
- "[BaseMailingService] Email sent successfully:",
1750
- body
1751
- );
1752
- resolve(body);
1753
- }
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
- }
1765
- }
1766
- );
1647
+ hasText: !!data.text
1767
1648
  }
1768
- } catch (error) {
1769
- Logger.error("[BaseMailingService] Error in sendEmail:", {
1770
- error: error instanceof Error ? error.message : error,
1771
- stack: error instanceof Error ? error.stack : void 0
1772
- });
1773
- throw error;
1774
- }
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) {
1649
+ );
1798
1650
  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;
1651
+ const result = await this.mailgunClient.messages.create(domain, data);
1652
+ Logger.info(
1653
+ "[BaseMailingService] Email sent successfully with mailgun.js client",
1654
+ result
1655
+ );
1656
+ return result;
1657
+ } catch (error) {
1658
+ Logger.error(
1659
+ "[BaseMailingService] Error sending email with mailgun.js client",
1660
+ {
1661
+ errorMessage: error == null ? void 0 : error.message,
1662
+ errorDetails: error == null ? void 0 : error.details,
1663
+ // mailgun.js errors often have a details field
1664
+ errorStatusCode: error == null ? void 0 : error.status,
1665
+ // and a status field for HTTP status code
1666
+ stack: error == null ? void 0 : error.stack,
1667
+ domain,
1668
+ to: data.to
1669
+ }
1670
+ );
1671
+ const mailgunError = new Error(
1672
+ `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"}`
1673
+ );
1674
+ mailgunError.originalError = error;
1675
+ throw mailgunError;
1808
1676
  }
1809
1677
  }
1810
1678
  /**
@@ -1821,7 +1689,12 @@ var BaseMailingService = class {
1821
1689
  subject: emailData.subject,
1822
1690
  templateName: emailData.templateName,
1823
1691
  success,
1824
- error: error ? error instanceof Error ? { message: error.message, stack: error.stack } : JSON.stringify(error) : null,
1692
+ error: error ? {
1693
+ message: error.message,
1694
+ details: error.details,
1695
+ status: error.status,
1696
+ stack: error.stack
1697
+ } : null,
1825
1698
  sentAt: admin6.firestore.FieldValue.serverTimestamp()
1826
1699
  });
1827
1700
  Logger.info(
@@ -1829,8 +1702,8 @@ var BaseMailingService = class {
1829
1702
  );
1830
1703
  } catch (logError) {
1831
1704
  Logger.error("[BaseMailingService] Error logging email attempt:", {
1832
- error: logError instanceof Error ? logError.message : logError,
1833
- stack: logError instanceof Error ? logError.stack : void 0
1705
+ errorMessage: logError.message,
1706
+ stack: logError.stack
1834
1707
  });
1835
1708
  }
1836
1709
  }
@@ -1847,16 +1720,16 @@ var BaseMailingService = class {
1847
1720
  try {
1848
1721
  let rendered = template;
1849
1722
  Object.entries(variables).forEach(([key, value]) => {
1850
- const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
1723
+ const regex = new RegExp(`{{s*${key}s*}}`, "g");
1851
1724
  rendered = rendered.replace(regex, value || "");
1852
1725
  });
1853
1726
  return rendered;
1854
1727
  } catch (renderError) {
1855
1728
  Logger.error("[BaseMailingService] Error rendering template:", {
1856
- error: renderError instanceof Error ? renderError.message : renderError
1729
+ errorMessage: renderError.message
1857
1730
  });
1858
1731
  throw new Error(
1859
- `Template rendering failed: ${renderError instanceof Error ? renderError.message : "Unknown error"}`
1732
+ `Template rendering failed: ${renderError.message || "Unknown error"}`
1860
1733
  );
1861
1734
  }
1862
1735
  }
@@ -1967,13 +1840,13 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
1967
1840
  /**
1968
1841
  * Constructor for PractitionerInviteMailingService
1969
1842
  * @param firestore Firestore instance provided by the caller
1970
- * @param mailgunClient Mailgun client instance provided by the caller
1843
+ * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
1971
1844
  */
1972
1845
  constructor(firestore8, mailgunClient) {
1973
1846
  super(firestore8, mailgunClient);
1974
- this.DEFAULT_REGISTRATION_URL = "https://metaestetics.net/register";
1847
+ this.DEFAULT_REGISTRATION_URL = "https://metaesthetics.net/register";
1975
1848
  this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
1976
- this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaesthetics.net>";
1849
+ this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
1977
1850
  }
1978
1851
  /**
1979
1852
  * Sends a practitioner invitation email
@@ -1981,7 +1854,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
1981
1854
  * @returns Promise resolved when email is sent
1982
1855
  */
1983
1856
  async sendInvitationEmail(data) {
1984
- var _a, _b, _c, _d;
1857
+ var _a, _b, _c, _d, _e, _f;
1985
1858
  try {
1986
1859
  Logger.info(
1987
1860
  "[PractitionerInviteMailingService] Sending invitation email to",
@@ -1997,7 +1870,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
1997
1870
  const contactName = data.clinic.contactName || "Clinic Administrator";
1998
1871
  const contactEmail = data.clinic.contactEmail;
1999
1872
  const subject = ((_b = data.options) == null ? void 0 : _b.customSubject) || this.DEFAULT_SUBJECT;
2000
- const fromAddress = ((_c = data.options) == null ? void 0 : _c.fromAddress) || this.DEFAULT_FROM_ADDRESS;
1873
+ 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}>`;
2001
1874
  const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
2002
1875
  const practitionerName = `${data.practitioner.firstName} ${data.practitioner.lastName}`;
2003
1876
  const templateVariables = {
@@ -2024,22 +1897,24 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2024
1897
  practitionerInvitationTemplate,
2025
1898
  templateVariables
2026
1899
  );
2027
- const emailData = {
1900
+ const mailgunSendData = {
2028
1901
  to: data.token.email,
2029
1902
  from: fromAddress,
2030
1903
  subject,
2031
1904
  html
2032
1905
  };
1906
+ const domainToSendFrom = ((_e = data.options) == null ? void 0 : _e.mailgunDomain) || this.DEFAULT_MAILGUN_DOMAIN;
2033
1907
  Logger.info(
2034
1908
  "[PractitionerInviteMailingService] Sending email with data:",
2035
1909
  {
2036
- to: emailData.to,
2037
- from: emailData.from,
2038
- subject: emailData.subject,
2039
- hasHtml: !!emailData.html
1910
+ domain: domainToSendFrom,
1911
+ to: mailgunSendData.to,
1912
+ from: mailgunSendData.from,
1913
+ subject: mailgunSendData.subject,
1914
+ hasHtml: !!mailgunSendData.html
2040
1915
  }
2041
1916
  );
2042
- const result = await this.sendEmail(emailData);
1917
+ const result = await this.sendEmail(domainToSendFrom, mailgunSendData);
2043
1918
  await this.logEmailAttempt(
2044
1919
  {
2045
1920
  to: data.token.email,
@@ -2053,14 +1928,16 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2053
1928
  Logger.error(
2054
1929
  "[PractitionerInviteMailingService] Error sending invitation email:",
2055
1930
  {
2056
- error: error instanceof Error ? error.message : error,
2057
- stack: error instanceof Error ? error.stack : void 0
1931
+ errorMessage: error.message,
1932
+ errorDetails: error.details,
1933
+ errorStatus: error.status,
1934
+ stack: error.stack
2058
1935
  }
2059
1936
  );
2060
1937
  await this.logEmailAttempt(
2061
1938
  {
2062
1939
  to: data.token.email,
2063
- subject: ((_d = data.options) == null ? void 0 : _d.customSubject) || this.DEFAULT_SUBJECT,
1940
+ subject: ((_f = data.options) == null ? void 0 : _f.customSubject) || this.DEFAULT_SUBJECT,
2064
1941
  templateName: "practitioner_invitation"
2065
1942
  },
2066
1943
  false,
@@ -2075,9 +1952,10 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2075
1952
  * and sends the invitation email.
2076
1953
  * @param tokenData The fully typed token object including its id
2077
1954
  * @param fromAddress The 'from' email address to use, obtained from config
1955
+ * @param mailgunDomain The mailgun domain to use for sending
2078
1956
  * @returns Promise resolved when the email is sent
2079
1957
  */
2080
- async handleTokenCreationEvent(tokenData, fromAddress) {
1958
+ async handleTokenCreationEvent(tokenData, mailgunConfig) {
2081
1959
  try {
2082
1960
  Logger.info(
2083
1961
  "[PractitionerInviteMailingService] Handling token creation event for token:",
@@ -2099,8 +1977,12 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2099
1977
  if (!tokenData.expiresAt) {
2100
1978
  throw new Error(`Token ${tokenData.id} is missing expiration date`);
2101
1979
  }
2102
- if (!tokenData.status) {
2103
- throw new Error(`Token ${tokenData.id} has no status defined`);
1980
+ if (tokenData.status !== "active" /* ACTIVE */ && String(tokenData.status).toLowerCase() !== "active") {
1981
+ Logger.warn(
1982
+ "[PractitionerInviteMailingService] Token is not active, skipping email.",
1983
+ { tokenId: tokenData.id, status: tokenData.status }
1984
+ );
1985
+ return;
2104
1986
  }
2105
1987
  Logger.info(
2106
1988
  `[PractitionerInviteMailingService] Token status validation:`,
@@ -2144,11 +2026,11 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2144
2026
  Logger.info(
2145
2027
  `[PractitionerInviteMailingService] Clinic found: ${clinicData.name}`
2146
2028
  );
2147
- if (!fromAddress) {
2029
+ if (!mailgunConfig.fromAddress) {
2148
2030
  Logger.warn(
2149
2031
  "[PractitionerInviteMailingService] No fromAddress provided, using default"
2150
2032
  );
2151
- fromAddress = this.DEFAULT_FROM_ADDRESS;
2033
+ mailgunConfig.fromAddress = `MetaEstetics <no-reply@${this.DEFAULT_MAILGUN_DOMAIN}>`;
2152
2034
  }
2153
2035
  const emailData = {
2154
2036
  token: {
@@ -2170,7 +2052,9 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2170
2052
  contactName: "Clinic Admin"
2171
2053
  },
2172
2054
  options: {
2173
- fromAddress
2055
+ fromAddress: mailgunConfig.fromAddress,
2056
+ mailgunDomain: mailgunConfig.domain,
2057
+ registrationUrl: mailgunConfig.registrationUrl
2174
2058
  }
2175
2059
  };
2176
2060
  Logger.info(
@@ -2184,8 +2068,11 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2184
2068
  Logger.error(
2185
2069
  "[PractitionerInviteMailingService] Error handling token creation event:",
2186
2070
  {
2187
- error: error instanceof Error ? error.message : error,
2188
- stack: error instanceof Error ? error.stack : void 0
2071
+ errorMessage: error.message,
2072
+ errorDetails: error.details,
2073
+ errorStatus: error.status,
2074
+ stack: error.stack,
2075
+ tokenId: tokenData == null ? void 0 : tokenData.id
2189
2076
  }
2190
2077
  );
2191
2078
  throw error;
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.51",
4
+ "version": "1.5.52",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",