mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 18:53:12 +00:00
193 lines
9.1 KiB
C#
193 lines
9.1 KiB
C#
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;
|
|
|
|
namespace Api.SeaHavenIndustries.Controllers
|
|
{
|
|
[Authorize]
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
[Route("api/uplifts")]
|
|
public class UpliftController : Controller
|
|
{
|
|
private readonly IUpliftService _upliftService;
|
|
private readonly ILogger<UpliftController> _logger;
|
|
|
|
public UpliftController(IUpliftService upliftService, ILogger<UpliftController> logger)
|
|
{
|
|
_upliftService = upliftService;
|
|
_logger = logger;
|
|
}
|
|
|
|
[HttpGet]
|
|
public async Task<IActionResult> List([FromQuery] string? status = "Pending", [FromQuery] int? tier = null, [FromQuery] int page = 1, [FromQuery] int pageSize = 25, CancellationToken cancellationToken = default)
|
|
{
|
|
var result = await _upliftService.ListAsync(User, status, tier, page, pageSize, cancellationToken);
|
|
return Ok(new DataResponse { Status = "Success", Data = result });
|
|
}
|
|
|
|
[HttpGet("dispatch/{dispatchId:int}")]
|
|
public async Task<IActionResult> ListForDispatch(int dispatchId, CancellationToken cancellationToken = default)
|
|
{
|
|
var items = await _upliftService.ListForDispatchAsync(User, dispatchId, cancellationToken);
|
|
return Ok(new DataResponse { Status = "Success", Data = items });
|
|
}
|
|
|
|
[HttpPost("{id:int}/approve")]
|
|
public async Task<IActionResult> Approve(int id, [FromBody] DecisionRequest? body, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var result = await _upliftService.ApproveAsync(User, id, body?.Note, cancellationToken);
|
|
return Ok(new DataResponse { Status = "Success", Data = result });
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
return NotFound(new Response { Status = "Error", Message = _logger.Sanitize(ex, "Uplift request not found") });
|
|
}
|
|
catch (UpliftForbiddenException ex)
|
|
{
|
|
return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action on this uplift request") });
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "This uplift request cannot be modified in its current state") });
|
|
}
|
|
}
|
|
|
|
[HttpPost("{id:int}/deny")]
|
|
public async Task<IActionResult> Deny(int id, [FromBody] DecisionRequest? body, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var result = await _upliftService.DenyAsync(User, id, body?.Note, cancellationToken);
|
|
return Ok(new DataResponse { Status = "Success", Data = result });
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
return NotFound(new Response { Status = "Error", Message = _logger.Sanitize(ex, "Uplift request not found") });
|
|
}
|
|
catch (UpliftForbiddenException ex)
|
|
{
|
|
return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action on this uplift request") });
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "This uplift request cannot be modified in its current state") });
|
|
}
|
|
}
|
|
|
|
// SH-101: canonical reject route (result is Rejected); deny alias stays compatible.
|
|
[HttpPost("{id:int}/reject")]
|
|
public async Task<IActionResult> Reject(int id, [FromBody] DecisionRequest? body, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var result = await _upliftService.RejectAsync(User, id, body?.Note, cancellationToken);
|
|
return Ok(new DataResponse { Status = "Success", Data = result });
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
return NotFound(new Response { Status = "Error", Message = _logger.Sanitize(ex, "Uplift request not found") });
|
|
}
|
|
catch (UpliftForbiddenException ex)
|
|
{
|
|
return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action on this uplift request") });
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "This uplift request cannot be modified in its current state") });
|
|
}
|
|
}
|
|
|
|
// SH-101: internal request-changes route (note + tier authorization + audit).
|
|
[HttpPost("{id:int}/request-changes")]
|
|
public async Task<IActionResult> RequestChanges(int id, [FromBody] DecisionRequest? body, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var result = await _upliftService.RequestChangesAsync(User, id, body?.Note ?? string.Empty, cancellationToken);
|
|
return Ok(new DataResponse { Status = "Success", Data = result });
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
return NotFound(new Response { Status = "Error", Message = _logger.Sanitize(ex, "Uplift request not found") });
|
|
}
|
|
catch (UpliftForbiddenException ex)
|
|
{
|
|
return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action on this uplift request") });
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "This uplift request cannot be modified in its current state") });
|
|
}
|
|
}
|
|
|
|
[HttpPost("{id:int}/revoke")]
|
|
public async Task<IActionResult> Revoke(
|
|
int id,
|
|
[FromBody] DecisionRequest? body,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var result = await _upliftService.RevokeAsync(
|
|
User,
|
|
id,
|
|
body?.Note ?? string.Empty,
|
|
cancellationToken);
|
|
return Ok(new DataResponse { Status = "Success", Data = result });
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
return NotFound(new Response { Status = "Error", Message = _logger.Sanitize(ex, "Uplift request not found") });
|
|
}
|
|
catch (UpliftForbiddenException ex)
|
|
{
|
|
return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to revoke this uplift") });
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "This uplift request cannot be revoked") });
|
|
}
|
|
}
|
|
|
|
[HttpGet("can-approve")]
|
|
public IActionResult CanApprove([FromQuery] int tier)
|
|
{
|
|
return Ok(new DataResponse { Status = "Success", Data = new { canApprove = _upliftService.CanApprove(User, tier) } });
|
|
}
|
|
|
|
// SH-101: authorized internal download of the Passed UpliftEvidence file linked to
|
|
// a specific uplift request. Server-side linkage only; no vendor/public path or
|
|
// document id is accepted from the client. 404 covers missing request/evidence and
|
|
// any non-UpliftEvidence document; 423 covers a scan that has not Passed.
|
|
[HttpGet("{id:int}/evidence")]
|
|
public async Task<IActionResult> DownloadEvidence(int id, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var result = await _upliftService.GetEvidenceForDownloadAsync(User, id, cancellationToken);
|
|
return result.Outcome switch
|
|
{
|
|
VendorDocumentDownloadOutcome.Ok => File(result.Content!, result.ContentType!, result.FileName!),
|
|
VendorDocumentDownloadOutcome.Locked => StatusCode(StatusCodes.Status423Locked, new Response { Status = "Locked", Message = "The uplift evidence is not available for download yet." }),
|
|
_ => NotFound(new Response { Status = "Error", Message = "Uplift evidence not found" })
|
|
};
|
|
}
|
|
catch (UpliftForbiddenException ex)
|
|
{
|
|
return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to view evidence for this uplift request") });
|
|
}
|
|
}
|
|
|
|
public class DecisionRequest
|
|
{
|
|
public string? Note { get; set; }
|
|
}
|
|
}
|
|
}
|