9cff87fe85
Remove project bookkeeping citations from shipped code comments across the solution: hyphenated task IDs (WP-14, StoreAndForward-025), milestone/task/ issue refs (M3, Task 4, Audit Log #23, #21), Bundle X task-bundle labels, and C/D/K/S/T phase labels. Comment text only — no code logic, string/log literals, or XML-doc structure changed. Genuine descriptions are preserved (only the citation is stripped), and technical lookalikes are retained (UTF-8, SHA-256, T00:00:00, M365, UTC-5, pre-C4/pre-C5 schema versions). Flagged by the new CommentChecker TaskReferenceInComment / TrackingReferenceInComment checks plus targeted grep passes; full solution builds clean, append-only guard tests pass.
168 lines
10 KiB
C#
168 lines
10 KiB
C#
using Microsoft.AspNetCore.Components.Authorization;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Options;
|
|
using ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
|
|
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
|
using ZB.MOM.WW.ScadaBridge.CentralUI.ScriptAnalysis;
|
|
using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
|
using ZB.MOM.WW.ScadaBridge.HealthMonitoring;
|
|
using ZB.MOM.WW.ScadaBridge.KpiHistory;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.CentralUI;
|
|
|
|
public static class ServiceCollectionExtensions
|
|
{
|
|
/// <summary>
|
|
/// Registers all Central UI services including Blazor, auth state, dialogs, audit query, and script analysis.
|
|
/// </summary>
|
|
/// <param name="services">The service collection to configure.</param>
|
|
/// <returns>The <paramref name="services"/> instance for chaining.</returns>
|
|
public static IServiceCollection AddCentralUI(this IServiceCollection services)
|
|
{
|
|
services.AddRazorComponents()
|
|
.AddInteractiveServerComponents();
|
|
|
|
services.AddHttpContextAccessor();
|
|
|
|
services.AddScoped<AuthenticationStateProvider, CookieAuthenticationStateProvider>();
|
|
services.AddCascadingAuthenticationState();
|
|
|
|
// Resolves the current user's permitted site set from their SiteId claims
|
|
// so Deployment/Monitoring pages can enforce site scoping.
|
|
services.AddScoped<SiteScopeService>();
|
|
|
|
// Centralised dialog service: pages inject IDialogService and a single
|
|
// <DialogHost /> in MainLayout renders the active dialog. See
|
|
// Components/Shared/IDialogService.cs.
|
|
services.AddScoped<IDialogService, DialogService>();
|
|
|
|
// CentralUI facade over IAuditLogRepository so the
|
|
// results grid can be tested with a stubbed query source.
|
|
//
|
|
// Registered with an explicit factory so the IServiceScopeFactory ctor is
|
|
// always chosen — AuditLogQueryService has a second (test-seam) ctor that
|
|
// takes IAuditLogRepository directly, and both are constructor-resolvable,
|
|
// so default activation would be ambiguous. The scope-factory ctor opens a
|
|
// fresh DbContext per query, which is what keeps the page's auto-load from
|
|
// racing AuditFilterBar's site enumeration on the shared scoped context.
|
|
services.AddScoped<IAuditLogQueryService>(sp => new AuditLogQueryService(
|
|
sp.GetRequiredService<IServiceScopeFactory>(),
|
|
sp.GetRequiredService<ICentralHealthAggregator>()));
|
|
|
|
// Server-side streaming CSV export.
|
|
// Backs the Audit Log page's Export button via GET /api/centralui/audit/export.
|
|
services.AddScoped<IAuditLogExportService, AuditLogExportService>();
|
|
|
|
// CentralUI facade over IKpiHistoryRepository that
|
|
// fetches a raw series and reduces it with KpiSeriesBucketer for the trend chart.
|
|
//
|
|
// Registered with an explicit factory so the IServiceScopeFactory ctor is
|
|
// always chosen — KpiHistoryQueryService has a second (test-seam) ctor that
|
|
// takes IKpiHistoryRepository directly, and both are constructor-resolvable,
|
|
// so default activation would be ambiguous. The scope-factory ctor opens a
|
|
// fresh DbContext per query, mirroring AuditLogQueryService so a chart's
|
|
// auto-load never races other reads on the shared circuit-scoped context.
|
|
services.AddScoped<IKpiHistoryQueryService>(sp => new KpiHistoryQueryService(
|
|
sp.GetRequiredService<IServiceScopeFactory>(),
|
|
sp.GetRequiredService<IOptions<KpiHistoryOptions>>()));
|
|
|
|
// OPC UA Tag Browser: facade over CommunicationService.BrowseNodeAsync
|
|
// that enforces the CentralUI-side Design-role trust boundary and translates
|
|
// transport failures into typed BrowseFailure results for the dialog.
|
|
services.AddScoped<IBrowseService, BrowseService>();
|
|
|
|
// Verify Endpoint: facade over CommunicationService.VerifyEndpointAsync
|
|
// that enforces the same CentralUI-side Design-role trust boundary as the browse
|
|
// service, serializes the in-progress endpoint config, and translates transport
|
|
// failures into typed VerifyEndpointResults. Backs the "Verify endpoint" button
|
|
// on the OPC UA endpoint editor (read-only connect probe, never trusts certs).
|
|
services.AddScoped<IEndpointVerificationService, EndpointVerificationService>();
|
|
|
|
// OPC UA Cert Management: facade over the three
|
|
// CommunicationService cert-trust relay methods. Enforces the CentralUI-side
|
|
// role trust boundary (Trust + Remove require Administrator, List requires
|
|
// Designer) and translates transport failures into typed CertTrustResults.
|
|
// Backs the "Trust certificate" button on the OPC UA endpoint editor and the
|
|
// connection-certificates management page (node-wide site PKI store).
|
|
services.AddScoped<ICertManagementService, CertManagementService>();
|
|
|
|
// Test Bindings: facade over CommunicationService.ReadTagValuesAsync —
|
|
// same Design-role guard + typed-failure translation as the browse
|
|
// service. Backs the Test Bindings dialog on the Configure Instance
|
|
// page (one-shot live read of every bound attribute, grouped by
|
|
// connection).
|
|
services.AddScoped<IBindingTester, BindingTester>();
|
|
|
|
// Operator Alarm Summary: read-only page that aggregates the
|
|
// current alarms across a site's Enabled instances. The service fans out
|
|
// one debug snapshot per instance via IInstanceSnapshotClient — a thin
|
|
// facade over CommunicationService.RequestDebugSnapshotAsync (the same
|
|
// single-shot Ask the Debug View uses) — and flattens the alarm states.
|
|
services.AddScoped<IInstanceSnapshotClient, CommunicationInstanceSnapshotClient>();
|
|
services.AddScoped<IAlarmSummaryService, AlarmSummaryService>();
|
|
|
|
// Secured Writes: dispatches the two-person secured-write commands
|
|
// (submit / approve / reject / list) to the central ManagementActor through the
|
|
// in-process ManagementActorHolder seam — the same Ask path the HTTP /management
|
|
// endpoint uses. The server stays the single enforcer of role gating,
|
|
// separation-of-duties (no self-approval), the MxGateway device relay on approve,
|
|
// and the append-only audit trail; the page only SUBMITS commands.
|
|
services.AddScoped<ISecuredWriteService, SecuredWriteService>();
|
|
|
|
// Connection live-status: projects the per-site health reports'
|
|
// name-keyed connection statuses onto a connection-id → ConnectionHealth map
|
|
// so the design DataConnections page can render a live badge per connection
|
|
// node. Reuses the existing site→central health transport via
|
|
// ICentralHealthAggregator — no new plumbing.
|
|
services.AddScoped<IConnectionHealthQueryService, ConnectionHealthQueryService>();
|
|
|
|
// Move-data-connection: dispatches MoveDataConnectionCommand to the
|
|
// central ManagementActor through the in-process ManagementActorHolder seam —
|
|
// the same Ask path the HTTP /management endpoint uses. The server stays the
|
|
// single enforcer of the Designer gate and every move guard (target exists, no
|
|
// name collision, no instance binding, no native-alarm-source name reference);
|
|
// the move dialog only SUBMITS the command and renders the returned outcome.
|
|
services.AddScoped<IDataConnectionMoveService, DataConnectionMoveService>();
|
|
|
|
// Schema Library: authoring + read accessors for the reusable named
|
|
// JSON-Schema library (SharedSchema + ISharedSchemaRepository).
|
|
//
|
|
// ISchemaLibraryService dispatches the CRUD commands to the central
|
|
// ManagementActor through the in-process ManagementActorHolder seam — the same
|
|
// Ask path the HTTP /management endpoint uses — so the server stays the single
|
|
// enforcer of the Designer gate, the unique-name guard, and JSON-Schema
|
|
// validation; the page only SUBMITS commands.
|
|
services.AddScoped<ISchemaLibraryService, SchemaLibraryService>();
|
|
|
|
// ISchemaLibraryQueryService is the READ counterpart that the schema-driven
|
|
// value-entry forms reuse to resolve {"$ref":"lib:Name"} pointers — a name →
|
|
// schema-JSON map backed by ISharedSchemaRepository over a fresh DI scope per
|
|
// query (mirroring the AuditLog / KPI query services, off the circuit-scoped
|
|
// DbContext). Read-only; no mutation goes through it.
|
|
services.AddScoped<ISchemaLibraryQueryService, SchemaLibraryQueryService>();
|
|
|
|
// Template inheritance preview: a read-only facade that dispatches
|
|
// GetResolvedTemplateMembersCommand to the central ManagementActor through the
|
|
// in-process ManagementActorHolder seam (same Ask path as the HTTP /management
|
|
// endpoint). Powers the template editor's FULL transitively-inherited member set
|
|
// + base-changed staleness banner; never mutates rows, not on the deploy path.
|
|
services.AddScoped<ITemplateInheritanceQueryService, TemplateInheritanceQueryService>();
|
|
|
|
// SMS Notifications: the NotificationListForm Type selector offers only the
|
|
// notification channels that actually have a registered delivery adapter. The
|
|
// catalog projects the registered INotificationDeliveryAdapter set (Email + SMS,
|
|
// registered scoped by AddNotificationOutbox into this same container) to its
|
|
// distinct NotificationType set — no hardcoded {Email, Sms}. Scoped because the
|
|
// adapters it enumerates are scoped (they hold a scoped INotificationRepository).
|
|
services.AddScoped<INotificationChannelCatalog, NotificationChannelCatalog>();
|
|
|
|
// Roslyn-backed C# analysis for the Monaco script editor.
|
|
// Scoped because SharedScriptCatalog wraps a scoped service.
|
|
services.AddMemoryCache(o => o.SizeLimit = 200);
|
|
services.AddScoped<ISharedScriptCatalog, SharedScriptCatalog>();
|
|
services.AddScoped<ScriptAnalysisService>();
|
|
|
|
return services;
|
|
}
|
|
}
|