frontend: enhance participant token management and update related services
- Renamed `setParticipantToken` to `setParticipantTokenAndUpdateContext` in ContextService to clarify its functionality. - Introduced ParticipantTokenService to encapsulate token generation logic and manage role/permissions extraction. - Updated VideoRoomComponent to utilize the new ParticipantTokenService for generating participant tokens. - Refactored access room method to improve form validation and participant name initialization. - Added unit tests for ParticipantTokenService to ensure proper functionality. - Updated sidenav model comments for clarity.
This commit is contained in:
parent
1cd58c19b9
commit
b2f1e2194a
@ -53,7 +53,7 @@ export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, ne
|
||||
return from(httpService.refreshParticipantToken({ roomId, participantName, secret })).pipe(
|
||||
switchMap((data) => {
|
||||
console.log('Participant token refreshed');
|
||||
contextService.setParticipantToken(data.token);
|
||||
contextService.setParticipantTokenAndUpdateContext(data.token);
|
||||
return next(req);
|
||||
}),
|
||||
catchError((error: HttpErrorResponse) => {
|
||||
@ -127,7 +127,7 @@ export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, ne
|
||||
// refresh the participant token
|
||||
return refreshParticipantToken(error);
|
||||
}
|
||||
|
||||
|
||||
// Expired access token
|
||||
if (!pageUrl.startsWith('/console/login') && !pageUrl.startsWith('/login')) {
|
||||
// If the error occurred in a page that is not the login page, refresh the access token
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
import { OpenViduMeetPermissions, ParticipantRole } from 'shared-meet-components';
|
||||
|
||||
export interface TokenGenerationResult {
|
||||
token: string; // The generated participant token
|
||||
role: ParticipantRole; // Role of the participant (e.g., 'moderator', 'publisher')
|
||||
permissions: OpenViduMeetPermissions; // List of permissions granted to the participant
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
export interface ConsoleNavLink {
|
||||
label: string; // Nombre del enlace
|
||||
icon?: string; // Icono opcional
|
||||
route?: string; // Ruta para la navegación (opcional)
|
||||
clickHandler?: () => void; // Función para manejar clics (opcional)
|
||||
label: string; // Link name
|
||||
icon?: string; // Optional icon
|
||||
route?: string; // Route for navigation (optional)
|
||||
clickHandler?: () => void; // Function to handle clicks (optional)
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<h2 class="form-title">
|
||||
Access room <strong>{{ roomId }}</strong>
|
||||
</h2>
|
||||
<form [formGroup]="participantForm" (ngSubmit)="accessRoom()">
|
||||
<form [formGroup]="participantForm" (ngSubmit)="submitAccessRoom()">
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>Name</mat-label>
|
||||
<input
|
||||
|
||||
@ -26,6 +26,7 @@ import {
|
||||
SessionStorageService,
|
||||
WebComponentManagerService
|
||||
} from '../../services';
|
||||
import { ParticipantTokenService } from '@lib/services/participant-token/participant-token.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-video-room',
|
||||
@ -83,6 +84,7 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
|
||||
|
||||
constructor(
|
||||
protected httpService: HttpService,
|
||||
protected participantTokenService: ParticipantTokenService,
|
||||
protected router: Router,
|
||||
protected route: ActivatedRoute,
|
||||
protected location: Location,
|
||||
@ -99,32 +101,28 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
|
||||
const storageSecret = this.sessionStorageService.getModeratorSecret(this.roomId);
|
||||
this.roomSecret = storageSecret || secret;
|
||||
|
||||
// Apply participant name from context if set, otherwise use authenticated username
|
||||
const contextParticipantName = this.ctxService.getParticipantName();
|
||||
const username = await this.authService.getUsername();
|
||||
const participantName = contextParticipantName || username;
|
||||
|
||||
if (participantName) {
|
||||
this.participantForm.get('name')?.setValue(participantName);
|
||||
}
|
||||
await this.initializeParticipantName();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.wcManagerService.stopCommandsListener();
|
||||
}
|
||||
|
||||
async accessRoom() {
|
||||
if (!this.participantForm.valid) {
|
||||
async submitAccessRoom() {
|
||||
const { valid, value } = this.participantForm;
|
||||
if (!valid || !value.name?.trim()) {
|
||||
// If the form is invalid, do not proceed
|
||||
console.warn('Participant form is invalid. Cannot access room.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.participantName = this.participantForm.value.name!;
|
||||
this.participantName = value.name.trim();
|
||||
|
||||
try {
|
||||
await this.generateParticipantToken();
|
||||
await this.replaceUrlQueryParams();
|
||||
// await this.loadRoomPreferences();
|
||||
this.applyParticipantPermissions();
|
||||
await this.loadRoomPreferences();
|
||||
this.updateFeatureConfiguration();
|
||||
this.showRoom = true;
|
||||
} catch (error) {
|
||||
console.error('Error accessing room:', error);
|
||||
@ -136,14 +134,44 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
|
||||
this.participantToken = this.ctxService.getParticipantToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the participant name in the form control.
|
||||
*
|
||||
* Retrieves the participant name from the context service first, and if not available,
|
||||
* falls back to the authenticated username. Sets the retrieved name value in the
|
||||
* participant form's 'name' control if a valid name is found.
|
||||
*
|
||||
* @private
|
||||
* @async
|
||||
* @returns {Promise<void>} A promise that resolves when the participant name has been initialized
|
||||
*/
|
||||
private async initializeParticipantName() {
|
||||
// Apply participant name from context if set, otherwise use authenticated username
|
||||
const contextParticipantName = this.ctxService.getParticipantName();
|
||||
const username = await this.authService.getUsername();
|
||||
const participantName = contextParticipantName || username;
|
||||
|
||||
if (participantName) {
|
||||
this.participantForm.get('name')?.setValue(participantName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a participant token for joining a video room.
|
||||
*
|
||||
* @throws {Error} When participant already exists in the room (status 409)
|
||||
* @returns {Promise<void>} Promise that resolves when token is generated and set, or rejects on participant conflict
|
||||
*/
|
||||
private async generateParticipantToken() {
|
||||
try {
|
||||
const response = await this.httpService.generateParticipantToken({
|
||||
roomId: this.roomId,
|
||||
participantName: this.participantName,
|
||||
secret: this.roomSecret
|
||||
});
|
||||
this.setParticipantToken(response.token);
|
||||
const { token, role, permissions } = await this.participantTokenService.generateToken(
|
||||
this.roomId,
|
||||
this.participantName,
|
||||
this.roomSecret
|
||||
);
|
||||
this.participantToken = token;
|
||||
this.participantRole = role;
|
||||
this.participantPermissions = permissions;
|
||||
} catch (error: any) {
|
||||
console.error('Error generating participant token:', error);
|
||||
switch (error.status) {
|
||||
@ -166,16 +194,6 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private setParticipantToken(token: string): void {
|
||||
try {
|
||||
this.ctxService.setParticipantToken(token);
|
||||
this.participantRole = this.ctxService.getParticipantRole();
|
||||
this.participantPermissions = this.ctxService.getParticipantPermissions();
|
||||
} catch (error: any) {
|
||||
console.error('Error setting token in context', error);
|
||||
}
|
||||
}
|
||||
|
||||
private async replaceUrlQueryParams() {
|
||||
let secretQueryParam = this.roomSecret;
|
||||
|
||||
|
||||
@ -119,15 +119,19 @@ export class ContextService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the token for the current session.
|
||||
* @param token - A string representing the token.
|
||||
* Sets the participant token in the context and updates feature configuration.
|
||||
* @param token - The JWT token to set.
|
||||
* @throws Error if the token is invalid or expired.
|
||||
*/
|
||||
setParticipantToken(token: string): void {
|
||||
setParticipantTokenAndUpdateContext(token: string): void {
|
||||
try {
|
||||
const decodedToken = this.getValidDecodedToken(token);
|
||||
this.context.participantToken = token;
|
||||
this.context.participantPermissions = decodedToken.metadata.permissions;
|
||||
this.context.participantRole = decodedToken.metadata.role;
|
||||
|
||||
// Update feature configuration based on the new token
|
||||
this.updateFeatureConfiguration();
|
||||
} catch (error: any) {
|
||||
this.log.e('Error setting token in context', error);
|
||||
throw new Error('Error setting token', error);
|
||||
|
||||
@ -6,3 +6,4 @@ export * from './room/room.service';
|
||||
export * from './notification/notification.service';
|
||||
export * from './webcomponent-manager/webcomponent-manager.service';
|
||||
export * from './session-storage/session-storage.service';
|
||||
export * from './participant-token/participant-token.service';
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ParticipantTokenService } from './participant-token.service';
|
||||
|
||||
describe('ParticipantTokenService', () => {
|
||||
let service: ParticipantTokenService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ParticipantTokenService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TokenGenerationResult } from '@lib/models/auth.model';
|
||||
import { HttpService, ContextService, SessionStorageService } from 'shared-meet-components';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ParticipantTokenService {
|
||||
constructor(
|
||||
private httpService: HttpService,
|
||||
private ctxService: ContextService,
|
||||
private sessionStorageService: SessionStorageService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Generates a participant token and extracts role/permissions
|
||||
*/
|
||||
async generateToken(roomId: string, participantName: string, secret: string): Promise<TokenGenerationResult> {
|
||||
const response = await this.httpService.generateParticipantToken({
|
||||
roomId,
|
||||
participantName,
|
||||
secret
|
||||
});
|
||||
|
||||
this.ctxService.setParticipantTokenAndUpdateContext(response.token);
|
||||
|
||||
return {
|
||||
token: response.token,
|
||||
role: this.ctxService.getParticipantRole(),
|
||||
permissions: this.ctxService.getParticipantPermissions()
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user