mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-05-15 22:48:00 +02:00
157 lines
7.8 KiB
HTML
157 lines
7.8 KiB
HTML
<div class="container paper-page admin-page">
|
|
<div class="paper-crumbs">Admin / <span class="here">Queues</span></div>
|
|
|
|
<div class="q-header">
|
|
<h1 class="paper-page-title">Queues</h1>
|
|
<div class="q-header-actions">
|
|
<div class="q-range-btns">
|
|
<button class="btn btn-sm" ng-class="{active: range == '1h'}" ng-click="setRange('1h')">1h</button>
|
|
<button class="btn btn-sm" ng-class="{active: range == '6h'}" ng-click="setRange('6h')">6h</button>
|
|
<button class="btn btn-sm" ng-class="{active: range == '24h'}" ng-click="setRange('24h')">24h</button>
|
|
<button class="btn btn-sm" ng-class="{active: range == '7d'}" ng-click="setRange('7d')">7d</button>
|
|
</div>
|
|
<button class="btn btn-sm" ng-click="pauseAll()">Pause all</button>
|
|
<button class="btn btn-sm btn-dark" ng-click="drainSelected()">Drain {{selectedQueue}}</button>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="admin-nav">
|
|
<a href="/admin/"><i class="fas fa-tachometer-alt"></i> Overview</a>
|
|
<a href="/admin/repositories"><i class="fas fa-code-branch"></i> Repositories</a>
|
|
<a href="/admin/users"><i class="fas fa-users"></i> Users</a>
|
|
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
|
<a href="/admin/queues" class="active"><i class="fas fa-tasks"></i> Queues</a>
|
|
<a href="/admin/errors"><i class="fas fa-bug"></i> Errors</a>
|
|
</nav>
|
|
|
|
<!-- Queue overview cards -->
|
|
<div class="q-cards">
|
|
<div class="q-card" ng-repeat="q in queueList" ng-class="{selected: selectedQueue == q.key, paused: q.paused}" ng-click="selectQueue(q.key)">
|
|
<div class="q-card-head">
|
|
<span class="q-dot" ng-class="{'q-dot-red': q.paused || q.counts.failed > 0}"></span>
|
|
<span class="q-card-name" ng-bind="q.label"></span>
|
|
</div>
|
|
<div class="q-card-count" ng-bind="(q.counts.waiting || 0) + (q.counts.active || 0) + (q.counts.delayed || 0)"></div>
|
|
<div class="q-card-sub">
|
|
<span>waiting · {{q.counts.active || 0}} active</span>
|
|
<div class="q-card-bar" ng-if="q.counts.active">
|
|
<div class="q-card-bar-fill" ng-style="{width: (q.counts.active / ((q.counts.waiting || 0) + (q.counts.active || 0) + (q.counts.delayed || 0) || 1) * 100) + '%'}"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Detail: throughput chart + stats panel -->
|
|
<div class="q-detail" ng-if="selectedStats">
|
|
<div class="q-throughput">
|
|
<div class="q-section-label">{{selectedQueue}}·throughput <span class="q-section-right">COMPLETED / MIN · {{range | uppercase}}</span></div>
|
|
<canvas id="q-throughput-chart" height="180"></canvas>
|
|
</div>
|
|
<div class="q-stats-panel">
|
|
<div class="q-section-label">{{selectedQueue}}·stats</div>
|
|
<div class="q-stats-grid">
|
|
<div class="q-stat">
|
|
<div class="q-stat-label">WAITING</div>
|
|
<div class="q-stat-value" ng-bind="selectedStats.counts.waiting || 0"></div>
|
|
</div>
|
|
<div class="q-stat">
|
|
<div class="q-stat-label">ACTIVE</div>
|
|
<div class="q-stat-value" ng-bind="selectedStats.counts.active || 0"></div>
|
|
</div>
|
|
<div class="q-stat">
|
|
<div class="q-stat-label">COMPLETED (24H)</div>
|
|
<div class="q-stat-value" ng-bind="selectedStats.completed24h | number"></div>
|
|
</div>
|
|
<div class="q-stat">
|
|
<div class="q-stat-label">FAILED (24H)</div>
|
|
<div class="q-stat-value" ng-bind="selectedStats.failed24h || 0"></div>
|
|
</div>
|
|
<div class="q-stat">
|
|
<div class="q-stat-label">DELAYED</div>
|
|
<div class="q-stat-value" ng-bind="selectedStats.counts.delayed || 0"></div>
|
|
</div>
|
|
<div class="q-stat">
|
|
<div class="q-stat-label">WORKERS</div>
|
|
<div class="q-stat-value" ng-bind="selectedStats.workers || 0"></div>
|
|
</div>
|
|
</div>
|
|
<div class="q-stats-actions">
|
|
<button class="btn btn-sm" ng-click="togglePause()">{{selectedStats.paused ? 'Resume' : 'Pause'}}</button>
|
|
<button class="btn btn-sm" ng-click="retryFailed()" ng-disabled="!selectedStats.counts.failed">Retry failed</button>
|
|
<button class="btn btn-sm" ng-click="emptyQueue()">Empty</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Jobs table -->
|
|
<div class="q-jobs">
|
|
<div class="q-jobs-header">
|
|
<div class="q-section-label">{{query.state | uppercase}} JOBS · {{selectedQueue | uppercase}}</div>
|
|
<div class="q-tabs">
|
|
<button class="q-tab" ng-class="{active: query.state == 'active'}" ng-click="query.state = 'active'">Active</button>
|
|
<button class="q-tab" ng-class="{active: query.state == 'waiting'}" ng-click="query.state = 'waiting'">Waiting</button>
|
|
<button class="q-tab" ng-class="{active: query.state == 'completed'}" ng-click="query.state = 'completed'">Completed</button>
|
|
<button class="q-tab" ng-class="{active: query.state == 'failed'}" ng-click="query.state = 'failed'">Failed</button>
|
|
<button class="q-tab" ng-class="{active: query.state == 'delayed'}" ng-click="query.state = 'delayed'">Delayed</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="q-search-row">
|
|
<input type="search" class="form-control" placeholder="Search by job/repo id…" ng-model="query.search" autocomplete="off" />
|
|
<label class="q-auto-refresh">
|
|
<input type="checkbox" ng-model="query.autoRefresh" />
|
|
Auto-refresh
|
|
</label>
|
|
<button class="btn btn-sm" type="button" ng-click="refreshNow()" title="Refresh now"><i class="fas fa-sync"></i></button>
|
|
</div>
|
|
|
|
<table class="q-table" ng-if="jobs.length > 0">
|
|
<thead>
|
|
<tr>
|
|
<th>JOB ID</th>
|
|
<th>PAYLOAD</th>
|
|
<th>ATTEMPTS</th>
|
|
<th>DURATION</th>
|
|
<th>PROGRESS</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr ng-repeat="job in jobs" ng-class="{'q-row-failed': job.failedReason || job.stacktrace.length}">
|
|
<td class="q-cell-id">
|
|
<a target="_blank" ng-href="/r/{{job.id}}" ng-bind="'job:' + (job.id | limitTo:6)"></a>
|
|
</td>
|
|
<td class="q-cell-payload">
|
|
<span ng-bind="job.name || 'anonymize'"></span>
|
|
<span class="q-payload-detail" ng-if="job.data.repoId"> · {{job.data.repoId}}</span>
|
|
</td>
|
|
<td class="q-cell-num" ng-bind="job.attemptsMade || 1"></td>
|
|
<td class="q-cell-num" ng-bind="jobDuration(job)"></td>
|
|
<td class="q-cell-progress">
|
|
<div class="q-progress-wrap" ng-if="jobProgressPct(job) !== null">
|
|
<div class="q-progress-bar" ng-style="{'--pct': jobProgressPct(job) + '%'}"></div>
|
|
<span class="q-progress-label" ng-bind="jobProgressPct(job) + '%'"></span>
|
|
</div>
|
|
</td>
|
|
<td class="q-cell-actions">
|
|
<button class="btn btn-sm" ng-click="retryJob(job)" title="Retry"><i class="fas fa-sync"></i></button>
|
|
<button class="btn btn-sm" ng-click="removeJob(job)" title="Remove"><i class="fas fa-trash-alt"></i></button>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- Expanded error detail -->
|
|
<div ng-repeat="job in jobs" ng-if="job.failedReason || job.stacktrace.length" class="q-error-detail" style="display:none;">
|
|
<div ng-if="job.failedReason" class="q-error-reason" ng-bind="job.failedReason"></div>
|
|
<pre ng-repeat="stack in job.stacktrace track by $index" class="q-error-stack"><code ng-bind="stack"></code></pre>
|
|
</div>
|
|
|
|
<div class="paper-table-empty" ng-if="jobs.length == 0" style="border:1px solid var(--border-color);border-radius:10px;background:var(--paper-card);">
|
|
<i class="fas fa-check-circle"></i>
|
|
<span ng-if="!query.search">No {{query.state}} jobs in the {{selectedQueue}} queue.</span>
|
|
<span ng-if="query.search">No jobs match the current filters.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|