0e56b5befb
Add a shared ConfirmDialog component and route Sessions, Workers, and SessionDetails Close/Kill buttons through it. The dialog shows the target session id and a color-matched confirm button (yellow Close, red Kill); Cancel dismisses without invoking the admin service. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
155 lines
5.2 KiB
Plaintext
155 lines
5.2 KiB
Plaintext
@page "/workers"
|
|
@inherits DashboardPageBase
|
|
@inject AuthenticationStateProvider AuthenticationStateProvider
|
|
@inject IDashboardSessionAdminService SessionAdminService
|
|
|
|
<PageTitle>Dashboard Workers</PageTitle>
|
|
|
|
@if (Snapshot is null)
|
|
{
|
|
<div class="empty-state">Loading workers.</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="dashboard-page-header">
|
|
<div>
|
|
<h1>Workers</h1>
|
|
<div class="text-secondary">@Snapshot.Workers.Count worker rows</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if (CanManage && !string.IsNullOrWhiteSpace(ResultMessage))
|
|
{
|
|
<div class="alert @(LastOperationSucceeded ? "alert-success" : "alert-danger")" role="alert">
|
|
@ResultMessage
|
|
</div>
|
|
}
|
|
|
|
@if (CanManage)
|
|
{
|
|
<ConfirmDialog IsOpen="@(PendingSessionId is not null)"
|
|
Title="Kill worker?"
|
|
Message="@($"Forcefully kill the worker for session {PendingSessionId}? This skips graceful shutdown.")"
|
|
ConfirmLabel="Kill"
|
|
ConfirmButtonClass="btn-danger"
|
|
IsBusy="IsBusy"
|
|
OnConfirm="ConfirmKillAsync"
|
|
OnCancel="CancelPending" />
|
|
}
|
|
|
|
<section class="dashboard-section">
|
|
@if (Snapshot.Workers.Count == 0)
|
|
{
|
|
<div class="empty-state">No worker processes are attached.</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="table-responsive">
|
|
<table class="table table-sm align-middle dashboard-table">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Process</th>
|
|
<th scope="col">State</th>
|
|
<th scope="col">Session</th>
|
|
<th scope="col">Heartbeat</th>
|
|
<th scope="col">Fault</th>
|
|
@if (CanManage)
|
|
{
|
|
<th scope="col">Actions</th>
|
|
}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (DashboardWorkerSummary worker in Snapshot.Workers)
|
|
{
|
|
<tr>
|
|
<td>@(worker.ProcessId?.ToString(System.Globalization.CultureInfo.InvariantCulture) ?? "-")</td>
|
|
<td><StatusBadge Text="@worker.State.ToString()" /></td>
|
|
<td><NavLink href="@($"sessions/{Uri.EscapeDataString(worker.SessionId)}")"><code>@worker.SessionId</code></NavLink></td>
|
|
<td>@DashboardDisplay.DateTime(worker.LastHeartbeatAt)</td>
|
|
<td>@DashboardDisplay.Text(worker.LastFault)</td>
|
|
@if (CanManage)
|
|
{
|
|
<td>
|
|
<button type="button" class="btn btn-sm btn-outline-danger"
|
|
disabled="@IsBusy"
|
|
@onclick="() => RequestKill(worker.SessionId)">
|
|
Kill
|
|
</button>
|
|
</td>
|
|
}
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
</section>
|
|
}
|
|
|
|
@code {
|
|
private bool CanManage { get; set; }
|
|
|
|
private bool IsBusy { get; set; }
|
|
|
|
private string? ResultMessage { get; set; }
|
|
|
|
private bool LastOperationSucceeded { get; set; }
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await base.OnInitializedAsync().ConfigureAwait(false);
|
|
|
|
AuthenticationState authenticationState = await AuthenticationStateProvider.GetAuthenticationStateAsync()
|
|
.ConfigureAwait(false);
|
|
CanManage = SessionAdminService.CanManage(authenticationState.User);
|
|
}
|
|
|
|
private string? PendingSessionId { get; set; }
|
|
|
|
private void RequestKill(string sessionId)
|
|
{
|
|
if (IsBusy)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PendingSessionId = sessionId;
|
|
}
|
|
|
|
private void CancelPending()
|
|
{
|
|
if (!IsBusy)
|
|
{
|
|
PendingSessionId = null;
|
|
}
|
|
}
|
|
|
|
private async Task ConfirmKillAsync()
|
|
{
|
|
if (IsBusy || PendingSessionId is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string sessionId = PendingSessionId;
|
|
IsBusy = true;
|
|
try
|
|
{
|
|
AuthenticationState authenticationState = await AuthenticationStateProvider.GetAuthenticationStateAsync()
|
|
.ConfigureAwait(false);
|
|
CanManage = SessionAdminService.CanManage(authenticationState.User);
|
|
DashboardSessionAdminResult result = await SessionAdminService
|
|
.KillWorkerAsync(authenticationState.User, sessionId, CancellationToken.None)
|
|
.ConfigureAwait(false);
|
|
ResultMessage = result.Message;
|
|
LastOperationSucceeded = result.Succeeded;
|
|
}
|
|
finally
|
|
{
|
|
IsBusy = false;
|
|
PendingSessionId = null;
|
|
}
|
|
}
|
|
}
|