diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/meeting/meeting.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/meeting/meeting.component.ts index 5265fae8..17113ee3 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/meeting/meeting.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/meeting/meeting.component.ts @@ -17,7 +17,7 @@ import { FeatureConfigurationService } from '../../../../shared/services/feature import { GlobalConfigService } from '../../../../shared/services/global-config.service'; import { NotificationService } from '../../../../shared/services/notification.service'; import { SoundService } from '../../../../shared/services/sound.service'; -import { RoomMemberService } from '../../../rooms/services/room-member.service'; +import { RoomMemberContextService } from '../../../room-members/services/room-member-context.service'; import { MeetingLobbyComponent } from '../../components/meeting-lobby/meeting-lobby.component'; import { MeetingParticipantItemComponent } from '../../customization/meeting-participant-item/meeting-participant-item.component'; import { MeetingCaptionsService } from '../../services/meeting-captions.service'; @@ -63,7 +63,7 @@ export class MeetingComponent implements OnInit { protected isMeetingLeft = signal(false); protected features: Signal; - protected participantService = inject(RoomMemberService); + protected participantService = inject(RoomMemberContextService); protected featureConfService = inject(FeatureConfigurationService); protected ovThemeService = inject(OpenViduThemeService); protected configService = inject(GlobalConfigService); diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-event-handler.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-event-handler.service.ts index 3bcbaed1..cf8c2596 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-event-handler.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-event-handler.service.ts @@ -25,7 +25,7 @@ import { SessionStorageService } from '../../../shared/services/session-storage. import { SoundService } from '../../../shared/services/sound.service'; import { TokenStorageService } from '../../../shared/services/token-storage.service'; import { RecordingService } from '../../recordings/services/recording.service'; -import { RoomMemberService } from '../../rooms/services/room-member.service'; +import { RoomMemberContextService } from '../../room-members/services/room-member-context.service'; import { MeetingContextService } from './meeting-context.service'; import { MeetingWebComponentManagerService } from './meeting-webcomponent-manager.service'; @@ -40,7 +40,7 @@ export class MeetingEventHandlerService { protected meetingContext = inject(MeetingContextService); protected featureConfService = inject(FeatureConfigurationService); protected recordingService = inject(RecordingService); - protected roomMemberService = inject(RoomMemberService); + protected roomMemberService = inject(RoomMemberContextService); protected sessionStorageService = inject(SessionStorageService); protected tokenStorageService = inject(TokenStorageService); protected wcManagerService = inject(MeetingWebComponentManagerService); diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-lobby.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-lobby.service.ts index f1d372fa..2c32f4f6 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-lobby.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-lobby.service.ts @@ -8,7 +8,7 @@ import { AppDataService } from '../../../shared/services/app-data.service'; import { NavigationService } from '../../../shared/services/navigation.service'; import { AuthService } from '../../auth/services/auth.service'; import { RecordingService } from '../../recordings/services/recording.service'; -import { RoomMemberService } from '../../rooms/services/room-member.service'; +import { RoomMemberContextService } from '../../room-members/services/room-member-context.service'; import { RoomService } from '../../rooms/services/room.service'; import { LobbyState } from '../models/lobby.model'; import { MeetingContextService } from './meeting-context.service'; @@ -46,7 +46,7 @@ export class MeetingLobbyService { protected meetingService: MeetingService = inject(MeetingService); protected recordingService: RecordingService = inject(RecordingService); protected authService: AuthService = inject(AuthService); - protected roomMemberService: RoomMemberService = inject(RoomMemberService); + protected roomMemberService: RoomMemberContextService = inject(RoomMemberContextService); protected navigationService: NavigationService = inject(NavigationService); protected appDataService: AppDataService = inject(AppDataService); protected wcManagerService: MeetingWebComponentManagerService = inject(MeetingWebComponentManagerService); @@ -287,7 +287,7 @@ export class MeetingLobbyService { */ protected async checkForRecordings(): Promise { try { - const canRetrieveRecordings = this.roomMemberService.canRetrieveRecordings(); + const canRetrieveRecordings = this.roomMemberService.hasPermission('canRetrieveRecordings'); if (!canRetrieveRecordings) { this._state.update((state) => ({ ...state, showRecordingCard: false })); diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-webcomponent-manager.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-webcomponent-manager.service.ts index 39f06a71..2c18ee64 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-webcomponent-manager.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-webcomponent-manager.service.ts @@ -7,7 +7,7 @@ import { } from '@openvidu-meet/typings'; import { LoggerService, OpenViduService } from 'openvidu-components-angular'; import { AppDataService } from '../../../shared/services/app-data.service'; -import { RoomMemberService } from '../../rooms/services/room-member.service'; +import { RoomMemberContextService } from '../../room-members/services/room-member-context.service'; import { MeetingContextService } from './meeting-context.service'; import { MeetingService } from './meeting.service'; @@ -26,7 +26,7 @@ export class MeetingWebComponentManagerService { protected log; protected readonly meetingContextService = inject(MeetingContextService); - protected readonly roomMemberService = inject(RoomMemberService); + protected readonly roomMemberService = inject(RoomMemberContextService); protected readonly openviduService = inject(OpenViduService); protected readonly meetingService = inject(MeetingService); protected readonly loggerService = inject(LoggerService); @@ -121,9 +121,11 @@ export class MeetingWebComponentManagerService { console.debug('Message received from parent:', event.data); switch (command) { case WebComponentCommand.END_MEETING: - // Only moderators can end the meeting - if (!this.roomMemberService.isModerator()) { - this.log.w('End meeting command received but participant is not a moderator'); + // Only participants with canEndMeeting permission can end the meeting + if (!this.roomMemberService.hasPermission('canEndMeeting')) { + this.log.w( + 'End meeting command received but participant does not have permissions to end the meeting' + ); return; } @@ -141,9 +143,11 @@ export class MeetingWebComponentManagerService { await this.openviduService.disconnectRoom(); break; case WebComponentCommand.KICK_PARTICIPANT: - // Only moderators can kick participants - if (!this.roomMemberService.isModerator()) { - this.log.w('Kick participant command received but participant is not a moderator'); + // Only participants with canKickParticipants permission can kick participants + if (!this.roomMemberService.hasPermission('canKickParticipants')) { + this.log.w( + 'Kick participant command received but participant does not have permissions to kick participants' + ); return; } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/pages/room-recordings/room-recordings.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/pages/room-recordings/room-recordings.component.ts index 6bdf3779..659b81db 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/pages/room-recordings/room-recordings.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/pages/room-recordings/room-recordings.component.ts @@ -9,7 +9,7 @@ import { ILogger, LoggerService } from 'openvidu-components-angular'; import { NavigationService } from '../../../../shared/services/navigation.service'; import { NotificationService } from '../../../../shared/services/notification.service'; import { MeetingContextService } from '../../../meeting/services'; -import { RoomMemberService } from '../../../rooms/services/room-member.service'; +import { RoomMemberContextService } from '../../../room-members/services/room-member-context.service'; import { RecordingListsComponent } from '../../components/recording-lists/recording-lists.component'; import { RecordingTableAction, RecordingTableFilter } from '../../models/recording-list.model'; import { RecordingService } from '../../services/recording.service'; @@ -46,7 +46,7 @@ export class RoomRecordingsComponent implements OnInit { protected readonly loggerService = inject(LoggerService); protected readonly recordingService = inject(RecordingService); - protected readonly roomMemberService = inject(RoomMemberService); + protected readonly roomMemberService = inject(RoomMemberContextService); protected readonly notificationService = inject(NotificationService); protected readonly navigationService = inject(NavigationService); protected readonly meetingContextService = inject(MeetingContextService); @@ -58,7 +58,7 @@ export class RoomRecordingsComponent implements OnInit { async ngOnInit() { this.roomId = this.route.snapshot.paramMap.get('room-id')!; - this.canDeleteRecordings = this.roomMemberService.canDeleteRecordings(); + this.canDeleteRecordings = this.roomMemberService.hasPermission('canDeleteRecordings'); // Load recordings const delayLoader = setTimeout(() => { diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/index.ts new file mode 100644 index 00000000..c287d65a --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/index.ts @@ -0,0 +1,3 @@ +export * from './interceptor-handlers'; +export * from './providers'; +export * from './services'; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/interceptor-handlers/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/interceptor-handlers/index.ts similarity index 100% rename from meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/interceptor-handlers/index.ts rename to meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/interceptor-handlers/index.ts diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/interceptor-handlers/room-member-error-handler.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/interceptor-handlers/room-member-error-handler.service.ts similarity index 95% rename from meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/interceptor-handlers/room-member-error-handler.service.ts rename to meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/interceptor-handlers/room-member-error-handler.service.ts index c1d59465..f10c9ff0 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/interceptor-handlers/room-member-error-handler.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/interceptor-handlers/room-member-error-handler.service.ts @@ -8,7 +8,7 @@ import { } from '../../../shared/services/http-error-notifier.service'; import { TokenStorageService } from '../../../shared/services/token-storage.service'; import { MeetingContextService } from '../../meeting/services/meeting-context.service'; -import { RoomMemberService } from '../services/room-member.service'; +import { RoomMemberContextService } from '../services/room-member-context.service'; /** * Handler for room member token-related HTTP errors. @@ -19,7 +19,7 @@ import { RoomMemberService } from '../services/room-member.service'; providedIn: 'root' }) export class RoomMemberInterceptorErrorHandlerService implements HttpErrorHandler { - private readonly roomMemberService = inject(RoomMemberService); + private readonly roomMemberService = inject(RoomMemberContextService); private readonly meetingContextService = inject(MeetingContextService); private readonly tokenStorageService = inject(TokenStorageService); private readonly httpErrorNotifier = inject(HttpErrorNotifierService); diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/providers/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/providers/index.ts similarity index 100% rename from meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/providers/index.ts rename to meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/providers/index.ts diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/providers/room-member-adapter.provider.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/providers/room-member-adapter.provider.ts similarity index 72% rename from meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/providers/room-member-adapter.provider.ts rename to meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/providers/room-member-adapter.provider.ts index edd9dbe8..9f530e85 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/providers/room-member-adapter.provider.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/providers/room-member-adapter.provider.ts @@ -1,6 +1,6 @@ import { Provider } from '@angular/core'; import { ROOM_MEMBER_ADAPTER } from '../../../shared/adapters'; -import { RoomMemberService } from '../services/room-member.service'; +import { RoomMemberContextService } from '../../room-members/services/room-member-context.service'; /** * Provides the RoomMemberAdapter using the existing RoomMemberService. @@ -8,5 +8,5 @@ import { RoomMemberService } from '../services/room-member.service'; */ export const ROOM_MEMBER_ADAPTER_PROVIDER: Provider = { provide: ROOM_MEMBER_ADAPTER, - useExisting: RoomMemberService + useExisting: RoomMemberContextService }; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/index.ts new file mode 100644 index 00000000..0a7d4006 --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/index.ts @@ -0,0 +1,2 @@ +export * from './room-member-context.service'; +export * from './room-member.service'; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/room-member.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member-context.service.ts similarity index 60% rename from meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/room-member.service.ts rename to meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member-context.service.ts index 8e21411b..f1dd8a24 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/room-member.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member-context.service.ts @@ -7,15 +7,14 @@ import { } from '@openvidu-meet/typings'; import { E2eeService, LoggerService } from 'openvidu-components-angular'; import { FeatureConfigurationService } from '../../../shared/services/feature-configuration.service'; -import { HttpService } from '../../../shared/services/http.service'; import { TokenStorageService } from '../../../shared/services/token-storage.service'; -import { getValidDecodedToken } from '../../../shared/utils/token.utils'; +import { decodeToken } from '../../../shared/utils/token.utils'; +import { RoomMemberService } from './room-member.service'; @Injectable({ providedIn: 'root' }) -export class RoomMemberService { - protected readonly ROOM_MEMBERS_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/rooms`; +export class RoomMemberContextService { protected readonly PARTICIPANT_NAME_KEY = 'ovMeet-participantName'; protected participantName?: string; @@ -27,39 +26,65 @@ export class RoomMemberService { constructor( protected loggerService: LoggerService, - protected httpService: HttpService, + protected roomMemberService: RoomMemberService, protected featureConfService: FeatureConfigurationService, protected tokenStorageService: TokenStorageService, protected e2eeService: E2eeService ) { - this.log = this.loggerService.get('OpenVidu Meet - ParticipantTokenService'); - } - - protected getRoomMemberApiPath(roomId: string): string { - return `${this.ROOM_MEMBERS_API}/${roomId}/members`; + this.log = this.loggerService.get('OpenVidu Meet - RoomMemberContextService'); } + /** + * Sets the participant's display name and stores it in localStorage. + * + * @param participantName - The display name of the participant + */ setParticipantName(participantName: string): void { this.participantName = participantName; localStorage.setItem(this.PARTICIPANT_NAME_KEY, participantName); } + /** + * Retrieves the participant's display name from memory or localStorage. + * + * @returns The display name of the participant, or undefined if not set + */ getParticipantName(): string | undefined { return this.participantName || localStorage.getItem(this.PARTICIPANT_NAME_KEY) || undefined; } + /** + * Retrieves the participant's identity. + * + * @returns The identity of the participant, or undefined if not set + */ getParticipantIdentity(): string | undefined { return this.participantIdentity; } + /** + * Clears the participant's identity. + */ clearParticipantIdentity(): void { this.participantIdentity = undefined; } /** - * Generates a room member token and extracts role/permissions + * Checks if the current room member has a specific permission. * + * @param permission - The permission to check + * @returns True if the member has the permission, false otherwise + */ + hasPermission(permission: keyof MeetRoomMemberPermissions): boolean { + return this.permissions?.[permission] ?? false; + } + + /** + * Generates a room member token and updates the context with role and permissions. + * + * @param roomId - The unique identifier of the room * @param tokenOptions - The options for the token generation + * @param e2eeKey - Optional E2EE encryption key * @return A promise that resolves to the room member token */ async generateToken(roomId: string, tokenOptions: MeetRoomMemberTokenOptions, e2eeKey?: string): Promise { @@ -70,23 +95,21 @@ export class RoomMemberService { tokenOptions.participantName = encryptedName; } - const path = `${this.getRoomMemberApiPath(roomId)}/token`; - const { token } = await this.httpService.postRequest<{ token: string }>(path, tokenOptions); - + const { token } = await this.roomMemberService.generateRoomMemberToken(roomId, tokenOptions); this.tokenStorageService.setRoomMemberToken(token); - await this.updateRoomMemberTokenInfo(token); + await this.updateContextFromToken(token); return token; } /** - * Updates the current room member token information, including role and permissions. + * Updates the room member context (role and permissions) based on the provided token. * - * @param token - The JWT token to set. + * @param token - The room member token * @throws Error if the token is invalid or expired. */ - protected async updateRoomMemberTokenInfo(token: string): Promise { + protected async updateContextFromToken(token: string): Promise { try { - const decodedToken = getValidDecodedToken(token); + const decodedToken = decodeToken(token); const metadata = decodedToken.metadata as MeetRoomMemberTokenMetadata; if (decodedToken.sub && decodedToken.name) { @@ -102,24 +125,8 @@ export class RoomMemberService { this.featureConfService.setRoomMemberRole(this.role); this.featureConfService.setRoomMemberPermissions(this.permissions); } catch (error) { - this.log.e('Error updating room member token info', error); - throw new Error('Error updating room member token info'); + this.log.e('Error decoding room member token:', error); + throw new Error('Invalid room member token'); } } - - isModerator(): boolean { - return this.role === MeetRoomMemberRole.MODERATOR; - } - - getRoomMemberPermissions(): MeetRoomMemberPermissions | undefined { - return this.permissions; - } - - canRetrieveRecordings(): boolean { - return this.permissions?.canRetrieveRecordings ?? false; - } - - canDeleteRecordings(): boolean { - return this.permissions?.canDeleteRecordings ?? false; - } } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member.service.ts new file mode 100644 index 00000000..54a7d70f --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member.service.ts @@ -0,0 +1,155 @@ +import { Injectable } from '@angular/core'; +import { + MeetRoomMember, + MeetRoomMemberFilters, + MeetRoomMemberOptions, + MeetRoomMemberTokenOptions +} from '@openvidu-meet/typings'; +import { HttpService } from '../../../shared/services/http.service'; + +@Injectable({ + providedIn: 'root' +}) +export class RoomMemberService { + protected readonly ROOM_MEMBERS_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/rooms`; + + constructor(protected httpService: HttpService) {} + + /** + * Constructs the API path for room member operations based on the provided room ID. + * + * @param roomId - The unique identifier of the room + * @returns The API path for room member operations + */ + protected getRoomMemberApiPath(roomId: string): string { + return `${this.ROOM_MEMBERS_API}/${roomId}/members`; + } + + /** + * Creates a new room member with the specified options. + * + * @param roomId - The unique identifier of the room + * @param options - The options for creating the room member + * @returns A promise that resolves to the created MeetRoomMember object + */ + async createRoomMember(roomId: string, options: MeetRoomMemberOptions): Promise { + const path = this.getRoomMemberApiPath(roomId); + return this.httpService.postRequest(path, options); + } + + /** + * Lists room members with optional filters. + * + * @param roomId - The unique identifier of the room + * @param filters - Optional filters for pagination and fields + * @returns A promise that resolves to an object containing room members and pagination info + */ + async listRoomMembers( + roomId: string, + filters?: MeetRoomMemberFilters + ): Promise<{ + members: MeetRoomMember[]; + pagination: { + isTruncated: boolean; + nextPageToken?: string; + maxItems: number; + }; + }> { + let path = this.getRoomMemberApiPath(roomId); + + if (filters) { + const queryParams = new URLSearchParams(); + + Object.entries(filters).forEach(([key, value]) => { + if (value !== undefined && value !== null) { + queryParams.set(key, value.toString()); + } + }); + + const queryString = queryParams.toString(); + if (queryString) { + path += `?${queryString}`; + } + } + + return this.httpService.getRequest(path); + } + + /** + * Gets a specific room member by their ID. + * + * @param roomId - The unique identifier of the room + * @param memberId - The unique identifier of the room member + * @returns A promise that resolves to the MeetRoomMember object + */ + async getRoomMember(roomId: string, memberId: string): Promise { + const path = `${this.getRoomMemberApiPath(roomId)}/${memberId}`; + return this.httpService.getRequest(path); + } + + /** + * Updates a room member's information. + * + * @param roomId - The unique identifier of the room + * @param memberId - The unique identifier of the room member + * @param updates - The updates to apply to the room member + * @returns A promise that resolves to the updated MeetRoomMember object + */ + async updateRoomMember( + roomId: string, + memberId: string, + updates: Partial + ): Promise { + const path = `${this.getRoomMemberApiPath(roomId)}/${memberId}`; + return this.httpService.putRequest(path, updates); + } + + /** + * Deletes a room member by their ID. + * + * @param roomId - The unique identifier of the room + * @param memberId - The unique identifier of the room member to delete + * @returns A promise that resolves when the room member is deleted + */ + async deleteRoomMember(roomId: string, memberId: string): Promise { + const path = `${this.getRoomMemberApiPath(roomId)}/${memberId}`; + return this.httpService.deleteRequest(path); + } + + /** + * Bulk deletes multiple room members by their IDs. + * + * @param roomId - The unique identifier of the room + * @param memberIds - An array of room member IDs to delete + * @returns A promise that resolves when the room members are deleted + */ + async bulkDeleteRoomMembers( + roomId: string, + memberIds: string[] + ): Promise<{ + message: string; + deleted: string[]; + }> { + if (memberIds.length === 0) { + throw new Error('No room member IDs provided for bulk deletion'); + } + + const path = `${this.getRoomMemberApiPath(roomId)}?memberIds=${memberIds.join(',')}`; + return this.httpService.deleteRequest(path); + } + + /** + * Generates a room member token for accessing the room and its resources. + * + * @param roomId - The unique identifier of the room + * @param tokenOptions - The options for the token generation + * @returns A promise that resolves to an object containing the generated token + */ + async generateRoomMemberToken( + roomId: string, + tokenOptions: MeetRoomMemberTokenOptions + ): Promise<{ token: string }> { + const path = `${this.getRoomMemberApiPath(roomId)}/token`; + return this.httpService.postRequest(path, tokenOptions); + } +} diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/guards/room-validate-access.guard.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/guards/room-validate-access.guard.ts index 31289093..1151ab36 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/guards/room-validate-access.guard.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/guards/room-validate-access.guard.ts @@ -1,9 +1,10 @@ import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router'; import { NavigationErrorReason } from '../../../shared/models/navigation.model'; -import { NavigationService } from '../../../shared/services'; -import { MeetingContextService } from '../../meeting/services'; -import { RoomMemberService } from '../services'; +import { RoomMemberContextService } from '../../room-members/services/room-member-context.service'; +import { NavigationService } from '../../../shared/services/navigation.service'; +import { MeetingContextService } from '../../meeting/services/meeting-context.service'; + /** * Guard to validate access to a room by generating a room member token. @@ -33,7 +34,7 @@ export const validateRoomRecordingsAccessGuard: CanActivateFn = async ( * @returns True if access is granted, or UrlTree for redirection */ const validateRoomAccessInternal = async (pageUrl: string, validateRecordingPermissions = false) => { - const roomMemberService = inject(RoomMemberService); + const roomMemberService = inject(RoomMemberContextService); const navigationService = inject(NavigationService); const meetingContextService = inject(MeetingContextService); @@ -56,7 +57,7 @@ const validateRoomAccessInternal = async (pageUrl: string, validateRecordingPerm // Perform recording validation if requested if (validateRecordingPermissions) { - if (!roomMemberService.canRetrieveRecordings()) { + if (!roomMemberService.hasPermission('canRetrieveRecordings')) { // If the user does not have permission to retrieve recordings, redirect to the error page return navigationService.redirectToErrorPage(NavigationErrorReason.UNAUTHORIZED_RECORDING_ACCESS); } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/index.ts index 8d6630d2..bcd37985 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/index.ts @@ -1,7 +1,5 @@ export * from './components'; export * from './guards'; -export * from './interceptor-handlers'; export * from './models'; export * from './pages'; -export * from './providers'; export * from './services'; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/index.ts index 52e85c25..ea4fd50f 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/index.ts @@ -1,3 +1,2 @@ -export * from './room-member.service'; export * from './room.service'; export * from './wizard-state.service'; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/index.ts index 8d4f15ca..7019bf96 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/index.ts @@ -6,4 +6,3 @@ export * from './models'; export * from './routes/base-routes'; export * from './services'; export * from './utils'; - diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/token.utils.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/token.utils.ts index 3d8899b6..3521b0b7 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/token.utils.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/token.utils.ts @@ -1,6 +1,6 @@ import { jwtDecode } from 'jwt-decode'; -export const getValidDecodedToken = (token: string) => { +export const decodeToken = (token: string) => { checkIsJWTValid(token); const decodedToken: any = jwtDecode(token); decodedToken.metadata = JSON.parse(decodedToken.metadata); diff --git a/meet-ce/frontend/projects/shared-meet-components/src/public-api.ts b/meet-ce/frontend/projects/shared-meet-components/src/public-api.ts index 6bb1f815..2ed4ba22 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/public-api.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/public-api.ts @@ -10,6 +10,6 @@ export * from './lib/domains/auth'; export * from './lib/domains/console'; export * from './lib/domains/meeting'; export * from './lib/domains/recordings'; +export * from './lib/domains/room-members'; export * from './lib/domains/rooms'; export * from './lib/domains/users'; -