Files
SerpentRace/SerpentRace_Backend/src/functions/Users/Commands/UserCommandHandler.ts
T
2025-07-11 19:56:28 +02:00

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);
}
}