mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-07-16 09:37:23 +00:00
Auth and Emails land together since Auth's registration/resend flows need Emails to exist first. Service.Auth's four services (Register, Login, Confirmation, Token) collapse into MediatR commands/queries in Features.Auth; TokenService stays as a slice-internal service since multiple handlers call it. Auth no longer references Emails directly — it sends SendRegistrationEmailCommand/SendResendConfirmationEmailCommand (defined in Shared.Application) which Features.Emails handles, so neither slice has a project reference on the other. Service.Emails' IEmailService becomes Features.Emails' IEmailDispatcher, simplified to take (firstName, email, token) instead of a full UserAccount since that's all it ever used. API.Specs' TestApiFactory/MockEmailService are updated to swap in the relocated interface. Also deletes Infrastructure.Repository now that Breweries, UserManagement, and Auth have all moved their repos out of it, and replaces the two now-dead docker-compose test services (repository.tests, service.auth.tests, both pointing at deleted Dockerfiles) with a single unit.tests service that runs every Features.*.Tests project via Dockerfile.tests.
45 lines
1.7 KiB
C#
45 lines
1.7 KiB
C#
using Features.Auth.Repository;
|
|
using Features.Auth.Services;
|
|
using MediatR;
|
|
using Shared.Application.Emails;
|
|
|
|
namespace Features.Auth.Commands.ResendConfirmationEmail;
|
|
|
|
/// <summary>
|
|
/// Handles <see cref="ResendConfirmationEmailCommand"/> by generating a fresh confirmation token and
|
|
/// sending it via Features.Emails.
|
|
/// </summary>
|
|
/// <param name="authRepository">Repository used to look up the user and check verification status.</param>
|
|
/// <param name="tokenService">Service used to generate the confirmation token.</param>
|
|
/// <param name="mediator">Used to send the cross-slice command that triggers the email.</param>
|
|
/// <remarks>
|
|
/// Returns silently without sending an email if the user does not exist (to prevent user enumeration)
|
|
/// or if the user's account is already verified.
|
|
/// </remarks>
|
|
public class ResendConfirmationEmailHandler(
|
|
IAuthRepository authRepository,
|
|
ITokenService tokenService,
|
|
IMediator mediator
|
|
) : IRequestHandler<ResendConfirmationEmailCommand>
|
|
{
|
|
public async Task Handle(ResendConfirmationEmailCommand request, CancellationToken cancellationToken)
|
|
{
|
|
var user = await authRepository.GetUserByIdAsync(request.UserId);
|
|
if (user == null)
|
|
{
|
|
return; // Silent return to prevent user enumeration
|
|
}
|
|
|
|
if (await authRepository.IsUserVerifiedAsync(request.UserId))
|
|
{
|
|
return; // Already confirmed, no-op
|
|
}
|
|
|
|
var confirmationToken = tokenService.GenerateConfirmationToken(user);
|
|
await mediator.Send(
|
|
new SendResendConfirmationEmailCommand(user.FirstName, user.Email, confirmationToken),
|
|
cancellationToken
|
|
);
|
|
}
|
|
}
|