diff --git a/src/modules/call/call.controller.ts b/src/modules/call/call.controller.ts index 932e18d..b02f3a8 100644 --- a/src/modules/call/call.controller.ts +++ b/src/modules/call/call.controller.ts @@ -119,12 +119,15 @@ export class CallController { @Body() joinCallDto?: JoinCallDto, ): Promise { const { uuid, email } = user || {}; + const isUserAnonymous = + (!!joinCallDto?.anonymousId || joinCallDto?.anonymous) ?? !user; return await this.callUseCase.joinCall(roomId, { userId: uuid, name: joinCallDto?.name, lastName: joinCallDto?.lastName, - anonymous: joinCallDto?.anonymous || !user, + anonymous: isUserAnonymous, + anonymousId: joinCallDto?.anonymousId, email: email, }); } diff --git a/src/modules/call/call.usecase.ts b/src/modules/call/call.usecase.ts index cdd8c45..27a43d0 100644 --- a/src/modules/call/call.usecase.ts +++ b/src/modules/call/call.usecase.ts @@ -1,11 +1,12 @@ import { + BadRequestException, ConflictException, ForbiddenException, Injectable, Logger, NotFoundException, } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; +import { v4 } from 'uuid'; import { UserTokenData } from '../auth/dto/user.dto'; import { RoomService } from './services/room.service'; import { Room } from './domain/room.domain'; @@ -73,6 +74,7 @@ export class CallUseCase { name?: string; lastName?: string; anonymous?: boolean; + anonymousId?: string; email?: string; }, ): Promise { @@ -81,28 +83,55 @@ export class CallUseCase { throw new NotFoundException(`Specified room not found`); } - const processedUserData = this.processUserData(userData); - const isOwner = processedUserData.userId === room.hostId; + const joiningUserData = { + userId: userData?.anonymous + ? (userData?.anonymousId ?? v4()) + : userData?.userId, + name: userData?.name, + lastName: userData?.lastName, + anonymous: userData?.anonymous || !userData.userId, + email: userData?.email, + }; + + const isOwner = joiningUserData.userId === room.hostId; if (!isOwner && room.isClosed) { throw new ForbiddenException('Room is closed'); } - const roomUser = await this.roomService.addUserToRoom( - roomId, - processedUserData, + const existentUserInRoom = await this.roomService.getUserInRoom( + joiningUserData.userId, + room.id, ); + const currentUsersCount = await this.roomService.countUsersInRoom(room.id); + + const isRoomFull = currentUsersCount >= room.maxUsersAllowed; + + if (isRoomFull && !existentUserInRoom) { + throw new BadRequestException('The room is full'); + } + + const roomUser = + existentUserInRoom ?? + (await this.roomService.createUserInRoom({ + roomId: room.id, + userId: joiningUserData?.userId, + name: joiningUserData?.name, + lastName: joiningUserData?.lastName, + anonymous: Boolean(joiningUserData?.anonymous), + })); + // Generate token for the user const tokenData = this.callService.createCallTokenForParticipant( roomUser.userId, roomId, !!roomUser.anonymous, isOwner, - processedUserData, + joiningUserData, ); - if (processedUserData.userId === room.hostId && room.isClosed) { + if (joiningUserData.userId === room.hostId && room.isClosed) { await this.roomService.openRoom(roomId); } @@ -114,39 +143,6 @@ export class CallUseCase { }; } - private processUserData(userData: { - userId?: string; - name?: string; - lastName?: string; - anonymous?: boolean; - email?: string; - }): { - userId: string; - name?: string; - lastName?: string; - anonymous: boolean; - email?: string; - } { - const { userId, name, lastName, anonymous = false, email } = userData; - - if (anonymous || !userId) { - return { - userId: uuidv4(), - name, - lastName, - anonymous: true, - }; - } - - return { - userId, - name, - lastName, - anonymous: false, - email, - }; - } - async leaveCall(roomId: string, userId: string): Promise { const room = await this.roomService.getRoomByRoomId(roomId); diff --git a/src/modules/call/dto/join-call.dto.ts b/src/modules/call/dto/join-call.dto.ts index b26aa6a..8f2b617 100644 --- a/src/modules/call/dto/join-call.dto.ts +++ b/src/modules/call/dto/join-call.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsBoolean, IsOptional, IsString } from 'class-validator'; +import { IsBoolean, IsOptional, IsString, IsUUID } from 'class-validator'; export class JoinCallDto { @ApiPropertyOptional({ @@ -21,12 +21,21 @@ export class JoinCallDto { @ApiPropertyOptional({ description: 'Whether the user is joining anonymously', type: Boolean, - default: false, + deprecated: true, }) @IsBoolean() @IsOptional() anonymous?: boolean; + @ApiPropertyOptional({ + description: 'Id to use for anonymous user', + type: String, + required: false, + }) + @IsUUID() + @IsOptional() + anonymousId?: string; + @IsString() @IsOptional() email?: string; diff --git a/src/modules/call/services/room.service.ts b/src/modules/call/services/room.service.ts index 12c7662..c5d8155 100644 --- a/src/modules/call/services/room.service.ts +++ b/src/modules/call/services/room.service.ts @@ -7,7 +7,7 @@ import { import { SequelizeRoomRepository } from '../infrastructure/room.repository'; import { SequelizeRoomUserRepository } from '../infrastructure/room-user.repository'; import { Room, RoomAttributes } from '../domain/room.domain'; -import { RoomUser } from '../domain/room-user.domain'; +import { RoomUser, RoomUserAttributes } from '../domain/room-user.domain'; import { v4 as uuidv4 } from 'uuid'; import { UsersInRoomDto } from '../dto/users-in-room.dto'; import { UserRepository } from '../../../shared/user/user.repository'; @@ -27,6 +27,10 @@ export class RoomService { return this.roomRepository.create(data); } + async createUserInRoom(data: Omit) { + return this.roomUserRepository.create(data); + } + async getRoomByRoomId(id: RoomAttributes['id']) { return this.roomRepository.findById(id); } @@ -59,7 +63,7 @@ export class RoomService { } async addUserToRoom( - roomId: string, + room: Room, userData: { userId?: string; name?: string; @@ -67,27 +71,19 @@ export class RoomService { anonymous?: boolean; }, ): Promise { - const room = await this.getRoomByRoomId(roomId); - if (!room) { - throw new NotFoundException(`Specified room not found`); - } + const currentUsersCount = await this.roomUserRepository.countByRoomId( + room.id, + ); - const currentUsersCount = - await this.roomUserRepository.countByRoomId(roomId); if (currentUsersCount >= room.maxUsersAllowed) { throw new BadRequestException('The room is full'); } const { userId, name, lastName, anonymous = false } = userData; - let userIdToUse = userId; - - if (anonymous || !userIdToUse) { - userIdToUse = uuidv4(); - } const existingUser = await this.roomUserRepository.findByUserIdAndRoomId( - userIdToUse, - roomId, + userId, + room.id, ); if (existingUser) { @@ -95,8 +91,8 @@ export class RoomService { } return this.roomUserRepository.create({ - roomId, - userId: userIdToUse, + roomId: room.id, + userId, name, lastName, anonymous: Boolean(anonymous), @@ -159,4 +155,13 @@ export class RoomService { return userAvatars; } + + async getUserInRoom(userId: string, roomId: string) { + const existingUser = await this.roomUserRepository.findByUserIdAndRoomId( + userId, + roomId, + ); + + return existingUser; + } }