From 34d297c0f7d28c4ed1c48dbb704c35084efa5f01 Mon Sep 17 00:00:00 2001 From: ajoysr Date: Thu, 27 Mar 2025 10:43:24 +0600 Subject: [PATCH 1/3] feat: implement UserBots module with entity, DTOs, and error messages --- src/app.module.ts | 3 + src/entities/messages.entity.ts | 10 +++ src/i18n/en/message.json | 9 ++ src/modules/bot/bot.module.ts | 11 ++- src/modules/bot/bot.service.ts | 22 ++++- src/modules/profile/profile.module.ts | 3 +- src/modules/profile/profile.service.ts | 7 +- src/modules/qna/qna.repository.ts | 8 +- src/modules/user-bots/dtos/user-bots.dto.ts | 29 +++++++ .../user-bots/entities/user-bots.entity.ts | 6 ++ .../user-bots/entities/user-bots.model.ts | 29 +++++++ src/modules/user-bots/user-bots.module.ts | 21 +++++ src/modules/user-bots/user-bots.repository.ts | 65 ++++++++++++++ src/modules/user-bots/user-bots.service.ts | 87 +++++++++++++++++++ src/modules/user/dto/user.dto.ts | 50 ++--------- src/modules/user/entities/user.entity.ts | 1 + src/modules/user/user.module.ts | 4 + src/modules/user/user.service.ts | 81 ++++++++++------- 18 files changed, 367 insertions(+), 79 deletions(-) create mode 100644 src/modules/user-bots/dtos/user-bots.dto.ts create mode 100644 src/modules/user-bots/entities/user-bots.entity.ts create mode 100644 src/modules/user-bots/entities/user-bots.model.ts create mode 100644 src/modules/user-bots/user-bots.module.ts create mode 100644 src/modules/user-bots/user-bots.repository.ts create mode 100644 src/modules/user-bots/user-bots.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 115ba27..8f49c60 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -19,6 +19,7 @@ import { UnresolvedQueryModule } from './modules/message/unresolved-message.modu import { ProfileModule } from './modules/profile/profile.module'; import { RequestLoggerMiddleware } from './internal/middlewares/request-logger.middleware'; import { GeminiModule } from './modules/gemini/gemini.module'; +import { UserBotsModule } from './modules/user-bots/user-bots.module'; @Module({ imports: [ @@ -48,7 +49,9 @@ import { GeminiModule } from './modules/gemini/gemini.module'; QnAModule, ProfileModule, GeminiModule, + UserBotsModule, ], + providers: [], }) export class AppModule { configure(consumer: MiddlewareConsumer) { diff --git a/src/entities/messages.entity.ts b/src/entities/messages.entity.ts index 95e8daf..ea5f2e3 100644 --- a/src/entities/messages.entity.ts +++ b/src/entities/messages.entity.ts @@ -92,3 +92,13 @@ export const enum QnaErrorMessages { export const enum QnaSuccessMessages { QNA_CREATED_SUCCESSFULLY = 'message.qna.success.successFullyCreated', } + +export const enum UserBotsErrorMessages { + USER_BOTS_ALREADY_EXISTS = 'message.userBots.errors.alreadyExists', + COULD_NOT_CREATED_USER_BOTS = 'message.userBots.errors.couldNotCreateUserBot', + COULD_NOT_UPDATED_USER_BOTS = 'message.userBots.errors.couldNotUpdatedUserBot', + INVALID_USER_BOTS_ID = 'message.userBots.errors.invalidUserBotId', +} +export const enum UserBotsSuccessMessages { + USER_BOTS_CREATED_SUCCESSFULLY = 'message.bot.errors.botCreationSuccessful', +} diff --git a/src/i18n/en/message.json b/src/i18n/en/message.json index 8b53e8d..315e3ee 100644 --- a/src/i18n/en/message.json +++ b/src/i18n/en/message.json @@ -97,6 +97,15 @@ "couldNotDeletedQnA": "Failed to deleted the QnA", "couldNotCreateQnA": "Failed to create a QnA" } + }, + "userBots":{ + "errors": { + "invalidUserBotId": "Invalid UserBot ID", + "couldNotUpdatedUserBot": "Failed to update a UserBot", + "couldNotDeletedUserBot": "Failed to deleted the UserBot", + "couldNotCreateUserBot": "Failed to create a UserBot", + "alreadyExists": "This user already exists in this botCreationSuccessful" + } } } \ No newline at end of file diff --git a/src/modules/bot/bot.module.ts b/src/modules/bot/bot.module.ts index eac95a1..95560b7 100644 --- a/src/modules/bot/bot.module.ts +++ b/src/modules/bot/bot.module.ts @@ -6,10 +6,19 @@ import { BotRepository } from './bot.repository'; import { BotService } from './bot.service'; import { APIResponse } from 'src/internal/api-response/api-response.service'; import { PaginationService } from '../pagination/pagination.service'; +import { UserBotsRepository } from '../user-bots/user-bots.repository'; +import { UserRepository } from '../user/user.repository'; @Module({ imports: [MongooseModule.forFeature([{ name: 'Bot', schema: BotSchema }])], controllers: [BotController], - providers: [BotService, BotRepository, APIResponse, PaginationService], + providers: [ + BotService, + BotRepository, + APIResponse, + PaginationService, + UserBotsRepository, + UserRepository, + ], }) export class BotModule {} diff --git a/src/modules/bot/bot.service.ts b/src/modules/bot/bot.service.ts index f5b7e5a..62914f2 100644 --- a/src/modules/bot/bot.service.ts +++ b/src/modules/bot/bot.service.ts @@ -23,6 +23,8 @@ import { } from 'src/entities/messages.entity'; import { ROLE } from 'src/entities/enum.entity'; import { startSession } from 'mongoose'; +import { UserBotsRepository } from '../user-bots/user-bots.repository'; +import { UserRepository } from '../user/user.repository'; @Injectable() export class BotService { @@ -30,6 +32,8 @@ export class BotService { private readonly botRepository: BotRepository, private readonly apiResponse: APIResponse, private readonly pagination: PaginationService, + private readonly userBotsRepo: UserBotsRepository, + private readonly userRepo: UserRepository, ) {} async createBot( @@ -74,13 +78,29 @@ export class BotService { logo?: Express.Multer.File, icon?: Express.Multer.File, ): Promise> { - const bot = await this.botRepository.findBotById(botId); + // Check if the bot exists + const [creatorRole, bot] = await Promise.all([ + await this.userRepo.findRole({ _id: user?.roleId?._id }), + await this.botRepository.findBotById(botId), + ]); + console.log('🚀 ~ BotService ~ creatorRole:', creatorRole); + if (!bot) throw new HttpException( { message: BotErrorMessages.INVALID_BOT_ID }, HttpStatus.BAD_REQUEST, ); + const isOwner = await this.userBotsRepo.findByUserAndBot(user._id, botId); + + // Check if the creatorRole is CUSTOMER or if the user is not the owner + if (!isOwner && creatorRole.name !== ROLE.SUPER_ADMIN) { + throw new HttpException( + { message: UserErrorMessages.FORBIDDEN_PERMISSION }, + HttpStatus.BAD_REQUEST, + ); + } + const updatedBot = await this.botRepository.update(botId, data); if (!updatedBot) { diff --git a/src/modules/profile/profile.module.ts b/src/modules/profile/profile.module.ts index 9cb0fbc..0574015 100644 --- a/src/modules/profile/profile.module.ts +++ b/src/modules/profile/profile.module.ts @@ -3,10 +3,11 @@ import { ProfileController } from './profile.controller'; import { ProfileService } from './profile.service'; import { UserModule } from 'src/modules/user/user.module'; import { APIResponse } from 'src/internal/api-response/api-response.service'; +import { UserBotsRepository } from '../user-bots/user-bots.repository'; @Module({ imports: [UserModule], controllers: [ProfileController], - providers: [ProfileService, APIResponse], + providers: [ProfileService, APIResponse, UserBotsRepository], }) export class ProfileModule {} diff --git a/src/modules/profile/profile.service.ts b/src/modules/profile/profile.service.ts index 49ed59b..ed9c776 100644 --- a/src/modules/profile/profile.service.ts +++ b/src/modules/profile/profile.service.ts @@ -21,6 +21,7 @@ import { UserInterface } from 'src/modules/user/entities/user.entity'; import { UserRepository } from 'src/modules/user/user.repository'; import { UserService } from 'src/modules/user/user.service'; import { getRoleLabel } from 'src/utils'; +import { UserBotsRepository } from '../user-bots/user-bots.repository'; @Injectable() export class ProfileService { @@ -28,7 +29,8 @@ export class ProfileService { private userRepo: UserRepository, private readonly response: APIResponse, private readonly i18n: I18nService, - private userService: UserService, + private readonly userService: UserService, + private readonly userBotsRepo: UserBotsRepository, ) {} /** @@ -62,6 +64,9 @@ export class ProfileService { (userInfo as any).permissions = await this.userRepo.getPermissionsArray({ roleId: user?.roleId._id, }); + const listOfUserBots = await this.userBotsRepo.findUserBots(user._id); + + userInfo.botList = listOfUserBots.map((bot) => bot.botId); return this.response.success(userInfo); } diff --git a/src/modules/qna/qna.repository.ts b/src/modules/qna/qna.repository.ts index 809c73e..4358d94 100644 --- a/src/modules/qna/qna.repository.ts +++ b/src/modules/qna/qna.repository.ts @@ -28,11 +28,11 @@ export class QnARepository { query: Record, pagination: { skip: number; limit: number }, ) { + console.log('🚀 ~ QnARepository ~ query:', query); const client = await this.dbService.getClient(); try { const { skip, limit } = pagination; const { botId } = query; - console.log('🚀 ~ QnARepository ~ botId:', botId); let sqlQuery = 'SELECT id, question, answer, "botId", "createdAt", "updatedAt" FROM question_n_answers'; @@ -67,11 +67,15 @@ export class QnARepository { if (botId) { sqlQuery += ' WHERE "botId" = $1'; - queryParams.push(botId); + queryParams.push(botId.toString()); } const result = await client.query(sqlQuery, queryParams); + return parseInt(result.rows[0].count); + } catch (error) { + console.error('Error in countVectors:', error); + throw error; } finally { client.release(); } diff --git a/src/modules/user-bots/dtos/user-bots.dto.ts b/src/modules/user-bots/dtos/user-bots.dto.ts new file mode 100644 index 0000000..60f2ed8 --- /dev/null +++ b/src/modules/user-bots/dtos/user-bots.dto.ts @@ -0,0 +1,29 @@ +import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator'; + +export class CreateUserBotDto { + @IsString({ + message: 'validation.isString', + }) + @IsNotEmpty({ + message: 'validation.isNotEmpty', + }) + userId: string; + + @IsString({ + message: 'validation.isString', + }) + @IsNotEmpty({ + message: 'validation.isNotEmpty', + }) + botId: string; + + @IsOptional() + @IsBoolean() + isActive?: boolean; +} + +export class UpdateUserBotDto { + @IsOptional() + @IsBoolean() + isActive?: boolean; +} diff --git a/src/modules/user-bots/entities/user-bots.entity.ts b/src/modules/user-bots/entities/user-bots.entity.ts new file mode 100644 index 0000000..3448f1a --- /dev/null +++ b/src/modules/user-bots/entities/user-bots.entity.ts @@ -0,0 +1,6 @@ +export class UserBots { + _id?: string; + userId: any; + botId?: any; + isActive?: boolean; +} diff --git a/src/modules/user-bots/entities/user-bots.model.ts b/src/modules/user-bots/entities/user-bots.model.ts new file mode 100644 index 0000000..c7fde34 --- /dev/null +++ b/src/modules/user-bots/entities/user-bots.model.ts @@ -0,0 +1,29 @@ +import { model, Schema } from 'mongoose'; +import { UserBots } from './user-bots.entity'; + +const UserBotsSchema = new Schema( + { + userId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'User', + }, + botId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'Bot', + }, + isActive: { + type: Boolean, + default: true, + }, + }, + { + timestamps: true, + versionKey: false, + }, +); + +const UserBotsModel = model('userbots', UserBotsSchema); + +export { UserBotsModel, UserBotsSchema }; diff --git a/src/modules/user-bots/user-bots.module.ts b/src/modules/user-bots/user-bots.module.ts new file mode 100644 index 0000000..2850740 --- /dev/null +++ b/src/modules/user-bots/user-bots.module.ts @@ -0,0 +1,21 @@ +import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { UserBotsService } from './user-bots.service'; +import { UserBotsRepository } from './user-bots.repository'; +import { UserBotsSchema } from './entities/user-bots.model'; +import { BotRepository } from '../bot/bot.repository'; +import { UserRepository } from '../user/user.repository'; + +@Module({ + imports: [ + MongooseModule.forFeature([{ name: 'UserBots', schema: UserBotsSchema }]), + ], + providers: [ + UserBotsService, + UserBotsRepository, + BotRepository, + UserRepository, + ], + exports: [UserBotsService], +}) +export class UserBotsModule {} diff --git a/src/modules/user-bots/user-bots.repository.ts b/src/modules/user-bots/user-bots.repository.ts new file mode 100644 index 0000000..db645ad --- /dev/null +++ b/src/modules/user-bots/user-bots.repository.ts @@ -0,0 +1,65 @@ +import { Injectable } from '@nestjs/common'; +import { UserBotsModel } from './entities/user-bots.model'; +import { CreateUserBotDto } from './dtos/user-bots.dto'; +import { UserBots } from './entities/user-bots.entity'; + +@Injectable() +export class UserBotsRepository { + async create(createUserBotDto: CreateUserBotDto): Promise { + console.log( + '🚀 ~ UserBotsRepository ~ create ~ createUserBotDto:', + createUserBotDto, + ); + try { + return await UserBotsModel.create(createUserBotDto); + } catch (error) { + console.log('🚀 ~ UserBotsRepository ~ create ~ error:', error); + + throw error; + } + } + + async findByUserAndBot( + userId: string, + botId: string, + ): Promise { + try { + return await UserBotsModel.findOne({ userId, botId }); + } catch (error) { + console.log('🚀 ~ UserBotsRepository ~ error:', error); + throw error; + } + } + + async findById(id: string): Promise { + try { + return await UserBotsModel.findById(id); + } catch (error) { + console.log('🚀 ~ UserBotsRepository ~ findById ~ error:', error); + throw error; + } + } + + async update( + id: string, + updateData: Partial, + ): Promise { + try { + return await UserBotsModel.findByIdAndUpdate(id, updateData, { + new: true, + }); + } catch (error) { + console.log('🚀 ~ UserBotsRepository ~ error:', error); + throw error; + } + } + + async findUserBots(userId: string): Promise { + try { + return await UserBotsModel.find({ userId, isActive: true }); + } catch (error) { + console.log('🚀 ~ UserBotsRepository ~ findUserBots ~ error:', error); + throw error; + } + } +} diff --git a/src/modules/user-bots/user-bots.service.ts b/src/modules/user-bots/user-bots.service.ts new file mode 100644 index 0000000..a87d73c --- /dev/null +++ b/src/modules/user-bots/user-bots.service.ts @@ -0,0 +1,87 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { UserBotsRepository } from './user-bots.repository'; +import { CreateUserBotDto, UpdateUserBotDto } from './dtos/user-bots.dto'; +import { UserBots } from './entities/user-bots.entity'; +import { + BotErrorMessages, + UserBotsErrorMessages, + UserErrorMessages, +} from 'src/entities/messages.entity'; +import { BotRepository } from '../bot/bot.repository'; +import { UserRepository } from '../user/user.repository'; + +@Injectable() +export class UserBotsService { + constructor( + private readonly userBotsRepository: UserBotsRepository, + private readonly botRepo: BotRepository, + private readonly userRepo: UserRepository, + ) {} + + async assignBotToUser(createUserBotDto: CreateUserBotDto): Promise { + try { + const { userId, botId } = createUserBotDto ?? {}; + const validUser = userId && (await this.userRepo.findUserById(userId)); + if (!validUser) throw new Error(UserErrorMessages.INVALID_USER_ID); + + const validBot = botId && (await this.botRepo.findBotById(botId)); + if (!validBot) throw new Error(BotErrorMessages.INVALID_BOT_ID); + + const existingAssignment = await this.userBotsRepository.findByUserAndBot( + userId, + botId, + ); + + if (existingAssignment) { + throw new Error(UserBotsErrorMessages.USER_BOTS_ALREADY_EXISTS); + } + + return await this.userBotsRepository.create(createUserBotDto); + } catch (error) { + throw new HttpException( + { + message: + error.message || UserBotsErrorMessages.COULD_NOT_CREATED_USER_BOTS, + }, + HttpStatus.BAD_REQUEST, + ); + } + } + + async updateUserBot( + id: string, + updateUserBotDto: UpdateUserBotDto, + ): Promise { + try { + const userBot = await this.userBotsRepository.findById(id); + if (!userBot) { + throw new Error(UserBotsErrorMessages.INVALID_USER_BOTS_ID); + } + + return await this.userBotsRepository.update(id, updateUserBotDto); + } catch (error) { + throw new HttpException( + { + message: + error.message || UserBotsErrorMessages.COULD_NOT_UPDATED_USER_BOTS, + }, + HttpStatus.BAD_REQUEST, + ); + } + } + + async getUserBots(userId: string): Promise { + try { + const validUser = userId && (await this.userRepo.findUserById(userId)); + if (!validUser) throw new Error(UserErrorMessages.INVALID_USER_ID); + return await this.userBotsRepository.findUserBots(userId); + } catch (error) { + throw new HttpException( + { + message: error.message || UserErrorMessages.INVALID_USER_ID, + }, + HttpStatus.BAD_REQUEST, + ); + } + } +} diff --git a/src/modules/user/dto/user.dto.ts b/src/modules/user/dto/user.dto.ts index 28dd2e5..a5ebf37 100644 --- a/src/modules/user/dto/user.dto.ts +++ b/src/modules/user/dto/user.dto.ts @@ -5,7 +5,6 @@ import { IsEnum, IsBoolean, IsString, - IsArray, IsOptional, MinLength, Matches, @@ -45,21 +44,9 @@ export class CreateUserDto { example: '+1234567890', }) @IsString({ message: 'validation.isString' }) - @IsPhoneNumber(null, { message: 'validation.isMobilePhone' }) + @IsPhoneNumber('BD', { message: 'validation.isMobilePhone' }) phone: string; - @ApiProperty({ - description: - 'User password (min 6 chars, must contain letters and numbers)', - example: 'Password123!', - }) - @IsString({ message: 'validation.isString' }) - @MinLength(6, { message: 'validation.minLength' }) - @Matches(/^(?=.*[a-zA-Z])(?=.*\d)/, { - message: 'validation.passwordComplexity', - }) - password: string; - @ApiProperty({ description: 'User role', enum: ROLE, @@ -68,6 +55,14 @@ export class CreateUserDto { @IsString({ message: 'validation.isString' }) @IsNotEmpty({ message: 'isNotEmpty' }) role: string; + + @ApiProperty({ + description: 'Bot ID, which is assigned to the user', + example: '60d21b4667d0d8992e610c85', + }) + @IsString({ message: 'validation.isString' }) + @IsNotEmpty({ message: 'validation.isNotEmpty' }) + botId: string; } // update-user.dto.ts @@ -109,20 +104,6 @@ export class UpdateUserDto { @IsOptional() phone?: string; - @ApiProperty({ - description: - 'User password (min 8 chars, must contain letters and numbers)', - example: 'NewPassword123!', - required: false, - }) - @IsString({ message: 'validation.isString' }) - @MinLength(8, { message: 'validation.minLength' }) - @Matches(/^(?=.*[a-zA-Z])(?=.*\d)/, { - message: 'validation.passwordComplexity', - }) - @IsOptional() - password?: string; - @ApiProperty({ description: 'User role', enum: ROLE, @@ -133,19 +114,6 @@ export class UpdateUserDto { @IsOptional() role?: ROLE; - @ApiProperty({ - description: 'User permissions', - type: [String], - enum: PERMISSIONS, - isArray: true, - example: [PERMISSIONS.VIEW_USER_PROFILE, PERMISSIONS.VIEW_BOT_LIST], - required: false, - }) - @IsArray({ message: 'validation.isArray' }) - @IsEnum(PERMISSIONS, { each: true, message: 'validation.isEnum' }) - @IsOptional() - permissions?: PERMISSIONS[]; - @ApiProperty({ description: 'User active status', example: true, diff --git a/src/modules/user/entities/user.entity.ts b/src/modules/user/entities/user.entity.ts index d3dae92..9dd7daa 100644 --- a/src/modules/user/entities/user.entity.ts +++ b/src/modules/user/entities/user.entity.ts @@ -9,6 +9,7 @@ export interface UserInterface { firstName: string; lastName: string; permissions: PERMISSIONS[]; + botList: any[]; isActive: boolean; isEmailVerified: boolean; createdAt?: Date; diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts index c827d8f..4642500 100644 --- a/src/modules/user/user.module.ts +++ b/src/modules/user/user.module.ts @@ -12,6 +12,8 @@ import { UserRepository } from './user.repository'; import { MailService } from 'src/helper/email'; import { APIResponse } from 'src/internal/api-response/api-response.service'; import { PaginationService } from '../pagination/pagination.service'; +import { BotRepository } from '../bot/bot.repository'; +import { UserBotsRepository } from '../user-bots/user-bots.repository'; @Module({ imports: [ @@ -30,6 +32,8 @@ import { PaginationService } from '../pagination/pagination.service'; MailService, APIResponse, PaginationService, + BotRepository, + UserBotsRepository, ], exports: [UserService, UserRepository], }) diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 5011d15..de29a00 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -11,8 +11,10 @@ import { UserRepository } from './user.repository'; import { PaginationService } from '../pagination/pagination.service'; import { JwtPayload } from 'src/entities/auth.entity'; import { UserInterface } from './entities/user.entity'; -import { UserErrorMessages } from 'src/entities/messages.entity'; -import { CreateUserRequest } from 'src/entities/user.entity'; +import { + BotErrorMessages, + UserErrorMessages, +} from 'src/entities/messages.entity'; import { ROLE } from 'src/entities/enum.entity'; import { generateStrongPassword } from 'src/helper/utils'; import { authConfig } from 'src/config/auth'; @@ -20,6 +22,8 @@ import { Role } from 'src/entities/role-permission.entity'; import { MailService } from 'src/helper/email'; import { PaginationQueryDto } from '../pagination/types'; import { CreateUserDto, UpdateUserDto } from './dto/user.dto'; +import { BotRepository } from '../bot/bot.repository'; +import { UserBotsRepository } from '../user-bots/user-bots.repository'; @Injectable() export class UserService { @@ -28,6 +32,8 @@ export class UserService { private readonly mailService: MailService, private readonly response: APIResponse, private readonly pagination: PaginationService, + private readonly botRepo: BotRepository, + private readonly userBotRepo: UserBotsRepository, ) {} /** @@ -37,47 +43,56 @@ export class UserService { * @returns A response object containing either the newly added user or an error message. * @throws HttpException with status BAD_REQUEST if the request is invalid or an error occurs during user creation. */ - async addUser( - userInfo: JwtPayload, - data: CreateUserDto, - ): Promise> { - try { - const { email, role } = data; + async addUser(userInfo: JwtPayload, data: CreateUserDto): Promise { + const { email, role, botId } = data; - // Validate user addition and gun range (if applicable) - const [newRoleCreated] = await Promise.all([ - this.validateUserAddition(userInfo, email, role), + try { + // Validate creator and target roles + const [creatorRole, targetRole] = await Promise.all([ + this.userRepo.findRole({ _id: userInfo?.roleId?._id }), + this.userRepo.findRole({ name: role as ROLE }), ]); - if (newRoleCreated) { - return this.response.success(newRoleCreated); - } - - if (userInfo.roleId?.name === ROLE.SUPER_ADMIN) { + // Ensure creator is a super admin + if (creatorRole?.name !== ROLE.SUPER_ADMIN) { throw new Error(UserErrorMessages.FORBIDDEN_PERMISSION); } - // Generate a temporary password and hash it + // Validate user and bot + await this.validateUserAddition(userInfo, email, targetRole?._id); + + // Validate bot + const validBot = botId && (await this.botRepo.findBotById(botId)); + if (!validBot) throw new Error(BotErrorMessages.INVALID_BOT_ID); + + // Generate and hash temporary password const temporaryPassword = generateStrongPassword(); - // hash the random password const hashedPassword = await bcrypt.hash( temporaryPassword, authConfig.salt, ); - // Create the user - const addedUser = await this.userRepo.createUser({ + // Prepare user data + const newUserData = { ...data, + role: targetRole._id, password: hashedPassword, isEmailVerified: true, - }); + }; + // Create user and user-bot relationship + const addedUser = await this.userRepo.createUser(newUserData); if (!addedUser) { throw new Error(UserErrorMessages.COULD_NOT_CREATE_USER); } - // Send the temporary password email - this.sendTemporaryPasswordEmail(data.email, temporaryPassword); + await this.userBotRepo.create({ + userId: addedUser._id, + botId, + }); + + // Send temporary password email + this.sendTemporaryPasswordEmail(email, temporaryPassword); return this.response.success(addedUser); } catch (error) { @@ -272,29 +287,31 @@ export class UserService { userInfo: JwtPayload, email: string, roleId: string, - ): Promise { + ): Promise { try { - const [creatorRole, user, targetRole] = await Promise.all([ - this.userRepo.findRoleById(userInfo?.roleId?._id), + // Check if user exists and role is valid + const [existingUser, targetRole] = await Promise.all([ this.userRepo.findUser({ email }), this.userRepo.findRoleById(roleId), ]); + // Validate target role if (!targetRole) { throw new Error(UserErrorMessages.INVALID_ROLE_ID); } - // If the user is already exists - if (user) { + // Check if user already exists + if (existingUser) { throw new HttpException( - { - message: UserErrorMessages.EMAIL_ALREADY_EXISTS, - }, + { message: UserErrorMessages.EMAIL_ALREADY_EXISTS }, HttpStatus.BAD_REQUEST, ); } - // Checks whether a user with a certain role can manage a user with a certain target role. + // Check user permissions + const creatorRole = await this.userRepo.findRoleById( + userInfo?.roleId?._id, + ); this.checkUserPermission(creatorRole?.name, targetRole.name); } catch (error) { throw new HttpException( From cabc95b8c2fae054d1690de6d3586f96e0335e7f Mon Sep 17 00:00:00 2001 From: ajoysr Date: Thu, 27 Mar 2025 14:03:19 +0600 Subject: [PATCH 2/3] refactor: remove debug logging and update findUserBots to exclude createdAt and updatedAt fields --- src/modules/user-bots/user-bots.repository.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/user-bots/user-bots.repository.ts b/src/modules/user-bots/user-bots.repository.ts index db645ad..0e00aea 100644 --- a/src/modules/user-bots/user-bots.repository.ts +++ b/src/modules/user-bots/user-bots.repository.ts @@ -6,10 +6,6 @@ import { UserBots } from './entities/user-bots.entity'; @Injectable() export class UserBotsRepository { async create(createUserBotDto: CreateUserBotDto): Promise { - console.log( - '🚀 ~ UserBotsRepository ~ create ~ createUserBotDto:', - createUserBotDto, - ); try { return await UserBotsModel.create(createUserBotDto); } catch (error) { @@ -56,7 +52,9 @@ export class UserBotsRepository { async findUserBots(userId: string): Promise { try { - return await UserBotsModel.find({ userId, isActive: true }); + return await UserBotsModel.find({ userId, isActive: true }).select( + '-cratedAt updatedAT', + ); } catch (error) { console.log('🚀 ~ UserBotsRepository ~ findUserBots ~ error:', error); throw error; From be0825e1952859f5dbb3c2f276d4eadfe22bb006 Mon Sep 17 00:00:00 2001 From: ajoysr Date: Thu, 27 Mar 2025 14:03:28 +0600 Subject: [PATCH 3/3] fix: correct typo in findUserBots select fields to exclude createdAt and updatedAt --- src/modules/user-bots/user-bots.repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/user-bots/user-bots.repository.ts b/src/modules/user-bots/user-bots.repository.ts index 0e00aea..5b3e369 100644 --- a/src/modules/user-bots/user-bots.repository.ts +++ b/src/modules/user-bots/user-bots.repository.ts @@ -53,7 +53,7 @@ export class UserBotsRepository { async findUserBots(userId: string): Promise { try { return await UserBotsModel.find({ userId, isActive: true }).select( - '-cratedAt updatedAT', + '-createdAt -updatedAt', ); } catch (error) { console.log('🚀 ~ UserBotsRepository ~ findUserBots ~ error:', error);