using line_gestao_api.Data; using line_gestao_api.Dtos; using line_gestao_api.Models; using line_gestao_api.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace line_gestao_api.Controllers; [ApiController] [Route("api/users")] [Authorize] public class UsersController : ControllerBase { private static readonly HashSet AllowedRoles = new(StringComparer.OrdinalIgnoreCase) { "admin", "gestor" }; private readonly AppDbContext _db; private readonly UserManager _userManager; private readonly RoleManager> _roleManager; private readonly ITenantProvider _tenantProvider; public UsersController( AppDbContext db, UserManager userManager, RoleManager> roleManager, ITenantProvider tenantProvider) { _db = db; _userManager = userManager; _roleManager = roleManager; _tenantProvider = tenantProvider; } [HttpPost] [Authorize(Roles = "admin")] public async Task> Create([FromBody] UserCreateRequest req) { var errors = ValidateCreate(req); if (errors.Count > 0) { return BadRequest(new ValidationErrorResponse { Errors = errors }); } if (_tenantProvider.TenantId == null) { return Unauthorized(); } var tenantId = _tenantProvider.TenantId.Value; var email = req.Email.Trim().ToLowerInvariant(); var normalizedEmail = _userManager.NormalizeEmail(email); var exists = await _userManager.Users .AnyAsync(u => u.TenantId == tenantId && u.NormalizedEmail == normalizedEmail); if (exists) { return BadRequest(new ValidationErrorResponse { Errors = new List { new() { Field = "email", Message = "E-mail já cadastrado." } } }); } var role = req.Permissao.Trim().ToLowerInvariant(); if (!AllowedRoles.Contains(role) || !await _roleManager.RoleExistsAsync(role)) { return BadRequest(new ValidationErrorResponse { Errors = new List { new() { Field = "permissao", Message = "Permissão inválida." } } }); } var user = new ApplicationUser { Name = req.Nome.Trim(), Email = email, UserName = email, TenantId = tenantId, IsActive = true, EmailConfirmed = true }; var createResult = await _userManager.CreateAsync(user, req.Senha); if (!createResult.Succeeded) { return BadRequest(new ValidationErrorResponse { Errors = createResult.Errors.Select(e => new ValidationErrorDto { Field = "senha", Message = e.Description }).ToList() }); } await _userManager.AddToRoleAsync(user, role); var response = new UserListItemDto { Id = user.Id, Nome = user.Name, Email = user.Email ?? string.Empty, Permissao = role, TenantId = user.TenantId, Ativo = user.IsActive }; return CreatedAtAction(nameof(GetById), new { id = user.Id }, response); } [HttpGet] [Authorize(Roles = "admin")] public async Task>> GetAll( [FromQuery] string? search, [FromQuery] string? permissao, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { page = page < 1 ? 1 : page; pageSize = pageSize < 1 ? 20 : pageSize; var usersQuery = _userManager.Users.AsNoTracking(); if (!string.IsNullOrWhiteSpace(search)) { var term = search.Trim(); usersQuery = usersQuery.Where(u => EF.Functions.ILike(u.Name, $"%{term}%") || EF.Functions.ILike(u.Email ?? string.Empty, $"%{term}%")); } IQueryable userIdsByRole = Enumerable.Empty().AsQueryable(); if (!string.IsNullOrWhiteSpace(permissao)) { var roleName = permissao.Trim().ToLowerInvariant(); userIdsByRole = from ur in _db.UserRoles join r in _db.Roles on ur.RoleId equals r.Id where r.Name == roleName select ur.UserId; usersQuery = usersQuery.Where(u => userIdsByRole.Contains(u.Id)); } var total = await usersQuery.CountAsync(); var users = await usersQuery .OrderBy(u => u.Name) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); var roleLookup = await (from ur in _db.UserRoles join r in _db.Roles on ur.RoleId equals r.Id where users.Select(u => u.Id).Contains(ur.UserId) select new { ur.UserId, Role = r.Name ?? "" }) .ToListAsync(); var roleMap = roleLookup .GroupBy(x => x.UserId) .ToDictionary(g => g.Key, g => g.First().Role); var items = users.Select(u => new UserListItemDto { Id = u.Id, Nome = u.Name, Email = u.Email ?? string.Empty, Permissao = roleMap.GetValueOrDefault(u.Id, string.Empty), TenantId = u.TenantId, Ativo = u.IsActive }).ToList(); return Ok(new PagedResult { Page = page, PageSize = pageSize, Total = total, Items = items }); } [HttpGet("{id:guid}")] [Authorize(Roles = "admin")] public async Task> GetById(Guid id) { var user = await _userManager.Users.AsNoTracking().FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return NotFound(); } var roles = await _userManager.GetRolesAsync(user); var role = roles.FirstOrDefault() ?? string.Empty; return Ok(new UserListItemDto { Id = user.Id, Nome = user.Name, Email = user.Email ?? string.Empty, Permissao = role, TenantId = user.TenantId, Ativo = user.IsActive }); } [HttpPatch("{id:guid}")] [Authorize(Roles = "admin")] public async Task Update(Guid id, [FromBody] UserUpdateRequest req) { var errors = await ValidateUpdateAsync(id, req); if (errors.Count > 0) { return BadRequest(new ValidationErrorResponse { Errors = errors }); } var user = await _userManager.Users.FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return NotFound(); } if (!string.IsNullOrWhiteSpace(req.Nome)) { user.Name = req.Nome.Trim(); } if (!string.IsNullOrWhiteSpace(req.Email)) { var email = req.Email.Trim().ToLowerInvariant(); if (!string.Equals(user.Email, email, StringComparison.OrdinalIgnoreCase)) { await _userManager.SetEmailAsync(user, email); await _userManager.SetUserNameAsync(user, email); } } if (req.Ativo.HasValue) { user.IsActive = req.Ativo.Value; } if (!string.IsNullOrWhiteSpace(req.Permissao)) { var roleName = req.Permissao.Trim().ToLowerInvariant(); if (!AllowedRoles.Contains(roleName) || !await _roleManager.RoleExistsAsync(roleName)) { return BadRequest(new ValidationErrorResponse { Errors = new List { new() { Field = "permissao", Message = "Permissão inválida." } } }); } var existingRoles = await _userManager.GetRolesAsync(user); if (existingRoles.Count > 0) { await _userManager.RemoveFromRolesAsync(user, existingRoles); } await _userManager.AddToRoleAsync(user, roleName); } if (!string.IsNullOrWhiteSpace(req.Senha)) { var token = await _userManager.GeneratePasswordResetTokenAsync(user); var resetResult = await _userManager.ResetPasswordAsync(user, token, req.Senha); if (!resetResult.Succeeded) { return BadRequest(new ValidationErrorResponse { Errors = resetResult.Errors.Select(e => new ValidationErrorDto { Field = "senha", Message = e.Description }).ToList() }); } } await _userManager.UpdateAsync(user); return NoContent(); } private static List ValidateCreate(UserCreateRequest req) { var errors = new List(); if (string.IsNullOrWhiteSpace(req.Nome)) { errors.Add(new ValidationErrorDto { Field = "nome", Message = "Nome é obrigatório." }); } if (string.IsNullOrWhiteSpace(req.Email)) { errors.Add(new ValidationErrorDto { Field = "email", Message = "Email é obrigatório." }); } if (string.IsNullOrWhiteSpace(req.Senha) || req.Senha.Length < 6) { errors.Add(new ValidationErrorDto { Field = "senha", Message = "Senha deve ter pelo menos 6 caracteres." }); } if (req.Senha != req.ConfirmarSenha) { errors.Add(new ValidationErrorDto { Field = "confirmarSenha", Message = "Confirmação de senha inválida." }); } if (string.IsNullOrWhiteSpace(req.Permissao)) { errors.Add(new ValidationErrorDto { Field = "permissao", Message = "Permissão é obrigatória." }); } else if (!AllowedRoles.Contains(req.Permissao.Trim())) { errors.Add(new ValidationErrorDto { Field = "permissao", Message = "Permissão inválida." }); } return errors; } private async Task> ValidateUpdateAsync(Guid userId, UserUpdateRequest req) { var errors = new List(); if (!string.IsNullOrWhiteSpace(req.Nome) && req.Nome.Trim().Length < 2) { errors.Add(new ValidationErrorDto { Field = "nome", Message = "Nome inválido." }); } if (!string.IsNullOrWhiteSpace(req.Email)) { var email = req.Email.Trim().ToLowerInvariant(); var normalized = _userManager.NormalizeEmail(email); var tenantId = _tenantProvider.TenantId; if (tenantId == null) { errors.Add(new ValidationErrorDto { Field = "email", Message = "Tenant inválido." }); } else { var exists = await _userManager.Users.AnyAsync(u => u.Id != userId && u.TenantId == tenantId && u.NormalizedEmail == normalized); if (exists) { errors.Add(new ValidationErrorDto { Field = "email", Message = "E-mail já cadastrado." }); } } } if (!string.IsNullOrWhiteSpace(req.Senha)) { if (req.Senha.Length < 6) { errors.Add(new ValidationErrorDto { Field = "senha", Message = "Senha deve ter pelo menos 6 caracteres." }); } if (req.Senha != req.ConfirmarSenha) { errors.Add(new ValidationErrorDto { Field = "confirmarSenha", Message = "Confirmação de senha inválida." }); } } if (!string.IsNullOrWhiteSpace(req.Permissao) && !AllowedRoles.Contains(req.Permissao.Trim())) { errors.Add(new ValidationErrorDto { Field = "permissao", Message = "Permissão inválida." }); } return errors; } }