@page "/monitoring/audit-log" @using ScadaLink.Security @using ScadaLink.Commons.Entities.Audit @using ScadaLink.Commons.Interfaces.Repositories @attribute [Authorize(Policy = AuthorizationPolicies.RequireAdmin)] @inject ICentralUiRepository CentralUiRepository @inject IJSRuntime JS

Audit Log

@if (_errorMessage != null) {
@_errorMessage
} @if (_entries != null) { @if (_entries.Count == 0) { } @foreach (var entry in _entries) { var entityIdShort = entry.EntityId is { Length: > 0 } ? entry.EntityId[..Math.Min(12, entry.EntityId.Length)] : ""; var hasState = !string.IsNullOrWhiteSpace(entry.AfterStateJson); var isLarge = hasState && entry.AfterStateJson!.Length > 1024; @if (hasState && !isLarge && _expandedEntryId == entry.Id) { } }
Timestamp User Action Entity Type Entity ID Entity Name State
No audit entries found.
@entry.User @entry.Action @entry.EntityType @if (!string.IsNullOrEmpty(entry.EntityId)) { @entityIdShort… } else { — } @entry.EntityName @if (hasState) { if (isLarge) { } else { } } else { — }
@FormatJson(entry.AfterStateJson!)
Page @_page of @TotalPages (@_totalCount total)
} @if (_modalEntry != null) { }
@code { private string? _filterUser; private string? _filterEntityType; private string? _filterAction; private DateTime? _filterFrom; private DateTime? _filterTo; private List? _entries; private int _totalCount; private int _page = 1; private int _pageSize = 50; private bool _searching; private string? _errorMessage; private int? _expandedEntryId; private AuditLogEntry? _modalEntry; private ToastNotification _toast = default!; private int TotalPages => _pageSize > 0 ? Math.Max(1, (_totalCount + _pageSize - 1) / _pageSize) : 1; private bool HasMore => _page * _pageSize < _totalCount; private async Task Search() { _page = 1; await FetchPage(); } private async Task ClearFilters() { _filterUser = null; _filterEntityType = null; _filterAction = null; _filterFrom = null; _filterTo = null; _page = 1; await FetchPage(); } private async Task PrevPage() { _page--; await FetchPage(); } private async Task NextPage() { _page++; await FetchPage(); } private async Task FetchPage() { _searching = true; _errorMessage = null; try { var (entries, totalCount) = await CentralUiRepository.GetAuditLogEntriesAsync( user: string.IsNullOrWhiteSpace(_filterUser) ? null : _filterUser.Trim(), entityType: string.IsNullOrWhiteSpace(_filterEntityType) ? null : _filterEntityType.Trim(), action: string.IsNullOrWhiteSpace(_filterAction) ? null : _filterAction.Trim(), from: _filterFrom.HasValue ? new DateTimeOffset(_filterFrom.Value, TimeSpan.Zero) : null, to: _filterTo.HasValue ? new DateTimeOffset(_filterTo.Value, TimeSpan.Zero) : null, page: _page, pageSize: _pageSize); _entries = entries.ToList(); _totalCount = totalCount; } catch (Exception ex) { _errorMessage = $"Query failed: {ex.Message}"; } _searching = false; } private void ToggleStateView(int entryId) { _expandedEntryId = _expandedEntryId == entryId ? null : entryId; } private void ShowStateModal(AuditLogEntry entry) { _modalEntry = entry; } private void CloseStateModal() { _modalEntry = null; } private async Task CopyAsync(string text) { try { await JS.InvokeVoidAsync("navigator.clipboard.writeText", text); _toast.ShowSuccess("Copied to clipboard."); } catch { _toast.ShowError("Copy failed."); } } private static string GetActionBadge(string action) => action switch { "Create" => "bg-success", "Update" => "bg-primary", "Delete" => "bg-danger", "Deploy" => "bg-info text-dark", _ => "bg-secondary" }; private static string FormatJson(string json) { try { var doc = System.Text.Json.JsonDocument.Parse(json); return System.Text.Json.JsonSerializer.Serialize(doc, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }); } catch { return json; } } }