@blackcode_sa/metaestetics-api 1.5.46 → 1.5.48

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.
@@ -737,6 +737,10 @@ interface PractitionerToken {
737
737
  expiresAt: Timestamp;
738
738
  usedBy?: string;
739
739
  usedAt?: Timestamp;
740
+ emailSent?: boolean;
741
+ emailSentAt?: Timestamp;
742
+ emailError?: string;
743
+ emailErrorAt?: Timestamp;
740
744
  }
741
745
 
742
746
  /**
@@ -1514,6 +1518,11 @@ declare class BaseMailingService {
1514
1518
  * @param mailgunClient Mailgun client instance provided by the caller
1515
1519
  */
1516
1520
  constructor(firestore: FirebaseFirestore.Firestore, mailgunClient: mailgun.Mailgun);
1521
+ /**
1522
+ * Validates that the Mailgun client is configured correctly
1523
+ * Particularly checks for EU region configuration
1524
+ */
1525
+ private validateMailgunClient;
1517
1526
  /**
1518
1527
  * Sends an email using Mailgun
1519
1528
  * @param data Email data to send, including the 'from' address
@@ -737,6 +737,10 @@ interface PractitionerToken {
737
737
  expiresAt: Timestamp;
738
738
  usedBy?: string;
739
739
  usedAt?: Timestamp;
740
+ emailSent?: boolean;
741
+ emailSentAt?: Timestamp;
742
+ emailError?: string;
743
+ emailErrorAt?: Timestamp;
740
744
  }
741
745
 
742
746
  /**
@@ -1514,6 +1518,11 @@ declare class BaseMailingService {
1514
1518
  * @param mailgunClient Mailgun client instance provided by the caller
1515
1519
  */
1516
1520
  constructor(firestore: FirebaseFirestore.Firestore, mailgunClient: mailgun.Mailgun);
