131 lines
5.3 KiB
C#
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);
|
|
}
|
|
}
|