@blackcode_sa/metaestetics-api 1.5.44 → 1.5.45

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.5.44",
4
+ "version": "1.5.45",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -29,9 +29,20 @@ export class BaseMailingService {
29
29
  // Use provided instances
30
30
  this.db = firestore;
31
31
  this.mailgunClient = mailgunClient;
32
- // Removed internal config reading and client creation
33
- // this.config = mailgunConfig || getMailgunConfig();
34
- // this.mailgunClient = createMailgunClient(this.config);
32
+
33
+ // Validate instances
34
+ if (!this.db) {
35
+ console.error("[BaseMailingService] No Firestore instance provided");
36
+ throw new Error("Firestore instance is required");
37
+ }
38
+
39
+ if (!this.mailgunClient) {
40
+ console.error("[BaseMailingService] No Mailgun client provided");
41
+ throw new Error("Mailgun client is required");
42
+ }
43
+
44
+ // Log successful initialization
45
+ console.log("[BaseMailingService] Service initialized successfully");
35
46
  }
36
47
 
37
48
  /**
@@ -43,37 +54,80 @@ export class BaseMailingService {
43
54
  data: mailgun.messages.SendData // Caller must provide 'from'
44
55
  ): Promise<mailgun.messages.SendResponse> {
45
56
  try {
57
+ // Validate email data fields
58
+ if (!data) {
59
+ throw new Error("Email data object is required");
60
+ }
61
+
62
+ // Ensure all required fields are provided
63
+ if (!data.to) {
64
+ throw new Error("Email 'to' address is required");
65
+ }
66
+
46
67
  // Ensure 'from' field is provided by the caller
47
68
  if (!data.from) {
48
69
  throw new Error(
49
70
  "Email 'from' address must be provided in sendEmail data."
50
71
  );
51
72
  }
52
- // Removed fallback to internal config.from
53
- // const emailData = {
54
- // ...data,
55
- // from: data.from || this.config.from,
56
- // };
73
+
74
+ if (!data.subject) {
75
+ throw new Error("Email 'subject' is required");
76
+ }
77
+
78
+ if (!data.html && !data.text) {
79
+ throw new Error("Email must have either 'html' or 'text' content");
80
+ }
81
+
82
+ console.log("[BaseMailingService] Sending email via Mailgun", {
83
+ to: data.to,
84
+ from: data.from,
85
+ subject: data.subject,
86
+ hasHtml: !!data.html,
87
+ hasText: !!data.text,
88
+ });
57
89
 
58
90
  // Send the email
59
91
  return await new Promise<mailgun.messages.SendResponse>(
60
92
  (resolve, reject) => {
61
- this.mailgunClient.messages().send(data, (error, body) => {
62
- if (error) {
63
- console.error("[BaseMailingService] Error sending email:", error);
64
- reject(error);
65
- } else {
66
- console.log(
67
- "[BaseMailingService] Email sent successfully:",
68
- body
69
- );
70
- resolve(body);
93
+ try {
94
+ const messagesApi = this.mailgunClient.messages();
95
+ if (!messagesApi) {
96
+ throw new Error("Could not get Mailgun messages API");
71
97
  }
72
- });
98
+
99
+ messagesApi.send(data, (error, body) => {
100
+ if (error) {
101
+ console.error(
102
+ "[BaseMailingService] Mailgun API error:",
103
+ error instanceof Error ? error.message : error,
104
+ error instanceof Error ? error.stack : ""
105
+ );
106
+ reject(error);
107
+ } else {
108
+ console.log(
109
+ "[BaseMailingService] Email sent successfully:",
110
+ body
111
+ );
112
+ resolve(body);
113
+ }
114
+ });
115
+ } catch (sendError) {
116
+ console.error(
117
+ "[BaseMailingService] Error in mailgun.messages().send():",
118
+ sendError instanceof Error ? sendError.message : sendError,
119
+ sendError instanceof Error ? sendError.stack : ""
120
+ );
121
+ reject(sendError);
122
+ }
73
123
  }
74
124
  );
75
125
  } catch (error) {
76
- console.error("[BaseMailingService] Error in sendEmail:", error);
126
+ console.error(
127
+ "[BaseMailingService] Error in sendEmail:",
128
+ error instanceof Error ? error.message : error,
129
+ error instanceof Error ? error.stack : ""
130
+ );
77
131
  throw error;
78
132
  }
79
133
  }
@@ -96,13 +150,22 @@ export class BaseMailingService {
96
150
  subject: emailData.subject,
97
151
  templateName: emailData.templateName,
98
152
  success,
99
- error: error ? JSON.stringify(error) : null,
153
+ error: error
154
+ ? error instanceof Error
155
+ ? { message: error.message, stack: error.stack }
156
+ : JSON.stringify(error)
157
+ : null,
100
158
  sentAt: admin.firestore.FieldValue.serverTimestamp(),
101
159
  });
160
+
161
+ console.log(
162
+ `[BaseMailingService] Email log recorded. Success: ${success}`
163
+ );
102
164
  } catch (logError) {
103
165
  console.error(
104
166
  "[BaseMailingService] Error logging email attempt:",
105
- logError
167
+ logError instanceof Error ? logError.message : logError,
168
+ logError instanceof Error ? logError.stack : ""
106
169
  );
107
170
  // Don't throw here to prevent disrupting the main flow
108
171
  }
@@ -118,14 +181,30 @@ export class BaseMailingService {
118
181
  template: string,
119
182
  variables: Record<string, string>
120
183
  ): string {
121
- let rendered = template;
184
+ if (!template) {
185
+ throw new Error("Email template is required");
186
+ }
122
187
 
123
- // Replace template variables (format: {{variable_name}})
124
- Object.entries(variables).forEach(([key, value]) => {
125
- const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
126
- rendered = rendered.replace(regex, value);
127
- });
188
+ try {
189
+ let rendered = template;
190
+
191
+ // Replace template variables (format: {{variable_name}})
192
+ Object.entries(variables).forEach(([key, value]) => {
193
+ const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
194
+ rendered = rendered.replace(regex, value || "");
195
+ });
128
196
 
129
- return rendered;
197
+ return rendered;
198
+ } catch (renderError) {
199
+ console.error(
200
+ "[BaseMailingService] Error rendering template:",
201
+ renderError instanceof Error ? renderError.message : renderError
202
+ );
203
+ throw new Error(
204
+ `Template rendering failed: ${
205
+ renderError instanceof Error ? renderError.message : "Unknown error"
206
+ }`
207
+ );
208
+ }
130
209
  }
131
210
  }
@@ -126,6 +126,18 @@ export class PractitionerInviteMailingService extends BaseMailingService {
126
126
  currentYear,
127
127
  };
128
128
 
129
+ // Debug log for template variables (excluding token for security)
130
+ console.log("[PractitionerInviteMailingService] Template variables:", {
131
+ clinicName: templateVariables.clinicName,
132
+ practitionerName: templateVariables.practitionerName,
133
+ expirationDate: templateVariables.expirationDate,
134
+ registrationUrl: templateVariables.registrationUrl,
135
+ contactName: templateVariables.contactName,
136
+ contactEmail: templateVariables.contactEmail,
137
+ // Don't log the invite token for security
138
+ hasInviteToken: !!templateVariables.inviteToken,
139
+ });
140
+
129
141
  // Render HTML email
130
142
  const html = this.renderTemplate(
131
143
  practitionerInvitationTemplate,
@@ -140,6 +152,16 @@ export class PractitionerInviteMailingService extends BaseMailingService {
140
152
  html,
141
153
  };
142
154
 
155
+ console.log(
156
+ "[PractitionerInviteMailingService] Sending email with data:",
157
+ {
158
+ to: emailData.to,
159
+ from: emailData.from,
160
+ subject: emailData.subject,
161
+ hasHtml: !!emailData.html,
162
+ }
163
+ );
164
+
143
165
  const result = await this.sendEmail(emailData);
144
166
 
145
167
  // Log success
@@ -156,7 +178,8 @@ export class PractitionerInviteMailingService extends BaseMailingService {
156
178
  } catch (error) {
157
179
  console.error(
158
180
  "[PractitionerInviteMailingService] Error sending invitation email:",
159
- error
181
+ error instanceof Error ? error.message : error,
182
+ error instanceof Error ? error.stack : ""
160
183
  );
161
184
 
162
185
  // Log failure
@@ -192,7 +215,31 @@ export class PractitionerInviteMailingService extends BaseMailingService {
192
215
  tokenData.id
193
216
  );
194
217
 
218
+ // Validate token data
219
+ if (!tokenData || !tokenData.id || !tokenData.token || !tokenData.email) {
220
+ throw new Error(
221
+ `Invalid token data: Missing required properties. Token ID: ${tokenData?.id}`
222
+ );
223
+ }
224
+
225
+ if (!tokenData.practitionerId) {
226
+ throw new Error(
227
+ `Token ${tokenData.id} is missing practitionerId reference`
228
+ );
229
+ }
230
+
231
+ if (!tokenData.clinicId) {
232
+ throw new Error(`Token ${tokenData.id} is missing clinicId reference`);
233
+ }
234
+
235
+ if (!tokenData.expiresAt) {
236
+ throw new Error(`Token ${tokenData.id} is missing expiration date`);
237
+ }
238
+
195
239
  // Get practitioner data using constant and type
240
+ console.log(
241
+ `[PractitionerInviteMailingService] Fetching practitioner data: ${tokenData.practitionerId}`
242
+ );
196
243
  const practitionerRef = this.db
197
244
  .collection(PRACTITIONERS_COLLECTION)
198
245
  .doc(tokenData.practitionerId);
@@ -203,8 +250,20 @@ export class PractitionerInviteMailingService extends BaseMailingService {
203
250
  }
204
251
 
205
252
  const practitionerData = practitionerDoc.data() as Practitioner;
253
+ if (!practitionerData || !practitionerData.basicInfo) {
254
+ throw new Error(
255
+ `Practitioner ${tokenData.practitionerId} has invalid data structure`
256
+ );
257
+ }
258
+
259
+ console.log(
260
+ `[PractitionerInviteMailingService] Practitioner found: ${practitionerData.basicInfo.firstName} ${practitionerData.basicInfo.lastName}`
261
+ );
206
262
 
207
263
  // Get clinic data using constant and type
264
+ console.log(
265
+ `[PractitionerInviteMailingService] Fetching clinic data: ${tokenData.clinicId}`
266
+ );
208
267
  const clinicRef = this.db
209
268
  .collection(CLINICS_COLLECTION)
210
269
  .doc(tokenData.clinicId);
@@ -215,6 +274,26 @@ export class PractitionerInviteMailingService extends BaseMailingService {
215
274
  }
216
275
 
217
276
  const clinicData = clinicDoc.data() as Clinic;
277
+ if (!clinicData || !clinicData.contactInfo) {
278
+ throw new Error(
279
+ `Clinic ${tokenData.clinicId} has invalid data structure`
280
+ );
281
+ }
282
+
283
+ console.log(
284
+ `[PractitionerInviteMailingService] Clinic found: ${clinicData.name}`
285
+ );
286
+
287
+ // Clinic model doesn't have contactPerson, only contactInfo
288
+ // So we'll use simple contact information from contactInfo
289
+
290
+ // Validate fromAddress
291
+ if (!fromAddress) {
292
+ console.warn(
293
+ "[PractitionerInviteMailingService] No fromAddress provided, using default"
294
+ );
295
+ fromAddress = this.DEFAULT_FROM_ADDRESS;
296
+ }
218
297
 
219
298
  // Prepare email data using typed data
220
299
  const emailData: PractitionerInviteEmailData = {
@@ -233,12 +312,18 @@ export class PractitionerInviteMailingService extends BaseMailingService {
233
312
  clinic: {
234
313
  name: clinicData.name || "Medical Clinic",
235
314
  contactEmail: clinicData.contactInfo.email || "contact@medclinic.com",
315
+ // Since there's no contactPerson in the Clinic model, we'll just use "Clinic Admin"
316
+ contactName: "Clinic Admin",
236
317
  },
237
318
  options: {
238
319
  fromAddress: fromAddress,
239
320
  },
240
321
  };
241
322
 
323
+ console.log(
324
+ "[PractitionerInviteMailingService] Email data prepared, sending invitation"
325
+ );
326
+
242
327
  // Send the invitation email
243
328
  await this.sendInvitationEmail(emailData);
244
329
 
@@ -248,7 +333,8 @@ export class PractitionerInviteMailingService extends BaseMailingService {
248
333
  } catch (error) {
249
334
  console.error(
250
335
  "[PractitionerInviteMailingService] Error handling token creation event:",
251
- error
336
+ error instanceof Error ? error.message : error,
337
+ error instanceof Error ? error.stack : ""
252
338
  );
253
339
  throw error;
254
340
  }