@blackcode_sa/metaestetics-api 1.12.13 → 1.12.15

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/index.d.mts CHANGED
@@ -6055,7 +6055,7 @@ declare class UserService extends BaseService {
6055
6055
  */
6056
6056
  updateUserLoginTimestamp(uid: string): Promise<User>;
6057
6057
  upgradeAnonymousUser(uid: string, email: string): Promise<User>;
6058
- updateUser(uid: string, updates: Partial<Omit<User, "uid">>): Promise<User>;
6058
+ updateUser(uid: string, updates: Partial<Omit<User, 'uid'>>): Promise<User>;
6059
6059
  /**
6060
6060
  * Dodaje novu rolu korisniku
6061
6061
  */
@@ -6185,10 +6185,11 @@ declare class AuthService extends BaseService {
6185
6185
  }>;
6186
6186
  /**
6187
6187
  * Signs in a user with a Google ID token from a mobile client.
6188
- * If the user does not exist, a new user is created.
6188
+ * If the user does not exist in our database, the login is rejected.
6189
6189
  * @param idToken - The Google ID token obtained from the mobile app.
6190
- * @param initialRole - The role to assign to the user if they are being created.
6191
- * @returns The signed-in or newly created user.
6190
+ * @param initialRole - The role to assign to the user (currently unused).
6191
+ * @returns The signed-in user if they exist in our database.
6192
+ * @throws AuthError if no user profile is found.
6192
6193
  */
6193
6194
  signInWithGoogleIdToken(idToken: string, initialRole?: UserRole): Promise<User>;
6194
6195
  /**
package/dist/index.d.ts CHANGED
@@ -6055,7 +6055,7 @@ declare class UserService extends BaseService {
6055
6055
  */
6056
6056
  updateUserLoginTimestamp(uid: string): Promise<User>;
6057
6057
  upgradeAnonymousUser(uid: string, email: string): Promise<User>;
6058
- updateUser(uid: string, updates: Partial<Omit<User, "uid">>): Promise<User>;
6058
+ updateUser(uid: string, updates: Partial<Omit<User, 'uid'>>): Promise<User>;
6059
6059
  /**
6060
6060
  * Dodaje novu rolu korisniku
6061
6061
  */
@@ -6185,10 +6185,11 @@ declare class AuthService extends BaseService {
6185
6185
  }>;
6186
6186
  /**
6187
6187
  * Signs in a user with a Google ID token from a mobile client.
6188
- * If the user does not exist, a new user is created.
6188
+ * If the user does not exist in our database, the login is rejected.
6189
6189
  * @param idToken - The Google ID token obtained from the mobile app.
6190
- * @param initialRole - The role to assign to the user if they are being created.
6191
- * @returns The signed-in or newly created user.
6190
+ * @param initialRole - The role to assign to the user (currently unused).
6191
+ * @returns The signed-in user if they exist in our database.
6192
+ * @throws AuthError if no user profile is found.
6192
6193
  */
6193
6194
  signInWithGoogleIdToken(idToken: string, initialRole?: UserRole): Promise<User>;
6194
6195
  /**
package/dist/index.js CHANGED
@@ -7368,11 +7368,7 @@ var UserService = class extends BaseService {
7368
7368
  if (options == null ? void 0 : options.skipProfileCreation) {
7369
7369
  return this.getUserById(userData.uid);
7370
7370
  }
7371
- const profiles = await this.createProfilesForRoles(
7372
- userData.uid,
7373
- roles,
7374
- options
7375
- );
7371
+ const profiles = await this.createProfilesForRoles(userData.uid, roles, options);
7376
7372
  await (0, import_firestore22.updateDoc)((0, import_firestore22.doc)(this.db, USERS_COLLECTION, userData.uid), profiles);
7377
7373
  return this.getUserById(userData.uid);
7378
7374
  }
@@ -7399,19 +7395,13 @@ var UserService = class extends BaseService {
7399
7395
  case "patient" /* PATIENT */:
7400
7396
  if (options == null ? void 0 : options.patientInviteToken) {
7401
7397
  const patientService = this.getPatientService();
7402
- const token = await patientService.validatePatientToken(
7403
- options.patientInviteToken
7404
- );
7398
+ const token = await patientService.validatePatientToken(options.patientInviteToken);
7405
7399
  if (!token) {
7406
7400
  throw new Error("Invalid or expired patient invitation token.");
7407
7401
  }
7408
- const patientProfile2 = await patientService.getPatientProfile(
7409
- token.patientId
7410
- );
7402
+ const patientProfile2 = await patientService.getPatientProfile(token.patientId);
7411
7403
  if (!patientProfile2 || !patientProfile2.isManual) {
7412
- throw new Error(
7413
- "Patient profile not found or has already been claimed."
7414
- );
7404
+ throw new Error("Patient profile not found or has already been claimed.");
7415
7405
  }
7416
7406
  if ((await this.getUserById(userId)).patientProfile || patientProfile2.userRef) {
7417
7407
  throw new Error("User already has a patient profile.");
@@ -7420,11 +7410,7 @@ var UserService = class extends BaseService {
7420
7410
  userRef: userId,
7421
7411
  isManual: false
7422
7412
  });
7423
- await patientService.markPatientTokenAsUsed(
7424
- token.id,
7425
- token.patientId,
7426
- userId
7427
- );
7413
+ await patientService.markPatientTokenAsUsed(token.id, token.patientId, userId);
7428
7414
  profiles.patientProfile = patientProfile2.id;
7429
7415
  break;
7430
7416
  }
@@ -7528,9 +7514,7 @@ var UserService = class extends BaseService {
7528
7514
  return userSchema.parse(userData);
7529
7515
  }
7530
7516
  async getUsersByRole(role) {
7531
- const constraints = [
7532
- (0, import_firestore22.where)("roles", "array-contains", role)
7533
- ];
7517
+ const constraints = [(0, import_firestore22.where)("roles", "array-contains", role)];
7534
7518
  const q = (0, import_firestore22.query)((0, import_firestore22.collection)(this.db, USERS_COLLECTION), ...constraints);
7535
7519
  const querySnapshot = await (0, import_firestore22.getDocs)(q);
7536
7520
  const users = querySnapshot.docs.map((doc38) => doc38.data());
@@ -7612,23 +7596,17 @@ var UserService = class extends BaseService {
7612
7596
  switch (role) {
7613
7597
  case "patient" /* PATIENT */:
7614
7598
  if (user.patientProfile) {
7615
- await this.getPatientService().deletePatientProfile(
7616
- user.patientProfile
7617
- );
7599
+ await this.getPatientService().deletePatientProfile(user.patientProfile);
7618
7600
  }
7619
7601
  break;
7620
7602
  case "clinic_admin" /* CLINIC_ADMIN */:
7621
7603
  if (user.adminProfile) {
7622
- await this.getClinicAdminService().deleteClinicAdmin(
7623
- user.adminProfile
7624
- );
7604
+ await this.getClinicAdminService().deleteClinicAdmin(user.adminProfile);
7625
7605
  }
7626
7606
  break;
7627
7607
  case "practitioner" /* PRACTITIONER */:
7628
7608
  if (user.practitionerProfile) {
7629
- await this.getPractitionerService().deletePractitioner(
7630
- user.practitionerProfile
7631
- );
7609
+ await this.getPractitionerService().deletePractitioner(user.practitionerProfile);
7632
7610
  }
7633
7611
  break;
7634
7612
  }
@@ -7647,19 +7625,13 @@ var UserService = class extends BaseService {
7647
7625
  const userData = userDoc.data();
7648
7626
  try {
7649
7627
  if (userData.patientProfile) {
7650
- await this.getPatientService().deletePatientProfile(
7651
- userData.patientProfile
7652
- );
7628
+ await this.getPatientService().deletePatientProfile(userData.patientProfile);
7653
7629
  }
7654
7630
  if (userData.practitionerProfile) {
7655
- await this.getPractitionerService().deletePractitioner(
7656
- userData.practitionerProfile
7657
- );
7631
+ await this.getPractitionerService().deletePractitioner(userData.practitionerProfile);
7658
7632
  }
7659
7633
  if (userData.adminProfile) {
7660
- await this.getClinicAdminService().deleteClinicAdmin(
7661
- userData.adminProfile
7662
- );
7634
+ await this.getClinicAdminService().deleteClinicAdmin(userData.adminProfile);
7663
7635
  }
7664
7636
  await (0, import_firestore22.deleteDoc)(userRef);
7665
7637
  } catch (error) {
@@ -10372,15 +10344,37 @@ var AuthService = class extends BaseService {
10372
10344
  }
10373
10345
  /**
10374
10346
  * 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.
10347
+ * If the user does not exist in our database, the login is rejected.
10376
10348
  * @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.
10349
+ * @param initialRole - The role to assign to the user (currently unused).
10350
+ * @returns The signed-in user if they exist in our database.
10351
+ * @throws AuthError if no user profile is found.
10379
10352
  */
10380
10353
  async signInWithGoogleIdToken(idToken, initialRole = "patient" /* PATIENT */) {
10381
10354
  try {
10382
10355
  console.log("[AUTH] Signing in with Google ID Token");
10383
10356
  const credential = import_auth7.GoogleAuthProvider.credential(idToken);
10357
+ const decodedToken = JSON.parse(atob(idToken.split(".")[1]));
10358
+ const userEmail = decodedToken.email;
10359
+ const userUid = decodedToken.sub;
10360
+ console.log("[AUTH] Checking if Firebase Auth user exists with email:", userEmail);
10361
+ let existingAuthUser;
10362
+ try {
10363
+ const admin = await import("firebase-admin");
10364
+ existingAuthUser = await admin.auth().getUserByEmail(userEmail);
10365
+ console.log("[AUTH] Firebase Auth user found:", existingAuthUser.uid);
10366
+ } catch (authError) {
10367
+ if (authError.code === "auth/user-not-found") {
10368
+ console.log("[AUTH] No Firebase Auth user found for email:", userEmail);
10369
+ throw new AuthError(
10370
+ 'No account found. Please complete registration by starting with "Get Started".',
10371
+ "AUTH/USER_NOT_FOUND",
10372
+ 404
10373
+ );
10374
+ }
10375
+ throw authError;
10376
+ }
10377
+ console.log("[AUTH] Firebase Auth user exists, proceeding with sign-in");
10384
10378
  const { user: firebaseUser } = await (0, import_auth7.signInWithCredential)(this.auth, credential);
10385
10379
  console.log("[AUTH] Firebase user signed in:", firebaseUser.uid);
10386
10380
  const existingUser = await this.userService.getUserById(firebaseUser.uid);
@@ -10388,12 +10382,11 @@ var AuthService = class extends BaseService {
10388
10382
  console.log("[AUTH] Existing user found, returning profile:", existingUser.uid);
10389
10383
  return existingUser;
10390
10384
  }
10391
- console.log("[AUTH] No existing user found for Google account:", firebaseUser.email);
10392
10385
  await (0, import_auth7.signOut)(this.auth);
10393
10386
  throw new AuthError(
10394
- 'No account found. Please complete registration by starting with "Get Started".',
10395
- "AUTH/USER_NOT_FOUND",
10396
- 404
10387
+ "Account found but registration incomplete. Please complete registration.",
10388
+ "AUTH/INCOMPLETE_REGISTRATION",
10389
+ 400
10397
10390
  );
10398
10391
  } catch (error) {
10399
10392
  console.error("[AUTH] Error in signInWithGoogleIdToken:", error);
package/dist/index.mjs CHANGED
@@ -7408,11 +7408,7 @@ var UserService = class extends BaseService {
7408
7408
  if (options == null ? void 0 : options.skipProfileCreation) {
7409
7409
  return this.getUserById(userData.uid);
7410
7410
  }
7411
- const profiles = await this.createProfilesForRoles(
7412
- userData.uid,
7413
- roles,
7414
- options
7415
- );
7411
+ const profiles = await this.createProfilesForRoles(userData.uid, roles, options);
7416
7412
  await updateDoc12(doc12(this.db, USERS_COLLECTION, userData.uid), profiles);
7417
7413
  return this.getUserById(userData.uid);
7418
7414
  }
@@ -7439,19 +7435,13 @@ var UserService = class extends BaseService {
7439
7435
  case "patient" /* PATIENT */:
7440
7436
  if (options == null ? void 0 : options.patientInviteToken) {
7441
7437
  const patientService = this.getPatientService();
7442
- const token = await patientService.validatePatientToken(
7443
- options.patientInviteToken
7444
- );
7438
+ const token = await patientService.validatePatientToken(options.patientInviteToken);
7445
7439
  if (!token) {
7446
7440
  throw new Error("Invalid or expired patient invitation token.");
7447
7441
  }
7448
- const patientProfile2 = await patientService.getPatientProfile(
7449
- token.patientId
7450
- );
7442
+ const patientProfile2 = await patientService.getPatientProfile(token.patientId);
7451
7443
  if (!patientProfile2 || !patientProfile2.isManual) {
7452
- throw new Error(
7453
- "Patient profile not found or has already been claimed."
7454
- );
7444
+ throw new Error("Patient profile not found or has already been claimed.");
7455
7445
  }
7456
7446
  if ((await this.getUserById(userId)).patientProfile || patientProfile2.userRef) {
7457
7447
  throw new Error("User already has a patient profile.");
@@ -7460,11 +7450,7 @@ var UserService = class extends BaseService {
7460
7450
  userRef: userId,
7461
7451
  isManual: false
7462
7452
  });
7463
- await patientService.markPatientTokenAsUsed(
7464
- token.id,
7465
- token.patientId,
7466
- userId
7467
- );
7453
+ await patientService.markPatientTokenAsUsed(token.id, token.patientId, userId);
7468
7454
  profiles.patientProfile = patientProfile2.id;
7469
7455
  break;
7470
7456
  }
@@ -7568,9 +7554,7 @@ var UserService = class extends BaseService {
7568
7554
  return userSchema.parse(userData);
7569
7555
  }
7570
7556
  async getUsersByRole(role) {
7571
- const constraints = [
7572
- where11("roles", "array-contains", role)
7573
- ];
7557
+ const constraints = [where11("roles", "array-contains", role)];
7574
7558
  const q = query11(collection11(this.db, USERS_COLLECTION), ...constraints);
7575
7559
  const querySnapshot = await getDocs11(q);
7576
7560
  const users = querySnapshot.docs.map((doc38) => doc38.data());
@@ -7652,23 +7636,17 @@ var UserService = class extends BaseService {
7652
7636
  switch (role) {
7653
7637
  case "patient" /* PATIENT */:
7654
7638
  if (user.patientProfile) {
7655
- await this.getPatientService().deletePatientProfile(
7656
- user.patientProfile
7657
- );
7639
+ await this.getPatientService().deletePatientProfile(user.patientProfile);
7658
7640
  }
7659
7641
  break;
7660
7642
  case "clinic_admin" /* CLINIC_ADMIN */:
7661
7643
  if (user.adminProfile) {
7662
- await this.getClinicAdminService().deleteClinicAdmin(
7663
- user.adminProfile
7664
- );
7644
+ await this.getClinicAdminService().deleteClinicAdmin(user.adminProfile);
7665
7645
  }
7666
7646
  break;
7667
7647
  case "practitioner" /* PRACTITIONER */:
7668
7648
  if (user.practitionerProfile) {
7669
- await this.getPractitionerService().deletePractitioner(
7670
- user.practitionerProfile
7671
- );
7649
+ await this.getPractitionerService().deletePractitioner(user.practitionerProfile);
7672
7650
  }
7673
7651
  break;
7674
7652
  }
@@ -7687,19 +7665,13 @@ var UserService = class extends BaseService {
7687
7665
  const userData = userDoc.data();
7688
7666
  try {
7689
7667
  if (userData.patientProfile) {
7690
- await this.getPatientService().deletePatientProfile(
7691
- userData.patientProfile
7692
- );
7668
+ await this.getPatientService().deletePatientProfile(userData.patientProfile);
7693
7669
  }
7694
7670
  if (userData.practitionerProfile) {
7695
- await this.getPractitionerService().deletePractitioner(
7696
- userData.practitionerProfile
7697
- );
7671
+ await this.getPractitionerService().deletePractitioner(userData.practitionerProfile);
7698
7672
  }
7699
7673
  if (userData.adminProfile) {
7700
- await this.getClinicAdminService().deleteClinicAdmin(
7701
- userData.adminProfile
7702
- );
7674
+ await this.getClinicAdminService().deleteClinicAdmin(userData.adminProfile);
7703
7675
  }
7704
7676
  await deleteDoc4(userRef);
7705
7677
  } catch (error) {
@@ -10476,15 +10448,37 @@ var AuthService = class extends BaseService {
10476
10448
  }
10477
10449
  /**
10478
10450
  * Signs in a user with a Google ID token from a mobile client.
10479
- * If the user does not exist, a new user is created.
10451
+ * If the user does not exist in our database, the login is rejected.
10480
10452
  * @param idToken - The Google ID token obtained from the mobile app.
10481
- * @param initialRole - The role to assign to the user if they are being created.
10482
- * @returns The signed-in or newly created user.
10453
+ * @param initialRole - The role to assign to the user (currently unused).
10454
+ * @returns The signed-in user if they exist in our database.
10455
+ * @throws AuthError if no user profile is found.
10483
10456
  */
10484
10457
  async signInWithGoogleIdToken(idToken, initialRole = "patient" /* PATIENT */) {
10485
10458
  try {
10486
10459
  console.log("[AUTH] Signing in with Google ID Token");
10487
10460
  const credential = GoogleAuthProvider.credential(idToken);
10461
+ const decodedToken = JSON.parse(atob(idToken.split(".")[1]));
10462
+ const userEmail = decodedToken.email;
10463
+ const userUid = decodedToken.sub;
10464
+ console.log("[AUTH] Checking if Firebase Auth user exists with email:", userEmail);
10465
+ let existingAuthUser;
10466
+ try {
10467
+ const admin = await import("firebase-admin");
10468
+ existingAuthUser = await admin.auth().getUserByEmail(userEmail);
10469
+ console.log("[AUTH] Firebase Auth user found:", existingAuthUser.uid);
10470
+ } catch (authError) {
10471
+ if (authError.code === "auth/user-not-found") {
10472
+ console.log("[AUTH] No Firebase Auth user found for email:", userEmail);
10473
+ throw new AuthError(
10474
+ 'No account found. Please complete registration by starting with "Get Started".',
10475
+ "AUTH/USER_NOT_FOUND",
10476
+ 404
10477
+ );
10478
+ }
10479
+ throw authError;
10480
+ }
10481
+ console.log("[AUTH] Firebase Auth user exists, proceeding with sign-in");
10488
10482
  const { user: firebaseUser } = await signInWithCredential(this.auth, credential);
10489
10483
  console.log("[AUTH] Firebase user signed in:", firebaseUser.uid);
10490
10484
  const existingUser = await this.userService.getUserById(firebaseUser.uid);
@@ -10492,12 +10486,11 @@ var AuthService = class extends BaseService {
10492
10486
  console.log("[AUTH] Existing user found, returning profile:", existingUser.uid);
10493
10487
  return existingUser;
10494
10488
  }
10495
- console.log("[AUTH] No existing user found for Google account:", firebaseUser.email);
10496
10489
  await firebaseSignOut(this.auth);
10497
10490
  throw new AuthError(
10498
- 'No account found. Please complete registration by starting with "Get Started".',
10499
- "AUTH/USER_NOT_FOUND",
10500
- 404
10491
+ "Account found but registration incomplete. Please complete registration.",
10492
+ "AUTH/INCOMPLETE_REGISTRATION",
10493
+ 400
10501
10494
  );
10502
10495
  } catch (error) {
10503
10496
  console.error("[AUTH] Error in signInWithGoogleIdToken:", error);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.12.13",
4
+ "version": "1.12.15",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -872,10 +872,11 @@ export class AuthService extends BaseService {
872
872
 
873
873
  /**
874
874
  * Signs in a user with a Google ID token from a mobile client.
875
- * If the user does not exist, a new user is created.
875
+ * If the user does not exist in our database, the login is rejected.
876
876
  * @param idToken - The Google ID token obtained from the mobile app.
877
- * @param initialRole - The role to assign to the user if they are being created.
878
- * @returns The signed-in or newly created user.
877
+ * @param initialRole - The role to assign to the user (currently unused).
878
+ * @returns The signed-in user if they exist in our database.
879
+ * @throws AuthError if no user profile is found.
879
880
  */
880
881
  async signInWithGoogleIdToken(
881
882
  idToken: string,
@@ -883,27 +884,56 @@ export class AuthService extends BaseService {
883
884
  ): Promise<User> {
884
885
  try {
885
886
  console.log('[AUTH] Signing in with Google ID Token');
887
+
888
+ // First, decode the ID token to get the user's email without signing them in
886
889
  const credential = GoogleAuthProvider.credential(idToken);
890
+
891
+ // Parse the ID token to get user info without creating a Firebase Auth session
892
+ const decodedToken = JSON.parse(atob(idToken.split('.')[1]));
893
+ const userEmail = decodedToken.email;
894
+ const userUid = decodedToken.sub; // This will be the Firebase UID
895
+
896
+ console.log('[AUTH] Checking if Firebase Auth user exists with email:', userEmail);
897
+
898
+ // Check if a Firebase Auth user with this email already exists
899
+ let existingAuthUser;
900
+ try {
901
+ const admin = await import('firebase-admin');
902
+ existingAuthUser = await admin.auth().getUserByEmail(userEmail);
903
+ console.log('[AUTH] Firebase Auth user found:', existingAuthUser.uid);
904
+ } catch (authError: any) {
905
+ if (authError.code === 'auth/user-not-found') {
906
+ // No Firebase Auth user exists - reject the login
907
+ console.log('[AUTH] No Firebase Auth user found for email:', userEmail);
908
+ throw new AuthError(
909
+ 'No account found. Please complete registration by starting with "Get Started".',
910
+ 'AUTH/USER_NOT_FOUND',
911
+ 404,
912
+ );
913
+ }
914
+ // Re-throw other auth errors
915
+ throw authError;
916
+ }
917
+
918
+ console.log('[AUTH] Firebase Auth user exists, proceeding with sign-in');
919
+
920
+ // Now proceed with the actual Firebase sign-in since we know the auth user exists
887
921
  const { user: firebaseUser } = await signInWithCredential(this.auth, credential);
888
922
  console.log('[AUTH] Firebase user signed in:', firebaseUser.uid);
889
923
 
890
- // Check if the user already has a profile in our database
924
+ // Get the user profile from our database
891
925
  const existingUser = await this.userService.getUserById(firebaseUser.uid);
892
926
  if (existingUser) {
893
927
  console.log('[AUTH] Existing user found, returning profile:', existingUser.uid);
894
928
  return existingUser;
895
929
  }
896
930
 
897
- // If no profile exists, reject the login - user must complete onboarding first
898
- console.log('[AUTH] No existing user found for Google account:', firebaseUser.email);
899
-
900
- // Sign out the Firebase user since we don't allow auto-registration
931
+ // Auth user exists but no profile - this means incomplete registration
901
932
  await firebaseSignOut(this.auth);
902
-
903
933
  throw new AuthError(
904
- 'No account found. Please complete registration by starting with "Get Started".',
905
- 'AUTH/USER_NOT_FOUND',
906
- 404,
934
+ 'Account found but registration incomplete. Please complete registration.',
935
+ 'AUTH/INCOMPLETE_REGISTRATION',
936
+ 400,
907
937
  );
908
938
  } catch (error) {
909
939
  console.error('[AUTH] Error in signInWithGoogleIdToken:', error);
@@ -12,24 +12,24 @@ import {
12
12
  setDoc,
13
13
  serverTimestamp,
14
14
  FieldValue,
15
- } from "firebase/firestore";
16
- import { initializeFirebase } from "../../config/firebase";
17
- import { User, UserRole, USERS_COLLECTION, CreateUserData } from "../../types";
18
- import { userSchema } from "../../validations/schemas";
19
- import { AuthError } from "../../errors/auth.errors";
20
- import { USER_ERRORS } from "../../errors/user.errors";
21
- import { AUTH_ERRORS } from "../../errors/auth.errors";
22
- import { z } from "zod";
23
- import { BaseService } from "../base.service";
24
- import { PatientService } from "../patient/patient.service";
25
- import { ClinicAdminService } from "../clinic/clinic-admin.service";
26
- import { PatientProfile, PATIENTS_COLLECTION } from "../../types/patient";
27
- import { User as FirebaseUser } from "firebase/auth";
28
- import { Auth } from "firebase/auth";
29
- import { PractitionerService } from "../practitioner/practitioner.service";
30
- import { CertificationLevel } from "../../backoffice/types/static/certification.types";
31
- import { Firestore } from "firebase/firestore";
32
- import { FirebaseApp } from "firebase/app";
15
+ } from 'firebase/firestore';
16
+ import { initializeFirebase } from '../../config/firebase';
17
+ import { User, UserRole, USERS_COLLECTION, CreateUserData } from '../../types';
18
+ import { userSchema } from '../../validations/schemas';
19
+ import { AuthError } from '../../errors/auth.errors';
20
+ import { USER_ERRORS } from '../../errors/user.errors';
21
+ import { AUTH_ERRORS } from '../../errors/auth.errors';
22
+ import { z } from 'zod';
23
+ import { BaseService } from '../base.service';
24
+ import { PatientService } from '../patient/patient.service';
25
+ import { ClinicAdminService } from '../clinic/clinic-admin.service';
26
+ import { PatientProfile, PATIENTS_COLLECTION } from '../../types/patient';
27
+ import { User as FirebaseUser } from 'firebase/auth';
28
+ import { Auth } from 'firebase/auth';
29
+ import { PractitionerService } from '../practitioner/practitioner.service';
30
+ import { CertificationLevel } from '../../backoffice/types/static/certification.types';
31
+ import { Firestore } from 'firebase/firestore';
32
+ import { FirebaseApp } from 'firebase/app';
33
33
 
34
34
  export class UserService extends BaseService {
35
35
  private patientService: PatientService;
@@ -42,7 +42,7 @@ export class UserService extends BaseService {
42
42
  app: FirebaseApp,
43
43
  patientService?: PatientService,
44
44
  clinicAdminService?: ClinicAdminService,
45
- practitionerService?: PractitionerService
45
+ practitionerService?: PractitionerService,
46
46
  ) {
47
47
  super(db, auth, app);
48
48
 
@@ -88,7 +88,7 @@ export class UserService extends BaseService {
88
88
  };
89
89
  patientInviteToken?: string;
90
90
  skipProfileCreation?: boolean;
91
- }
91
+ },
92
92
  ): Promise<User> {
93
93
  const userData: CreateUserData = {
94
94
  uid: firebaseUser.uid,
@@ -108,11 +108,7 @@ export class UserService extends BaseService {
108
108
  return this.getUserById(userData.uid);
109
109
  }
110
110
 
111
- const profiles = await this.createProfilesForRoles(
112
- userData.uid,
113
- roles,
114
- options
115
- );
111
+ const profiles = await this.createProfilesForRoles(userData.uid, roles, options);
116
112
 
117
113
  // Ažuriramo korisnika sa referencama na profile
118
114
  await updateDoc(doc(this.db, USERS_COLLECTION, userData.uid), profiles);
@@ -123,10 +119,7 @@ export class UserService extends BaseService {
123
119
  /**
124
120
  * Dohvata ili kreira korisnika na osnovu Firebase korisnika
125
121
  */
126
- async getOrCreateUser(
127
- firebaseUser: FirebaseUser,
128
- initialRole?: UserRole
129
- ): Promise<User> {
122
+ async getOrCreateUser(firebaseUser: FirebaseUser, initialRole?: UserRole): Promise<User> {
130
123
  try {
131
124
  const existingUser = await this.getUserById(firebaseUser.uid);
132
125
  await this.updateUserLoginTimestamp(firebaseUser.uid);
@@ -150,7 +143,7 @@ export class UserService extends BaseService {
150
143
  };
151
144
  patientInviteToken?: string;
152
145
  skipProfileCreation?: boolean;
153
- }
146
+ },
154
147
  ): Promise<{
155
148
  patientProfile?: string;
156
149
  practitionerProfile?: string;
@@ -168,30 +161,21 @@ export class UserService extends BaseService {
168
161
  // If a token is provided, claim the existing manual profile
169
162
  if (options?.patientInviteToken) {
170
163
  const patientService = this.getPatientService();
171
- const token = await patientService.validatePatientToken(
172
- options.patientInviteToken
173
- );
164
+ const token = await patientService.validatePatientToken(options.patientInviteToken);
174
165
 
175
166
  if (!token) {
176
- throw new Error("Invalid or expired patient invitation token.");
167
+ throw new Error('Invalid or expired patient invitation token.');
177
168
  }
178
169
 
179
170
  // Get the patient profile
180
- const patientProfile = await patientService.getPatientProfile(
181
- token.patientId
182
- );
171
+ const patientProfile = await patientService.getPatientProfile(token.patientId);
183
172
  if (!patientProfile || !patientProfile.isManual) {
184
- throw new Error(
185
- "Patient profile not found or has already been claimed."
186
- );
173
+ throw new Error('Patient profile not found or has already been claimed.');
187
174
  }
188
175
 
189
176
  // Check if user already has a patient profile
190
- if (
191
- (await this.getUserById(userId)).patientProfile ||
192
- patientProfile.userRef
193
- ) {
194
- throw new Error("User already has a patient profile.");
177
+ if ((await this.getUserById(userId)).patientProfile || patientProfile.userRef) {
178
+ throw new Error('User already has a patient profile.');
195
179
  }
196
180
 
197
181
  // Claim the profile: link userRef and set isManual to false
@@ -201,29 +185,24 @@ export class UserService extends BaseService {
201
185
  });
202
186
 
203
187
  // Mark the token as used
204
- await patientService.markPatientTokenAsUsed(
205
- token.id,
206
- token.patientId,
207
- userId
208
- );
188
+ await patientService.markPatientTokenAsUsed(token.id, token.patientId, userId);
209
189
 
210
190
  profiles.patientProfile = patientProfile.id;
211
191
  break;
212
192
  }
213
193
 
214
- const patientProfile =
215
- await this.getPatientService().createPatientProfile({
216
- userRef: userId,
217
- displayName: "Patient", // Default displayName, može se kasnije promeniti
218
- expoTokens: [],
219
- gamification: {
220
- level: 1,
221
- points: 0,
222
- },
223
- isActive: true,
224
- isVerified: false,
225
- isManual: false, // Explicitly set to false for standard signups
226
- });
194
+ const patientProfile = await this.getPatientService().createPatientProfile({
195
+ userRef: userId,
196
+ displayName: 'Patient', // Default displayName, može se kasnije promeniti
197
+ expoTokens: [],
198
+ gamification: {
199
+ level: 1,
200
+ points: 0,
201
+ },
202
+ isActive: true,
203
+ isVerified: false,
204
+ isManual: false, // Explicitly set to false for standard signups
205
+ });
227
206
  profiles.patientProfile = patientProfile.id;
228
207
  break;
229
208
  case UserRole.CLINIC_ADMIN:
@@ -234,66 +213,61 @@ export class UserService extends BaseService {
234
213
  }
235
214
 
236
215
  // Ako imamo token, verifikujemo ga i dodajemo admina u postojeću grupu
237
- if (
238
- options?.clinicAdminData?.groupToken &&
239
- options?.clinicAdminData?.groupId
240
- ) {
216
+ if (options?.clinicAdminData?.groupToken && options?.clinicAdminData?.groupId) {
241
217
  const isValid = await this.getClinicAdminService()
242
218
  .getClinicGroupService()
243
219
  .verifyAndUseAdminToken(
244
220
  options.clinicAdminData.groupId,
245
221
  options.clinicAdminData.groupToken,
246
- userId
222
+ userId,
247
223
  );
248
224
 
249
225
  if (!isValid) {
250
- throw new Error("Invalid admin token");
226
+ throw new Error('Invalid admin token');
251
227
  }
252
228
  }
253
229
 
254
- const clinicAdminProfile =
255
- await this.getClinicAdminService().createClinicAdmin({
256
- userRef: userId,
257
- clinicGroupId: options?.clinicAdminData?.groupId || "",
258
- isGroupOwner: options?.clinicAdminData?.isGroupOwner || false,
259
- clinicsManaged: [],
260
- contactInfo: {
261
- firstName: "",
262
- lastName: "",
263
- title: "Clinic Administrator",
264
- email: "",
265
- phoneNumber: "",
266
- },
267
- roleTitle: "Clinic Administrator",
268
- isActive: true,
269
- });
230
+ const clinicAdminProfile = await this.getClinicAdminService().createClinicAdmin({
231
+ userRef: userId,
232
+ clinicGroupId: options?.clinicAdminData?.groupId || '',
233
+ isGroupOwner: options?.clinicAdminData?.isGroupOwner || false,
234
+ clinicsManaged: [],
235
+ contactInfo: {
236
+ firstName: '',
237
+ lastName: '',
238
+ title: 'Clinic Administrator',
239
+ email: '',
240
+ phoneNumber: '',
241
+ },
242
+ roleTitle: 'Clinic Administrator',
243
+ isActive: true,
244
+ });
270
245
  profiles.adminProfile = clinicAdminProfile.id;
271
246
  break;
272
247
  case UserRole.PRACTITIONER:
273
- const practitionerProfile =
274
- await this.getPractitionerService().createPractitioner({
275
- userRef: userId,
276
- basicInfo: {
277
- firstName: "",
278
- lastName: "",
279
- email: "",
280
- phoneNumber: "",
281
- title: "",
282
- dateOfBirth: Timestamp.now(),
283
- gender: "other",
284
- languages: ["Serbian"],
285
- },
286
- certification: {
287
- level: CertificationLevel.AESTHETICIAN,
288
- specialties: [],
289
- licenseNumber: "",
290
- issuingAuthority: "",
291
- issueDate: Timestamp.now(),
292
- verificationStatus: "pending",
293
- },
294
- isActive: true,
295
- isVerified: false,
296
- });
248
+ const practitionerProfile = await this.getPractitionerService().createPractitioner({
249
+ userRef: userId,
250
+ basicInfo: {
251
+ firstName: '',
252
+ lastName: '',
253
+ email: '',
254
+ phoneNumber: '',
255
+ title: '',
256
+ dateOfBirth: Timestamp.now(),
257
+ gender: 'other',
258
+ languages: ['Serbian'],
259
+ },
260
+ certification: {
261
+ level: CertificationLevel.AESTHETICIAN,
262
+ specialties: [],
263
+ licenseNumber: '',
264
+ issuingAuthority: '',
265
+ issueDate: Timestamp.now(),
266
+ verificationStatus: 'pending',
267
+ },
268
+ isActive: true,
269
+ isVerified: false,
270
+ });
297
271
  profiles.practitionerProfile = practitionerProfile.id;
298
272
  break;
299
273
  }
@@ -321,7 +295,7 @@ export class UserService extends BaseService {
321
295
  */
322
296
  async getUserByEmail(email: string): Promise<User | null> {
323
297
  const usersRef = collection(this.db, USERS_COLLECTION);
324
- const q = query(usersRef, where("email", "==", email));
298
+ const q = query(usersRef, where('email', '==', email));
325
299
  const querySnapshot = await getDocs(q);
326
300
 
327
301
  if (querySnapshot.empty) return null;
@@ -331,14 +305,12 @@ export class UserService extends BaseService {
331
305
  }
332
306
 
333
307
  async getUsersByRole(role: UserRole): Promise<User[]> {
334
- const constraints: QueryConstraint[] = [
335
- where("roles", "array-contains", role),
336
- ];
308
+ const constraints: QueryConstraint[] = [where('roles', 'array-contains', role)];
337
309
  const q = query(collection(this.db, USERS_COLLECTION), ...constraints);
338
310
  const querySnapshot = await getDocs(q);
339
311
 
340
- const users = querySnapshot.docs.map((doc) => doc.data());
341
- return users.map((userData) => userSchema.parse(userData) as User);
312
+ const users = querySnapshot.docs.map(doc => doc.data());
313
+ return users.map(userData => userSchema.parse(userData) as User);
342
314
  }
343
315
 
344
316
  /**
@@ -377,10 +349,7 @@ export class UserService extends BaseService {
377
349
  return this.getUserById(uid);
378
350
  }
379
351
 
380
- async updateUser(
381
- uid: string,
382
- updates: Partial<Omit<User, "uid">>
383
- ): Promise<User> {
352
+ async updateUser(uid: string, updates: Partial<Omit<User, 'uid'>>): Promise<User> {
384
353
  const userRef = doc(this.db, USERS_COLLECTION, uid);
385
354
  const userDoc = await getDoc(userRef);
386
355
 
@@ -426,7 +395,7 @@ export class UserService extends BaseService {
426
395
  groupToken?: string;
427
396
  groupId?: string;
428
397
  };
429
- }
398
+ },
430
399
  ): Promise<void> {
431
400
  const user = await this.getUserById(uid);
432
401
  if (user.roles.includes(role)) return;
@@ -451,23 +420,17 @@ export class UserService extends BaseService {
451
420
  switch (role) {
452
421
  case UserRole.PATIENT:
453
422
  if (user.patientProfile) {
454
- await this.getPatientService().deletePatientProfile(
455
- user.patientProfile
456
- );
423
+ await this.getPatientService().deletePatientProfile(user.patientProfile);
457
424
  }
458
425
  break;
459
426
  case UserRole.CLINIC_ADMIN:
460
427
  if (user.adminProfile) {
461
- await this.getClinicAdminService().deleteClinicAdmin(
462
- user.adminProfile
463
- );
428
+ await this.getClinicAdminService().deleteClinicAdmin(user.adminProfile);
464
429
  }
465
430
  break;
466
431
  case UserRole.PRACTITIONER:
467
432
  if (user.practitionerProfile) {
468
- await this.getPractitionerService().deletePractitioner(
469
- user.practitionerProfile
470
- );
433
+ await this.getPractitionerService().deletePractitioner(user.practitionerProfile);
471
434
  }
472
435
  break;
473
436
  // Dodati ostale role po potrebi
@@ -475,7 +438,7 @@ export class UserService extends BaseService {
475
438
 
476
439
  // Zatim uklanjamo rolu
477
440
  await updateDoc(doc(this.db, USERS_COLLECTION, uid), {
478
- roles: user.roles.filter((r) => r !== role),
441
+ roles: user.roles.filter(r => r !== role),
479
442
  updatedAt: serverTimestamp(),
480
443
  });
481
444
  }
@@ -494,21 +457,15 @@ export class UserService extends BaseService {
494
457
  try {
495
458
  // Delete all associated profiles
496
459
  if (userData.patientProfile) {
497
- await this.getPatientService().deletePatientProfile(
498
- userData.patientProfile
499
- );
460
+ await this.getPatientService().deletePatientProfile(userData.patientProfile);
500
461
  }
501
462
 
502
463
  if (userData.practitionerProfile) {
503
- await this.getPractitionerService().deletePractitioner(
504
- userData.practitionerProfile
505
- );
464
+ await this.getPractitionerService().deletePractitioner(userData.practitionerProfile);
506
465
  }
507
466
 
508
467
  if (userData.adminProfile) {
509
- await this.getClinicAdminService().deleteClinicAdmin(
510
- userData.adminProfile
511
- );
468
+ await this.getClinicAdminService().deleteClinicAdmin(userData.adminProfile);
512
469
  }
513
470
 
514
471
  await deleteDoc(userRef);