backend: Enhance webhook events creator checking

This commit is contained in:
Carlos Santos 2025-05-07 14:25:22 +02:00
parent 185d6e9def
commit e2b9fcd532
4 changed files with 89 additions and 16 deletions

View File

@ -1,4 +1,5 @@
import { MeetRoom, MeetRoomOptions } from '@typings-ce';
import { MEET_NAME_ID } from '../environment.js';
export class MeetRoomHelper {
private constructor() {
@ -40,4 +41,20 @@ export class MeetRoomHelper {
const moderatorSecret = moderatorUrl.searchParams.get('secret') || '';
return { publisherSecret, moderatorSecret };
}
/**
* Safely parses JSON metadata and checks if createdBy matches MEET_NAME_ID.
* @returns true if metadata indicates OpenVidu Meet as creator, false otherwise
*/
static checkIfMeetingBelogsToOpenViduMeet(metadata?: string): boolean {
if (!metadata) return false;
try {
const parsed = JSON.parse(metadata);
const isOurs = parsed?.createdBy === MEET_NAME_ID;
return isOurs;
} catch (err: unknown) {
return false;
}
}
}

View File

@ -2,7 +2,7 @@ import { MeetRecordingInfo, MeetRecordingStatus } from '@typings-ce';
import { inject, injectable } from 'inversify';
import { EgressInfo, ParticipantInfo, Room, WebhookEvent, WebhookReceiver } from 'livekit-server-sdk';
import { LIVEKIT_API_KEY, LIVEKIT_API_SECRET } from '../environment.js';
import { RecordingHelper } from '../helpers/index.js';
import { MeetRoomHelper, RecordingHelper } from '../helpers/index.js';
import { SystemEventType } from '../models/system-event.model.js';
import {
LiveKitService,
@ -49,33 +49,49 @@ export class LivekitWebhookService {
}
/**
* Checks if the webhook event belongs to OpenVidu Meet by verifying if the room exist in OpenVidu Meet.
* Checks if the webhook event belongs to OpenVidu Meet.
* Uses a systematic approach to verify through different sources.
* !KNOWN ISSUE: Room metadata may be empty when track_publish and track_unpublish events are received.
*/
async webhookEventBelongsToOpenViduMeet(webhookEvent: WebhookEvent): Promise<boolean> {
// Extract relevant properties from the webhook event
const { room, egressInfo, ingressInfo } = webhookEvent;
this.logger.debug(`[webhookEventBelongsToOpenViduMeet] Checking webhook event: ${webhookEvent.event}`);
// Determine the room name from room object or egress/ingress info
const roomName = room?.name ?? egressInfo?.roomName ?? ingressInfo?.roomName;
// Case 1: Check using room object from the event
if (room) {
this.logger.debug(`[webhookEventBelongsToOpenViduMeet] Checking room metadata for room: ${room.name}`);
if (!roomName) {
this.logger.debug('Room name not found in webhook event');
return false;
}
if (!room.metadata) {
this.logger.debug(`[webhookEventBelongsToOpenViduMeet] Room metadata is empty for room: ${room.name}`);
try {
const meetRoom = await this.roomService.getMeetRoom(roomName);
const updatedMetadata = await this.livekitService.getRoomMetadata(room.name);
if (!meetRoom) {
this.logger.debug(`Room ${roomName} not found in OpenVidu Meet.`);
return false;
if (MeetRoomHelper.checkIfMeetingBelogsToOpenViduMeet(updatedMetadata)) return true;
return await this.roomService.meetRoomExists(room.name);
}
return true;
} catch (error) {
this.logger.error(`Error checking if room ${roomName} was created by OpenVidu Meet:`, error);
this.logger.debug(`[webhookEventBelongsToOpenViduMeet] Room metadata found for room: ${room.name}`);
return (
MeetRoomHelper.checkIfMeetingBelogsToOpenViduMeet(room.metadata) ||
(await this.roomService.meetRoomExists(room.name))
);
}
// Case 2: No room in event - use roomName from egress/ingress info
const roomName = egressInfo?.roomName ?? ingressInfo?.roomName;
if (!roomName) {
this.logger.debug('[webhookEventBelongsToOpenViduMeet] Room name not found in webhook event');
return false;
}
const updatedMetadata = await this.livekitService.getRoomMetadata(roomName);
if (MeetRoomHelper.checkIfMeetingBelogsToOpenViduMeet(updatedMetadata)) return true;
return await this.roomService.meetRoomExists(roomName);
}
/**

View File

@ -100,6 +100,26 @@ export class LiveKitService {
return rooms[0];
}
/**
* Retrieves the metadata associated with a LiveKit room.
*
* @param roomName - The name of the room to get metadata from
* @returns The room's metadata as a string if it exists, or undefined if the room has no metadata or an error occurs
*/
async getRoomMetadata(roomName: string): Promise<string | undefined> {
try {
const room = await this.getRoom(roomName);
if (room.metadata) {
return room.metadata;
}
return undefined;
} catch (error) {
return undefined;
}
}
async listRooms(): Promise<Room[]> {
try {
return await this.roomClient.listRooms();

View File

@ -24,6 +24,7 @@ import {
TokenService
} from './index.js';
import ms from 'ms';
import { MEET_NAME_ID } from '../environment.js';
/**
* Service for managing OpenVidu Meet rooms.
@ -99,6 +100,7 @@ export class RoomService {
const livekitRoomOptions: CreateOptions = {
name: roomId,
metadata: JSON.stringify({
createdBy: MEET_NAME_ID,
roomOptions: MeetRoomHelper.toOpenViduOptions(meetRoom)
}),
emptyTimeout: MEETING_EMPTY_TIMEOUT ? ms(MEETING_EMPTY_TIMEOUT) / 1000 : undefined,
@ -125,6 +127,24 @@ export class RoomService {
return await this.storageService.saveMeetRoom(room);
}
/**
* Checks if a meeting room with the specified name exists
*
* @param roomName - The name of the meeting room to check
* @returns A Promise that resolves to true if the room exists, false otherwise
*/
async meetRoomExists(roomName: string): Promise<boolean> {
try {
const meetRoom = await this.getMeetRoom(roomName);
if (meetRoom) return true;
return false;
} catch (err: unknown) {
return false;
}
}
/**
* Retrieves a list of rooms.
* @returns A Promise that resolves to an array of {@link MeetRoom} objects.