From 8604704a4976902a056d598b1a7876f8ba6ca024 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Fri, 14 Feb 2025 12:37:07 +0100 Subject: [PATCH] frontend: Added redirect url feature in standalone mode --- frontend/e2e/routes.test.ts | 37 ++++++++++++++--- frontend/e2e/utils.po.test.ts | 6 ++- .../src/lib/guards/redirect-url.guard.ts | 4 ++ .../video-room/video-room.component.html | 2 +- .../pages/video-room/video-room.component.ts | 40 ++++++++----------- .../src/lib/routes/base-routes.ts | 4 +- 6 files changed, 60 insertions(+), 33 deletions(-) diff --git a/frontend/e2e/routes.test.ts b/frontend/e2e/routes.test.ts index 05ce3ae4..2accff71 100644 --- a/frontend/e2e/routes.test.ts +++ b/frontend/e2e/routes.test.ts @@ -173,6 +173,18 @@ describe('Testing Standalone Mode', () => { await utils.removeIframe(); }); + it('should redirect to "unauthorized" if external URL and token provided without embedded path', async () => { + const redirectUrl = 'https://openvidu.io'; + const token = await utils.getJWTToken(); + await utils.buildIframeAndSwitch(`${url}/?token=${token}&redirectUrl=${redirectUrl}`); + + await utils.waitForElement('#unauthorized-content'); + + await utils.waitForElement('#error-reason'); + + await utils.removeIframe(); + }); + it('should redirect to home page if no token and room name are provided', async () => { await browser.get(url); expect(await browser.getCurrentUrl()).to.include('home'); @@ -181,21 +193,34 @@ describe('Testing Standalone Mode', () => { expect(await browser.getCurrentUrl()).to.include('home'); }); - it('should NOT redirect to a redirect URL if provided in standalone mode', async () => { - await browser.get(`${url}/room123?redirectUrl=https://openvidu.io`); - + it('should redirect to an external URL without token provided', async () => { + const redirectUrl = 'https://openvidu.io'; + await browser.get(`${url}/Room123?redirectUrl=${redirectUrl}`); await utils.checkPrejoinIsPresent(); - await utils.joinRoom(); + await utils.checkLayoutIsPresent(); + await utils.checkToolbarIsPresent(); + + await utils.leaveRoom(); + + const currentUrl = await browser.getCurrentUrl(); + expect(currentUrl).to.include(redirectUrl); + }); + + it('should redirect to an external URL with token provided', async () => { + const redirectUrl = 'https://openvidu.io'; + const token = await utils.getJWTToken(); + await browser.get(`${url}/?token=${token}&redirectUrl=${redirectUrl}`); await utils.checkLayoutIsPresent(); await utils.checkToolbarIsPresent(); await utils.leaveRoom(); - const redirectUrl = await browser.getCurrentUrl(); - expect(redirectUrl).to.include(url); + const currentUrl = await browser.getCurrentUrl(); + expect(currentUrl).to.include(redirectUrl); }); + }); describe('Testing Console Routes', () => { diff --git a/frontend/e2e/utils.po.test.ts b/frontend/e2e/utils.po.test.ts index 707564a7..98c1b5ba 100644 --- a/frontend/e2e/utils.po.test.ts +++ b/frontend/e2e/utils.po.test.ts @@ -51,7 +51,11 @@ export class OpenViduCallPO { await this.browser.executeScript(`document.getElementById('test-iframe')?.remove();`); } - async getJWTToken(roomName = 'TestRoom', participantName = 'TestParticipant'): Promise { + async getJWTToken(roomName?: string, participantName?: string): Promise { + if(!roomName) roomName = 'TestRoom-'+ Math.random().toString(36).substring(7); + + if(!participantName) participantName = 'ParticipantName-' + Math.random().toString(36).substring(7); + const token = await fetch('http://localhost:6080/v1/embedded/api/token', { method: 'POST', headers: { diff --git a/frontend/projects/shared-call-components/src/lib/guards/redirect-url.guard.ts b/frontend/projects/shared-call-components/src/lib/guards/redirect-url.guard.ts index 004cdfd7..b995fdf3 100644 --- a/frontend/projects/shared-call-components/src/lib/guards/redirect-url.guard.ts +++ b/frontend/projects/shared-call-components/src/lib/guards/redirect-url.guard.ts @@ -2,6 +2,10 @@ import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router'; import { ContextService } from '../services'; +/** + * Guard that checks for a 'redirectUrl' query parameter in the route. + * If the 'redirectUrl' parameter is valid, it sets the redirect URL in the context service. + */ export const redirectUrlGuard: CanActivateFn = async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { const contextService = inject(ContextService); const redirectUrlParameter = route.queryParams['redirectUrl']; diff --git a/frontend/projects/shared-call-components/src/lib/pages/video-room/video-room.component.html b/frontend/projects/shared-call-components/src/lib/pages/video-room/video-room.component.html index e482126a..e7235b3a 100644 --- a/frontend/projects/shared-call-components/src/lib/pages/video-room/video-room.component.html +++ b/frontend/projects/shared-call-components/src/lib/pages/video-room/video-room.component.html @@ -12,7 +12,7 @@ [toolbarBackgroundEffectsButton]="featureFlags.showBackgrounds" [toolbarActivitiesPanelButton]="featureFlags.showActivityPanel" (onTokenRequested)="onTokenRequested($event)" - (onParticipantLeft)="onParticipantLeft()" + (onParticipantLeft)="onParticipantLeft($event)" (onRecordingStartRequested)="onRecordingStartRequested($event)" (onRecordingStopRequested)="onRecordingStopRequested($event)" (onRecordingDeleteRequested)="onRecordingDeleteRequested($event)" diff --git a/frontend/projects/shared-call-components/src/lib/pages/video-room/video-room.component.ts b/frontend/projects/shared-call-components/src/lib/pages/video-room/video-room.component.ts index b634c333..a0fba1f6 100644 --- a/frontend/projects/shared-call-components/src/lib/pages/video-room/video-room.component.ts +++ b/frontend/projects/shared-call-components/src/lib/pages/video-room/video-room.component.ts @@ -8,7 +8,8 @@ import { RecordingStartRequestedEvent, RecordingStopRequestedEvent, OpenViduComponentsModule, - ApiDirectiveModule + ApiDirectiveModule, + ParticipantLeftEvent } from 'openvidu-components-angular'; import { @@ -92,30 +93,12 @@ export class VideoRoomComponent implements OnInit { this.cdr.detectChanges(); } - onParticipantLeft() { + onParticipantLeft(event: ParticipantLeftEvent) { console.warn('Participant left the room. Redirecting to:'); + const redirectURL = this.contextService.getRedirectURL() || '/'; + const isExternalURL = /^https?:\/\//.test(redirectURL); - if (this.contextService.isEmbeddedMode()) { - const redirectURL = this.contextService.getRedirectURL(); - if (redirectURL) { - // Check if the redirect URL is an external URL - const isExternalURL = /^https?:\/\//.test(redirectURL); - - if (isExternalURL) { - console.log('Redirecting to external URL:', redirectURL); - window.location.href = redirectURL; - } else { - console.log('Participant left the room. Redirecting to internal route:', redirectURL); - this.router.navigate([redirectURL], { replaceUrl: true }); - } - } else { - console.warn('Participant left the room. Redirecting to:', `/`); - this.router.navigate([`/`]); - } - return; - } - - this.router.navigate([`/`]); + this.redirectTo(redirectURL, isExternalURL); } async onRecordingStartRequested(event: RecordingStartRequestedEvent) { @@ -207,4 +190,15 @@ export class VideoRoomComponent implements OnInit { this.featureFlags.showPrejoin = false; } + + private redirectTo(url: string, isExternal: boolean) { + if (isExternal) { + console.log('Redirecting to external URL:', url); + window.location.href = url; + } else { + console.log('Redirecting to internal route:', url); + this.router.navigate([url], { replaceUrl: true }); + } + } + } diff --git a/frontend/projects/shared-call-components/src/lib/routes/base-routes.ts b/frontend/projects/shared-call-components/src/lib/routes/base-routes.ts index 520dca2c..0731a611 100644 --- a/frontend/projects/shared-call-components/src/lib/routes/base-routes.ts +++ b/frontend/projects/shared-call-components/src/lib/routes/base-routes.ts @@ -23,7 +23,7 @@ export const baseRoutes: Routes = [ }, { path: 'embedded/unauthorized', component: UnauthorizedComponent }, - { path: '', component: VideoRoomComponent, canActivate: [standaloneModeGuard, ensureValidTokenOrRoomNameGuard] }, + { path: '', component: VideoRoomComponent, canActivate: [standaloneModeGuard, ensureValidTokenOrRoomNameGuard, redirectUrlGuard] }, { path: 'login', component: ConsoleLoginComponent }, { path: 'console', @@ -44,7 +44,7 @@ export const baseRoutes: Routes = [ { path: ':roomName', component: VideoRoomComponent, - canActivate: [standaloneModeGuard, ensureValidTokenOrRoomNameGuard] + canActivate: [standaloneModeGuard, ensureValidTokenOrRoomNameGuard, redirectUrlGuard] }, // Redirect all other routes to home