Backend half
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user