@page "/clusters/{ClusterId}/audit" @attribute [Microsoft.AspNetCore.Authorization.Authorize] @rendermode RenderMode.InteractiveServer @using Microsoft.EntityFrameworkCore @using ZB.MOM.WW.OtOpcUa.Configuration @using ZB.MOM.WW.OtOpcUa.Configuration.Entities @using ZB.MOM.WW.OtOpcUa.Configuration.Queries @inject IDbContextFactory DbFactory

Audit log · @ClusterId

@if (_rows is null) {

Loading…

} else {
Latest @PageSize audit rows scoped to this cluster, newest first. EventId/CorrelationId columns (F3) make cross-restart deduplication possible — Akka actors that retry an apply won't insert duplicate rows. Details JSON is shown verbatim.
@_rows.Count row@(_rows.Count == 1 ? "" : "s")
@if (_rows.Count == 0) {
No audit rows for this cluster yet.
} else {
@foreach (var a in _rows) { }
Timestamp Principal Event Node Correlation Details
@a.Timestamp.ToString("u") @a.Principal @a.EventType @(a.NodeId ?? "—") @(a.CorrelationId?.ToString("N")[..8] ?? "—") @(a.DetailsJson ?? "")
}
} @code { private const int PageSize = 200; [Parameter] public string ClusterId { get; set; } = ""; private List? _rows; protected override async Task OnInitializedAsync() { await using var db = await DbFactory.CreateDbContextAsync(); // Shared query: matches both the SP path (stamps ClusterId) and the structured // AuditWriterActor path (stamps NodeId, ClusterId null) so the latter's rows are visible. _rows = await ClusterAuditQuery.ForClusterAsync(db, ClusterId, PageSize); } }