Carlos Santos b055ef0333 update file exclusion patterns in workspace settings
webcomponent: Added missing and necessary js file

Update .gitignore to specify backend public directory exclusion

webcomponent: Add error handling for invalid base URL in OpenViduMeet component

webcomponent: Update Jest configuration for improved testing setup

webcomponent: Enhance iframe attribute tests and add support for optional query parameters

webcomponent: Refactor documentation copying in build_webcomponent_doc function for improved readability and add absolute path resolution

Add E2EE_KEY property to WebComponentProperty enum for end-to-end encryption support

meet.sh: Enhance build_rest_api_doc function with output file handling and user confirmation for overwriting

frontend: replace removeRoomSecretGuard with removeQueryParamsGuard for enhanced query parameter management

frontend: add E2EE key handling in room service and update query params guard

Updated pnpm-lock.yaml

Enables end-to-end encryption (E2EE)

Adds E2EE functionality to meeting rooms.

Significant changes:
- Allows encryption of the participant name
- Introduces setting and getting E2EE keys
- Ensures recording is disabled when encryption is enabled

webcomponent: Added e2e test for checking the e2ee funcionality

frontend: Sanitize participant name before request for a token

fix: clean up formatting in openvidu-meet.code-workspace
2025-11-10 17:54:33 +01:00

142 lines
4.4 KiB
TypeScript

import { inject } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivateFn } from '@angular/router';
import { ErrorReason } from '../models';
import { AppDataService, NavigationService, ParticipantService, RoomService, SessionStorageService } from '../services';
import { WebComponentProperty } from '@openvidu-meet/typings';
export const extractRoomQueryParamsGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {
const navigationService = inject(NavigationService);
const roomService = inject(RoomService);
const participantService = inject(ParticipantService);
const sessionStorageService = inject(SessionStorageService);
const {
roomId,
secret: querySecret,
participantName,
leaveRedirectUrl,
showOnlyRecordings,
e2eeKey
} = extractParams(route);
const secret = querySecret || sessionStorageService.getRoomSecret();
// Handle leave redirect URL logic
handleLeaveRedirectUrl(leaveRedirectUrl);
if (!secret) {
// If no secret is provided, redirect to the error page
return navigationService.redirectToErrorPage(ErrorReason.MISSING_ROOM_SECRET);
}
roomService.setRoomId(roomId);
roomService.setRoomSecret(secret);
roomService.setE2EEKey(e2eeKey);
if (participantName) {
participantService.setParticipantName(participantName);
}
if (showOnlyRecordings === 'true') {
// Redirect to the room recordings page
return navigationService.createRedirectionTo(`room/${roomId}/recordings`, { secret });
}
return true;
};
export const extractRecordingQueryParamsGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {
const navigationService = inject(NavigationService);
const roomService = inject(RoomService);
const sessionStorageService = inject(SessionStorageService);
const { roomId, secret: querySecret } = extractParams(route);
const secret = querySecret || sessionStorageService.getRoomSecret();
if (!secret) {
// If no secret is provided, redirect to the error page
return navigationService.redirectToErrorPage(ErrorReason.MISSING_ROOM_SECRET);
}
roomService.setRoomId(roomId);
roomService.setRoomSecret(secret);
return true;
};
const extractParams = ({ params, queryParams }: ActivatedRouteSnapshot) => ({
roomId: params['room-id'] as string,
secret: queryParams['secret'] as string,
participantName: queryParams[WebComponentProperty.PARTICIPANT_NAME] as string,
leaveRedirectUrl: queryParams[WebComponentProperty.LEAVE_REDIRECT_URL] as string,
showOnlyRecordings: (queryParams[WebComponentProperty.SHOW_ONLY_RECORDINGS] as string) || 'false',
e2eeKey: queryParams[WebComponentProperty.E2EE_KEY] as string
});
/**
* Handles the leave redirect URL logic with automatic referrer detection
*/
const handleLeaveRedirectUrl = (leaveRedirectUrl: string | undefined) => {
const navigationService = inject(NavigationService);
const appDataService = inject(AppDataService);
const isEmbeddedMode = appDataService.isEmbeddedMode();
// Explicit valid URL provided - use as is
if (leaveRedirectUrl && isValidUrl(leaveRedirectUrl)) {
navigationService.setLeaveRedirectUrl(leaveRedirectUrl);
return;
}
// Absolute path provided in embedded mode - construct full URL based on parent origin
if (isEmbeddedMode && leaveRedirectUrl?.startsWith('/')) {
const parentUrl = document.referrer;
const parentOrigin = new URL(parentUrl).origin;
navigationService.setLeaveRedirectUrl(parentOrigin + leaveRedirectUrl);
return;
}
// Auto-detect from referrer (only if no explicit URL provided and not embedded)
if (!leaveRedirectUrl && !isEmbeddedMode) {
const autoRedirectUrl = getAutoRedirectUrl();
if (autoRedirectUrl) {
navigationService.setLeaveRedirectUrl(autoRedirectUrl);
}
}
};
/**
* Automatically detects if user came from another domain and returns appropriate redirect URL
*/
const getAutoRedirectUrl = (): string | null => {
try {
const referrer = document.referrer;
// No referrer means user typed URL directly or came from bookmark
if (!referrer) {
return null;
}
const referrerUrl = new URL(referrer);
const currentUrl = new URL(window.location.href);
// Check if referrer is from a different domain
if (referrerUrl.origin !== currentUrl.origin) {
console.log(`Auto-configuring leave redirect to referrer: ${referrer}`);
return referrer;
}
return null;
} catch (error) {
console.warn('Error detecting auto redirect URL:', error);
return null;
}
};
const isValidUrl = (url: string) => {
try {
new URL(url);
return true;
} catch (error) {
return false;
}
};