using AutoMapper; using MediatR; using mws.backend.dotnet.application.Audit; using mws.backend.dotnet.application.Auth; using mws.backend.dotnet.application.Common; using mws.backend.dotnet.application.Permissions; using mws.backend.dotnet.domain.Roles; using mws.backend.dotnet.domain.Users; namespace mws.backend.dotnet.application.Users; public record CreateUserCommand(Guid ActorUserId, CreateUserRequest Request) : IRequest, IAuditableRequest { public string Action => "User.Create"; public string EntityType => "User"; public string? EntityName => Request.Username; } public class CreateUserHandler(IUnitOfWork uow, IPasswordHasher passwordHasher, IPermissionService permissions, IMapper mapper) : IRequestHandler { private const string Screen = "users"; public async Task Handle(CreateUserCommand command, CancellationToken ct) { await permissions.EnsureAsync(command.ActorUserId, Screen, PermissionAction.Create, ct); var request = command.Request; var username = request.Username.Trim(); if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(request.Password)) { throw new BadRequestException("Username and password are required"); } if (await uow.Users.ExistsByUsernameAsync(username, ct)) { throw new BadRequestException("Username already exists"); } Role? role = null; if (request.RoleId.HasValue) { role = await uow.Roles.GetByIdAsync(request.RoleId.Value, ct) ?? throw new BadRequestException("Role not found"); } var now = DateTime.UtcNow; var user = new User { Id = Guid.NewGuid(), Username = username, PasswordHash = passwordHasher.Hash(request.Password), DisplayName = request.DisplayName.Trim(), IsActive = true, CreatedAt = now, UpdatedAt = now, UserRoles = role != null ? new List { new UserRole { RoleId = role.Id } } : new List() }; uow.Users.Add(user); await uow.SaveChangesAsync(ct); return mapper.Map(user); } }