@blackcode_sa/metaestetics-api 1.14.18 → 1.14.26
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 +2 -13
- package/dist/admin/index.mjs +2 -13
- package/dist/backoffice/index.d.mts +9 -4
- package/dist/backoffice/index.d.ts +9 -4
- package/dist/backoffice/index.js +18 -8
- package/dist/backoffice/index.mjs +18 -8
- package/dist/index.d.mts +20 -18
- package/dist/index.d.ts +20 -18
- package/dist/index.js +82 -179
- package/dist/index.mjs +135 -232
- package/package.json +4 -1
- package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +2 -13
- package/src/backoffice/services/brand.service.ts +21 -4
- package/src/backoffice/types/brand.types.ts +2 -0
- package/src/services/auth/auth.service.ts +7 -203
- package/src/services/practitioner/practitioner.service.ts +79 -4
- package/src/services/user/user.service.ts +1 -51
|
@@ -483,6 +483,34 @@ export class PractitionerService extends BaseService {
|
|
|
483
483
|
throw new Error("Practitioner is not associated with this clinic");
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
+
// Security check: Verify that the clinic belongs to the clinic group of the user creating the token
|
|
487
|
+
// createdBy can be either clinicGroupId or clinicId
|
|
488
|
+
let expectedClinicGroupId: string | null = null;
|
|
489
|
+
|
|
490
|
+
// First, check if createdBy matches the clinic's clinicGroupId directly
|
|
491
|
+
if (clinic.clinicGroupId === createdBy) {
|
|
492
|
+
// createdBy is the clinicGroupId, which matches - this is valid
|
|
493
|
+
expectedClinicGroupId = createdBy;
|
|
494
|
+
} else {
|
|
495
|
+
// createdBy might be a clinicId, check if that clinic belongs to the same group
|
|
496
|
+
try {
|
|
497
|
+
const creatorClinic = await this.getClinicService().getClinic(createdBy);
|
|
498
|
+
if (creatorClinic && creatorClinic.clinicGroupId === clinic.clinicGroupId) {
|
|
499
|
+
// Both clinics belong to the same group - valid
|
|
500
|
+
expectedClinicGroupId = clinic.clinicGroupId;
|
|
501
|
+
} else {
|
|
502
|
+
throw new Error("Clinic does not belong to your clinic group");
|
|
503
|
+
}
|
|
504
|
+
} catch (error: any) {
|
|
505
|
+
// If createdBy is not a valid clinicId, or clinics don't match, reject
|
|
506
|
+
if (error.message === "Clinic does not belong to your clinic group") {
|
|
507
|
+
throw error;
|
|
508
|
+
}
|
|
509
|
+
// If getClinic fails, createdBy might be a clinicGroupId that doesn't match
|
|
510
|
+
throw new Error("Clinic does not belong to your clinic group");
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
486
514
|
// Default expiration is 7 days from now if not specified
|
|
487
515
|
const expiration =
|
|
488
516
|
validatedData.expiresAt ||
|
|
@@ -522,21 +550,29 @@ export class PractitionerService extends BaseService {
|
|
|
522
550
|
/**
|
|
523
551
|
* Gets active tokens for a practitioner
|
|
524
552
|
* @param practitionerId ID of the practitioner
|
|
553
|
+
* @param clinicId Optional clinic ID to filter tokens by. If provided, only returns tokens for this clinic.
|
|
525
554
|
* @returns Array of active tokens
|
|
526
555
|
*/
|
|
527
556
|
async getPractitionerActiveTokens(
|
|
528
|
-
practitionerId: string
|
|
557
|
+
practitionerId: string,
|
|
558
|
+
clinicId?: string
|
|
529
559
|
): Promise<PractitionerToken[]> {
|
|
530
560
|
const tokensRef = collection(
|
|
531
561
|
this.db,
|
|
532
562
|
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}`
|
|
533
563
|
);
|
|
534
564
|
|
|
535
|
-
const
|
|
536
|
-
tokensRef,
|
|
565
|
+
const conditions = [
|
|
537
566
|
where("status", "==", PractitionerTokenStatus.ACTIVE),
|
|
538
567
|
where("expiresAt", ">", Timestamp.now())
|
|
539
|
-
|
|
568
|
+
];
|
|
569
|
+
|
|
570
|
+
// Filter by clinic if provided
|
|
571
|
+
if (clinicId) {
|
|
572
|
+
conditions.push(where("clinicId", "==", clinicId));
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const q = query(tokensRef, ...conditions);
|
|
540
576
|
|
|
541
577
|
const querySnapshot = await getDocs(q);
|
|
542
578
|
return querySnapshot.docs.map((doc) => doc.data() as PractitionerToken);
|
|
@@ -628,6 +664,45 @@ export class PractitionerService extends BaseService {
|
|
|
628
664
|
});
|
|
629
665
|
}
|
|
630
666
|
|
|
667
|
+
/**
|
|
668
|
+
* Revokes a token by setting its status to REVOKED
|
|
669
|
+
* @param tokenId ID of the token
|
|
670
|
+
* @param practitionerId ID of the practitioner
|
|
671
|
+
* @param clinicId ID of the clinic that owns the token. Used to verify ownership before revoking.
|
|
672
|
+
* @throws Error if token doesn't exist or doesn't belong to the specified clinic
|
|
673
|
+
*/
|
|
674
|
+
async revokeToken(
|
|
675
|
+
tokenId: string,
|
|
676
|
+
practitionerId: string,
|
|
677
|
+
clinicId: string
|
|
678
|
+
): Promise<void> {
|
|
679
|
+
const tokenRef = doc(
|
|
680
|
+
this.db,
|
|
681
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}/${tokenId}`
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
// First, verify the token exists and belongs to the clinic
|
|
685
|
+
const tokenDoc = await getDoc(tokenRef);
|
|
686
|
+
if (!tokenDoc.exists()) {
|
|
687
|
+
throw new Error("Token not found");
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const tokenData = tokenDoc.data() as PractitionerToken;
|
|
691
|
+
if (tokenData.clinicId !== clinicId) {
|
|
692
|
+
throw new Error("Token does not belong to the specified clinic");
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Only revoke if token is still active
|
|
696
|
+
if (tokenData.status !== PractitionerTokenStatus.ACTIVE) {
|
|
697
|
+
throw new Error("Token is not active and cannot be revoked");
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
await updateDoc(tokenRef, {
|
|
701
|
+
status: PractitionerTokenStatus.REVOKED,
|
|
702
|
+
updatedAt: serverTimestamp(),
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
|
|
631
706
|
/**
|
|
632
707
|
* Dohvata zdravstvenog radnika po ID-u
|
|
633
708
|
*/
|
|
@@ -45,18 +45,6 @@ export class UserService extends BaseService {
|
|
|
45
45
|
) {
|
|
46
46
|
super(db, auth, app);
|
|
47
47
|
|
|
48
|
-
// DEBUG: Tag the auth instance
|
|
49
|
-
// @ts-ignore
|
|
50
|
-
if (!this.auth.__userServiceId) {
|
|
51
|
-
// @ts-ignore
|
|
52
|
-
this.auth.__userServiceId = 'user-service-' + Date.now();
|
|
53
|
-
}
|
|
54
|
-
// @ts-ignore
|
|
55
|
-
console.log('[USER_SERVICE] Constructor - auth ID:', this.auth.__userServiceId);
|
|
56
|
-
// @ts-ignore
|
|
57
|
-
console.log('[USER_SERVICE] Constructor - auth.__authServiceId:', this.auth.__authServiceId || 'NOT SET');
|
|
58
|
-
|
|
59
|
-
// Kreiramo servise samo ako nisu prosleđeni
|
|
60
48
|
if (!patientService) {
|
|
61
49
|
patientService = new PatientService(db, auth, app);
|
|
62
50
|
}
|
|
@@ -100,27 +88,6 @@ export class UserService extends BaseService {
|
|
|
100
88
|
skipProfileCreation?: boolean;
|
|
101
89
|
},
|
|
102
90
|
): Promise<User> {
|
|
103
|
-
// DEBUG LOGGING - Check auth state before creating user document
|
|
104
|
-
console.log('[USER_SERVICE] ====== CREATE USER DEBUG ======');
|
|
105
|
-
// @ts-ignore - Debug: Check auth instance ID
|
|
106
|
-
console.log(this.auth)
|
|
107
|
-
console.log('[USER_SERVICE] Auth instance ID:', (this.auth as any)?.__debugId || 'no-id');
|
|
108
|
-
console.log('[USER_SERVICE] Current auth state:', {
|
|
109
|
-
currentUser: this.auth?.currentUser?.uid || 'NULL',
|
|
110
|
-
currentUserEmail: this.auth?.currentUser?.email || 'NULL',
|
|
111
|
-
currentUserProvider: this.auth?.currentUser?.providerId || 'NULL',
|
|
112
|
-
});
|
|
113
|
-
console.log('[USER_SERVICE] Firebase user passed to createUser:', {
|
|
114
|
-
uid: firebaseUser?.uid || 'NULL',
|
|
115
|
-
email: firebaseUser?.email || 'NULL',
|
|
116
|
-
providerId: firebaseUser?.providerId || 'NULL',
|
|
117
|
-
isAnonymous: firebaseUser?.isAnonymous,
|
|
118
|
-
});
|
|
119
|
-
console.log('[USER_SERVICE] Auth instances match:', this.auth?.currentUser?.uid === firebaseUser?.uid);
|
|
120
|
-
console.log('[USER_SERVICE] Document path:', `${USERS_COLLECTION}/${firebaseUser?.uid}`);
|
|
121
|
-
console.log('[USER_SERVICE] Roles:', roles);
|
|
122
|
-
console.log('[USER_SERVICE] ================================');
|
|
123
|
-
|
|
124
91
|
const userData: CreateUserData = {
|
|
125
92
|
uid: firebaseUser.uid,
|
|
126
93
|
email: firebaseUser.email,
|
|
@@ -131,24 +98,7 @@ export class UserService extends BaseService {
|
|
|
131
98
|
lastLoginAt: serverTimestamp(),
|
|
132
99
|
};
|
|
133
100
|
|
|
134
|
-
|
|
135
|
-
uid: userData.uid,
|
|
136
|
-
email: userData.email,
|
|
137
|
-
roles: userData.roles,
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// Kreiramo osnovnog korisnika
|
|
141
|
-
try {
|
|
142
|
-
await setDoc(doc(this.db, USERS_COLLECTION, userData.uid), userData);
|
|
143
|
-
console.log('[USER_SERVICE] ✅ setDoc SUCCEEDED for:', userData.uid);
|
|
144
|
-
} catch (error: any) {
|
|
145
|
-
console.error('[USER_SERVICE] ❌ setDoc FAILED:', {
|
|
146
|
-
errorCode: error?.code,
|
|
147
|
-
errorMessage: error?.message,
|
|
148
|
-
uid: userData.uid,
|
|
149
|
-
});
|
|
150
|
-
throw error;
|
|
151
|
-
}
|
|
101
|
+
await setDoc(doc(this.db, USERS_COLLECTION, userData.uid), userData);
|
|
152
102
|
|
|
153
103
|
// Kreiramo odgovarajuće profile na osnovu rola
|
|
154
104
|
if (options?.skipProfileCreation) {
|