using AutoMapper; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using mws.backend.dotnet.application.Common; using mws.backend.dotnet.application.Permissions; using mws.backend.dotnet.application.Users; using mws.backend.dotnet.domain.Users; using mws.backend.dotnet.infrastructure.Persistence; namespace mws.backend.dotnet.api.Controllers; [ApiController] [Route("api/users")] [Authorize] public class UsersController(ISender sender, AppDbContext db, IPermissionService permissions, IMapper mapper) : ControllerBase { [HttpGet] public async Task>> GetAll( [FromQuery] string? q, [FromQuery] bool? isActive, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, CancellationToken ct = default) { var filter = new UserListFilter(q, isActive); return Ok(await sender.Send(new GetUsersQuery(User.GetUserId(), filter, page, pageSize), ct)); } [HttpPost] public async Task> Create([FromBody] CreateUserRequest request, CancellationToken ct) { return Ok(await sender.Send(new CreateUserCommand(User.GetUserId(), request), ct)); } [HttpPut("{id:guid}")] public async Task> Update(Guid id, [FromBody] UpdateUserRequest request, CancellationToken ct) { return Ok(await sender.Send(new UpdateUserCommand(User.GetUserId(), id, request), ct)); } [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken ct) { await sender.Send(new DeleteUserCommand(User.GetUserId(), id), ct); return NoContent(); } [HttpPost("{id:guid}/reset-password")] public async Task ResetPassword(Guid id, [FromBody] ResetPasswordRequest request, CancellationToken ct) { await sender.Send(new ResetPasswordCommand(User.GetUserId(), id, request), ct); return NoContent(); } [HttpGet("list")] public async Task>> GetList([FromQuery] string? q, CancellationToken ct) { var query = db.Users.AsQueryable(); if (!string.IsNullOrWhiteSpace(q)) { var lower = q.Trim().ToLower(); query = query.Where(u => u.Username.ToLower().Contains(lower) || u.DisplayName.ToLower().Contains(lower)); } var users = await query.OrderBy(u => u.DisplayName).ToListAsync(ct); return Ok(mapper.Map>(users)); } [HttpGet("{id:guid}/roles")] public async Task> GetUserRoles(Guid id, CancellationToken ct) { await permissions.EnsureAsync(User.GetUserId(), "permissions", PermissionAction.View, ct); var user = await db.Users .Include(u => u.UserRoles) .ThenInclude(ur => ur.Role) .FirstOrDefaultAsync(u => u.Id == id, ct); if (user is null) return NotFound("User not found"); var allRoles = await db.Roles.Include(r => r.Permissions).OrderBy(r => r.Name).ToListAsync(ct); var assignedRoleIds = user.UserRoles.Select(ur => ur.RoleId).ToHashSet(); var assigned = allRoles.Where(r => assignedRoleIds.Contains(r.Id)).ToList(); var unassigned = allRoles.Where(r => !assignedRoleIds.Contains(r.Id)).ToList(); return Ok(new UserRoleDetailDto { UserId = user.Id, Username = user.Username, DisplayName = user.DisplayName, AssignedRoles = mapper.Map>(assigned), UnassignedRoles = mapper.Map>(unassigned) }); } [HttpPost("{id:guid}/roles")] public async Task AssignRole(Guid id, [FromBody] AssignRoleRequest req, CancellationToken ct) { await permissions.EnsureAsync(User.GetUserId(), "permissions", PermissionAction.Edit, ct); var user = await db.Users.Include(u => u.UserRoles).FirstOrDefaultAsync(u => u.Id == id, ct); if (user is null) return NotFound("User not found"); if (!user.UserRoles.Any(ur => ur.RoleId == req.RoleId)) { user.UserRoles.Add(new UserRole { UserId = id, RoleId = req.RoleId }); await db.SaveChangesAsync(ct); } return NoContent(); } [HttpDelete("{id:guid}/roles/{roleId:guid}")] public async Task UnassignRole(Guid id, Guid roleId, CancellationToken ct) { await permissions.EnsureAsync(User.GetUserId(), "permissions", PermissionAction.Edit, ct); var ur = await db.UserRoles.FirstOrDefaultAsync(x => x.UserId == id && x.RoleId == roleId, ct); if (ur != null) { db.UserRoles.Remove(ur); await db.SaveChangesAsync(ct); } return NoContent(); } [HttpGet("{id:guid}/permissions")] public async Task>> GetUserPermissions(Guid id, CancellationToken ct) { await permissions.EnsureAsync(User.GetUserId(), "permissions", PermissionAction.View, ct); var roleIds = await db.UserRoles.Where(ur => ur.UserId == id).Select(ur => ur.RoleId).ToListAsync(ct); var permissionsList = await db.RolePermissions.Where(p => roleIds.Contains(p.RoleId)).ToListAsync(ct); var merged = permissionsList.GroupBy(p => p.Screen).ToDictionary( g => g.Key, g => new { CanView = g.Any(p => p.CanView), CanCreate = g.Any(p => p.CanCreate), CanEdit = g.Any(p => p.CanEdit), CanDelete = g.Any(p => p.CanDelete), } ); var res = ScreenCatalog.Screens.Select(s => { merged.TryGetValue(s.Key, out var p); return new MenuItemDto { Key = s.Key, Label = s.Label, Path = s.Path, CanView = p?.CanView ?? false, CanCreate = p?.CanCreate ?? false, CanEdit = p?.CanEdit ?? false, CanDelete = p?.CanDelete ?? false, }; }).ToList(); return Ok(res); } }