Frontend gaps fill work. Testing fixes work. Auditing in progress.

This commit is contained in:
StellaOps Bot
2025-12-30 01:22:58 +02:00
parent 1dc4bcbf10
commit 7a5210e2aa
928 changed files with 183942 additions and 3941 deletions

View File

@@ -0,0 +1,495 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using StellaOps.Platform.WebService.Constants;
using StellaOps.Platform.WebService.Contracts;
using StellaOps.Platform.WebService.Services;
namespace StellaOps.Platform.WebService.Endpoints;
public static class PlatformEndpoints
{
public static IEndpointRouteBuilder MapPlatformEndpoints(this IEndpointRouteBuilder app)
{
var platform = app.MapGroup("/api/v1/platform")
.WithTags("Platform");
MapHealthEndpoints(platform);
MapQuotaEndpoints(platform);
MapOnboardingEndpoints(platform);
MapPreferencesEndpoints(platform);
MapSearchEndpoints(app, platform);
MapMetadataEndpoints(platform);
return app;
}
private static void MapHealthEndpoints(IEndpointRouteBuilder platform)
{
var health = platform.MapGroup("/health").WithTags("Platform Health");
health.MapGet("/summary", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformHealthService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var result = await service.GetSummaryAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(new PlatformItemResponse<PlatformHealthSummary>(
requestContext!.TenantId,
requestContext.ActorId,
result.DataAsOf,
result.Cached,
result.CacheTtlSeconds,
result.Value));
}).RequireAuthorization(PlatformPolicies.HealthRead);
health.MapGet("/dependencies", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformHealthService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var result = await service.GetDependenciesAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(new PlatformListResponse<PlatformDependencyStatus>(
requestContext!.TenantId,
requestContext.ActorId,
result.DataAsOf,
result.Cached,
result.CacheTtlSeconds,
result.Value,
result.Value.Count));
}).RequireAuthorization(PlatformPolicies.HealthRead);
health.MapGet("/incidents", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformHealthService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var result = await service.GetIncidentsAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(new PlatformListResponse<PlatformIncident>(
requestContext!.TenantId,
requestContext.ActorId,
result.DataAsOf,
result.Cached,
result.CacheTtlSeconds,
result.Value,
result.Value.Count));
}).RequireAuthorization(PlatformPolicies.HealthRead);
health.MapGet("/metrics", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformHealthService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var result = await service.GetMetricsAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(new PlatformListResponse<PlatformHealthMetric>(
requestContext!.TenantId,
requestContext.ActorId,
result.DataAsOf,
result.Cached,
result.CacheTtlSeconds,
result.Value,
result.Value.Count));
}).RequireAuthorization(PlatformPolicies.HealthAdmin);
}
private static void MapQuotaEndpoints(IEndpointRouteBuilder platform)
{
var quotas = platform.MapGroup("/quotas").WithTags("Platform Quotas");
quotas.MapGet("/summary", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformQuotaService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var result = await service.GetSummaryAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(new PlatformListResponse<PlatformQuotaUsage>(
requestContext!.TenantId,
requestContext.ActorId,
result.DataAsOf,
result.Cached,
result.CacheTtlSeconds,
result.Value,
result.Value.Count));
}).RequireAuthorization(PlatformPolicies.QuotaRead);
quotas.MapGet("/tenants/{tenantId}", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformQuotaService service,
string tenantId,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
if (string.IsNullOrWhiteSpace(tenantId))
{
return Results.BadRequest(new { error = "tenant_missing" });
}
var result = await service.GetTenantAsync(tenantId.Trim().ToLowerInvariant(), cancellationToken).ConfigureAwait(false);
return Results.Ok(new PlatformListResponse<PlatformQuotaUsage>(
requestContext!.TenantId,
requestContext.ActorId,
result.DataAsOf,
result.Cached,
result.CacheTtlSeconds,
result.Value,
result.Value.Count));
}).RequireAuthorization(PlatformPolicies.QuotaRead);
quotas.MapGet("/alerts", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformQuotaService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var result = await service.GetAlertsAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(new PlatformListResponse<PlatformQuotaAlert>(
requestContext!.TenantId,
requestContext.ActorId,
result.DataAsOf,
result.Cached,
result.CacheTtlSeconds,
result.Value,
result.Value.Count));
}).RequireAuthorization(PlatformPolicies.QuotaRead);
quotas.MapPost("/alerts", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformQuotaService service,
PlatformQuotaAlertRequest request,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
try
{
var alert = await service.CreateAlertAsync(requestContext!, request, cancellationToken).ConfigureAwait(false);
return Results.Created($"/api/v1/platform/quotas/alerts/{alert.AlertId}", alert);
}
catch (InvalidOperationException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
}).RequireAuthorization(PlatformPolicies.QuotaAdmin);
}
private static void MapOnboardingEndpoints(IEndpointRouteBuilder platform)
{
var onboarding = platform.MapGroup("/onboarding").WithTags("Platform Onboarding");
onboarding.MapGet("/status", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformOnboardingService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var state = await service.GetStatusAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(state);
}).RequireAuthorization(PlatformPolicies.OnboardingRead);
onboarding.MapPost("/complete/{step}", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformOnboardingService service,
string step,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
try
{
var state = await service.CompleteStepAsync(requestContext!, step, cancellationToken).ConfigureAwait(false);
return Results.Ok(state);
}
catch (InvalidOperationException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
}).RequireAuthorization(PlatformPolicies.OnboardingWrite);
onboarding.MapPost("/skip", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformOnboardingService service,
PlatformOnboardingSkipRequest request,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var state = await service.SkipAsync(requestContext!, request?.Reason, cancellationToken).ConfigureAwait(false);
return Results.Ok(state);
}).RequireAuthorization(PlatformPolicies.OnboardingWrite);
platform.MapGet("/tenants/{tenantId}/setup-status", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformOnboardingService service,
string tenantId,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
if (string.IsNullOrWhiteSpace(tenantId))
{
return Results.BadRequest(new { error = "tenant_missing" });
}
var status = await service.GetTenantSetupStatusAsync(tenantId.Trim().ToLowerInvariant(), cancellationToken).ConfigureAwait(false);
return Results.Ok(status);
}).RequireAuthorization(PlatformPolicies.OnboardingRead);
}
private static void MapPreferencesEndpoints(IEndpointRouteBuilder platform)
{
var preferences = platform.MapGroup("/preferences").WithTags("Platform Preferences");
preferences.MapGet("/dashboard", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformPreferencesService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var prefs = await service.GetPreferencesAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(prefs);
}).RequireAuthorization(PlatformPolicies.PreferencesRead);
preferences.MapPut("/dashboard", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformPreferencesService service,
PlatformDashboardPreferencesRequest request,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
try
{
var prefs = await service.UpsertPreferencesAsync(requestContext!, request, cancellationToken).ConfigureAwait(false);
return Results.Ok(prefs);
}
catch (InvalidOperationException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
}).RequireAuthorization(PlatformPolicies.PreferencesWrite);
var profiles = platform.MapGroup("/dashboard/profiles").WithTags("Platform Preferences");
profiles.MapGet("/", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformPreferencesService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var items = await service.GetProfilesAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(items);
}).RequireAuthorization(PlatformPolicies.PreferencesRead);
profiles.MapGet("/{profileId}", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformPreferencesService service,
string profileId,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var profile = await service.GetProfileAsync(requestContext!, profileId, cancellationToken).ConfigureAwait(false);
return profile is null ? Results.NotFound() : Results.Ok(profile);
}).RequireAuthorization(PlatformPolicies.PreferencesRead);
profiles.MapPost("/", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformPreferencesService service,
PlatformDashboardProfileRequest request,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
try
{
var profile = await service.CreateProfileAsync(requestContext!, request, cancellationToken).ConfigureAwait(false);
return Results.Created($"/api/v1/platform/dashboard/profiles/{profile.ProfileId}", profile);
}
catch (InvalidOperationException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
}).RequireAuthorization(PlatformPolicies.PreferencesWrite);
}
private static void MapSearchEndpoints(IEndpointRouteBuilder app, IEndpointRouteBuilder platform)
{
var searchGroup = platform.MapGroup("/search").WithTags("Platform Search");
async Task<IResult> HandleSearch(
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformSearchService service,
[AsParameters] SearchQuery query,
CancellationToken cancellationToken)
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var sources = query.Sources
?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.ToArray();
var result = await service.SearchAsync(
requestContext!,
query.Query,
sources,
query.Limit,
query.Offset,
cancellationToken).ConfigureAwait(false);
return Results.Ok(new PlatformListResponse<PlatformSearchItem>(
requestContext!.TenantId,
requestContext.ActorId,
result.DataAsOf,
result.Cached,
result.CacheTtlSeconds,
result.Value.Items,
result.Value.Total,
result.Value.Limit,
result.Value.Offset,
result.Value.Query));
}
searchGroup.MapGet("/", HandleSearch).RequireAuthorization(PlatformPolicies.SearchRead);
app.MapGet("/api/v1/search", HandleSearch)
.WithTags("Platform Search")
.RequireAuthorization(PlatformPolicies.SearchRead);
}
private static void MapMetadataEndpoints(IEndpointRouteBuilder platform)
{
platform.MapGet("/metadata", async Task<IResult> (
HttpContext context,
PlatformRequestContextResolver resolver,
PlatformMetadataService service,
CancellationToken cancellationToken) =>
{
if (!TryResolveContext(context, resolver, out var requestContext, out var failure))
{
return failure!;
}
var result = await service.GetMetadataAsync(requestContext!, cancellationToken).ConfigureAwait(false);
return Results.Ok(new PlatformItemResponse<PlatformMetadata>(
requestContext!.TenantId,
requestContext.ActorId,
result.DataAsOf,
result.Cached,
result.CacheTtlSeconds,
result.Value));
}).RequireAuthorization(PlatformPolicies.MetadataRead);
}
private static bool TryResolveContext(
HttpContext context,
PlatformRequestContextResolver resolver,
out PlatformRequestContext? requestContext,
out IResult? failure)
{
if (resolver.TryResolve(context, out requestContext, out var error))
{
failure = null;
return true;
}
failure = Results.BadRequest(new { error = error ?? "tenant_missing" });
return false;
}
private sealed record SearchQuery(
[FromQuery(Name = "q")] string? Query,
string? Sources,
int? Limit,
int? Offset);
}