import { ParticipantOptions, ParticipantPermissions, ParticipantRole } from '@typings-ce'; import { inject, injectable } from 'inversify'; import { ParticipantInfo } from 'livekit-server-sdk'; import { errorParticipantAlreadyExists, errorParticipantNotFound } from '../models/error.model.js'; import { LiveKitService, LoggerService, RoomService, TokenService } from './index.js'; @injectable() export class ParticipantService { constructor( @inject(LoggerService) protected logger: LoggerService, @inject(RoomService) protected roomService: RoomService, @inject(LiveKitService) protected livekitService: LiveKitService, @inject(TokenService) protected tokenService: TokenService ) {} async generateOrRefreshParticipantToken(participantOptions: ParticipantOptions, refresh = false): Promise { const { roomId, participantName, secret } = participantOptions; // Check if participant with same participantName exists in the room const participantExists = await this.participantExists(roomId, participantName); if (!refresh && participantExists) { this.logger.verbose(`Participant ${participantName} already exists in room ${roomId}`); throw errorParticipantAlreadyExists(participantName, roomId); } if (refresh && !participantExists) { this.logger.verbose(`Participant ${participantName} does not exist in room ${roomId}`); throw errorParticipantNotFound(participantName, roomId); } const role = await this.roomService.getRoomRoleBySecret(roomId, secret); const token = await this.generateParticipantToken(role, participantOptions); this.logger.verbose(`Participant token generated for room ${roomId}`); return token; } protected async generateParticipantToken( role: ParticipantRole, participantOptions: ParticipantOptions ): Promise { const permissions = this.getParticipantPermissions(role, participantOptions.roomId); return this.tokenService.generateParticipantToken(participantOptions, permissions, role); } async getParticipant(roomId: string, participantName: string): Promise { this.logger.verbose(`Fetching participant ${participantName}`); return this.livekitService.getParticipant(roomId, participantName); } async participantExists(roomId: string, participantName: string): Promise { this.logger.verbose(`Checking if participant ${participantName} exists in room ${roomId}`); try { const participant = await this.getParticipant(roomId, participantName); return participant !== null; } catch (error) { return false; } } async deleteParticipant(participantName: string, roomId: string): Promise { this.logger.verbose(`Deleting participant ${participantName} from room ${roomId}`); return this.livekitService.deleteParticipant(participantName, roomId); } getParticipantPermissions(role: ParticipantRole, roomId: string): ParticipantPermissions { switch (role) { case ParticipantRole.MODERATOR: return this.generateModeratorPermissions(roomId); case ParticipantRole.PUBLISHER: return this.generatePublisherPermissions(roomId); default: throw new Error(`Role ${role} not supported`); } } protected generateModeratorPermissions(roomId: string): ParticipantPermissions { return { livekit: { roomCreate: true, roomJoin: true, roomList: true, roomRecord: true, roomAdmin: true, room: roomId, ingressAdmin: true, canPublish: true, canSubscribe: true, canPublishData: true, canUpdateOwnMetadata: true, hidden: false, recorder: true, agent: false }, openvidu: { canPublishScreen: true, canRecord: true, canChat: true, canChangeVirtualBackground: true } }; } protected generatePublisherPermissions(roomId: string): ParticipantPermissions { return { livekit: { roomJoin: true, roomList: true, roomRecord: false, roomAdmin: false, room: roomId, ingressAdmin: false, canPublish: true, canSubscribe: true, canPublishData: true, canUpdateOwnMetadata: true, hidden: false, recorder: false, agent: false }, openvidu: { canPublishScreen: true, canRecord: false, canChat: true, canChangeVirtualBackground: true } }; } }