Files
git.stella-ops.org/src/Platform/StellaOps.Platform.WebService/Endpoints/EnvironmentSettingsEndpoints.cs

131 lines
5.3 KiB
C#

// Copyright (c) StellaOps. All rights reserved.
// Licensed under BUSL-1.1. See LICENSE in the project root.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Options;
using StellaOps.Auth.ServerIntegration.Tenancy;
using StellaOps.Platform.WebService.Contracts;
using StellaOps.Platform.WebService.Options;
using StellaOps.Platform.WebService.Services;
using System.Collections.Generic;
using System.Linq;
namespace StellaOps.Platform.WebService.Endpoints;
/// <summary>
/// Serves <c>GET /platform/envsettings.json</c> — an anonymous endpoint that returns
/// the Angular frontend's <see cref="EnvironmentSettingsResponse"/> (matching <c>AppConfig</c>).
/// The payload is assembled from three layers: env vars (Layer 1), YAML/JSON config (Layer 2),
/// and database overrides (Layer 3) via <see cref="EnvironmentSettingsComposer"/>.
/// </summary>
public static class EnvironmentSettingsEndpoints
{
public static IEndpointRouteBuilder MapEnvironmentSettingsEndpoints(this IEndpointRouteBuilder app)
{
// Primary route (used when accessed via gateway: /platform/envsettings.json)
// and alias route (used when accessing the Platform service directly: /envsettings.json)
app.MapGet("/envsettings.json", Handler)
.WithTags("Environment Settings")
.WithName("GetEnvironmentSettingsAlias")
.WithSummary("Alias for /platform/envsettings.json (direct service access)")
.Produces<EnvironmentSettingsResponse>(StatusCodes.Status200OK)
.AllowAnonymous()
.ExcludeFromDescription();
app.MapGet("/platform/envsettings.json", Handler)
.WithTags("Environment Settings")
.WithName("GetEnvironmentSettings")
.WithSummary("Returns frontend environment configuration (AppConfig)")
.WithDescription(
"Anonymous endpoint that returns the Angular frontend's AppConfig payload. " +
"The response merges three configuration layers: environment variables (lowest), " +
"YAML/JSON config, and database overrides (highest). Includes OIDC authority settings, " +
"API base URLs, and optional telemetry/welcome/doctor configuration.")
.Produces<EnvironmentSettingsResponse>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.AllowAnonymous();
return app;
}
private static async Task<IResult> Handler(
IOptions<PlatformServiceOptions> options,
EnvironmentSettingsComposer composer,
IEnvironmentSettingsStore envSettingsStore,
SetupStateDetector setupDetector,
CancellationToken ct)
{
var platform = options.Value;
var env = await composer.ComposeAsync(ct);
// Detect setup state from DB settings (cached, no extra round-trip)
var dbSettings = await envSettingsStore.GetAllAsync(ct);
var setupState = setupDetector.Detect(platform.Storage, dbSettings);
var authority = new EnvironmentAuthoritySettings
{
Issuer = platform.Authority.Issuer,
ClientId = env.ClientId,
AuthorizeEndpoint = env.AuthorizeEndpoint
?? $"{platform.Authority.Issuer.TrimEnd('/')}/connect/authorize",
TokenEndpoint = env.TokenEndpoint
?? $"{platform.Authority.Issuer.TrimEnd('/')}/connect/token",
LogoutEndpoint = env.LogoutEndpoint,
RedirectUri = env.RedirectUri,
SilentRefreshRedirectUri = env.SilentRefreshRedirectUri,
PostLogoutRedirectUri = env.PostLogoutRedirectUri,
Scope = env.Scope,
Audience = env.Audience
?? platform.Authority.Audiences.FirstOrDefault()
?? "stella-ops-api",
DpopAlgorithms = env.DpopAlgorithms.Count > 0 ? env.DpopAlgorithms : null,
RefreshLeewaySeconds = env.RefreshLeewaySeconds,
};
EnvironmentTelemetrySettings? telemetry = null;
if (!string.IsNullOrWhiteSpace(env.OtlpEndpoint) || env.TelemetrySampleRate > 0)
{
telemetry = new EnvironmentTelemetrySettings
{
OtlpEndpoint = env.OtlpEndpoint,
SampleRate = env.TelemetrySampleRate,
};
}
EnvironmentWelcomeSettings? welcome = null;
if (env.WelcomeTitle is not null || env.WelcomeMessage is not null || env.WelcomeDocsUrl is not null)
{
welcome = new EnvironmentWelcomeSettings
{
Title = env.WelcomeTitle,
Message = env.WelcomeMessage,
DocsUrl = env.WelcomeDocsUrl,
};
}
EnvironmentDoctorSettings? doctor = null;
if (env.DoctorFixEnabled)
{
doctor = new EnvironmentDoctorSettings
{
FixEnabled = env.DoctorFixEnabled,
};
}
var response = new EnvironmentSettingsResponse
{
Authority = authority,
ApiBaseUrls = new Dictionary<string, string>(env.ApiBaseUrls),
Telemetry = telemetry,
Welcome = welcome,
Doctor = doctor,
Setup = setupState,
};
return Results.Json(response, statusCode: StatusCodes.Status200OK);
}
}