import { IUserRepository } from '../../../Repositories/interfaces/IUserRepository'; import { UserResponseDto, UsersListResponseDto, UserCreateDto, UserUpdateDto } from '../../../Database/dto/user.dto'; import { hashPassword, comparePasswords } from '../../../middlewares/security'; import { CreateUserCommand, UpdateUserCommand, DeleteUserCommand, DeleteUsersCommand, CreateUsersCommand, UpdateUserPasswordCommand, ResetUserPasswordCommand } from './UserCommand'; export class UserCommandHandler { constructor(private readonly userRepository: IUserRepository) {} // Create user async handleCreate(command: CreateUserCommand): Promise { // Check if username already exists const usernameExists = await this.userRepository.usernameExists(command.userCreateDto.username); if (usernameExists) { throw new Error('Username already exists'); } // Check if email already exists const emailExists = await this.userRepository.emailExists(command.userCreateDto.email); if (emailExists) { throw new Error('Email already exists'); } console.log('Creating user with DTO:', command.userCreateDto); // Hash password using security middleware const hashedPassword = await hashPassword(command.userCreateDto.password); console.log('Hashed password:', hashedPassword); const userWithHashedPassword = new UserCreateDto( command.userCreateDto.username, command.userCreateDto.FirstName, command.userCreateDto.LastName, command.userCreateDto.email, hashedPassword, command.userCreateDto.CompanyId // Pass the optional CompanyId ); console.log('Creating user with hashed password:', userWithHashedPassword); return await this.userRepository.create(userWithHashedPassword); } // Update user async handleUpdate(command: UpdateUserCommand): Promise { // Check if user exists const userExists = await this.userRepository.exists(command.id); if (!userExists) { throw new Error('User not found'); } // If updating username, check if it's already taken by another user if (command.userUpdateDto.username) { const existingUser = await this.userRepository.findByUsername(command.userUpdateDto.username); if (existingUser && existingUser.id !== command.id) { throw new Error('Username already exists'); } } // If updating email, check if it's already taken by another user if (command.userUpdateDto.email) { const existingUser = await this.userRepository.findByEmail(command.userUpdateDto.email); if (existingUser && existingUser.id !== command.id) { throw new Error('Email already exists'); } } // Create new DTO with hashed password if password is being updated let updateDto = command.userUpdateDto; if (command.userUpdateDto.password) { const hashedPassword = await hashPassword(command.userUpdateDto.password); updateDto = new UserUpdateDto({ ...command.userUpdateDto, password: hashedPassword }); } return await this.userRepository.update(command.id, updateDto); } // Delete user async handleDelete(command: DeleteUserCommand): Promise { const userExists = await this.userRepository.exists(command.id); if (!userExists) { throw new Error('User not found'); } return await this.userRepository.deleteById(command.id); } // Delete multiple users async handleDeleteUsers(command: DeleteUsersCommand): Promise { if (command.ids.length === 0) { throw new Error('No user IDs provided'); } // Check if all users exist for (const id of command.ids) { const userExists = await this.userRepository.exists(id); if (!userExists) { throw new Error(`User with ID ${id} not found`); } } return await this.userRepository.deleteByIds(command.ids); } // Create multiple users async handleCreateUsers(command: CreateUsersCommand): Promise { if (command.userCreateDtos.length === 0) { throw new Error('No users provided'); } // Check for duplicate usernames and emails within the batch const usernames = command.userCreateDtos.map(dto => dto.username); const emails = command.userCreateDtos.map(dto => dto.email); const uniqueUsernames = new Set(usernames); const uniqueEmails = new Set(emails); if (uniqueUsernames.size !== usernames.length) { throw new Error('Duplicate usernames in batch'); } if (uniqueEmails.size !== emails.length) { throw new Error('Duplicate emails in batch'); } // Check if any usernames or emails already exist for (const dto of command.userCreateDtos) { const usernameExists = await this.userRepository.usernameExists(dto.username); if (usernameExists) { throw new Error(`Username ${dto.username} already exists`); } const emailExists = await this.userRepository.emailExists(dto.email); if (emailExists) { throw new Error(`Email ${dto.email} already exists`); } } // Hash all passwords using security middleware const usersWithHashedPasswords = await Promise.all( command.userCreateDtos.map(async (dto) => { const hashedPassword = await hashPassword(dto.password); return new UserCreateDto( dto.username, dto.FirstName, dto.LastName, dto.email, hashedPassword ); }) ); return await this.userRepository.createMany(usersWithHashedPasswords); } // Update user password (with current password verification) async handleUpdatePassword(command: UpdateUserPasswordCommand): Promise { // Get raw user entity for password verification const user = await this.userRepository.findRawById(command.userId); if (!user) { throw new Error('User not found'); } // Verify current password using security middleware const isCurrentPasswordValid = await comparePasswords(command.currentPassword, user.password); if (!isCurrentPasswordValid) { throw new Error('Current password is incorrect'); } // Hash new password using security middleware const hashedNewPassword = await hashPassword(command.newPassword); const updateDto = new UserUpdateDto({ password: hashedNewPassword }); return await this.userRepository.update(command.userId, updateDto); } // Reset user password (admin only - no current password verification) async handleResetPassword(command: ResetUserPasswordCommand): Promise { const userExists = await this.userRepository.exists(command.userId); if (!userExists) { throw new Error('User not found'); } // Hash new password using security middleware const hashedNewPassword = await hashPassword(command.newPassword); const updateDto = new UserUpdateDto({ password: hashedNewPassword }); return await this.userRepository.update(command.userId, updateDto); } }