205 lines
7.0 KiB
TypeScript
205 lines
7.0 KiB
TypeScript
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<UserResponseDto> {
|
|
// 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<UserResponseDto | null> {
|
|
// 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<boolean> {
|
|
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<boolean> {
|
|
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<UsersListResponseDto> {
|
|
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<UserResponseDto | null> {
|
|
// 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<UserResponseDto | null> {
|
|
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);
|
|
}
|
|
} |