Skip to content

Commit e7f8a3d

Browse files
fix(dashboard): use bun-queue devtools API instead of direct imports
Fetch queue and job data from bun-queue devtools HTTP API (localhost:4400) via server-side fetch instead of importing getQueueManager() directly. This decouples the dashboard from bun-queue internals and reuses the devtools API surface. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 66c08dc commit e7f8a3d

2 files changed

Lines changed: 41 additions & 89 deletions

File tree

storage/framework/defaults/dashboard/pages/app/jobs.stx

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,29 @@
11
<script server>
2-
import { getQueueManager } from '@stacksjs/queue/bun-queue'
2+
const BQ_API = 'http://localhost:4400'
33

44
let jobs = []
55

66
try {
7-
const manager = getQueueManager()
8-
if (manager) {
9-
for (const connName of manager.getConnections()) {
10-
try {
11-
const conn = manager.connection(connName)
12-
for (const q of conn.queues.values()) {
13-
const waiting = await q.getJobs('waiting', 0, 25)
14-
const active = await q.getJobs('active', 0, 25)
15-
const completed = await q.getJobs('completed', 0, 25)
16-
const failed = await q.getJobs('failed', 0, 25)
17-
18-
const mapJob = function(j, status) {
19-
return {
20-
id: j.id || '',
21-
name: j.name || j.data?.name || 'Unknown',
22-
queue: q.name,
23-
status: status,
24-
attempts: j.attemptsMade || j.attempts || 0,
25-
maxAttempts: j.opts?.attempts || 3,
26-
duration: j.finishedOn && j.processedOn ? (j.finishedOn - j.processedOn) + 'ms' : '-',
27-
createdAt: j.timestamp ? new Date(j.timestamp).toLocaleString() : '-',
28-
}
29-
}
30-
31-
for (const j of waiting) jobs.push(mapJob(j, 'pending'))
32-
for (const j of active) jobs.push(mapJob(j, 'active'))
33-
for (const j of completed) jobs.push(mapJob(j, 'completed'))
34-
for (const j of failed) jobs.push(mapJob(j, 'failed'))
35-
}
36-
} catch {}
37-
}
7+
const res = await fetch(`${BQ_API}/api/jobs`)
8+
if (res.ok) {
9+
const data = await res.json()
10+
jobs = data.slice(0, 50).map(function(j) {
11+
return {
12+
id: j.id || '',
13+
name: j.name || 'Unknown',
14+
queue: j.queue || '-',
15+
status: j.status || 'waiting',
16+
attempts: j.attempts || 0,
17+
maxAttempts: j.maxAttempts || 3,
18+
duration: j.duration ? j.duration + 'ms' : '-',
19+
createdAt: j.createdAt || '-',
20+
}
21+
})
3822
}
3923
} catch {
40-
// Queue system not initialized — show empty state
24+
// bun-queue devtools not running — show empty state
4125
}
4226

43-
// Sort by most recent first
44-
jobs.sort(function(a, b) { return b.createdAt.localeCompare(a.createdAt) })
45-
jobs = jobs.slice(0, 50)
46-
4727
function statusClass(s) {
4828
if (!s) return 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300'
4929
s = String(s).toLowerCase()

storage/framework/defaults/dashboard/pages/app/queue.stx

Lines changed: 24 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,37 @@
11
<script server>
2-
import { getQueueManager } from '@stacksjs/queue/bun-queue'
3-
import { checkQueueHealth } from '@stacksjs/queue'
2+
const BQ_API = 'http://localhost:4400'
43

54
let queues = []
65
let stats = { totalQueues: 0, totalJobs: 0, activeJobs: 0, completedJobs: 0, failedJobs: 0, throughputPerMinute: 0 }
7-
let healthStatus = null
86

97
try {
10-
const manager = getQueueManager()
11-
if (manager) {
12-
for (const connName of manager.getConnections()) {
13-
try {
14-
const conn = manager.connection(connName)
15-
for (const q of conn.queues.values()) {
16-
const counts = await q.getJobCounts()
17-
const isPaused = typeof q.isPaused === 'function' ? await q.isPaused() : false
18-
queues.push({
19-
name: q.name,
20-
status: isPaused ? 'paused' : counts.active > 0 ? 'active' : counts.waiting > 0 ? 'active' : 'idle',
21-
pending: counts.waiting || 0,
22-
active: counts.active || 0,
23-
completed: counts.completed || 0,
24-
failed: counts.failed || 0,
25-
total: (counts.waiting || 0) + (counts.active || 0) + (counts.completed || 0) + (counts.failed || 0),
26-
})
27-
stats.totalQueues++
28-
stats.totalJobs += (counts.waiting || 0) + (counts.active || 0) + (counts.completed || 0) + (counts.failed || 0)
29-
stats.activeJobs += counts.active || 0
30-
stats.completedJobs += counts.completed || 0
31-
stats.failedJobs += counts.failed || 0
32-
}
33-
} catch {}
34-
}
8+
const [queuesRes, statsRes] = await Promise.all([
9+
fetch(`${BQ_API}/api/queues`),
10+
fetch(`${BQ_API}/api/stats`),
11+
])
12+
13+
if (queuesRes.ok) {
14+
const data = await queuesRes.json()
15+
queues = data.map(function(q) {
16+
return {
17+
name: q.name,
18+
status: q.status || 'idle',
19+
pending: q.pendingJobs || 0,
20+
active: q.activeJobs || 0,
21+
completed: q.completedJobs || 0,
22+
failed: q.failedJobs || 0,
23+
total: q.jobCount || 0,
24+
}
25+
})
26+
}
27+
28+
if (statsRes.ok) {
29+
stats = await statsRes.json()
3530
}
3631
} catch {
37-
// Queue system not initialized — show empty state
32+
// bun-queue devtools not running — show empty state
3833
}
3934

40-
try {
41-
healthStatus = await checkQueueHealth()
42-
} catch {}
43-
4435
function statusClass(s) {
4536
if (!s) return 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300'
4637
s = String(s).toLowerCase()
@@ -117,23 +108,4 @@ function statusClass(s) {
117108
</div>
118109
@endif
119110

120-
<!-- Health Status -->
121-
@if (healthStatus)
122-
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-5">
123-
<div class="flex items-center justify-between mb-4">
124-
<span class="font-semibold text-gray-900 dark:text-white">Queue Health</span>
125-
<span class="px-2.5 py-1 text-xs font-semibold rounded-full {{ statusClass(healthStatus.status || 'unknown') }}">{{ healthStatus.status || 'unknown' }}</span>
126-
</div>
127-
@if (healthStatus.queues)
128-
<div class="divide-y divide-gray-100 dark:divide-gray-700">
129-
@foreach (healthStatus.queues as qh)
130-
<div class="flex items-center justify-between py-3">
131-
<span class="text-sm text-gray-900 dark:text-white">{{ qh.name }}</span>
132-
<span class="px-2.5 py-1 text-xs font-semibold rounded-full {{ statusClass(qh.status) }}">{{ qh.status }}</span>
133-
</div>
134-
@endforeach
135-
</div>
136-
@endif
137-
</div>
138-
@endif
139111
</div>

0 commit comments

Comments
 (0)