From 58f9d759f5c5e2c960c615ce34561de506e6faf5 Mon Sep 17 00:00:00 2001 From: master <> Date: Tue, 31 Mar 2026 23:46:47 +0300 Subject: [PATCH] Add advisory source aggregation report to Advisory & VEX Sources tab Enhances the Advisory & VEX Sources catalog page with per-source advisory download counts, last sync timestamps, and freshness status. Stats bar additions: - Total advisory count across all sources - "With Data" count (sources that have downloaded advisories) - "Stale" count (sources past their freshness SLA) Per-source row additions: - Advisory count badge (e.g., "4,231 advisories") - Freshness pill showing relative time since last sync ("2h ago", "3d ago") - Color-coded freshness: green=healthy, yellow=warning, red=stale, gray=unavailable Expanded detail section additions: - "Sync & Advisory Data" section showing: - Total advisories, last successful sync, last attempt, sync runs, errors - Freshness status badge - Last error message (if any) Data source: GET /api/v1/advisory-sources?includeDisabled=false (already returns totalAdvisories, lastSuccessAt, syncCount, etc.) Loaded non-blocking alongside existing catalog+status calls. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../advisory-source-catalog.component.ts | 185 ++++++++++++++++-- .../source-management.api.ts | 30 +++ 2 files changed, 202 insertions(+), 13 deletions(-) diff --git a/src/Web/StellaOps.Web/src/app/features/integrations/advisory-vex-sources/advisory-source-catalog.component.ts b/src/Web/StellaOps.Web/src/app/features/integrations/advisory-vex-sources/advisory-source-catalog.component.ts index 40ba3e062..aa97cf441 100644 --- a/src/Web/StellaOps.Web/src/app/features/integrations/advisory-vex-sources/advisory-source-catalog.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/integrations/advisory-vex-sources/advisory-source-catalog.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal } from '@angular/core'; import { Router, RouterModule } from '@angular/router'; +import { PlatformContextStore } from '../../../core/context/platform-context.store'; import { forkJoin } from 'rxjs'; import { take } from 'rxjs/operators'; @@ -13,6 +14,7 @@ import { SourceCatalogItem, SourceStatusItem, SourceConnectivityResultDto, + AdvisorySourceMetrics, } from './source-management.api'; import { buildMirrorCommands, getAdvisoryVexNavigationExtras } from './advisory-vex-route-helpers'; @@ -130,6 +132,13 @@ interface CategoryGroup { {{ enabledCount() }} enabled {{ healthyCount() }} healthy {{ failedCount() }} failed + @if (totalAdvisoryCount() > 0) { + {{ totalAdvisoryCount().toLocaleString() }} advisories + {{ sourcesWithData() }} with data + } + @if (staleSourceCount() > 0) { + {{ staleSourceCount() }} stale + } @if (mirrorHealth() && mirrorHealth()!.totalDomains > 0) { {{ mirrorHealth()!.totalDomains }} mirror domains {{ mirrorHealth()!.totalAdvisoryCount }} bundled advisories @@ -144,16 +153,7 @@ interface CategoryGroup { [value]="searchTerm()" (input)="onSearchInput($event)" /> - + @for (group of groupedByCategory(); track group.category) { @@ -217,6 +217,16 @@ interface CategoryGroup { + @if (getSourceMetric(source.id); as m) { + + {{ m.totalAdvisories.toLocaleString() }} advisories + + + {{ formatRelativeTime(m.lastSuccessAt) }} + + } +