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