@blackcode_sa/metaestetics-api 1.5.44 → 1.5.46
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.
- package/dist/admin/index.js +236 -34
- package/dist/admin/index.mjs +243 -34
- package/dist/index.d.mts +28 -2
- package/dist/index.d.ts +28 -2
- package/dist/index.js +1096 -942
- package/dist/index.mjs +969 -797
- package/package.json +4 -1
- package/src/admin/logger/index.ts +78 -0
- package/src/admin/mailing/base.mailing.service.ts +116 -39
- package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +100 -10
- package/src/services/patient/patient.service.ts +180 -57
- package/src/services/patient/utils/clinic.utils.ts +80 -0
- package/src/services/patient/utils/practitioner.utils.ts +80 -0
package/dist/admin/index.js
CHANGED
|
@@ -1559,9 +1559,73 @@ var PatientAggregationService = class {
|
|
|
1559
1559
|
|
|
1560
1560
|
// src/admin/mailing/base.mailing.service.ts
|
|
1561
1561
|
var admin6 = __toESM(require("firebase-admin"));
|
|
1562
|
+
|
|
1563
|
+
// src/admin/logger/index.ts
|
|
1564
|
+
var firebaseFunctionsLogger;
|
|
1565
|
+
try {
|
|
1566
|
+
firebaseFunctionsLogger = require("firebase-functions/logger");
|
|
1567
|
+
require("firebase-functions/logger/compat");
|
|
1568
|
+
} catch (e) {
|
|
1569
|
+
}
|
|
1570
|
+
var Logger = class {
|
|
1571
|
+
/**
|
|
1572
|
+
* Log an error message
|
|
1573
|
+
* @param message Message to log
|
|
1574
|
+
* @param data Optional data to include
|
|
1575
|
+
*/
|
|
1576
|
+
static error(message, data) {
|
|
1577
|
+
if (firebaseFunctionsLogger) {
|
|
1578
|
+
firebaseFunctionsLogger.error(message, data);
|
|
1579
|
+
} else {
|
|
1580
|
+
console.error(message, data !== void 0 ? data : "");
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Log a warning message
|
|
1585
|
+
* @param message Message to log
|
|
1586
|
+
* @param data Optional data to include
|
|
1587
|
+
*/
|
|
1588
|
+
static warn(message, data) {
|
|
1589
|
+
if (firebaseFunctionsLogger) {
|
|
1590
|
+
firebaseFunctionsLogger.warn(message, data);
|
|
1591
|
+
} else {
|
|
1592
|
+
console.warn(message, data !== void 0 ? data : "");
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* Log an info message
|
|
1597
|
+
* @param message Message to log
|
|
1598
|
+
* @param data Optional data to include
|
|
1599
|
+
*/
|
|
1600
|
+
static info(message, data) {
|
|
1601
|
+
if (firebaseFunctionsLogger) {
|
|
1602
|
+
firebaseFunctionsLogger.info(message, data);
|
|
1603
|
+
} else {
|
|
1604
|
+
console.info(message, data !== void 0 ? data : "");
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Log a debug message
|
|
1609
|
+
* @param message Message to log
|
|
1610
|
+
* @param data Optional data to include
|
|
1611
|
+
*/
|
|
1612
|
+
static debug(message, data) {
|
|
1613
|
+
if (firebaseFunctionsLogger) {
|
|
1614
|
+
firebaseFunctionsLogger.debug(message, data);
|
|
1615
|
+
} else {
|
|
1616
|
+
console.debug(message, data !== void 0 ? data : "");
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
// src/admin/mailing/base.mailing.service.ts
|
|
1562
1622
|
var BaseMailingService = class {
|
|
1563
1623
|
// Removed config property as it's no longer managed here
|
|
1564
|
-
//
|
|
1624
|
+
// import {
|
|
1625
|
+
// getMailgunConfig,
|
|
1626
|
+
// createMailgunClient,
|
|
1627
|
+
// MailgunConfig,
|
|
1628
|
+
// } from "./mailgun.config";
|
|
1565
1629
|
/**
|
|
1566
1630
|
* Constructor for BaseMailingService
|
|
1567
1631
|
* @param firestore Firestore instance provided by the caller
|
|
@@ -1570,6 +1634,15 @@ var BaseMailingService = class {
|
|
|
1570
1634
|
constructor(firestore8, mailgunClient) {
|
|
1571
1635
|
this.db = firestore8;
|
|
1572
1636
|
this.mailgunClient = mailgunClient;
|
|
1637
|
+
if (!this.db) {
|
|
1638
|
+
Logger.error("[BaseMailingService] No Firestore instance provided");
|
|
1639
|
+
throw new Error("Firestore instance is required");
|
|
1640
|
+
}
|
|
1641
|
+
if (!this.mailgunClient) {
|
|
1642
|
+
Logger.error("[BaseMailingService] No Mailgun client provided");
|
|
1643
|
+
throw new Error("Mailgun client is required");
|
|
1644
|
+
}
|
|
1645
|
+
Logger.info("[BaseMailingService] Service initialized successfully");
|
|
1573
1646
|
}
|
|
1574
1647
|
/**
|
|
1575
1648
|
* Sends an email using Mailgun
|
|
@@ -1578,29 +1651,69 @@ var BaseMailingService = class {
|
|
|
1578
1651
|
*/
|
|
1579
1652
|
async sendEmail(data) {
|
|
1580
1653
|
try {
|
|
1654
|
+
if (!data) {
|
|
1655
|
+
throw new Error("Email data object is required");
|
|
1656
|
+
}
|
|
1657
|
+
if (!data.to) {
|
|
1658
|
+
throw new Error("Email 'to' address is required");
|
|
1659
|
+
}
|
|
1581
1660
|
if (!data.from) {
|
|
1582
1661
|
throw new Error(
|
|
1583
1662
|
"Email 'from' address must be provided in sendEmail data."
|
|
1584
1663
|
);
|
|
1585
1664
|
}
|
|
1665
|
+
if (!data.subject) {
|
|
1666
|
+
throw new Error("Email 'subject' is required");
|
|
1667
|
+
}
|
|
1668
|
+
if (!data.html && !data.text) {
|
|
1669
|
+
throw new Error("Email must have either 'html' or 'text' content");
|
|
1670
|
+
}
|
|
1671
|
+
Logger.info("[BaseMailingService] Sending email via Mailgun", {
|
|
1672
|
+
to: data.to,
|
|
1673
|
+
from: data.from,
|
|
1674
|
+
subject: data.subject,
|
|
1675
|
+
hasHtml: !!data.html,
|
|
1676
|
+
hasText: !!data.text
|
|
1677
|
+
});
|
|
1586
1678
|
return await new Promise(
|
|
1587
1679
|
(resolve, reject) => {
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
} else {
|
|
1593
|
-
console.log(
|
|
1594
|
-
"[BaseMailingService] Email sent successfully:",
|
|
1595
|
-
body
|
|
1596
|
-
);
|
|
1597
|
-
resolve(body);
|
|
1680
|
+
try {
|
|
1681
|
+
const messagesApi = this.mailgunClient.messages();
|
|
1682
|
+
if (!messagesApi) {
|
|
1683
|
+
throw new Error("Could not get Mailgun messages API");
|
|
1598
1684
|
}
|
|
1599
|
-
|
|
1685
|
+
messagesApi.send(data, (error, body) => {
|
|
1686
|
+
if (error) {
|
|
1687
|
+
Logger.error("[BaseMailingService] Mailgun API error:", {
|
|
1688
|
+
error: error instanceof Error ? error.message : error,
|
|
1689
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1690
|
+
});
|
|
1691
|
+
reject(error);
|
|
1692
|
+
} else {
|
|
1693
|
+
Logger.info(
|
|
1694
|
+
"[BaseMailingService] Email sent successfully:",
|
|
1695
|
+
body
|
|
1696
|
+
);
|
|
1697
|
+
resolve(body);
|
|
1698
|
+
}
|
|
1699
|
+
});
|
|
1700
|
+
} catch (sendError) {
|
|
1701
|
+
Logger.error(
|
|
1702
|
+
"[BaseMailingService] Error in mailgun.messages().send():",
|
|
1703
|
+
{
|
|
1704
|
+
error: sendError instanceof Error ? sendError.message : sendError,
|
|
1705
|
+
stack: sendError instanceof Error ? sendError.stack : void 0
|
|
1706
|
+
}
|
|
1707
|
+
);
|
|
1708
|
+
reject(sendError);
|
|
1709
|
+
}
|
|
1600
1710
|
}
|
|
1601
1711
|
);
|
|
1602
1712
|
} catch (error) {
|
|
1603
|
-
|
|
1713
|
+
Logger.error("[BaseMailingService] Error in sendEmail:", {
|
|
1714
|
+
error: error instanceof Error ? error.message : error,
|
|
1715
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1716
|
+
});
|
|
1604
1717
|
throw error;
|
|
1605
1718
|
}
|
|
1606
1719
|
}
|
|
@@ -1618,14 +1731,17 @@ var BaseMailingService = class {
|
|
|
1618
1731
|
subject: emailData.subject,
|
|
1619
1732
|
templateName: emailData.templateName,
|
|
1620
1733
|
success,
|
|
1621
|
-
error: error ? JSON.stringify(error) : null,
|
|
1734
|
+
error: error ? error instanceof Error ? { message: error.message, stack: error.stack } : JSON.stringify(error) : null,
|
|
1622
1735
|
sentAt: admin6.firestore.FieldValue.serverTimestamp()
|
|
1623
1736
|
});
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
"[BaseMailingService] Error logging email attempt:",
|
|
1627
|
-
logError
|
|
1737
|
+
Logger.info(
|
|
1738
|
+
`[BaseMailingService] Email log recorded. Success: ${success}`
|
|
1628
1739
|
);
|
|
1740
|
+
} catch (logError) {
|
|
1741
|
+
Logger.error("[BaseMailingService] Error logging email attempt:", {
|
|
1742
|
+
error: logError instanceof Error ? logError.message : logError,
|
|
1743
|
+
stack: logError instanceof Error ? logError.stack : void 0
|
|
1744
|
+
});
|
|
1629
1745
|
}
|
|
1630
1746
|
}
|
|
1631
1747
|
/**
|
|
@@ -1635,12 +1751,24 @@ var BaseMailingService = class {
|
|
|
1635
1751
|
* @returns Rendered HTML string
|
|
1636
1752
|
*/
|
|
1637
1753
|
renderTemplate(template, variables) {
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1754
|
+
if (!template) {
|
|
1755
|
+
throw new Error("Email template is required");
|
|
1756
|
+
}
|
|
1757
|
+
try {
|
|
1758
|
+
let rendered = template;
|
|
1759
|
+
Object.entries(variables).forEach(([key, value]) => {
|
|
1760
|
+
const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
|
|
1761
|
+
rendered = rendered.replace(regex, value || "");
|
|
1762
|
+
});
|
|
1763
|
+
return rendered;
|
|
1764
|
+
} catch (renderError) {
|
|
1765
|
+
Logger.error("[BaseMailingService] Error rendering template:", {
|
|
1766
|
+
error: renderError instanceof Error ? renderError.message : renderError
|
|
1767
|
+
});
|
|
1768
|
+
throw new Error(
|
|
1769
|
+
`Template rendering failed: ${renderError instanceof Error ? renderError.message : "Unknown error"}`
|
|
1770
|
+
);
|
|
1771
|
+
}
|
|
1644
1772
|
}
|
|
1645
1773
|
};
|
|
1646
1774
|
|
|
@@ -1753,9 +1881,9 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1753
1881
|
*/
|
|
1754
1882
|
constructor(firestore8, mailgunClient) {
|
|
1755
1883
|
super(firestore8, mailgunClient);
|
|
1756
|
-
this.DEFAULT_REGISTRATION_URL = "https://
|
|
1884
|
+
this.DEFAULT_REGISTRATION_URL = "https://metaestetics.net/register";
|
|
1757
1885
|
this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
|
|
1758
|
-
this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@
|
|
1886
|
+
this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaestetics.net>";
|
|
1759
1887
|
}
|
|
1760
1888
|
/**
|
|
1761
1889
|
* Sends a practitioner invitation email
|
|
@@ -1765,7 +1893,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1765
1893
|
async sendInvitationEmail(data) {
|
|
1766
1894
|
var _a, _b, _c, _d;
|
|
1767
1895
|
try {
|
|
1768
|
-
|
|
1896
|
+
Logger.info(
|
|
1769
1897
|
"[PractitionerInviteMailingService] Sending invitation email to",
|
|
1770
1898
|
data.token.email
|
|
1771
1899
|
);
|
|
@@ -1792,6 +1920,16 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1792
1920
|
contactEmail,
|
|
1793
1921
|
currentYear
|
|
1794
1922
|
};
|
|
1923
|
+
Logger.info("[PractitionerInviteMailingService] Template variables:", {
|
|
1924
|
+
clinicName: templateVariables.clinicName,
|
|
1925
|
+
practitionerName: templateVariables.practitionerName,
|
|
1926
|
+
expirationDate: templateVariables.expirationDate,
|
|
1927
|
+
registrationUrl: templateVariables.registrationUrl,
|
|
1928
|
+
contactName: templateVariables.contactName,
|
|
1929
|
+
contactEmail: templateVariables.contactEmail,
|
|
1930
|
+
// Don't log the invite token for security
|
|
1931
|
+
hasInviteToken: !!templateVariables.inviteToken
|
|
1932
|
+
});
|
|
1795
1933
|
const html = this.renderTemplate(
|
|
1796
1934
|
practitionerInvitationTemplate,
|
|
1797
1935
|
templateVariables
|
|
@@ -1802,6 +1940,15 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1802
1940
|
subject,
|
|
1803
1941
|
html
|
|
1804
1942
|
};
|
|
1943
|
+
Logger.info(
|
|
1944
|
+
"[PractitionerInviteMailingService] Sending email with data:",
|
|
1945
|
+
{
|
|
1946
|
+
to: emailData.to,
|
|
1947
|
+
from: emailData.from,
|
|
1948
|
+
subject: emailData.subject,
|
|
1949
|
+
hasHtml: !!emailData.html
|
|
1950
|
+
}
|
|
1951
|
+
);
|
|
1805
1952
|
const result = await this.sendEmail(emailData);
|
|
1806
1953
|
await this.logEmailAttempt(
|
|
1807
1954
|
{
|
|
@@ -1813,9 +1960,12 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1813
1960
|
);
|
|
1814
1961
|
return result;
|
|
1815
1962
|
} catch (error) {
|
|
1816
|
-
|
|
1963
|
+
Logger.error(
|
|
1817
1964
|
"[PractitionerInviteMailingService] Error sending invitation email:",
|
|
1818
|
-
|
|
1965
|
+
{
|
|
1966
|
+
error: error instanceof Error ? error.message : error,
|
|
1967
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1968
|
+
}
|
|
1819
1969
|
);
|
|
1820
1970
|
await this.logEmailAttempt(
|
|
1821
1971
|
{
|
|
@@ -1839,22 +1989,66 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1839
1989
|
*/
|
|
1840
1990
|
async handleTokenCreationEvent(tokenData, fromAddress) {
|
|
1841
1991
|
try {
|
|
1842
|
-
|
|
1992
|
+
Logger.info(
|
|
1843
1993
|
"[PractitionerInviteMailingService] Handling token creation event for token:",
|
|
1844
1994
|
tokenData.id
|
|
1845
1995
|
);
|
|
1996
|
+
if (!tokenData || !tokenData.id || !tokenData.token || !tokenData.email) {
|
|
1997
|
+
throw new Error(
|
|
1998
|
+
`Invalid token data: Missing required properties. Token ID: ${tokenData == null ? void 0 : tokenData.id}`
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
if (!tokenData.practitionerId) {
|
|
2002
|
+
throw new Error(
|
|
2003
|
+
`Token ${tokenData.id} is missing practitionerId reference`
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
if (!tokenData.clinicId) {
|
|
2007
|
+
throw new Error(`Token ${tokenData.id} is missing clinicId reference`);
|
|
2008
|
+
}
|
|
2009
|
+
if (!tokenData.expiresAt) {
|
|
2010
|
+
throw new Error(`Token ${tokenData.id} is missing expiration date`);
|
|
2011
|
+
}
|
|
2012
|
+
Logger.info(
|
|
2013
|
+
`[PractitionerInviteMailingService] Fetching practitioner data: ${tokenData.practitionerId}`
|
|
2014
|
+
);
|
|
1846
2015
|
const practitionerRef = this.db.collection(PRACTITIONERS_COLLECTION).doc(tokenData.practitionerId);
|
|
1847
2016
|
const practitionerDoc = await practitionerRef.get();
|
|
1848
2017
|
if (!practitionerDoc.exists) {
|
|
1849
2018
|
throw new Error(`Practitioner ${tokenData.practitionerId} not found`);
|
|
1850
2019
|
}
|
|
1851
2020
|
const practitionerData = practitionerDoc.data();
|
|
2021
|
+
if (!practitionerData || !practitionerData.basicInfo) {
|
|
2022
|
+
throw new Error(
|
|
2023
|
+
`Practitioner ${tokenData.practitionerId} has invalid data structure`
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
Logger.info(
|
|
2027
|
+
`[PractitionerInviteMailingService] Practitioner found: ${practitionerData.basicInfo.firstName} ${practitionerData.basicInfo.lastName}`
|
|
2028
|
+
);
|
|
2029
|
+
Logger.info(
|
|
2030
|
+
`[PractitionerInviteMailingService] Fetching clinic data: ${tokenData.clinicId}`
|
|
2031
|
+
);
|
|
1852
2032
|
const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(tokenData.clinicId);
|
|
1853
2033
|
const clinicDoc = await clinicRef.get();
|
|
1854
2034
|
if (!clinicDoc.exists) {
|
|
1855
2035
|
throw new Error(`Clinic ${tokenData.clinicId} not found`);
|
|
1856
2036
|
}
|
|
1857
2037
|
const clinicData = clinicDoc.data();
|
|
2038
|
+
if (!clinicData || !clinicData.contactInfo) {
|
|
2039
|
+
throw new Error(
|
|
2040
|
+
`Clinic ${tokenData.clinicId} has invalid data structure`
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
Logger.info(
|
|
2044
|
+
`[PractitionerInviteMailingService] Clinic found: ${clinicData.name}`
|
|
2045
|
+
);
|
|
2046
|
+
if (!fromAddress) {
|
|
2047
|
+
Logger.warn(
|
|
2048
|
+
"[PractitionerInviteMailingService] No fromAddress provided, using default"
|
|
2049
|
+
);
|
|
2050
|
+
fromAddress = this.DEFAULT_FROM_ADDRESS;
|
|
2051
|
+
}
|
|
1858
2052
|
const emailData = {
|
|
1859
2053
|
token: {
|
|
1860
2054
|
id: tokenData.id,
|
|
@@ -1870,20 +2064,28 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1870
2064
|
},
|
|
1871
2065
|
clinic: {
|
|
1872
2066
|
name: clinicData.name || "Medical Clinic",
|
|
1873
|
-
contactEmail: clinicData.contactInfo.email || "contact@medclinic.com"
|
|
2067
|
+
contactEmail: clinicData.contactInfo.email || "contact@medclinic.com",
|
|
2068
|
+
// Since there's no contactPerson in the Clinic model, we'll just use "Clinic Admin"
|
|
2069
|
+
contactName: "Clinic Admin"
|
|
1874
2070
|
},
|
|
1875
2071
|
options: {
|
|
1876
2072
|
fromAddress
|
|
1877
2073
|
}
|
|
1878
2074
|
};
|
|
2075
|
+
Logger.info(
|
|
2076
|
+
"[PractitionerInviteMailingService] Email data prepared, sending invitation"
|
|
2077
|
+
);
|
|
1879
2078
|
await this.sendInvitationEmail(emailData);
|
|
1880
|
-
|
|
2079
|
+
Logger.info(
|
|
1881
2080
|
"[PractitionerInviteMailingService] Invitation email sent successfully"
|
|
1882
2081
|
);
|
|
1883
2082
|
} catch (error) {
|
|
1884
|
-
|
|
2083
|
+
Logger.error(
|
|
1885
2084
|
"[PractitionerInviteMailingService] Error handling token creation event:",
|
|
1886
|
-
|
|
2085
|
+
{
|
|
2086
|
+
error: error instanceof Error ? error.message : error,
|
|
2087
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
2088
|
+
}
|
|
1887
2089
|
);
|
|
1888
2090
|
throw error;
|
|
1889
2091
|
}
|