backend: update room retrieval to use selective fields for efficiency

This commit is contained in:
juancarmore 2026-02-26 18:37:48 +01:00
parent 37023fe077
commit 2572fd3960
8 changed files with 45 additions and 46 deletions

View File

@ -15,7 +15,7 @@ export const endMeeting = async (req: Request, res: Response) => {
// Check if the room exists
try {
await roomService.getMeetRoom(roomId);
await roomService.getMeetRoom(roomId, ['roomId']);
} catch (error) {
return handleError(res, error, `getting room '${roomId}'`);
}
@ -53,7 +53,7 @@ export const kickParticipantFromMeeting = async (req: Request, res: Response) =>
// Check if the room exists
try {
await roomService.getMeetRoom(roomId);
await roomService.getMeetRoom(roomId, ['roomId']);
} catch (error) {
return handleError(res, error, `getting room '${roomId}'`);
}

View File

@ -45,7 +45,7 @@ export class RecordingHelper {
const layout = RecordingHelper.extractRecordingLayout(egressInfo);
const encoding = RecordingHelper.extractRecordingEncoding(egressInfo);
const roomService = container.get(RoomService);
const { roomName } = await roomService.getMeetRoom(roomId);
const { roomName } = await roomService.getMeetRoom(roomId, ['roomName']);
return {
recordingId,

View File

@ -2,6 +2,7 @@ import {
MEET_ROOM_EXTRA_FIELDS,
MEET_ROOM_FIELDS,
MeetRoom,
MeetRoomAnonymous,
MeetRoomExtraField,
MeetRoomField,
MeetRoomMemberPermissions,
@ -89,19 +90,19 @@ export class MeetRoomHelper {
}
/**
* Extracts speaker and moderator secrets from a MeetRoom object's URLs.
* Extracts speaker and moderator secrets from MeetRoom anonymous access URLs.
*
* This method parses the 'secret' query parameter from both speaker and moderator
* anonymous access URLs associated with the meeting room.
*
* @param room - The MeetRoom object containing speakerUrl and moderatorUrl properties
* @param room - The anonymous access configuration of the MeetRoom from which to extract secrets.
* @returns An object containing the extracted secrets with the following properties:
* - speakerSecret: The secret extracted from the speaker anonymous access URL
* - moderatorSecret: The secret extracted from the moderator anonymous access URL
*/
static extractSecretsFromRoom(room: MeetRoom): { speakerSecret: string; moderatorSecret: string } {
const speakerUrl = room.anonymous.speaker.accessUrl;
const moderatorUrl = room.anonymous.moderator.accessUrl;
static extractSecretsFromRoom(anonymous: MeetRoomAnonymous): { speakerSecret: string; moderatorSecret: string } {
const speakerUrl = anonymous.speaker.accessUrl;
const moderatorUrl = anonymous.moderator.accessUrl;
const parsedSpeakerUrl = new URL(speakerUrl);
const speakerSecret = parsedSpeakerUrl.searchParams.get('secret') || '';

View File

@ -28,9 +28,9 @@ export const withRecordingEnabled = async (req: Request, res: Response, next: Ne
try {
const { roomId } = req.body as { roomId: string };
const room = await roomService.getMeetRoom(roomId!);
const { config } = await roomService.getMeetRoom(roomId!, ['config']);
if (!room.config.recording.enabled) {
if (!config.recording.enabled) {
logger.debug(`Recording is disabled for room '${roomId}'`);
const error = errorRecordingDisabled(roomId!);
return rejectRequestFromMeetError(res, error);

View File

@ -1,4 +1,5 @@
import {
MeetRecordingConfig,
MeetRecordingEncodingOptions,
MeetRecordingEncodingPreset,
MeetRecordingField,
@ -6,8 +7,6 @@ import {
MeetRecordingInfo,
MeetRecordingLayout,
MeetRecordingStatus,
MeetRoom,
MeetRoomConfig,
MeetRoomMemberPermissions
} from '@openvidu-meet/typings';
import { inject, injectable } from 'inversify';
@ -86,7 +85,7 @@ export class RecordingService {
if (!acquiredLock) throw errorRecordingAlreadyStarted(roomId);
const room = await this.validateRoomForStartRecording(roomId);
const roomRecordingConfig = await this.validateRoomForStartRecording(roomId);
// Manually send the recording signal to OpenVidu Components for avoiding missing event if timeout occurs
// and the egress_started webhook is not received.
@ -131,7 +130,7 @@ export class RecordingService {
// Promise that starts the recording process
const startRecordingPromise = (async (): Promise<MeetRecordingInfo> => {
try {
const options = this.generateCompositeOptionsFromRequest(room.config, configOverride);
const options = this.generateCompositeOptionsFromRequest(roomRecordingConfig, configOverride);
const output = this.generateFileOutputFromRequest(roomId);
const egressInfo = await this.livekitService.startRoomComposite(roomId, output, options);
@ -592,18 +591,18 @@ export class RecordingService {
* Validates that a room exists and has participants before starting a recording.
*
* @param roomId
* @returns The MeetRoom object if validation passes.
* @returns The MeetRecordingConfig object if validation passes.
* @throws Will throw an error if the room does not exist or has no participants.
*/
protected async validateRoomForStartRecording(roomId: string): Promise<MeetRoom> {
protected async validateRoomForStartRecording(roomId: string): Promise<MeetRecordingConfig> {
const roomService = await this.getRoomService();
const room = await roomService.getMeetRoom(roomId);
const { config } = await roomService.getMeetRoom(roomId, ['config']);
const hasParticipants = await this.livekitService.roomHasParticipants(roomId);
if (!hasParticipants) throw errorRoomHasNoParticipants(roomId);
return room;
return config.recording;
}
/**
@ -738,21 +737,20 @@ export class RecordingService {
}
/**
* Generates composite options for recording based on the provided room configuration.
* Generates composite options for recording based on the provided room recording configuration.
* If configOverride is provided, its values will take precedence over room configuration.
*
* @param roomConfig The room configuration
* @param roomRecordingConfig The recording configuration defined for the room
* @param configOverride Optional configuration override from the request
* @returns The generated RoomCompositeOptions object.
*/
protected generateCompositeOptionsFromRequest(
roomConfig: MeetRoomConfig,
roomRecordingConfig: MeetRecordingConfig,
configOverride?: {
layout?: MeetRecordingLayout;
encoding?: MeetRecordingEncodingPreset | MeetRecordingEncodingOptions;
}
): RoomCompositeOptions {
const roomRecordingConfig = roomConfig.recording;
const layout = configOverride?.layout ?? roomRecordingConfig.layout;
const encoding = configOverride?.encoding ?? roomRecordingConfig.encoding;
const encodingOptions = EncodingConverter.toLivekit(encoding);

View File

@ -109,8 +109,8 @@ export class RoomMemberService {
}
// Compute effective permissions
const room = await this.roomService.getMeetRoom(roomId, ['roles']);
const effectivePermissions = this.computeEffectivePermissions(room.roles, baseRole, customPermissions);
const { roles } = await this.roomService.getMeetRoom(roomId, ['roles']);
const effectivePermissions = this.computeEffectivePermissions(roles, baseRole, customPermissions);
const now = Date.now();
const roomMember = {
@ -219,9 +219,9 @@ export class RoomMemberService {
}
// Recompute effective permissions
const room = await this.roomService.getMeetRoom(roomId, ['roles']);
const { roles } = await this.roomService.getMeetRoom(roomId, ['roles']);
member.effectivePermissions = this.computeEffectivePermissions(
room.roles,
roles,
member.baseRole,
member.customPermissions
);
@ -419,14 +419,14 @@ export class RoomMemberService {
} else {
// If secret matches anonymous access URL secret, assign role and permissions based on it
baseRole = await this.getRoomMemberRoleBySecret(roomId, secret);
const room = await this.roomService.getMeetRoom(roomId, ['roles', 'anonymous']);
const { roles, anonymous } = await this.roomService.getMeetRoom(roomId, ['roles', 'anonymous']);
// Check that anonymous access is enabled for the role
if (!room.anonymous[baseRole].enabled) {
if (!anonymous[baseRole].enabled) {
throw errorAnonymousAccessDisabled(roomId, baseRole);
}
effectivePermissions = room.roles[baseRole].permissions;
effectivePermissions = roles[baseRole].permissions;
}
} else {
// Case 2: Authenticated user
@ -491,9 +491,9 @@ export class RoomMemberService {
userId?: string
): Promise<string> {
// Check that room is open
const room = await this.roomService.getMeetRoom(roomId, ['status', 'config']);
const { status, config } = await this.roomService.getMeetRoom(roomId, ['status', 'config']);
if (room.status === MeetRoomStatus.CLOSED) {
if (status === MeetRoomStatus.CLOSED) {
throw errorRoomClosed(roomId);
}
@ -557,7 +557,7 @@ export class RoomMemberService {
customPermissions,
effectivePermissions
};
const roomWithCaptions = room.config.captions.enabled;
const roomWithCaptions = config.captions.enabled;
// Generate token with participant name
return this.tokenService.generateRoomMemberToken({
@ -610,8 +610,8 @@ export class RoomMemberService {
* @throws Error if the provided secret doesn't match any of the room's secrets (unauthorized)
*/
protected async getRoomMemberRoleBySecret(roomId: string, secret: string): Promise<MeetRoomMemberRole> {
const room = await this.roomService.getMeetRoom(roomId, ['roomId', 'anonymous']);
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
const { anonymous } = await this.roomService.getMeetRoom(roomId, ['anonymous']);
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(anonymous);
switch (secret) {
case moderatorSecret:
@ -619,7 +619,7 @@ export class RoomMemberService {
case speakerSecret:
return MeetRoomMemberRole.SPEAKER;
default:
throw errorInvalidRoomSecret(room.roomId, secret);
throw errorInvalidRoomSecret(roomId, secret);
}
}
@ -779,7 +779,7 @@ export class RoomMemberService {
newRole: MeetRoomMemberRole
): Promise<void> {
try {
const meetRoom = await this.roomService.getMeetRoom(roomId, ['roles', 'anonymous']);
const { roles, anonymous } = await this.roomService.getMeetRoom(roomId, ['roles', 'anonymous']);
const participant = await this.getParticipantFromMeeting(roomId, participantIdentity);
const metadata: MeetRoomMemberTokenMetadata = this.tokenService.parseRoomMemberTokenMetadata(
participant.metadata
@ -788,11 +788,11 @@ export class RoomMemberService {
// Update role and permissions in metadata
metadata.baseRole = newRole;
metadata.customPermissions = undefined;
metadata.effectivePermissions = meetRoom.roles[newRole].permissions;
metadata.effectivePermissions = roles[newRole].permissions;
await this.livekitService.updateParticipantMetadata(roomId, participantIdentity, JSON.stringify(metadata));
const { speakerSecret, moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(meetRoom);
const { speakerSecret, moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(anonymous);
const secret = newRole === MeetRoomMemberRole.MODERATOR ? moderatorSecret : speakerSecret;
await this.frontendEventService.sendParticipantRoleUpdatedSignal(
roomId,

View File

@ -313,7 +313,7 @@ export class RoomService {
* @returns A Promise that resolves to true if the room exists, false otherwise
*/
async meetRoomExists(roomId: string): Promise<boolean> {
const meetRoom = await this.roomRepository.findByRoomId(roomId);
const meetRoom = await this.roomRepository.findByRoomId(roomId, ['roomId']);
return !!meetRoom;
}
@ -857,8 +857,8 @@ export class RoomService {
* @throws Error if room not found
*/
async isRoomOwner(roomId: string, userId: string): Promise<boolean> {
const room = await this.getMeetRoom(roomId, ['owner']);
return room.owner === userId;
const { owner } = await this.getMeetRoom(roomId, ['owner']);
return owner === userId;
}
/**
@ -870,8 +870,8 @@ export class RoomService {
* @throws Error if room not found
*/
async isValidRoomSecret(roomId: string, secret: string): Promise<boolean> {
const room = await this.getMeetRoom(roomId, ['anonymous']);
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
const { anonymous } = await this.getMeetRoom(roomId, ['anonymous']);
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(anonymous);
return secret === moderatorSecret || secret === speakerSecret;
}
@ -934,7 +934,7 @@ export class RoomService {
*/
async canUserAccessRoom(roomId: string, user: MeetUser): Promise<boolean> {
// Verify room exists first (throws 404 if not found)
const room = await this.getMeetRoom(roomId, ['owner']);
const { owner } = await this.getMeetRoom(roomId, ['owner']);
if (user.role === MeetUserRole.ADMIN) {
// Admins can access all rooms
@ -942,7 +942,7 @@ export class RoomService {
}
// Users can access rooms they own or are members of
const isOwner = room.owner === user.userId;
const isOwner = owner === user.userId;
if (isOwner) {
return true;

View File

@ -51,7 +51,7 @@ export const setupSingleRoom = async (
});
// Extract the room secrets and generate room member tokens
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room.anonymous);
const [moderatorToken, speakerToken] = await Promise.all([
generateRoomMemberToken(room.roomId, { secret: moderatorSecret, joinMeeting: false }),
generateRoomMemberToken(room.roomId, { secret: speakerSecret, joinMeeting: false })