131 lines
4.8 KiB
C#
131 lines
4.8 KiB
C#
using Indotalent.Data.Entities;
|
|
using Indotalent.Infrastructure.Authorization.Identity;
|
|
using Indotalent.Infrastructure.Database;
|
|
using MediatR;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.WebUtilities;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Text;
|
|
|
|
namespace Indotalent.Features.Account.Register.Cqrs;
|
|
|
|
public class CreateUserRequest
|
|
{
|
|
public string FullName { get; set; } = string.Empty;
|
|
public string Email { get; set; } = string.Empty;
|
|
public string Password { get; set; } = string.Empty;
|
|
public bool IsActive { get; set; } = true;
|
|
public bool EmailConfirmed { get; set; } = false;
|
|
}
|
|
|
|
public class CreateUserResponse
|
|
{
|
|
public string? Id { get; set; }
|
|
public string? Email { get; set; }
|
|
}
|
|
|
|
public record CreateUserCommand(CreateUserRequest Data) : IRequest<CreateUserResponse>;
|
|
|
|
public class RegisterHandler : IRequestHandler<CreateUserCommand, CreateUserResponse>
|
|
{
|
|
private readonly UserManager<ApplicationUser> _userManager;
|
|
private readonly IEmailSender<ApplicationUser> _emailSender;
|
|
private readonly IConfiguration _configuration;
|
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
private readonly AppDbContext _context;
|
|
|
|
public RegisterHandler(
|
|
UserManager<ApplicationUser> userManager,
|
|
IEmailSender<ApplicationUser> emailSender,
|
|
IConfiguration configuration,
|
|
IHttpContextAccessor httpContextAccessor,
|
|
AppDbContext context)
|
|
{
|
|
_userManager = userManager;
|
|
_emailSender = emailSender;
|
|
_configuration = configuration;
|
|
_httpContextAccessor = httpContextAccessor;
|
|
_context = context;
|
|
}
|
|
|
|
public async Task<CreateUserResponse> Handle(CreateUserCommand request, CancellationToken cancellationToken)
|
|
{
|
|
var user = new ApplicationUser
|
|
{
|
|
UserName = request.Data.Email,
|
|
Email = request.Data.Email,
|
|
FullName = request.Data.FullName,
|
|
IsActive = request.Data.IsActive,
|
|
EmailConfirmed = request.Data.EmailConfirmed,
|
|
};
|
|
|
|
var result = await _userManager.CreateAsync(user, request.Data.Password);
|
|
|
|
if (!result.Succeeded)
|
|
{
|
|
var errors = result.Errors.Select(x => x.Description).ToList();
|
|
throw new Exception(string.Join(", ", errors));
|
|
}
|
|
|
|
// Assign default roles: Member and TenantAdmin (but not Admin)
|
|
var rolesToAdd = new List<string> { ApplicationRoles.Member, ApplicationRoles.TenantAdmin };
|
|
|
|
await _userManager.AddToRolesAsync(user, rolesToAdd);
|
|
|
|
// Create default tenant for the new user
|
|
var tenant = new Data.Entities.Tenant
|
|
{
|
|
Name = "Default Tenant",
|
|
Description = $"Default tenant for {request.Data.Email}",
|
|
EmailAddress = request.Data.Email,
|
|
IsActive = true
|
|
};
|
|
|
|
_context.Tenant.Add(tenant);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
// Create TenantUser linking the user to the default tenant
|
|
var tenantUser = new Data.Entities.TenantUser
|
|
{
|
|
TenantId = tenant.Id,
|
|
UserId = user.Id,
|
|
Summary = $"Default tenant assignment for {request.Data.FullName}",
|
|
IsActive = true
|
|
};
|
|
|
|
_context.TenantUser.Add(tenantUser);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
var requireConfirmation = _configuration.GetValue<bool>("IdentitySettings:SignIn:RequireConfirmedAccount");
|
|
|
|
if (requireConfirmation)
|
|
{
|
|
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
|
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
|
|
|
|
var requestHttp = _httpContextAccessor.HttpContext?.Request;
|
|
var scheme = requestHttp?.Scheme ?? "https";
|
|
var host = requestHttp?.Host.Value ?? "localhost:8080";
|
|
|
|
var callbackUrl = $"{scheme}://{host}/account/confirm-email?userId={user.Id}&code={code}";
|
|
|
|
var message = $@"
|
|
<div style=""font-family: Arial, sans-serif;"">
|
|
<h3>Welcome, {user.FullName}!</h3>
|
|
<p>Please confirm your account by clicking the link below:</p>
|
|
<p><a href=""{callbackUrl}"" style=""color: #2196F3; font-weight: bold; text-decoration: underline;"">Confirm Account</a></p>
|
|
<br/>
|
|
<p style=""font-size: 0.8em; color: #666;"">If the link doesn't work, copy and paste this URL into your browser:</p>
|
|
<p style=""font-size: 0.8em; color: #666;"">{callbackUrl}</p>
|
|
</div>";
|
|
|
|
await _emailSender.SendConfirmationLinkAsync(user, user.Email!, message);
|
|
}
|
|
|
|
return new CreateUserResponse
|
|
{
|
|
Id = user.Id,
|
|
Email = user.Email
|
|
};
|
|
}
|
|
} |