| | | 1 | | using ArturRios.Data.Relational.Core.DependencyInjection; |
| | | 2 | | using ArturRios.Heimdall.WebApi.Metrics; |
| | | 3 | | using ArturRios.Heimdall.WebApi.Security; |
| | | 4 | | using ArturRios.Util.WebApi.Configuration; |
| | | 5 | | using ArturRios.Util.WebApi.Security.Extensions; |
| | | 6 | | using Microsoft.AspNetCore.Rewrite; |
| | | 7 | | |
| | | 8 | | namespace ArturRios.Heimdall.WebApi; |
| | | 9 | | |
| | | 10 | | /// <summary> |
| | | 11 | | /// The middlewares that must run ahead of the standard pipeline. A startup filter is how they |
| | | 12 | | /// get there: ASP.NET Core wraps the application's whole pipeline in it, so what it adds runs |
| | | 13 | | /// before anything <c>Build</c> adds. |
| | | 14 | | /// </summary> |
| | 973 | 15 | | internal sealed class EdgePipeline(MetricsOptions metrics, SensitiveDiagnostics diagnostics) : IStartupFilter |
| | | 16 | | { |
| | 966 | 17 | | public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) => app => |
| | 966 | 18 | | { |
| | 966 | 19 | | // First, ahead of everything else — the standard pipeline's ExceptionMiddleware and |
| | 966 | 20 | | // AuthenticationMiddleware included. The scrape endpoint is a terminal branch taken only on |
| | 966 | 21 | | // the metrics port, so Prometheus is never asked for a token, never rate limited, never |
| | 966 | 22 | | // subject to CORS, and the endpoint never reaches the OpenAPI document. Every request on |
| | 966 | 23 | | // the public port falls straight through, /metrics included. |
| | 966 | 24 | | app.UseHeimdallMetrics(metrics); |
| | 966 | 25 | | |
| | 966 | 26 | | // The shared decision, never the environment directly: on a public host without the |
| | 966 | 27 | | // override it is off in Development too (NFR-28). This call is what Local needs; in |
| | 966 | 28 | | // Development the host adds its own page further in, which SuppressedDeveloperPageFilter |
| | 966 | 29 | | // holds to the same decision. |
| | 966 | 30 | | if (diagnostics.DeveloperExceptionPage) |
| | 966 | 31 | | { |
| | 965 | 32 | | app.UseDeveloperExceptionPage(); |
| | 966 | 33 | | } |
| | 966 | 34 | | |
| | 966 | 35 | | // Ahead of HTTPS redirection, which decides on the scheme: behind Traefik every request |
| | 966 | 36 | | // arrives as http, and only X-Forwarded-Proto says the caller used https. The standard |
| | 966 | 37 | | // pipeline runs the same middleware again first thing; by then the trusted hop has been |
| | 966 | 38 | | // consumed and the connection's address is no longer a proxy's, so it changes nothing. |
| | 966 | 39 | | app.UseForwardedHeaders(); |
| | 966 | 40 | | |
| | 966 | 41 | | app.UseHttpsRedirection(); |
| | 966 | 42 | | |
| | 966 | 43 | | // The health check moved under api/ so the web UI can reach it through its own host; the |
| | 966 | 44 | | // container probes and yggdrasil's status catalog still call the root addresses. Rewritten |
| | 966 | 45 | | // here, ahead of routing and authentication, so the alias runs the same endpoint with the |
| | 966 | 46 | | // same authorization, and stays out of the OpenAPI document. |
| | 966 | 47 | | app.UseRewriter(new RewriteOptions() |
| | 966 | 48 | | .AddRewrite("(?i)^healthcheck(/detailed|/ready)?/?$", "api/healthcheck$1", skipRemainingRules: true)); |
| | 966 | 49 | | |
| | 966 | 50 | | next(app); |
| | 1932 | 51 | | }; |
| | | 52 | | } |