143 lines
No EOL
5.5 KiB
C#
143 lines
No EOL
5.5 KiB
C#
using System.Collections.Immutable;
|
|
using Iceshrimp.Backend.Core.Database;
|
|
using Iceshrimp.Backend.Core.Database.Tables;
|
|
using Iceshrimp.Backend.Core.Middleware;
|
|
using Iceshrimp.Backend.Core.Services;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using static Iceshrimp.Backend.Core.Database.DatabaseContext;
|
|
|
|
namespace Iceshrimp.Backend.Pages;
|
|
|
|
public class QueueModel(DatabaseContext db, QueueService queueSvc, MetaService meta, CacheService cache) : PageModel
|
|
{
|
|
private NamedCache _cache = cache.GetNamedCache("admin:queue-dash");
|
|
|
|
public int? DelayedCount;
|
|
public Job.JobStatus? Filter;
|
|
public List<Job> Jobs = [];
|
|
public int? NextPage;
|
|
public int? PrevPage;
|
|
public string? Queue;
|
|
public int? QueuedCount;
|
|
public int? RunningCount;
|
|
public int? TotalCount;
|
|
public List<QueueStatus>? QueueStatuses;
|
|
public List<DelayedDeliverTarget> TopDelayed = [];
|
|
public long? Last;
|
|
public string InstanceName = "Iceshrimp.NET";
|
|
|
|
public static readonly ImmutableArray<string> ScheduledQueues = ["background-task", "backfill"];
|
|
|
|
public async Task<IActionResult> OnGet(
|
|
[FromRoute] string? queue, [FromRoute(Name = "pagination")] int? page, [FromRoute] string? status
|
|
)
|
|
{
|
|
if (!Request.Cookies.TryGetValue("admin_session", out var cookie))
|
|
return Redirect("/login");
|
|
if (!await db.Sessions.AnyAsync(p => p.Token == cookie && p.Active && p.User.IsAdmin))
|
|
return Redirect("/login");
|
|
|
|
Request.HttpContext.HideFooter();
|
|
InstanceName = await meta.GetAsync(MetaEntity.InstanceName) ?? InstanceName;
|
|
|
|
if (queue == null)
|
|
{
|
|
// Should be 15, but the table styles look more pleasing with even numbers
|
|
Jobs = await db.Jobs.OrderByDescending(p => p.LastUpdatedAt).Take(16).ToListAsync();
|
|
if (Request.Query.TryGetValue("last", out var last) && long.TryParse(last, out var parsed))
|
|
Last = parsed;
|
|
|
|
//TODO: write an expression generator for the job count calculation
|
|
QueueStatuses = await db.Jobs
|
|
.GroupBy(job => job.Queue)
|
|
.OrderBy(p => p.Key)
|
|
.Select(p => p.Key)
|
|
.Select(queueName => new QueueStatus
|
|
{
|
|
Name = queueName,
|
|
JobCounts = new Dictionary<Job.JobStatus, int>
|
|
{
|
|
{
|
|
Job.JobStatus.Queued, db.Jobs.Count(job =>
|
|
job.Queue == queueName
|
|
&& job.Status
|
|
== Job.JobStatus.Queued)
|
|
},
|
|
{
|
|
Job.JobStatus.Delayed, db.Jobs.Count(job =>
|
|
job.Queue == queueName && job.Status == Job.JobStatus.Delayed)
|
|
},
|
|
{
|
|
Job.JobStatus.Running, db.Jobs.Count(job =>
|
|
job.Queue == queueName && job.Status == Job.JobStatus.Running)
|
|
},
|
|
{
|
|
Job.JobStatus.Completed, db.Jobs.Count(job =>
|
|
job.Queue == queueName
|
|
&& job.Status == Job.JobStatus.Completed)
|
|
},
|
|
{
|
|
Job.JobStatus.Failed, db.Jobs.Count(job =>
|
|
job.Queue == queueName
|
|
&& job.Status
|
|
== Job.JobStatus.Failed)
|
|
}
|
|
}.AsReadOnly()
|
|
})
|
|
.ToListAsync();
|
|
|
|
TopDelayed = await _cache.FetchAsync("top-delayed", TimeSpan.FromSeconds(60),
|
|
() => db.GetDelayedDeliverTargets().ToListAsync());
|
|
|
|
return Page();
|
|
}
|
|
|
|
if (!queueSvc.QueueNames.Contains(queue))
|
|
throw GracefulException.BadRequest($"Unknown queue: {queue}");
|
|
|
|
Queue = queue;
|
|
|
|
if (page is null or < 1)
|
|
page = 1;
|
|
|
|
var query = db.Jobs.Where(p => p.Queue == queue);
|
|
if (status is { Length: > 0 })
|
|
{
|
|
if (!Enum.TryParse<Job.JobStatus>(status, true, out var jobStatus))
|
|
throw GracefulException.BadRequest($"Unknown status: {status}");
|
|
query = query.Where(p => p.Status == jobStatus);
|
|
Filter = jobStatus;
|
|
}
|
|
|
|
Jobs = await query.OrderByDescending(p => p.Id)
|
|
.Skip((page.Value - 1) * 50)
|
|
.Take(50)
|
|
.ToListAsync();
|
|
|
|
if (Filter == null)
|
|
{
|
|
TotalCount = await db.Jobs.CountAsync(p => p.Queue == queue);
|
|
QueuedCount = await db.Jobs.CountAsync(p => p.Queue == queue && p.Status == Job.JobStatus.Queued);
|
|
RunningCount = await db.Jobs.CountAsync(p => p.Queue == queue && p.Status == Job.JobStatus.Running);
|
|
DelayedCount = await db.Jobs.CountAsync(p => p.Queue == queue && p.Status == Job.JobStatus.Delayed);
|
|
}
|
|
else
|
|
{
|
|
TotalCount = await query.CountAsync();
|
|
}
|
|
|
|
if (Jobs.Count >= 50)
|
|
NextPage = page + 1;
|
|
if (page is > 1)
|
|
PrevPage = page - 1;
|
|
return Page();
|
|
}
|
|
|
|
public class QueueStatus
|
|
{
|
|
public required string Name { get; init; }
|
|
public required IReadOnlyDictionary<Job.JobStatus, int> JobCounts { get; init; }
|
|
}
|
|
} |