This commit is contained in:
StellaOps Bot
2025-12-13 02:22:15 +02:00
parent 564df71bfb
commit 999e26a48e
395 changed files with 25045 additions and 2224 deletions

View File

@@ -178,6 +178,7 @@ builder.Services.AddSingleton<StellaOps.Findings.Ledger.Infrastructure.Snapshot.
builder.Services.AddSingleton<StellaOps.Findings.Ledger.Infrastructure.Snapshot.ITimeTravelRepository,
StellaOps.Findings.Ledger.Infrastructure.Postgres.PostgresTimeTravelRepository>();
builder.Services.AddSingleton<SnapshotService>();
builder.Services.AddSingleton<VexConsensusService>();
var app = builder.Build();
@@ -1271,6 +1272,222 @@ app.MapGet("/v1/ledger/current-point", async Task<Results<JsonHttpResult<QueryPo
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
// VexLens Consensus Endpoints (UI-PROOF-VEX-0215-010)
app.MapPost("/v1/vex-consensus/compute", async Task<Results<JsonHttpResult<VexConsensusResponse>, ProblemHttpResult>> (
HttpContext httpContext,
ComputeVexConsensusRequest request,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
if (!TryGetTenant(httpContext, out var tenantProblem, out var tenantId))
{
return tenantProblem!;
}
var result = await consensusService.ComputeConsensusAsync(tenantId, request, cancellationToken).ConfigureAwait(false);
return TypedResults.Json(result);
})
.WithName("ComputeVexConsensus")
.RequireAuthorization(LedgerExportPolicy)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.MapPost("/v1/vex-consensus/compute-batch", async Task<Results<JsonHttpResult<VexConsensusBatchResponse>, ProblemHttpResult>> (
HttpContext httpContext,
ComputeVexConsensusBatchRequest request,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
if (!TryGetTenant(httpContext, out var tenantProblem, out var tenantId))
{
return tenantProblem!;
}
var result = await consensusService.ComputeConsensusBatchAsync(tenantId, request, cancellationToken).ConfigureAwait(false);
return TypedResults.Json(result);
})
.WithName("ComputeVexConsensusBatch")
.RequireAuthorization(LedgerExportPolicy)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.MapGet("/v1/vex-consensus/projections/{projectionId}", async Task<Results<JsonHttpResult<VexProjectionDetailResponse>, NotFound, ProblemHttpResult>> (
string projectionId,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
var result = await consensusService.GetProjectionAsync(projectionId, cancellationToken).ConfigureAwait(false);
if (result is null)
{
return TypedResults.NotFound();
}
return TypedResults.Json(result);
})
.WithName("GetVexProjection")
.RequireAuthorization(LedgerExportPolicy)
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.MapGet("/v1/vex-consensus/projections", async Task<Results<JsonHttpResult<QueryVexProjectionsResponse>, ProblemHttpResult>> (
HttpContext httpContext,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
if (!TryGetTenant(httpContext, out var tenantProblem, out var tenantId))
{
return tenantProblem!;
}
var request = new QueryVexProjectionsRequest(
VulnerabilityId: httpContext.Request.Query["vulnerability_id"].ToString(),
ProductKey: httpContext.Request.Query["product_key"].ToString(),
Status: httpContext.Request.Query["status"].ToString(),
Outcome: httpContext.Request.Query["outcome"].ToString(),
MinimumConfidence: ParseDecimal(httpContext.Request.Query["min_confidence"].ToString()) is decimal d ? (double)d : null,
ComputedAfter: ParseDate(httpContext.Request.Query["computed_after"].ToString()),
ComputedBefore: ParseDate(httpContext.Request.Query["computed_before"].ToString()),
StatusChanged: ParseBool(httpContext.Request.Query["status_changed"].ToString()),
Limit: ParseInt(httpContext.Request.Query["limit"].ToString()),
Offset: ParseInt(httpContext.Request.Query["offset"].ToString()),
SortBy: httpContext.Request.Query["sort_by"].ToString(),
SortDescending: ParseBool(httpContext.Request.Query["sort_desc"].ToString()));
var result = await consensusService.QueryProjectionsAsync(tenantId, request, cancellationToken).ConfigureAwait(false);
return TypedResults.Json(result);
})
.WithName("QueryVexProjections")
.RequireAuthorization(LedgerExportPolicy)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.MapGet("/v1/vex-consensus/projections/latest", async Task<Results<JsonHttpResult<VexProjectionDetailResponse>, NotFound, ProblemHttpResult>> (
HttpContext httpContext,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
if (!TryGetTenant(httpContext, out var tenantProblem, out var tenantId))
{
return tenantProblem!;
}
var vulnId = httpContext.Request.Query["vulnerability_id"].ToString();
var productKey = httpContext.Request.Query["product_key"].ToString();
if (string.IsNullOrEmpty(vulnId) || string.IsNullOrEmpty(productKey))
{
return TypedResults.Problem(statusCode: StatusCodes.Status400BadRequest, title: "missing_params", detail: "vulnerability_id and product_key are required.");
}
var result = await consensusService.GetLatestProjectionAsync(tenantId, vulnId, productKey, cancellationToken).ConfigureAwait(false);
if (result is null)
{
return TypedResults.NotFound();
}
return TypedResults.Json(result);
})
.WithName("GetLatestVexProjection")
.RequireAuthorization(LedgerExportPolicy)
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.MapGet("/v1/vex-consensus/history", async Task<Results<JsonHttpResult<VexProjectionHistoryResponse>, ProblemHttpResult>> (
HttpContext httpContext,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
if (!TryGetTenant(httpContext, out var tenantProblem, out var tenantId))
{
return tenantProblem!;
}
var vulnId = httpContext.Request.Query["vulnerability_id"].ToString();
var productKey = httpContext.Request.Query["product_key"].ToString();
var limit = ParseInt(httpContext.Request.Query["limit"].ToString());
if (string.IsNullOrEmpty(vulnId) || string.IsNullOrEmpty(productKey))
{
return TypedResults.Problem(statusCode: StatusCodes.Status400BadRequest, title: "missing_params", detail: "vulnerability_id and product_key are required.");
}
var result = await consensusService.GetProjectionHistoryAsync(tenantId, vulnId, productKey, limit, cancellationToken).ConfigureAwait(false);
return TypedResults.Json(result);
})
.WithName("GetVexProjectionHistory")
.RequireAuthorization(LedgerExportPolicy)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.MapGet("/v1/vex-consensus/statistics", async Task<Results<JsonHttpResult<VexConsensusStatisticsResponse>, ProblemHttpResult>> (
HttpContext httpContext,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
if (!TryGetTenant(httpContext, out var tenantProblem, out var tenantId))
{
return tenantProblem!;
}
var result = await consensusService.GetStatisticsAsync(tenantId, cancellationToken).ConfigureAwait(false);
return TypedResults.Json(result);
})
.WithName("GetVexConsensusStatistics")
.RequireAuthorization(LedgerExportPolicy)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.MapGet("/v1/vex-consensus/issuers", async Task<Results<JsonHttpResult<VexIssuerListResponse>, ProblemHttpResult>> (
HttpContext httpContext,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
var result = await consensusService.ListIssuersAsync(
httpContext.Request.Query["category"].ToString(),
httpContext.Request.Query["min_trust_tier"].ToString(),
httpContext.Request.Query["status"].ToString(),
httpContext.Request.Query["search"].ToString(),
ParseInt(httpContext.Request.Query["limit"].ToString()),
ParseInt(httpContext.Request.Query["offset"].ToString()),
cancellationToken).ConfigureAwait(false);
return TypedResults.Json(result);
})
.WithName("ListVexIssuers")
.RequireAuthorization(LedgerExportPolicy)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.MapGet("/v1/vex-consensus/issuers/{issuerId}", async Task<Results<JsonHttpResult<VexIssuerDetailResponse>, NotFound, ProblemHttpResult>> (
string issuerId,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
var result = await consensusService.GetIssuerAsync(issuerId, cancellationToken).ConfigureAwait(false);
if (result is null)
{
return TypedResults.NotFound();
}
return TypedResults.Json(result);
})
.WithName("GetVexIssuer")
.RequireAuthorization(LedgerExportPolicy)
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.MapPost("/v1/vex-consensus/issuers", async Task<Results<Created<VexIssuerDetailResponse>, ProblemHttpResult>> (
RegisterVexIssuerRequest request,
VexConsensusService consensusService,
CancellationToken cancellationToken) =>
{
var result = await consensusService.RegisterIssuerAsync(request, cancellationToken).ConfigureAwait(false);
return TypedResults.Created($"/v1/vex-consensus/issuers/{result.IssuerId}", result);
})
.WithName("RegisterVexIssuer")
.RequireAuthorization(LedgerWritePolicy)
.Produces(StatusCodes.Status201Created)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.Run();
static Created<LedgerEventResponse> CreateCreatedResponse(LedgerEventRecord record)