backend: Enhance webhook events creator checking
This commit is contained in:
parent
185d6e9def
commit
e2b9fcd532
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user