backend: Refactor room finished and recording webhook handling for improved clarity and error logging

This commit is contained in:
Carlos Santos 2025-05-07 14:25:39 +02:00
parent e2b9fcd532
commit e75b21fa49

View File

@ -170,26 +170,32 @@ export class LivekitWebhookService {
* @param {Room} room - The room object that has finished. * @param {Room} room - The room object that has finished.
* @returns {Promise<void>} A promise that resolves when the webhook has been sent. * @returns {Promise<void>} A promise that resolves when the webhook has been sent.
*/ */
async handleRoomFinished(room: Room): Promise<void> { async handleRoomFinished({ name: roomName }: Room): Promise<void> {
try { try {
const meetRoom = await this.roomService.getMeetRoom(room.name); const meetRoom = await this.roomService.getMeetRoom(roomName);
if (!meetRoom) { if (!meetRoom) {
this.logger.warn(`Room ${room.name} not found in OpenVidu Meet.`); this.logger.warn(`Room ${roomName} not found in OpenVidu Meet.`);
return; return;
} }
await Promise.all([ this.logger.info(`Processing room_finished event for room: ${roomName}`);
this.recordingService.releaseRecordingLockIfNoEgress(room.name),
const results = await Promise.allSettled([
this.recordingService.releaseRecordingLockIfNoEgress(roomName),
this.openViduWebhookService.sendMeetingEndedWebhook(meetRoom) this.openViduWebhookService.sendMeetingEndedWebhook(meetRoom)
]); ]);
results.forEach((result) => {
if (result.status === 'rejected') {
this.logger.error(`Error processing room_finished event: ${result.reason}`);
}
});
if (meetRoom.markedForDeletion) { if (meetRoom.markedForDeletion) {
// If the room is marked for deletion, we need to delete it // If the room is marked for deletion, we need to delete it
this.logger.info( this.logger.info(`Deleting room ${roomName} after meeting finished because it was marked for deletion`);
`Deleting room ${room.name} after meeting finished because it was marked for deletion` this.roomService.bulkDeleteRooms([roomName], true);
);
this.roomService.bulkDeleteRooms([room.name], true);
} }
} catch (error) { } catch (error) {
this.logger.error(`Error handling room finished event: ${error}`); this.logger.error(`Error handling room finished event: ${error}`);
@ -210,36 +216,37 @@ export class LivekitWebhookService {
): Promise<void> { ): Promise<void> {
if (!RecordingHelper.isRecordingEgress(egressInfo)) return; if (!RecordingHelper.isRecordingEgress(egressInfo)) return;
this.logger.debug(`Handling recording_${webhookAction} webhook.`); this.logger.debug(`Processing recording_${webhookAction} webhook for egress: ${egressInfo.egressId}.`);
try {
const recordingInfo: MeetRecordingInfo = RecordingHelper.toRecordingInfo(egressInfo); const recordingInfo: MeetRecordingInfo = RecordingHelper.toRecordingInfo(egressInfo);
const { roomId, recordingId, status } = recordingInfo; const { roomId, recordingId, status } = recordingInfo;
const metadataPath = RecordingHelper.buildMetadataFilePath(recordingId); const metadataPath = RecordingHelper.buildMetadataFilePath(recordingId);
this.logger.debug(`Recording '${recordingId}' status: '${status}'`); this.logger.debug(`Recording '${recordingId}' in room '${roomId}' status: '${status}'`);
const tasks: Promise<unknown>[] = []; // Common tasks for all webhook types
const commonTasks = [
// Update recording metadata
tasks.push(
this.s3Service.saveObject(metadataPath, recordingInfo), this.s3Service.saveObject(metadataPath, recordingInfo),
this.recordingService.sendRecordingSignalToOpenViduComponents(roomId, recordingInfo) this.recordingService.sendRecordingSignalToOpenViduComponents(roomId, recordingInfo)
); ];
const specificTasks: Promise<unknown>[] = [];
// Send webhook notification // Send webhook notification
switch (webhookAction) { switch (webhookAction) {
case 'started': case 'started':
tasks.push( specificTasks.push(
this.storageService.archiveRoomMetadata(roomId), this.storageService.archiveRoomMetadata(roomId),
this.openViduWebhookService.sendRecordingStartedWebhook(recordingInfo) this.openViduWebhookService.sendRecordingStartedWebhook(recordingInfo)
); );
break; break;
case 'updated': case 'updated':
tasks.push(this.openViduWebhookService.sendRecordingUpdatedWebhook(recordingInfo)); specificTasks.push(this.openViduWebhookService.sendRecordingUpdatedWebhook(recordingInfo));
if (recordingInfo.status === MeetRecordingStatus.ACTIVE) { if (recordingInfo.status === MeetRecordingStatus.ACTIVE) {
// Send system event for active recording with the aim of cancelling the cleanup timer // Send system event for active recording with the aim of cancelling the cleanup timer
tasks.push( specificTasks.push(
this.systemEventService.publishEvent( this.systemEventService.publishEvent(
SystemEventType.RECORDING_ACTIVE, SystemEventType.RECORDING_ACTIVE,
recordingInfo as unknown as Record<string, unknown> recordingInfo as unknown as Record<string, unknown>
@ -249,19 +256,18 @@ export class LivekitWebhookService {
break; break;
case 'ended': case 'ended':
tasks.push( specificTasks.push(
this.openViduWebhookService.sendRecordingEndedWebhook(recordingInfo), this.openViduWebhookService.sendRecordingEndedWebhook(recordingInfo),
this.recordingService.releaseRecordingLockIfNoEgress(roomId) this.recordingService.releaseRecordingLockIfNoEgress(roomId)
); );
break; break;
} }
try {
// Wait for all promises to resolve // Wait for all promises to resolve
await Promise.all(tasks); await Promise.all([...commonTasks, ...specificTasks]);
} catch (error) { } catch (error) {
this.logger.warn( this.logger.warn(
`Error processing recording ${webhookAction} webhook for egress ${egressInfo.egressId}: ${error}` `Error processing recording_${webhookAction} webhook for egress ${egressInfo.egressId}: ${error}`
); );
} }
} }