Add advisory source catalog UI, mirror wizard, and mirror dashboard
Source catalog component: browsable catalog of 75 advisory sources grouped by 14 categories with search, filter, enable/disable toggles, batch operations, health checks, and category descriptions. Mirror domain builder: 3-step wizard (select sources → configure domain → review & create) with category-level selection, auto-naming, format choice, rate limits, signing options, and optional immediate generation. Mirror dashboard: domain cards with staleness indicators, regenerate and delete actions, consumer config panel, endpoint viewer, and empty-state CTA leading to the wizard. Catalog mirror header: mode badge, domain stats, and quick-access buttons for mirror configuration integrated into the source catalog. Supporting: source management API client (9 endpoints), mirror management API client (12 endpoints), integration hub route wiring, onboarding hub advisory section, security page health display fix, E2E Playwright tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,30 @@ public sealed class MirrorDistributionOptions
|
||||
{
|
||||
public const string SectionName = "Excititor:Mirror";
|
||||
|
||||
/// <summary>
|
||||
/// All source categories recognized by the mirror export system. This list must stay
|
||||
/// in sync with <c>SourceCategory</c> in Concelier.Core. Used by the
|
||||
/// <c>sourceCategory</c> filter shorthand in <see cref="MirrorExportOptions.ResolveFilters"/>.
|
||||
/// Operators can specify one or more comma-separated values from this set.
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<string> SupportedCategories = new[]
|
||||
{
|
||||
"Primary",
|
||||
"Vendor",
|
||||
"Distribution",
|
||||
"Ecosystem",
|
||||
"Cert",
|
||||
"Csaf",
|
||||
"Threat",
|
||||
"Exploit",
|
||||
"Container",
|
||||
"Hardware",
|
||||
"Ics",
|
||||
"PackageManager",
|
||||
"Mirror",
|
||||
"Other",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Global enable flag for mirror distribution surfaces and bundle generation.
|
||||
/// </summary>
|
||||
@@ -94,6 +118,12 @@ public sealed class MirrorExportOptions
|
||||
/// into normalized multi-value lists. Source definitions are required for resolving
|
||||
/// <c>sourceCategory</c> and <c>sourceTag</c> shorthands; pass <c>null</c> when
|
||||
/// category/tag expansion is not needed.
|
||||
/// <para>
|
||||
/// Both <c>sourceCategory</c> and <c>sourceTag</c> accept comma-separated values,
|
||||
/// e.g. <c>"Exploit,Container,Ics,PackageManager"</c>. All matching source IDs are
|
||||
/// merged into the resolved <c>sourceVendor</c> list.
|
||||
/// See <see cref="MirrorDistributionOptions.SupportedCategories"/> for valid category names.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="sourceDefinitions">
|
||||
/// Optional catalog of source definitions used to resolve <c>sourceCategory</c> and
|
||||
@@ -116,9 +146,13 @@ public sealed class MirrorExportOptions
|
||||
|
||||
if (key.Equals("sourceCategory", StringComparison.OrdinalIgnoreCase) && sourceDefinitions is not null)
|
||||
{
|
||||
// Resolve category to source IDs
|
||||
// Resolve one or more comma-separated categories to source IDs.
|
||||
// Supports both single ("Exploit") and multi-value ("Exploit,Container,Ics,PackageManager").
|
||||
var categories = value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
var categorySet = new HashSet<string>(categories, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var matchingIds = sourceDefinitions
|
||||
.Where(s => s.Category.Equals(value, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(s => categorySet.Contains(s.Category))
|
||||
.Select(s => s.Id)
|
||||
.OrderBy(id => id, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
@@ -130,9 +164,12 @@ public sealed class MirrorExportOptions
|
||||
}
|
||||
else if (key.Equals("sourceTag", StringComparison.OrdinalIgnoreCase) && sourceDefinitions is not null)
|
||||
{
|
||||
// Resolve tag to source IDs
|
||||
// Resolve one or more comma-separated tags to source IDs.
|
||||
var tags = value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
var tagSet = new HashSet<string>(tags, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var matchingIds = sourceDefinitions
|
||||
.Where(s => s.Tags.Contains(value, StringComparer.OrdinalIgnoreCase))
|
||||
.Where(s => s.Tags.Any(t => tagSet.Contains(t)))
|
||||
.Select(s => s.Id)
|
||||
.OrderBy(id => id, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
Reference in New Issue
Block a user