Iceshrimp.NET/Iceshrimp.Backend/Pages/QueueJob.cshtml
Laura Hausmann b6714dbba9
[backend/razor] Switch to MapStaticAssets
This allows for fingerprinted filenames, ETags & enhanced caching.
2024-11-18 19:02:42 +01:00

172 lines
No EOL
5.4 KiB
Text

@page "/queue/job/{id::guid:required}"
@using System.Text.Json
@using System.Text.Json.Nodes
@using System.Text.Json.Serialization
@using Iceshrimp.Backend.Core.Database.Tables
@using Iceshrimp.Backend.Core.Extensions
@model QueueJobModel
@{
ViewData["title"] = $"Job details - {Model.InstanceName}";
}
@section head {
<link rel="stylesheet" href="~/css/queue.css"/>
}
@section scripts {
<script src="~/js/queue.js"></script>
}
<h1>Queue Dashboard</h1>
<button role="link" data-target="/queue/@Model.Job.Queue" onclick="navigate(event)">Return to job list</button>
<h2>Job details</h2>
<table>
<tbody>
<tr>
<td class="width20">ID</td>
<td>@Model.Job.Id.ToStringLower()</td>
</tr>
<tr>
<td>Queue</td>
<td>@Model.Job.Queue</td>
</tr>
<tr>
@{
var status = Model.Job is { Status: Job.JobStatus.Delayed, RetryCount: 0 } ? "Scheduled" : Model.Job.Status.ToString();
}
<td>Status</td>
<td class="status-@status.ToLowerInvariant()">@status</td>
</tr>
@if (Model.Job.Status.Equals(Job.JobStatus.Failed))
{
<tr>
<td>Actions</td>
<td>
<a class="fake-link" onclick="retry('@Model.Job.Id.ToStringLower()')">Retry</a>
</td>
</tr>
}
else if (Model.Job.Status.Equals(Job.JobStatus.Delayed))
{
var abandonName = Model.Job.RetryCount == 0 ? "Deschedule" : "Abandon";
<tr>
<td>Actions</td>
<td>
<a class="fake-link" onclick="abandon('@Model.Job.Id.ToStringLower()', event.target)">@abandonName</a>
</td>
</tr>
}
<tr>
<td>Queued at</td>
<td>@Model.Job.QueuedAt.ToLocalTime().ToDisplayStringTz()</td>
</tr>
@if (Model.Job.Status is not Job.JobStatus.Queued and not Job.JobStatus.Delayed)
{
<tr>
<td>Started at</td>
<td>@(Model.Job.StartedAt?.ToLocalTime().ToDisplayStringTz() ?? "<unknown>")</td>
</tr>
}
@if (Model.Job.Status is Job.JobStatus.Completed or Job.JobStatus.Failed)
{
<tr>
<td>Finished at</td>
<td>@(Model.Job.FinishedAt?.ToLocalTime().ToDisplayStringTz() ?? "<unknown>")</td>
</tr>
}
@if (Model.Job.Status == Job.JobStatus.Delayed)
{
<tr>
<td>Delayed until</td>
<td>@(Model.Job.DelayedUntil?.ToLocalTime().ToDisplayStringTz() ?? "<unknown>")</td>
</tr>
}
@if (TimeSpan.FromMilliseconds(Model.Job.Duration).TotalHours <= 72)
{
<tr>
<td>Duration</td>
<td>@Model.Job.Duration.ToDurationDisplayString()</td>
</tr>
}
@if (TimeSpan.FromMilliseconds(Model.Job.QueueDuration).TotalHours <= 72)
{
<tr>
<td>Queue duration</td>
<td>@Model.Job.QueueDuration.ToDurationDisplayString()</td>
</tr>
}
@if (Model.Job.RetryCount > 0)
{
<tr>
<td>Retry count</td>
<td>@Model.Job.RetryCount</td>
</tr>
}
@if (Model.Job is { ExceptionMessage: not null, Exception: null })
{
<tr>
<td>Exception message</td>
<td>@Model.Job.ExceptionMessage</td>
</tr>
}
@if (Model.Job is { ExceptionSource: not null, Exception: null })
{
<tr>
<td>Exception source</td>
<td>@Model.Job.ExceptionSource</td>
</tr>
}
</tbody>
</table>
@if (Model.Job is { StackTrace: not null, Exception: null })
{
<h3>Exception stack trace</h3>
<pre><code id="exceptionStackTrace">@Model.Job.StackTrace</code></pre>
<button onclick="copyElementToClipboard('exceptionStackTrace')">Copy to clipboard</button>
}
@if (Model.Job.Exception != null)
{
<h3>Exception details</h3>
<pre><code id="exceptionDetails">@Model.Job.Exception</code></pre>
<button onclick="copyElementToClipboard('exceptionDetails')">Copy to clipboard</button>
}
<h3>Job data</h3>
@{
var dataOpts = new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
var payloadOpts = new JsonSerializerOptions { WriteIndented = true };
if (Model.Lookup.TryGetValue(Model.Job.Queue, out var payloadKey))
{
var data = JsonNode.Parse(Model.Job.Data)?.AsObject() ??
throw new Exception($"Failed to deserialize {Model.Job.Queue} job data");
var payloadElem = data[payloadKey];
var payload = payloadElem?.GetValue<string>() ??
throw new Exception($"Failed to deserialize {Model.Job.Queue} job data");
var payloadJson = JsonNode.Parse(payload)?.ToJsonString(payloadOpts) ??
throw new Exception($"Failed to serialize {Model.Job.Queue} job data");
data.Remove(payloadKey);
foreach (var item in data.Where(p => p.Value?.GetValueKind() is null or JsonValueKind.Null).ToList())
data.Remove(item.Key);
var dataJson = data.ToJsonString(dataOpts);
<pre><code>@dataJson</code></pre>
<h3>Job payload</h3>
<pre><code id="payload">@payloadJson</code></pre>
}
else
{
var json = JsonNode.Parse(Model.Job.Data)?.ToJsonString(payloadOpts) ??
throw new Exception($"Failed to serialize {Model.Job.Queue} job data");
<pre><code id="payload">@json</code></pre>
}
}
<button onclick="copyElementToClipboard('payload');">Copy to clipboard</button>