initial commit
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using System.Text;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class AdminForgotPasswordResponse
|
||||
{
|
||||
public bool IsSuccess { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public record AdminForgotPasswordCommand(string UserId) : IRequest<AdminForgotPasswordResponse>;
|
||||
|
||||
public class AdminForgotPasswordHandler : IRequestHandler<AdminForgotPasswordCommand, AdminForgotPasswordResponse>
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly IEmailSender<ApplicationUser> _emailSender;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public AdminForgotPasswordHandler(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
IEmailSender<ApplicationUser> emailSender,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_emailSender = emailSender;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
public async Task<AdminForgotPasswordResponse> Handle(AdminForgotPasswordCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(request.UserId);
|
||||
if (user == null) return new AdminForgotPasswordResponse { IsSuccess = false, Message = "User not found" };
|
||||
|
||||
var code = await _userManager.GeneratePasswordResetTokenAsync(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/reset-password?userId={user.Id}&code={code}";
|
||||
|
||||
var message = $@"
|
||||
<div style=""font-family: Arial, sans-serif;"">
|
||||
<h3>System Administrator Password Reset</h3>
|
||||
<p>Hello, {user.FullName}!</p>
|
||||
<p>An administrator has initiated a password reset for your account. Please click the link below to set your new password:</p>
|
||||
<p><a href=""{callbackUrl}"" style=""color: #2196F3; font-weight: bold;"">Reset My Password</a></p>
|
||||
<br/>
|
||||
<p style=""font-size: 0.8em; color: #666;"">If you believe this is an error, please contact your IT support.</p>
|
||||
</div>";
|
||||
|
||||
await _emailSender.SendPasswordResetLinkAsync(user, user.Email!, message);
|
||||
return new AdminForgotPasswordResponse
|
||||
{
|
||||
IsSuccess = true,
|
||||
Message = "Reset link sent successfully"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using Indotalent.Infrastructure.File;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class ChangeAvatarResponse
|
||||
{
|
||||
public bool IsSuccess { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ChangeAvatarRequest
|
||||
{
|
||||
public string UserId { get; set; } = string.Empty;
|
||||
public byte[] FileData { get; set; } = Array.Empty<byte>();
|
||||
public string FileName { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public record ChangeAvatarCommand(ChangeAvatarRequest Data) : IRequest<ChangeAvatarResponse>;
|
||||
|
||||
public class ChangeAvatarHandler : IRequestHandler<ChangeAvatarCommand, ChangeAvatarResponse>
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly FileStorageService _fileStorage;
|
||||
|
||||
public ChangeAvatarHandler(UserManager<ApplicationUser> userManager, FileStorageService fileStorage)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_fileStorage = fileStorage;
|
||||
}
|
||||
|
||||
public async Task<ChangeAvatarResponse> Handle(ChangeAvatarCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(request.Data.UserId);
|
||||
if (user == null) return new ChangeAvatarResponse { IsSuccess = false, Message = "User not found" };
|
||||
|
||||
try
|
||||
{
|
||||
var extension = Path.GetExtension(request.Data.FileName);
|
||||
|
||||
_fileStorage.DeleteOldAvatar(user.AvatarFile);
|
||||
|
||||
var newFileName = await _fileStorage.SaveAvatarAsync(user.Id, request.Data.FileData, extension);
|
||||
|
||||
user.AvatarFile = newFileName;
|
||||
user.UpdatedAt = DateTime.Now;
|
||||
|
||||
var result = await _userManager.UpdateAsync(user);
|
||||
|
||||
return new ChangeAvatarResponse
|
||||
{
|
||||
IsSuccess = result.Succeeded,
|
||||
Message = result.Succeeded ? "Avatar updated successfully" : "Failed to update database"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ChangeAvatarResponse { IsSuccess = false, Message = ex.Message };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class ChangePasswordRequest
|
||||
{
|
||||
public string UserId { get; set; } = string.Empty;
|
||||
public string NewPassword { get; set; } = string.Empty;
|
||||
public string ConfirmPassword { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ChangePasswordResponse
|
||||
{
|
||||
public bool IsSuccess { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public record ChangePasswordCommand(ChangePasswordRequest Data) : IRequest<ChangePasswordResponse>;
|
||||
|
||||
public class ChangePasswordHandler : IRequestHandler<ChangePasswordCommand, ChangePasswordResponse>
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public ChangePasswordHandler(UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task<ChangePasswordResponse> Handle(ChangePasswordCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(request.Data.UserId);
|
||||
if (user == null) return new ChangePasswordResponse { IsSuccess = false, Message = "User not found" };
|
||||
|
||||
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
|
||||
var result = await _userManager.ResetPasswordAsync(user, token, request.Data.NewPassword);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return new ChangePasswordResponse { IsSuccess = true, Message = "Password has been changed successfully" };
|
||||
}
|
||||
|
||||
return new ChangePasswordResponse
|
||||
{
|
||||
IsSuccess = false,
|
||||
Message = string.Join(", ", result.Errors.Select(e => e.Description))
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class ChangePasswordValidator : AbstractValidator<ChangePasswordRequest>
|
||||
{
|
||||
public ChangePasswordValidator()
|
||||
{
|
||||
RuleFor(x => x.UserId).NotEmpty();
|
||||
RuleFor(x => x.NewPassword)
|
||||
.NotEmpty().WithMessage("New password is required")
|
||||
.MinimumLength(6).WithMessage("Password must be at least 6 characters");
|
||||
RuleFor(x => x.ConfirmPassword)
|
||||
.Equal(x => x.NewPassword).WithMessage("Passwords do not match");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class ChangeRoleResponse
|
||||
{
|
||||
public bool IsSuccess { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ChangeRoleRequest
|
||||
{
|
||||
public string UserId { get; set; } = string.Empty;
|
||||
public List<string> Roles { get; set; } = new();
|
||||
}
|
||||
|
||||
public record ChangeRoleCommand(ChangeRoleRequest Data) : IRequest<ChangeRoleResponse>;
|
||||
|
||||
public class ChangeRoleHandler : IRequestHandler<ChangeRoleCommand, ChangeRoleResponse>
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public ChangeRoleHandler(UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task<ChangeRoleResponse> Handle(ChangeRoleCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(request.Data.UserId);
|
||||
if (user == null) return new ChangeRoleResponse { IsSuccess = false, Message = "User not found" };
|
||||
|
||||
var currentRoles = await _userManager.GetRolesAsync(user);
|
||||
|
||||
var removeResult = await _userManager.RemoveFromRolesAsync(user, currentRoles);
|
||||
if (!removeResult.Succeeded) return new ChangeRoleResponse { IsSuccess = false, Message = "Remove role fail" };
|
||||
|
||||
var addResult = await _userManager.AddToRolesAsync(user, request.Data.Roles);
|
||||
return new ChangeRoleResponse
|
||||
{
|
||||
IsSuccess = true,
|
||||
Message = "Roles updated successfully"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using Indotalent.Infrastructure.Authorization.Identity;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class CreateSystemUserRequest
|
||||
{
|
||||
public string? FullName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public bool EmailConfirmed { get; set; }
|
||||
public bool PhoneNumberConfirmed { get; set; }
|
||||
public bool TwoFactorEnabled { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool LockoutEnabled { get; set; }
|
||||
public DateTimeOffset? LockoutEnd { get; set; }
|
||||
public string? SsoIdFirebase { get; set; }
|
||||
public string? SsoIdKeycloak { get; set; }
|
||||
public string? SsoIdAzure { get; set; }
|
||||
public string? SsoIdAws { get; set; }
|
||||
public string? SsoIdOther1 { get; set; }
|
||||
public string? SsoIdOther2 { get; set; }
|
||||
public string? SsoIdOther3 { get; set; }
|
||||
}
|
||||
|
||||
public class CreateSystemUserResponse
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? Email { get; set; }
|
||||
}
|
||||
|
||||
public record CreateSystemUserCommand(CreateSystemUserRequest Data) : IRequest<CreateSystemUserResponse>;
|
||||
|
||||
public class CreateSystemUserHandler : IRequestHandler<CreateSystemUserCommand, CreateSystemUserResponse>
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
public CreateSystemUserHandler(UserManager<ApplicationUser> userManager) => _userManager = userManager;
|
||||
|
||||
public async Task<CreateSystemUserResponse> Handle(CreateSystemUserCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = new ApplicationUser
|
||||
{
|
||||
FullName = request.Data.FullName,
|
||||
Email = request.Data.Email,
|
||||
UserName = request.Data.Email,
|
||||
PhoneNumber = request.Data.PhoneNumber,
|
||||
EmailConfirmed = request.Data.EmailConfirmed,
|
||||
PhoneNumberConfirmed = request.Data.PhoneNumberConfirmed,
|
||||
TwoFactorEnabled = request.Data.TwoFactorEnabled,
|
||||
IsActive = request.Data.IsActive,
|
||||
LockoutEnabled = request.Data.LockoutEnabled,
|
||||
LockoutEnd = request.Data.LockoutEnd,
|
||||
SsoIdFirebase = request.Data.SsoIdFirebase,
|
||||
SsoIdKeycloak = request.Data.SsoIdKeycloak,
|
||||
SsoIdAzure = request.Data.SsoIdAzure,
|
||||
SsoIdAws = request.Data.SsoIdAws,
|
||||
SsoIdOther1 = request.Data.SsoIdOther1,
|
||||
SsoIdOther2 = request.Data.SsoIdOther2,
|
||||
SsoIdOther3 = request.Data.SsoIdOther3,
|
||||
CreatedAt = DateTime.Now
|
||||
};
|
||||
|
||||
var result = await _userManager.CreateAsync(user, request.Data.Password ?? "123456");
|
||||
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||
throw new Exception(errors);
|
||||
}
|
||||
|
||||
var rolesToAdd = ApplicationRoles.AllRoles
|
||||
.Where(role => role != ApplicationRoles.Admin)
|
||||
.ToList();
|
||||
|
||||
if (rolesToAdd.Any())
|
||||
{
|
||||
await _userManager.AddToRolesAsync(user, rolesToAdd);
|
||||
}
|
||||
|
||||
return new CreateSystemUserResponse { Id = user.Id, Email = user.Email };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using FluentValidation;
|
||||
using Indotalent.Shared.Consts;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class CreateSystemUserValidator : AbstractValidator<CreateSystemUserRequest>
|
||||
{
|
||||
public CreateSystemUserValidator()
|
||||
{
|
||||
RuleFor(x => x.Email)
|
||||
.NotEmpty().WithMessage("Email is required")
|
||||
.EmailAddress().WithMessage("Invalid email format");
|
||||
|
||||
RuleFor(x => x.Password)
|
||||
.NotEmpty().WithMessage("Password is required")
|
||||
.MinimumLength(4).WithMessage("Password must be at least 4 characters");
|
||||
|
||||
RuleFor(x => x.FullName)
|
||||
.NotEmpty().WithMessage("Full Name is required")
|
||||
.MaximumLength(GlobalConsts.StringLengthShort);
|
||||
|
||||
RuleFor(x => x.SsoIdFirebase).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdKeycloak).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdAzure).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdAws).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdOther1).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdOther2).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdOther3).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
|
||||
public record DeleteSystemUserByIdRequest(string Id);
|
||||
|
||||
public record DeleteSystemUserByIdCommand(DeleteSystemUserByIdRequest Data) : IRequest<bool>;
|
||||
|
||||
public class DeleteSystemUserByIdHandler : IRequestHandler<DeleteSystemUserByIdCommand, bool>
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public DeleteSystemUserByIdHandler(UserManager<ApplicationUser> userManager) => _userManager = userManager;
|
||||
|
||||
public async Task<bool> Handle(DeleteSystemUserByIdCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(request.Data.Id);
|
||||
if (user == null) return false;
|
||||
|
||||
var result = await _userManager.DeleteAsync(user);
|
||||
return result.Succeeded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using Indotalent.Infrastructure.Database;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class GetSystemUserByIdResponse
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public bool EmailConfirmed { get; set; }
|
||||
public bool PhoneNumberConfirmed { get; set; }
|
||||
public bool TwoFactorEnabled { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool LockoutEnabled { get; set; }
|
||||
public DateTimeOffset? LockoutEnd { get; set; }
|
||||
public int AccessFailedCount { get; set; }
|
||||
public string? SsoIdFirebase { get; set; }
|
||||
public string? SsoIdKeycloak { get; set; }
|
||||
public string? SsoIdAzure { get; set; }
|
||||
public string? SsoIdAws { get; set; }
|
||||
public string? SsoIdOther1 { get; set; }
|
||||
public string? SsoIdOther2 { get; set; }
|
||||
public string? SsoIdOther3 { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? LastLoginAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public List<string> UserRoles { get; set; } = new();
|
||||
public string? AvatarFile { get; set; }
|
||||
}
|
||||
|
||||
public record GetSystemUserByIdQuery(string Id) : IRequest<GetSystemUserByIdResponse?>;
|
||||
|
||||
public class GetSystemUserByIdHandler : IRequestHandler<GetSystemUserByIdQuery, GetSystemUserByIdResponse?>
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
|
||||
public GetSystemUserByIdHandler(AppDbContext context) => _context = context;
|
||||
|
||||
public async Task<GetSystemUserByIdResponse?> Handle(GetSystemUserByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _context.Users
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Id == request.Id)
|
||||
.Select(x => new GetSystemUserByIdResponse
|
||||
{
|
||||
Id = x.Id,
|
||||
FullName = x.FullName,
|
||||
Email = x.Email,
|
||||
PhoneNumber = x.PhoneNumber,
|
||||
UserName = x.UserName,
|
||||
EmailConfirmed = x.EmailConfirmed,
|
||||
PhoneNumberConfirmed = x.PhoneNumberConfirmed,
|
||||
TwoFactorEnabled = x.TwoFactorEnabled,
|
||||
IsActive = x.IsActive,
|
||||
LockoutEnabled = x.LockoutEnabled,
|
||||
LockoutEnd = x.LockoutEnd,
|
||||
AccessFailedCount = x.AccessFailedCount,
|
||||
SsoIdFirebase = x.SsoIdFirebase,
|
||||
SsoIdKeycloak = x.SsoIdKeycloak,
|
||||
SsoIdAzure = x.SsoIdAzure,
|
||||
SsoIdAws = x.SsoIdAws,
|
||||
SsoIdOther1 = x.SsoIdOther1,
|
||||
SsoIdOther2 = x.SsoIdOther2,
|
||||
SsoIdOther3 = x.SsoIdOther3,
|
||||
CreatedAt = x.CreatedAt,
|
||||
LastLoginAt = x.LastLoginAt,
|
||||
UpdatedAt = x.UpdatedAt,
|
||||
AvatarFile = x.AvatarFile,
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (user == null) return null;
|
||||
|
||||
user.UserRoles = await (from ur in _context.UserRoles
|
||||
join r in _context.Roles on ur.RoleId equals r.Id
|
||||
where ur.UserId == user.Id
|
||||
select r.Name!)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Indotalent.Infrastructure.Database;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class GetSystemUserListResponse
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
public string? AvatarFile { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? SsoIdFirebase { get; set; }
|
||||
public string? SsoIdKeycloak { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public List<string> UserRoles { get; set; } = new();
|
||||
}
|
||||
|
||||
public record GetSystemUserListQuery() : IRequest<List<GetSystemUserListResponse>>;
|
||||
|
||||
public class GetSystemUserListHandler : IRequestHandler<GetSystemUserListQuery, List<GetSystemUserListResponse>>
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
|
||||
public GetSystemUserListHandler(AppDbContext context) => _context = context;
|
||||
|
||||
public async Task<List<GetSystemUserListResponse>> Handle(GetSystemUserListQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var users = await _context.Users
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
.Select(x => new GetSystemUserListResponse
|
||||
{
|
||||
Id = x.Id,
|
||||
FullName = x.FullName,
|
||||
Email = x.Email,
|
||||
SsoIdFirebase = x.SsoIdFirebase,
|
||||
SsoIdKeycloak = x.SsoIdKeycloak,
|
||||
IsActive = x.IsActive,
|
||||
AvatarFile = x.AvatarFile,
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var userIds = users.Select(u => u.Id).ToList();
|
||||
|
||||
var userRolesMap = await (from ur in _context.UserRoles
|
||||
join r in _context.Roles on ur.RoleId equals r.Id
|
||||
where userIds.Contains(ur.UserId)
|
||||
select new { ur.UserId, r.Name })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
user.UserRoles = userRolesMap
|
||||
.Where(x => x.UserId == user.Id)
|
||||
.Select(x => x.Name!)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class UpdateSystemUserRequest
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public bool EmailConfirmed { get; set; }
|
||||
public bool PhoneNumberConfirmed { get; set; }
|
||||
public bool TwoFactorEnabled { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool LockoutEnabled { get; set; }
|
||||
public DateTimeOffset? LockoutEnd { get; set; }
|
||||
public int AccessFailedCount { get; set; }
|
||||
public string? SsoIdFirebase { get; set; }
|
||||
public string? SsoIdKeycloak { get; set; }
|
||||
public string? SsoIdAzure { get; set; }
|
||||
public string? SsoIdAws { get; set; }
|
||||
public string? SsoIdOther1 { get; set; }
|
||||
public string? SsoIdOther2 { get; set; }
|
||||
public string? SsoIdOther3 { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? LastLoginAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public List<string> UserRoles { get; set; } = new();
|
||||
public string? AvatarFile { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateSystemUserResponse
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public record UpdateSystemUserCommand(UpdateSystemUserRequest Data) : IRequest<UpdateSystemUserResponse>;
|
||||
|
||||
public class UpdateSystemUserHandler : IRequestHandler<UpdateSystemUserCommand, UpdateSystemUserResponse>
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
public UpdateSystemUserHandler(UserManager<ApplicationUser> userManager) => _userManager = userManager;
|
||||
|
||||
public async Task<UpdateSystemUserResponse> Handle(UpdateSystemUserCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(request.Data.Id ?? string.Empty);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return new UpdateSystemUserResponse { Id = request.Data.Id, Success = false };
|
||||
}
|
||||
|
||||
user.FullName = request.Data.FullName;
|
||||
user.PhoneNumber = request.Data.PhoneNumber;
|
||||
user.IsActive = request.Data.IsActive;
|
||||
user.EmailConfirmed = request.Data.EmailConfirmed;
|
||||
user.PhoneNumberConfirmed = request.Data.PhoneNumberConfirmed;
|
||||
user.TwoFactorEnabled = request.Data.TwoFactorEnabled;
|
||||
user.LockoutEnabled = request.Data.LockoutEnabled;
|
||||
user.LockoutEnd = request.Data.LockoutEnd;
|
||||
|
||||
user.SsoIdFirebase = request.Data.SsoIdFirebase;
|
||||
user.SsoIdKeycloak = request.Data.SsoIdKeycloak;
|
||||
user.SsoIdAzure = request.Data.SsoIdAzure;
|
||||
user.SsoIdAws = request.Data.SsoIdAws;
|
||||
user.SsoIdOther1 = request.Data.SsoIdOther1;
|
||||
user.SsoIdOther2 = request.Data.SsoIdOther2;
|
||||
user.SsoIdOther3 = request.Data.SsoIdOther3;
|
||||
|
||||
user.UpdatedAt = DateTime.Now;
|
||||
|
||||
user.AvatarFile = request.Data.AvatarFile;
|
||||
|
||||
var result = await _userManager.UpdateAsync(user);
|
||||
|
||||
return new UpdateSystemUserResponse
|
||||
{
|
||||
Id = user.Id,
|
||||
Success = result.Succeeded
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using FluentValidation;
|
||||
using Indotalent.Shared.Consts;
|
||||
|
||||
namespace Indotalent.Features.Setting.SystemUser.Cqrs;
|
||||
|
||||
public class UpdateSystemUserValidator : AbstractValidator<UpdateSystemUserRequest>
|
||||
{
|
||||
public UpdateSystemUserValidator()
|
||||
{
|
||||
RuleFor(x => x.Id).NotEmpty().WithMessage("User ID is required for update");
|
||||
RuleFor(x => x.Email)
|
||||
.NotEmpty().WithMessage("Email is required")
|
||||
.EmailAddress().WithMessage("Invalid email format");
|
||||
|
||||
RuleFor(x => x.FullName)
|
||||
.NotEmpty().WithMessage("Full Name is required")
|
||||
.MaximumLength(GlobalConsts.StringLengthShort);
|
||||
|
||||
RuleFor(x => x.SsoIdFirebase).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdKeycloak).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdAzure).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdAws).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdOther1).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdOther2).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
RuleFor(x => x.SsoIdOther3).MaximumLength(GlobalConsts.StringLengthMedium);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user