Rename room preferences to room config

This commit is contained in:
juancarmore 2025-09-10 00:22:11 +02:00
parent 686af46102
commit 40742d22f8
61 changed files with 745 additions and 773 deletions

View File

@ -0,0 +1,16 @@
description: New room config
content:
application/json:
schema:
type: object
properties:
config:
$ref: '../schemas/meet-room-config.yaml#/MeetRoomConfig'
example:
config:
chatConfig:
enabled: true
recordingConfig:
enabled: false
virtualBackgroundConfig:
enabled: true

View File

@ -1,16 +0,0 @@
description: New room preferences
content:
application/json:
schema:
type: object
properties:
preferences:
$ref: '../schemas/meet-room-preferences.yaml#/MeetRoomPreferences'
example:
preferences:
chatPreferences:
enabled: true
recordingPreferences:
enabled: false
virtualBackgroundPreferences:
enabled: true

View File

@ -0,0 +1,5 @@
description: Success response for retrieving the room config
content:
application/json:
schema:
$ref: '../schemas/meet-room-config.yaml#/MeetRoomConfig'

View File

@ -1,5 +0,0 @@
description: Success response for retrieving the room preferences
content:
application/json:
schema:
$ref: '../schemas/meet-room-preferences.yaml#/MeetRoomPreferences'

View File

@ -14,12 +14,12 @@ content:
autoDeletionPolicy:
withMeeting: when_meeting_ends
withRecordings: close
preferences:
chatPreferences:
config:
chatConfig:
enabled: true
recordingPreferences:
recordingConfig:
enabled: false
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: true
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
@ -31,19 +31,19 @@ content:
value:
roomId: 'room-123'
fields=roomId,roomName,creationDate,autoDeletionDate,preferences:
summary: Room details with roomId, roomName, creationDate, autoDeletionDate, and preferences
fields=roomId,roomName,creationDate,autoDeletionDate,config:
summary: Room details with roomId, roomName, creationDate, autoDeletionDate, and config
value:
roomId: 'room-123'
roomName: 'room'
creationDate: 1620000000000
autoDeletionDate: 1900000000000
preferences:
chatPreferences:
config:
chatConfig:
enabled: true
recordingPreferences:
recordingConfig:
enabled: false
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: true
fields=moderatorUrl,speakerUrl:

View File

@ -23,12 +23,12 @@ content:
autoDeletionPolicy:
withMeeting: when_meeting_ends
withRecordings: close
preferences:
chatPreferences:
config:
chatConfig:
enabled: true
recordingPreferences:
recordingConfig:
enabled: false
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: true
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
@ -41,12 +41,12 @@ content:
autoDeletionPolicy:
withMeeting: when_meeting_ends
withRecordings: close
preferences:
chatPreferences:
config:
chatConfig:
enabled: false
recordingPreferences:
recordingConfig:
enabled: true
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: false
moderatorUrl: 'http://localhost:6080/room/room-456?secret=789012'
speakerUrl: 'http://localhost:6080/room/room-456?secret=210987'
@ -65,31 +65,31 @@ content:
isTruncated: false
maxItems: 10
fields=roomId,roomName,creationDate,autoDeletionDate,preferences:
summary: Room details including preferences but no URLs
fields=roomId,roomName,creationDate,autoDeletionDate,config:
summary: Room details including config but no URLs
value:
rooms:
- roomId: 'room-123'
roomName: 'room'
creationDate: 1620000000000
autoDeletionDate: 1900000000000
preferences:
chatPreferences:
config:
chatConfig:
enabled: true
recordingPreferences:
recordingConfig:
enabled: false
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: true
- roomId: 'room-456'
roomName: 'room'
creationDate: 1620001000000
autoDeletionDate: 1900000000000
preferences:
chatPreferences:
config:
chatConfig:
enabled: false
recordingPreferences:
recordingConfig:
enabled: true
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: false
pagination:
isTruncated: true

View File

@ -40,12 +40,12 @@ content:
roomId: room-123
roomName: room
creationDate: 1620000000000
preferences:
chatPreferences:
config:
chatConfig:
enabled: true
recordingPreferences:
recordingConfig:
enabled: false
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: true
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
@ -63,12 +63,12 @@ content:
roomId: room-123
roomName: room
creationDate: 1620000000000
preferences:
chatPreferences:
config:
chatConfig:
enabled: true
recordingPreferences:
recordingConfig:
enabled: false
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: true
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'

View File

@ -25,12 +25,12 @@ content:
roomId: room-123
roomName: room
creationDate: 1620000000000
preferences:
chatPreferences:
config:
chatConfig:
enabled: true
recordingPreferences:
recordingConfig:
enabled: false
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: true
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
@ -44,12 +44,12 @@ content:
roomId: room-123
roomName: room
creationDate: 1620000000000
preferences:
chatPreferences:
config:
chatConfig:
enabled: true
recordingPreferences:
recordingConfig:
enabled: false
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: true
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
@ -63,12 +63,12 @@ content:
roomId: room-123
roomName: room
creationDate: 1620000000000
preferences:
chatPreferences:
config:
chatConfig:
enabled: true
recordingPreferences:
recordingConfig:
enabled: false
virtualBackgroundPreferences:
virtualBackgroundConfig:
enabled: true
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'

View File

@ -1,4 +1,4 @@
description: Success response for updating room preferences
description: Success response for updating room config
content:
application/json:
schema:
@ -7,4 +7,4 @@ content:
message:
type: string
example:
message: Room preferences for room 'room-123' updated successfully
message: Room config for room 'room-123' updated successfully

View File

@ -1,16 +1,16 @@
MeetRoomPreferences:
MeetRoomConfig:
type: object
properties:
chatPreferences:
$ref: '#/MeetChatPreferences'
description: Preferences for the chat feature in the room.
recordingPreferences:
$ref: '#/MeetRecordingPreferences'
description: Preferences for recording the room.
virtualBackgroundPreferences:
$ref: '#/MeetVirtualBackgroundPreferences'
description: Preferences for virtual background in the room.
MeetChatPreferences:
chatConfig:
$ref: '#/MeetChatConfig'
description: Config for the chat feature in the room.
recordingConfig:
$ref: '#/MeetRecordingConfig'
description: Config for recording the room.
virtualBackgroundConfig:
$ref: '#/MeetVirtualBackgroundConfig'
description: Config for virtual background in the room.
MeetChatConfig:
type: object
properties:
enabled:
@ -18,7 +18,7 @@ MeetChatPreferences:
default: true
example: true
description: If true, the room will be allowed to send and receive chat messages.
MeetRecordingPreferences:
MeetRecordingConfig:
type: object
properties:
enabled:
@ -39,7 +39,7 @@ MeetRecordingPreferences:
- `admin`: Only administrators can access the recording.
- `admin_moderator`: Administrators and moderators can access the recording.
- `admin_moderator_speaker`: Administrators, moderators and speakers can access the recording.
MeetVirtualBackgroundPreferences:
MeetVirtualBackgroundConfig:
type: object
properties:
enabled:

View File

@ -54,7 +54,7 @@ properties:
# description: >
# The maximum number of participants allowed in the room. If the number of participants exceeds
# this limit, new participants will not be allowed to join.
preferences:
$ref: './meet-room-preferences.yaml#/MeetRoomPreferences'
config:
$ref: './meet-room-config.yaml#/MeetRoomConfig'
description: >
The preferences for the room. These preferences will be used to configure the room's settings.
The config for the room. These config will be used to configure the room's settings.

View File

@ -58,9 +58,9 @@ properties:
Policy for automatic deletion when the room has recordings. Options are:
- force: The room and its recordings will be deleted.
- close: The room will be closed instead of deleted, maintaining its recordings.
preferences:
$ref: meet-room-preferences.yaml#/MeetRoomPreferences
description: The preferences for the room.
config:
$ref: meet-room-config.yaml#/MeetRoomConfig
description: The config for the room.
# maxParticipants:
# type: integer
# example: 10

View File

@ -13,8 +13,8 @@ paths:
$ref: './paths/rooms.yaml#/~1rooms'
/rooms/{roomId}:
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}'
/rooms/{roomId}/preferences:
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1preferences'
/rooms/{roomId}/config:
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1config'
/recordings:
$ref: './paths/recordings.yaml#/~1recordings'
/recordings/download:
@ -33,8 +33,8 @@ components:
$ref: components/schemas/meet-room.yaml
MeetRoomOptions:
$ref: components/schemas/meet-room-options.yaml
MeetRoomPreferences:
$ref: './components/schemas/meet-room-preferences.yaml#/MeetRoomPreferences'
MeetRoomConfig:
$ref: './components/schemas/meet-room-config.yaml#/MeetRoomConfig'
MeetRecording:
$ref: components/schemas/meet-recording.yaml
Error:

View File

@ -63,8 +63,8 @@ components:
$ref: components/schemas/meet-room.yaml
MeetRoomOptions:
$ref: components/schemas/meet-room-options.yaml
MeetRoomPreferences:
$ref: components/schemas/meet-room-preferences.yaml#/MeetRoomPreferences
MeetRoomConfig:
$ref: components/schemas/meet-room-config.yaml#/MeetRoomConfig
MeetRoomRoleAndPermissions:
$ref: components/schemas/internal/meet-room-role-permissions.yaml
MeetRecording:

View File

@ -151,12 +151,12 @@
$ref: '../components/responses/validation-error.yaml'
'500':
$ref: '../components/responses/internal-server-error.yaml'
/rooms/{roomId}/preferences:
/rooms/{roomId}/config:
get:
operationId: getRoomPreferences
summary: Get room preferences
operationId: getRoomConfig
summary: Get room config
description: >
Retrieves the preferences of an OpenVidu Meet room with the specified room ID.
Retrieves the config of an OpenVidu Meet room with the specified room ID.
tags:
- OpenVidu Meet - Rooms
security:
@ -168,7 +168,7 @@
- $ref: '../components/parameters/internal/x-participant-role.yaml'
responses:
'200':
$ref: '../components/responses/success-get-room-preferences.yaml'
$ref: '../components/responses/success-get-room-config.yaml'
'400':
$ref: '../components/responses/internal/error-invalid-participant-role.yaml'
'401':
@ -182,10 +182,10 @@
'500':
$ref: '../components/responses/internal-server-error.yaml'
put:
operationId: updateRoomPreferences
summary: Update room preferences
operationId: updateRoomConfig
summary: Update room config
description: >
Updates the preferences of an OpenVidu Meet room with the specified room ID.
Updates the config of an OpenVidu Meet room with the specified room ID.
tags:
- OpenVidu Meet - Rooms
security:
@ -194,10 +194,10 @@
parameters:
- $ref: '../components/parameters/room-id-path.yaml'
requestBody:
$ref: '../components/requestBodies/update-room-preferences-request.yaml'
$ref: '../components/requestBodies/update-room-config-request.yaml'
responses:
'200':
$ref: '../components/responses/success-update-room-preferences.yaml'
$ref: '../components/responses/success-update-room-config.yaml'
'401':
$ref: '../components/responses/unauthorized-error.yaml'
'403':

View File

@ -128,34 +128,34 @@ export const bulkDeleteRooms = async (req: Request, res: Response) => {
}
};
export const getRoomPreferences = async (req: Request, res: Response) => {
export const getRoomConfig = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const { roomId } = req.params;
logger.verbose(`Getting room preferences for room '${roomId}'`);
logger.verbose(`Getting room config for room '${roomId}'`);
try {
const { preferences } = await roomService.getMeetRoom(roomId);
return res.status(200).json(preferences);
const { config } = await roomService.getMeetRoom(roomId);
return res.status(200).json(config);
} catch (error) {
handleError(res, error, `getting room preferences for room '${roomId}'`);
handleError(res, error, `getting room config for room '${roomId}'`);
}
};
export const updateRoomPreferences = async (req: Request, res: Response) => {
export const updateRoomConfig = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const { preferences } = req.body;
const { config } = req.body;
const { roomId } = req.params;
logger.verbose(`Updating room preferences for room '${roomId}'`);
logger.verbose(`Updating room config for room '${roomId}'`);
try {
await roomService.updateMeetRoomPreferences(roomId, preferences);
return res.status(200).json({ message: `Room preferences for room '${roomId}' updated successfully` });
await roomService.updateMeetRoomConfig(roomId, config);
return res.status(200).json({ message: `Room config for room '${roomId}' updated successfully` });
} catch (error) {
handleError(res, error, `updating room preferences for room '${roomId}'`);
handleError(res, error, `updating room config for room '${roomId}'`);
}
};

