@blackcode_sa/metaestetics-api 1.12.8 → 1.12.11
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 +30 -9
- package/dist/admin/index.mjs +30 -9
- package/dist/index.d.mts +49 -32
- package/dist/index.d.ts +49 -32
- package/dist/index.js +376 -582
- package/dist/index.mjs +390 -597
- package/package.json +1 -1
- package/src/admin/aggregation/forms/README.md +13 -0
- package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +71 -51
- package/src/services/appointment/README.md +17 -0
- package/src/services/appointment/appointment.service.ts +233 -386
- package/src/services/auth/auth.service.ts +422 -466
package/dist/index.js
CHANGED
|
@@ -1190,24 +1190,18 @@ var AppointmentService = class extends BaseService {
|
|
|
1190
1190
|
`[APPOINTMENT_SERVICE] Getting available booking slots via HTTP for clinic: ${clinicId}, practitioner: ${practitionerId}, procedure: ${procedureId}`
|
|
1191
1191
|
);
|
|
1192
1192
|
if (!clinicId || !practitionerId || !procedureId || !startDate || !endDate) {
|
|
1193
|
-
throw new Error(
|
|
1194
|
-
"Missing required parameters for booking slots calculation"
|
|
1195
|
-
);
|
|
1193
|
+
throw new Error("Missing required parameters for booking slots calculation");
|
|
1196
1194
|
}
|
|
1197
1195
|
if (endDate <= startDate) {
|
|
1198
1196
|
throw new Error("End date must be after start date");
|
|
1199
1197
|
}
|
|
1200
1198
|
const currentUser = this.auth.currentUser;
|
|
1201
1199
|
if (!currentUser) {
|
|
1202
|
-
throw new Error(
|
|
1203
|
-
"User must be authenticated to get available booking slots"
|
|
1204
|
-
);
|
|
1200
|
+
throw new Error("User must be authenticated to get available booking slots");
|
|
1205
1201
|
}
|
|
1206
1202
|
const functionUrl = `https://europe-west6-metaestetics.cloudfunctions.net/bookingApi/getAvailableBookingSlots`;
|
|
1207
1203
|
const idToken = await currentUser.getIdToken();
|
|
1208
|
-
console.log(
|
|
1209
|
-
`[APPOINTMENT_SERVICE] Got user token, user ID: ${currentUser.uid}`
|
|
1210
|
-
);
|
|
1204
|
+
console.log(`[APPOINTMENT_SERVICE] Got user token, user ID: ${currentUser.uid}`);
|
|
1211
1205
|
const requestData = {
|
|
1212
1206
|
clinicId,
|
|
1213
1207
|
practitionerId,
|
|
@@ -1218,9 +1212,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1218
1212
|
end: endDate.getTime()
|
|
1219
1213
|
}
|
|
1220
1214
|
};
|
|
1221
|
-
console.log(
|
|
1222
|
-
`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`
|
|
1223
|
-
);
|
|
1215
|
+
console.log(`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`);
|
|
1224
1216
|
const response = await fetch(functionUrl, {
|
|
1225
1217
|
method: "POST",
|
|
1226
1218
|
mode: "cors",
|
|
@@ -1242,9 +1234,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1242
1234
|
);
|
|
1243
1235
|
if (!response.ok) {
|
|
1244
1236
|
const errorText = await response.text();
|
|
1245
|
-
console.error(
|
|
1246
|
-
`[APPOINTMENT_SERVICE] Error response details: ${errorText}`
|
|
1247
|
-
);
|
|
1237
|
+
console.error(`[APPOINTMENT_SERVICE] Error response details: ${errorText}`);
|
|
1248
1238
|
throw new Error(
|
|
1249
1239
|
`Failed to get available booking slots: ${response.status} ${response.statusText} - ${errorText}`
|
|
1250
1240
|
);
|
|
@@ -1252,24 +1242,15 @@ var AppointmentService = class extends BaseService {
|
|
|
1252
1242
|
const result = await response.json();
|
|
1253
1243
|
console.log(`[APPOINTMENT_SERVICE] Response parsed successfully`, result);
|
|
1254
1244
|
if (!result.success) {
|
|
1255
|
-
throw new Error(
|
|
1256
|
-
result.error || "Failed to get available booking slots"
|
|
1257
|
-
);
|
|
1245
|
+
throw new Error(result.error || "Failed to get available booking slots");
|
|
1258
1246
|
}
|
|
1259
|
-
const slots = result.availableSlots.map(
|
|
1260
|
-
(slot)
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
);
|
|
1264
|
-
console.log(
|
|
1265
|
-
`[APPOINTMENT_SERVICE] Found ${slots.length} available booking slots via HTTP`
|
|
1266
|
-
);
|
|
1247
|
+
const slots = result.availableSlots.map((slot) => ({
|
|
1248
|
+
start: new Date(slot.start)
|
|
1249
|
+
}));
|
|
1250
|
+
console.log(`[APPOINTMENT_SERVICE] Found ${slots.length} available booking slots via HTTP`);
|
|
1267
1251
|
return slots;
|
|
1268
1252
|
} catch (error) {
|
|
1269
|
-
console.error(
|
|
1270
|
-
"[APPOINTMENT_SERVICE] Error getting available booking slots via HTTP:",
|
|
1271
|
-
error
|
|
1272
|
-
);
|
|
1253
|
+
console.error("[APPOINTMENT_SERVICE] Error getting available booking slots via HTTP:", error);
|
|
1273
1254
|
throw error;
|
|
1274
1255
|
}
|
|
1275
1256
|
}
|
|
@@ -1281,9 +1262,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1281
1262
|
*/
|
|
1282
1263
|
async createAppointmentHttp(data) {
|
|
1283
1264
|
try {
|
|
1284
|
-
console.log(
|
|
1285
|
-
"[APPOINTMENT_SERVICE] Creating appointment via cloud function"
|
|
1286
|
-
);
|
|
1265
|
+
console.log("[APPOINTMENT_SERVICE] Creating appointment via cloud function");
|
|
1287
1266
|
const currentUser = this.auth.currentUser;
|
|
1288
1267
|
if (!currentUser) {
|
|
1289
1268
|
throw new Error("User must be authenticated to create an appointment");
|
|
@@ -1297,9 +1276,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1297
1276
|
appointmentEndTime: data.appointmentEndTime.toMillis ? data.appointmentEndTime.toMillis() : new Date(data.appointmentEndTime).getTime(),
|
|
1298
1277
|
patientNotes: (data == null ? void 0 : data.patientNotes) || null
|
|
1299
1278
|
};
|
|
1300
|
-
console.log(
|
|
1301
|
-
`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`
|
|
1302
|
-
);
|
|
1279
|
+
console.log(`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`);
|
|
1303
1280
|
const response = await fetch(functionUrl, {
|
|
1304
1281
|
method: "POST",
|
|
1305
1282
|
mode: "cors",
|
|
@@ -1318,9 +1295,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1318
1295
|
);
|
|
1319
1296
|
if (!response.ok) {
|
|
1320
1297
|
const errorText = await response.text();
|
|
1321
|
-
console.error(
|
|
1322
|
-
`[APPOINTMENT_SERVICE] Error response details: ${errorText}`
|
|
1323
|
-
);
|
|
1298
|
+
console.error(`[APPOINTMENT_SERVICE] Error response details: ${errorText}`);
|
|
1324
1299
|
throw new Error(
|
|
1325
1300
|
`Failed to create appointment: ${response.status} ${response.statusText} - ${errorText}`
|
|
1326
1301
|
);
|
|
@@ -1330,25 +1305,16 @@ var AppointmentService = class extends BaseService {
|
|
|
1330
1305
|
throw new Error(result.error || "Failed to create appointment");
|
|
1331
1306
|
}
|
|
1332
1307
|
if (result.appointmentData) {
|
|
1333
|
-
console.log(
|
|
1334
|
-
`[APPOINTMENT_SERVICE] Appointment created with ID: ${result.appointmentId}`
|
|
1335
|
-
);
|
|
1308
|
+
console.log(`[APPOINTMENT_SERVICE] Appointment created with ID: ${result.appointmentId}`);
|
|
1336
1309
|
return result.appointmentData;
|
|
1337
1310
|
}
|
|
1338
|
-
const createdAppointment = await this.getAppointmentById(
|
|
1339
|
-
result.appointmentId
|
|
1340
|
-
);
|
|
1311
|
+
const createdAppointment = await this.getAppointmentById(result.appointmentId);
|
|
1341
1312
|
if (!createdAppointment) {
|
|
1342
|
-
throw new Error(
|
|
1343
|
-
`Failed to retrieve created appointment with ID: ${result.appointmentId}`
|
|
1344
|
-
);
|
|
1313
|
+
throw new Error(`Failed to retrieve created appointment with ID: ${result.appointmentId}`);
|
|
1345
1314
|
}
|
|
1346
1315
|
return createdAppointment;
|
|
1347
1316
|
} catch (error) {
|
|
1348
|
-
console.error(
|
|
1349
|
-
"[APPOINTMENT_SERVICE] Error creating appointment via cloud function:",
|
|
1350
|
-
error
|
|
1351
|
-
);
|
|
1317
|
+
console.error("[APPOINTMENT_SERVICE] Error creating appointment via cloud function:", error);
|
|
1352
1318
|
throw error;
|
|
1353
1319
|
}
|
|
1354
1320
|
}
|
|
@@ -1360,19 +1326,14 @@ var AppointmentService = class extends BaseService {
|
|
|
1360
1326
|
*/
|
|
1361
1327
|
async getAppointmentById(appointmentId) {
|
|
1362
1328
|
try {
|
|
1363
|
-
console.log(
|
|
1364
|
-
`[APPOINTMENT_SERVICE] Getting appointment with ID: ${appointmentId}`
|
|
1365
|
-
);
|
|
1329
|
+
console.log(`[APPOINTMENT_SERVICE] Getting appointment with ID: ${appointmentId}`);
|
|
1366
1330
|
const appointment = await getAppointmentByIdUtil(this.db, appointmentId);
|
|
1367
1331
|
console.log(
|
|
1368
1332
|
`[APPOINTMENT_SERVICE] Appointment ${appointmentId} ${appointment ? "found" : "not found"}`
|
|
1369
1333
|
);
|
|
1370
1334
|
return appointment;
|
|
1371
1335
|
} catch (error) {
|
|
1372
|
-
console.error(
|
|
1373
|
-
`[APPOINTMENT_SERVICE] Error getting appointment ${appointmentId}:`,
|
|
1374
|
-
error
|
|
1375
|
-
);
|
|
1336
|
+
console.error(`[APPOINTMENT_SERVICE] Error getting appointment ${appointmentId}:`, error);
|
|
1376
1337
|
throw error;
|
|
1377
1338
|
}
|
|
1378
1339
|
}
|
|
@@ -1385,24 +1346,13 @@ var AppointmentService = class extends BaseService {
|
|
|
1385
1346
|
*/
|
|
1386
1347
|
async updateAppointment(appointmentId, data) {
|
|
1387
1348
|
try {
|
|
1388
|
-
console.log(
|
|
1389
|
-
`[APPOINTMENT_SERVICE] Updating appointment with ID: ${appointmentId}`
|
|
1390
|
-
);
|
|
1349
|
+
console.log(`[APPOINTMENT_SERVICE] Updating appointment with ID: ${appointmentId}`);
|
|
1391
1350
|
const validatedData = await updateAppointmentSchema.parseAsync(data);
|
|
1392
|
-
const updatedAppointment = await updateAppointmentUtil(
|
|
1393
|
-
|
|
1394
|
-
appointmentId,
|
|
1395
|
-
validatedData
|
|
1396
|
-
);
|
|
1397
|
-
console.log(
|
|
1398
|
-
`[APPOINTMENT_SERVICE] Appointment ${appointmentId} updated successfully`
|
|
1399
|
-
);
|
|
1351
|
+
const updatedAppointment = await updateAppointmentUtil(this.db, appointmentId, validatedData);
|
|
1352
|
+
console.log(`[APPOINTMENT_SERVICE] Appointment ${appointmentId} updated successfully`);
|
|
1400
1353
|
return updatedAppointment;
|
|
1401
1354
|
} catch (error) {
|
|
1402
|
-
console.error(
|
|
1403
|
-
`[APPOINTMENT_SERVICE] Error updating appointment ${appointmentId}:`,
|
|
1404
|
-
error
|
|
1405
|
-
);
|
|
1355
|
+
console.error(`[APPOINTMENT_SERVICE] Error updating appointment ${appointmentId}:`, error);
|
|
1406
1356
|
throw error;
|
|
1407
1357
|
}
|
|
1408
1358
|
}
|
|
@@ -1414,21 +1364,13 @@ var AppointmentService = class extends BaseService {
|
|
|
1414
1364
|
*/
|
|
1415
1365
|
async searchAppointments(params) {
|
|
1416
1366
|
try {
|
|
1417
|
-
console.log(
|
|
1418
|
-
"[APPOINTMENT_SERVICE] Searching appointments with params:",
|
|
1419
|
-
params
|
|
1420
|
-
);
|
|
1367
|
+
console.log("[APPOINTMENT_SERVICE] Searching appointments with params:", params);
|
|
1421
1368
|
await searchAppointmentsSchema.parseAsync(params);
|
|
1422
1369
|
const result = await searchAppointmentsUtil(this.db, params);
|
|
1423
|
-
console.log(
|
|
1424
|
-
`[APPOINTMENT_SERVICE] Found ${result.appointments.length} appointments`
|
|
1425
|
-
);
|
|
1370
|
+
console.log(`[APPOINTMENT_SERVICE] Found ${result.appointments.length} appointments`);
|
|
1426
1371
|
return result;
|
|
1427
1372
|
} catch (error) {
|
|
1428
|
-
console.error(
|
|
1429
|
-
"[APPOINTMENT_SERVICE] Error searching appointments:",
|
|
1430
|
-
error
|
|
1431
|
-
);
|
|
1373
|
+
console.error("[APPOINTMENT_SERVICE] Error searching appointments:", error);
|
|
1432
1374
|
throw error;
|
|
1433
1375
|
}
|
|
1434
1376
|
}
|
|
@@ -1440,9 +1382,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1440
1382
|
* @returns Found appointments and the last document for pagination
|
|
1441
1383
|
*/
|
|
1442
1384
|
async getPatientAppointments(patientId, options) {
|
|
1443
|
-
console.log(
|
|
1444
|
-
`[APPOINTMENT_SERVICE] Getting appointments for patient: ${patientId}`
|
|
1445
|
-
);
|
|
1385
|
+
console.log(`[APPOINTMENT_SERVICE] Getting appointments for patient: ${patientId}`);
|
|
1446
1386
|
const searchParams = {
|
|
1447
1387
|
patientId,
|
|
1448
1388
|
startDate: options == null ? void 0 : options.startDate,
|
|
@@ -1461,9 +1401,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1461
1401
|
* @returns Found appointments and the last document for pagination
|
|
1462
1402
|
*/
|
|
1463
1403
|
async getPractitionerAppointments(practitionerId, options) {
|
|
1464
|
-
console.log(
|
|
1465
|
-
`[APPOINTMENT_SERVICE] Getting appointments for practitioner: ${practitionerId}`
|
|
1466
|
-
);
|
|
1404
|
+
console.log(`[APPOINTMENT_SERVICE] Getting appointments for practitioner: ${practitionerId}`);
|
|
1467
1405
|
const searchParams = {
|
|
1468
1406
|
practitionerId,
|
|
1469
1407
|
startDate: options == null ? void 0 : options.startDate,
|
|
@@ -1482,9 +1420,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1482
1420
|
* @returns Found appointments and the last document for pagination
|
|
1483
1421
|
*/
|
|
1484
1422
|
async getClinicAppointments(clinicBranchId, options) {
|
|
1485
|
-
console.log(
|
|
1486
|
-
`[APPOINTMENT_SERVICE] Getting appointments for clinic: ${clinicBranchId}`
|
|
1487
|
-
);
|
|
1423
|
+
console.log(`[APPOINTMENT_SERVICE] Getting appointments for clinic: ${clinicBranchId}`);
|
|
1488
1424
|
const searchParams = {
|
|
1489
1425
|
clinicBranchId,
|
|
1490
1426
|
practitionerId: options == null ? void 0 : options.practitionerId,
|
|
@@ -1535,68 +1471,42 @@ var AppointmentService = class extends BaseService {
|
|
|
1535
1471
|
* Confirms a PENDING appointment by an Admin/Clinic.
|
|
1536
1472
|
*/
|
|
1537
1473
|
async confirmAppointmentAdmin(appointmentId) {
|
|
1538
|
-
console.log(
|
|
1539
|
-
`[APPOINTMENT_SERVICE] Admin confirming appointment: ${appointmentId}`
|
|
1540
|
-
);
|
|
1474
|
+
console.log(`[APPOINTMENT_SERVICE] Admin confirming appointment: ${appointmentId}`);
|
|
1541
1475
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1542
|
-
if (!appointment)
|
|
1543
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1476
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1544
1477
|
if (appointment.status !== "pending" /* PENDING */) {
|
|
1545
|
-
throw new Error(
|
|
1546
|
-
`Appointment ${appointmentId} is not in PENDING state to be confirmed.`
|
|
1547
|
-
);
|
|
1478
|
+
throw new Error(`Appointment ${appointmentId} is not in PENDING state to be confirmed.`);
|
|
1548
1479
|
}
|
|
1549
|
-
return this.updateAppointmentStatus(
|
|
1550
|
-
appointmentId,
|
|
1551
|
-
"confirmed" /* CONFIRMED */
|
|
1552
|
-
);
|
|
1480
|
+
return this.updateAppointmentStatus(appointmentId, "confirmed" /* CONFIRMED */);
|
|
1553
1481
|
}
|
|
1554
1482
|
/**
|
|
1555
1483
|
* Cancels an appointment by the User (Patient).
|
|
1556
1484
|
*/
|
|
1557
1485
|
async cancelAppointmentUser(appointmentId, reason) {
|
|
1558
|
-
console.log(
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
"canceled_patient" /* CANCELED_PATIENT */,
|
|
1564
|
-
{
|
|
1565
|
-
cancellationReason: reason,
|
|
1566
|
-
canceledBy: "patient"
|
|
1567
|
-
}
|
|
1568
|
-
);
|
|
1486
|
+
console.log(`[APPOINTMENT_SERVICE] User canceling appointment: ${appointmentId}`);
|
|
1487
|
+
return this.updateAppointmentStatus(appointmentId, "canceled_patient" /* CANCELED_PATIENT */, {
|
|
1488
|
+
cancellationReason: reason,
|
|
1489
|
+
canceledBy: "patient"
|
|
1490
|
+
});
|
|
1569
1491
|
}
|
|
1570
1492
|
/**
|
|
1571
1493
|
* Cancels an appointment by an Admin/Clinic.
|
|
1572
1494
|
*/
|
|
1573
1495
|
async cancelAppointmentAdmin(appointmentId, reason) {
|
|
1574
|
-
console.log(
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
"canceled_clinic" /* CANCELED_CLINIC */,
|
|
1580
|
-
{
|
|
1581
|
-
cancellationReason: reason,
|
|
1582
|
-
canceledBy: "clinic"
|
|
1583
|
-
}
|
|
1584
|
-
);
|
|
1496
|
+
console.log(`[APPOINTMENT_SERVICE] Admin canceling appointment: ${appointmentId}`);
|
|
1497
|
+
return this.updateAppointmentStatus(appointmentId, "canceled_clinic" /* CANCELED_CLINIC */, {
|
|
1498
|
+
cancellationReason: reason,
|
|
1499
|
+
canceledBy: "clinic"
|
|
1500
|
+
});
|
|
1585
1501
|
}
|
|
1586
1502
|
/**
|
|
1587
1503
|
* Admin proposes to reschedule an appointment.
|
|
1588
1504
|
* Sets status to RESCHEDULED_BY_CLINIC and updates times.
|
|
1589
1505
|
*/
|
|
1590
1506
|
async rescheduleAppointmentAdmin(params) {
|
|
1591
|
-
console.log(
|
|
1592
|
-
|
|
1593
|
-
);
|
|
1594
|
-
const validatedParams = await rescheduleAppointmentSchema.parseAsync(
|
|
1595
|
-
params
|
|
1596
|
-
);
|
|
1597
|
-
const startTimestamp = this.convertToTimestamp(
|
|
1598
|
-
validatedParams.newStartTime
|
|
1599
|
-
);
|
|
1507
|
+
console.log(`[APPOINTMENT_SERVICE] Admin rescheduling appointment: ${params.appointmentId}`);
|
|
1508
|
+
const validatedParams = await rescheduleAppointmentSchema.parseAsync(params);
|
|
1509
|
+
const startTimestamp = this.convertToTimestamp(validatedParams.newStartTime);
|
|
1600
1510
|
const endTimestamp = this.convertToTimestamp(validatedParams.newEndTime);
|
|
1601
1511
|
if (endTimestamp.toMillis() <= startTimestamp.toMillis()) {
|
|
1602
1512
|
throw new Error("New end time must be after new start time.");
|
|
@@ -1639,48 +1549,31 @@ var AppointmentService = class extends BaseService {
|
|
|
1639
1549
|
if (value && typeof value.seconds === "number") {
|
|
1640
1550
|
return new import_firestore2.Timestamp(value.seconds, value.nanoseconds || 0);
|
|
1641
1551
|
}
|
|
1642
|
-
throw new Error(
|
|
1643
|
-
`Invalid timestamp format: ${typeof value}, value: ${JSON.stringify(
|
|
1644
|
-
value
|
|
1645
|
-
)}`
|
|
1646
|
-
);
|
|
1552
|
+
throw new Error(`Invalid timestamp format: ${typeof value}, value: ${JSON.stringify(value)}`);
|
|
1647
1553
|
}
|
|
1648
1554
|
/**
|
|
1649
1555
|
* User confirms a reschedule proposed by the clinic.
|
|
1650
1556
|
* Status changes from RESCHEDULED_BY_CLINIC to CONFIRMED.
|
|
1651
1557
|
*/
|
|
1652
1558
|
async rescheduleAppointmentConfirmUser(appointmentId) {
|
|
1653
|
-
console.log(
|
|
1654
|
-
`[APPOINTMENT_SERVICE] User confirming reschedule for: ${appointmentId}`
|
|
1655
|
-
);
|
|
1559
|
+
console.log(`[APPOINTMENT_SERVICE] User confirming reschedule for: ${appointmentId}`);
|
|
1656
1560
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1657
|
-
if (!appointment)
|
|
1658
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1561
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1659
1562
|
if (appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
1660
|
-
throw new Error(
|
|
1661
|
-
`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`
|
|
1662
|
-
);
|
|
1563
|
+
throw new Error(`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`);
|
|
1663
1564
|
}
|
|
1664
|
-
return this.updateAppointmentStatus(
|
|
1665
|
-
appointmentId,
|
|
1666
|
-
"confirmed" /* CONFIRMED */
|
|
1667
|
-
);
|
|
1565
|
+
return this.updateAppointmentStatus(appointmentId, "confirmed" /* CONFIRMED */);
|
|
1668
1566
|
}
|
|
1669
1567
|
/**
|
|
1670
1568
|
* User rejects a reschedule proposed by the clinic.
|
|
1671
1569
|
* Status changes from RESCHEDULED_BY_CLINIC to CANCELED_PATIENT_RESCHEDULED.
|
|
1672
1570
|
*/
|
|
1673
1571
|
async rescheduleAppointmentRejectUser(appointmentId, reason) {
|
|
1674
|
-
console.log(
|
|
1675
|
-
`[APPOINTMENT_SERVICE] User rejecting reschedule for: ${appointmentId}`
|
|
1676
|
-
);
|
|
1572
|
+
console.log(`[APPOINTMENT_SERVICE] User rejecting reschedule for: ${appointmentId}`);
|
|
1677
1573
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1678
|
-
if (!appointment)
|
|
1679
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1574
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1680
1575
|
if (appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
1681
|
-
throw new Error(
|
|
1682
|
-
`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`
|
|
1683
|
-
);
|
|
1576
|
+
throw new Error(`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`);
|
|
1684
1577
|
}
|
|
1685
1578
|
return this.updateAppointmentStatus(
|
|
1686
1579
|
appointmentId,
|
|
@@ -1696,17 +1589,12 @@ var AppointmentService = class extends BaseService {
|
|
|
1696
1589
|
* Requires all pending user forms to be completed.
|
|
1697
1590
|
*/
|
|
1698
1591
|
async checkInPatientAdmin(appointmentId) {
|
|
1699
|
-
console.log(
|
|
1700
|
-
`[APPOINTMENT_SERVICE] Admin checking in patient for: ${appointmentId}`
|
|
1701
|
-
);
|
|
1592
|
+
console.log(`[APPOINTMENT_SERVICE] Admin checking in patient for: ${appointmentId}`);
|
|
1702
1593
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1703
|
-
if (!appointment)
|
|
1704
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1594
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1705
1595
|
if (appointment.pendingUserFormsIds && appointment.pendingUserFormsIds.length > 0) {
|
|
1706
1596
|
throw new Error(
|
|
1707
|
-
`Cannot check in: Patient has ${appointment.pendingUserFormsIds.length} pending required form(s). IDs: ${appointment.pendingUserFormsIds.join(
|
|
1708
|
-
", "
|
|
1709
|
-
)}`
|
|
1597
|
+
`Cannot check in: Patient has ${appointment.pendingUserFormsIds.length} pending required form(s). IDs: ${appointment.pendingUserFormsIds.join(", ")}`
|
|
1710
1598
|
);
|
|
1711
1599
|
}
|
|
1712
1600
|
if (appointment.status !== "confirmed" /* CONFIRMED */ && appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
@@ -1714,25 +1602,17 @@ var AppointmentService = class extends BaseService {
|
|
|
1714
1602
|
`Checking in appointment ${appointmentId} with status ${appointment.status}. Ensure this is intended.`
|
|
1715
1603
|
);
|
|
1716
1604
|
}
|
|
1717
|
-
return this.updateAppointmentStatus(
|
|
1718
|
-
appointmentId,
|
|
1719
|
-
"checked_in" /* CHECKED_IN */
|
|
1720
|
-
);
|
|
1605
|
+
return this.updateAppointmentStatus(appointmentId, "checked_in" /* CHECKED_IN */);
|
|
1721
1606
|
}
|
|
1722
1607
|
/**
|
|
1723
1608
|
* Doctor starts the appointment procedure.
|
|
1724
1609
|
*/
|
|
1725
1610
|
async startAppointmentDoctor(appointmentId) {
|
|
1726
|
-
console.log(
|
|
1727
|
-
`[APPOINTMENT_SERVICE] Doctor starting appointment: ${appointmentId}`
|
|
1728
|
-
);
|
|
1611
|
+
console.log(`[APPOINTMENT_SERVICE] Doctor starting appointment: ${appointmentId}`);
|
|
1729
1612
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1730
|
-
if (!appointment)
|
|
1731
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1613
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1732
1614
|
if (appointment.status !== "checked_in" /* CHECKED_IN */) {
|
|
1733
|
-
throw new Error(
|
|
1734
|
-
`Appointment ${appointmentId} must be CHECKED_IN to start.`
|
|
1735
|
-
);
|
|
1615
|
+
throw new Error(`Appointment ${appointmentId} must be CHECKED_IN to start.`);
|
|
1736
1616
|
}
|
|
1737
1617
|
const updateData = {
|
|
1738
1618
|
status: "in_progress" /* IN_PROGRESS */,
|
|
@@ -1746,24 +1626,18 @@ var AppointmentService = class extends BaseService {
|
|
|
1746
1626
|
* Doctor completes and finalizes the appointment.
|
|
1747
1627
|
*/
|
|
1748
1628
|
async completeAppointmentDoctor(appointmentId, finalizationNotes, actualDurationMinutesInput) {
|
|
1749
|
-
console.log(
|
|
1750
|
-
`[APPOINTMENT_SERVICE] Doctor completing appointment: ${appointmentId}`
|
|
1751
|
-
);
|
|
1629
|
+
console.log(`[APPOINTMENT_SERVICE] Doctor completing appointment: ${appointmentId}`);
|
|
1752
1630
|
const currentUser = this.auth.currentUser;
|
|
1753
|
-
if (!currentUser)
|
|
1754
|
-
throw new Error("Authentication required to complete appointment.");
|
|
1631
|
+
if (!currentUser) throw new Error("Authentication required to complete appointment.");
|
|
1755
1632
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1756
|
-
if (!appointment)
|
|
1757
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1633
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1758
1634
|
let calculatedDurationMinutes = actualDurationMinutesInput;
|
|
1759
1635
|
const procedureCompletionTime = import_firestore2.Timestamp.now();
|
|
1760
1636
|
if (calculatedDurationMinutes === void 0 && appointment.procedureActualStartTime) {
|
|
1761
1637
|
const startTimeMillis = appointment.procedureActualStartTime.toMillis();
|
|
1762
1638
|
const endTimeMillis = procedureCompletionTime.toMillis();
|
|
1763
1639
|
if (endTimeMillis > startTimeMillis) {
|
|
1764
|
-
calculatedDurationMinutes = Math.round(
|
|
1765
|
-
(endTimeMillis - startTimeMillis) / 6e4
|
|
1766
|
-
);
|
|
1640
|
+
calculatedDurationMinutes = Math.round((endTimeMillis - startTimeMillis) / 6e4);
|
|
1767
1641
|
}
|
|
1768
1642
|
}
|
|
1769
1643
|
const updateData = {
|
|
@@ -1787,31 +1661,22 @@ var AppointmentService = class extends BaseService {
|
|
|
1787
1661
|
* Admin marks an appointment as No-Show.
|
|
1788
1662
|
*/
|
|
1789
1663
|
async markNoShowAdmin(appointmentId) {
|
|
1790
|
-
console.log(
|
|
1791
|
-
`[APPOINTMENT_SERVICE] Admin marking no-show for: ${appointmentId}`
|
|
1792
|
-
);
|
|
1664
|
+
console.log(`[APPOINTMENT_SERVICE] Admin marking no-show for: ${appointmentId}`);
|
|
1793
1665
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1794
|
-
if (!appointment)
|
|
1795
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1666
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1796
1667
|
if (import_firestore2.Timestamp.now().toMillis() < appointment.appointmentStartTime.toMillis()) {
|
|
1797
1668
|
throw new Error("Cannot mark no-show before appointment start time.");
|
|
1798
1669
|
}
|
|
1799
|
-
return this.updateAppointmentStatus(
|
|
1800
|
-
|
|
1801
|
-
"
|
|
1802
|
-
|
|
1803
|
-
cancellationReason: "Patient did not show up for the appointment.",
|
|
1804
|
-
canceledBy: "clinic"
|
|
1805
|
-
}
|
|
1806
|
-
);
|
|
1670
|
+
return this.updateAppointmentStatus(appointmentId, "no_show" /* NO_SHOW */, {
|
|
1671
|
+
cancellationReason: "Patient did not show up for the appointment.",
|
|
1672
|
+
canceledBy: "clinic"
|
|
1673
|
+
});
|
|
1807
1674
|
}
|
|
1808
1675
|
/**
|
|
1809
1676
|
* Adds a media item to an appointment.
|
|
1810
1677
|
*/
|
|
1811
1678
|
async addMediaToAppointment(appointmentId, mediaItemData) {
|
|
1812
|
-
console.log(
|
|
1813
|
-
`[APPOINTMENT_SERVICE] Adding media to appointment ${appointmentId}`
|
|
1814
|
-
);
|
|
1679
|
+
console.log(`[APPOINTMENT_SERVICE] Adding media to appointment ${appointmentId}`);
|
|
1815
1680
|
const currentUser = this.auth.currentUser;
|
|
1816
1681
|
if (!currentUser) throw new Error("Authentication required.");
|
|
1817
1682
|
const newMediaItem = {
|
|
@@ -1851,9 +1716,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1851
1716
|
* Adds or updates review information for an appointment.
|
|
1852
1717
|
*/
|
|
1853
1718
|
async addReviewToAppointment(appointmentId, reviewData) {
|
|
1854
|
-
console.log(
|
|
1855
|
-
`[APPOINTMENT_SERVICE] Adding review to appointment ${appointmentId}`
|
|
1856
|
-
);
|
|
1719
|
+
console.log(`[APPOINTMENT_SERVICE] Adding review to appointment ${appointmentId}`);
|
|
1857
1720
|
const newReviewInfo = {
|
|
1858
1721
|
...reviewData,
|
|
1859
1722
|
reviewId: this.generateId(),
|
|
@@ -1887,9 +1750,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1887
1750
|
* @returns The updated appointment
|
|
1888
1751
|
*/
|
|
1889
1752
|
async updateInternalNotes(appointmentId, notes) {
|
|
1890
|
-
console.log(
|
|
1891
|
-
`[APPOINTMENT_SERVICE] Updating internal notes for appointment: ${appointmentId}`
|
|
1892
|
-
);
|
|
1753
|
+
console.log(`[APPOINTMENT_SERVICE] Updating internal notes for appointment: ${appointmentId}`);
|
|
1893
1754
|
const updateData = {
|
|
1894
1755
|
internalNotes: notes
|
|
1895
1756
|
};
|
|
@@ -1905,9 +1766,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1905
1766
|
*/
|
|
1906
1767
|
async getUpcomingPatientAppointments(patientId, options) {
|
|
1907
1768
|
try {
|
|
1908
|
-
console.log(
|
|
1909
|
-
`[APPOINTMENT_SERVICE] Getting upcoming appointments for patient: ${patientId}`
|
|
1910
|
-
);
|
|
1769
|
+
console.log(`[APPOINTMENT_SERVICE] Getting upcoming appointments for patient: ${patientId}`);
|
|
1911
1770
|
const effectiveStartDate = (options == null ? void 0 : options.startDate) || /* @__PURE__ */ new Date();
|
|
1912
1771
|
const upcomingStatuses = [
|
|
1913
1772
|
"pending" /* PENDING */,
|
|
@@ -1919,21 +1778,9 @@ var AppointmentService = class extends BaseService {
|
|
|
1919
1778
|
const constraints = [];
|
|
1920
1779
|
constraints.push((0, import_firestore2.where)("patientId", "==", patientId));
|
|
1921
1780
|
constraints.push((0, import_firestore2.where)("status", "in", upcomingStatuses));
|
|
1922
|
-
constraints.push(
|
|
1923
|
-
(0, import_firestore2.where)(
|
|
1924
|
-
"appointmentStartTime",
|
|
1925
|
-
">=",
|
|
1926
|
-
import_firestore2.Timestamp.fromDate(effectiveStartDate)
|
|
1927
|
-
)
|
|
1928
|
-
);
|
|
1781
|
+
constraints.push((0, import_firestore2.where)("appointmentStartTime", ">=", import_firestore2.Timestamp.fromDate(effectiveStartDate)));
|
|
1929
1782
|
if (options == null ? void 0 : options.endDate) {
|
|
1930
|
-
constraints.push(
|
|
1931
|
-
(0, import_firestore2.where)(
|
|
1932
|
-
"appointmentStartTime",
|
|
1933
|
-
"<=",
|
|
1934
|
-
import_firestore2.Timestamp.fromDate(options.endDate)
|
|
1935
|
-
)
|
|
1936
|
-
);
|
|
1783
|
+
constraints.push((0, import_firestore2.where)("appointmentStartTime", "<=", import_firestore2.Timestamp.fromDate(options.endDate)));
|
|
1937
1784
|
}
|
|
1938
1785
|
constraints.push((0, import_firestore2.orderBy)("appointmentStartTime", "asc"));
|
|
1939
1786
|
if (options == null ? void 0 : options.limit) {
|
|
@@ -1942,14 +1789,9 @@ var AppointmentService = class extends BaseService {
|
|
|
1942
1789
|
if (options == null ? void 0 : options.startAfter) {
|
|
1943
1790
|
constraints.push((0, import_firestore2.startAfter)(options.startAfter));
|
|
1944
1791
|
}
|
|
1945
|
-
const q = (0, import_firestore2.query)(
|
|
1946
|
-
(0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION),
|
|
1947
|
-
...constraints
|
|
1948
|
-
);
|
|
1792
|
+
const q = (0, import_firestore2.query)((0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION), ...constraints);
|
|
1949
1793
|
const querySnapshot = await (0, import_firestore2.getDocs)(q);
|
|
1950
|
-
const appointments = querySnapshot.docs.map(
|
|
1951
|
-
(doc38) => doc38.data()
|
|
1952
|
-
);
|
|
1794
|
+
const appointments = querySnapshot.docs.map((doc38) => doc38.data());
|
|
1953
1795
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
1954
1796
|
console.log(
|
|
1955
1797
|
`[APPOINTMENT_SERVICE] Found ${appointments.length} upcoming appointments for patient ${patientId}`
|
|
@@ -1974,9 +1816,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1974
1816
|
*/
|
|
1975
1817
|
async getPastPatientAppointments(patientId, options) {
|
|
1976
1818
|
try {
|
|
1977
|
-
console.log(
|
|
1978
|
-
`[APPOINTMENT_SERVICE] Getting past appointments for patient: ${patientId}`
|
|
1979
|
-
);
|
|
1819
|
+
console.log(`[APPOINTMENT_SERVICE] Getting past appointments for patient: ${patientId}`);
|
|
1980
1820
|
const effectiveEndDate = (options == null ? void 0 : options.endDate) || /* @__PURE__ */ new Date();
|
|
1981
1821
|
const pastStatuses = ["completed" /* COMPLETED */];
|
|
1982
1822
|
if (options == null ? void 0 : options.showCanceled) {
|
|
@@ -1994,20 +1834,10 @@ var AppointmentService = class extends BaseService {
|
|
|
1994
1834
|
constraints.push((0, import_firestore2.where)("status", "in", pastStatuses));
|
|
1995
1835
|
if (options == null ? void 0 : options.startDate) {
|
|
1996
1836
|
constraints.push(
|
|
1997
|
-
(0, import_firestore2.where)(
|
|
1998
|
-
"appointmentStartTime",
|
|
1999
|
-
">=",
|
|
2000
|
-
import_firestore2.Timestamp.fromDate(options.startDate)
|
|
2001
|
-
)
|
|
1837
|
+
(0, import_firestore2.where)("appointmentStartTime", ">=", import_firestore2.Timestamp.fromDate(options.startDate))
|
|
2002
1838
|
);
|
|
2003
1839
|
}
|
|
2004
|
-
constraints.push(
|
|
2005
|
-
(0, import_firestore2.where)(
|
|
2006
|
-
"appointmentStartTime",
|
|
2007
|
-
"<=",
|
|
2008
|
-
import_firestore2.Timestamp.fromDate(effectiveEndDate)
|
|
2009
|
-
)
|
|
2010
|
-
);
|
|
1840
|
+
constraints.push((0, import_firestore2.where)("appointmentStartTime", "<=", import_firestore2.Timestamp.fromDate(effectiveEndDate)));
|
|
2011
1841
|
constraints.push((0, import_firestore2.orderBy)("appointmentStartTime", "desc"));
|
|
2012
1842
|
if (options == null ? void 0 : options.limit) {
|
|
2013
1843
|
constraints.push((0, import_firestore2.limit)(options.limit));
|
|
@@ -2015,14 +1845,9 @@ var AppointmentService = class extends BaseService {
|
|
|
2015
1845
|
if (options == null ? void 0 : options.startAfter) {
|
|
2016
1846
|
constraints.push((0, import_firestore2.startAfter)(options.startAfter));
|
|
2017
1847
|
}
|
|
2018
|
-
const q = (0, import_firestore2.query)(
|
|
2019
|
-
(0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION),
|
|
2020
|
-
...constraints
|
|
2021
|
-
);
|
|
1848
|
+
const q = (0, import_firestore2.query)((0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION), ...constraints);
|
|
2022
1849
|
const querySnapshot = await (0, import_firestore2.getDocs)(q);
|
|
2023
|
-
const appointments = querySnapshot.docs.map(
|
|
2024
|
-
(doc38) => doc38.data()
|
|
2025
|
-
);
|
|
1850
|
+
const appointments = querySnapshot.docs.map((doc38) => doc38.data());
|
|
2026
1851
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
2027
1852
|
console.log(
|
|
2028
1853
|
`[APPOINTMENT_SERVICE] Found ${appointments.length} past appointments for patient ${patientId}`
|
|
@@ -2036,6 +1861,46 @@ var AppointmentService = class extends BaseService {
|
|
|
2036
1861
|
throw error;
|
|
2037
1862
|
}
|
|
2038
1863
|
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Counts completed appointments for a patient with optional clinic filtering.
|
|
1866
|
+
*
|
|
1867
|
+
* @param patientId ID of the patient.
|
|
1868
|
+
* @param clinicBranchId Optional ID of the clinic branch to either include or exclude.
|
|
1869
|
+
* @param excludeClinic Optional boolean. If true (default), excludes the specified clinic. If false, includes only that clinic.
|
|
1870
|
+
* @returns The count of completed appointments.
|
|
1871
|
+
*/
|
|
1872
|
+
async countCompletedAppointments(patientId, clinicBranchId, excludeClinic = true) {
|
|
1873
|
+
try {
|
|
1874
|
+
console.log(
|
|
1875
|
+
`[APPOINTMENT_SERVICE] Counting completed appointments for patient: ${patientId}`,
|
|
1876
|
+
{ clinicBranchId, excludeClinic }
|
|
1877
|
+
);
|
|
1878
|
+
const constraints = [
|
|
1879
|
+
(0, import_firestore2.where)("patientId", "==", patientId),
|
|
1880
|
+
(0, import_firestore2.where)("status", "==", "completed" /* COMPLETED */)
|
|
1881
|
+
];
|
|
1882
|
+
if (clinicBranchId) {
|
|
1883
|
+
if (excludeClinic) {
|
|
1884
|
+
constraints.push((0, import_firestore2.where)("clinicBranchId", "!=", clinicBranchId));
|
|
1885
|
+
} else {
|
|
1886
|
+
constraints.push((0, import_firestore2.where)("clinicBranchId", "==", clinicBranchId));
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
const q = (0, import_firestore2.query)((0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION), ...constraints);
|
|
1890
|
+
const snapshot = await (0, import_firestore2.getCountFromServer)(q);
|
|
1891
|
+
const count = snapshot.data().count;
|
|
1892
|
+
console.log(
|
|
1893
|
+
`[APPOINTMENT_SERVICE] Found ${count} completed appointments for patient ${patientId}`
|
|
1894
|
+
);
|
|
1895
|
+
return count;
|
|
1896
|
+
} catch (error) {
|
|
1897
|
+
console.error(
|
|
1898
|
+
`[APPOINTMENT_SERVICE] Error counting completed appointments for patient ${patientId}:`,
|
|
1899
|
+
error
|
|
1900
|
+
);
|
|
1901
|
+
throw error;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
2039
1904
|
};
|
|
2040
1905
|
|
|
2041
1906
|
// src/services/auth/auth.service.ts
|
|
@@ -9909,19 +9774,13 @@ var AuthService = class extends BaseService {
|
|
|
9909
9774
|
constructor(db, auth, app, userService) {
|
|
9910
9775
|
super(db, auth, app);
|
|
9911
9776
|
this.googleProvider = new import_auth7.GoogleAuthProvider();
|
|
9912
|
-
this.facebookProvider = new import_auth7.FacebookAuthProvider();
|
|
9913
|
-
this.appleProvider = new import_auth7.OAuthProvider("apple.com");
|
|
9914
9777
|
this.userService = userService || new UserService(db, auth, app);
|
|
9915
9778
|
}
|
|
9916
9779
|
/**
|
|
9917
9780
|
* Registruje novog korisnika sa email-om i lozinkom
|
|
9918
9781
|
*/
|
|
9919
9782
|
async signUp(email, password, initialRole = "patient" /* PATIENT */, options) {
|
|
9920
|
-
const { user: firebaseUser } = await (0, import_auth7.createUserWithEmailAndPassword)(
|
|
9921
|
-
this.auth,
|
|
9922
|
-
email,
|
|
9923
|
-
password
|
|
9924
|
-
);
|
|
9783
|
+
const { user: firebaseUser } = await (0, import_auth7.createUserWithEmailAndPassword)(this.auth, email, password);
|
|
9925
9784
|
return this.userService.createUser(firebaseUser, [initialRole], options);
|
|
9926
9785
|
}
|
|
9927
9786
|
/**
|
|
@@ -9940,20 +9799,13 @@ var AuthService = class extends BaseService {
|
|
|
9940
9799
|
await clinicAdminSignupSchema.parseAsync(data);
|
|
9941
9800
|
console.log("[AUTH] Clinic admin signup data validation passed");
|
|
9942
9801
|
} catch (validationError) {
|
|
9943
|
-
console.error(
|
|
9944
|
-
"[AUTH] Validation error in signUpClinicAdmin:",
|
|
9945
|
-
validationError
|
|
9946
|
-
);
|
|
9802
|
+
console.error("[AUTH] Validation error in signUpClinicAdmin:", validationError);
|
|
9947
9803
|
throw validationError;
|
|
9948
9804
|
}
|
|
9949
9805
|
console.log("[AUTH] Creating Firebase user");
|
|
9950
9806
|
let firebaseUser;
|
|
9951
9807
|
try {
|
|
9952
|
-
const result = await (0, import_auth7.createUserWithEmailAndPassword)(
|
|
9953
|
-
this.auth,
|
|
9954
|
-
data.email,
|
|
9955
|
-
data.password
|
|
9956
|
-
);
|
|
9808
|
+
const result = await (0, import_auth7.createUserWithEmailAndPassword)(this.auth, data.email, data.password);
|
|
9957
9809
|
firebaseUser = result.user;
|
|
9958
9810
|
console.log("[AUTH] Firebase user created successfully", {
|
|
9959
9811
|
uid: firebaseUser.uid
|
|
@@ -9965,13 +9817,9 @@ var AuthService = class extends BaseService {
|
|
|
9965
9817
|
console.log("[AUTH] Creating user with CLINIC_ADMIN role");
|
|
9966
9818
|
let user;
|
|
9967
9819
|
try {
|
|
9968
|
-
user = await this.userService.createUser(
|
|
9969
|
-
|
|
9970
|
-
|
|
9971
|
-
{
|
|
9972
|
-
skipProfileCreation: true
|
|
9973
|
-
}
|
|
9974
|
-
);
|
|
9820
|
+
user = await this.userService.createUser(firebaseUser, ["clinic_admin" /* CLINIC_ADMIN */], {
|
|
9821
|
+
skipProfileCreation: true
|
|
9822
|
+
});
|
|
9975
9823
|
console.log("[AUTH] User with CLINIC_ADMIN role created successfully", {
|
|
9976
9824
|
userId: user.uid
|
|
9977
9825
|
});
|
|
@@ -9988,11 +9836,7 @@ var AuthService = class extends BaseService {
|
|
|
9988
9836
|
};
|
|
9989
9837
|
console.log("[AUTH] Contact person object created");
|
|
9990
9838
|
console.log("[AUTH] Initializing clinic services");
|
|
9991
|
-
const clinicAdminService = new ClinicAdminService(
|
|
9992
|
-
this.db,
|
|
9993
|
-
this.auth,
|
|
9994
|
-
this.app
|
|
9995
|
-
);
|
|
9839
|
+
const clinicAdminService = new ClinicAdminService(this.db, this.auth, this.app);
|
|
9996
9840
|
const clinicGroupService = new ClinicGroupService(
|
|
9997
9841
|
this.db,
|
|
9998
9842
|
this.auth,
|
|
@@ -10009,18 +9853,14 @@ var AuthService = class extends BaseService {
|
|
|
10009
9853
|
mediaService
|
|
10010
9854
|
);
|
|
10011
9855
|
clinicAdminService.setServices(clinicGroupService, clinicService);
|
|
10012
|
-
console.log(
|
|
10013
|
-
"[AUTH] Services initialized and circular dependencies resolved"
|
|
10014
|
-
);
|
|
9856
|
+
console.log("[AUTH] Services initialized and circular dependencies resolved");
|
|
10015
9857
|
let clinicGroup = null;
|
|
10016
9858
|
let adminProfile = null;
|
|
10017
9859
|
if (data.isCreatingNewGroup) {
|
|
10018
9860
|
console.log("[AUTH] Creating new clinic group flow");
|
|
10019
9861
|
if (!data.clinicGroupData) {
|
|
10020
9862
|
console.error("[AUTH] Clinic group data is missing");
|
|
10021
|
-
throw new Error(
|
|
10022
|
-
"Clinic group data is required when creating a new group"
|
|
10023
|
-
);
|
|
9863
|
+
throw new Error("Clinic group data is required when creating a new group");
|
|
10024
9864
|
}
|
|
10025
9865
|
console.log("[AUTH] Creating clinic admin first (without group)");
|
|
10026
9866
|
const createClinicAdminData = {
|
|
@@ -10033,17 +9873,12 @@ var AuthService = class extends BaseService {
|
|
|
10033
9873
|
// No clinicGroupId yet
|
|
10034
9874
|
};
|
|
10035
9875
|
try {
|
|
10036
|
-
adminProfile = await clinicAdminService.createClinicAdmin(
|
|
10037
|
-
createClinicAdminData
|
|
10038
|
-
);
|
|
9876
|
+
adminProfile = await clinicAdminService.createClinicAdmin(createClinicAdminData);
|
|
10039
9877
|
console.log("[AUTH] Clinic admin created successfully", {
|
|
10040
9878
|
adminId: adminProfile.id
|
|
10041
9879
|
});
|
|
10042
9880
|
} catch (adminCreationError) {
|
|
10043
|
-
console.error(
|
|
10044
|
-
"[AUTH] Clinic admin creation failed:",
|
|
10045
|
-
adminCreationError
|
|
10046
|
-
);
|
|
9881
|
+
console.error("[AUTH] Clinic admin creation failed:", adminCreationError);
|
|
10047
9882
|
throw adminCreationError;
|
|
10048
9883
|
}
|
|
10049
9884
|
try {
|
|
@@ -10051,14 +9886,9 @@ var AuthService = class extends BaseService {
|
|
|
10051
9886
|
user = await this.userService.updateUser(firebaseUser.uid, {
|
|
10052
9887
|
adminProfile: adminProfile.id
|
|
10053
9888
|
});
|
|
10054
|
-
console.log(
|
|
10055
|
-
"[AUTH] User updated with admin profile reference successfully"
|
|
10056
|
-
);
|
|
9889
|
+
console.log("[AUTH] User updated with admin profile reference successfully");
|
|
10057
9890
|
} catch (userUpdateError) {
|
|
10058
|
-
console.error(
|
|
10059
|
-
"[AUTH] Failed to update user with admin profile:",
|
|
10060
|
-
userUpdateError
|
|
10061
|
-
);
|
|
9891
|
+
console.error("[AUTH] Failed to update user with admin profile:", userUpdateError);
|
|
10062
9892
|
throw userUpdateError;
|
|
10063
9893
|
}
|
|
10064
9894
|
const createClinicGroupData = {
|
|
@@ -10096,23 +9926,16 @@ var AuthService = class extends BaseService {
|
|
|
10096
9926
|
clinicGroupId: clinicGroup.id
|
|
10097
9927
|
});
|
|
10098
9928
|
console.log("[AUTH] Admin updated with clinic group ID successfully");
|
|
10099
|
-
adminProfile = await clinicAdminService.getClinicAdmin(
|
|
10100
|
-
adminProfile.id
|
|
10101
|
-
);
|
|
9929
|
+
adminProfile = await clinicAdminService.getClinicAdmin(adminProfile.id);
|
|
10102
9930
|
} catch (groupCreationError) {
|
|
10103
|
-
console.error(
|
|
10104
|
-
"[AUTH] Clinic group creation failed:",
|
|
10105
|
-
groupCreationError
|
|
10106
|
-
);
|
|
9931
|
+
console.error("[AUTH] Clinic group creation failed:", groupCreationError);
|
|
10107
9932
|
throw groupCreationError;
|
|
10108
9933
|
}
|
|
10109
9934
|
} else {
|
|
10110
9935
|
console.log("[AUTH] Joining existing clinic group flow");
|
|
10111
9936
|
if (!data.inviteToken) {
|
|
10112
9937
|
console.error("[AUTH] Invite token is missing");
|
|
10113
|
-
throw new Error(
|
|
10114
|
-
"Invite token is required when joining an existing group"
|
|
10115
|
-
);
|
|
9938
|
+
throw new Error("Invite token is required when joining an existing group");
|
|
10116
9939
|
}
|
|
10117
9940
|
console.log("[AUTH] Invite token provided", {
|
|
10118
9941
|
token: data.inviteToken
|
|
@@ -10123,11 +9946,7 @@ var AuthService = class extends BaseService {
|
|
|
10123
9946
|
const querySnapshot = await (0, import_firestore29.getDocs)(q);
|
|
10124
9947
|
let foundGroup = null;
|
|
10125
9948
|
let foundToken = null;
|
|
10126
|
-
console.log(
|
|
10127
|
-
"[AUTH] Found",
|
|
10128
|
-
querySnapshot.size,
|
|
10129
|
-
"clinic groups to check"
|
|
10130
|
-
);
|
|
9949
|
+
console.log("[AUTH] Found", querySnapshot.size, "clinic groups to check");
|
|
10131
9950
|
for (const docSnapshot of querySnapshot.docs) {
|
|
10132
9951
|
const group = docSnapshot.data();
|
|
10133
9952
|
console.log("[AUTH] Checking group", {
|
|
@@ -10173,17 +9992,12 @@ var AuthService = class extends BaseService {
|
|
|
10173
9992
|
isActive: true
|
|
10174
9993
|
};
|
|
10175
9994
|
try {
|
|
10176
|
-
adminProfile = await clinicAdminService.createClinicAdmin(
|
|
10177
|
-
createClinicAdminData
|
|
10178
|
-
);
|
|
9995
|
+
adminProfile = await clinicAdminService.createClinicAdmin(createClinicAdminData);
|
|
10179
9996
|
console.log("[AUTH] Clinic admin created successfully", {
|
|
10180
9997
|
adminId: adminProfile.id
|
|
10181
9998
|
});
|
|
10182
9999
|
} catch (adminCreationError) {
|
|
10183
|
-
console.error(
|
|
10184
|
-
"[AUTH] Clinic admin creation failed:",
|
|
10185
|
-
adminCreationError
|
|
10186
|
-
);
|
|
10000
|
+
console.error("[AUTH] Clinic admin creation failed:", adminCreationError);
|
|
10187
10001
|
throw adminCreationError;
|
|
10188
10002
|
}
|
|
10189
10003
|
try {
|
|
@@ -10204,9 +10018,7 @@ var AuthService = class extends BaseService {
|
|
|
10204
10018
|
clinicAdminId: (adminProfile == null ? void 0 : adminProfile.id) || "unknown"
|
|
10205
10019
|
});
|
|
10206
10020
|
if (!clinicGroup || !adminProfile) {
|
|
10207
|
-
throw new Error(
|
|
10208
|
-
"Failed to create or retrieve clinic group or admin profile"
|
|
10209
|
-
);
|
|
10021
|
+
throw new Error("Failed to create or retrieve clinic group or admin profile");
|
|
10210
10022
|
}
|
|
10211
10023
|
return {
|
|
10212
10024
|
user,
|
|
@@ -10234,11 +10046,7 @@ var AuthService = class extends BaseService {
|
|
|
10234
10046
|
* Prijavljuje korisnika sa email-om i lozinkom
|
|
10235
10047
|
*/
|
|
10236
10048
|
async signIn(email, password) {
|
|
10237
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(
|
|
10238
|
-
this.auth,
|
|
10239
|
-
email,
|
|
10240
|
-
password
|
|
10241
|
-
);
|
|
10049
|
+
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(this.auth, email, password);
|
|
10242
10050
|
return this.userService.getOrCreateUser(firebaseUser);
|
|
10243
10051
|
}
|
|
10244
10052
|
/**
|
|
@@ -10252,11 +10060,7 @@ var AuthService = class extends BaseService {
|
|
|
10252
10060
|
async signInClinicAdmin(email, password) {
|
|
10253
10061
|
var _a;
|
|
10254
10062
|
try {
|
|
10255
|
-
const clinicAdminService = new ClinicAdminService(
|
|
10256
|
-
this.db,
|
|
10257
|
-
this.auth,
|
|
10258
|
-
this.app
|
|
10259
|
-
);
|
|
10063
|
+
const clinicAdminService = new ClinicAdminService(this.db, this.auth, this.app);
|
|
10260
10064
|
const clinicGroupService = new ClinicGroupService(
|
|
10261
10065
|
this.db,
|
|
10262
10066
|
this.auth,
|
|
@@ -10273,11 +10077,7 @@ var AuthService = class extends BaseService {
|
|
|
10273
10077
|
mediaService
|
|
10274
10078
|
);
|
|
10275
10079
|
clinicAdminService.setServices(clinicGroupService, clinicService);
|
|
10276
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(
|
|
10277
|
-
this.auth,
|
|
10278
|
-
email,
|
|
10279
|
-
password
|
|
10280
|
-
);
|
|
10080
|
+
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(this.auth, email, password);
|
|
10281
10081
|
const user = await this.userService.getOrCreateUser(firebaseUser);
|
|
10282
10082
|
if (!((_a = user.roles) == null ? void 0 : _a.includes("clinic_admin" /* CLINIC_ADMIN */))) {
|
|
10283
10083
|
console.error("[AUTH] User is not a clinic admin:", user.uid);
|
|
@@ -10287,21 +10087,14 @@ var AuthService = class extends BaseService {
|
|
|
10287
10087
|
console.error("[AUTH] User has no admin profile:", user.uid);
|
|
10288
10088
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10289
10089
|
}
|
|
10290
|
-
const adminProfile = await clinicAdminService.getClinicAdmin(
|
|
10291
|
-
user.adminProfile
|
|
10292
|
-
);
|
|
10090
|
+
const adminProfile = await clinicAdminService.getClinicAdmin(user.adminProfile);
|
|
10293
10091
|
if (!adminProfile) {
|
|
10294
10092
|
console.error("[AUTH] Admin profile not found:", user.adminProfile);
|
|
10295
10093
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10296
10094
|
}
|
|
10297
|
-
const clinicGroup = await clinicGroupService.getClinicGroup(
|
|
10298
|
-
adminProfile.clinicGroupId
|
|
10299
|
-
);
|
|
10095
|
+
const clinicGroup = await clinicGroupService.getClinicGroup(adminProfile.clinicGroupId);
|
|
10300
10096
|
if (!clinicGroup) {
|
|
10301
|
-
console.error(
|
|
10302
|
-
"[AUTH] Clinic group not found:",
|
|
10303
|
-
adminProfile.clinicGroupId
|
|
10304
|
-
);
|
|
10097
|
+
console.error("[AUTH] Clinic group not found:", adminProfile.clinicGroupId);
|
|
10305
10098
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10306
10099
|
}
|
|
10307
10100
|
return {
|
|
@@ -10314,39 +10107,6 @@ var AuthService = class extends BaseService {
|
|
|
10314
10107
|
throw error;
|
|
10315
10108
|
}
|
|
10316
10109
|
}
|
|
10317
|
-
/**
|
|
10318
|
-
* Prijavljuje korisnika sa Facebook-om
|
|
10319
|
-
*/
|
|
10320
|
-
async signInWithFacebook() {
|
|
10321
|
-
const provider = new import_auth7.FacebookAuthProvider();
|
|
10322
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithPopup)(this.auth, provider);
|
|
10323
|
-
return this.userService.getOrCreateUser(firebaseUser);
|
|
10324
|
-
}
|
|
10325
|
-
/**
|
|
10326
|
-
* Prijavljuje korisnika sa Google nalogom
|
|
10327
|
-
*/
|
|
10328
|
-
async signInWithGoogle(initialRole = "patient" /* PATIENT */, options) {
|
|
10329
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithPopup)(
|
|
10330
|
-
this.auth,
|
|
10331
|
-
this.googleProvider
|
|
10332
|
-
);
|
|
10333
|
-
const existingUser = await this.userService.getUserByEmail(
|
|
10334
|
-
firebaseUser.email
|
|
10335
|
-
);
|
|
10336
|
-
if (existingUser) {
|
|
10337
|
-
await this.userService.updateUserLoginTimestamp(existingUser.uid);
|
|
10338
|
-
return existingUser;
|
|
10339
|
-
}
|
|
10340
|
-
return this.userService.createUser(firebaseUser, [initialRole], options);
|
|
10341
|
-
}
|
|
10342
|
-
/**
|
|
10343
|
-
* Prijavljuje korisnika sa Apple-om
|
|
10344
|
-
*/
|
|
10345
|
-
async signInWithApple() {
|
|
10346
|
-
const provider = new import_auth7.OAuthProvider("apple.com");
|
|
10347
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithPopup)(this.auth, provider);
|
|
10348
|
-
return this.userService.getOrCreateUser(firebaseUser);
|
|
10349
|
-
}
|
|
10350
10110
|
/**
|
|
10351
10111
|
* Prijavljuje korisnika anonimno
|
|
10352
10112
|
*/
|
|
@@ -10383,18 +10143,11 @@ var AuthService = class extends BaseService {
|
|
|
10383
10143
|
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10384
10144
|
}
|
|
10385
10145
|
if (!currentUser.isAnonymous) {
|
|
10386
|
-
throw new AuthError(
|
|
10387
|
-
"User is not anonymous",
|
|
10388
|
-
"AUTH/NOT_ANONYMOUS_USER",
|
|
10389
|
-
400
|
|
10390
|
-
);
|
|
10146
|
+
throw new AuthError("User is not anonymous", "AUTH/NOT_ANONYMOUS_USER", 400);
|
|
10391
10147
|
}
|
|
10392
10148
|
const credential = import_auth7.EmailAuthProvider.credential(email, password);
|
|
10393
10149
|
await (0, import_auth7.linkWithCredential)(currentUser, credential);
|
|
10394
|
-
return await this.userService.upgradeAnonymousUser(
|
|
10395
|
-
currentUser.uid,
|
|
10396
|
-
email
|
|
10397
|
-
);
|
|
10150
|
+
return await this.userService.upgradeAnonymousUser(currentUser.uid, email);
|
|
10398
10151
|
} catch (error) {
|
|
10399
10152
|
if (error instanceof import_zod23.z.ZodError) {
|
|
10400
10153
|
throw AUTH_ERRORS.VALIDATION_ERROR;
|
|
@@ -10406,109 +10159,6 @@ var AuthService = class extends BaseService {
|
|
|
10406
10159
|
throw error;
|
|
10407
10160
|
}
|
|
10408
10161
|
}
|
|
10409
|
-
/**
|
|
10410
|
-
* Upgrades an anonymous user to a regular user by signing in with a Google account.
|
|
10411
|
-
*
|
|
10412
|
-
* @throws {AuthError} If the user is not anonymous.
|
|
10413
|
-
* @throws {AuthError} If the user is not authenticated.
|
|
10414
|
-
* @throws {AuthError} If the popup window is closed by the user.
|
|
10415
|
-
* @throws {FirebaseError} If any other Firebase error occurs.
|
|
10416
|
-
*
|
|
10417
|
-
* @returns {Promise<User>} The upgraded user.
|
|
10418
|
-
*/
|
|
10419
|
-
async upgradeAnonymousUserWithGoogle() {
|
|
10420
|
-
try {
|
|
10421
|
-
const currentUser = this.auth.currentUser;
|
|
10422
|
-
if (!currentUser) {
|
|
10423
|
-
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10424
|
-
}
|
|
10425
|
-
if (!currentUser.isAnonymous) {
|
|
10426
|
-
throw new AuthError(
|
|
10427
|
-
"User is not anonymous",
|
|
10428
|
-
"AUTH/NOT_ANONYMOUS_USER",
|
|
10429
|
-
400
|
|
10430
|
-
);
|
|
10431
|
-
}
|
|
10432
|
-
const userCredential = await (0, import_auth7.signInWithPopup)(
|
|
10433
|
-
this.auth,
|
|
10434
|
-
this.googleProvider
|
|
10435
|
-
);
|
|
10436
|
-
if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
|
|
10437
|
-
return await this.userService.upgradeAnonymousUser(
|
|
10438
|
-
currentUser.uid,
|
|
10439
|
-
userCredential.user.email
|
|
10440
|
-
);
|
|
10441
|
-
} catch (error) {
|
|
10442
|
-
const firebaseError = error;
|
|
10443
|
-
if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
|
|
10444
|
-
throw AUTH_ERRORS.POPUP_CLOSED;
|
|
10445
|
-
}
|
|
10446
|
-
throw error;
|
|
10447
|
-
}
|
|
10448
|
-
}
|
|
10449
|
-
async upgradeAnonymousUserWithFacebook() {
|
|
10450
|
-
try {
|
|
10451
|
-
const currentUser = this.auth.currentUser;
|
|
10452
|
-
if (!currentUser) {
|
|
10453
|
-
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10454
|
-
}
|
|
10455
|
-
if (!currentUser.isAnonymous) {
|
|
10456
|
-
throw new AuthError(
|
|
10457
|
-
"User is not anonymous",
|
|
10458
|
-
"AUTH/NOT_ANONYMOUS_USER",
|
|
10459
|
-
400
|
|
10460
|
-
);
|
|
10461
|
-
}
|
|
10462
|
-
this.facebookProvider.addScope("email");
|
|
10463
|
-
const userCredential = await (0, import_auth7.signInWithPopup)(
|
|
10464
|
-
this.auth,
|
|
10465
|
-
this.facebookProvider
|
|
10466
|
-
);
|
|
10467
|
-
if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
|
|
10468
|
-
return await this.userService.upgradeAnonymousUser(
|
|
10469
|
-
currentUser.uid,
|
|
10470
|
-
userCredential.user.email
|
|
10471
|
-
);
|
|
10472
|
-
} catch (error) {
|
|
10473
|
-
const firebaseError = error;
|
|
10474
|
-
if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
|
|
10475
|
-
throw AUTH_ERRORS.POPUP_CLOSED;
|
|
10476
|
-
}
|
|
10477
|
-
throw error;
|
|
10478
|
-
}
|
|
10479
|
-
}
|
|
10480
|
-
async upgradeAnonymousUserWithApple() {
|
|
10481
|
-
try {
|
|
10482
|
-
const currentUser = this.auth.currentUser;
|
|
10483
|
-
if (!currentUser) {
|
|
10484
|
-
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10485
|
-
}
|
|
10486
|
-
if (!currentUser.isAnonymous) {
|
|
10487
|
-
throw new AuthError(
|
|
10488
|
-
"User is not anonymous",
|
|
10489
|
-
"AUTH/NOT_ANONYMOUS_USER",
|
|
10490
|
-
400
|
|
10491
|
-
);
|
|
10492
|
-
}
|
|
10493
|
-
this.appleProvider.addScope("email");
|
|
10494
|
-
this.appleProvider.addScope("name");
|
|
10495
|
-
const userCredential = await (0, import_auth7.signInWithPopup)(
|
|
10496
|
-
this.auth,
|
|
10497
|
-
this.appleProvider
|
|
10498
|
-
);
|
|
10499
|
-
if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
|
|
10500
|
-
return await this.userService.upgradeAnonymousUser(
|
|
10501
|
-
currentUser.uid,
|
|
10502
|
-
userCredential.user.email
|
|
10503
|
-
);
|
|
10504
|
-
} catch (error) {
|
|
10505
|
-
const firebaseError = error;
|
|
10506
|
-
if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
|
|
10507
|
-
throw AUTH_ERRORS.POPUP_CLOSED;
|
|
10508
|
-
}
|
|
10509
|
-
throw error;
|
|
10510
|
-
}
|
|
10511
|
-
}
|
|
10512
10162
|
/**
|
|
10513
10163
|
* Šalje email za resetovanje lozinke korisniku
|
|
10514
10164
|
* @param email Email adresa korisnika
|
|
@@ -10589,11 +10239,7 @@ var AuthService = class extends BaseService {
|
|
|
10589
10239
|
await this.validateSignupData(data);
|
|
10590
10240
|
console.log("[AUTH] Creating Firebase user");
|
|
10591
10241
|
try {
|
|
10592
|
-
const result = await (0, import_auth7.createUserWithEmailAndPassword)(
|
|
10593
|
-
this.auth,
|
|
10594
|
-
data.email,
|
|
10595
|
-
data.password
|
|
10596
|
-
);
|
|
10242
|
+
const result = await (0, import_auth7.createUserWithEmailAndPassword)(this.auth, data.email, data.password);
|
|
10597
10243
|
firebaseUser = result.user;
|
|
10598
10244
|
console.log("[AUTH] Firebase user created successfully", {
|
|
10599
10245
|
uid: firebaseUser.uid
|
|
@@ -10603,64 +10249,43 @@ var AuthService = class extends BaseService {
|
|
|
10603
10249
|
throw handleFirebaseError(firebaseError);
|
|
10604
10250
|
}
|
|
10605
10251
|
console.log("[AUTH] Starting Firestore transaction");
|
|
10606
|
-
const transactionResult = await (0, import_firestore29.runTransaction)(
|
|
10607
|
-
|
|
10608
|
-
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
firebaseUser,
|
|
10620
|
-
["practitioner" /* PRACTITIONER */],
|
|
10621
|
-
{ skipProfileCreation: true }
|
|
10252
|
+
const transactionResult = await (0, import_firestore29.runTransaction)(this.db, async (transaction) => {
|
|
10253
|
+
console.log("[AUTH] Transaction started - creating user and practitioner");
|
|
10254
|
+
const practitionerService = new PractitionerService(this.db, this.auth, this.app);
|
|
10255
|
+
console.log("[AUTH] Creating user document");
|
|
10256
|
+
const user = await this.userService.createUser(firebaseUser, ["practitioner" /* PRACTITIONER */], {
|
|
10257
|
+
skipProfileCreation: true
|
|
10258
|
+
});
|
|
10259
|
+
let practitioner;
|
|
10260
|
+
if (data.token) {
|
|
10261
|
+
console.log("[AUTH] Claiming existing practitioner profile with token");
|
|
10262
|
+
const claimedPractitioner = await practitionerService.validateTokenAndClaimProfile(
|
|
10263
|
+
data.token,
|
|
10264
|
+
firebaseUser.uid
|
|
10622
10265
|
);
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
console.log(
|
|
10626
|
-
"[AUTH] Claiming existing practitioner profile with token"
|
|
10627
|
-
);
|
|
10628
|
-
const claimedPractitioner = await practitionerService.validateTokenAndClaimProfile(
|
|
10629
|
-
data.token,
|
|
10630
|
-
firebaseUser.uid
|
|
10631
|
-
);
|
|
10632
|
-
if (!claimedPractitioner) {
|
|
10633
|
-
throw new Error("Invalid or expired invitation token");
|
|
10634
|
-
}
|
|
10635
|
-
practitioner = claimedPractitioner;
|
|
10636
|
-
} else {
|
|
10637
|
-
console.log("[AUTH] Creating new practitioner profile");
|
|
10638
|
-
const practitionerData = buildPractitionerData(
|
|
10639
|
-
data,
|
|
10640
|
-
firebaseUser.uid
|
|
10641
|
-
);
|
|
10642
|
-
practitioner = await practitionerService.createPractitioner(
|
|
10643
|
-
practitionerData
|
|
10644
|
-
);
|
|
10266
|
+
if (!claimedPractitioner) {
|
|
10267
|
+
throw new Error("Invalid or expired invitation token");
|
|
10645
10268
|
}
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
10651
|
-
return { user, practitioner };
|
|
10269
|
+
practitioner = claimedPractitioner;
|
|
10270
|
+
} else {
|
|
10271
|
+
console.log("[AUTH] Creating new practitioner profile");
|
|
10272
|
+
const practitionerData = buildPractitionerData(data, firebaseUser.uid);
|
|
10273
|
+
practitioner = await practitionerService.createPractitioner(practitionerData);
|
|
10652
10274
|
}
|
|
10653
|
-
|
|
10275
|
+
console.log("[AUTH] Linking practitioner to user");
|
|
10276
|
+
await this.userService.updateUser(firebaseUser.uid, {
|
|
10277
|
+
practitionerProfile: practitioner.id
|
|
10278
|
+
});
|
|
10279
|
+
console.log("[AUTH] Transaction operations completed successfully");
|
|
10280
|
+
return { user, practitioner };
|
|
10281
|
+
});
|
|
10654
10282
|
console.log("[AUTH] Atomic practitioner signup completed successfully", {
|
|
10655
10283
|
userId: transactionResult.user.uid,
|
|
10656
10284
|
practitionerId: transactionResult.practitioner.id
|
|
10657
10285
|
});
|
|
10658
10286
|
return transactionResult;
|
|
10659
10287
|
} catch (error) {
|
|
10660
|
-
console.error(
|
|
10661
|
-
"[AUTH] Atomic signup failed, initiating cleanup...",
|
|
10662
|
-
error
|
|
10663
|
-
);
|
|
10288
|
+
console.error("[AUTH] Atomic signup failed, initiating cleanup...", error);
|
|
10664
10289
|
if (firebaseUser) {
|
|
10665
10290
|
await cleanupFirebaseUser(firebaseUser);
|
|
10666
10291
|
}
|
|
@@ -10683,14 +10308,8 @@ var AuthService = class extends BaseService {
|
|
|
10683
10308
|
}
|
|
10684
10309
|
console.log("[AUTH] Email availability confirmed");
|
|
10685
10310
|
if (data.token) {
|
|
10686
|
-
const practitionerService = new PractitionerService(
|
|
10687
|
-
|
|
10688
|
-
this.auth,
|
|
10689
|
-
this.app
|
|
10690
|
-
);
|
|
10691
|
-
const isValidToken = await practitionerService.validateToken(
|
|
10692
|
-
data.token
|
|
10693
|
-
);
|
|
10311
|
+
const practitionerService = new PractitionerService(this.db, this.auth, this.app);
|
|
10312
|
+
const isValidToken = await practitionerService.validateToken(data.token);
|
|
10694
10313
|
if (!isValidToken) {
|
|
10695
10314
|
console.log("[AUTH] Invalid token provided:", data.token);
|
|
10696
10315
|
throw new Error("Invalid or expired invitation token");
|
|
@@ -10721,16 +10340,8 @@ var AuthService = class extends BaseService {
|
|
|
10721
10340
|
console.log("[AUTH] Starting practitioner signin process", {
|
|
10722
10341
|
email
|
|
10723
10342
|
});
|
|
10724
|
-
const practitionerService = new PractitionerService(
|
|
10725
|
-
|
|
10726
|
-
this.auth,
|
|
10727
|
-
this.app
|
|
10728
|
-
);
|
|
10729
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(
|
|
10730
|
-
this.auth,
|
|
10731
|
-
email,
|
|
10732
|
-
password
|
|
10733
|
-
);
|
|
10343
|
+
const practitionerService = new PractitionerService(this.db, this.auth, this.app);
|
|
10344
|
+
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(this.auth, email, password);
|
|
10734
10345
|
const user = await this.userService.getOrCreateUser(firebaseUser);
|
|
10735
10346
|
console.log("[AUTH] User retrieved", { uid: user.uid });
|
|
10736
10347
|
if (!((_a = user.roles) == null ? void 0 : _a.includes("practitioner" /* PRACTITIONER */))) {
|
|
@@ -10741,14 +10352,9 @@ var AuthService = class extends BaseService {
|
|
|
10741
10352
|
console.error("[AUTH] User has no practitioner profile:", user.uid);
|
|
10742
10353
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10743
10354
|
}
|
|
10744
|
-
const practitioner = await practitionerService.getPractitioner(
|
|
10745
|
-
user.practitionerProfile
|
|
10746
|
-
);
|
|
10355
|
+
const practitioner = await practitionerService.getPractitioner(user.practitionerProfile);
|
|
10747
10356
|
if (!practitioner) {
|
|
10748
|
-
console.error(
|
|
10749
|
-
"[AUTH] Practitioner profile not found:",
|
|
10750
|
-
user.practitionerProfile
|
|
10751
|
-
);
|
|
10357
|
+
console.error("[AUTH] Practitioner profile not found:", user.practitionerProfile);
|
|
10752
10358
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10753
10359
|
}
|
|
10754
10360
|
console.log("[AUTH] Practitioner signin completed successfully", {
|
|
@@ -10764,6 +10370,194 @@ var AuthService = class extends BaseService {
|
|
|
10764
10370
|
throw error;
|
|
10765
10371
|
}
|
|
10766
10372
|
}
|
|
10373
|
+
/**
|
|
10374
|
+
* Signs in a user with a Google ID token from a mobile client.
|
|
10375
|
+
* If the user does not exist, a new user is created.
|
|
10376
|
+
* @param idToken - The Google ID token obtained from the mobile app.
|
|
10377
|
+
* @param initialRole - The role to assign to the user if they are being created.
|
|
10378
|
+
* @returns The signed-in or newly created user.
|
|
10379
|
+
*/
|
|
10380
|
+
async signInWithGoogleIdToken(idToken, initialRole = "patient" /* PATIENT */) {
|
|
10381
|
+
try {
|
|
10382
|
+
console.log("[AUTH] Signing in with Google ID Token");
|
|
10383
|
+
const credential = import_auth7.GoogleAuthProvider.credential(idToken);
|
|
10384
|
+
const { user: firebaseUser } = await (0, import_auth7.signInWithCredential)(this.auth, credential);
|
|
10385
|
+
console.log("[AUTH] Firebase user signed in:", firebaseUser.uid);
|
|
10386
|
+
const existingUser = await this.userService.getUserById(firebaseUser.uid);
|
|
10387
|
+
if (existingUser) {
|
|
10388
|
+
console.log("[AUTH] Existing user found, returning profile:", existingUser.uid);
|
|
10389
|
+
return existingUser;
|
|
10390
|
+
}
|
|
10391
|
+
console.log("[AUTH] No existing user found, creating new profile for:", firebaseUser.uid);
|
|
10392
|
+
return this.userService.createUser(firebaseUser, [initialRole]);
|
|
10393
|
+
} catch (error) {
|
|
10394
|
+
console.error("[AUTH] Error in signInWithGoogleIdToken:", error);
|
|
10395
|
+
throw handleFirebaseError(error);
|
|
10396
|
+
}
|
|
10397
|
+
}
|
|
10398
|
+
/**
|
|
10399
|
+
* Signs in a user with a Google authorization code from a mobile client.
|
|
10400
|
+
* This method directly exchanges the authorization code for tokens using Google's OAuth2 API
|
|
10401
|
+
* with the platform-specific client ID (no client secret needed for native apps).
|
|
10402
|
+
* @param authorizationCode - The Google authorization code obtained from the mobile app.
|
|
10403
|
+
* @param redirectUri - The redirect URI used in the OAuth flow.
|
|
10404
|
+
* @param platform - The platform (ios/android) to determine which client ID to use.
|
|
10405
|
+
* @param initialRole - The role to assign to the user if they are being created.
|
|
10406
|
+
* @returns The signed-in or newly created user.
|
|
10407
|
+
*/
|
|
10408
|
+
async signInWithGoogleAuthCode(authorizationCode, redirectUri, platform, initialRole = "patient" /* PATIENT */) {
|
|
10409
|
+
try {
|
|
10410
|
+
console.log("[AUTH] Signing in with Google authorization code (native flow)");
|
|
10411
|
+
console.log("[AUTH] Platform:", platform);
|
|
10412
|
+
console.log("[AUTH] Redirect URI:", redirectUri);
|
|
10413
|
+
console.log("[AUTH] Code length:", authorizationCode.length);
|
|
10414
|
+
const clientId = platform === "ios" ? process.env.GOOGLE_IOS_CLIENT_ID : process.env.GOOGLE_ANDROID_CLIENT_ID;
|
|
10415
|
+
if (!clientId) {
|
|
10416
|
+
throw new Error(
|
|
10417
|
+
`Missing Google ${platform.toUpperCase()} client ID in environment variables`
|
|
10418
|
+
);
|
|
10419
|
+
}
|
|
10420
|
+
console.log("[AUTH] Using client ID:", `${clientId.substring(0, 20)}...`);
|
|
10421
|
+
const tokenEndpoint = "https://oauth2.googleapis.com/token";
|
|
10422
|
+
const params = new URLSearchParams({
|
|
10423
|
+
client_id: clientId,
|
|
10424
|
+
code: authorizationCode,
|
|
10425
|
+
grant_type: "authorization_code",
|
|
10426
|
+
redirect_uri: redirectUri
|
|
10427
|
+
// For native apps, we don't include client_secret
|
|
10428
|
+
});
|
|
10429
|
+
console.log("[AUTH] Making request to Google token endpoint...");
|
|
10430
|
+
const response = await fetch(tokenEndpoint, {
|
|
10431
|
+
method: "POST",
|
|
10432
|
+
headers: {
|
|
10433
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
10434
|
+
},
|
|
10435
|
+
body: params.toString()
|
|
10436
|
+
});
|
|
10437
|
+
if (!response.ok) {
|
|
10438
|
+
const errorText = await response.text();
|
|
10439
|
+
console.error("[AUTH] Google token exchange failed:", {
|
|
10440
|
+
status: response.status,
|
|
10441
|
+
statusText: response.statusText,
|
|
10442
|
+
error: errorText
|
|
10443
|
+
});
|
|
10444
|
+
throw new Error(`Google token exchange failed: ${response.status} - ${errorText}`);
|
|
10445
|
+
}
|
|
10446
|
+
const tokenData = await response.json();
|
|
10447
|
+
console.log("[AUTH] Token exchange response received:", {
|
|
10448
|
+
hasIdToken: !!tokenData.id_token,
|
|
10449
|
+
hasAccessToken: !!tokenData.access_token,
|
|
10450
|
+
hasRefreshToken: !!tokenData.refresh_token
|
|
10451
|
+
});
|
|
10452
|
+
if (!tokenData.id_token) {
|
|
10453
|
+
console.error("[AUTH] No ID token in response:", tokenData);
|
|
10454
|
+
throw new Error("No ID token received from Google token exchange");
|
|
10455
|
+
}
|
|
10456
|
+
console.log("[AUTH] Successfully obtained ID token from Google");
|
|
10457
|
+
console.log("[AUTH] ID token length:", tokenData.id_token.length);
|
|
10458
|
+
return await this.signInWithGoogleIdToken(tokenData.id_token, initialRole);
|
|
10459
|
+
} catch (error) {
|
|
10460
|
+
console.error("[AUTH] Error in signInWithGoogleAuthCode:", error);
|
|
10461
|
+
throw handleFirebaseError(error);
|
|
10462
|
+
}
|
|
10463
|
+
}
|
|
10464
|
+
/**
|
|
10465
|
+
* Links a Google account to the currently signed-in user using an ID token.
|
|
10466
|
+
* This is used to upgrade an anonymous user or to allow an existing user
|
|
10467
|
+
* to sign in with Google in the future.
|
|
10468
|
+
* @param idToken - The Google ID token obtained from the mobile app.
|
|
10469
|
+
* @returns The updated user profile.
|
|
10470
|
+
*/
|
|
10471
|
+
async linkGoogleAccount(idToken) {
|
|
10472
|
+
try {
|
|
10473
|
+
console.log("[AUTH] Linking Google account with ID Token");
|
|
10474
|
+
const currentUser = this.auth.currentUser;
|
|
10475
|
+
if (!currentUser) {
|
|
10476
|
+
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10477
|
+
}
|
|
10478
|
+
const wasAnonymous = currentUser.isAnonymous;
|
|
10479
|
+
console.log(`[AUTH] Current user is ${wasAnonymous ? "anonymous" : "not anonymous"}`);
|
|
10480
|
+
const credential = import_auth7.GoogleAuthProvider.credential(idToken);
|
|
10481
|
+
const userCredential = await (0, import_auth7.linkWithCredential)(currentUser, credential);
|
|
10482
|
+
const linkedFirebaseUser = userCredential.user;
|
|
10483
|
+
console.log("[AUTH] Google account linked successfully to user:", linkedFirebaseUser.uid);
|
|
10484
|
+
if (wasAnonymous) {
|
|
10485
|
+
console.log("[AUTH] Upgrading anonymous user profile");
|
|
10486
|
+
return await this.userService.upgradeAnonymousUser(
|
|
10487
|
+
linkedFirebaseUser.uid,
|
|
10488
|
+
linkedFirebaseUser.email
|
|
10489
|
+
);
|
|
10490
|
+
}
|
|
10491
|
+
return await this.userService.getUserById(linkedFirebaseUser.uid);
|
|
10492
|
+
} catch (error) {
|
|
10493
|
+
console.error("[AUTH] Error in linkGoogleAccount:", error);
|
|
10494
|
+
throw handleFirebaseError(error);
|
|
10495
|
+
}
|
|
10496
|
+
}
|
|
10497
|
+
/**
|
|
10498
|
+
* Links a Google account to the currently signed-in user using an authorization code.
|
|
10499
|
+
* This method directly exchanges the authorization code for tokens using Google's OAuth2 API
|
|
10500
|
+
* with the platform-specific client ID, then links the Google account to the current user.
|
|
10501
|
+
* @param authorizationCode - The Google authorization code obtained from the mobile app.
|
|
10502
|
+
* @param redirectUri - The redirect URI used in the OAuth flow.
|
|
10503
|
+
* @param platform - The platform (ios/android) to determine which client ID to use.
|
|
10504
|
+
* @returns The updated user profile.
|
|
10505
|
+
*/
|
|
10506
|
+
async linkGoogleAccountWithAuthCode(authorizationCode, redirectUri, platform) {
|
|
10507
|
+
try {
|
|
10508
|
+
console.log("[AUTH] Linking Google account with authorization code (native flow)");
|
|
10509
|
+
console.log("[AUTH] Platform:", platform);
|
|
10510
|
+
console.log("[AUTH] Redirect URI:", redirectUri);
|
|
10511
|
+
console.log("[AUTH] Code length:", authorizationCode.length);
|
|
10512
|
+
const clientId = platform === "ios" ? process.env.GOOGLE_IOS_CLIENT_ID : process.env.GOOGLE_ANDROID_CLIENT_ID;
|
|
10513
|
+
if (!clientId) {
|
|
10514
|
+
throw new Error(
|
|
10515
|
+
`Missing Google ${platform.toUpperCase()} client ID in environment variables`
|
|
10516
|
+
);
|
|
10517
|
+
}
|
|
10518
|
+
console.log("[AUTH] Using client ID:", `${clientId.substring(0, 20)}...`);
|
|
10519
|
+
const tokenEndpoint = "https://oauth2.googleapis.com/token";
|
|
10520
|
+
const params = new URLSearchParams({
|
|
10521
|
+
client_id: clientId,
|
|
10522
|
+
code: authorizationCode,
|
|
10523
|
+
grant_type: "authorization_code",
|
|
10524
|
+
redirect_uri: redirectUri
|
|
10525
|
+
});
|
|
10526
|
+
console.log("[AUTH] Making request to Google token endpoint...");
|
|
10527
|
+
const response = await fetch(tokenEndpoint, {
|
|
10528
|
+
method: "POST",
|
|
10529
|
+
headers: {
|
|
10530
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
10531
|
+
},
|
|
10532
|
+
body: params.toString()
|
|
10533
|
+
});
|
|
10534
|
+
if (!response.ok) {
|
|
10535
|
+
const errorText = await response.text();
|
|
10536
|
+
console.error("[AUTH] Google token exchange failed:", {
|
|
10537
|
+
status: response.status,
|
|
10538
|
+
statusText: response.statusText,
|
|
10539
|
+
error: errorText
|
|
10540
|
+
});
|
|
10541
|
+
throw new Error(`Google token exchange failed: ${response.status} - ${errorText}`);
|
|
10542
|
+
}
|
|
10543
|
+
const tokenData = await response.json();
|
|
10544
|
+
console.log("[AUTH] Token exchange response received:", {
|
|
10545
|
+
hasIdToken: !!tokenData.id_token,
|
|
10546
|
+
hasAccessToken: !!tokenData.access_token,
|
|
10547
|
+
hasRefreshToken: !!tokenData.refresh_token
|
|
10548
|
+
});
|
|
10549
|
+
if (!tokenData.id_token) {
|
|
10550
|
+
console.error("[AUTH] No ID token in response:", tokenData);
|
|
10551
|
+
throw new Error("No ID token received from Google token exchange");
|
|
10552
|
+
}
|
|
10553
|
+
console.log("[AUTH] Successfully obtained ID token from Google");
|
|
10554
|
+
console.log("[AUTH] ID token length:", tokenData.id_token.length);
|
|
10555
|
+
return await this.linkGoogleAccount(tokenData.id_token);
|
|
10556
|
+
} catch (error) {
|
|
10557
|
+
console.error("[AUTH] Error in linkGoogleAccountWithAuthCode:", error);
|
|
10558
|
+
throw handleFirebaseError(error);
|
|
10559
|
+
}
|
|
10560
|
+
}
|
|
10767
10561
|
};
|
|
10768
10562
|
|
|
10769
10563
|
// src/services/calendar/calendar.v2.service.ts
|