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 { MeetRoom, MeetRoomOptions } from '@typings-ce';
|
||||||
|
import { MEET_NAME_ID } from '../environment.js';
|
||||||
|
|
||||||
export class MeetRoomHelper {
|
export class MeetRoomHelper {
|
||||||
private constructor() {
|
private constructor() {
|
||||||
@ -40,4 +41,20 @@ export class MeetRoomHelper {
|
|||||||
const moderatorSecret = moderatorUrl.searchParams.get('secret') || '';
|
const moderatorSecret = moderatorUrl.searchParams.get('secret') || '';
|
||||||
return { publisherSecret, moderatorSecret };
|
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 { inject, injectable } from 'inversify';
|
||||||
import { EgressInfo, ParticipantInfo, Room, WebhookEvent, WebhookReceiver } from 'livekit-server-sdk';
|
import { EgressInfo, ParticipantInfo, Room, WebhookEvent, WebhookReceiver } from 'livekit-server-sdk';
|
||||||
import { LIVEKIT_API_KEY, LIVEKIT_API_SECRET } from '../environment.js';
|
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 { SystemEventType } from '../models/system-event.model.js';
|
||||||
import {
|
import {
|
||||||
LiveKitService,
|
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> {
|
async webhookEventBelongsToOpenViduMeet(webhookEvent: WebhookEvent): Promise<boolean> {
|
||||||
// Extract relevant properties from the webhook event
|
// Extract relevant properties from the webhook event
|
||||||
const { room, egressInfo, ingressInfo } = webhookEvent;
|
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
|
// Case 1: Check using room object from the event
|
||||||
const roomName = room?.name ?? egressInfo?.roomName ?? ingressInfo?.roomName;
|
if (room) {
|
||||||
|
this.logger.debug(`[webhookEventBelongsToOpenViduMeet] Checking room metadata for room: ${room.name}`);
|
||||||
|
|
||||||
if (!roomName) {
|
if (!room.metadata) {
|
||||||
this.logger.debug('Room name not found in webhook event');
|
this.logger.debug(`[webhookEventBelongsToOpenViduMeet] Room metadata is empty for room: ${room.name}`);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
const updatedMetadata = await this.livekitService.getRoomMetadata(room.name);
|
||||||
const meetRoom = await this.roomService.getMeetRoom(roomName);
|
|
||||||
|
|
||||||
if (!meetRoom) {
|
if (MeetRoomHelper.checkIfMeetingBelogsToOpenViduMeet(updatedMetadata)) return true;
|
||||||
this.logger.debug(`Room ${roomName} not found in OpenVidu Meet.`);
|
|
||||||
return false;
|
return await this.roomService.meetRoomExists(room.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
this.logger.debug(`[webhookEventBelongsToOpenViduMeet] Room metadata found for room: ${room.name}`);
|
||||||
} catch (error) {
|
return (
|
||||||
this.logger.error(`Error checking if room ${roomName} was created by OpenVidu Meet:`, error);
|
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;
|
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];
|
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[]> {
|
async listRooms(): Promise<Room[]> {
|
||||||
try {
|
try {
|
||||||
return await this.roomClient.listRooms();
|
return await this.roomClient.listRooms();
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import {
|
|||||||
TokenService
|
TokenService
|
||||||
} from './index.js';
|
} from './index.js';
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
|
import { MEET_NAME_ID } from '../environment.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for managing OpenVidu Meet rooms.
|
* Service for managing OpenVidu Meet rooms.
|
||||||
@ -99,6 +100,7 @@ export class RoomService {
|
|||||||
const livekitRoomOptions: CreateOptions = {
|
const livekitRoomOptions: CreateOptions = {
|
||||||
name: roomId,
|
name: roomId,
|
||||||
metadata: JSON.stringify({
|
metadata: JSON.stringify({
|
||||||
|
createdBy: MEET_NAME_ID,
|
||||||
roomOptions: MeetRoomHelper.toOpenViduOptions(meetRoom)
|
roomOptions: MeetRoomHelper.toOpenViduOptions(meetRoom)
|
||||||
}),
|
}),
|
||||||
emptyTimeout: MEETING_EMPTY_TIMEOUT ? ms(MEETING_EMPTY_TIMEOUT) / 1000 : undefined,
|
emptyTimeout: MEETING_EMPTY_TIMEOUT ? ms(MEETING_EMPTY_TIMEOUT) / 1000 : undefined,
|
||||||
@ -125,6 +127,24 @@ export class RoomService {
|
|||||||
return await this.storageService.saveMeetRoom(room);
|
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.
|
* Retrieves a list of rooms.
|
||||||
* @returns A Promise that resolves to an array of {@link MeetRoom} objects.
|
* @returns A Promise that resolves to an array of {@link MeetRoom} objects.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user