1521
+ /**
1522
+ * Validates that the Mailgun client is configured correctly
1523
+ * Particularly checks for EU region configuration
1524
+ */
1525
+ private validateMailgunClient;
1517
1526
  /**
1518
1527
  * Sends an email using Mailgun
1519
1528
  * @param data Email data to send, including the 'from' address
@@ -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";
@@ -1642,8 +1650,34 @@ var BaseMailingService = class {
1642
1650
  Logger.error("[BaseMailingService] No Mailgun client provided");
1643
1651
  throw new Error("Mailgun client is required");
1644
1652
  }
1653
+ this.validateMailgunClient();
1645
1654
  Logger.info("[BaseMailingService] Service initialized successfully");
1646
1655
  }
1656
+ /**
1657
+ * Validates that the Mailgun client is configured correctly
1658
+ * Particularly checks for EU region configuration
1659
+ */
1660
+ validateMailgunClient() {
1661
+ try {
1662
+ const clientOptions = this.mailgunClient.options || {};
1663
+ const host = clientOptions.host || "";
1664
+ const isEuRegion = host.includes("eu.mailgun.net");
1665
+ Logger.info("[BaseMailingService] Mailgun client configuration:", {
1666
+ host: host || "default",
1667
+ isEuRegion,
1668
+ domain: clientOptions.domain || "unknown"
1669
+ });
1670
+ if (clientOptions.domain && clientOptions.domain.endsWith(".eu") && !isEuRegion) {
1671
+ Logger.warn(
1672
+ "[BaseMailingService] Domain appears to be in EU region but not using EU endpoint. If you're getting 401 Forbidden errors, ensure the host is set to 'api.eu.mailgun.net'"
1673
+ );
1674
+ }
1675
+ } catch (error) {
1676
+ Logger.warn("[BaseMailingService] Could not validate Mailgun client:", {
1677
+ error: error instanceof Error ? error.message : String(error)
1678
+ });
1679
+ }
1680
+ }
1647
1681
  /**
1648
1682
  * Sends an email using Mailgun
1649
1683
  * @param data Email data to send, including the 'from' address
@@ -1684,10 +1718,25 @@ var BaseMailingService = class {
1684
1718
  }
1685
1719
  messagesApi.send(data, (error, body) => {
1686
1720
  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
- });
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
+ }
1732
+ );
1733
+ } else {
1734
+ Logger.error("[BaseMailingService] Mailgun API error:", {
1735
+ error: error instanceof Error ? error.message : error,
1736
+ statusCode: error.statusCode,
1737
+ stack: error instanceof Error ? error.stack : void 0
1738
+ });
1739
+ }
1691
1740
  reject(error);
1692
1741
  } else {
1693
1742
  Logger.info(
@@ -2009,6 +2058,17 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
2009
2058
  if (!tokenData.expiresAt) {
2010
2059
  throw new Error(`Token ${tokenData.id} is missing expiration date`);
2011
2060
  }
2061
+ if (!tokenData.status) {
2062
+ throw new Error(`Token ${tokenData.id} has no status defined`);
2063
+ }
2064
+ Logger.info(
2065
+ `[PractitionerInviteMailingService] Token status validation:`,
2066
+ {
2067
+ tokenId: tokenData.id,
2068
+ status: tokenData.status,
2069
+ statusType: typeof tokenData.status
2070
+ }
2071
+ );
2012
2072
  Logger.info(
2013
2073
  `[PractitionerInviteMailingService] Fetching practitioner data: ${tokenData.practitionerId}`
2014
2074
  );
@@ -2696,6 +2756,7 @@ console.log("[Admin Module] Initialized and services exported.");
2696
2756
  PatientAggregationService,
2697
2757
  PractitionerAggregationService,
2698
2758
  PractitionerInviteMailingService,
2759
+ PractitionerTokenStatus,
2699
2760
  ProcedureAggregationService,
2700
2761
  UserRole
2701
2762
  });
@@ -198,6 +198,13 @@ import * as admin2 from "firebase-admin";
198
198
 
199
199
  // src/types/practitioner/index.ts
200
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 || {});
201
208
 
202
209
  // src/types/procedure/index.ts
203
210
  var PROCEDURES_COLLECTION = "procedures";
@@ -1602,8 +1609,34 @@ var BaseMailingService = class {
1602
1609
  Logger.error("[BaseMailingService] No Mailgun client provided");
1603
1610
  throw new Error("Mailgun client is required");
1604
1611
  }
1612
+ this.validateMailgunClient();
1605
1613
  Logger.info("[BaseMailingService] Service initialized successfully");
1606
1614
  }
1615
+ /**
1616
+ * Validates that the Mailgun client is configured correctly
1617
+ * Particularly checks for EU region configuration
1618
+ */
1619
+ validateMailgunClient() {
1620
+ try {
1621
+ const clientOptions = this.mailgunClient.options || {};
1622
+ const host = clientOptions.host || "";
1623
+ const isEuRegion = host.includes("eu.mailgun.net");
1624
+ Logger.info("[BaseMailingService] Mailgun client configuration:", {
1625
+ host: host || "default",
1626
+ isEuRegion,
1627
+ domain: clientOptions.domain || "unknown"
1628
+ });
1629
+ if (clientOptions.domain && clientOptions.domain.endsWith(".eu") && !isEuRegion) {
1630
+ Logger.warn(
1631
+ "[BaseMailingService] Domain appears to be in EU region but not using EU endpoint. If you're getting 401 Forbidden errors, ensure the host is set to 'api.eu.mailgun.net'"
1632
+ );
1633
+ }
1634
+ } catch (error) {
1635
+ Logger.warn("[BaseMailingService] Could not validate Mailgun client:", {
1636
+ error: error instanceof Error ? error.message : String(error)
1637
+ });
1638
+ }
1639
+ }
1607
1640
  /**
1608
1641
  * Sends an email using Mailgun
1609
1642
  * @param data Email data to send, including the 'from' address
@@ -1644,10 +1677,25 @@ var BaseMailingService = class {
1644
1677
  }
1645
1678
  messagesApi.send(data, (error, body) => {
1646
1679
  if (error) {
1647
- Logger.error("[BaseMailingService] Mailgun API error:", {
1648
- error: error instanceof Error ? error.message : error,
1649
- stack: error instanceof Error ? error.stack : void 0
1650
- });
1680
+ if (error.statusCode === 401 || error.statusCode === 403) {
1681
+ const clientOptions = this.mailgunClient.options || {};
1682
+ Logger.error(
1683
+ "[BaseMailingService] Mailgun authentication error - possible region mismatch:",
1684
+ {
1685
+ error: error instanceof Error ? error.message : error,
1686
+ statusCode: error.statusCode,
1687
+ domain: clientOptions.domain || "unknown",
1688
+ host: clientOptions.host || "default api.mailgun.net",
1689
+ suggestion: "If using EU region domains, ensure host is set to 'api.eu.mailgun.net'"
1690
+ }
1691
+ );
1692
+ } else {
1693
+ Logger.error("[BaseMailingService] Mailgun API error:", {
1694
+ error: error instanceof Error ? error.message : error,
1695
+ statusCode: error.statusCode,
1696
+ stack: error instanceof Error ? error.stack : void 0
1697
+ });
1698
+ }
1651
1699
  reject(error);
1652
1700
  } else {
1653
1701
  Logger.info(
@@ -1969,6 +2017,17 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
1969
2017
  if (!tokenData.expiresAt) {
1970
2018
  throw new Error(`Token ${tokenData.id} is missing expiration date`);
1971
2019
  }
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
+ );
1972
2031
  Logger.info(
1973
2032
  `[PractitionerInviteMailingService] Fetching practitioner data: ${tokenData.practitionerId}`
1974
2033
  );
@@ -2655,6 +2714,7 @@ export {
2655
2714
  PatientAggregationService,
2656
2715
  PractitionerAggregationService,
2657
2716
  PractitionerInviteMailingService,
2717
+ PractitionerTokenStatus,
2658
2718
  ProcedureAggregationService,
2659
2719
  UserRole
2660
2720
  };
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.46",
4
+ "version": "1.5.48",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -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 {
@@ -40,10 +40,49 @@ export class BaseMailingService {
40
40
  throw new Error("Mailgun client is required");
41
41
  }
42
42
 
43
+ // Validate the Mailgun client configuration
44
+ this.validateMailgunClient();
45
+
43
46
  // Log successful initialization
44
47
  Logger.info("[BaseMailingService] Service initialized successfully");
45
48
  }
46
49
 
50
+ /**
51
+ * Validates that the Mailgun client is configured correctly
52
+ * Particularly checks for EU region configuration
53
+ */
54
+ private validateMailgunClient(): void {
55
+ try {
56
+ // Access the host property to check if it's configured for EU region
57
+ const clientOptions = (this.mailgunClient as any).options || {};
58
+ const host = clientOptions.host || "";
59
+ const isEuRegion = host.includes("eu.mailgun.net");
60
+
61
+ Logger.info("[BaseMailingService] Mailgun client configuration:", {
62
+ host: host || "default",
63
+ isEuRegion,
64
+ domain: clientOptions.domain || "unknown",
65
+ });
66
+
67
+ // Check if this appears to be an EU domain but not using EU endpoint
68
+ if (
69
+ clientOptions.domain &&
70
+ clientOptions.domain.endsWith(".eu") &&
71
+ !isEuRegion
72
+ ) {
73
+ Logger.warn(
74
+ "[BaseMailingService] Domain appears to be in EU region but not using EU endpoint. " +
75
+ "If you're getting 401 Forbidden errors, ensure the host is set to 'api.eu.mailgun.net'"
76
+ );
77
+ }
78
+ } catch (error) {
79
+ // Just log the error, don't throw
80
+ Logger.warn("[BaseMailingService] Could not validate Mailgun client:", {
81
+ error: error instanceof Error ? error.message : String(error),
82
+ });
83
+ }
84
+ }
85
+
47
86
  /**
48
87
  * Sends an email using Mailgun
49
88
  * @param data Email data to send, including the 'from' address
@@ -97,10 +136,28 @@ export class BaseMailingService {
97
136
 
98
137
  messagesApi.send(data, (error, body) => {
99
138
  if (error) {
100
- Logger.error("[BaseMailingService] Mailgun API error:", {
101
- error: error instanceof Error ? error.message : error,
102
- stack: error instanceof Error ? error.stack : undefined,
103
- });
139
+ // Enhanced error logging for auth/region issues
140
+ if (error.statusCode === 401 || error.statusCode === 403) {
141
+ const clientOptions =
142
+ (this.mailgunClient as any).options || {};
143
+ Logger.error(
144
+ "[BaseMailingService] Mailgun authentication error - possible region mismatch:",
145
+ {
146
+ error: error instanceof Error ? error.message : error,
147
+ statusCode: error.statusCode,
148
+ domain: clientOptions.domain || "unknown",
149
+ host: clientOptions.host || "default api.mailgun.net",
150
+ suggestion:
151
+ "If using EU region domains, ensure host is set to 'api.eu.mailgun.net'",
152
+ }
153
+ );
154
+ } else {
155
+ Logger.error("[BaseMailingService] Mailgun API error:", {
156
+ error: error instanceof Error ? error.message : error,
157
+ statusCode: error.statusCode,
158
+ stack: error instanceof Error ? error.stack : undefined,
159
+ });
160
+ }
104
161
  reject(error);
105
162
  } else {
106
163
  Logger.info(
@@ -238,6 +238,21 @@ export class PractitionerInviteMailingService extends BaseMailingService {
238
238
  throw new Error(`Token ${tokenData.id} is missing expiration date`);
239
239
  }
240
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
+
241
256
  // Get practitioner data using constant and type
242
257
  Logger.info(
243
258
  `[PractitionerInviteMailingService] Fetching practitioner data: ${tokenData.practitionerId}`
@@ -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
  /**