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
This commit is contained in:
juancarmore 2026-02-06 13:53:06 +01:00
parent ae4217a4d4
commit 6c3d87c8bf
28 changed files with 242 additions and 229 deletions

View File

@ -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';

View File

@ -13,8 +13,13 @@
<mat-card-content>
<form [formGroup]="loginForm" (ngSubmit)="login()" id="login-form">
<mat-form-field class="form-input" appearance="outline">
<mat-label>Username</mat-label>
<input matInput formControlName="username" placeholder="Enter your username" id="username-input" />
<mat-label>User ID</mat-label>
<input
matInput
formControlName="userId"
placeholder="Enter your user ID"
id="userId-input"
/>
<mat-icon matPrefix>person</mat-icon>
</mat-form-field>

View File

@ -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';
}
}
}

View File

@ -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<any>(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<boolean> {
await this.getAuthenticatedUser();
return !!this.user;
}
async getUsername(): Promise<string | undefined> {
/**
* Retrieves the authenticated user's ID.
*
* @return A promise that resolves to the user's ID if authenticated, undefined otherwise
*/
async getUserId(): Promise<string | undefined> {
await this.getAuthenticatedUser();
return this.user?.username;
return this.user?.userId;
}
async getUserRoles(): Promise<MeetUserRole[] | undefined> {
/**
* Retrieves the authenticated user's name.
*
* @return A promise that resolves to the user's name if authenticated, undefined otherwise
*/
async getUserName(): Promise<string | undefined> {
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<MeetUserRole | undefined> {
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<boolean> {
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<MeetUserDTO>(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<any> {
const path = `${this.USERS_API}/change-password`;
return this.httpService.postRequest(path, { currentPassword, newPassword });
}
}

View File

@ -1 +1,2 @@
export * from './console-nav/console-nav.component';
export * from './logo-selector/logo-selector.component';

View File

@ -1,4 +1,4 @@
@use '../../../../../../src/assets/styles/design-tokens';
@use '../../../../../../../../src/assets/styles/design-tokens';
// Form styling
.form-section {

View File

@ -1,3 +1,2 @@
export * from './components';
export * from './pages';

View File

@ -53,8 +53,4 @@ export class OverviewComponent implements OnInit {
console.error(`Error navigating to ${section}:`, error);
}
}
async refreshData() {
await this.loadStats();
}
}

View File

@ -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';

View File

@ -4,4 +4,3 @@ export * from './models';
export * from './pages';
export * from './providers';
export * from './services';

View File

@ -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;
};

View File

@ -2,4 +2,3 @@ export * from './captions.model';
export * from './custom-participant.model';
export * from './layout.model';
export * from './lobby.model';

View File

@ -1,4 +1,2 @@
export * from './end-meeting/end-meeting.component';
export * from './meeting/meeting.component';

View File

@ -5,4 +5,3 @@ export * from './meeting-layout.service';
export * from './meeting-lobby.service';
export * from './meeting-webcomponent-manager.service';
export * from './meeting.service';

View File

@ -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
});

View File

@ -330,7 +330,7 @@ export class MeetingLobbyService {
protected async initializeParticipantName(): Promise<void> {
// 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()

View File

@ -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';

View File

@ -3,4 +3,3 @@ export * from './guards';
export * from './models';
export * from './pages';
export * from './services';

View File

@ -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);

View File

@ -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';

View File

@ -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
}
}
};

View File

@ -5,4 +5,3 @@ export * from './models';
export * from './pages';
export * from './providers';
export * from './services';

View File

@ -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
})

View File

@ -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;
}
}

View File

@ -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<MeetRoomConfig> {
const path = `${this.ROOMS_API}/${roomId}/config`;
return this.httpService.getRequest<MeetRoomConfig>(path);
}
/**
* Loads the room config and updates the feature configuration service.
*
* @param roomId - The unique identifier of the room
*/
async loadRoomConfig(roomId: string): Promise<void> {
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<MeetRoomConfig>): Promise<void> {
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<void> {
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<void> {
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<MeetRoomConfig> {
this.log.d('Fetching room config for roomId:', roomId);
try {
const path = `${this.ROOMS_API}/${roomId}/config`;
const config = await this.httpService.getRequest<MeetRoomConfig>(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<void> {
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<MeetRoomConfig>): Promise<void> {
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<MeetRoomMemberRoleAndPermissions> {
const path = `${this.INTERNAL_ROOMS_API}/${roomId}/roles/${secret}`;
return this.httpService.getRequest(path);
}
}

View File

@ -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';

View File

@ -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<SecurityConfig> {
if (this.securityConfig && !forceRefresh) {
return this.securityConfig;
}
try {
const path = `${this.GLOBAL_CONFIG_API}/security`;
this.securityConfig = await this.httpService.getRequest<SecurityConfig>(path);
return this.securityConfig;
} catch (error) {
this.log.e('Error fetching security config:', error);
throw error;
}
}
async getAuthModeToAccessRoom(): Promise<AuthMode> {
await this.getSecurityConfig();
return this.securityConfig!.authentication.authModeToAccessRoom;
async getSecurityConfig(): Promise<SecurityConfig> {
const path = `${this.GLOBAL_CONFIG_API}/security`;
return await this.httpService.getRequest<SecurityConfig>(path);
}
async saveSecurityConfig(config: SecurityConfig) {
const path = `${this.GLOBAL_CONFIG_API}/security`;
await this.httpService.putRequest(path, config);
this.securityConfig = config;
}
async getWebhookConfig(): Promise<WebhookConfig> {

View File

@ -5,6 +5,7 @@ export const setsAreEqual = <T>(setA: Set<T>, setB: Set<T>): boolean => {
}
return true;
};
export const arraysAreEqual = <T>(arrayA: T[], arrayB: T[]): boolean => {
if (arrayA.length !== arrayB.length) return false;
for (let i = 0; i < arrayA.length; i++) {