| | | 1 | | using ArturRios.Heimdall.Domain.Enums; |
| | | 2 | | using ArturRios.Heimdall.Query.HealthChecks; |
| | | 3 | | using ArturRios.Heimdall.Query.Input; |
| | | 4 | | using ArturRios.Heimdall.Query.Output; |
| | | 5 | | using ArturRios.Mediator.Query; |
| | | 6 | | using ArturRios.Output; |
| | | 7 | | using ArturRios.Util.Http; |
| | | 8 | | using ArturRios.Util.WebApi.AspNetCore; |
| | | 9 | | using ArturRios.Util.WebApi.Security.Attributes; |
| | | 10 | | using Microsoft.AspNetCore.Mvc; |
| | | 11 | | |
| | | 12 | | namespace ArturRios.Heimdall.WebApi.Controllers; |
| | | 13 | | |
| | | 14 | | /// <remarks> |
| | | 15 | | /// Under <c>api/</c> like every other route, because that prefix is all that carries a request on |
| | | 16 | | /// the web UI's host through to the API — see <c>OpenApiContractTests</c>. The container probe and |
| | | 17 | | /// yggdrasil's status catalog call the root <c>/healthcheck</c>, which the edge pipeline in |
| | | 18 | | /// <see cref="Startup" /> rewrites to this address. |
| | | 19 | | /// </remarks> |
| | | 20 | | [Route("api/healthcheck")] |
| | 23 | 21 | | public class HealthCheckController(QueryMediator queryMediator) : Controller |
| | | 22 | | { |
| | | 23 | | /// <summary> |
| | | 24 | | /// Basic liveness check (UC-30, FR-HC-01): confirms the API process is up and responding. |
| | | 25 | | /// Public — no authentication required. |
| | | 26 | | /// </summary> |
| | | 27 | | [HttpGet] |
| | | 28 | | [Route("")] |
| | | 29 | | [AllowAnonymous] |
| | | 30 | | public ActionResult<DataOutput<string?>> HelloWorld() |
| | | 31 | | { |
| | 21 | 32 | | var result = DataOutput<string?>.New |
| | 21 | 33 | | .WithData("Hello world!") |
| | 21 | 34 | | .WithMessage("Heimdall API is working."); |
| | | 35 | | |
| | 21 | 36 | | return result.ToActionResult(HttpStatusCodes.Ok); |
| | | 37 | | } |
| | | 38 | | |
| | | 39 | | /// <summary> |
| | | 40 | | /// Detailed health check (UC-30, FR-HC-02…07): reports the status of each verified service |
| | | 41 | | /// plus an aggregate general status. Restricted to System Admins (AF-30a → 403, AF-30b → 401). |
| | | 42 | | /// Returns <c>200 OK</c> when healthy and <c>503 Service Unavailable</c> when any verified |
| | | 43 | | /// service is down (FR-HC-07, AF-30c). |
| | | 44 | | /// </summary> |
| | | 45 | | [HttpGet] |
| | | 46 | | [Route("detailed")] |
| | | 47 | | [RoleRequirement((int)Roles.SystemAdmin)] |
| | | 48 | | public async Task<ActionResult<DataOutput<HealthCheckOutput?>>> Detailed() |
| | | 49 | | { |
| | 2 | 50 | | var result = await queryMediator |
| | 2 | 51 | | .ExecuteQueryAsync<DetailedHealthQuery, HealthCheckOutput>(new DetailedHealthQuery()); |
| | | 52 | | |
| | 2 | 53 | | var statusCode = result.Data?.Status == HealthStatuses.Healthy |
| | 2 | 54 | | ? HttpStatusCodes.Ok |
| | 2 | 55 | | : HttpStatusCodes.ServiceUnavailable; |
| | | 56 | | |
| | 2 | 57 | | return result.ToActionResult(statusCode); |
| | 2 | 58 | | } |
| | | 59 | | } |