From 7ee20f31c1ca39028bce496fae6f075cac6bd6b6 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Wed, 14 Jan 2026 13:19:27 +0100 Subject: [PATCH] backend: implement participant kick mechanism during room member deletion --- .../repositories/room-member.repository.ts | 1 + .../src/services/room-member.service.ts | 54 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/meet-ce/backend/src/repositories/room-member.repository.ts b/meet-ce/backend/src/repositories/room-member.repository.ts index 962b2012..bfeffd58 100644 --- a/meet-ce/backend/src/repositories/room-member.repository.ts +++ b/meet-ce/backend/src/repositories/room-member.repository.ts @@ -115,6 +115,7 @@ export class RoomMemberRepository extends BaseRepository { diff --git a/meet-ce/backend/src/services/room-member.service.ts b/meet-ce/backend/src/services/room-member.service.ts index 2e692208..f4f3ce81 100644 --- a/meet-ce/backend/src/services/room-member.service.ts +++ b/meet-ce/backend/src/services/room-member.service.ts @@ -230,6 +230,19 @@ export class RoomMemberService { throw errorRoomMemberNotFound(roomId, memberId); } + // If member is currently in a meeting, kick them out first + if (member.currentParticipantIdentity) { + try { + await this.kickParticipantFromMeeting(roomId, member.currentParticipantIdentity); + this.logger.info( + `Kicked participant '${member.currentParticipantIdentity}' from meeting before deleting member '${memberId}'` + ); + } catch (error) { + this.logger.warn(`Failed to kick participant from meeting during member deletion:`, error); + // Continue with deletion even if kick fails + } + } + return this.roomMemberRepository.deleteByRoomAndMemberId(roomId, memberId); } @@ -247,7 +260,11 @@ export class RoomMemberService { deleted: string[]; failed: { memberId: string; error: string }[]; }> { - const membersToDelete = await this.roomMemberRepository.findByRoomAndMemberIds(roomId, memberIds, 'memberId'); + const membersToDelete = await this.roomMemberRepository.findByRoomAndMemberIds( + roomId, + memberIds, + 'memberId,currentParticipantIdentity' + ); const foundMemberIds = membersToDelete.map((m) => m.memberId); const failed = memberIds @@ -255,6 +272,41 @@ export class RoomMemberService { .map((id) => ({ memberId: id, error: 'Room member not found' })); if (foundMemberIds.length > 0) { + // Kick participants that are currently in a meeting + const membersInMeeting = membersToDelete.filter((m) => m.currentParticipantIdentity); + + if (membersInMeeting.length > 0) { + const KICK_BATCH_SIZE = 10; + + // Process kicks in batches to avoid overwhelming the system + for (let i = 0; i < membersInMeeting.length; i += KICK_BATCH_SIZE) { + const batch = membersInMeeting.slice(i, i + KICK_BATCH_SIZE); + + await Promise.allSettled( + batch.map(async (member) => { + try { + await this.kickParticipantFromMeeting(roomId, member.currentParticipantIdentity!); + this.logger.info( + `Kicked participant '${member.currentParticipantIdentity}' from meeting before deleting member '${member.memberId}'` + ); + } catch (error) { + this.logger.warn( + `Failed to kick participant '${member.currentParticipantIdentity}' from meeting during bulk deletion:`, + error + ); + // Continue with deletion even if kick fails + } + }) + ); + + this.logger.verbose(`Processed batch of ${batch.length} participant kicks for room '${roomId}'`); + } + + this.logger.info( + `Kicked ${membersInMeeting.length} participant(s) from meeting before bulk deletion in room '${roomId}'` + ); + } + await this.roomMemberRepository.deleteByRoomIdAndMemberIds(roomId, foundMemberIds); }