mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-07-16 17:47:22 +00:00
103 lines
3.7 KiB
C#
103 lines
3.7 KiB
C#
using Domain.Entities;
|
|
using Domain.Exceptions;
|
|
using Features.Auth.Dtos;
|
|
using Features.Auth.Repository;
|
|
using Features.Auth.Services;
|
|
using Infrastructure.PasswordHashing;
|
|
using MediatR;
|
|
using Shared.Application.Emails;
|
|
|
|
namespace Features.Auth.Commands.RegisterUser;
|
|
|
|
/// <summary>
|
|
/// Handles <see cref="RegisterUserCommand" />: validates uniqueness, hashes the password, persists the
|
|
/// account, issues access/refresh/confirmation tokens, and attempts to send the registration
|
|
/// confirmation email via Features.Emails.
|
|
/// </summary>
|
|
/// <param name="authRepo">Repository used to check for existing users and persist the new account.</param>
|
|
/// <param name="passwordInfrastructure">Infrastructure component used to hash the user's plain-text password.</param>
|
|
/// <param name="tokenService">Service used to generate access, refresh, and confirmation tokens.</param>
|
|
/// <param name="mediator">Used to send the cross-slice command that triggers the confirmation email.</param>
|
|
public class RegisterUserHandler(
|
|
IAuthRepository authRepo,
|
|
IPasswordInfrastructure passwordInfrastructure,
|
|
ITokenService tokenService,
|
|
IMediator mediator
|
|
) : IRequestHandler<RegisterUserCommand, RegistrationPayload>
|
|
{
|
|
/// <exception cref="ConflictException">
|
|
/// Thrown when an existing account already has the same username or email address.
|
|
/// </exception>
|
|
public async Task<RegistrationPayload> Handle(
|
|
RegisterUserCommand request,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
await ValidateUserDoesNotExist(request.Username, request.Email);
|
|
|
|
string hashed = passwordInfrastructure.Hash(request.Password);
|
|
|
|
UserAccount createdUser = await authRepo.RegisterUserAsync(
|
|
request.Username,
|
|
request.FirstName,
|
|
request.LastName,
|
|
request.Email,
|
|
request.DateOfBirth,
|
|
hashed
|
|
);
|
|
|
|
string accessToken = tokenService.GenerateAccessToken(createdUser);
|
|
string refreshToken = tokenService.GenerateRefreshToken(createdUser);
|
|
string confirmationToken = tokenService.GenerateConfirmationToken(createdUser);
|
|
|
|
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(refreshToken))
|
|
return new RegistrationPayload(
|
|
createdUser.UserAccountId,
|
|
createdUser.Username,
|
|
string.Empty,
|
|
string.Empty,
|
|
false
|
|
);
|
|
|
|
bool emailSent = false;
|
|
try
|
|
{
|
|
await mediator.Send(
|
|
new SendRegistrationEmailCommand(
|
|
createdUser.FirstName,
|
|
createdUser.Email,
|
|
confirmationToken
|
|
),
|
|
cancellationToken
|
|
);
|
|
emailSent = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await Console.Error.WriteLineAsync(ex.Message);
|
|
Console.WriteLine("Could not send email.");
|
|
// ignored
|
|
}
|
|
|
|
return new RegistrationPayload(
|
|
createdUser.UserAccountId,
|
|
createdUser.Username,
|
|
refreshToken,
|
|
accessToken,
|
|
emailSent
|
|
);
|
|
}
|
|
|
|
/// <exception cref="ConflictException">
|
|
/// Thrown when an existing account already has the same username or email address.
|
|
/// </exception>
|
|
private async Task ValidateUserDoesNotExist(string username, string email)
|
|
{
|
|
UserAccount? existingUsername = await authRepo.GetUserByUsernameAsync(username);
|
|
UserAccount? existingEmail = await authRepo.GetUserByEmailAsync(email);
|
|
|
|
if (existingUsername != null || existingEmail != null)
|
|
throw new ConflictException("Username or email already exists");
|
|
}
|
|
}
|