@page "/admin/api-keys"
@using ZB.MOM.WW.ScadaBridge.Security
@using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Security
@attribute [Authorize(Policy = AuthorizationPolicies.RequireAdmin)]
@inject IInboundApiKeyAdmin ApiKeyAdmin
@inject NavigationManager NavigationManager
@inject IDialogService Dialog
API Key Management
@if (_loading)
{
}
else if (_errorMessage != null)
{
@_errorMessage
}
else
{
@if (_keys.Count == 0)
{
No API keys configured.
}
else if (!FilteredKeys.Any())
{
No API keys match the filter.
}
else
{
| Key ID |
Name |
Methods |
Actions |
@foreach (var key in FilteredKeys)
{
@TruncateKeyId(key.KeyId) |
@key.Name
@if (!key.Enabled)
{
Disabled
}
|
@key.Methods.Count |
|
}
}
}
@code {
// Inbound-API key re-arch (C3): this page reads keys from the IInboundApiKeyAdmin seam
// (string KeyId, method-scopes) rather than the SQL Server ApiKey entity. The seam has no
// retrievable hash, so the old masked Key-Hash column is gone; KeyId identifies each row.
private List _keys = new();
private bool _loading = true;
private string? _errorMessage;
private string _search = string.Empty;
private ToastNotification _toast = default!;
private IEnumerable FilteredKeys =>
string.IsNullOrWhiteSpace(_search)
? _keys
: _keys.Where(k =>
k.Name?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false);
protected override async Task OnInitializedAsync()
{
await LoadDataAsync();
}
private async Task LoadDataAsync()
{
_loading = true;
_errorMessage = null;
try
{
_keys = (await ApiKeyAdmin.ListAsync()).ToList();
}
catch (Exception ex)
{
_errorMessage = $"Failed to load API keys: {ex.Message}";
}
_loading = false;
}
// Show a short, recognizable prefix of the opaque KeyId rather than the full 32-char value.
private static string TruncateKeyId(string keyId)
{
if (string.IsNullOrEmpty(keyId)) return keyId;
return keyId.Length <= 12 ? keyId : keyId[..12] + "…";
}
private async Task ToggleKey(InboundApiKeyInfo key)
{
try
{
var newEnabled = !key.Enabled;
// The seam persists; there is no separate SaveChangesAsync.
await ApiKeyAdmin.SetEnabledAsync(key.KeyId, newEnabled);
_toast.ShowSuccess($"API key '{key.Name}' {(newEnabled ? "enabled" : "disabled")}.");
await LoadDataAsync();
}
catch (Exception ex)
{
_toast.ShowError($"Toggle failed: {ex.Message}");
}
}
private async Task DeleteKey(InboundApiKeyInfo key)
{
var confirmed = await Dialog.ConfirmAsync(
"Delete API Key",
$"Delete API key '{key.Name}'? This cannot be undone.",
danger: true);
if (!confirmed) return;
try
{
await ApiKeyAdmin.DeleteAsync(key.KeyId);
_toast.ShowSuccess($"API key '{key.Name}' deleted.");
await LoadDataAsync();
}
catch (Exception ex)
{
_toast.ShowError($"Delete failed: {ex.Message}");
}
}
}