search and ai stabilization work, localization stablized.

This commit is contained in:
master
2026-02-24 23:29:36 +02:00
parent 4f947a8b61
commit b07d27772e
766 changed files with 55299 additions and 3221 deletions

View File

@@ -13,6 +13,7 @@ using StellaOps.Concelier.Core.Canonical;
using StellaOps.Concelier.Interest;
using StellaOps.Concelier.Merge.Backport;
using StellaOps.Concelier.WebService.Results;
using static StellaOps.Localization.T;
namespace StellaOps.Concelier.WebService.Extensions;
@@ -42,7 +43,7 @@ internal static class CanonicalAdvisoryEndpointExtensions
if (canonical is null)
{
return HttpResults.NotFound(new { error = "Canonical advisory not found", id });
return HttpResults.NotFound(new { error = _t("concelier.error.advisory_not_found"), id });
}
// Fetch interest score if scoring service is available
@@ -140,17 +141,17 @@ internal static class CanonicalAdvisoryEndpointExtensions
{
if (string.IsNullOrWhiteSpace(source))
{
return HttpResults.BadRequest(new { error = "Source is required" });
return HttpResults.BadRequest(new { error = _t("concelier.validation.source_required") });
}
if (string.IsNullOrWhiteSpace(request.Cve))
{
return HttpResults.BadRequest(new { error = "CVE is required" });
return HttpResults.BadRequest(new { error = _t("concelier.validation.cve_required") });
}
if (string.IsNullOrWhiteSpace(request.AffectsKey))
{
return HttpResults.BadRequest(new { error = "AffectsKey is required" });
return HttpResults.BadRequest(new { error = _t("concelier.validation.affects_key_required") });
}
var rawAdvisory = new RawAdvisory
@@ -204,15 +205,15 @@ internal static class CanonicalAdvisoryEndpointExtensions
{
if (string.IsNullOrWhiteSpace(source))
{
return HttpResults.BadRequest(new { error = "Source is required" });
return HttpResults.BadRequest(new { error = _t("concelier.validation.source_required") });
}
var defaultFetchedAt = timeProvider.GetUtcNow();
var rawAdvisories = requests.Select(request => new RawAdvisory
{
SourceAdvisoryId = request.SourceAdvisoryId ?? $"{source.ToUpperInvariant()}-{request.Cve}",
Cve = request.Cve ?? throw new InvalidOperationException("CVE is required"),
AffectsKey = request.AffectsKey ?? throw new InvalidOperationException("AffectsKey is required"),
Cve = request.Cve ?? throw new InvalidOperationException(_t("concelier.validation.cve_required")),
AffectsKey = request.AffectsKey ?? throw new InvalidOperationException(_t("concelier.validation.affects_key_required")),
VersionRangeJson = request.VersionRangeJson,
Weaknesses = request.Weaknesses ?? [],
PatchLineage = request.PatchLineage,
@@ -266,7 +267,7 @@ internal static class CanonicalAdvisoryEndpointExtensions
{
if (!Enum.TryParse<CanonicalStatus>(request.Status, true, out var status))
{
return HttpResults.BadRequest(new { error = "Invalid status", validValues = Enum.GetNames<CanonicalStatus>() });
return HttpResults.BadRequest(new { error = _t("concelier.validation.invalid_status"), validValues = Enum.GetNames<CanonicalStatus>() });
}
await service.UpdateStatusAsync(id, status, ct).ConfigureAwait(false);
@@ -292,7 +293,7 @@ internal static class CanonicalAdvisoryEndpointExtensions
var canonical = await canonicalService.GetByIdAsync(id, ct).ConfigureAwait(false);
if (canonical is null)
{
return HttpResults.NotFound(new { error = "Canonical advisory not found", id });
return HttpResults.NotFound(new { error = _t("concelier.error.advisory_not_found"), id });
}
if (provenanceService is null)

View File

@@ -9,6 +9,7 @@ using StellaOps.Concelier.Federation.Models;
using StellaOps.Concelier.WebService.Options;
using StellaOps.Concelier.WebService.Results;
using System.Globalization;
using static StellaOps.Localization.T;
namespace StellaOps.Concelier.WebService.Extensions;
@@ -45,12 +46,12 @@ internal static class FederationEndpointExtensions
// Validate parameters
if (maxItems < 1 || maxItems > 100_000)
{
return HttpResults.BadRequest(new { error = "max_items must be between 1 and 100000" });
return HttpResults.BadRequest(new { error = _t("concelier.validation.max_items_range") });
}
if (compressLevel < 1 || compressLevel > 19)
{
return HttpResults.BadRequest(new { error = "compress_level must be between 1 and 19" });
return HttpResults.BadRequest(new { error = _t("concelier.validation.compress_level_range") });
}
var exportOptions = new BundleExportOptions
@@ -170,7 +171,7 @@ internal static class FederationEndpointExtensions
(!contentType.Contains("application/zstd") &&
!contentType.Contains("application/octet-stream")))
{
return HttpResults.BadRequest(new { error = "Content-Type must be application/zstd or application/octet-stream" });
return HttpResults.BadRequest(new { error = _t("concelier.validation.content_type_zstd") });
}
// Parse conflict resolution
@@ -179,7 +180,7 @@ internal static class FederationEndpointExtensions
{
if (!Enum.TryParse<ConflictResolution>(onConflict, ignoreCase: true, out conflictResolution))
{
return HttpResults.BadRequest(new { error = "on_conflict must be one of: PreferRemote, PreferLocal, Fail" });
return HttpResults.BadRequest(new { error = _t("concelier.validation.on_conflict_values") });
}
}
@@ -388,7 +389,7 @@ internal static class FederationEndpointExtensions
var site = await ledgerRepository.GetPolicyAsync(siteId, cancellationToken);
if (site == null)
{
return HttpResults.NotFound(new { error = $"Site '{siteId}' not found" });
return HttpResults.NotFound(new { error = _t("concelier.error.site_not_found", siteId) });
}
// Get recent sync history

View File

@@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc;
using StellaOps.Auth.ServerIntegration.Tenancy;
using StellaOps.Concelier.Interest;
using StellaOps.Concelier.Interest.Models;
using static StellaOps.Localization.T;
namespace StellaOps.Concelier.WebService.Extensions;
@@ -37,7 +38,7 @@ internal static class InterestScoreEndpointExtensions
var score = await scoringService.GetScoreAsync(id, ct).ConfigureAwait(false);
return score is null
? HttpResults.NotFound(new { error = "Interest score not found", canonicalId = id })
? HttpResults.NotFound(new { error = _t("concelier.error.interest_score_not_found"), canonicalId = id })
: HttpResults.Ok(MapToResponse(score));
})
.WithName("GetInterestScore")

View File

@@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc;
using StellaOps.Auth.ServerIntegration.Tenancy;
using StellaOps.Concelier.SbomIntegration;
using StellaOps.Concelier.SbomIntegration.Models;
using static StellaOps.Localization.T;
namespace StellaOps.Concelier.WebService.Extensions;
@@ -73,7 +74,7 @@ internal static class SbomEndpointExtensions
var registration = await registryService.GetByDigestAsync(digest, ct).ConfigureAwait(false);
if (registration is null)
{
return HttpResults.NotFound(new { error = "SBOM not found", digest });
return HttpResults.NotFound(new { error = _t("concelier.error.sbom_not_found"), digest });
}
var matches = await registryService.GetMatchesAsync(digest, ct).ConfigureAwait(false);
@@ -156,7 +157,7 @@ internal static class SbomEndpointExtensions
if (registration is null)
{
return HttpResults.NotFound(new { error = "SBOM not found", digest });
return HttpResults.NotFound(new { error = _t("concelier.error.sbom_not_found"), digest });
}
return HttpResults.Ok(new SbomDetailResponse

View File

@@ -57,6 +57,7 @@ using StellaOps.Configuration;
using StellaOps.Plugin.DependencyInjection;
using StellaOps.Plugin.Hosting;
using StellaOps.Provenance;
using StellaOps.Localization;
using StellaOps.Router.AspNet;
using System;
using System.Collections.Generic;
@@ -841,6 +842,8 @@ builder.Services.RegisterPluginRoutines(builder.Configuration, pluginHostOptions
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
builder.Services.AddStellaOpsTenantServices();
builder.Services.AddStellaOpsLocalization(builder.Configuration);
builder.Services.AddTranslationBundle(System.Reflection.Assembly.GetExecutingAssembly());
builder.TryAddStellaOpsLocalBinding("concelier");
var app = builder.Build();
@@ -875,6 +878,7 @@ if (resolvedAuthority.Enabled && resolvedAuthority.AllowAnonymousFallback)
}
app.UseStellaOpsCors();
app.UseStellaOpsLocalization();
if (authorityConfigured)
{
@@ -4431,6 +4435,7 @@ app.MapGet("/v1/signals/symbols/exists/{advisoryId}", async (
// Refresh Router endpoint cache after all endpoints are registered
app.TryRefreshStellaRouterEndpoints(routerEnabled);
await app.LoadTranslationsAsync();
await app.RunAsync();
}

View File

@@ -46,6 +46,10 @@
ReferenceOutputAssembly="false" />
<ProjectReference Include="../../Router/__Libraries/StellaOps.Router.AspNet/StellaOps.Router.AspNet.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Replay.Core/StellaOps.Replay.Core.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Localization/StellaOps.Localization.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Translations\*.json" />
</ItemGroup>
<!-- Federation files excluded from Core due to circular dependency; compiled here for DI registration -->
<ItemGroup>

View File

@@ -0,0 +1,17 @@
{
"_meta": { "locale": "en-US", "namespace": "concelier", "version": "1.0" },
"concelier.error.advisory_not_found": "Canonical advisory not found.",
"concelier.error.sbom_not_found": "SBOM not found.",
"concelier.error.interest_score_not_found": "Interest score not found.",
"concelier.error.site_not_found": "Site '{0}' not found.",
"concelier.validation.source_required": "source is required.",
"concelier.validation.cve_required": "CVE is required.",
"concelier.validation.affects_key_required": "affectsKey is required.",
"concelier.validation.invalid_status": "Invalid status.",
"concelier.validation.max_items_range": "max_items must be between 1 and 100000.",
"concelier.validation.compress_level_range": "compress_level must be between 1 and 19.",
"concelier.validation.content_type_zstd": "Content-Type must be application/zstd or application/octet-stream.",
"concelier.validation.on_conflict_values": "on_conflict must be one of: PreferRemote, PreferLocal, Fail."
}