49 lines
1.8 KiB
C#
49 lines
1.8 KiB
C#
|
|
using FluentValidation;
|
||
|
|
using Microsoft.AspNetCore.Mvc;
|
||
|
|
using SIGCM2.Application.Abstractions;
|
||
|
|
using SIGCM2.Application.Auth.Login;
|
||
|
|
|
||
|
|
namespace SIGCM2.Api.Controllers;
|
||
|
|
|
||
|
|
[ApiController]
|
||
|
|
[Route("api/v1/auth")]
|
||
|
|
public sealed class AuthController : ControllerBase
|
||
|
|
{
|
||
|
|
private readonly IDispatcher _dispatcher;
|
||
|
|
private readonly IValidator<LoginCommand> _validator;
|
||
|
|
|
||
|
|
public AuthController(IDispatcher dispatcher, IValidator<LoginCommand> validator)
|
||
|
|
{
|
||
|
|
_dispatcher = dispatcher;
|
||
|
|
_validator = validator;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Authenticates a user and returns a JWT access token.</summary>
|
||
|
|
/// <response code="200">Returns access token and refresh token.</response>
|
||
|
|
/// <response code="400">Validation error — missing or empty fields.</response>
|
||
|
|
/// <response code="401">Invalid credentials.</response>
|
||
|
|
[HttpPost("login")]
|
||
|
|
[ProducesResponseType(typeof(LoginResponseDto), StatusCodes.Status200OK)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
|
|
public async Task<IActionResult> Login([FromBody] LoginRequest request)
|
||
|
|
{
|
||
|
|
var command = new LoginCommand(request.Username ?? string.Empty, request.Password ?? string.Empty);
|
||
|
|
|
||
|
|
var validation = await _validator.ValidateAsync(command);
|
||
|
|
if (!validation.IsValid)
|
||
|
|
{
|
||
|
|
var errors = validation.Errors
|
||
|
|
.GroupBy(e => e.PropertyName)
|
||
|
|
.ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray());
|
||
|
|
return BadRequest(new { errors });
|
||
|
|
}
|
||
|
|
|
||
|
|
var result = await _dispatcher.Send<LoginCommand, LoginResponseDto>(command);
|
||
|
|
return Ok(result);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Login request body — nullable to catch missing field scenarios.</summary>
|
||
|
|
public sealed record LoginRequest(string? Username, string? Password);
|