From 6c3d87c8bf897e5478869a0fe2668804b9e406c9 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Fri, 6 Feb 2026 13:53:06 +0100 Subject: [PATCH] fix(frontend): update app to match backend API changes and restore build Refactored frontend code to align with recent backend API updates. Adjusted endpoints, data models, and related logic to ensure successful compilation --- .../auth-error-handler.service.ts | 6 +- .../auth/pages/login/login.component.html | 9 +- .../auth/pages/login/login.component.ts | 36 ++-- .../lib/domains/auth/services/auth.service.ts | 103 ++++++++-- .../lib/domains/console/components/index.ts | 1 + .../logo-selector.component.scss | 2 +- .../src/lib/domains/console/index.ts | 1 - .../pages/overview/overview.component.ts | 4 - .../lib/domains/meeting/components/index.ts | 1 - .../src/lib/domains/meeting/index.ts | 1 - .../models/custom-participant.model.ts | 2 +- .../src/lib/domains/meeting/models/index.ts | 1 - .../src/lib/domains/meeting/pages/index.ts | 2 - .../src/lib/domains/meeting/services/index.ts | 1 - .../services/meeting-event-handler.service.ts | 2 +- .../meeting/services/meeting-lobby.service.ts | 4 +- .../domains/recordings/components/index.ts | 1 - .../src/lib/domains/recordings/index.ts | 1 - .../recordings/services/recording.service.ts | 37 ++-- .../src/lib/domains/rooms/components/index.ts | 1 - .../guards/room-validate-access.guard.ts | 3 +- .../src/lib/domains/rooms/index.ts | 1 - .../room-member-error-handler.service.ts | 10 +- .../rooms/services/room-member.service.ts | 29 ++- .../domains/rooms/services/room.service.ts | 179 ++++++++---------- .../services/feature-configuration.service.ts | 4 +- .../shared/services/global-config.service.ts | 28 +-- .../src/lib/shared/utils/array.utils.ts | 1 + 28 files changed, 242 insertions(+), 229 deletions(-) diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/interceptor-handlers/auth-error-handler.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/interceptor-handlers/auth-error-handler.service.ts index 23a922b8..f5ce1eea 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/interceptor-handlers/auth-error-handler.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/interceptor-handlers/auth-error-handler.service.ts @@ -2,7 +2,11 @@ import { HttpErrorResponse, HttpEvent } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; import { Router } from '@angular/router'; import { Observable, catchError, from, switchMap } from 'rxjs'; -import { HttpErrorContext, HttpErrorHandler, HttpErrorNotifierService } from '../../../shared/services/http-error-notifier.service'; +import { + HttpErrorContext, + HttpErrorHandler, + HttpErrorNotifierService +} from '../../../shared/services/http-error-notifier.service'; import { TokenStorageService } from '../../../shared/services/token-storage.service'; import { AuthService } from '../services/auth.service'; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/pages/login/login.component.html b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/pages/login/login.component.html index c5fd5d12..944f6b81 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/pages/login/login.component.html +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/pages/login/login.component.html @@ -13,8 +13,13 @@
- Username - + User ID + person diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/pages/login/login.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/pages/login/login.component.ts index 1fee7e8a..0a660b20 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/pages/login/login.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/pages/login/login.component.ts @@ -12,24 +12,24 @@ import { NavigationService } from '../../../../shared/services/navigation.servic import { AuthService } from '../../services/auth.service'; @Component({ - selector: 'ov-login', - imports: [ - MatFormFieldModule, - ReactiveFormsModule, - MatInputModule, - MatButtonModule, - FormsModule, - MatCardModule, - MatIconModule, - MatTooltipModule, - RouterModule - ], - templateUrl: './login.component.html', - styleUrl: './login.component.scss' + selector: 'ov-login', + imports: [ + MatFormFieldModule, + ReactiveFormsModule, + MatInputModule, + MatButtonModule, + FormsModule, + MatCardModule, + MatIconModule, + MatTooltipModule, + RouterModule + ], + templateUrl: './login.component.html', + styleUrl: './login.component.scss' }) export class LoginComponent implements OnInit { loginForm = new FormGroup({ - username: new FormControl('', [Validators.required]), + userId: new FormControl('', [Validators.required]), password: new FormControl('', [Validators.required]) }); @@ -54,16 +54,16 @@ export class LoginComponent implements OnInit { async login() { this.loginErrorMessage = undefined; - const { username, password } = this.loginForm.value; + const { userId, password } = this.loginForm.value; try { - await this.authService.login(username!, password!); + await this.authService.login(userId!, password!); await this.navigationService.redirectTo(this.redirectTo); } catch (error) { if ((error as HttpErrorResponse).status === 429) { this.loginErrorMessage = 'Too many login attempts. Please try again later'; } else { - this.loginErrorMessage = 'Invalid username or password'; + this.loginErrorMessage = 'Invalid user ID or password'; } } } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/services/auth.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/services/auth.service.ts index b8b398cf..2efa07f3 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/services/auth.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/services/auth.service.ts @@ -4,34 +4,54 @@ import { MeetUserDTO, MeetUserRole } from '@openvidu-meet/typings'; import { HttpService } from '../../../shared/services/http.service'; import { NavigationService } from '../../../shared/services/navigation.service'; import { TokenStorageService } from '../../../shared/services/token-storage.service'; +import { UserService } from '../../users/services/user.service'; @Injectable({ providedIn: 'root' }) export class AuthService { protected readonly AUTH_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/auth`; - protected readonly USERS_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/users`; + protected hasCheckAuth = false; protected user: MeetUserDTO | null = null; constructor( protected httpService: HttpService, + protected userService: UserService, protected tokenStorageService: TokenStorageService, protected navigationService: NavigationService ) {} - async login(username: string, password: string) { + /** + * Logs in a user with the provided credentials. + * + * @param userId - The unique identifier of the user + * @param password - The user's password + * @returns A promise that resolves when the login is successful + */ + async login(userId: string, password: string) { try { const path = `${this.AUTH_API}/login`; - const body = { username, password }; - const response = await this.httpService.postRequest(path, body); + const body = { userId, password }; + const response = await this.httpService.postRequest<{ + message: string; + accessToken: string; + refreshToken?: string; + mustChangePassword?: boolean; + }>(path, body); - // Check if we got tokens in the response (header mode) - if (response.accessToken && response.refreshToken) { - this.tokenStorageService.setAccessToken(response.accessToken); + // Save tokens in localStorage + this.tokenStorageService.setAccessToken(response.accessToken); + if (response.refreshToken) { this.tokenStorageService.setRefreshToken(response.refreshToken); } + // TODO: Redirect user to profile page in order to change password on first login if required by backend + // if (response.mustChangePassword) { + // this.navigationService.redirectToProfilePage(); + // return; + // } + await this.getAuthenticatedUser(true); } catch (err) { const error = err as HttpErrorResponse; @@ -40,6 +60,11 @@ export class AuthService { } } + /** + * Refreshes the access token using the refresh token. + * + * @returns A promise that resolves to the response from the refresh endpoint + */ async refreshToken() { const path = `${this.AUTH_API}/refresh`; const refreshToken = this.tokenStorageService.getRefreshToken(); @@ -61,6 +86,13 @@ export class AuthService { return response; } + /** + * Logs out the currently authenticated user and clears authentication tokens. + * Redirects to the login page after logout, optionally with a query parameter to redirect back after login. + * + * @param redirectToAfterLogin - Optional path to redirect to after login + * @returns A promise that resolves when the logout is successful + */ async logout(redirectToAfterLogin?: string) { try { const path = `${this.AUTH_API}/logout`; @@ -78,31 +110,67 @@ export class AuthService { } } + /** + * Checks if the user is authenticated by attempting to retrieve the authenticated user's information. + * + * @return A promise that resolves to true if the user is authenticated, false otherwise + */ async isUserAuthenticated(): Promise { await this.getAuthenticatedUser(); return !!this.user; } - async getUsername(): Promise { + /** + * Retrieves the authenticated user's ID. + * + * @return A promise that resolves to the user's ID if authenticated, undefined otherwise + */ + async getUserId(): Promise { await this.getAuthenticatedUser(); - return this.user?.username; + return this.user?.userId; } - async getUserRoles(): Promise { + /** + * Retrieves the authenticated user's name. + * + * @return A promise that resolves to the user's name if authenticated, undefined otherwise + */ + async getUserName(): Promise { await this.getAuthenticatedUser(); - return this.user?.roles; + return this.user?.name; } + /** + * Retrieves the authenticated user's role. + * + * @return A promise that resolves to the user's role if authenticated, undefined otherwise + */ + async getUserRole(): Promise { + await this.getAuthenticatedUser(); + return this.user?.role; + } + + /** + * Checks if the authenticated user has an admin role. + * + * @return A promise that resolves to true if the user is an admin, false otherwise + */ async isAdmin(): Promise { - const roles = await this.getUserRoles(); - return roles ? roles.includes(MeetUserRole.ADMIN) : false; + const role = await this.getUserRole(); + return role === MeetUserRole.ADMIN; } + /** + * Retrieves the authenticated user's information and caches it in the service. + * If the user information is already cached and force is not true, it returns immediately. + * If force is true, it will attempt to fetch the user information again from the server. + * + * @param force - If true, forces a refresh of the user information from the server + */ private async getAuthenticatedUser(force = false) { if (force || (!this.user && !this.hasCheckAuth)) { try { - const path = `${this.USERS_API}/profile`; - const user = await this.httpService.getRequest(path); + const user = await this.userService.getMe(); this.user = user; } catch (error) { this.user = null; @@ -111,9 +179,4 @@ export class AuthService { this.hasCheckAuth = true; } } - - async changePassword(currentPassword: string, newPassword: string): Promise { - const path = `${this.USERS_API}/change-password`; - return this.httpService.postRequest(path, { currentPassword, newPassword }); - } } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/components/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/components/index.ts index 6b8190aa..f1df2478 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/components/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/components/index.ts @@ -1 +1,2 @@ export * from './console-nav/console-nav.component'; +export * from './logo-selector/logo-selector.component'; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/components/logo-selector/logo-selector.component.scss b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/components/logo-selector/logo-selector.component.scss index b1d74af2..4b091400 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/components/logo-selector/logo-selector.component.scss +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/components/logo-selector/logo-selector.component.scss @@ -1,4 +1,4 @@ -@use '../../../../../../src/assets/styles/design-tokens'; +@use '../../../../../../../../src/assets/styles/design-tokens'; // Form styling .form-section { diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/index.ts index e3be5653..9d412aac 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/index.ts @@ -1,3 +1,2 @@ export * from './components'; export * from './pages'; - diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/pages/overview/overview.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/pages/overview/overview.component.ts index d47c8c20..73b896be 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/pages/overview/overview.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/pages/overview/overview.component.ts @@ -53,8 +53,4 @@ export class OverviewComponent implements OnInit { console.error(`Error navigating to ${section}:`, error); } } - - async refreshData() { - await this.loadStats(); - } } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/components/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/components/index.ts index 0630ad23..e091a8ce 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/components/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/components/index.ts @@ -2,4 +2,3 @@ export * from './hidden-participants-indicator/hidden-participants-indicator.com export * from './meeting-lobby/meeting-lobby.component'; export * from './meeting-share-link-overlay/meeting-share-link-overlay.component'; export * from './share-meeting-link/share-meeting-link.component'; - diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/index.ts index a3962816..9f8a0c90 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/index.ts @@ -4,4 +4,3 @@ 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/meeting/models/custom-participant.model.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/models/custom-participant.model.ts index ed62edd5..818efbc9 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/models/custom-participant.model.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/models/custom-participant.model.ts @@ -58,5 +58,5 @@ const extractParticipantRole = (metadata: any): MeetRoomMemberRole => { if (!parsedMetadata || typeof parsedMetadata !== 'object') { return MeetRoomMemberRole.SPEAKER; } - return parsedMetadata.role || MeetRoomMemberRole.SPEAKER; + return parsedMetadata.baseRole || MeetRoomMemberRole.SPEAKER; }; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/models/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/models/index.ts index 7959167a..c179358b 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/models/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/models/index.ts @@ -2,4 +2,3 @@ export * from './captions.model'; export * from './custom-participant.model'; export * from './layout.model'; export * from './lobby.model'; - diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/index.ts index 6225345c..473aefe7 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/index.ts @@ -1,4 +1,2 @@ export * from './end-meeting/end-meeting.component'; export * from './meeting/meeting.component'; - - diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/index.ts index 73494d1b..d0893829 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/index.ts @@ -5,4 +5,3 @@ export * from './meeting-layout.service'; export * from './meeting-lobby.service'; export * from './meeting-webcomponent-manager.service'; export * from './meeting.service'; - 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 ace01d5a..3bcbaed1 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 @@ -271,7 +271,7 @@ export class MeetingEventHandlerService { // Refresh participant token with new role await this.roomMemberService.generateToken(roomId, { secret, - grantJoinMeetingPermission: true, + joinMeeting: true, participantName, participantIdentity }); 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 ed20ecd7..f1d372fa 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 @@ -330,7 +330,7 @@ export class MeetingLobbyService { protected async initializeParticipantName(): Promise { // Apply participant name from RoomMemberService if set, otherwise use authenticated username const currentParticipantName = this.roomMemberService.getParticipantName(); - const username = await this.authService.getUsername(); + const username = await this.authService.getUserName(); const participantName = currentParticipantName || username; if (participantName) { @@ -351,7 +351,7 @@ export class MeetingLobbyService { roomId!, { secret: roomSecret!, - grantJoinMeetingPermission: true, + joinMeeting: true, participantName: this.participantName() }, this.e2eeKeyValue() diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/components/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/components/index.ts index 311a30a9..2f470705 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/components/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/components/index.ts @@ -1,4 +1,3 @@ export * from './recording-lists/recording-lists.component'; export * from './recording-share-dialog/recording-share-dialog.component'; export * from './recording-video-player/recording-video-player.component'; - diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/index.ts index 4ed759bc..bcd37985 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/index.ts @@ -3,4 +3,3 @@ export * from './guards'; export * from './models'; export * from './pages'; export * from './services'; - diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/services/recording.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/services/recording.service.ts index b6320212..b4c60ace 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/services/recording.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/services/recording.service.ts @@ -76,33 +76,18 @@ export class RecordingService { let path = this.RECORDINGS_API; if (filters) { - const params = new URLSearchParams(); - if (filters.roomId) { - params.append('roomId', filters.roomId); - } - if (filters.roomName) { - params.append('roomName', filters.roomName); - } - if (filters.status) { - params.append('status', filters.status); - } - if (filters.fields) { - params.append('fields', filters.fields); - } - if (filters.maxItems) { - params.append('maxItems', filters.maxItems.toString()); - } - if (filters.nextPageToken) { - params.append('nextPageToken', filters.nextPageToken); - } - if (filters.sortField) { - params.append('sortField', filters.sortField); - } - if (filters.sortOrder) { - params.append('sortOrder', filters.sortOrder); - } + const queryParams = new URLSearchParams(); - path += `?${params.toString()}`; + Object.entries(filters).forEach(([key, value]) => { + if (value) { + queryParams.set(key, value.toString()); + } + }); + + const queryString = queryParams.toString(); + if (queryString) { + path += `?${queryString}`; + } } return this.httpService.getRequest(path); diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/components/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/components/index.ts index 432e9404..25453d7b 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/components/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/components/index.ts @@ -2,4 +2,3 @@ export * from './delete-room-dialog/delete-room-dialog.component'; export * from './rooms-lists/rooms-lists.component'; export * from './step-indicator/step-indicator.component'; export * from './wizard-nav/wizard-nav.component'; - 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 7ce77e9b..31289093 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 @@ -51,7 +51,7 @@ const validateRoomAccessInternal = async (pageUrl: string, validateRecordingPerm try { await roomMemberService.generateToken(roomId, { secret, - grantJoinMeetingPermission: false + joinMeeting: false }); // Perform recording validation if requested @@ -81,4 +81,3 @@ const validateRoomAccessInternal = async (pageUrl: string, validateRecordingPerm } } }; - 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 07f676b5..8d6630d2 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 @@ -5,4 +5,3 @@ 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/interceptor-handlers/room-member-error-handler.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/interceptor-handlers/room-member-error-handler.service.ts index a275b858..c1d59465 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/rooms/interceptor-handlers/room-member-error-handler.service.ts @@ -1,7 +1,11 @@ import { HttpErrorResponse, HttpEvent } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; import { Observable, catchError, from, switchMap, throwError } from 'rxjs'; -import { HttpErrorContext, HttpErrorHandler, HttpErrorNotifierService } from '../../../shared/services/http-error-notifier.service'; +import { + HttpErrorContext, + HttpErrorHandler, + HttpErrorNotifierService +} 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'; @@ -75,12 +79,12 @@ export class RoomMemberInterceptorErrorHandlerService implements HttpErrorHandle const participantName = this.roomMemberService.getParticipantName(); const participantIdentity = this.roomMemberService.getParticipantIdentity(); - const grantJoinMeetingPermission = !!participantIdentity; // Grant join permission if identity is set + const joinMeeting = !!participantIdentity; // Grant join permission if identity is set return from( this.roomMemberService.generateToken(roomId, { secret, - grantJoinMeetingPermission, + joinMeeting, participantName, participantIdentity }) 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/rooms/services/room-member.service.ts index df8fe48a..8e21411b 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/rooms/services/room-member.service.ts @@ -15,6 +15,7 @@ import { getValidDecodedToken } from '../../../shared/utils/token.utils'; providedIn: 'root' }) export class RoomMemberService { + protected readonly ROOM_MEMBERS_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/rooms`; protected readonly PARTICIPANT_NAME_KEY = 'ovMeet-participantName'; protected participantName?: string; @@ -34,6 +35,10 @@ export class RoomMemberService { this.log = this.loggerService.get('OpenVidu Meet - ParticipantTokenService'); } + protected getRoomMemberApiPath(roomId: string): string { + return `${this.ROOM_MEMBERS_API}/${roomId}/members`; + } + setParticipantName(participantName: string): void { this.participantName = participantName; localStorage.setItem(this.PARTICIPANT_NAME_KEY, participantName); @@ -65,7 +70,7 @@ export class RoomMemberService { tokenOptions.participantName = encryptedName; } - const path = `${HttpService.INTERNAL_API_PATH_PREFIX}/rooms/${roomId}/token`; + const path = `${this.getRoomMemberApiPath(roomId)}/token`; const { token } = await this.httpService.postRequest<{ token: string }>(path, tokenOptions); this.tokenStorageService.setRoomMemberToken(token); @@ -90,11 +95,8 @@ export class RoomMemberService { this.participantIdentity = decodedToken.sub; } - this.role = metadata.role; - this.permissions = { - livekit: decodedToken.video, - meet: metadata.permissions - }; + this.role = metadata.baseRole; + this.permissions = metadata.effectivePermissions; // Update feature configuration this.featureConfService.setRoomMemberRole(this.role); @@ -105,17 +107,8 @@ export class RoomMemberService { } } - setRoomMemberRole(role: MeetRoomMemberRole): void { - this.role = role; - this.featureConfService.setRoomMemberRole(this.role); - } - - getRoomMemberRole(): MeetRoomMemberRole { - return this.role; - } - isModerator(): boolean { - return this.getRoomMemberRole() === MeetRoomMemberRole.MODERATOR; + return this.role === MeetRoomMemberRole.MODERATOR; } getRoomMemberPermissions(): MeetRoomMemberPermissions | undefined { @@ -123,10 +116,10 @@ export class RoomMemberService { } canRetrieveRecordings(): boolean { - return this.permissions?.meet.canRetrieveRecordings ?? false; + return this.permissions?.canRetrieveRecordings ?? false; } canDeleteRecordings(): boolean { - return this.permissions?.meet.canDeleteRecordings ?? false; + return this.permissions?.canDeleteRecordings ?? false; } } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/room.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/room.service.ts index 66925456..0625d944 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/room.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/room.service.ts @@ -1,40 +1,33 @@ import { inject, Injectable } from '@angular/core'; import { MeetRoom, + MeetRoomAnonymousConfig, MeetRoomConfig, MeetRoomDeletionPolicyWithMeeting, MeetRoomDeletionPolicyWithRecordings, MeetRoomDeletionSuccessCode, MeetRoomFilters, - MeetRoomMemberRoleAndPermissions, MeetRoomOptions, + MeetRoomRolesConfig, MeetRoomStatus } from '@openvidu-meet/typings'; import { ILogger, LoggerService } from 'openvidu-components-angular'; import { FeatureConfigurationService } from '../../../shared/services/feature-configuration.service'; import { HttpService } from '../../../shared/services/http.service'; -/** - * RoomService - Persistence Layer for Room Data - * - * This service acts as a PERSISTENCE LAYER for room-related data and CRUD operations. - * - * Responsibilities: - * - Persist room data (roomId, roomSecret) in SessionStorage for page refresh/reload - * - Automatically sync persisted data to MeetingContextService (Single Source of Truth) - * - Provide HTTP API methods for room CRUD operations - * - Load and update room configuration - */ @Injectable({ providedIn: 'root' }) export class RoomService { protected readonly ROOMS_API = `${HttpService.API_PATH_PREFIX}/rooms`; protected readonly INTERNAL_ROOMS_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/rooms`; + protected httpService: HttpService = inject(HttpService); protected loggerService: LoggerService = inject(LoggerService); protected featureConfService: FeatureConfigurationService = inject(FeatureConfigurationService); + protected log: ILogger = this.loggerService.get('OpenVidu Meet - RoomService'); + constructor() {} /** @@ -48,9 +41,9 @@ export class RoomService { } /** - * Lists rooms with optional filters for pagination and fields. + * Lists rooms with optional filters. * - * @param filters - Optional filters for pagination and fields + * @param filters - Optional filters * @return A promise that resolves to an object containing rooms and pagination info */ async listRooms(filters?: MeetRoomFilters): Promise<{ @@ -65,29 +58,17 @@ export class RoomService { if (filters) { const queryParams = new URLSearchParams(); - if (filters.roomName) { - queryParams.set('roomName', filters.roomName); - } - if (filters.status) { - queryParams.set('status', filters.status); - } - if (filters.fields) { - queryParams.set('fields', filters.fields); - } - if (filters.maxItems) { - queryParams.set('maxItems', filters.maxItems.toString()); - } - if (filters.nextPageToken) { - queryParams.set('nextPageToken', filters.nextPageToken); - } - if (filters.sortField) { - queryParams.set('sortField', filters.sortField); - } - if (filters.sortOrder) { - queryParams.set('sortOrder', filters.sortOrder); - } - path += `?${queryParams.toString()}`; + Object.entries(filters).forEach(([key, value]) => { + if (value) { + queryParams.set(key, value.toString()); + } + }); + + const queryString = queryParams.toString(); + if (queryString) { + path += `?${queryString}`; + } } return this.httpService.getRequest(path); @@ -104,6 +85,48 @@ export class RoomService { return this.httpService.getRequest(path); } + /** + * Retrieves the config for a specific room. + * + * @param roomId - The unique identifier of the room + * @return A promise that resolves to the MeetRoomConfig object + */ + async getRoomConfig(roomId: string): Promise { + const path = `${this.ROOMS_API}/${roomId}/config`; + return this.httpService.getRequest(path); + } + + /** + * Loads the room config and updates the feature configuration service. + * + * @param roomId - The unique identifier of the room + */ + async loadRoomConfig(roomId: string): Promise { + this.log.d('Fetching room config for roomId:', roomId); + + try { + const config = await this.getRoomConfig(roomId); + this.featureConfService.setRoomConfig(config); + this.log.d('Room config loaded:', config); + } catch (error) { + this.log.e('Error loading room config', error); + throw new Error('Failed to load room config'); + } + } + + /** + * Saves new room config. + * + * @param roomId - The unique identifier of the room + * @param config - The room config to be saved. + * @returns A promise that resolves when the config have been saved. + */ + async updateRoomConfig(roomId: string, config: Partial): Promise { + this.log.d('Saving room config', config); + const path = `${this.ROOMS_API}/${roomId}/config`; + await this.httpService.putRequest(path, { config }); + } + /** * Updates the status of a room. * @@ -116,6 +139,30 @@ export class RoomService { return this.httpService.putRequest(path, { status }); } + /** + * Updates the roles permissions of a room. + * + * @param roomId - The unique identifier of the room + * @param rolesConfig - The new roles configuration to be set + * @returns A promise that resolves when the roles configuration has been updated + */ + async updateRoomRoles(roomId: string, rolesConfig: MeetRoomRolesConfig): Promise { + const path = `${this.ROOMS_API}/${roomId}/roles`; + return this.httpService.putRequest(path, { roles: rolesConfig }); + } + + /** + * Updates the anonymous access configuration of a room. + * + * @param roomId - The unique identifier of the room + * @param anonymousConfig - The new anonymous access configuration to be set + * @returns A promise that resolves when the anonymous access configuration has been updated + */ + async updateRoomAnonymous(roomId: string, anonymousConfig: MeetRoomAnonymousConfig): Promise { + const path = `${this.ROOMS_API}/${roomId}/anonymous`; + return this.httpService.putRequest(path, { anonymous: anonymousConfig }); + } + /** * Deletes a room by its ID. * @@ -165,64 +212,4 @@ export class RoomService { const path = `${this.ROOMS_API}?${queryParams.toString()}`; return this.httpService.deleteRequest(path); } - - /** - * Retrieves the config for a specific room. - * - * @param roomId - The unique identifier of the room - * @return A promise that resolves to the MeetRoomConfig object - */ - async getRoomConfig(roomId: string): Promise { - this.log.d('Fetching room config for roomId:', roomId); - - try { - const path = `${this.ROOMS_API}/${roomId}/config`; - const config = await this.httpService.getRequest(path); - return config; - } catch (error) { - this.log.e('Error fetching room config', error); - throw new Error(`Failed to fetch room config for roomId: ${roomId}`); - } - } - - /** - * Loads the room config and updates the feature configuration service. - * - * @param roomId - The unique identifier of the room - */ - async loadRoomConfig(roomId: string): Promise { - try { - const config = await this.getRoomConfig(roomId); - this.featureConfService.setRoomConfig(config); - this.log.d('Room config loaded:', config); - } catch (error) { - this.log.e('Error loading room config', error); - throw new Error('Failed to load room config'); - } - } - - /** - * Saves new room config. - * - * @param roomId - The unique identifier of the room - * @param config - The room config to be saved. - * @returns A promise that resolves when the config have been saved. - */ - async updateRoomConfig(roomId: string, config: Partial): Promise { - this.log.d('Saving room config', config); - const path = `${this.ROOMS_API}/${roomId}/config`; - await this.httpService.putRequest(path, { config }); - } - - /** - * Retrieves the role and permissions for a specified room and secret. - * - * @param roomId - The unique identifier of the room - * @param secret - The secret parameter for the room - * @returns A promise that resolves to an object containing the role and permissions - */ - async getRoomMemberRoleAndPermissions(roomId: string, secret: string): Promise { - const path = `${this.INTERNAL_ROOMS_API}/${roomId}/roles/${secret}`; - return this.httpService.getRequest(path); - } } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/feature-configuration.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/feature-configuration.service.ts index 45797a15..104d10e2 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/feature-configuration.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/feature-configuration.service.ts @@ -181,10 +181,10 @@ export class FeatureConfigurationService { * DISABLED_WITH_WARNING: room config enabled BUT global config disabled */ protected computeCaptionsStatus( - roomCaptionsConfig: MeetRoomCaptionsConfig | undefined, + roomCaptionsConfig: MeetRoomCaptionsConfig, globalEnabled: boolean ): CaptionsStatus { - if (!roomCaptionsConfig?.enabled) { + if (!roomCaptionsConfig.enabled) { return 'HIDDEN'; } return globalEnabled ? 'ENABLED' : 'DISABLED_WITH_WARNING'; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/global-config.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/global-config.service.ts index b092e616..b9f14401 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/global-config.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/global-config.service.ts @@ -1,5 +1,5 @@ import { inject, Injectable } from '@angular/core'; -import { AuthMode, MeetAppearanceConfig, SecurityConfig, WebhookConfig } from '@openvidu-meet/typings'; +import { MeetAppearanceConfig, SecurityConfig, WebhookConfig } from '@openvidu-meet/typings'; import { ILogger, LoggerService } from 'openvidu-components-angular'; import { FeatureConfigurationService } from './feature-configuration.service'; import { HttpService } from './http.service'; @@ -9,37 +9,23 @@ import { HttpService } from './http.service'; }) export class GlobalConfigService { protected readonly GLOBAL_CONFIG_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/config`; - protected securityConfig?: SecurityConfig; + protected loggerService: LoggerService = inject(LoggerService); protected httpService: HttpService = inject(HttpService); protected featureConfService: FeatureConfigurationService = inject(FeatureConfigurationService); + protected log: ILogger = this.loggerService.get('OpenVidu Meet - GlobalConfigService'); + constructor() {} - async getSecurityConfig(forceRefresh = false): Promise { - if (this.securityConfig && !forceRefresh) { - return this.securityConfig; - } - - try { - const path = `${this.GLOBAL_CONFIG_API}/security`; - this.securityConfig = await this.httpService.getRequest(path); - return this.securityConfig; - } catch (error) { - this.log.e('Error fetching security config:', error); - throw error; - } - } - - async getAuthModeToAccessRoom(): Promise { - await this.getSecurityConfig(); - return this.securityConfig!.authentication.authModeToAccessRoom; + async getSecurityConfig(): Promise { + const path = `${this.GLOBAL_CONFIG_API}/security`; + return await this.httpService.getRequest(path); } async saveSecurityConfig(config: SecurityConfig) { const path = `${this.GLOBAL_CONFIG_API}/security`; await this.httpService.putRequest(path, config); - this.securityConfig = config; } async getWebhookConfig(): Promise { diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/array.utils.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/array.utils.ts index 48e18d21..3715b90e 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/array.utils.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/array.utils.ts @@ -5,6 +5,7 @@ export const setsAreEqual = (setA: Set, setB: Set): boolean => { } return true; }; + export const arraysAreEqual = (arrayA: T[], arrayB: T[]): boolean => { if (arrayA.length !== arrayB.length) return false; for (let i = 0; i < arrayA.length; i++) {