Prevents editing rooms with active meetings
Enhances room management by preventing modifications to rooms with active meetings. Adds validation to backend to prevent updates to room configuration during an active meeting. Improves frontend user experience by disabling the room editing option and adding a guard to redirect users away from the edit page.
This commit is contained in:
parent
449f9cbf25
commit
d72149c97d
@ -0,0 +1,8 @@
|
||||
description: Room not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../schemas/error.yaml'
|
||||
example:
|
||||
error: 'Room Error'
|
||||
message: 'Room "room_123" has an active meeting'
|
||||
@ -56,10 +56,10 @@
|
||||
description: |
|
||||
Delete multiple OpenVidu Meet rooms at once with the specified room IDs.
|
||||
|
||||
If any of the rooms have active meetings or recordings,
|
||||
If any of the rooms have active meetings or recordings,
|
||||
deletion behavior is determined by the provided `withMeeting` and `withRecordings` deletion policies.
|
||||
|
||||
Depending on these policies, the rooms may be deleted/closed immediately, scheduled to be deleted/closed once the meetings end,
|
||||
Depending on these policies, the rooms may be deleted/closed immediately, scheduled to be deleted/closed once the meetings end,
|
||||
or the operation may fail if deletion is not permitted.
|
||||
tags:
|
||||
- OpenVidu Meet - Rooms
|
||||
@ -120,10 +120,10 @@
|
||||
description: |
|
||||
Deletes the specified OpenVidu Meet room by its room ID.
|
||||
|
||||
If the room has an active meeting or existing recordings,
|
||||
If the room has an active meeting or existing recordings,
|
||||
deletion behavior is determined by the provided `withMeeting` and `withRecordings` deletion policies.
|
||||
|
||||
Depending on these policies, the room may be deleted/closed immediately, scheduled to be deleted/closed once the meeting ends,
|
||||
Depending on these policies, the room may be deleted/closed immediately, scheduled to be deleted/closed once the meeting ends,
|
||||
or the operation may fail if deletion is not permitted.
|
||||
tags:
|
||||
- OpenVidu Meet - Rooms
|
||||
@ -204,6 +204,8 @@
|
||||
$ref: '../components/responses/forbidden-error.yaml'
|
||||
'404':
|
||||
$ref: '../components/responses/error-room-not-found.yaml'
|
||||
'409':
|
||||
$ref: '../components/responses/error-room-active-meeting.yaml'
|
||||
'422':
|
||||
$ref: '../components/responses/validation-error.yaml'
|
||||
'500':
|
||||
|
||||
@ -205,6 +205,10 @@ export const errorRoomClosed = (roomId: string): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('Room Error', `Room '${roomId}' is closed and cannot be joined`, 409);
|
||||
};
|
||||
|
||||
export const errorRoomActiveMeeting = (roomId: string): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('Room Error', `Room '${roomId}' has an active meeting`, 409);
|
||||
};
|
||||
|
||||
export const errorInvalidRoomSecret = (roomId: string, secret: string): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('Room Error', `Secret '${secret}' is not recognized for room '${roomId}'`, 400);
|
||||
};
|
||||
|
||||
@ -25,6 +25,7 @@ import { validateRecordingTokenMetadata } from '../middlewares/index.js';
|
||||
import {
|
||||
errorDeletingRoom,
|
||||
errorInvalidRoomSecret,
|
||||
errorRoomActiveMeeting,
|
||||
errorRoomNotFound,
|
||||
internalError,
|
||||
OpenViduMeetError
|
||||
@ -139,6 +140,12 @@ export class RoomService {
|
||||
*/
|
||||
async updateMeetRoomConfig(roomId: string, config: MeetRoomConfig): Promise<MeetRoom> {
|
||||
const room = await this.getMeetRoom(roomId);
|
||||
|
||||
if (room.status === MeetRoomStatus.ACTIVE_MEETING) {
|
||||
// Reject config updates during active meetings
|
||||
throw errorRoomActiveMeeting(roomId);
|
||||
}
|
||||
|
||||
room.config = config;
|
||||
|
||||
await this.roomRepository.update(room);
|
||||
|
||||
@ -5,10 +5,12 @@ import { FrontendEventService } from '../../../../src/services/index.js';
|
||||
import {
|
||||
createRoom,
|
||||
deleteAllRooms,
|
||||
disconnectFakeParticipants,
|
||||
getRoom,
|
||||
startTestServer,
|
||||
updateRoomConfig
|
||||
} from '../../../helpers/request-helpers.js';
|
||||
import { RoomData, setupSingleRoom } from '../../../helpers/test-scenarios.js';
|
||||
|
||||
describe('Room API Tests', () => {
|
||||
beforeAll(async () => {
|
||||
@ -133,6 +135,42 @@ describe('Room API Tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Update Room Config with Active Meeting Tests', () => {
|
||||
let roomData: RoomData;
|
||||
|
||||
afterEach(async () => {
|
||||
await disconnectFakeParticipants();
|
||||
});
|
||||
|
||||
it('should reject room config update when there is an active meeting', async () => {
|
||||
// Create a room and start a meeting
|
||||
roomData = await setupSingleRoom(true);
|
||||
|
||||
// Try to update room config
|
||||
const newConfig = {
|
||||
recording: {
|
||||
enabled: false
|
||||
},
|
||||
chat: {
|
||||
enabled: false
|
||||
},
|
||||
virtualBackground: {
|
||||
enabled: false
|
||||
},
|
||||
e2ee: {
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
|
||||
const response = await updateRoomConfig(roomData.room.roomId, newConfig);
|
||||
|
||||
// Should return 409 Conflict
|
||||
expect(response.status).toBe(409);
|
||||
expect(response.body.error).toBe('Room Error');
|
||||
expect(response.body.message).toContain(`Room '${roomData.room.roomId}' has an active meeting`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Update Room Config Validation failures', () => {
|
||||
it('should fail when config has incorrect structure', async () => {
|
||||
const { roomId } = await createRoom({
|
||||
|
||||
@ -323,8 +323,8 @@ export class RoomsListsComponent implements OnInit, OnChanges {
|
||||
return room.status !== MeetRoomStatus.CLOSED;
|
||||
}
|
||||
|
||||
canEditRoom(_room: MeetRoom): boolean {
|
||||
return true;
|
||||
canEditRoom(room: MeetRoom): boolean {
|
||||
return room.status !== MeetRoomStatus.ACTIVE_MEETING;
|
||||
}
|
||||
|
||||
// ===== UI HELPER METHODS =====
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
import { inject } from '@angular/core';
|
||||
import { CanActivateFn, Router } from '@angular/router';
|
||||
import { MeetRoomStatus } from '@openvidu-meet/typings';
|
||||
import { RoomService } from '../services/room.service';
|
||||
|
||||
/**
|
||||
* Guard that prevents editing a room when there's an active meeting.
|
||||
* Redirects to /rooms if the room has an active meeting.
|
||||
*/
|
||||
export const checkRoomEditGuard: CanActivateFn = async (route) => {
|
||||
const roomService = inject(RoomService);
|
||||
const router = inject(Router);
|
||||
|
||||
const roomId = route.paramMap.get('roomId');
|
||||
|
||||
if (!roomId) {
|
||||
console.warn('No roomId provided in route params');
|
||||
router.navigate(['/rooms']);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const room = await roomService.getRoom(roomId);
|
||||
|
||||
if (room.status === MeetRoomStatus.ACTIVE_MEETING) {
|
||||
console.warn(`Cannot edit room ${roomId} - active meeting in progress`);
|
||||
router.navigate(['/rooms']);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`Error checking room status for ${roomId}:`, error);
|
||||
router.navigate(['/rooms']);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@ -1,4 +1,5 @@
|
||||
export * from './auth.guard';
|
||||
export * from './check-room-edit.guard';
|
||||
export * from './extract-query-params.guard';
|
||||
export * from './remove-query-params.guard';
|
||||
export * from './run-serially.guard';
|
||||
|
||||
@ -2,6 +2,7 @@ import { Routes } from '@angular/router';
|
||||
import {
|
||||
checkParticipantRoleAndAuthGuard,
|
||||
checkRecordingAuthGuard,
|
||||
checkRoomEditGuard,
|
||||
checkUserAuthenticatedGuard,
|
||||
checkUserNotAuthenticatedGuard,
|
||||
extractRecordingQueryParamsGuard,
|
||||
@ -90,7 +91,8 @@ export const baseRoutes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'rooms/:roomId/edit',
|
||||
component: RoomWizardComponent
|
||||
component: RoomWizardComponent,
|
||||
canActivate: [checkRoomEditGuard]
|
||||
},
|
||||
{
|
||||
path: 'recordings',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user