backend: add currentParticipantIdentity to room member schema and update it based on participant webhooks

This commit is contained in:
juancarmore 2026-01-14 13:19:03 +01:00
parent c60cb244a7
commit f0a6d63f4d
4 changed files with 71 additions and 4 deletions

View File

@ -87,6 +87,10 @@ const MeetRoomMemberSchema = new Schema<MeetRoomMemberDocument>(
customPermissions: {
type: MeetRoomMemberPartialPermissionsSchema,
required: false
},
currentParticipantIdentity: {
type: String,
required: false
}
},
{

View File

@ -8,6 +8,7 @@ import { MeetLock } from '../helpers/redis.helper.js';
import { MeetRoomHelper } from '../helpers/room.helper.js';
import { DistributedEventType } from '../models/distributed-event.model.js';
import { RecordingRepository } from '../repositories/recording.repository.js';
import { RoomMemberRepository } from '../repositories/room-member.repository.js';
import { RoomRepository } from '../repositories/room.repository.js';
import { DistributedEventService } from './distributed-event.service.js';
import { FrontendEventService } from './frontend-event.service.js';
@ -18,7 +19,6 @@ import { OpenViduWebhookService } from './openvidu-webhook.service.js';
import { RecordingService } from './recording.service.js';
import { RoomMemberService } from './room-member.service.js';
import { RoomService } from './room.service.js';
import { RoomMemberRepository } from '../repositories/room-member.repository.js';
@injectable()
export class LivekitWebhookService {
@ -160,7 +160,8 @@ export class LivekitWebhookService {
/**
*
* Handles the 'participant_joined' event by gathering relevant room and participant information,
* checking room status, and sending a data payload with room status information to the newly joined participant.
* updating the room member's currentParticipantIdentity if applicable,
* and sending a room status signal to OpenVidu components.
* @param room - Information about the room where the participant joined.
* @param participant - Information about the newly joined participant.
*/
@ -168,6 +169,23 @@ export class LivekitWebhookService {
// Skip if the participant is an egress participant
if (this.livekitService.isEgressParticipant(participant)) return;
// Update room member's currentParticipantIdentity if this is an identified member
if (participant.metadata) {
try {
const metadata = JSON.parse(participant.metadata);
if (metadata.memberId) {
await this.roomMemberService.updateCurrentParticipantIdentity(
room.name,
metadata.memberId,
participant.identity
);
}
} catch (error) {
this.logger.warn(`Failed to set room member currentParticipantIdentity:`, error);
}
}
try {
const { recordings } = await this.recordingService.getAllRecordings({ roomId: room.name });
await this.frontendEventService.sendRoomStatusSignalToOpenViduComponents(
@ -181,8 +199,9 @@ export class LivekitWebhookService {
}
/**
* Handles the 'participant_left' event by releasing the participant's reserved name
* to make it available for other participants.
* Handles the 'participant_left' event by gathering relevant room and participant information,
* clearing the participant identity from the room member if applicable,
* and releasing any reserved participant names.
* @param room - Information about the room where the participant left.
* @param participant - Information about the participant who left.
*/
@ -190,6 +209,23 @@ export class LivekitWebhookService {
// Skip if the participant is an egress participant
if (this.livekitService.isEgressParticipant(participant)) return;
// Clear room member's currentParticipantIdentity if this is an identified member
if (participant.metadata) {
try {
const metadata = JSON.parse(participant.metadata);
if (metadata.memberId) {
await this.roomMemberService.updateCurrentParticipantIdentity(
room.name,
metadata.memberId,
undefined
);
}
} catch (error) {
this.logger.warn(`Failed to clear room member currentParticipantIdentity:`, error);
}
}
try {
// Release the participant's reserved name
await this.roomMemberService.releaseParticipantName(room.name, participant.name);

View File

@ -191,6 +191,32 @@ export class RoomMemberService {
return this.roomMemberRepository.update(member);
}
/**
* Updates the currentParticipantIdentity for a room member.
*
* @param roomId - The ID of the room
* @param memberId - The ID of the member
* @param participantIdentity - The participant identity to set (or undefined to clear it)
*/
async updateCurrentParticipantIdentity(
roomId: string,
memberId: string,
participantIdentity: string | undefined
): Promise<void> {
const member = await this.getRoomMember(roomId, memberId);
if (!member) {
this.logger.warn(
`Cannot update currentParticipantIdentity: member '${memberId}' not found in room '${roomId}'`
);
return;
}
member.currentParticipantIdentity = participantIdentity;
await this.roomMemberRepository.update(member);
this.logger.info(`Updated currentParticipantIdentity for member '${memberId}' in room '${roomId}'`);
}
/**
* Deletes a room member.
*

View File

@ -24,6 +24,7 @@ export interface MeetRoomMember {
baseRole: MeetRoomMemberRole; // The base role of the member in the room
customPermissions?: Partial<MeetRoomMemberPermissions>; // Custom permissions for the member (if any)
effectivePermissions: MeetRoomMemberPermissions; // Effective permissions for the member (base role + custom permissions)
currentParticipantIdentity?: string; // The participant identity if the member is currently in a meeting, undefined otherwise
}
/**