shoc-backend/Api.SeaHavenIndustries/Controllers/UserController.cs
Alexandre Brandizzi 7d245eb717
refactor: enforce backend boundaries and optimize dispatch (#30)
* refactor(api): enforce service and data-service boundaries

* refactor(api): complete feature service boundaries

* refactor(identity): enforce service and data boundaries

* refactor(vendors): enforce service and data boundaries

* refactor(workorders): enforce service and data boundaries

* refactor(backend): enforce architecture and optimize dispatch

* style(backend): format changed architecture files

* fix(architecture): address backend review follow-ups

* fix(backend): sanitize exception disclosure in changed API endpoints

Replace raw exception-message disclosure (ex.Message) returned to API
callers with a stable sanitized public message plus correlated structured
internal logging, across the endpoints changed in this PR.

- Add SanitizedErrors helper: logs the original exception at Error with a
  generated correlation id and returns a stable public message referencing
  it so support can trace without exposing internals.
- Inject ILogger<T> into the 14 changed controllers and route every
  ex.Message/dbex.Message disclosure through the helper, preserving status
  codes, response shapes, and business data (e.g. OpenWorkOrders).
- Leave FluentValidation (vex.Errors) and existing fixed-message catches
  untouched; out-of-scope controllers (Account/Contact/Employee/Asset/
  PMSchedule) are unchanged.
- Add focused tests proving internal exception text is not returned and
  that Error logging carrying the original exception is invoked.

* fix(architecture): abstract job run state access

* style: format board update service

* test: use collection assertion idiom
2026-07-24 17:35:34 -03:00

137 lines
4.8 KiB
C#

using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
using System.Security.Claims;
namespace Api.SeaHavenIndustries.Controllers
{
[Authorize]
[ApiController]
[Route("api/User")]
public class UserController : Controller
{
private readonly IUserService _userService;
private readonly ILogger<UserController> _logger;
public UserController(IUserService userService, ILogger<UserController> logger)
{
_userService = userService;
_logger = logger;
}
[Route("")]
[HttpGet]
public async Task<IActionResult> GetUsers(CancellationToken cancellationToken)
{
var users = await _userService.GetUsersAsync(cancellationToken);
return Ok(users);
}
[HttpPost]
[Route("AddUser")]
public async Task<IActionResult> AddUser(User_DTO user, CancellationToken cancellationToken)
{
try
{
var dto = new AddUserRequestDTO
{
Name = user.Name,
Email = user.Email,
Contact = user.Contact,
Role = user.Role
};
var outcome = await _userService.AddUserAsync(dto, cancellationToken);
if (!outcome.Success)
{
return BadRequest(new Response { Status = "Error", Message = outcome.Error });
}
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
}
}
[HttpPut]
[Route("EditUser")]
public async Task<IActionResult> EditUser(EditUser_DTO user, CancellationToken cancellationToken)
{
try
{
var dto = new EditUserRequestDTO
{
Id = user.Id,
Name = user.Name,
Email = user.Email,
Role = user.Role
};
var succeeded = await _userService.EditUserAsync(dto, cancellationToken);
if (!succeeded)
return BadRequest(new Response { Status = "Error", Message = "User not found" });
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
}
}
[HttpDelete]
[Route("DeleteUser")]
public async Task<IActionResult> DeleteUser(EditUser_DTO req, CancellationToken cancellationToken)
{
string id = req.Id ?? "";
try
{
var found = await _userService.DeleteUserAsync(id, cancellationToken);
if (!found)
{
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
}
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
}
}
[HttpPost]
[Route("DeleteCurrentUser")]
public async Task<IActionResult> DeleteCurrentUser(CancellationToken cancellationToken)
{
try
{
var userid = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "";
await _userService.DeleteCurrentUserAsync(userid, cancellationToken);
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
}
}
[HttpGet]
[Route("UserProfile")]
public async Task<IActionResult> UserProfile(CancellationToken cancellationToken)
{
var userid = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "";
var data = await _userService.GetUserProfileAsync(userid, cancellationToken);
return Ok(data.Select(d => new
{
d.Id,
Name = d.Name,
addeddate = d.AddedDate,
d.Email,
d.Contact
}));
}
}
}