backend: refactor code to generate participantIdentity based on name and unique ID
This commit is contained in:
parent
2478845fb6
commit
13c88d201c
@ -7,7 +7,8 @@ import { nonEmptySanitizedRoomId } from './room-validator.middleware.js';
|
||||
const ParticipantTokenRequestSchema: z.ZodType<ParticipantOptions> = z.object({
|
||||
roomId: nonEmptySanitizedRoomId('roomId'),
|
||||
secret: z.string().nonempty('Secret is required'),
|
||||
participantName: z.string().optional()
|
||||
participantName: z.string().optional(),
|
||||
participantIdentity: z.string().optional()
|
||||
});
|
||||
|
||||
const UpdateParticipantRequestSchema = z.object({
|
||||
|
||||
@ -242,6 +242,10 @@ export const errorInvalidParticipantRole = (): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('Participant', 'No valid participant role provided', 400);
|
||||
};
|
||||
|
||||
export const errorParticipantIdentityNotProvided = (): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('Participant', 'No participant identity provided', 400);
|
||||
};
|
||||
|
||||
// Handlers
|
||||
|
||||
export const handleError = (res: Response, error: OpenViduMeetError | unknown, operationDescription: string) => {
|
||||
|
||||
@ -182,6 +182,34 @@ export class LiveKitService {
|
||||
}
|
||||
}
|
||||
|
||||
async participantExists(
|
||||
roomName: string,
|
||||
participantNameOrIdentity: string,
|
||||
participantField: 'name' | 'identity' = 'identity'
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const participants: ParticipantInfo[] = await this.listRoomParticipants(roomName);
|
||||
return participants.some((participant) => {
|
||||
let fieldValue = participant[participantField];
|
||||
|
||||
// If the field is empty or undefined, use identity as a fallback
|
||||
if (!fieldValue && participantField === 'name') {
|
||||
fieldValue = participant.identity;
|
||||
}
|
||||
|
||||
return fieldValue === participantNameOrIdentity;
|
||||
});
|
||||
} catch (error: any) {
|
||||
this.logger.error(error);
|
||||
|
||||
if (error?.cause?.code === 'ECONNREFUSED') {
|
||||
throw errorLivekitNotAvailable();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves information about a specific participant in a LiveKit room.
|
||||
*
|
||||
@ -383,19 +411,4 @@ export class LiveKitService {
|
||||
// TODO: Remove deprecated warning by using ParticipantInfo_Kind: participant.kind === ParticipantInfo_Kind.EGRESS;
|
||||
return participant.identity.startsWith('EG_') && participant.permission?.recorder === true;
|
||||
}
|
||||
|
||||
private async participantExists(roomName: string, participantIdentity: string): Promise<boolean> {
|
||||
try {
|
||||
const participants: ParticipantInfo[] = await this.listRoomParticipants(roomName);
|
||||
return participants.some((participant) => participant.identity === participantIdentity);
|
||||
} catch (error: any) {
|
||||
this.logger.error(error);
|
||||
|
||||
if (error?.cause?.code === 'ECONNREFUSED') {
|
||||
throw errorLivekitNotAvailable();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,11 @@ import { inject, injectable } from 'inversify';
|
||||
import { ParticipantInfo } from 'livekit-server-sdk';
|
||||
import { MeetRoomHelper } from '../helpers/room.helper.js';
|
||||
import { validateMeetTokenMetadata } from '../middlewares/index.js';
|
||||
import { errorParticipantAlreadyExists, errorParticipantNotFound } from '../models/error.model.js';
|
||||
import {
|
||||
errorParticipantAlreadyExists,
|
||||
errorParticipantIdentityNotProvided,
|
||||
errorParticipantNotFound
|
||||
} from '../models/error.model.js';
|
||||
import { FrontendEventService, LiveKitService, LoggerService, RoomService, TokenService } from './index.js';
|
||||
|
||||
@injectable()
|
||||
@ -27,20 +31,29 @@ export class ParticipantService {
|
||||
currentRoles: { role: ParticipantRole; permissions: OpenViduMeetPermissions }[],
|
||||
refresh = false
|
||||
): Promise<string> {
|
||||
const { roomId, participantName, secret } = participantOptions;
|
||||
const { roomId, secret, participantName, participantIdentity } = participantOptions;
|
||||
|
||||
if (participantName) {
|
||||
// Check if participant with same participantName exists in the room
|
||||
const participantExists = await this.participantExists(roomId, participantName);
|
||||
if (!refresh) {
|
||||
// Check if participant with same participantName exists in the room
|
||||
const participantExists = await this.participantExists(roomId, participantName, 'name');
|
||||
|
||||
if (!refresh && participantExists) {
|
||||
this.logger.verbose(`Participant '${participantName}' already exists in room '${roomId}'`);
|
||||
throw errorParticipantAlreadyExists(participantName, roomId);
|
||||
}
|
||||
if (participantExists) {
|
||||
this.logger.verbose(`Participant '${participantName}' already exists in room '${roomId}'`);
|
||||
throw errorParticipantAlreadyExists(participantName, roomId);
|
||||
}
|
||||
} else {
|
||||
if (!participantIdentity) {
|
||||
throw errorParticipantIdentityNotProvided();
|
||||
}
|
||||
|
||||
if (refresh && !participantExists) {
|
||||
this.logger.verbose(`Participant '${participantName}' does not exist in room '${roomId}'`);
|
||||
throw errorParticipantNotFound(participantName, roomId);
|
||||
// Check if participant with same participantIdentity exists in the room
|
||||
const participantExists = await this.participantExists(roomId, participantIdentity, 'identity');
|
||||
|
||||
if (!participantExists) {
|
||||
this.logger.verbose(`Participant '${participantName}' does not exist in room '${roomId}'`);
|
||||
throw errorParticipantNotFound(participantName, roomId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,15 +83,13 @@ export class ParticipantService {
|
||||
return this.livekitService.getParticipant(roomId, participantIdentity);
|
||||
}
|
||||
|
||||
async participantExists(roomId: string, participantIdentity: string): Promise<boolean> {
|
||||
this.logger.verbose(`Checking if participant '${participantIdentity}' exists in room '${roomId}'`);
|
||||
|
||||
try {
|
||||
await this.getParticipant(roomId, participantIdentity);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
async participantExists(
|
||||
roomId: string,
|
||||
participantNameOrIdentity: string,
|
||||
participantField: 'name' | 'identity' = 'identity'
|
||||
): Promise<boolean> {
|
||||
this.logger.verbose(`Checking if participant '${participantNameOrIdentity}' exists in room '${roomId}'`);
|
||||
return this.livekitService.participantExists(roomId, participantNameOrIdentity, participantField);
|
||||
}
|
||||
|
||||
async deleteParticipant(roomId: string, participantIdentity: string): Promise<void> {
|
||||
|
||||
@ -13,6 +13,7 @@ import { AccessToken, AccessTokenOptions, ClaimGrants, TokenVerifier, VideoGrant
|
||||
import INTERNAL_CONFIG from '../config/internal-config.js';
|
||||
import { LIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_URL } from '../environment.js';
|
||||
import { LoggerService } from './index.js';
|
||||
import { uid } from 'uid';
|
||||
|
||||
@injectable()
|
||||
export class TokenService {
|
||||
@ -47,7 +48,17 @@ export class TokenService {
|
||||
selectedRole: ParticipantRole
|
||||
): Promise<string> {
|
||||
const { roomId, participantName } = participantOptions;
|
||||
this.logger.info(`Generating token for room '${roomId}'`);
|
||||
this.logger.info(
|
||||
`Generating token for room '${roomId}'` + (participantName ? ` and participant '${participantName}'` : '')
|
||||
);
|
||||
|
||||
let { participantIdentity } = participantOptions;
|
||||
|
||||
if (participantName && !participantIdentity) {
|
||||
// Generate participant identity based on name and unique ID
|
||||
const identityPrefix = participantName.replace(/\s+/g, ''); // Remove all spaces
|
||||
participantIdentity = `${identityPrefix}-${uid(5)}`;
|
||||
}
|
||||
|
||||
const metadata: MeetTokenMetadata = {
|
||||
livekitUrl: LIVEKIT_URL,
|
||||
@ -55,7 +66,7 @@ export class TokenService {
|
||||
selectedRole
|
||||
};
|
||||
const tokenOptions: AccessTokenOptions = {
|
||||
identity: participantName,
|
||||
identity: participantIdentity,
|
||||
name: participantName,
|
||||
ttl: INTERNAL_CONFIG.PARTICIPANT_TOKEN_EXPIRATION,
|
||||
metadata: JSON.stringify(metadata)
|
||||
|
||||
@ -17,6 +17,10 @@ export interface ParticipantOptions {
|
||||
* The name of the participant.
|
||||
*/
|
||||
participantName?: string;
|
||||
/**
|
||||
* The identity of the participant.
|
||||
*/
|
||||
participantIdentity?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user