View File

@ -17,7 +17,7 @@ export class MeetRoomHelper {
roomName: room.roomName,
autoDeletionDate: room.autoDeletionDate,
autoDeletionPolicy: room.autoDeletionPolicy,
preferences: room.preferences
config: room.config
// maxParticipants: room.maxParticipants
};
}

View File

@ -27,7 +27,7 @@ export const withRecordingEnabled = async (req: Request, res: Response, next: Ne
const roomId = extractRoomIdFromRequest(req);
const room: MeetRoom = await roomService.getMeetRoom(roomId!);
if (!room.preferences?.recordingPreferences?.enabled) {
if (!room.config?.recordingConfig?.enabled) {
logger.debug(`Recording is disabled for room '${roomId}'`);
const error = errorRecordingDisabled(roomId!);
return rejectRequestFromMeetError(res, error);
@ -35,7 +35,7 @@ export const withRecordingEnabled = async (req: Request, res: Response, next: Ne
return next();
} catch (error) {
handleError(res, error, 'checking recording preferences');
handleError(res, error, 'checking recording config');
}
};

View File

@ -1,15 +1,15 @@
import {
MeetChatPreferences,
MeetChatConfig,
MeetRecordingAccess,
MeetRecordingPreferences,
MeetRecordingConfig,
MeetRoomAutoDeletionPolicy,
MeetRoomConfig,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomFilters,
MeetRoomOptions,
MeetRoomPreferences,
MeetRoomStatus,
MeetVirtualBackgroundPreferences,
MeetVirtualBackgroundConfig,
ParticipantRole,
RecordingPermissions
} from '@typings-ce';
@ -65,7 +65,7 @@ const RecordingAccessSchema: z.ZodType<MeetRecordingAccess> = z.enum([
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
]);
const RecordingPreferencesSchema: z.ZodType<MeetRecordingPreferences> = z
const RecordingConfigSchema: z.ZodType<MeetRecordingConfig> = z
.object({
enabled: z.boolean(),
allowAccessTo: RecordingAccessSchema.optional()
@ -81,18 +81,18 @@ const RecordingPreferencesSchema: z.ZodType<MeetRecordingPreferences> = z
}
);
const ChatPreferencesSchema: z.ZodType<MeetChatPreferences> = z.object({
const ChatConfigSchema: z.ZodType<MeetChatConfig> = z.object({
enabled: z.boolean()
});
const VirtualBackgroundPreferencesSchema: z.ZodType<MeetVirtualBackgroundPreferences> = z.object({
const VirtualBackgroundConfigSchema: z.ZodType<MeetVirtualBackgroundConfig> = z.object({
enabled: z.boolean()
});
const RoomPreferencesSchema: z.ZodType<MeetRoomPreferences> = z.object({
recordingPreferences: RecordingPreferencesSchema,
chatPreferences: ChatPreferencesSchema,
virtualBackgroundPreferences: VirtualBackgroundPreferencesSchema
const RoomConfigSchema: z.ZodType<MeetRoomConfig> = z.object({
recordingConfig: RecordingConfigSchema,
chatConfig: ChatConfigSchema,
virtualBackgroundConfig: VirtualBackgroundConfigSchema
});
const RoomDeletionPolicyWithMeetingSchema: z.ZodType<MeetRoomDeletionPolicyWithMeeting> = z.enum([
@ -150,10 +150,10 @@ const RoomRequestOptionsSchema: z.ZodType<MeetRoomOptions> = z.object({
path: ['withRecordings']
}
),
preferences: RoomPreferencesSchema.optional().default({
recordingPreferences: { enabled: true, allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER },
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
config: RoomConfigSchema.optional().default({
recordingConfig: { enabled: true, allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER },
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
})
// maxParticipants: z
// .number()
@ -223,8 +223,8 @@ const BulkDeleteRoomsSchema = z.object({
withRecordings: RoomDeletionPolicyWithRecordingsSchema.optional().default(MeetRoomDeletionPolicyWithRecordings.FAIL)
});
const UpdateRoomPreferencesSchema = z.object({
preferences: RoomPreferencesSchema
const UpdateRoomConfigSchema = z.object({
config: RoomConfigSchema
});
const UpdateRoomStatusSchema = z.object({
@ -270,8 +270,8 @@ export const withValidRoomFiltersRequest = (req: Request, res: Response, next: N
next();
};
export const withValidRoomPreferences = (req: Request, res: Response, next: NextFunction) => {
const { success, error, data } = UpdateRoomPreferencesSchema.safeParse(req.body);
export const withValidRoomConfig = (req: Request, res: Response, next: NextFunction) => {
const { success, error, data } = UpdateRoomConfigSchema.safeParse(req.body);
if (!success) {
return rejectUnprocessableRequest(res, error);

View File

@ -62,7 +62,7 @@ export const configureRecordingTokenAuth = async (req: Request, res: Response, n
throw errorRoomMetadataNotFound(roomId);
}
const recordingAccess = room.preferences!.recordingPreferences.allowAccessTo;
const recordingAccess = room.config!.recordingConfig.allowAccessTo;
if (!recordingAccess || recordingAccess === MeetRecordingAccess.ADMIN) {
// Deny request if the room is configured to allow access to recordings only for admins

View File

@ -11,11 +11,11 @@ import {
tokenAndRoleValidator,
withAuth,
withValidRoomBulkDeleteRequest,
withValidRoomConfig,
withValidRoomDeleteRequest,
withValidRoomFiltersRequest,
withValidRoomId,
withValidRoomOptions,
withValidRoomPreferences,
withValidRoomSecret,
withValidRoomStatus
} from '../middlewares/index.js';
@ -59,18 +59,18 @@ roomRouter.delete(
);
roomRouter.get(
'/:roomId/preferences',
'/:roomId/config',
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN), participantTokenValidator),
withValidRoomId,
configureRoomAuthorization,
roomCtrl.getRoomPreferences
roomCtrl.getRoomConfig
);
roomRouter.put(
'/:roomId/preferences',
'/:roomId/config',
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
withValidRoomId,
withValidRoomPreferences,
roomCtrl.updateRoomPreferences
withValidRoomConfig,
roomCtrl.updateRoomConfig
);
roomRouter.put(

View File

@ -1,14 +1,14 @@
import { MeetRoom, MeetRecordingInfo, ParticipantRole } from '@typings-ce';
import { MeetRecordingInfo, MeetRoom, ParticipantRole } from '@typings-ce';
import { inject, injectable } from 'inversify';
import { SendDataOptions } from 'livekit-server-sdk';
import { OpenViduComponentsAdapterHelper, OpenViduComponentsSignalPayload } from '../helpers/index.js';
import { LiveKitService, LoggerService } from './index.js';
import {
MeetParticipantRoleUpdatedPayload,
MeetRoomPreferencesUpdatedPayload,
MeetRoomConfigUpdatedPayload,
MeetSignalPayload,
MeetSignalType
} from '../typings/ce/event.model.js';
import { LiveKitService, LoggerService } from './index.js';
/**
* Service responsible for all communication with the frontend
@ -67,25 +67,25 @@ export class FrontendEventService {
}
/**
* Sends a signal to notify participants in a room about updated room preferences.
* Sends a signal to notify participants in a room about updated room config.
*/
async sendRoomPreferencesUpdatedSignal(roomId: string, updatedRoom: MeetRoom): Promise<void> {
this.logger.debug(`Sending room preferences updated signal for room ${roomId}`);
async sendRoomConfigUpdatedSignal(roomId: string, updatedRoom: MeetRoom): Promise<void> {
this.logger.debug(`Sending room config updated signal for room ${roomId}`);
try {
const payload: MeetRoomPreferencesUpdatedPayload = {
const payload: MeetRoomConfigUpdatedPayload = {
roomId,
preferences: updatedRoom.preferences!,
config: updatedRoom.config!,
timestamp: Date.now()
};
const options: SendDataOptions = {
topic: MeetSignalType.MEET_ROOM_PREFERENCES_UPDATED
topic: MeetSignalType.MEET_ROOM_CONFIG_UPDATED
};
await this.sendSignal(roomId, payload, options);
} catch (error) {
this.logger.error(`Error sending room preferences updated signal for room ${roomId}:`, error);
this.logger.error(`Error sending room config updated signal for room ${roomId}:`, error);
}
}

View File

@ -2,13 +2,13 @@ import {
MeetingEndAction,
MeetRecordingAccess,
MeetRoom,
MeetRoomConfig,
MeetRoomDeletionErrorCode,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomDeletionSuccessCode,
MeetRoomFilters,
MeetRoomOptions,
MeetRoomPreferences,
MeetRoomStatus,
ParticipantRole,
RecordingPermissions
@ -80,7 +80,7 @@ export class RoomService {
*
*/
async createMeetRoom(baseUrl: string, roomOptions: MeetRoomOptions): Promise<MeetRoom> {
const { roomName, autoDeletionDate, autoDeletionPolicy, preferences } = roomOptions;
const { roomName, autoDeletionDate, autoDeletionPolicy, config } = roomOptions;
const roomIdPrefix = roomName!.replace(/\s+/g, ''); // Remove all spaces
const roomId = `${roomIdPrefix}-${uid(15)}`; // Generate a unique room ID based on the room name
@ -91,7 +91,7 @@ export class RoomService {
// maxParticipants,
autoDeletionDate,
autoDeletionPolicy,
preferences: preferences!,
config: config!,
moderatorUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`,
speakerUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`,
status: MeetRoomStatus.OPEN,
@ -135,21 +135,21 @@ export class RoomService {
}
/**
* Updates the preferences of a specific meeting room.
* Updates the config of a specific meeting room.
*
* @param roomId - The unique identifier of the meeting room to update
* @param preferences - The new preferences to apply to the meeting room
* @param config - The new config to apply to the meeting room
* @returns A Promise that resolves to the updated MeetRoom object
*/
async updateMeetRoomPreferences(roomId: string, preferences: MeetRoomPreferences): Promise<MeetRoom> {
async updateMeetRoomConfig(roomId: string, config: MeetRoomConfig): Promise<MeetRoom> {
const room = await this.getMeetRoom(roomId);
room.preferences = preferences;
room.config = config;
await this.storageService.saveMeetRoom(room);
// Update the archived room metadata if it exists
await Promise.all([
this.storageService.archiveRoomMetadata(roomId, true),
this.frontendEventService.sendRoomPreferencesUpdatedSignal(roomId, room)
this.frontendEventService.sendRoomConfigUpdatedSignal(roomId, room)
]);
return room;
}
@ -684,7 +684,7 @@ export class RoomService {
}
protected getRecordingPermissions(room: Partial<MeetRoom>, role: ParticipantRole): RecordingPermissions {
const recordingAccess = room.preferences!.recordingPreferences.allowAccessTo;
const recordingAccess = room.config!.recordingConfig.allowAccessTo;
// A participant can delete recordings if they are a moderator and the recording access is not set to admin
const canDeleteRecordings = role === ParticipantRole.MODERATOR && recordingAccess !== MeetRecordingAccess.ADMIN;

View File

@ -269,7 +269,7 @@ export class MeetStorageService<
* Archives room metadata by storing essential room information in both cache and persistent storage.
*
* This method retrieves the room data, extracts key metadata (moderator/speaker URLs and
* recording preferences), and saves it to an archived location for future reference.
* recording config), and saves it to an archived location for future reference.
*
* If `updateOnlyIfExists` is true, it will only save the archived metadata if it already exists,
* updating the existing entry.
@ -302,8 +302,8 @@ export class MeetStorageService<
const archivedRoom: Partial<MRoom> = {
moderatorUrl: room.moderatorUrl,
speakerUrl: room.speakerUrl,
preferences: {
recordingPreferences: room.preferences?.recordingPreferences
config: {
recordingConfig: room.config?.recordingConfig
}
} as Partial<MRoom>;

View File

@ -9,9 +9,9 @@ import {
MeetRecordingStatus,
MeetRoom,
MeetRoomAutoDeletionPolicy,
MeetRoomConfig,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomPreferences,
MeetRoomStatus,
ParticipantPermissions,
ParticipantRole
@ -93,22 +93,22 @@ export const expectSuccessRoomResponse = (
response: any,
roomName: string,
autoDeletionDate?: number,
preferences?: MeetRoomPreferences
config?: MeetRoomConfig
) => {
expect(response.status).toBe(200);
expectValidRoom(response.body, roomName, preferences, autoDeletionDate);
expectValidRoom(response.body, roomName, config, autoDeletionDate);
};
export const expectSuccessRoomPreferencesResponse = (response: any, preferences: MeetRoomPreferences) => {
export const expectSuccessRoomConfigResponse = (response: any, config: MeetRoomConfig) => {
expect(response.status).toBe(200);
expect(response.body).toBeDefined();
expect(response.body).toEqual(preferences);
expect(response.body).toEqual(config);
};
export const expectValidRoom = (
room: MeetRoom,
name: string,
preferences?: MeetRoomPreferences,
config?: MeetRoomConfig,
autoDeletionDate?: number,
autoDeletionPolicy?: MeetRoomAutoDeletionPolicy,
status?: MeetRoomStatus,
@ -140,18 +140,18 @@ export const expectValidRoom = (
});
}
expect(room.preferences).toBeDefined();
expect(room.config).toBeDefined();
if (preferences !== undefined) {
expect(room.preferences).toEqual(preferences);
if (config !== undefined) {
expect(room.config).toEqual(config);
} else {
expect(room.preferences).toEqual({
recordingPreferences: {
expect(room.config).toEqual({
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
});
}

View File

@ -243,34 +243,34 @@ export const getRoom = async (roomId: string, fields?: string, cookie?: string,
return await req;
};
export const getRoomPreferences = async (roomId: string) => {
export const getRoomConfig = async (roomId: string) => {
checkAppIsRunning();
return await request(app)
.get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/preferences`)
.get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/config`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send();
};
export const updateRoomPreferences = async (roomId: string, preferences: any) => {
export const updateRoomConfig = async (roomId: string, config: any) => {
checkAppIsRunning();
return await request(app)
.put(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/preferences`)
.put(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/config`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send({ preferences });
.send({ config });
};
export const updateRecordingAccessPreferencesInRoom = async (roomId: string, recordingAccess: MeetRecordingAccess) => {
const response = await updateRoomPreferences(roomId, {
recordingPreferences: {
export const updateRecordingAccessConfigInRoom = async (roomId: string, recordingAccess: MeetRecordingAccess) => {
const response = await updateRoomConfig(roomId, {
recordingConfig: {
enabled: true,
allowAccessTo: recordingAccess
},
chatPreferences: {
chatConfig: {
enabled: true
},
virtualBackgroundPreferences: {
virtualBackgroundConfig: {
enabled: true
}
});
@ -297,11 +297,7 @@ export const deleteRoom = async (roomId: string, query: Record<string, any> = {}
return result;
};
export const bulkDeleteRooms = async (
roomIds: any[],
withMeeting?: string,
withRecordings?: string
) => {
export const bulkDeleteRooms = async (roomIds: any[], withMeeting?: string, withRecordings?: string) => {
checkAppIsRunning();
const result = await request(app)

View File

@ -2,7 +2,7 @@ import express, { Request, Response } from 'express';
import http from 'http';
import { StringValue } from 'ms';
import { MeetRoomHelper } from '../../src/helpers';
import { MeetRoom, MeetRoomPreferences } from '../../src/typings/ce';
import { MeetRoom, MeetRoomConfig } from '../../src/typings/ce';
import { expectValidStartRecordingResponse } from './assertion-helpers';
import {
createRoom,
@ -35,17 +35,17 @@ export interface TestContext {
*
* @param withParticipant Whether to join a fake participant in the room.
* @param roomName Name of the room to create.
* @param preferences Optional room preferences.
* @param config Optional room config.
* @returns Room data including secrets and cookies.
*/
export const setupSingleRoom = async (
withParticipant = false,
roomName = 'TEST_ROOM',
preferences?: MeetRoomPreferences
config?: MeetRoomConfig
): Promise<RoomData> => {
const room = await createRoom({
roomName,
preferences
config
});
// Extract the room secrets and generate participant tokens, saved as cookies

View File

@ -73,7 +73,7 @@ describe('Recording API Tests', () => {
expect(archivedRoom).toBeDefined();
expect(archivedRoom?.moderatorUrl).toBeDefined();
expect(archivedRoom?.speakerUrl).toBeDefined();
expect(archivedRoom?.preferences).toBeDefined();
expect(archivedRoom?.config).toBeDefined();
const secretsResponse = await stopRecording(recordingId, moderatorCookie);
expectValidStopRecordingResponse(secretsResponse, recordingId, room.roomId, room.roomName);

View File

@ -58,25 +58,19 @@ describe('Room API Tests', () => {
withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE,
withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
},
preferences: {
recordingPreferences: {
config: {
recordingConfig: {
enabled: false,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: false },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: false },
virtualBackgroundConfig: { enabled: true }
}
};
const room = await createRoom(payload);
expectValidRoom(
room,
'Example Room',
payload.preferences,
validAutoDeletionDate,
payload.autoDeletionPolicy
);
expectValidRoom(room, 'Example Room', payload.config, validAutoDeletionDate, payload.autoDeletionPolicy);
});
});
@ -180,7 +174,9 @@ describe('Room API Tests', () => {
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
expect(JSON.stringify(response.body.details)).toContain('FAIL policy is not allowed for withMeeting auto-deletion policy');
expect(JSON.stringify(response.body.details)).toContain(
'FAIL policy is not allowed for withMeeting auto-deletion policy'
);
});
it('should fail when autoDeletionPolicy.withRecordings has FAIL policy', async () => {
@ -195,7 +191,9 @@ describe('Room API Tests', () => {
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
expect(JSON.stringify(response.body.details)).toContain('FAIL policy is not allowed for withRecordings auto-deletion policy');
expect(JSON.stringify(response.body.details)).toContain(
'FAIL policy is not allowed for withRecordings auto-deletion policy'
);
});
it('should fail when roomName is not a string (number provided)', async () => {
@ -220,11 +218,11 @@ describe('Room API Tests', () => {
expect(JSON.stringify(response.body.details)).toContain('Expected string');
});
it('should fail when preferences is not an object (string provided)', async () => {
it('should fail when config is not an object (string provided)', async () => {
const payload = {
roomName: 'TestRoom',
autoDeletionDate: validAutoDeletionDate,
preferences: 'invalid-preferences'
config: 'invalid-config'
};
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
@ -232,19 +230,19 @@ describe('Room API Tests', () => {
expect(JSON.stringify(response.body.details)).toContain('Expected object');
});
it('should fail when preferences has an invalid structure', async () => {
// Assuming preferences expects each sub-property to be an object with a boolean "enabled",
it('should fail when config has an invalid structure', async () => {
// Assuming config expects each sub-property to be an object with a boolean "enabled",
// here we deliberately use an invalid structure.
const payload = {
roomName: 'TestRoom',
autoDeletionDate: validAutoDeletionDate,
preferences: {
recordingPreferences: {
config: {
recordingConfig: {
enabled: 'yes', // invalid boolean
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
}
};

View File

@ -1,6 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { ParticipantRole } from '../../../../src/typings/ce/participant.js';
import { MeetRecordingAccess } from '../../../../src/typings/ce/room-preferences.js';
import { MeetRecordingAccess } from '../../../../src/typings/ce/room-config.js';
import { expectValidRecordingTokenResponse } from '../../../helpers/assertion-helpers.js';
import {
deleteAllRecordings,
@ -9,7 +9,7 @@ import {
disconnectFakeParticipants,
generateRecordingToken,
startTestServer,
updateRecordingAccessPreferencesInRoom
updateRecordingAccessConfigInRoom
} from '../../../helpers/request-helpers.js';
import { RoomData, setupSingleRoomWithRecording } from '../../../helpers/test-scenarios.js';
@ -28,44 +28,35 @@ describe('Room API Tests', () => {
describe('Generate Recording Token Tests', () => {
it('should generate a recording token with canRetrieve and canDelete permissions when using the moderator secret and recording access is admin_moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true);
});
it('should generate a recording token with canRetrieve and canDelete permissions when using the moderator secret and recording access is admin_moderator_speaker', async () => {
await updateRecordingAccessPreferencesInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true);
});
it('should generate a recording token without any permissions when using the speaker secret and recording access is admin_moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const response = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.SPEAKER, false, false);
});
it('should generate a recording token with canRetrieve permission but not canDelete when using the speaker secret and recording access is admin_moderator_speaker', async () => {
await updateRecordingAccessPreferencesInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER);
const response = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.SPEAKER, true, false);
});
it('should succeed even if the room is deleted', async () => {
await updateRecordingAccessPreferencesInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER);
await deleteRoom(roomData.room.roomId);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);

View File

@ -1,17 +1,17 @@
import { afterEach, beforeAll, describe, it } from '@jest/globals';
import { MeetRecordingAccess } from '../../../../src/typings/ce/index.js';
import { expectSuccessRoomPreferencesResponse } from '../../../helpers/assertion-helpers.js';
import { deleteAllRooms, getRoomPreferences, startTestServer } from '../../../helpers/request-helpers.js';
import { expectSuccessRoomConfigResponse } from '../../../helpers/assertion-helpers.js';
import { deleteAllRooms, getRoomConfig, startTestServer } from '../../../helpers/request-helpers.js';
import { setupSingleRoom } from '../../../helpers/test-scenarios.js';
describe('Room API Tests', () => {
const DEFAULT_PREFERENCES = {
recordingPreferences: {
const DEFAULT_CONFIG = {
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
};
beforeAll(() => {
@ -23,33 +23,33 @@ describe('Room API Tests', () => {
await deleteAllRooms();
});
describe('Get Room Preferences Tests', () => {
describe('Get Room Config Tests', () => {
it('should successfully retrieve a room by its ID', async () => {
const roomData = await setupSingleRoom();
const roomId = roomData.room.roomId;
const response = await getRoomPreferences(roomId);
expectSuccessRoomPreferencesResponse(response, DEFAULT_PREFERENCES);
const response = await getRoomConfig(roomId);
expectSuccessRoomConfigResponse(response, DEFAULT_CONFIG);
});
it('should retrieve custom room preferences', async () => {
it('should retrieve custom room config', async () => {
const payload = {
roomName: 'custom-prefs',
preferences: {
recordingPreferences: {
config: {
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: false }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: false }
}
};
const roomData = await setupSingleRoom(false, payload.roomName, payload.preferences);
const roomData = await setupSingleRoom(false, payload.roomName, payload.config);
const roomId = roomData.room.roomId;
const response = await getRoomPreferences(roomId);
expectSuccessRoomPreferencesResponse(response, payload.preferences);
const response = await getRoomConfig(roomId);
expectSuccessRoomConfigResponse(response, payload.config);
});
});
});

View File

@ -32,25 +32,25 @@ describe('Room API Tests', () => {
expectSuccessRoomResponse(response, 'test-room');
});
it('should retrieve a room with custom preferences', async () => {
it('should retrieve a room with custom config', async () => {
const payload = {
roomName: 'custom-prefs',
preferences: {
recordingPreferences: {
config: {
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: false }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: false }
}
};
// Create a room with custom preferences
// Create a room with custom config
const { roomId } = await createRoom(payload);
// Retrieve the room by its ID
const response = await getRoom(roomId);
expectSuccessRoomResponse(response, 'custom-prefs', undefined, payload.preferences);
expectSuccessRoomResponse(response, 'custom-prefs', undefined, payload.config);
});
it('should retrieve only specified fields when using fields parameter', async () => {

View File

@ -1,15 +1,15 @@
import { afterEach, beforeAll, describe, expect, it, jest } from '@jest/globals';
import { container } from '../../../../src/config/index.js';
import { FrontendEventService } from '../../../../src/services/index.js';
import { MeetSignalType } from '../../../../src/typings/ce/event.model.js';
import { MeetRecordingAccess } from '../../../../src/typings/ce/index.js';
import {
createRoom,
deleteAllRooms,
getRoom,
startTestServer,
updateRoomPreferences
updateRoomConfig
} from '../../../helpers/request-helpers.js';
import { FrontendEventService } from '../../../../src/services/index.js';
import { container } from '../../../../src/config/index.js';
import { MeetSignalType } from '../../../../src/typings/ce/event.model.js';
describe('Room API Tests', () => {
beforeAll(() => {
@ -21,7 +21,7 @@ describe('Room API Tests', () => {
await deleteAllRooms();
});
describe('Update Room Preferences Tests', () => {
describe('Update Room Config Tests', () => {
let frontendEventService: FrontendEventService;
beforeAll(() => {
@ -29,41 +29,41 @@ describe('Room API Tests', () => {
frontendEventService = container.get(FrontendEventService);
});
it('should successfully update room preferences', async () => {
it('should successfully update room config', async () => {
const sendSignalSpy = jest.spyOn(frontendEventService as any, 'sendSignal');
const createdRoom = await createRoom({
roomName: 'update-test',
preferences: {
recordingPreferences: {
config: {
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
}
});
// Update the room preferences
const updatedPreferences = {
recordingPreferences: {
// Update the room config
const updatedConfig = {
recordingConfig: {
enabled: false,
allowAccessTo: MeetRecordingAccess.ADMIN
},
chatPreferences: { enabled: false },
virtualBackgroundPreferences: { enabled: false }
chatConfig: { enabled: false },
virtualBackgroundConfig: { enabled: false }
};
const updateResponse = await updateRoomPreferences(createdRoom.roomId, updatedPreferences);
const updateResponse = await updateRoomConfig(createdRoom.roomId, updatedConfig);
// Verify a method of frontend event service is called
expect(sendSignalSpy).toHaveBeenCalledWith(
createdRoom.roomId,
{
roomId: createdRoom.roomId,
preferences: updatedPreferences,
config: updatedConfig,
timestamp: expect.any(Number)
},
{
topic: MeetSignalType.MEET_ROOM_PREFERENCES_UPDATED
topic: MeetSignalType.MEET_ROOM_CONFIG_UPDATED
}
);
@ -74,33 +74,33 @@ describe('Room API Tests', () => {
// Verify with a get request
const getResponse = await getRoom(createdRoom.roomId);
expect(getResponse.status).toBe(200);
expect(getResponse.body.preferences).toEqual(updatedPreferences);
expect(getResponse.body.config).toEqual(updatedConfig);
});
it('should allow partial preference updates', async () => {
// Create a room first with all preferences enabled
// Create a room first with all config enabled
const createdRoom = await createRoom({
roomName: 'partial-update',
preferences: {
recordingPreferences: {
config: {
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
}
});
// Update only one preference
const partialPreferences = {
recordingPreferences: {
const partialConfig = {
recordingConfig: {
enabled: false,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
};
const updateResponse = await updateRoomPreferences(createdRoom.roomId, partialPreferences);
const updateResponse = await updateRoomConfig(createdRoom.roomId, partialConfig);
// Verify update response
expect(updateResponse.status).toBe(200);
@ -109,59 +109,59 @@ describe('Room API Tests', () => {
// Verify with a get request
const getResponse = await getRoom(createdRoom.roomId);
expect(getResponse.status).toBe(200);
expect(getResponse.body.preferences).toEqual(partialPreferences);
expect(getResponse.body.config).toEqual(partialConfig);
});
});
describe('Update Room Preferences Validation failures', () => {
it('should fail when preferences have incorrect structure', async () => {
describe('Update Room Config Validation failures', () => {
it('should fail when config has incorrect structure', async () => {
const { roomId } = await createRoom({
roomName: 'validation-test'
});
// Invalid preferences (missing required fields)
const invalidPreferences = {
recordingPreferences: {
// Invalid config (missing required fields)
const invalidConfig = {
recordingConfig: {
enabled: false
},
// Missing chatPreferences
virtualBackgroundPreferences: { enabled: false }
// Missing chatConfig
virtualBackgroundConfig: { enabled: false }
};
const response = await updateRoomPreferences(roomId, invalidPreferences);
const response = await updateRoomConfig(roomId, invalidConfig);
expect(response.status).toBe(422);
expect(response.body.error).toContain('Unprocessable Entity');
expect(JSON.stringify(response.body.details)).toContain('chatPreferences');
expect(JSON.stringify(response.body.details)).toContain('chatConfig');
});
it('should fail when preferences have incorrect types', async () => {
it('should fail when config has incorrect types', async () => {
const createdRoom = await createRoom({
roomName: 'type-test'
});
// Invalid preferences (wrong types)
const invalidPreferences = {
recordingPreferences: {
// Invalid config (wrong types)
const invalidConfig = {
recordingConfig: {
enabled: 'true', // String instead of boolean
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: false },
virtualBackgroundPreferences: { enabled: false }
chatConfig: { enabled: false },
virtualBackgroundConfig: { enabled: false }
};
const response = await updateRoomPreferences(createdRoom.roomId, invalidPreferences);
const response = await updateRoomConfig(createdRoom.roomId, invalidConfig);
expect(response.status).toBe(422);
expect(response.body.error).toContain('Unprocessable Entity');
expect(JSON.stringify(response.body.details)).toContain('recordingPreferences.enabled');
expect(JSON.stringify(response.body.details)).toContain('recordingConfig.enabled');
});
it('should fail when preferences are missing required properties', async () => {
it('should fail when config is missing required properties', async () => {
const createdRoom = await createRoom({
roomName: 'missing-props'
});
const emptyPreferences = {};
const response = await updateRoomPreferences(createdRoom.roomId, emptyPreferences);
const emptyConfig = {};
const response = await updateRoomConfig(createdRoom.roomId, emptyConfig);
expect(response.status).toBe(422);
expect(response.body.error).toContain('Unprocessable Entity');
@ -172,32 +172,32 @@ describe('Room API Tests', () => {
roomName: 'missing-access'
});
const invalidPreferences = {
recordingPreferences: {
const invalidConfig = {
recordingConfig: {
enabled: true // Missing allowAccessTo
},
chatPreferences: { enabled: false },
virtualBackgroundPreferences: { enabled: false }
chatConfig: { enabled: false },
virtualBackgroundConfig: { enabled: false }
};
const response = await updateRoomPreferences(createdRoom.roomId, invalidPreferences);
const response = await updateRoomConfig(createdRoom.roomId, invalidConfig);
expect(response.status).toBe(422);
expect(response.body.error).toContain('Unprocessable Entity');
expect(JSON.stringify(response.body.details)).toContain('recordingPreferences.allowAccessTo');
expect(JSON.stringify(response.body.details)).toContain('recordingConfig.allowAccessTo');
});
it('should return 404 when updating non-existent room', async () => {
const nonExistentRoomId = 'non-existent-room';
const preferences = {
recordingPreferences: {
const config = {
recordingConfig: {
enabled: false,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: false },
virtualBackgroundPreferences: { enabled: false }
chatConfig: { enabled: false },
virtualBackgroundConfig: { enabled: false }
};
const response = await updateRoomPreferences(nonExistentRoomId, preferences);
const response = await updateRoomConfig(nonExistentRoomId, config);
expect(response.status).toBe(404);
expect(response.body.message).toContain(`'${nonExistentRoomId}' does not exist`);

View File

@ -15,7 +15,7 @@ import {
startTestServer,
stopAllRecordings,
stopRecording,
updateRecordingAccessPreferencesInRoom
updateRecordingAccessConfigInRoom
} from '../../../helpers/request-helpers.js';
import { RoomData, setupSingleRoom, setupSingleRoomWithRecording } from '../../../helpers/test-scenarios.js';
@ -170,7 +170,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -184,7 +184,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -198,7 +198,7 @@ describe('Recording API Security Tests', () => {
});
it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.speakerSecret
@ -209,7 +209,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.moderatorSecret
@ -234,7 +234,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -250,7 +250,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -266,7 +266,7 @@ describe('Recording API Security Tests', () => {
});
it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.speakerSecret
@ -279,7 +279,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.moderatorSecret
@ -363,7 +363,7 @@ describe('Recording API Security Tests', () => {
});
it('should fail when recording access is admin_moderator_speaker and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -379,7 +379,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -395,7 +395,7 @@ describe('Recording API Security Tests', () => {
});
it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.speakerSecret
@ -408,7 +408,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.moderatorSecret
@ -450,7 +450,7 @@ describe('Recording API Security Tests', () => {
});
it('should fail when recording access is admin_moderator_speaker and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -467,7 +467,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -484,7 +484,7 @@ describe('Recording API Security Tests', () => {
});
it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.speakerSecret
@ -498,7 +498,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.moderatorSecret
@ -528,7 +528,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -544,7 +544,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -560,7 +560,7 @@ describe('Recording API Security Tests', () => {
});
it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.speakerSecret
@ -573,7 +573,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.moderatorSecret
@ -652,7 +652,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -668,7 +668,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -684,7 +684,7 @@ describe('Recording API Security Tests', () => {
});
it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.speakerSecret
@ -697,7 +697,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.moderatorSecret
@ -728,7 +728,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -745,7 +745,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
@ -762,7 +762,7 @@ describe('Recording API Security Tests', () => {
});
it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.speakerSecret
@ -776,7 +776,7 @@ describe('Recording API Security Tests', () => {
});
it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie(
roomData.room.roomId,
roomData.moderatorSecret

View File

@ -12,7 +12,7 @@ import {
disconnectFakeParticipants,
loginUser,
startTestServer,
updateRecordingAccessPreferencesInRoom
updateRecordingAccessConfigInRoom
} from '../../../helpers/request-helpers.js';
import { RoomData, setupSingleRoom, setupSingleRoomWithRecording } from '../../../helpers/test-scenarios.js';
@ -179,7 +179,7 @@ describe('Room API Security Tests', () => {
});
});
describe('Get Room Preferences Tests', () => {
describe('Get Room Config Tests', () => {
let roomData: RoomData;
beforeAll(async () => {
@ -188,26 +188,26 @@ describe('Room API Security Tests', () => {
it('should succeed when request includes API key', async () => {
const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/preferences`)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY);
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/preferences`)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', adminCookie);
expect(response.status).toBe(200);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app).get(`${ROOMS_PATH}/${roomData.room.roomId}/preferences`);
const response = await request(app).get(`${ROOMS_PATH}/${roomData.room.roomId}/config`);
expect(response.status).toBe(401);
});
it('should succeed when participant is moderator', async () => {
const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/preferences`)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', roomData.moderatorCookie)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(200);
@ -217,7 +217,7 @@ describe('Room API Security Tests', () => {
const newRoomData = await setupSingleRoom();
const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/preferences`)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', newRoomData.moderatorCookie)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(403);
@ -225,7 +225,7 @@ describe('Room API Security Tests', () => {
it('should succeed when participant is speaker', async () => {
const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/preferences`)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', roomData.speakerCookie)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
expect(response.status).toBe(200);
@ -235,21 +235,21 @@ describe('Room API Security Tests', () => {
const newRoomData = await setupSingleRoom();
const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/preferences`)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', newRoomData.speakerCookie)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
expect(response.status).toBe(403);
});
});
describe('Update Room Preferences Tests', () => {
const roomPreferences = {
recordingPreferences: {
describe('Update Room Config Tests', () => {
const roomConfig = {
recordingConfig: {
enabled: false,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
};
let roomId: string;
@ -261,24 +261,22 @@ describe('Room API Security Tests', () => {
it('should succeed when request includes API key', async () => {
const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/preferences`)
.put(`${ROOMS_PATH}/${roomId}/config`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send({ preferences: roomPreferences });
.send({ config: roomConfig });
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/preferences`)
.put(`${ROOMS_PATH}/${roomId}/config`)
.set('Cookie', adminCookie)
.send({ preferences: roomPreferences });
.send({ config: roomConfig });
expect(response.status).toBe(200);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/preferences`)
.send({ preferences: roomPreferences });
const response = await request(app).put(`${ROOMS_PATH}/${roomId}/config`).send({ config: roomConfig });
expect(response.status).toBe(401);
});
});
@ -308,9 +306,7 @@ describe('Room API Security Tests', () => {
});
it('should fail when user is not authenticated', async () => {
const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/status`)
.send({ status: 'open' });
const response = await request(app).put(`${ROOMS_PATH}/${roomId}/status`).send({ status: 'open' });
expect(response.status).toBe(401);
});
});
@ -323,10 +319,7 @@ describe('Room API Security Tests', () => {
});
beforeEach(async () => {
await updateRecordingAccessPreferencesInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER);
});
it('should succeed when no authentication is required and participant is speaker', async () => {
@ -414,7 +407,7 @@ describe('Room API Security Tests', () => {
});
it('should fail when recording access is set to admin only', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN);
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN);
const response = await request(app)
.post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`)

View File

@ -212,12 +212,12 @@
.auto-deletion-expired {
font-weight: var(--ov-meet-font-weight-semibold);
color: var(--ov-meet-color-error);
color: var(--ov-meet-color-error);
.deletion-icon {
color: var(--ov-meet-color-error);
}
}
.deletion-icon {
color: var(--ov-meet-color-error);
}
}
}
.no-data {
@ -240,7 +240,7 @@
color: var(--ov-meet-color-primary);
}
&.room-preferences-btn {
&.room-config-btn {
color: var(--ov-meet-icon-settings);
}

View File

@ -56,7 +56,7 @@
<ov-room-wizard-room-details></ov-room-wizard-room-details>
}
@case ('recording') {
<ov-recording-preferences></ov-recording-preferences>
<ov-recording-config></ov-recording-config>
}
@case ('recordingTrigger') {
<ov-recording-trigger></ov-recording-trigger>
@ -64,8 +64,8 @@
@case ('recordingLayout') {
<ov-recording-layout></ov-recording-layout>
}
@case ('preferences') {
<ov-room-preferences></ov-room-preferences>
@case ('config') {
<ov-room-config></ov-room-config>
}
}
}

View File

@ -10,11 +10,11 @@ import { WizardNavigationConfig, WizardStep } from '@lib/models';
import { NavigationService, NotificationService, RoomService, RoomWizardStateService } from '@lib/services';
import { MeetRoomOptions } from '@lib/typings/ce';
import { RoomBasicCreationComponent } from '../room-basic-creation/room-basic-creation.component';
import { RecordingConfigComponent } from './steps/recording-config/recording-config.component';
import { RecordingLayoutComponent } from './steps/recording-layout/recording-layout.component';
import { RecordingPreferencesComponent } from './steps/recording-preferences/recording-preferences.component';
import { RecordingTriggerComponent } from './steps/recording-trigger/recording-trigger.component';
import { RoomConfigComponent } from './steps/room-config/room-config.component';
import { RoomWizardRoomDetailsComponent } from './steps/room-details/room-details.component';
import { RoomPreferencesComponent } from './steps/room-preferences/room-preferences.component';
@Component({
selector: 'ov-room-wizard',
@ -29,10 +29,10 @@ import { RoomPreferencesComponent } from './steps/room-preferences/room-preferen
MatSlideToggleModule,
RoomBasicCreationComponent,
RoomWizardRoomDetailsComponent,
RecordingPreferencesComponent,
RecordingConfigComponent,
RecordingTriggerComponent,
RecordingLayoutComponent,
RoomPreferencesComponent
RoomConfigComponent
],
templateUrl: './room-wizard.component.html',
styleUrl: './room-wizard.component.scss'
@ -89,8 +89,8 @@ export class RoomWizardComponent implements OnInit {
if (!this.roomId) return;
try {
const { roomName, autoDeletionDate, preferences } = await this.roomService.getRoom(this.roomId);
this.existingRoomData = { roomName, autoDeletionDate, preferences };
const { roomName, autoDeletionDate, config } = await this.roomService.getRoom(this.roomId);
this.existingRoomData = { roomName, autoDeletionDate, config };
if (this.existingRoomData) {
this.isBasicCreation.set(false);
}
@ -154,8 +154,8 @@ export class RoomWizardComponent implements OnInit {
this.isCreatingRoom.set(true);
try {
if (this.editMode && this.roomId && roomOptions.preferences) {
await this.roomService.updateRoomPreferences(this.roomId, roomOptions.preferences);
if (this.editMode && this.roomId && roomOptions.config) {
await this.roomService.updateRoomConfig(this.roomId, roomOptions.config);
await this.navigationService.navigateTo('rooms', undefined, true);
this.notificationService.showSnackbar('Room updated successfully');
} else {

View File

@ -1,9 +1,9 @@
<div class="recording-preferences-step fade-in">
<div class="recording-config-step fade-in">
<!-- Header Section -->
<header class="step-header">
<mat-icon class="ov-recording-icon step-icon">video_library</mat-icon>
<div class="step-title-group">
<h3 class="step-title">Recording Preferences</h3>
<h3 class="step-title">Recording Config</h3>
<p class="step-description">Choose whether to enable recording capabilities for this room</p>
</div>
</header>

View File

@ -1,6 +1,6 @@
@import '../../../../../../../../../../src/assets/styles/design-tokens';
.recording-preferences-step {
.recording-config-step {
@include ov-page-content;
@include ov-container;
@ -50,7 +50,6 @@
padding: var(--ov-meet-spacing-lg) var(--ov-meet-spacing-lg) 0 var(--ov-meet-spacing-lg);
overflow: hidden;
.access-header {
display: flex;
align-items: flex-start;

View File

@ -18,7 +18,7 @@ interface RecordingAccessOption {
}
@Component({
selector: 'ov-recording-preferences',
selector: 'ov-recording-config',
standalone: true,
imports: [
CommonModule,
@ -31,10 +31,10 @@ interface RecordingAccessOption {
MatFormFieldModule,
SelectableCardComponent
],
templateUrl: './recording-preferences.component.html',
styleUrl: './recording-preferences.component.scss'
templateUrl: './recording-config.component.html',
styleUrl: './recording-config.component.scss'
})
export class RecordingPreferencesComponent implements OnDestroy {
export class RecordingConfigComponent implements OnDestroy {
recordingForm: FormGroup;
isAnimatingOut = false;
@ -89,8 +89,8 @@ export class RecordingPreferencesComponent implements OnDestroy {
const enabled = formValue.recordingEnabled === 'enabled';
const stepData: any = {
preferences: {
recordingPreferences: {
config: {
recordingConfig: {
enabled,
...(enabled && { allowAccessTo: formValue.allowAccessTo })
}

View File

@ -1,18 +1,18 @@
<div class="room-preferences-step fade-in">
<div class="room-config-step fade-in">
<!-- Header Section -->
<header class="step-header">
<mat-icon class="ov-room-icon step-icon">video_chat</mat-icon>
<div class="step-title-group">
<h3 class="step-title">Room Preferences</h3>
<h3 class="step-title">Room Config</h3>
<p class="step-description">Configure additional features and functionality for your room</p>
</div>
</header>
<!-- Form Section -->
<main class="step-content">
<form [formGroup]="preferencesForm" class="preferences-form">
<!-- Preferences Cards Grid -->
<div class="preferences-grid">
<form [formGroup]="configForm" class="config-form">
<!-- Config Cards Grid -->
<div class="config-grid">
<!-- Chat Settings Card -->
<mat-card class="preference-card">
<mat-card-content>

View File

@ -1,6 +1,6 @@
@import '../../../../../../../../../../src/assets/styles/design-tokens';
.room-preferences-step {
.room-config-step {
@include ov-page-content;
@include ov-container;
@ -41,13 +41,13 @@
.step-content {
margin-bottom: var(--ov-meet-spacing-md);
.preferences-form {
.config-form {
display: flex;
flex-direction: column;
}
}
.preferences-grid {
.config-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: var(--ov-meet-spacing-md);
@ -123,7 +123,7 @@
// Responsive design
@media (max-width: 1024px) {
.room-preferences-step {
.room-config-step {
// padding: var(--ov-meet-spacing-xs);
.step-header {
@ -131,14 +131,14 @@
margin-bottom: var(--ov-meet-spacing-md);
}
.preferences-grid {
.config-grid {
gap: var(--ov-meet-spacing-sm);
}
}
}
@media (max-width: 768px) {
.room-preferences-step {
.room-config-step {
.step-header {
.step-title-group {
.step-title {
@ -151,7 +151,7 @@
}
}
.preferences-grid {
.config-grid {
grid-template-columns: 1fr;
}
@ -170,7 +170,7 @@
}
@media (max-width: 480px) {
.room-preferences-step {
.room-config-step {
.preference-card {
.card-header {
.icon-title-group {

View File

@ -7,22 +7,22 @@ import { RoomWizardStateService } from '@lib/services';
import { Subject, takeUntil } from 'rxjs';
@Component({
selector: 'ov-room-preferences',
selector: 'ov-room-config',
standalone: true,
imports: [ReactiveFormsModule, MatCardModule, MatIconModule, MatSlideToggleModule],
templateUrl: './room-preferences.component.html',
styleUrl: './room-preferences.component.scss'
templateUrl: './room-config.component.html',
styleUrl: './room-config.component.scss'
})
export class RoomPreferencesComponent implements OnDestroy {
preferencesForm: FormGroup;
export class RoomConfigComponent implements OnDestroy {
configForm: FormGroup;
private destroy$ = new Subject<void>();
constructor(private wizardService: RoomWizardStateService) {
const currentStep = this.wizardService.currentStep();
this.preferencesForm = currentStep!.formGroup;
this.configForm = currentStep!.formGroup;
this.preferencesForm.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.configForm.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.saveFormData(value);
});
}
@ -34,34 +34,34 @@ export class RoomPreferencesComponent implements OnDestroy {
private saveFormData(formValue: any): void {
const stepData: any = {
preferences: {
chatPreferences: {
config: {
chatConfig: {
enabled: formValue.chatEnabled
},
virtualBackgroundPreferences: {
virtualBackgroundConfig: {
enabled: formValue.virtualBackgroundsEnabled
}
}
};
this.wizardService.updateStepData('preferences', stepData);
this.wizardService.updateStepData('config', stepData);
}
onChatToggleChange(event: any): void {
const isEnabled = event.checked;
this.preferencesForm.patchValue({ chatEnabled: isEnabled });
this.configForm.patchValue({ chatEnabled: isEnabled });
}
onVirtualBackgroundToggleChange(event: any): void {
const isEnabled = event.checked;
this.preferencesForm.patchValue({ virtualBackgroundsEnabled: isEnabled });
this.configForm.patchValue({ virtualBackgroundsEnabled: isEnabled });
}
get chatEnabled(): boolean {
return this.preferencesForm.value.chatEnabled || false;
return this.configForm.value.chatEnabled || false;
}
get virtualBackgroundsEnabled(): boolean {
return this.preferencesForm.value.virtualBackgroundsEnabled || false;
return this.configForm.value.virtualBackgroundsEnabled || false;
}
}

View File

@ -31,7 +31,7 @@ import {
import { ILogger, LoggerService } from 'openvidu-components-angular';
@Component({
selector: 'ov-room-preferences',
selector: 'ov-room-config',
standalone: true,
imports: [
MatListModule,
@ -109,7 +109,7 @@ export class RoomsComponent implements OnInit {
this.openRoom(action.rooms[0]);
break;
case 'edit':
await this.editRoomPreferences(action.rooms[0]);
await this.editRoomConfig(action.rooms[0]);
break;
case 'copyModeratorLink':
this.copyModeratorLink(action.rooms[0]);
@ -254,12 +254,12 @@ export class RoomsComponent implements OnInit {
window.open(room.moderatorUrl, '_blank');
}
private async editRoomPreferences(room: MeetRoom) {
private async editRoomConfig(room: MeetRoom) {
try {
await this.navigationService.navigateTo(`rooms/${room.roomId}/edit`);
} catch (error) {
this.notificationService.showSnackbar('Error navigating to room preferences');
this.log.e('Error navigating to room preferences:', error);
this.notificationService.showSnackbar('Error navigating to room config');
this.log.e('Error navigating to room config:', error);
}
}

View File

@ -39,7 +39,7 @@ import {
} from '@lib/typings/ce';
import {
MeetParticipantRoleUpdatedPayload,
MeetRoomPreferencesUpdatedPayload,
MeetRoomConfigUpdatedPayload,
MeetSignalType
} from '@lib/typings/ce/event.model';
import {
@ -270,7 +270,7 @@ export class MeetingComponent implements OnInit {
try {
await this.generateParticipantToken();
await this.addParticipantNameToUrl();
await this.roomService.loadRoomPreferences(this.roomId);
await this.roomService.loadRoomConfig(this.roomId);
this.showMeeting = true;
// Subscribe to remote participants updates
@ -357,13 +357,13 @@ export class MeetingComponent implements OnInit {
break;
}
case MeetSignalType.MEET_ROOM_PREFERENCES_UPDATED: {
// Update room preferences
const { preferences } = event as MeetRoomPreferencesUpdatedPayload;
this.featureConfService.setRoomPreferences(preferences);
case MeetSignalType.MEET_ROOM_CONFIG_UPDATED: {
// Update room config
const { config } = event as MeetRoomConfigUpdatedPayload;
this.featureConfService.setRoomConfig(config);
// Refresh recording token if recording is enabled
if (preferences.recordingPreferences.enabled) {
if (config.recordingConfig.enabled) {
try {
await this.recordingService.generateRecordingToken(this.roomId, this.roomSecret);
} catch (error) {

View File

@ -1,6 +1,6 @@
import { computed, Injectable, signal } from '@angular/core';
import {
MeetRoomPreferences,
MeetRoomConfig,
ParticipantPermissions,
ParticipantRole,
RecordingPermissions,
@ -57,7 +57,7 @@ const DEFAULT_FEATURES: ApplicationFeatures = {
/**
* Centralized service to manage feature configuration
* based on room preferences and participant permissions
* based on room config and participant permissions
*/
@Injectable({
providedIn: 'root'
@ -66,7 +66,7 @@ export class FeatureConfigurationService {
protected log;
// Signals to handle reactive
protected roomPreferences = signal<MeetRoomPreferences | undefined>(undefined);
protected roomConfig = signal<MeetRoomConfig | undefined>(undefined);
protected participantPermissions = signal<ParticipantPermissions | undefined>(undefined);
protected participantRole = signal<ParticipantRole | undefined>(undefined);
protected recordingPermissions = signal<RecordingPermissions | undefined>(undefined);
@ -74,7 +74,7 @@ export class FeatureConfigurationService {
// Computed signal to derive features based on current configurations
public readonly features = computed<ApplicationFeatures>(() =>
this.calculateFeatures(
this.roomPreferences(),
this.roomConfig(),
this.participantPermissions(),
this.participantRole(),
this.recordingPermissions()
@ -86,11 +86,11 @@ export class FeatureConfigurationService {
}
/**
* Updates room preferences
* Updates room config
*/
setRoomPreferences(preferences: MeetRoomPreferences): void {
this.log.d('Updating room preferences', preferences);
this.roomPreferences.set(preferences);
setRoomConfig(config: MeetRoomConfig): void {
this.log.d('Updating room config', config);
this.roomConfig.set(config);
}
/**
@ -128,7 +128,7 @@ export class FeatureConfigurationService {
* Core logic to calculate features based on all configurations
*/
protected calculateFeatures(
roomPrefs?: MeetRoomPreferences,
roomPrefs?: MeetRoomConfig,
participantPerms?: ParticipantPermissions,
role?: ParticipantRole,
recordingPerms?: RecordingPermissions
@ -138,9 +138,9 @@ export class FeatureConfigurationService {
// Apply room configurations
if (roomPrefs) {
features.showRecordingPanel = roomPrefs.recordingPreferences.enabled;
features.showChat = roomPrefs.chatPreferences.enabled;
features.showBackgrounds = roomPrefs.virtualBackgroundPreferences.enabled;
features.showRecordingPanel = roomPrefs.recordingConfig.enabled;
features.showChat = roomPrefs.chatConfig.enabled;
features.showBackgrounds = roomPrefs.virtualBackgroundConfig.enabled;
}
// Apply participant permissions (these can restrict enabled features)
@ -184,7 +184,7 @@ export class FeatureConfigurationService {
* Resets all configurations to their initial values
*/
reset(): void {
this.roomPreferences.set(undefined);
this.roomConfig.set(undefined);
this.participantPermissions.set(undefined);
this.participantRole.set(undefined);
}

View File

@ -2,12 +2,12 @@ import { Injectable } from '@angular/core';
import { FeatureConfigurationService, HttpService, ParticipantService, SessionStorageService } from '@lib/services';
import {
MeetRoom,
MeetRoomConfig,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomDeletionSuccessCode,
MeetRoomFilters,
MeetRoomOptions,
MeetRoomPreferences,
MeetRoomRoleAndPermissions,
MeetRoomStatus
} from '@lib/typings/ce';
@ -176,52 +176,52 @@ export class RoomService {
}
/**
* Retrieves the preferences for a specific room.
* Retrieves the config for a specific room.
*
* @param roomId - The unique identifier of the room
* @return A promise that resolves to the MeetRoomPreferences object
* @return A promise that resolves to the MeetRoomConfig object
*/
async getRoomPreferences(roomId: string): Promise<MeetRoomPreferences> {
this.log.d('Fetching room preferences for roomId:', roomId);
async getRoomConfig(roomId: string): Promise<MeetRoomConfig> {
this.log.d('Fetching room config for roomId:', roomId);
try {
const path = `${this.ROOMS_API}/${roomId}/preferences`;
const path = `${this.ROOMS_API}/${roomId}/config`;
const headers = this.participantService.getParticipantRoleHeader();
const preferences = await this.httpService.getRequest<MeetRoomPreferences>(path, headers);
return preferences;
const config = await this.httpService.getRequest<MeetRoomConfig>(path, headers);
return config;
} catch (error) {
this.log.e('Error fetching room preferences', error);
throw new Error(`Failed to fetch room preferences for roomId: ${roomId}`);
this.log.e('Error fetching room config', error);
throw new Error(`Failed to fetch room config for roomId: ${roomId}`);
}
}
/**
* Loads the room preferences and updates the feature configuration service.
* Loads the room config and updates the feature configuration service.
*
* @param roomId - The unique identifier of the room
*/
async loadRoomPreferences(roomId: string): Promise<void> {
async loadRoomConfig(roomId: string): Promise<void> {
try {
const preferences = await this.getRoomPreferences(roomId);
this.featureConfService.setRoomPreferences(preferences);
console.log('Room preferences loaded:', preferences);
const config = await this.getRoomConfig(roomId);
this.featureConfService.setRoomConfig(config);
console.log('Room config loaded:', config);
} catch (error) {
this.log.e('Error loading room preferences', error);
throw new Error('Failed to load room preferences');
this.log.e('Error loading room config', error);
throw new Error('Failed to load room config');
}
}
/**
* Saves new room preferences.
* Saves new room config.
*
* @param roomId - The unique identifier of the room
* @param preferences - The room preferences to be saved.
* @returns A promise that resolves when the preferences have been saved.
* @param config - The room config to be saved.
* @returns A promise that resolves when the config have been saved.
*/
async updateRoomPreferences(roomId: string, preferences: MeetRoomPreferences): Promise<void> {
this.log.d('Saving room preferences', preferences);
const path = `${this.ROOMS_API}/${roomId}/preferences`;
await this.httpService.putRequest(path, { preferences });
async updateRoomConfig(roomId: string, config: MeetRoomConfig): Promise<void> {
this.log.d('Saving room config', config);
const path = `${this.ROOMS_API}/${roomId}/config`;
await this.httpService.putRequest(path, { config });
}
/**

View File

@ -3,20 +3,20 @@ import { AbstractControl, FormBuilder, ValidationErrors, Validators } from '@ang
import { WizardNavigationConfig, WizardStep } from '@lib/models';
import {
MeetRecordingAccess,
MeetRoomConfig,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomOptions,
MeetRoomPreferences
MeetRoomOptions
} from '@lib/typings/ce';
// Default room preferences following the app's defaults
const DEFAULT_PREFERENCES: MeetRoomPreferences = {
recordingPreferences: {
// Default room config following the app's defaults
const DEFAULT_CONFIG: MeetRoomConfig = {
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
};
/**
@ -32,7 +32,7 @@ export class RoomWizardStateService {
private _visibleSteps = computed(() => this._steps().filter((step) => step.isVisible));
private _currentStepIndex = signal<number>(0);
private _roomOptions = signal<MeetRoomOptions>({
preferences: DEFAULT_PREFERENCES
config: DEFAULT_CONFIG
});
public readonly steps = computed(() => this._steps());
@ -60,9 +60,9 @@ export class RoomWizardStateService {
// Initialize room options with defaults merged with existing data
const initialRoomOptions: MeetRoomOptions = {
...existingData,
preferences: {
...DEFAULT_PREFERENCES,
...(existingData?.preferences || {})
config: {
...DEFAULT_CONFIG,
...(existingData?.config || {})
}
};
@ -156,10 +156,8 @@ export class RoomWizardStateService {
isActive: editMode, // Only active in edit mode
isVisible: true,
formGroup: this.formBuilder.group({
recordingEnabled: initialRoomOptions.preferences!.recordingPreferences.enabled
? 'enabled'
: 'disabled',
allowAccessTo: initialRoomOptions.preferences!.recordingPreferences.allowAccessTo
recordingEnabled: initialRoomOptions.config!.recordingConfig.enabled ? 'enabled' : 'disabled',
allowAccessTo: initialRoomOptions.config!.recordingConfig.allowAccessTo
})
},
{
@ -183,14 +181,14 @@ export class RoomWizardStateService {
})
},
{
id: 'preferences',
id: 'config',
label: 'Room Features',
isCompleted: editMode, // In edit mode, all editable steps are completed
isActive: false,
isVisible: true,
formGroup: this.formBuilder.group({
chatEnabled: initialRoomOptions.preferences!.chatPreferences.enabled,
virtualBackgroundsEnabled: initialRoomOptions.preferences!.virtualBackgroundPreferences.enabled
chatEnabled: initialRoomOptions.config!.chatConfig.enabled,
virtualBackgroundsEnabled: initialRoomOptions.config!.virtualBackgroundConfig.enabled
})
}
];
@ -234,13 +232,13 @@ export class RoomWizardStateService {
case 'recording':
updatedOptions = {
...currentOptions,
preferences: {
...currentOptions.preferences,
recordingPreferences: {
...currentOptions.preferences?.recordingPreferences,
...stepData.preferences?.recordingPreferences
config: {
...currentOptions.config,
recordingConfig: {
...currentOptions.config?.recordingConfig,
...stepData.config?.recordingConfig
}
} as MeetRoomPreferences
} as MeetRoomConfig
};
break;
case 'recordingTrigger':
@ -248,24 +246,24 @@ export class RoomWizardStateService {
// These steps don't update room options
updatedOptions = { ...currentOptions };
break;
case 'preferences':
case 'config':
updatedOptions = {
...currentOptions,
preferences: {
...currentOptions.preferences,
chatPreferences: {
...currentOptions.preferences?.chatPreferences,
...stepData.preferences?.chatPreferences
config: {
...currentOptions.config,
chatConfig: {
...currentOptions.config?.chatConfig,
...stepData.config?.chatConfig
},
virtualBackgroundPreferences: {
...currentOptions.preferences?.virtualBackgroundPreferences,
...stepData.preferences?.virtualBackgroundPreferences
virtualBackgroundConfig: {
...currentOptions.config?.virtualBackgroundConfig,
...stepData.config?.virtualBackgroundConfig
},
recordingPreferences: {
...currentOptions.preferences?.recordingPreferences,
...stepData.preferences?.recordingPreferences
recordingConfig: {
...currentOptions.config?.recordingConfig,
...stepData.config?.recordingConfig
}
} as MeetRoomPreferences
} as MeetRoomConfig
};
break;
default:
@ -284,8 +282,8 @@ export class RoomWizardStateService {
private updateStepsVisibility(): void {
const currentSteps = this._steps();
const currentOptions = this._roomOptions();
// TODO: Uncomment when recording preferences are implemented
const recordingEnabled = false; // currentOptions.preferences?.recordingPreferences.enabled ?? false;
// TODO: Uncomment when recording config is fully implemented
const recordingEnabled = false; // currentOptions.config?.recordingConfig.enabled ?? false;
// Update recording steps visibility based on recordingEnabled
const updatedSteps = currentSteps.map((step) => {
@ -414,7 +412,7 @@ export class RoomWizardStateService {
*/
resetWizard(): void {
const defaultOptions: MeetRoomOptions = {
preferences: DEFAULT_PREFERENCES
config: DEFAULT_CONFIG
};
this._roomOptions.set(defaultOptions);
this._steps.set([]);

View File

@ -11,7 +11,7 @@
"test:e2e-core-room": "playwright test tests/e2e/core/room.test.ts",
"test:e2e-core-events": "playwright test tests/e2e/core/events.test.ts",
"test:e2e-core-webhooks": "playwright test tests/e2e/core/webhooks.test.ts",
"test:e2e-ui-features": "playwright test tests/e2e/ui-feature-preferences.test.ts",
"test:e2e-ui-features": "playwright test tests/e2e/ui-feature-config.test.ts",
"test:e2e-recording-access": "playwright test tests/e2e/recording-access.test.ts",
"lint": "eslint 'src/**/*.ts'"
},

View File

@ -1,5 +1,5 @@
import { test } from '@playwright/test';
import { MeetRecordingAccess } from '../../../../typings/src/room-preferences';
import { MeetRecordingAccess } from '../../../../typings/src/room-config';
import { MEET_TESTAPP_URL } from '../config';
import {
accessRoomAs,
@ -11,7 +11,7 @@ import {
loginAsAdmin,
prepareForJoiningRoom,
startStopRecording,
updateRoomPreferences,
updateRoomConfig,
viewRecordingsAs,
waitForElementInIframe
} from '../helpers/function-helpers';
@ -72,15 +72,15 @@ test.describe('Recording Access Tests', () => {
});
test('should moderator not be able to access recording when access level is set to admin', async ({ page }) => {
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -93,15 +93,15 @@ test.describe('Recording Access Tests', () => {
});
test('should speaker not be able to access recording when access level is set to admin', async ({ page }) => {
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -114,15 +114,15 @@ test.describe('Recording Access Tests', () => {
});
test('should allow moderator to access recording when access level is set to moderator', async ({ page }) => {
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -135,15 +135,15 @@ test.describe('Recording Access Tests', () => {
});
test('should speaker not be able to access recording when access level is set to moderator', async ({ page }) => {
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -156,15 +156,15 @@ test.describe('Recording Access Tests', () => {
});
test('should allow moderators to access recording when access level is set to speaker', async ({ page }) => {
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -177,15 +177,15 @@ test.describe('Recording Access Tests', () => {
});
test('should allow speaker to access recording when access level is set to speaker', async ({ page }) => {
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);

View File

@ -1,5 +1,5 @@
import { expect, test } from '@playwright/test';
import { MeetRecordingAccess } from '../../../../typings/src/room-preferences';
import { MeetRecordingAccess } from '../../../../typings/src/room-config';
import { MEET_TESTAPP_URL } from '../config';
import {
applyVirtualBackground,
@ -14,14 +14,14 @@ import {
loginAsAdmin,
openMoreOptionsMenu,
prepareForJoiningRoom,
updateRoomPreferences,
updateRoomConfig,
waitForElementInIframe,
waitForVirtualBackgroundToApply
} from '../helpers/function-helpers';
let subscribedToAppErrors = false;
test.describe('UI Feature Preferences Tests', () => {
test.describe('UI Feature Config Tests', () => {
let roomId: string;
let participantName: string;
let adminCookie: string;
@ -73,15 +73,15 @@ test.describe('UI Feature Preferences Tests', () => {
});
test('should show chat button when chat is enabled', async ({ page }) => {
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -97,15 +97,15 @@ test.describe('UI Feature Preferences Tests', () => {
test('should hide chat button when chat is disabled', async ({ page }) => {
// Disable chat via API
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: false },
recordingPreferences: {
chatConfig: { enabled: false },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -126,15 +126,15 @@ test.describe('UI Feature Preferences Tests', () => {
test.describe('Recording Feature', () => {
test('should show recording button for moderators', async ({ page }) => {
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -160,15 +160,15 @@ test.describe('UI Feature Preferences Tests', () => {
});
test('should not show recording button for speaker', async ({ page }) => {
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -185,15 +185,15 @@ test.describe('UI Feature Preferences Tests', () => {
test('should not show recording button for moderators when recording is disabled', async ({ page }) => {
// Disable recording via API
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: false,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -226,15 +226,15 @@ test.describe('UI Feature Preferences Tests', () => {
});
test('should show virtual background button when enabled', async ({ page }) => {
// Ensure virtual backgrounds are enabled
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -255,15 +255,15 @@ test.describe('UI Feature Preferences Tests', () => {
test('should hide virtual background button when disabled', async ({ page }) => {
// Disable virtual backgrounds via API
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: false }
virtualBackgroundConfig: { enabled: false }
},
adminCookie
);
@ -284,15 +284,15 @@ test.describe('UI Feature Preferences Tests', () => {
page
}) => {
// Ensure virtual backgrounds are enabled
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: true }
virtualBackgroundConfig: { enabled: true }
},
adminCookie
);
@ -305,15 +305,15 @@ test.describe('UI Feature Preferences Tests', () => {
await waitForVirtualBackgroundToApply(page);
// Now disable virtual backgrounds
await updateRoomPreferences(
await updateRoomConfig(
roomId,
{
chatPreferences: { enabled: true },
recordingPreferences: {
chatConfig: { enabled: true },
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
virtualBackgroundPreferences: { enabled: false }
virtualBackgroundConfig: { enabled: false }
},
adminCookie
);

View File

@ -1,7 +1,7 @@
import { expect, FrameLocator, Locator, Page } from '@playwright/test';
import * as fs from 'fs';
import { PNG } from 'pngjs';
import { MeetRecordingAccess, MeetRoomPreferences } from '../../../../typings/src/room-preferences';
import { MeetRecordingAccess, MeetRoomConfig } from '../../../../typings/src/room-config';
import { MEET_ADMIN_PASSWORD, MEET_ADMIN_USER, MEET_API_KEY, MEET_API_URL, MEET_TESTAPP_URL } from '../config';
/**
@ -88,21 +88,18 @@ export async function interactWithElementInIframe(
}
}
// Helper function to get default room preferences
const getDefaultRoomPreferences = (): MeetRoomPreferences => ({
recordingPreferences: {
// Helper function to get default room config
const getDefaultRoomConfig = (): MeetRoomConfig => ({
recordingConfig: {
enabled: true,
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
},
chatPreferences: { enabled: true },
virtualBackgroundPreferences: { enabled: true }
chatConfig: { enabled: true },
virtualBackgroundConfig: { enabled: true }
});
// Helper function to create a room for testing
export const createTestRoom = async (
roomName: string,
preferences: MeetRoomPreferences = getDefaultRoomPreferences()
) => {
export const createTestRoom = async (roomName: string, config: MeetRoomConfig = getDefaultRoomConfig()) => {
const response = await fetch(`${MEET_API_URL}/api/v1/rooms`, {
method: 'POST',
headers: {
@ -112,7 +109,7 @@ export const createTestRoom = async (
body: JSON.stringify({
roomName,
autoDeletionDate: new Date(Date.now() + 61 * 60 * 1000).getTime(), // 1 hour from now
preferences
config
})
});
@ -126,19 +123,19 @@ export const createTestRoom = async (
return room.roomId;
};
// Helper function to update room preferences via REST API
export const updateRoomPreferences = async (roomId: string, preferences: any, adminCookie: string) => {
const response = await fetch(`${MEET_API_URL}/api/v1/rooms/${roomId}/preferences`, {
// Helper function to update room config via REST API
export const updateRoomConfig = async (roomId: string, config: any, adminCookie: string) => {
const response = await fetch(`${MEET_API_URL}/api/v1/rooms/${roomId}/config`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Cookie: adminCookie
},
body: JSON.stringify({ preferences })
body: JSON.stringify({ config })
});
if (!response.ok) {
throw new Error(`Failed to update room preferences: ${response.status} ${await response.text()}`);
throw new Error(`Failed to update room config: ${response.status} ${await response.text()}`);
}
return response.json();

View File

@ -1,168 +1,168 @@
:root {
--primary-color: #4a90e2;
--secondary-color: #f4f4f4;
--primary-color: #4a90e2;
--secondary-color: #f4f4f4;
}
body,
html {
height: 100vh;
width: 100%;
margin: 0;
background-color: #f8f9fa;
padding: 0px;
box-sizing: border-box;
height: 100vh;
width: 100%;
margin: 0;
background-color: #f8f9fa;
padding: 0px;
box-sizing: border-box;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
display: flex;
gap: 25px;
min-height: calc(80vh - 40px);
max-height: calc(80vh - 40px);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
max-width: 1200px;
margin: 0 auto;
display: flex;
gap: 25px;
min-height: calc(80vh - 40px);
max-height: calc(80vh - 40px);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.action-buttons-container {
display: inline-flex;
gap: 2px;
display: inline-flex;
gap: 2px;
}
.rooms-container {
flex: 2;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden;
flex: 2;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden;
}
.create-room {
flex: 1;
background: var(--secondary-color);
padding: 15px;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden;
flex: 1;
background: var(--secondary-color);
padding: 15px;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden;
}
.create-room p,
.create-room h2 {
margin-bottom: 0 !important ;
font-size: 0.9rem;
margin-bottom: 0 !important ;
font-size: 0.9rem;
}
.create-room h2 {
font-size: 1.1rem; /* Smaller title */
font-size: 1.1rem; /* Smaller title */
}
/* Collapsible sections for room creation form */
.preferences-accordion {
flex: 1;
overflow-y: auto;
.config-accordion {
flex: 1;
overflow-y: auto;
}
.accordion-item {
border: none;
margin-bottom: 0.25rem;
border: none;
margin-bottom: 0.25rem;
}
.accordion-button {
background-color: var(--primary-color);
color: white;
border-radius: 0.25rem !important; /* Smaller radius */
font-weight: 500; /* Less bold */
font-size: 0.8rem; /* Smaller text */
padding: 0.4rem 0.8rem; /* Much smaller padding */
min-height: auto; /* Remove default height */
background-color: var(--primary-color);
color: white;
border-radius: 0.25rem !important; /* Smaller radius */
font-weight: 500; /* Less bold */
font-size: 0.8rem; /* Smaller text */
padding: 0.4rem 0.8rem; /* Much smaller padding */
min-height: auto; /* Remove default height */
}
.accordion-button:not(.collapsed) {
background-color: var(--primary-color);
color: white;
box-shadow: none;
background-color: var(--primary-color);
color: white;
box-shadow: none;
}
.accordion-button:focus {
box-shadow: 0 0 0 0.25rem rgba(74, 144, 226, 0.25);
box-shadow: 0 0 0 0.25rem rgba(74, 144, 226, 0.25);
}
.accordion-body {
padding: 0.5rem; /* Much smaller padding */
background-color: #f8f9fa;
font-size: 0.8rem; /* Smaller text */
padding: 0.5rem; /* Much smaller padding */
background-color: #f8f9fa;
font-size: 0.8rem; /* Smaller text */
}
form-label {
font-size: 0.8rem; /* Smaller labels */
margin-bottom: 0.25rem;
font-size: 0.8rem; /* Smaller labels */
margin-bottom: 0.25rem;
}
.form-control,
.form-select {
font-size: 0.8rem; /* Smaller inputs */
padding: 0.25rem 0.5rem; /* Smaller padding */
font-size: 0.8rem; /* Smaller inputs */
padding: 0.25rem 0.5rem; /* Smaller padding */
}
.form-check {
margin-bottom: 0.25rem; /* Smaller spacing */
margin-bottom: 0.25rem; /* Smaller spacing */
}
.form-check-label {
font-size: 0.8rem; /* Smaller text */
font-size: 0.8rem; /* Smaller text */
}
.form-text {
font-size: 0.7rem !important; /* Much smaller help text */
margin-top: 0.125rem;
font-size: 0.7rem !important; /* Much smaller help text */
margin-top: 0.125rem;
}
#recording-access-section {
background-color: rgba(13, 110, 253, 0.1);
padding: 0.4rem; /* Smaller padding */
border-radius: 0.25rem;
border-left: 2px solid var(--primary-color); /* Thinner border */
margin-top: 0.25rem;
background-color: rgba(13, 110, 253, 0.1);
padding: 0.4rem; /* Smaller padding */
border-radius: 0.25rem;
border-left: 2px solid var(--primary-color); /* Thinner border */
margin-top: 0.25rem;
}
/* Compact button */
.create-room-btn {
font-size: 0.85rem; /* Smaller button text */
padding: 0.4rem 0.8rem; /* Smaller button */
margin-top: 0.5rem; /* Less top margin */
font-size: 0.85rem; /* Smaller button text */
padding: 0.4rem 0.8rem; /* Smaller button */
margin-top: 0.5rem; /* Less top margin */
}
/* Responsive adjustments */
@media (max-height: 600px) {
.container {
flex-direction: column;
max-height: none;
min-height: auto;
}
.container {
flex-direction: column;
max-height: none;
min-height: auto;
}
.create-room {
max-height: 350px; /* Smaller max height */
}
.create-room {
max-height: 350px; /* Smaller max height */
}
}
@media (max-width: 768px) {
.container {
flex-direction: column;
max-height: none;
gap: 15px;
}
.container {
flex-direction: column;
max-height: none;
gap: 15px;
}
body,
html {
padding: 10px;
}
body,
html {
padding: 10px;
}
.create-room {
padding: 10px; /* Even smaller on mobile */
}
.create-room {
padding: 10px; /* Even smaller on mobile */
}
}

View File

@ -181,10 +181,10 @@
</div>
</div>
<!-- Preferences Accordion -->
<div class="preferences-accordion">
<div class="accordion" id="preferencesAccordion">
<!-- Chat Preferences -->
<!-- Config Accordion -->
<div class="config-accordion">
<div class="accordion" id="configAccordion">
<!-- Chat Config -->
<div class="accordion-item">
<h2 class="accordion-header">
<button
@ -194,7 +194,7 @@
data-bs-target="#chatCollapse"
aria-expanded="true"
aria-controls="chatCollapse"
data-testid="chat-preferences-toggle"
data-testid="chat-config-toggle"
>
Chat Settings
</button>
@ -202,13 +202,13 @@
<div
id="chatCollapse"
class="accordion-collapse collapse show"
data-bs-parent="#preferencesAccordion"
data-bs-parent="#configAccordion"
>
<div class="accordion-body">
<div class="form-check">
<input
type="checkbox"
name="preferences.chatPreferences.enabled"
name="config.chatConfig.enabled"
id="chat-enabled"
class="form-check-input"
checked
@ -222,7 +222,7 @@
</div>
</div>
<!-- Recording Preferences -->
<!-- Recording Config -->
<div class="accordion-item">
<h2 class="accordion-header">
<button
@ -232,7 +232,7 @@
data-bs-target="#recordingCollapse"
aria-expanded="false"
aria-controls="recordingCollapse"
data-testid="recording-preferences-toggle"
data-testid="recording-config-toggle"
>
Recording Settings
</button>
@ -240,13 +240,13 @@
<div
id="recordingCollapse"
class="accordion-collapse collapse"
data-bs-parent="#preferencesAccordion"
data-bs-parent="#configAccordion"
>
<div class="accordion-body">
<div class="form-check mb-2">
<input
type="checkbox"
name="preferences.recordingPreferences.enabled"
name="config.recordingConfig.enabled"
id="recording-enabled"
class="form-check-input"
checked
@ -262,7 +262,7 @@
>Recording Access Level</label
>
<select
name="preferences.recordingPreferences.allowAccessTo"
name="config.recordingConfig.allowAccessTo"
id="recording-access"
class="form-select"
data-testid="recording-access-select"
@ -280,7 +280,7 @@
</div>
</div>
<!-- Virtual Background Preferences -->
<!-- Virtual Background Config -->
<div class="accordion-item">
<h2 class="accordion-header">
<button
@ -290,7 +290,7 @@
data-bs-target="#backgroundCollapse"
aria-expanded="false"
aria-controls="backgroundCollapse"
data-testid="background-preferences-toggle"
data-testid="background-config-toggle"
>
Virtual Background Settings
</button>
@ -298,13 +298,13 @@
<div
id="backgroundCollapse"
class="accordion-collapse collapse"
data-bs-parent="#preferencesAccordion"
data-bs-parent="#configAccordion"
>
<div class="accordion-body">
<div class="form-check">
<input
type="checkbox"
name="preferences.virtualBackgroundPreferences.enabled"
name="config.virtualBackgroundConfig.enabled"
id="virtual-background-enabled"
class="form-check-input"
checked

View File

@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import { getAllRooms, createRoom, deleteRoom, deleteAllRooms } from '../services/roomService';
import { deleteAllRecordings, getAllRecordings } from '../services/recordingService';
import { createRoom, deleteAllRooms, deleteRoom, getAllRooms } from '../services/roomService';
export const getHome = async (_req: Request, res: Response) => {
try {
@ -38,12 +38,12 @@ export const postCreateRoom = async (req: Request, res: Response) => {
try {
console.log('Creating room with body:', JSON.stringify(req.body, null, 2));
const { roomName, autoDeletionDate } = req.body;
const preferences = processFormPreferences(req.body);
const config = processFormConfig(req.body);
console.log('Processed preferences:', JSON.stringify(preferences, null, 2));
console.log('Processed config:', JSON.stringify(config, null, 2));
console.log('Room creation parameters:', { roomName, autoDeletionDate });
const result = await createRoom({ roomName, autoDeletionDate, preferences });
const result = await createRoom({ roomName, autoDeletionDate, config });
console.log('Room created successfully:', result);
res.redirect('/');
} catch (error) {
@ -154,24 +154,24 @@ export const deleteAllRecordingsCtrl = async (_req: Request, res: Response) => {
};
/**
* Converts flat form data to nested MeetRoomPreferences object
* Converts flat form data to nested MeetRoomConfig object
*/
const processFormPreferences = (body: any): any => {
const preferences = {
chatPreferences: {
enabled: body['preferences.chatPreferences.enabled'] === 'on'
const processFormConfig = (body: any): any => {
const config = {
chatConfig: {
enabled: body['config.chatConfig.enabled'] === 'on'
},
recordingPreferences: {
enabled: body['preferences.recordingPreferences.enabled'] === 'on',
recordingConfig: {
enabled: body['config.recordingConfig.enabled'] === 'on',
// Only include allowAccessTo if recording is enabled
...(body['preferences.recordingPreferences.enabled'] === 'on' && {
allowAccessTo: body['preferences.recordingPreferences.allowAccessTo'] || 'admin_moderator_speaker'
...(body['config.recordingConfig.enabled'] === 'on' && {
allowAccessTo: body['config.recordingConfig.allowAccessTo'] || 'admin_moderator_speaker'
})
},
virtualBackgroundPreferences: {
enabled: body['preferences.virtualBackgroundPreferences.enabled'] === 'on'
virtualBackgroundConfig: {
enabled: body['config.virtualBackgroundConfig.enabled'] === 'on'
}
};
return preferences;
return config;
};

View File

@ -1,14 +1,14 @@
import { ParticipantRole } from './participant.js';
import { MeetRoomPreferences } from './room-preferences.js';
import { MeetRoomConfig } from './room-config.js';
export enum MeetSignalType {
MEET_ROOM_PREFERENCES_UPDATED = 'meet_room_preferences_updated',
MEET_ROOM_CONFIG_UPDATED = 'meet_room_config_updated',
MEET_PARTICIPANT_ROLE_UPDATED = 'meet_participant_role_updated'
}
export interface MeetRoomPreferencesUpdatedPayload {
export interface MeetRoomConfigUpdatedPayload {
roomId: string;
preferences: MeetRoomPreferences;
config: MeetRoomConfig;
timestamp: number;
}
@ -20,4 +20,4 @@ export interface MeetParticipantRoleUpdatedPayload {
timestamp: number;
}
export type MeetSignalPayload = MeetRoomPreferencesUpdatedPayload | MeetParticipantRoleUpdatedPayload;
export type MeetSignalPayload = MeetRoomConfigUpdatedPayload | MeetParticipantRoleUpdatedPayload;

View File

@ -7,7 +7,7 @@ export * from './permissions/openvidu-permissions.js';
export * from './participant.js';
export * from './user.js';
export * from './room-preferences.js';
export * from './room-config.js';
export * from './room.js';
export * from './recording.model.js';
export * from './webhook.model.js';

View File

@ -0,0 +1,30 @@
/**
* Interface representing the config for a room.
*/
export interface MeetRoomConfig {
chatConfig: MeetChatConfig;
recordingConfig: MeetRecordingConfig;
virtualBackgroundConfig: MeetVirtualBackgroundConfig;
}
/**
* Interface representing the config for recordings in a room.
*/
export interface MeetRecordingConfig {
enabled: boolean;
allowAccessTo?: MeetRecordingAccess;
}
export const enum MeetRecordingAccess {
ADMIN = 'admin', // Only admins can access the recording
ADMIN_MODERATOR = 'admin_moderator', // Admins and moderators can access
ADMIN_MODERATOR_SPEAKER = 'admin_moderator_speaker' // Admins, moderators and speakers can access
}
export interface MeetChatConfig {
enabled: boolean;
}
export interface MeetVirtualBackgroundConfig {
enabled: boolean;
}

View File

@ -1,30 +0,0 @@
/**
* Interface representing the preferences for a room.
*/
export interface MeetRoomPreferences {
chatPreferences: MeetChatPreferences;
recordingPreferences: MeetRecordingPreferences;
virtualBackgroundPreferences: MeetVirtualBackgroundPreferences;
}
/**
* Interface representing the preferences for recording.
*/
export interface MeetRecordingPreferences {
enabled: boolean;
allowAccessTo?: MeetRecordingAccess;
}
export const enum MeetRecordingAccess {
ADMIN = 'admin', // Only admins can access the recording
ADMIN_MODERATOR = 'admin_moderator', // Admins and moderators can access
ADMIN_MODERATOR_SPEAKER = 'admin_moderator_speaker' // Admins, moderators and speakers can access
}
export interface MeetChatPreferences {
enabled: boolean;
}
export interface MeetVirtualBackgroundPreferences {
enabled: boolean;
}

View File

@ -1,11 +1,11 @@
import { ParticipantPermissions, ParticipantRole } from './participant.js';
import { MeetRoomPreferences } from './room-preferences.js';
import { MeetRoomConfig } from './room-config.js';
interface BaseRoomOptions {
roomName?: string;
autoDeletionDate?: number;
autoDeletionPolicy?: MeetRoomAutoDeletionPolicy;
preferences?: MeetRoomPreferences;
config?: MeetRoomConfig;
// maxParticipants?: number | null;
}
@ -21,7 +21,7 @@ export interface MeetRoom extends BaseRoomOptions {
roomId: string;
roomName: string;
creationDate: number;
preferences: MeetRoomPreferences;
config: MeetRoomConfig;
moderatorUrl: string;
speakerUrl: string;
status: MeetRoomStatus;