@blackcode_sa/metaestetics-api 1.5.45 → 1.5.47
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.d.mts +4 -0
- package/dist/admin/index.d.ts +4 -0
- package/dist/admin/index.js +134 -48
- package/dist/admin/index.mjs +140 -48
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/package.json +4 -1
- package/src/admin/index.ts +2 -5
- package/src/admin/logger/index.ts +78 -0
- package/src/admin/mailing/base.mailing.service.ts +33 -35
- package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +39 -20
- package/src/types/practitioner/index.ts +5 -0
package/dist/admin/index.d.mts
CHANGED
package/dist/admin/index.d.ts
CHANGED
package/dist/admin/index.js
CHANGED
|
@@ -40,6 +40,7 @@ __export(index_exports, {
|
|
|
40
40
|
PatientAggregationService: () => PatientAggregationService,
|
|
41
41
|
PractitionerAggregationService: () => PractitionerAggregationService,
|
|
42
42
|
PractitionerInviteMailingService: () => PractitionerInviteMailingService,
|
|
43
|
+
PractitionerTokenStatus: () => PractitionerTokenStatus,
|
|
43
44
|
ProcedureAggregationService: () => ProcedureAggregationService,
|
|
44
45
|
UserRole: () => UserRole
|
|
45
46
|
});
|
|
@@ -238,6 +239,13 @@ var admin2 = __toESM(require("firebase-admin"));
|
|
|
238
239
|
|
|
239
240
|
// src/types/practitioner/index.ts
|
|
240
241
|
var PRACTITIONERS_COLLECTION = "practitioners";
|
|
242
|
+
var PractitionerTokenStatus = /* @__PURE__ */ ((PractitionerTokenStatus2) => {
|
|
243
|
+
PractitionerTokenStatus2["ACTIVE"] = "active";
|
|
244
|
+
PractitionerTokenStatus2["USED"] = "used";
|
|
245
|
+
PractitionerTokenStatus2["EXPIRED"] = "expired";
|
|
246
|
+
PractitionerTokenStatus2["REVOKED"] = "revoked";
|
|
247
|
+
return PractitionerTokenStatus2;
|
|
248
|
+
})(PractitionerTokenStatus || {});
|
|
241
249
|
|
|
242
250
|
// src/types/procedure/index.ts
|
|
243
251
|
var PROCEDURES_COLLECTION = "procedures";
|
|
@@ -1559,9 +1567,73 @@ var PatientAggregationService = class {
|
|
|
1559
1567
|
|
|
1560
1568
|
// src/admin/mailing/base.mailing.service.ts
|
|
1561
1569
|
var admin6 = __toESM(require("firebase-admin"));
|
|
1570
|
+
|
|
1571
|
+
// src/admin/logger/index.ts
|
|
1572
|
+
var firebaseFunctionsLogger;
|
|
1573
|
+
try {
|
|
1574
|
+
firebaseFunctionsLogger = require("firebase-functions/logger");
|
|
1575
|
+
require("firebase-functions/logger/compat");
|
|
1576
|
+
} catch (e) {
|
|
1577
|
+
}
|
|
1578
|
+
var Logger = class {
|
|
1579
|
+
/**
|
|
1580
|
+
* Log an error message
|
|
1581
|
+
* @param message Message to log
|
|
1582
|
+
* @param data Optional data to include
|
|
1583
|
+
*/
|
|
1584
|
+
static error(message, data) {
|
|
1585
|
+
if (firebaseFunctionsLogger) {
|
|
1586
|
+
firebaseFunctionsLogger.error(message, data);
|
|
1587
|
+
} else {
|
|
1588
|
+
console.error(message, data !== void 0 ? data : "");
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Log a warning message
|
|
1593
|
+
* @param message Message to log
|
|
1594
|
+
* @param data Optional data to include
|
|
1595
|
+
*/
|
|
1596
|
+
static warn(message, data) {
|
|
1597
|
+
if (firebaseFunctionsLogger) {
|
|
1598
|
+
firebaseFunctionsLogger.warn(message, data);
|
|
1599
|
+
} else {
|
|
1600
|
+
console.warn(message, data !== void 0 ? data : "");
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* Log an info message
|
|
1605
|
+
* @param message Message to log
|
|
1606
|
+
* @param data Optional data to include
|
|
1607
|
+
*/
|
|
1608
|
+
static info(message, data) {
|
|
1609
|
+
if (firebaseFunctionsLogger) {
|
|
1610
|
+
firebaseFunctionsLogger.info(message, data);
|
|
1611
|
+
} else {
|
|
1612
|
+
console.info(message, data !== void 0 ? data : "");
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Log a debug message
|
|
1617
|
+
* @param message Message to log
|
|
1618
|
+
* @param data Optional data to include
|
|
1619
|
+
*/
|
|
1620
|
+
static debug(message, data) {
|
|
1621
|
+
if (firebaseFunctionsLogger) {
|
|
1622
|
+
firebaseFunctionsLogger.debug(message, data);
|
|
1623
|
+
} else {
|
|
1624
|
+
console.debug(message, data !== void 0 ? data : "");
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
};
|
|
1628
|
+
|
|
1629
|
+
// src/admin/mailing/base.mailing.service.ts
|
|
1562
1630
|
var BaseMailingService = class {
|
|
1563
1631
|
// Removed config property as it's no longer managed here
|
|
1564
|
-
//
|
|
1632
|
+
// import {
|
|
1633
|
+
// getMailgunConfig,
|
|
1634
|
+
// createMailgunClient,
|
|
1635
|
+
// MailgunConfig,
|
|
1636
|
+
// } from "./mailgun.config";
|
|
1565
1637
|
/**
|
|
1566
1638
|
* Constructor for BaseMailingService
|
|
1567
1639
|
* @param firestore Firestore instance provided by the caller
|
|
@@ -1571,14 +1643,14 @@ var BaseMailingService = class {
|
|
|
1571
1643
|
this.db = firestore8;
|
|
1572
1644
|
this.mailgunClient = mailgunClient;
|
|
1573
1645
|
if (!this.db) {
|
|
1574
|
-
|
|
1646
|
+
Logger.error("[BaseMailingService] No Firestore instance provided");
|
|
1575
1647
|
throw new Error("Firestore instance is required");
|
|
1576
1648
|
}
|
|
1577
1649
|
if (!this.mailgunClient) {
|
|
1578
|
-
|
|
1650
|
+
Logger.error("[BaseMailingService] No Mailgun client provided");
|
|
1579
1651
|
throw new Error("Mailgun client is required");
|
|
1580
1652
|
}
|
|
1581
|
-
|
|
1653
|
+
Logger.info("[BaseMailingService] Service initialized successfully");
|
|
1582
1654
|
}
|
|
1583
1655
|
/**
|
|
1584
1656
|
* Sends an email using Mailgun
|
|
@@ -1604,7 +1676,7 @@ var BaseMailingService = class {
|
|
|
1604
1676
|
if (!data.html && !data.text) {
|
|
1605
1677
|
throw new Error("Email must have either 'html' or 'text' content");
|
|
1606
1678
|
}
|
|
1607
|
-
|
|
1679
|
+
Logger.info("[BaseMailingService] Sending email via Mailgun", {
|
|
1608
1680
|
to: data.to,
|
|
1609
1681
|
from: data.from,
|
|
1610
1682
|
subject: data.subject,
|
|
@@ -1620,14 +1692,13 @@ var BaseMailingService = class {
|
|
|
1620
1692
|
}
|
|
1621
1693
|
messagesApi.send(data, (error, body) => {
|
|
1622
1694
|
if (error) {
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
error instanceof Error ? error.
|
|
1626
|
-
|
|
1627
|
-
);
|
|
1695
|
+
Logger.error("[BaseMailingService] Mailgun API error:", {
|
|
1696
|
+
error: error instanceof Error ? error.message : error,
|
|
1697
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1698
|
+
});
|
|
1628
1699
|
reject(error);
|
|
1629
1700
|
} else {
|
|
1630
|
-
|
|
1701
|
+
Logger.info(
|
|
1631
1702
|
"[BaseMailingService] Email sent successfully:",
|
|
1632
1703
|
body
|
|
1633
1704
|
);
|
|
@@ -1635,21 +1706,22 @@ var BaseMailingService = class {
|
|
|
1635
1706
|
}
|
|
1636
1707
|
});
|
|
1637
1708
|
} catch (sendError) {
|
|
1638
|
-
|
|
1709
|
+
Logger.error(
|
|
1639
1710
|
"[BaseMailingService] Error in mailgun.messages().send():",
|
|
1640
|
-
|
|
1641
|
-
|
|
1711
|
+
{
|
|
1712
|
+
error: sendError instanceof Error ? sendError.message : sendError,
|
|
1713
|
+
stack: sendError instanceof Error ? sendError.stack : void 0
|
|
1714
|
+
}
|
|
1642
1715
|
);
|
|
1643
1716
|
reject(sendError);
|
|
1644
1717
|
}
|
|
1645
1718
|
}
|
|
1646
1719
|
);
|
|
1647
1720
|
} catch (error) {
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
error instanceof Error ? error.
|
|
1651
|
-
|
|
1652
|
-
);
|
|
1721
|
+
Logger.error("[BaseMailingService] Error in sendEmail:", {
|
|
1722
|
+
error: error instanceof Error ? error.message : error,
|
|
1723
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1724
|
+
});
|
|
1653
1725
|
throw error;
|
|
1654
1726
|
}
|
|
1655
1727
|
}
|
|
@@ -1670,15 +1742,14 @@ var BaseMailingService = class {
|
|
|
1670
1742
|
error: error ? error instanceof Error ? { message: error.message, stack: error.stack } : JSON.stringify(error) : null,
|
|
1671
1743
|
sentAt: admin6.firestore.FieldValue.serverTimestamp()
|
|
1672
1744
|
});
|
|
1673
|
-
|
|
1745
|
+
Logger.info(
|
|
1674
1746
|
`[BaseMailingService] Email log recorded. Success: ${success}`
|
|
1675
1747
|
);
|
|
1676
1748
|
} catch (logError) {
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
logError instanceof Error ? logError.
|
|
1680
|
-
|
|
1681
|
-
);
|
|
1749
|
+
Logger.error("[BaseMailingService] Error logging email attempt:", {
|
|
1750
|
+
error: logError instanceof Error ? logError.message : logError,
|
|
1751
|
+
stack: logError instanceof Error ? logError.stack : void 0
|
|
1752
|
+
});
|
|
1682
1753
|
}
|
|
1683
1754
|
}
|
|
1684
1755
|
/**
|
|
@@ -1699,10 +1770,9 @@ var BaseMailingService = class {
|
|
|
1699
1770
|
});
|
|
1700
1771
|
return rendered;
|
|
1701
1772
|
} catch (renderError) {
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
);
|
|
1773
|
+
Logger.error("[BaseMailingService] Error rendering template:", {
|
|
1774
|
+
error: renderError instanceof Error ? renderError.message : renderError
|
|
1775
|
+
});
|
|
1706
1776
|
throw new Error(
|
|
1707
1777
|
`Template rendering failed: ${renderError instanceof Error ? renderError.message : "Unknown error"}`
|
|
1708
1778
|
);
|
|
@@ -1819,9 +1889,9 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1819
1889
|
*/
|
|
1820
1890
|
constructor(firestore8, mailgunClient) {
|
|
1821
1891
|
super(firestore8, mailgunClient);
|
|
1822
|
-
this.DEFAULT_REGISTRATION_URL = "https://
|
|
1892
|
+
this.DEFAULT_REGISTRATION_URL = "https://metaestetics.net/register";
|
|
1823
1893
|
this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
|
|
1824
|
-
this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@
|
|
1894
|
+
this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaestetics.net>";
|
|
1825
1895
|
}
|
|
1826
1896
|
/**
|
|
1827
1897
|
* Sends a practitioner invitation email
|
|
@@ -1831,7 +1901,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1831
1901
|
async sendInvitationEmail(data) {
|
|
1832
1902
|
var _a, _b, _c, _d;
|
|
1833
1903
|
try {
|
|
1834
|
-
|
|
1904
|
+
Logger.info(
|
|
1835
1905
|
"[PractitionerInviteMailingService] Sending invitation email to",
|
|
1836
1906
|
data.token.email
|
|
1837
1907
|
);
|
|
@@ -1858,7 +1928,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1858
1928
|
contactEmail,
|
|
1859
1929
|
currentYear
|
|
1860
1930
|
};
|
|
1861
|
-
|
|
1931
|
+
Logger.info("[PractitionerInviteMailingService] Template variables:", {
|
|
1862
1932
|
clinicName: templateVariables.clinicName,
|
|
1863
1933
|
practitionerName: templateVariables.practitionerName,
|
|
1864
1934
|
expirationDate: templateVariables.expirationDate,
|
|
@@ -1878,7 +1948,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1878
1948
|
subject,
|
|
1879
1949
|
html
|
|
1880
1950
|
};
|
|
1881
|
-
|
|
1951
|
+
Logger.info(
|
|
1882
1952
|
"[PractitionerInviteMailingService] Sending email with data:",
|
|
1883
1953
|
{
|
|
1884
1954
|
to: emailData.to,
|
|
@@ -1898,10 +1968,12 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1898
1968
|
);
|
|
1899
1969
|
return result;
|
|
1900
1970
|
} catch (error) {
|
|
1901
|
-
|
|
1971
|
+
Logger.error(
|
|
1902
1972
|
"[PractitionerInviteMailingService] Error sending invitation email:",
|
|
1903
|
-
|
|
1904
|
-
|
|
1973
|
+
{
|
|
1974
|
+
error: error instanceof Error ? error.message : error,
|
|
1975
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1976
|
+
}
|
|
1905
1977
|
);
|
|
1906
1978
|
await this.logEmailAttempt(
|
|
1907
1979
|
{
|
|
@@ -1925,7 +1997,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1925
1997
|
*/
|
|
1926
1998
|
async handleTokenCreationEvent(tokenData, fromAddress) {
|
|
1927
1999
|
try {
|
|
1928
|
-
|
|
2000
|
+
Logger.info(
|
|
1929
2001
|
"[PractitionerInviteMailingService] Handling token creation event for token:",
|
|
1930
2002
|
tokenData.id
|
|
1931
2003
|
);
|
|
@@ -1945,7 +2017,18 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1945
2017
|
if (!tokenData.expiresAt) {
|
|
1946
2018
|
throw new Error(`Token ${tokenData.id} is missing expiration date`);
|
|
1947
2019
|
}
|
|
1948
|
-
|
|
2020
|
+
if (!tokenData.status) {
|
|
2021
|
+
throw new Error(`Token ${tokenData.id} has no status defined`);
|
|
2022
|
+
}
|
|
2023
|
+
Logger.info(
|
|
2024
|
+
`[PractitionerInviteMailingService] Token status validation:`,
|
|
2025
|
+
{
|
|
2026
|
+
tokenId: tokenData.id,
|
|
2027
|
+
status: tokenData.status,
|
|
2028
|
+
statusType: typeof tokenData.status
|
|
2029
|
+
}
|
|
2030
|
+
);
|
|
2031
|
+
Logger.info(
|
|
1949
2032
|
`[PractitionerInviteMailingService] Fetching practitioner data: ${tokenData.practitionerId}`
|
|
1950
2033
|
);
|
|
1951
2034
|
const practitionerRef = this.db.collection(PRACTITIONERS_COLLECTION).doc(tokenData.practitionerId);
|
|
@@ -1959,10 +2042,10 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1959
2042
|
`Practitioner ${tokenData.practitionerId} has invalid data structure`
|
|
1960
2043
|
);
|
|
1961
2044
|
}
|
|
1962
|
-
|
|
2045
|
+
Logger.info(
|
|
1963
2046
|
`[PractitionerInviteMailingService] Practitioner found: ${practitionerData.basicInfo.firstName} ${practitionerData.basicInfo.lastName}`
|
|
1964
2047
|
);
|
|
1965
|
-
|
|
2048
|
+
Logger.info(
|
|
1966
2049
|
`[PractitionerInviteMailingService] Fetching clinic data: ${tokenData.clinicId}`
|
|
1967
2050
|
);
|
|
1968
2051
|
const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(tokenData.clinicId);
|
|
@@ -1976,11 +2059,11 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1976
2059
|
`Clinic ${tokenData.clinicId} has invalid data structure`
|
|
1977
2060
|
);
|
|
1978
2061
|
}
|
|
1979
|
-
|
|
2062
|
+
Logger.info(
|
|
1980
2063
|
`[PractitionerInviteMailingService] Clinic found: ${clinicData.name}`
|
|
1981
2064
|
);
|
|
1982
2065
|
if (!fromAddress) {
|
|
1983
|
-
|
|
2066
|
+
Logger.warn(
|
|
1984
2067
|
"[PractitionerInviteMailingService] No fromAddress provided, using default"
|
|
1985
2068
|
);
|
|
1986
2069
|
fromAddress = this.DEFAULT_FROM_ADDRESS;
|
|
@@ -2008,18 +2091,20 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
2008
2091
|
fromAddress
|
|
2009
2092
|
}
|
|
2010
2093
|
};
|
|
2011
|
-
|
|
2094
|
+
Logger.info(
|
|
2012
2095
|
"[PractitionerInviteMailingService] Email data prepared, sending invitation"
|
|
2013
2096
|
);
|
|
2014
2097
|
await this.sendInvitationEmail(emailData);
|
|
2015
|
-
|
|
2098
|
+
Logger.info(
|
|
2016
2099
|
"[PractitionerInviteMailingService] Invitation email sent successfully"
|
|
2017
2100
|
);
|
|
2018
2101
|
} catch (error) {
|
|
2019
|
-
|
|
2102
|
+
Logger.error(
|
|
2020
2103
|
"[PractitionerInviteMailingService] Error handling token creation event:",
|
|
2021
|
-
|
|
2022
|
-
|
|
2104
|
+
{
|
|
2105
|
+
error: error instanceof Error ? error.message : error,
|
|
2106
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
2107
|
+
}
|
|
2023
2108
|
);
|
|
2024
2109
|
throw error;
|
|
2025
2110
|
}
|
|
@@ -2630,6 +2715,7 @@ console.log("[Admin Module] Initialized and services exported.");
|
|
|
2630
2715
|
PatientAggregationService,
|
|
2631
2716
|
PractitionerAggregationService,
|
|
2632
2717
|
PractitionerInviteMailingService,
|
|
2718
|
+
PractitionerTokenStatus,
|
|
2633
2719
|
ProcedureAggregationService,
|
|
2634
2720
|
UserRole
|
|
2635
2721
|
});
|
package/dist/admin/index.mjs
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/types/notifications/index.ts
|
|
2
9
|
var NotificationType = /* @__PURE__ */ ((NotificationType2) => {
|
|
3
10
|
NotificationType2["PRE_REQUIREMENT"] = "preRequirement";
|
|
@@ -191,6 +198,13 @@ import * as admin2 from "firebase-admin";
|
|
|
191
198
|
|
|
192
199
|
// src/types/practitioner/index.ts
|
|
193
200
|
var PRACTITIONERS_COLLECTION = "practitioners";
|
|
201
|
+
var PractitionerTokenStatus = /* @__PURE__ */ ((PractitionerTokenStatus2) => {
|
|
202
|
+
PractitionerTokenStatus2["ACTIVE"] = "active";
|
|
203
|
+
PractitionerTokenStatus2["USED"] = "used";
|
|
204
|
+
PractitionerTokenStatus2["EXPIRED"] = "expired";
|
|
205
|
+
PractitionerTokenStatus2["REVOKED"] = "revoked";
|
|
206
|
+
return PractitionerTokenStatus2;
|
|
207
|
+
})(PractitionerTokenStatus || {});
|
|
194
208
|
|
|
195
209
|
// src/types/procedure/index.ts
|
|
196
210
|
var PROCEDURES_COLLECTION = "procedures";
|
|
@@ -1512,9 +1526,73 @@ var PatientAggregationService = class {
|
|
|
1512
1526
|
|
|
1513
1527
|
// src/admin/mailing/base.mailing.service.ts
|
|
1514
1528
|
import * as admin6 from "firebase-admin";
|
|
1529
|
+
|
|
1530
|
+
// src/admin/logger/index.ts
|
|
1531
|
+
var firebaseFunctionsLogger;
|
|
1532
|
+
try {
|
|
1533
|
+
firebaseFunctionsLogger = __require("firebase-functions/logger");
|
|
1534
|
+
__require("firebase-functions/logger/compat");
|
|
1535
|
+
} catch (e) {
|
|
1536
|
+
}
|
|
1537
|
+
var Logger = class {
|
|
1538
|
+
/**
|
|
1539
|
+
* Log an error message
|
|
1540
|
+
* @param message Message to log
|
|
1541
|
+
* @param data Optional data to include
|
|
1542
|
+
*/
|
|
1543
|
+
static error(message, data) {
|
|
1544
|
+
if (firebaseFunctionsLogger) {
|
|
1545
|
+
firebaseFunctionsLogger.error(message, data);
|
|
1546
|
+
} else {
|
|
1547
|
+
console.error(message, data !== void 0 ? data : "");
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Log a warning message
|
|
1552
|
+
* @param message Message to log
|
|
1553
|
+
* @param data Optional data to include
|
|
1554
|
+
*/
|
|
1555
|
+
static warn(message, data) {
|
|
1556
|
+
if (firebaseFunctionsLogger) {
|
|
1557
|
+
firebaseFunctionsLogger.warn(message, data);
|
|
1558
|
+
} else {
|
|
1559
|
+
console.warn(message, data !== void 0 ? data : "");
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Log an info message
|
|
1564
|
+
* @param message Message to log
|
|
1565
|
+
* @param data Optional data to include
|
|
1566
|
+
*/
|
|
1567
|
+
static info(message, data) {
|
|
1568
|
+
if (firebaseFunctionsLogger) {
|
|
1569
|
+
firebaseFunctionsLogger.info(message, data);
|
|
1570
|
+
} else {
|
|
1571
|
+
console.info(message, data !== void 0 ? data : "");
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Log a debug message
|
|
1576
|
+
* @param message Message to log
|
|
1577
|
+
* @param data Optional data to include
|
|
1578
|
+
*/
|
|
1579
|
+
static debug(message, data) {
|
|
1580
|
+
if (firebaseFunctionsLogger) {
|
|
1581
|
+
firebaseFunctionsLogger.debug(message, data);
|
|
1582
|
+
} else {
|
|
1583
|
+
console.debug(message, data !== void 0 ? data : "");
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
|
|
1588
|
+
// src/admin/mailing/base.mailing.service.ts
|
|
1515
1589
|
var BaseMailingService = class {
|
|
1516
1590
|
// Removed config property as it's no longer managed here
|
|
1517
|
-
//
|
|
1591
|
+
// import {
|
|
1592
|
+
// getMailgunConfig,
|
|
1593
|
+
// createMailgunClient,
|
|
1594
|
+
// MailgunConfig,
|
|
1595
|
+
// } from "./mailgun.config";
|
|
1518
1596
|
/**
|
|
1519
1597
|
* Constructor for BaseMailingService
|
|
1520
1598
|
* @param firestore Firestore instance provided by the caller
|
|
@@ -1524,14 +1602,14 @@ var BaseMailingService = class {
|
|
|
1524
1602
|
this.db = firestore8;
|
|
1525
1603
|
this.mailgunClient = mailgunClient;
|
|
1526
1604
|
if (!this.db) {
|
|
1527
|
-
|
|
1605
|
+
Logger.error("[BaseMailingService] No Firestore instance provided");
|
|
1528
1606
|
throw new Error("Firestore instance is required");
|
|
1529
1607
|
}
|
|
1530
1608
|
if (!this.mailgunClient) {
|
|
1531
|
-
|
|
1609
|
+
Logger.error("[BaseMailingService] No Mailgun client provided");
|
|
1532
1610
|
throw new Error("Mailgun client is required");
|
|
1533
1611
|
}
|
|
1534
|
-
|
|
1612
|
+
Logger.info("[BaseMailingService] Service initialized successfully");
|
|
1535
1613
|
}
|
|
1536
1614
|
/**
|
|
1537
1615
|
* Sends an email using Mailgun
|
|
@@ -1557,7 +1635,7 @@ var BaseMailingService = class {
|
|
|
1557
1635
|
if (!data.html && !data.text) {
|
|
1558
1636
|
throw new Error("Email must have either 'html' or 'text' content");
|
|
1559
1637
|
}
|
|
1560
|
-
|
|
1638
|
+
Logger.info("[BaseMailingService] Sending email via Mailgun", {
|
|
1561
1639
|
to: data.to,
|
|
1562
1640
|
from: data.from,
|
|
1563
1641
|
subject: data.subject,
|
|
@@ -1573,14 +1651,13 @@ var BaseMailingService = class {
|
|
|
1573
1651
|
}
|
|
1574
1652
|
messagesApi.send(data, (error, body) => {
|
|
1575
1653
|
if (error) {
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
error instanceof Error ? error.
|
|
1579
|
-
|
|
1580
|
-
);
|
|
1654
|
+
Logger.error("[BaseMailingService] Mailgun API error:", {
|
|
1655
|
+
error: error instanceof Error ? error.message : error,
|
|
1656
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1657
|
+
});
|
|
1581
1658
|
reject(error);
|
|
1582
1659
|
} else {
|
|
1583
|
-
|
|
1660
|
+
Logger.info(
|
|
1584
1661
|
"[BaseMailingService] Email sent successfully:",
|
|
1585
1662
|
body
|
|
1586
1663
|
);
|
|
@@ -1588,21 +1665,22 @@ var BaseMailingService = class {
|
|
|
1588
1665
|
}
|
|
1589
1666
|
});
|
|
1590
1667
|
} catch (sendError) {
|
|
1591
|
-
|
|
1668
|
+
Logger.error(
|
|
1592
1669
|
"[BaseMailingService] Error in mailgun.messages().send():",
|
|
1593
|
-
|
|
1594
|
-
|
|
1670
|
+
{
|
|
1671
|
+
error: sendError instanceof Error ? sendError.message : sendError,
|
|
1672
|
+
stack: sendError instanceof Error ? sendError.stack : void 0
|
|
1673
|
+
}
|
|
1595
1674
|
);
|
|
1596
1675
|
reject(sendError);
|
|
1597
1676
|
}
|
|
1598
1677
|
}
|
|
1599
1678
|
);
|
|
1600
1679
|
} catch (error) {
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
error instanceof Error ? error.
|
|
1604
|
-
|
|
1605
|
-
);
|
|
1680
|
+
Logger.error("[BaseMailingService] Error in sendEmail:", {
|
|
1681
|
+
error: error instanceof Error ? error.message : error,
|
|
1682
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1683
|
+
});
|
|
1606
1684
|
throw error;
|
|
1607
1685
|
}
|
|
1608
1686
|
}
|
|
@@ -1623,15 +1701,14 @@ var BaseMailingService = class {
|
|
|
1623
1701
|
error: error ? error instanceof Error ? { message: error.message, stack: error.stack } : JSON.stringify(error) : null,
|
|
1624
1702
|
sentAt: admin6.firestore.FieldValue.serverTimestamp()
|
|
1625
1703
|
});
|
|
1626
|
-
|
|
1704
|
+
Logger.info(
|
|
1627
1705
|
`[BaseMailingService] Email log recorded. Success: ${success}`
|
|
1628
1706
|
);
|
|
1629
1707
|
} catch (logError) {
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
logError instanceof Error ? logError.
|
|
1633
|
-
|
|
1634
|
-
);
|
|
1708
|
+
Logger.error("[BaseMailingService] Error logging email attempt:", {
|
|
1709
|
+
error: logError instanceof Error ? logError.message : logError,
|
|
1710
|
+
stack: logError instanceof Error ? logError.stack : void 0
|
|
1711
|
+
});
|
|
1635
1712
|
}
|
|
1636
1713
|
}
|
|
1637
1714
|
/**
|
|
@@ -1652,10 +1729,9 @@ var BaseMailingService = class {
|
|
|
1652
1729
|
});
|
|
1653
1730
|
return rendered;
|
|
1654
1731
|
} catch (renderError) {
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
);
|
|
1732
|
+
Logger.error("[BaseMailingService] Error rendering template:", {
|
|
1733
|
+
error: renderError instanceof Error ? renderError.message : renderError
|
|
1734
|
+
});
|
|
1659
1735
|
throw new Error(
|
|
1660
1736
|
`Template rendering failed: ${renderError instanceof Error ? renderError.message : "Unknown error"}`
|
|
1661
1737
|
);
|
|
@@ -1772,9 +1848,9 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1772
1848
|
*/
|
|
1773
1849
|
constructor(firestore8, mailgunClient) {
|
|
1774
1850
|
super(firestore8, mailgunClient);
|
|
1775
|
-
this.DEFAULT_REGISTRATION_URL = "https://
|
|
1851
|
+
this.DEFAULT_REGISTRATION_URL = "https://metaestetics.net/register";
|
|
1776
1852
|
this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
|
|
1777
|
-
this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@
|
|
1853
|
+
this.DEFAULT_FROM_ADDRESS = "MedClinic <no-reply@mg.metaestetics.net>";
|
|
1778
1854
|
}
|
|
1779
1855
|
/**
|
|
1780
1856
|
* Sends a practitioner invitation email
|
|
@@ -1784,7 +1860,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1784
1860
|
async sendInvitationEmail(data) {
|
|
1785
1861
|
var _a, _b, _c, _d;
|
|
1786
1862
|
try {
|
|
1787
|
-
|
|
1863
|
+
Logger.info(
|
|
1788
1864
|
"[PractitionerInviteMailingService] Sending invitation email to",
|
|
1789
1865
|
data.token.email
|
|
1790
1866
|
);
|
|
@@ -1811,7 +1887,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1811
1887
|
contactEmail,
|
|
1812
1888
|
currentYear
|
|
1813
1889
|
};
|
|
1814
|
-
|
|
1890
|
+
Logger.info("[PractitionerInviteMailingService] Template variables:", {
|
|
1815
1891
|
clinicName: templateVariables.clinicName,
|
|
1816
1892
|
practitionerName: templateVariables.practitionerName,
|
|
1817
1893
|
expirationDate: templateVariables.expirationDate,
|
|
@@ -1831,7 +1907,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1831
1907
|
subject,
|
|
1832
1908
|
html
|
|
1833
1909
|
};
|
|
1834
|
-
|
|
1910
|
+
Logger.info(
|
|
1835
1911
|
"[PractitionerInviteMailingService] Sending email with data:",
|
|
1836
1912
|
{
|
|
1837
1913
|
to: emailData.to,
|
|
@@ -1851,10 +1927,12 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1851
1927
|
);
|
|
1852
1928
|
return result;
|
|
1853
1929
|
} catch (error) {
|
|
1854
|
-
|
|
1930
|
+
Logger.error(
|
|
1855
1931
|
"[PractitionerInviteMailingService] Error sending invitation email:",
|
|
1856
|
-
|
|
1857
|
-
|
|
1932
|
+
{
|
|
1933
|
+
error: error instanceof Error ? error.message : error,
|
|
1934
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1935
|
+
}
|
|
1858
1936
|
);
|
|
1859
1937
|
await this.logEmailAttempt(
|
|
1860
1938
|
{
|
|
@@ -1878,7 +1956,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1878
1956
|
*/
|
|
1879
1957
|
async handleTokenCreationEvent(tokenData, fromAddress) {
|
|
1880
1958
|
try {
|
|
1881
|
-
|
|
1959
|
+
Logger.info(
|
|
1882
1960
|
"[PractitionerInviteMailingService] Handling token creation event for token:",
|
|
1883
1961
|
tokenData.id
|
|
1884
1962
|
);
|
|
@@ -1898,7 +1976,18 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1898
1976
|
if (!tokenData.expiresAt) {
|
|
1899
1977
|
throw new Error(`Token ${tokenData.id} is missing expiration date`);
|
|
1900
1978
|
}
|
|
1901
|
-
|
|
1979
|
+
if (!tokenData.status) {
|
|
1980
|
+
throw new Error(`Token ${tokenData.id} has no status defined`);
|
|
1981
|
+
}
|
|
1982
|
+
Logger.info(
|
|
1983
|
+
`[PractitionerInviteMailingService] Token status validation:`,
|
|
1984
|
+
{
|
|
1985
|
+
tokenId: tokenData.id,
|
|
1986
|
+
status: tokenData.status,
|
|
1987
|
+
statusType: typeof tokenData.status
|
|
1988
|
+
}
|
|
1989
|
+
);
|
|
1990
|
+
Logger.info(
|
|
1902
1991
|
`[PractitionerInviteMailingService] Fetching practitioner data: ${tokenData.practitionerId}`
|
|
1903
1992
|
);
|
|
1904
1993
|
const practitionerRef = this.db.collection(PRACTITIONERS_COLLECTION).doc(tokenData.practitionerId);
|
|
@@ -1912,10 +2001,10 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1912
2001
|
`Practitioner ${tokenData.practitionerId} has invalid data structure`
|
|
1913
2002
|
);
|
|
1914
2003
|
}
|
|
1915
|
-
|
|
2004
|
+
Logger.info(
|
|
1916
2005
|
`[PractitionerInviteMailingService] Practitioner found: ${practitionerData.basicInfo.firstName} ${practitionerData.basicInfo.lastName}`
|
|
1917
2006
|
);
|
|
1918
|
-
|
|
2007
|
+
Logger.info(
|
|
1919
2008
|
`[PractitionerInviteMailingService] Fetching clinic data: ${tokenData.clinicId}`
|
|
1920
2009
|
);
|
|
1921
2010
|
const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(tokenData.clinicId);
|
|
@@ -1929,11 +2018,11 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1929
2018
|
`Clinic ${tokenData.clinicId} has invalid data structure`
|
|
1930
2019
|
);
|
|
1931
2020
|
}
|
|
1932
|
-
|
|
2021
|
+
Logger.info(
|
|
1933
2022
|
`[PractitionerInviteMailingService] Clinic found: ${clinicData.name}`
|
|
1934
2023
|
);
|
|
1935
2024
|
if (!fromAddress) {
|
|
1936
|
-
|
|
2025
|
+
Logger.warn(
|
|
1937
2026
|
"[PractitionerInviteMailingService] No fromAddress provided, using default"
|
|
1938
2027
|
);
|
|
1939
2028
|
fromAddress = this.DEFAULT_FROM_ADDRESS;
|
|
@@ -1961,18 +2050,20 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
1961
2050
|
fromAddress
|
|
1962
2051
|
}
|
|
1963
2052
|
};
|
|
1964
|
-
|
|
2053
|
+
Logger.info(
|
|
1965
2054
|
"[PractitionerInviteMailingService] Email data prepared, sending invitation"
|
|
1966
2055
|
);
|
|
1967
2056
|
await this.sendInvitationEmail(emailData);
|
|
1968
|
-
|
|
2057
|
+
Logger.info(
|
|
1969
2058
|
"[PractitionerInviteMailingService] Invitation email sent successfully"
|
|
1970
2059
|
);
|
|
1971
2060
|
} catch (error) {
|
|
1972
|
-
|
|
2061
|
+
Logger.error(
|
|
1973
2062
|
"[PractitionerInviteMailingService] Error handling token creation event:",
|
|
1974
|
-
|
|
1975
|
-
|
|
2063
|
+
{
|
|
2064
|
+
error: error instanceof Error ? error.message : error,
|
|
2065
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
2066
|
+
}
|
|
1976
2067
|
);
|
|
1977
2068
|
throw error;
|
|
1978
2069
|
}
|
|
@@ -2582,6 +2673,7 @@ export {
|
|
|
2582
2673
|
PatientAggregationService,
|
|
2583
2674
|
PractitionerAggregationService,
|
|
2584
2675
|
PractitionerInviteMailingService,
|
|
2676
|
+
PractitionerTokenStatus,
|
|
2585
2677
|
ProcedureAggregationService,
|
|
2586
2678
|
UserRole
|
|
2587
2679
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -1530,6 +1530,10 @@ interface PractitionerToken {
|
|
|
1530
1530
|
expiresAt: Timestamp;
|
|
1531
1531
|
usedBy?: string;
|
|
1532
1532
|
usedAt?: Timestamp;
|
|
1533
|
+
emailSent?: boolean;
|
|
1534
|
+
emailSentAt?: Timestamp;
|
|
1535
|
+
emailError?: string;
|
|
1536
|
+
emailErrorAt?: Timestamp;
|
|
1533
1537
|
}
|
|
1534
1538
|
/**
|
|
1535
1539
|
* Tip za kreiranje tokena za zdravstvenog radnika
|
package/dist/index.d.ts
CHANGED
|
@@ -1530,6 +1530,10 @@ interface PractitionerToken {
|
|
|
1530
1530
|
expiresAt: Timestamp;
|
|
1531
1531
|
usedBy?: string;
|
|
1532
1532
|
usedAt?: Timestamp;
|
|
1533
|
+
emailSent?: boolean;
|
|
1534
|
+
emailSentAt?: Timestamp;
|
|
1535
|
+
emailError?: string;
|
|
1536
|
+
emailErrorAt?: Timestamp;
|
|
1533
1537
|
}
|
|
1534
1538
|
/**
|
|
1535
1539
|
* Tip za kreiranje tokena za zdravstvenog radnika
|
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.
|
|
4
|
+
"version": "1.5.47",
|
|
5
5
|
"description": "Firebase authentication service with anonymous upgrade support",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
@@ -100,6 +100,9 @@
|
|
|
100
100
|
"geofire-common": "^6.0.0",
|
|
101
101
|
"zod": "^3.24.1"
|
|
102
102
|
},
|
|
103
|
+
"optionalDependencies": {
|
|
104
|
+
"firebase-functions": "^6.2.0"
|
|
105
|
+
},
|
|
103
106
|
"jest": {
|
|
104
107
|
"preset": "ts-jest",
|
|
105
108
|
"testEnvironment": "node",
|
package/src/admin/index.ts
CHANGED
|
@@ -43,11 +43,7 @@ export type {
|
|
|
43
43
|
// Re-export types needed by cloud functions
|
|
44
44
|
export type { Clinic, ClinicLocation } from "../types/clinic";
|
|
45
45
|
export type { ClinicInfo } from "../types/profile";
|
|
46
|
-
export type {
|
|
47
|
-
Practitioner,
|
|
48
|
-
PractitionerToken,
|
|
49
|
-
PractitionerTokenStatus,
|
|
50
|
-
} from "../types/practitioner";
|
|
46
|
+
export type { Practitioner, PractitionerToken } from "../types/practitioner";
|
|
51
47
|
export type { DoctorInfo } from "../types/clinic";
|
|
52
48
|
export type { Procedure, ProcedureSummaryInfo } from "../types/procedure";
|
|
53
49
|
export type { PatientProfile as Patient } from "../types/patient";
|
|
@@ -59,6 +55,7 @@ export {
|
|
|
59
55
|
NOTIFICATIONS_COLLECTION,
|
|
60
56
|
} from "../types/notifications";
|
|
61
57
|
export { UserRole } from "../types";
|
|
58
|
+
export { PractitionerTokenStatus } from "../types/practitioner";
|
|
62
59
|
|
|
63
60
|
// Export admin classes/services explicitly by name
|
|
64
61
|
export {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloud Functions-compatible logger with fallback for other environments
|
|
3
|
+
*
|
|
4
|
+
* This logger automatically detects if it's running in a Cloud Functions environment
|
|
5
|
+
* and uses the appropriate logging method. It falls back to console methods in other environments.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Try to import Firebase Functions logger, but don't cause errors if it's not available
|
|
9
|
+
let firebaseFunctionsLogger: any;
|
|
10
|
+
try {
|
|
11
|
+
// Use dynamic import to avoid requiring firebase-functions in non-Functions environments
|
|
12
|
+
firebaseFunctionsLogger = require("firebase-functions/logger");
|
|
13
|
+
// Import the compatibility module for console.log support if available
|
|
14
|
+
require("firebase-functions/logger/compat");
|
|
15
|
+
} catch (e) {
|
|
16
|
+
// Firebase Functions logger not available, will use fallback
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Logger class that uses Firebase Functions logger when available
|
|
21
|
+
* with fallback to console methods when not in a Cloud Functions environment
|
|
22
|
+
*/
|
|
23
|
+
export class Logger {
|
|
24
|
+
/**
|
|
25
|
+
* Log an error message
|
|
26
|
+
* @param message Message to log
|
|
27
|
+
* @param data Optional data to include
|
|
28
|
+
*/
|
|
29
|
+
static error(message: string, data?: any): void {
|
|
30
|
+
if (firebaseFunctionsLogger) {
|
|
31
|
+
firebaseFunctionsLogger.error(message, data);
|
|
32
|
+
} else {
|
|
33
|
+
console.error(message, data !== undefined ? data : "");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Log a warning message
|
|
39
|
+
* @param message Message to log
|
|
40
|
+
* @param data Optional data to include
|
|
41
|
+
*/
|
|
42
|
+
static warn(message: string, data?: any): void {
|
|
43
|
+
if (firebaseFunctionsLogger) {
|
|
44
|
+
firebaseFunctionsLogger.warn(message, data);
|
|
45
|
+
} else {
|
|
46
|
+
console.warn(message, data !== undefined ? data : "");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Log an info message
|
|
52
|
+
* @param message Message to log
|
|
53
|
+
* @param data Optional data to include
|
|
54
|
+
*/
|
|
55
|
+
static info(message: string, data?: any): void {
|
|
56
|
+
if (firebaseFunctionsLogger) {
|
|
57
|
+
firebaseFunctionsLogger.info(message, data);
|
|
58
|
+
} else {
|
|
59
|
+
console.info(message, data !== undefined ? data : "");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Log a debug message
|
|
65
|
+
* @param message Message to log
|
|
66
|
+
* @param data Optional data to include
|
|
67
|
+
*/
|
|
68
|
+
static debug(message: string, data?: any): void {
|
|
69
|
+
if (firebaseFunctionsLogger) {
|
|
70
|
+
firebaseFunctionsLogger.debug(message, data);
|
|
71
|
+
} else {
|
|
72
|
+
console.debug(message, data !== undefined ? data : "");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Default export for easier importing
|
|
78
|
+
export default Logger;
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import * as mailgun from "mailgun-js";
|
|
2
2
|
import * as admin from "firebase-admin";
|
|
3
|
-
|
|
4
|
-
// import {
|
|
5
|
-
// getMailgunConfig,
|
|
6
|
-
// createMailgunClient,
|
|
7
|
-
// MailgunConfig,
|
|
8
|
-
// } from "./mailgun.config";
|
|
3
|
+
import { Logger } from "../logger";
|
|
9
4
|
|
|
10
5
|
/**
|
|
11
6
|
* Base mailing service class that provides common functionality for all mailing services
|
|
@@ -14,7 +9,11 @@ export class BaseMailingService {
|
|
|
14
9
|
protected db: FirebaseFirestore.Firestore;
|
|
15
10
|
protected mailgunClient: mailgun.Mailgun;
|
|
16
11
|
// Removed config property as it's no longer managed here
|
|
17
|
-
//
|
|
12
|
+
// import {
|
|
13
|
+
// getMailgunConfig,
|
|
14
|
+
// createMailgunClient,
|
|
15
|
+
// MailgunConfig,
|
|
16
|
+
// } from "./mailgun.config";
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* Constructor for BaseMailingService
|
|
@@ -32,17 +31,17 @@ export class BaseMailingService {
|
|
|
32
31
|
|
|
33
32
|
// Validate instances
|
|
34
33
|
if (!this.db) {
|
|
35
|
-
|
|
34
|
+
Logger.error("[BaseMailingService] No Firestore instance provided");
|
|
36
35
|
throw new Error("Firestore instance is required");
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
if (!this.mailgunClient) {
|
|
40
|
-
|
|
39
|
+
Logger.error("[BaseMailingService] No Mailgun client provided");
|
|
41
40
|
throw new Error("Mailgun client is required");
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
// Log successful initialization
|
|
45
|
-
|
|
44
|
+
Logger.info("[BaseMailingService] Service initialized successfully");
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
/**
|
|
@@ -79,7 +78,7 @@ export class BaseMailingService {
|
|
|
79
78
|
throw new Error("Email must have either 'html' or 'text' content");
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
|
|
81
|
+
Logger.info("[BaseMailingService] Sending email via Mailgun", {
|
|
83
82
|
to: data.to,
|
|
84
83
|
from: data.from,
|
|
85
84
|
subject: data.subject,
|
|
@@ -98,14 +97,13 @@ export class BaseMailingService {
|
|
|
98
97
|
|
|
99
98
|
messagesApi.send(data, (error, body) => {
|
|
100
99
|
if (error) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
error instanceof Error ? error.
|
|
104
|
-
|
|
105
|
-
);
|
|
100
|
+
Logger.error("[BaseMailingService] Mailgun API error:", {
|
|
101
|
+
error: error instanceof Error ? error.message : error,
|
|
102
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
103
|
+
});
|
|
106
104
|
reject(error);
|
|
107
105
|
} else {
|
|
108
|
-
|
|
106
|
+
Logger.info(
|
|
109
107
|
"[BaseMailingService] Email sent successfully:",
|
|
110
108
|
body
|
|
111
109
|
);
|
|
@@ -113,21 +111,23 @@ export class BaseMailingService {
|
|
|
113
111
|
}
|
|
114
112
|
});
|
|
115
113
|
} catch (sendError) {
|
|
116
|
-
|
|
114
|
+
Logger.error(
|
|
117
115
|
"[BaseMailingService] Error in mailgun.messages().send():",
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
{
|
|
117
|
+
error:
|
|
118
|
+
sendError instanceof Error ? sendError.message : sendError,
|
|
119
|
+
stack: sendError instanceof Error ? sendError.stack : undefined,
|
|
120
|
+
}
|
|
120
121
|
);
|
|
121
122
|
reject(sendError);
|
|
122
123
|
}
|
|
123
124
|
}
|
|
124
125
|
);
|
|
125
126
|
} catch (error) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
error instanceof Error ? error.
|
|
129
|
-
|
|
130
|
-
);
|
|
127
|
+
Logger.error("[BaseMailingService] Error in sendEmail:", {
|
|
128
|
+
error: error instanceof Error ? error.message : error,
|
|
129
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
130
|
+
});
|
|
131
131
|
throw error;
|
|
132
132
|
}
|
|
133
133
|
}
|
|
@@ -158,15 +158,14 @@ export class BaseMailingService {
|
|
|
158
158
|
sentAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
Logger.info(
|
|
162
162
|
`[BaseMailingService] Email log recorded. Success: ${success}`
|
|
163
163
|
);
|
|
164
164
|
} catch (logError) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
logError instanceof Error ? logError.
|
|
168
|
-
|
|
169
|
-
);
|
|
165
|
+
Logger.error("[BaseMailingService] Error logging email attempt:", {
|
|
166
|
+
error: logError instanceof Error ? logError.message : logError,
|
|
167
|
+
stack: logError instanceof Error ? logError.stack : undefined,
|
|
168
|
+
});
|
|
170
169
|
// Don't throw here to prevent disrupting the main flow
|
|
171
170
|
}
|
|
172
171
|
}
|
|
@@ -196,10 +195,9 @@ export class BaseMailingService {
|
|
|
196
195
|
|
|
197
196
|
return rendered;
|
|
198
197
|
} catch (renderError) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
);
|
|
198
|
+
Logger.error("[BaseMailingService] Error rendering template:", {
|
|
199
|
+
error: renderError instanceof Error ? renderError.message : renderError,
|
|
200
|
+
});
|
|
203
201
|
throw new Error(
|
|
204
202
|
`Template rendering failed: ${
|
|
205
203
|
renderError instanceof Error ? renderError.message : "Unknown error"
|
|
@@ -2,7 +2,7 @@ import * as admin from "firebase-admin";
|
|
|
2
2
|
import * as mailgun from "mailgun-js";
|
|
3
3
|
import { BaseMailingService } from "../base.mailing.service";
|
|
4
4
|
import { practitionerInvitationTemplate } from "./templates/invitation.template";
|
|
5
|
-
|
|
5
|
+
import { Logger } from "../../logger";
|
|
6
6
|
// Import specific types and collection constants
|
|
7
7
|
import {
|
|
8
8
|
Practitioner,
|
|
@@ -51,11 +51,11 @@ export interface PractitionerInviteEmailData {
|
|
|
51
51
|
*/
|
|
52
52
|
export class PractitionerInviteMailingService extends BaseMailingService {
|
|
53
53
|
private readonly DEFAULT_REGISTRATION_URL =
|
|
54
|
-
"https://
|
|
54
|
+
"https://metaestetics.net/register";
|
|
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@
|
|
58
|
+
"MedClinic <no-reply@mg.metaestetics.net>";
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Constructor for PractitionerInviteMailingService
|
|
@@ -78,7 +78,7 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
78
78
|
data: PractitionerInviteEmailData
|
|
79
79
|
): Promise<mailgun.messages.SendResponse> {
|
|
80
80
|
try {
|
|
81
|
-
|
|
81
|
+
Logger.info(
|
|
82
82
|
"[PractitionerInviteMailingService] Sending invitation email to",
|
|
83
83
|
data.token.email
|
|
84
84
|
);
|
|
@@ -127,7 +127,7 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
127
127
|
};
|
|
128
128
|
|
|
129
129
|
// Debug log for template variables (excluding token for security)
|
|
130
|
-
|
|
130
|
+
Logger.info("[PractitionerInviteMailingService] Template variables:", {
|
|
131
131
|
clinicName: templateVariables.clinicName,
|
|
132
132
|
practitionerName: templateVariables.practitionerName,
|
|
133
133
|
expirationDate: templateVariables.expirationDate,
|
|
@@ -152,7 +152,7 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
152
152
|
html,
|
|
153
153
|
};
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
Logger.info(
|
|
156
156
|
"[PractitionerInviteMailingService] Sending email with data:",
|
|
157
157
|
{
|
|
158
158
|
to: emailData.to,
|
|
@@ -176,10 +176,12 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
176
176
|
|
|
177
177
|
return result;
|
|
178
178
|
} catch (error) {
|
|
179
|
-
|
|
179
|
+
Logger.error(
|
|
180
180
|
"[PractitionerInviteMailingService] Error sending invitation email:",
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
{
|
|
182
|
+
error: error instanceof Error ? error.message : error,
|
|
183
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
184
|
+
}
|
|
183
185
|
);
|
|
184
186
|
|
|
185
187
|
// Log failure
|
|
@@ -210,7 +212,7 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
210
212
|
fromAddress: string
|
|
211
213
|
): Promise<void> {
|
|
212
214
|
try {
|
|
213
|
-
|
|
215
|
+
Logger.info(
|
|
214
216
|
"[PractitionerInviteMailingService] Handling token creation event for token:",
|
|
215
217
|
tokenData.id
|
|
216
218
|
);
|
|
@@ -236,8 +238,23 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
236
238
|
throw new Error(`Token ${tokenData.id} is missing expiration date`);
|
|
237
239
|
}
|
|
238
240
|
|
|
241
|
+
// Validate token status (handle both enum and string values)
|
|
242
|
+
if (!tokenData.status) {
|
|
243
|
+
throw new Error(`Token ${tokenData.id} has no status defined`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Log token status to help with debugging
|
|
247
|
+
Logger.info(
|
|
248
|
+
`[PractitionerInviteMailingService] Token status validation:`,
|
|
249
|
+
{
|
|
250
|
+
tokenId: tokenData.id,
|
|
251
|
+
status: tokenData.status,
|
|
252
|
+
statusType: typeof tokenData.status,
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
|
|
239
256
|
// Get practitioner data using constant and type
|
|
240
|
-
|
|
257
|
+
Logger.info(
|
|
241
258
|
`[PractitionerInviteMailingService] Fetching practitioner data: ${tokenData.practitionerId}`
|
|
242
259
|
);
|
|
243
260
|
const practitionerRef = this.db
|
|
@@ -256,12 +273,12 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
256
273
|
);
|
|
257
274
|
}
|
|
258
275
|
|
|
259
|
-
|
|
276
|
+
Logger.info(
|
|
260
277
|
`[PractitionerInviteMailingService] Practitioner found: ${practitionerData.basicInfo.firstName} ${practitionerData.basicInfo.lastName}`
|
|
261
278
|
);
|
|
262
279
|
|
|
263
280
|
// Get clinic data using constant and type
|
|
264
|
-
|
|
281
|
+
Logger.info(
|
|
265
282
|
`[PractitionerInviteMailingService] Fetching clinic data: ${tokenData.clinicId}`
|
|
266
283
|
);
|
|
267
284
|
const clinicRef = this.db
|
|
@@ -280,7 +297,7 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
280
297
|
);
|
|
281
298
|
}
|
|
282
299
|
|
|
283
|
-
|
|
300
|
+
Logger.info(
|
|
284
301
|
`[PractitionerInviteMailingService] Clinic found: ${clinicData.name}`
|
|
285
302
|
);
|
|
286
303
|
|
|
@@ -289,7 +306,7 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
289
306
|
|
|
290
307
|
// Validate fromAddress
|
|
291
308
|
if (!fromAddress) {
|
|
292
|
-
|
|
309
|
+
Logger.warn(
|
|
293
310
|
"[PractitionerInviteMailingService] No fromAddress provided, using default"
|
|
294
311
|
);
|
|
295
312
|
fromAddress = this.DEFAULT_FROM_ADDRESS;
|
|
@@ -320,21 +337,23 @@ export class PractitionerInviteMailingService extends BaseMailingService {
|
|
|
320
337
|
},
|
|
321
338
|
};
|
|
322
339
|
|
|
323
|
-
|
|
340
|
+
Logger.info(
|
|
324
341
|
"[PractitionerInviteMailingService] Email data prepared, sending invitation"
|
|
325
342
|
);
|
|
326
343
|
|
|
327
344
|
// Send the invitation email
|
|
328
345
|
await this.sendInvitationEmail(emailData);
|
|
329
346
|
|
|
330
|
-
|
|
347
|
+
Logger.info(
|
|
331
348
|
"[PractitionerInviteMailingService] Invitation email sent successfully"
|
|
332
349
|
);
|
|
333
350
|
} catch (error) {
|
|
334
|
-
|
|
351
|
+
Logger.error(
|
|
335
352
|
"[PractitionerInviteMailingService] Error handling token creation event:",
|
|
336
|
-
|
|
337
|
-
|
|
353
|
+
{
|
|
354
|
+
error: error instanceof Error ? error.message : error,
|
|
355
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
356
|
+
}
|
|
338
357
|
);
|
|
339
358
|
throw error;
|
|
340
359
|
}
|
|
@@ -183,6 +183,11 @@ export interface PractitionerToken {
|
|
|
183
183
|
expiresAt: Timestamp;
|
|
184
184
|
usedBy?: string;
|
|
185
185
|
usedAt?: Timestamp;
|
|
186
|
+
// Email tracking fields added by Cloud Function
|
|
187
|
+
emailSent?: boolean;
|
|
188
|
+
emailSentAt?: Timestamp;
|
|
189
|
+
emailError?: string;
|
|
190
|
+
emailErrorAt?: Timestamp;
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
/**
|