From 5345963628e292a1d3bb8ecb53ac07d6832bc6df Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Thu, 10 Apr 2025 11:33:38 +0200 Subject: [PATCH] backend: Saved room secrets under recording directory when they do not exist --- backend/src/helpers/room.helper.ts | 22 ++++++++++ .../src/services/livekit-webhook.service.ts | 44 ++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/backend/src/helpers/room.helper.ts b/backend/src/helpers/room.helper.ts index 80f53a6..088b5bc 100644 --- a/backend/src/helpers/room.helper.ts +++ b/backend/src/helpers/room.helper.ts @@ -19,4 +19,26 @@ export class MeetRoomHelper { roomIdPrefix: room.roomIdPrefix }; } + + /** + * Extracts publisher and moderator secrets from a MeetRoom object's URLs. + * + * This method parses the 'secret' query parameter from both publisher and moderator + * room URLs associated with the meeting room. + * + * @param room - The MeetRoom object containing publisherRoomUrl and moderatorRoomUrl properties + * @returns An object containing the extracted secrets with the following properties: + * - publisherSecret: The secret extracted from the publisher room URL + * - moderatorSecret: The secret extracted from the moderator room URL + */ + static extractSecretsFromRoom(room: MeetRoom): { publisherSecret: string; moderatorSecret: string } { + const { publisherRoomUrl, moderatorRoomUrl } = room; + + + const publisherUrl = new URL(publisherRoomUrl); + const publisherSecret = publisherUrl.searchParams.get('secret') || ''; + const moderatorUrl = new URL(moderatorRoomUrl); + const moderatorSecret = moderatorUrl.searchParams.get('secret') || ''; + return { publisherSecret, moderatorSecret }; + } } diff --git a/backend/src/services/livekit-webhook.service.ts b/backend/src/services/livekit-webhook.service.ts index 61c3efa..221d3dc 100644 --- a/backend/src/services/livekit-webhook.service.ts +++ b/backend/src/services/livekit-webhook.service.ts @@ -12,6 +12,7 @@ import { OpenViduWebhookService } from './openvidu-webhook.service.js'; import { MutexService } from './mutex.service.js'; import { SystemEventService } from './system-event.service.js'; import { SystemEventType } from '../models/system-event.model.js'; +import { MeetRoomHelper } from '../helpers/room.helper.js'; @injectable() export class LivekitWebhookService { @@ -154,7 +155,9 @@ export class LivekitWebhookService { if (meetRoom.markedForDeletion) { // If the room is marked for deletion, we need to delete it - this.logger.info(`Deleting room ${room.name} after meeting finished because it was marked for deletion`); + this.logger.info( + `Deleting room ${room.name} after meeting finished because it was marked for deletion` + ); this.roomService.bulkDeleteRooms([room.name], true); } } catch (error) { @@ -195,7 +198,10 @@ export class LivekitWebhookService { // Send webhook notification switch (webhookAction) { case 'started': - tasks.push(this.openViduWebhookService.sendRecordingStartedWebhook(recordingInfo)); + tasks.push( + this.saveRoomSecretsIfNeeded(roomId), + this.openViduWebhookService.sendRecordingStartedWebhook(recordingInfo) + ); break; case 'updated': tasks.push(this.openViduWebhookService.sendRecordingUpdatedWebhook(recordingInfo)); @@ -229,6 +235,40 @@ export class LivekitWebhookService { } } + /** + * Saves room secrets to an S3 bucket if they haven't been saved already. + * + * This method checks if secrets for the specified room exist in the S3 storage. + * If they don't exist, it retrieves the room information, extracts the publisher + * and moderator secrets, and saves them to an S3 bucket under the path + * `${MEET_S3_RECORDINGS_PREFIX}/.metadata/${roomId}/secrets.json`. + */ + protected async saveRoomSecretsIfNeeded(roomId: string): Promise { + try { + const filePath = `${MEET_S3_RECORDINGS_PREFIX}/.metadata/${roomId}/secrets.json`; + const fileExists = await this.s3Service.exists(filePath); + + if (fileExists) { + this.logger.debug(`Room secrets already saved for room ${roomId}`); + return; + } + + const room = await this.roomService.getMeetRoom(roomId); + + if (room) { + const { publisherSecret, moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room); + const secrets = { + publisherSecret, + moderatorSecret + }; + await this.s3Service.saveObject(filePath, secrets); + this.logger.debug(`Room secrets saved for room ${roomId}`); + } + } catch (error) { + this.logger.error(`Error saving room secrets for room ${roomId}: ${error}`); + } + } + protected buildMetadataFilePath(recordingId: string): string { const { roomId, egressId, uid } = RecordingHelper.extractInfoFromRecordingId(recordingId);