using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using MoShiro.Backend.Application; using MoShiro.Protocol; namespace MoShiro.Backend.Api; /// 运维端点:/healthz、/metrics(admin 令牌)、安装脚本。 public static class OpsEndpoints { private const string Tag = "ops"; public static void Map(IEndpointRouteBuilder app) { app.MapGet("/healthz", HealthAsync) .Api("health_get", "SQLite and the raw data directory are both writable", Tag) .Returns().ProducesProblems(503); app.MapGet("/metrics", MetricsAsync) .Api("metrics_get", "Prometheus metrics (admin token)", Tag) .Produces(StatusCodes.Status200OK, contentType: "text/plain").ProducesProblems(401, 403); app.MapGet("/install.sh", (HttpContext http) => NotShipped(http)) .Api("install_sh", "Installer for Linux nodes", Tag).ProducesProblems(501); app.MapGet("/install.ps1", (HttpContext http) => NotShipped(http)) .Api("install_ps1", "Installer for Windows nodes", Tag).ProducesProblems(501); } private static async Task HealthAsync(HttpContext http, OpsService ops) { StorageHealth health = await ops.HealthAsync(http.RequestAborted); bool ok = health.Database && health.RawDirectory; HealthDto dto = new(ok ? "ok" : "unavailable", health.Database, health.RawDirectory); http.Response.Headers.CacheControl = "no-store"; return TypedResults.Json(dto, ApiJsonContext.Default.HealthDto, statusCode: ok ? StatusCodes.Status200OK : StatusCodes.Status503ServiceUnavailable); } private static async Task MetricsAsync(HttpContext http, OpsService ops) { Principal principal = http.GetPrincipal(); if (!principal.IsAdmin) { return principal.CanRead ? Problems.Create(http, MoShiroErrors.Forbidden, "an admin token is required") : Problems.Create(http, MoShiroErrors.Unauthorized, "an admin token is required"); } return TypedResults.Text(await ops.RenderPrometheusAsync(http.RequestAborted), "text/plain; version=0.0.4"); } /// 安装脚本需要下载节点程序的发布物,那属于打包与部署(M6),目前还没有。 private static IResult NotShipped(HttpContext http) => Problems.Create(http, StatusCodes.Status501NotImplemented, ErrorCodeHttpMap.NotImplemented.Value, "the node installer ships with packaging (M6)"); }