From 082aa8480c167b8479b266cf0d4d17f1a3130e7a Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Wed, 14 Jan 2026 13:57:03 +0100 Subject: [PATCH] frontend: Configures the application routing Sets up domain-based routing for different app features. This change introduces a structured approach to managing application routes, making it easier to add, modify, and maintain different sections of the application. It configures routes for authentication, meetings, rooms, recordings, and the console. --- .../lib/domains/auth/routes/auth.routes.ts | 15 ++ .../pages/console/console.component.ts | 40 +++-- .../domains/console/routes/console.routes.ts | 92 +++++++++++ .../domains/meeting/routes/meeting.routes.ts | 31 ++++ .../recordings/routes/recordings.routes.ts | 35 +++++ .../lib/domains/rooms/routes/rooms.routes.ts | 59 +++++++ .../lib/shared/models/domain-routes.model.ts | 39 +++++ .../src/lib/shared/models/index.ts | 3 + .../src/lib/shared/routes/base-routes.ts | 147 +++--------------- 9 files changed, 313 insertions(+), 148 deletions(-) create mode 100644 meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/routes/auth.routes.ts create mode 100644 meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/routes/console.routes.ts create mode 100644 meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/routes/meeting.routes.ts create mode 100644 meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/routes/recordings.routes.ts create mode 100644 meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/routes/rooms.routes.ts create mode 100644 meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/domain-routes.model.ts diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/routes/auth.routes.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/routes/auth.routes.ts new file mode 100644 index 00000000..5802e9a3 --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/auth/routes/auth.routes.ts @@ -0,0 +1,15 @@ +import { DomainRouteConfig } from '../../../shared/models/domain-routes.model'; +import { checkUserNotAuthenticatedGuard } from '../guards/auth.guard'; + +/** + * Auth domain route configurations + */ +export const authDomainRoutes: DomainRouteConfig[] = [ + { + route: { + path: 'login', + loadComponent: () => import('../pages/login/login.component').then((m) => m.LoginComponent), + canActivate: [checkUserNotAuthenticatedGuard] + } + } +]; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/pages/console/console.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/pages/console/console.component.ts index b1e83bfb..adb3b0d4 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/pages/console/console.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/pages/console/console.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { ConsoleNavLink } from '../../../../shared/models/sidenav.model'; import { AuthService } from '../../../auth/services/auth.service'; import { ConsoleNavComponent } from '../../components/console-nav/console-nav.component'; +import { consoleChildRoutes } from '../../routes/console.routes'; @Component({ selector: 'ov-console', @@ -10,28 +11,25 @@ import { ConsoleNavComponent } from '../../components/console-nav/console-nav.co styleUrl: './console.component.scss' }) export class ConsoleComponent { - navLinks: ConsoleNavLink[] = [ - { label: 'Overview', route: 'overview', icon: 'dashboard' }, - { label: 'Rooms', route: 'rooms', icon: 'video_chat', iconClass: 'ov-room-icon' }, - { label: 'Recordings', route: 'recordings', icon: 'video_library', iconClass: 'ov-recording-icon' }, - { - label: 'Embedded', - route: 'embedded', - icon: 'code_blocks', - iconClass: 'material-symbols-outlined ov-developer-icon' - }, - { - label: 'Users & Permissions', - route: 'users-permissions', - icon: 'passkey', - iconClass: 'ov-users-permissions material-symbols-outlined' - }, - { label: 'Configuration', route: 'config', icon: 'settings', iconClass: 'ov-settings-icon' } + navLinks: ConsoleNavLink[]; - // { label: 'About', route: 'about', icon: 'info' } - ]; - - constructor(private authService: AuthService) {} + constructor(private authService: AuthService) { + // Build navigation links from console child route configurations + this.navLinks = consoleChildRoutes + .filter((config) => config.navMetadata) // Only include routes with navigation metadata + .map((config) => ({ + label: config.navMetadata!.label, + route: config.navMetadata!.route, + icon: config.navMetadata!.icon, + iconClass: config.navMetadata?.iconClass + })) + .sort((a, b) => { + // Sort by order if available + const orderA = consoleChildRoutes.find((r) => r.navMetadata?.route === a.route)?.navMetadata?.order ?? 999; + const orderB = consoleChildRoutes.find((r) => r.navMetadata?.route === b.route)?.navMetadata?.order ?? 999; + return orderA - orderB; + }); + } async logout() { await this.authService.logout(); diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/routes/console.routes.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/routes/console.routes.ts new file mode 100644 index 00000000..cd7ff610 --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/console/routes/console.routes.ts @@ -0,0 +1,92 @@ +import { Route } from '@angular/router'; +import { DomainRouteConfig } from '../../../shared/models/domain-routes.model'; +import { checkUserAuthenticatedGuard } from '../../auth/guards/auth.guard'; +import { recordingsConsoleRoutes } from '../../recordings/routes/recordings.routes'; +import { roomsConsoleRoutes } from '../../rooms/routes/rooms.routes'; + +/** + * All console child routes configuration (includes console pages + rooms + recordings) + * Used by ConsoleComponent to build navigation links + */ +export const consoleChildRoutes: DomainRouteConfig[] = [ + { + route: { + path: 'overview', + loadComponent: () => import('../pages/overview/overview.component').then((m) => m.OverviewComponent) + }, + navMetadata: { + label: 'Overview', + route: 'overview', + icon: 'dashboard', + order: 1 + } + }, + ...roomsConsoleRoutes, + ...recordingsConsoleRoutes, + { + route: { + path: 'embedded', + loadComponent: () => import('../pages/embedded/embedded.component').then((m) => m.EmbeddedComponent) + }, + navMetadata: { + label: 'Embedded', + route: 'embedded', + icon: 'code_blocks', + iconClass: 'material-symbols-outlined ov-developer-icon', + order: 4 + } + }, + { + route: { + path: 'users-permissions', + loadComponent: () => + import('../pages/users-permissions/users-permissions.component').then((m) => m.UsersPermissionsComponent) + }, + navMetadata: { + label: 'Users & Permissions', + route: 'users-permissions', + icon: 'passkey', + iconClass: 'ov-users-permissions material-symbols-outlined', + order: 5 + } + }, + { + route: { + path: 'config', + loadComponent: () => import('../pages/config/config.component').then((m) => m.ConfigComponent) + }, + navMetadata: { + label: 'Configuration', + route: 'config', + icon: 'settings', + iconClass: 'ov-settings-icon', + order: 6 + } + } +]; + +/** + * Console domain routes (error page + authenticated console shell with all child routes) + * Used by base-routes.ts + */ +export const consoleDomainRoutes: Route[] = [ + { + path: 'error', + loadComponent: () => import('../pages/error/error.component').then((m) => m.ErrorComponent) + }, + { + path: '', + loadComponent: () => import('../pages/console/console.component').then((m) => m.ConsoleComponent), + canActivate: [checkUserAuthenticatedGuard], + children: [ + { + path: '', + redirectTo: 'overview', + pathMatch: 'full' + }, + ...consoleChildRoutes.map((config) => config.route), + { path: '**', redirectTo: 'overview' } + ] + } +]; + diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/routes/meeting.routes.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/routes/meeting.routes.ts new file mode 100644 index 00000000..e8fd20fa --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/routes/meeting.routes.ts @@ -0,0 +1,31 @@ +import { WebComponentProperty } from '@openvidu-meet/typings'; +import { extractRoomQueryParamsGuard } from '../../../shared/guards/extract-query-params.guard'; +import { removeQueryParamsGuard } from '../../../shared/guards/remove-query-params.guard'; +import { runGuardsSerially } from '../../../shared/guards/run-serially.guard'; +import { DomainRouteConfig } from '../../../shared/models/domain-routes.model'; +import { validateRoomAccessGuard } from '../../rooms/guards/room-validate-access.guard'; + +/** + * Meeting domain route configurations + */ +export const meetingDomainRoutes: DomainRouteConfig[] = [ + { + route: { + path: 'room/:room-id', + loadComponent: () => import('../pages/meeting/meeting.component').then((m) => m.MeetingComponent), + canActivate: [ + runGuardsSerially( + extractRoomQueryParamsGuard, + validateRoomAccessGuard, + removeQueryParamsGuard(['secret', WebComponentProperty.E2EE_KEY]) + ) + ] + } + }, + { + route: { + path: 'disconnected', + loadComponent: () => import('../pages/end-meeting/end-meeting.component').then((m) => m.EndMeetingComponent) + } + } +]; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/routes/recordings.routes.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/routes/recordings.routes.ts new file mode 100644 index 00000000..c805d390 --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/routes/recordings.routes.ts @@ -0,0 +1,35 @@ +import { DomainRouteConfig } from '../../../shared/models/domain-routes.model'; +import { validateRecordingAccessGuard } from '../guards/recording-validate-access.guard'; + +/** + * Recordings domain public route configurations + */ +export const recordingsDomainRoutes: DomainRouteConfig[] = [ + { + route: { + path: 'recording/:recording-id', + loadComponent: () => + import('../pages/view-recording/view-recording.component').then((m) => m.ViewRecordingComponent), + canActivate: [validateRecordingAccessGuard] + } + } +]; + +/** + * Console child routes for recordings domain + */ +export const recordingsConsoleRoutes: DomainRouteConfig[] = [ + { + route: { + path: 'recordings', + loadComponent: () => import('../pages/recordings/recordings.component').then((m) => m.RecordingsComponent) + }, + navMetadata: { + label: 'Recordings', + route: 'recordings', + icon: 'video_library', + iconClass: 'ov-recording-icon', + order: 3 + } + } +]; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/routes/rooms.routes.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/routes/rooms.routes.ts new file mode 100644 index 00000000..f6a61da9 --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/routes/rooms.routes.ts @@ -0,0 +1,59 @@ +import { WebComponentProperty } from '@openvidu-meet/typings'; +import { extractRecordingQueryParamsGuard } from '../../../shared/guards/extract-query-params.guard'; +import { removeQueryParamsGuard } from '../../../shared/guards/remove-query-params.guard'; +import { runGuardsSerially } from '../../../shared/guards/run-serially.guard'; +import { DomainRouteConfig } from '../../../shared/models/domain-routes.model'; +import { checkEditableRoomGuard } from '../guards/room-edit-check.guard'; +import { validateRoomRecordingsAccessGuard } from '../guards/room-validate-access.guard'; + +/** + * Rooms domain route configurations + */ +export const roomsDomainRoutes: DomainRouteConfig[] = [ + { + route: { + path: 'room/:room-id/recordings', + loadComponent: () => + import('../pages/room-recordings/room-recordings.component').then((m) => m.RoomRecordingsComponent), + canActivate: [ + runGuardsSerially( + extractRecordingQueryParamsGuard, + validateRoomRecordingsAccessGuard, + removeQueryParamsGuard(['secret', WebComponentProperty.E2EE_KEY]) + ) + ] + } + } +]; + +/** + * Console child routes for rooms domain + */ +export const roomsConsoleRoutes: DomainRouteConfig[] = [ + { + route: { + path: 'rooms', + loadComponent: () => import('../pages/rooms/rooms.component').then((m) => m.RoomsComponent) + }, + navMetadata: { + label: 'Rooms', + route: 'rooms', + icon: 'video_chat', + iconClass: 'ov-room-icon', + order: 2 + } + }, + { + route: { + path: 'rooms/new', + loadComponent: () => import('../pages/room-wizard/room-wizard.component').then((m) => m.RoomWizardComponent) + } + }, + { + route: { + path: 'rooms/:roomId/edit', + loadComponent: () => import('../pages/room-wizard/room-wizard.component').then((m) => m.RoomWizardComponent), + canActivate: [checkEditableRoomGuard] + } + } +]; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/domain-routes.model.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/domain-routes.model.ts new file mode 100644 index 00000000..c9089dd5 --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/domain-routes.model.ts @@ -0,0 +1,39 @@ +import { Route } from '@angular/router'; + +/** + * Navigation metadata for a domain route that should appear in the console navigation + */ +export interface DomainNavMetadata { + /** Display label for the navigation link */ + label: string; + /** Route path (relative to console) */ + route: string; + /** Material icon name */ + icon: string; + /** Optional CSS class for the icon */ + iconClass?: string; + /** Optional order for sorting navigation items */ + order?: number; +} + +/** + * Complete route configuration for a domain + * Includes both the route definition and optional navigation metadata + */ +export interface DomainRouteConfig { + /** Angular route configuration */ + route: Route; + /** Optional navigation metadata (if this route should appear in console nav) */ + navMetadata?: DomainNavMetadata; +} + +/** + * Domain routes registry + * Maps domain identifiers to their route configurations + */ +export interface DomainRoutesRegistry { + /** Console child routes (appear in the authenticated console area) */ + consoleRoutes: DomainRouteConfig[]; + /** Public routes (standalone routes outside console) */ + publicRoutes: DomainRouteConfig[]; +} diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/index.ts index eb84215f..bb0a0724 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/index.ts @@ -1,5 +1,8 @@ export * from './app.model'; export * from './config.model'; +export * from './domain-routes.model'; +export * from './navigation.model'; export * from './notification.model'; export * from './sidenav.model'; export * from './storage.model'; + diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/routes/base-routes.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/routes/base-routes.ts index 3c816d02..c5e2bab1 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/routes/base-routes.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/routes/base-routes.ts @@ -1,132 +1,25 @@ import { Routes } from '@angular/router'; -import { WebComponentProperty } from '@openvidu-meet/typings'; -import { checkUserAuthenticatedGuard, checkUserNotAuthenticatedGuard } from '../../domains/auth/guards/auth.guard'; -import { validateRecordingAccessGuard } from '../../domains/recordings/guards/recording-validate-access.guard'; -import { checkEditableRoomGuard } from '../../domains/rooms/guards/room-edit-check.guard'; -import { - validateRoomAccessGuard, - validateRoomRecordingsAccessGuard -} from '../../domains/rooms/guards/room-validate-access.guard'; -import { extractRecordingQueryParamsGuard, extractRoomQueryParamsGuard } from '../guards/extract-query-params.guard'; -import { removeQueryParamsGuard } from '../guards/remove-query-params.guard'; -import { runGuardsSerially } from '../guards/run-serially.guard'; -export const baseRoutes: Routes = [ - { - path: 'login', - loadComponent: () => import('../../domains/auth/pages/login/login.component').then((m) => m.LoginComponent), - canActivate: [checkUserNotAuthenticatedGuard] - }, - { - path: 'room/:room-id', - loadComponent: () => - import('../../domains/meeting/pages/meeting/meeting.component').then((m) => m.MeetingComponent), - canActivate: [ - runGuardsSerially( - extractRoomQueryParamsGuard, - validateRoomAccessGuard, - removeQueryParamsGuard(['secret', WebComponentProperty.E2EE_KEY]) - ) - ] - }, - { - path: 'room/:room-id/recordings', - loadComponent: () => - import('../../domains/rooms/pages/room-recordings/room-recordings.component').then( - (m) => m.RoomRecordingsComponent - ), - canActivate: [ - runGuardsSerially( - extractRecordingQueryParamsGuard, - validateRoomRecordingsAccessGuard, - removeQueryParamsGuard(['secret', WebComponentProperty.E2EE_KEY]) - ) - ] - }, - { - path: 'recording/:recording-id', - loadComponent: () => - import('../../domains/recordings/pages/view-recording/view-recording.component').then( - (m) => m.ViewRecordingComponent - ), - canActivate: [validateRecordingAccessGuard] - }, - { - path: 'disconnected', - loadComponent: () => - import('../../domains/meeting/pages/end-meeting/end-meeting.component').then((m) => m.EndMeetingComponent) - }, - { - path: 'error', - loadComponent: () => import('../../domains/console/pages/error/error.component').then((m) => m.ErrorComponent) - }, - { - path: '', - loadComponent: () => - import('../../domains/console/pages/console/console.component').then((m) => m.ConsoleComponent), - canActivate: [checkUserAuthenticatedGuard], - children: [ - { - path: '', - redirectTo: 'overview', - pathMatch: 'full' - }, - { - path: 'overview', - loadComponent: () => - import('../../domains/console/pages/overview/overview.component').then((m) => m.OverviewComponent) - }, - { - path: 'rooms', - loadComponent: () => - import('../../domains/rooms/pages/rooms/rooms.component').then((m) => m.RoomsComponent) - }, - { - path: 'rooms/new', +import { authDomainRoutes } from '../../domains/auth/routes/auth.routes'; +import { consoleDomainRoutes } from '../../domains/console/routes/console.routes'; +import { meetingDomainRoutes } from '../../domains/meeting/routes/meeting.routes'; +import { recordingsDomainRoutes } from '../../domains/recordings/routes/recordings.routes'; +import { roomsDomainRoutes } from '../../domains/rooms/routes/rooms.routes'; - loadComponent: () => - import('../../domains/rooms/pages/room-wizard/room-wizard.component').then( - (m) => m.RoomWizardComponent - ) - }, - { - path: 'rooms/:roomId/edit', - loadComponent: () => - import('../../domains/rooms/pages/room-wizard/room-wizard.component').then( - (m) => m.RoomWizardComponent - ), - canActivate: [checkEditableRoomGuard] - }, - { - path: 'recordings', - loadComponent: () => - import('../../domains/recordings/pages/recordings/recordings.component').then( - (m) => m.RecordingsComponent - ) - }, - { - path: 'embedded', - loadComponent: () => - import('../../domains/console/pages/embedded/embedded.component').then((m) => m.EmbeddedComponent) - }, - { - path: 'users-permissions', - loadComponent: () => - import('../../domains/console/pages/users-permissions/users-permissions.component').then( - (m) => m.UsersPermissionsComponent - ) - }, - { - path: 'config', - loadComponent: () => - import('../../domains/console/pages/config/config.component').then((m) => m.ConfigComponent) - }, - // { - // path: 'about', - // component: AboutComponent - // }, - { path: '**', redirectTo: 'overview' } - ] - }, +export const baseRoutes: Routes = [ + // Auth domain routes + ...authDomainRoutes.map((config) => config.route), + + // Meeting domain routes + ...meetingDomainRoutes.map((config) => config.route), + + // Rooms domain public routes + ...roomsDomainRoutes.map((config) => config.route), + + // Recordings domain public routes + ...recordingsDomainRoutes.map((config) => config.route), + + // Console domain routes (includes console shell, child routes, and guards) + ...consoleDomainRoutes, // Redirect all other routes to the console { path: '**', redirectTo: '' }