Skip to content

Commit 418f82f

Browse files
chriscrosstalkclaude
authored andcommitted
fix(downloads): allow users to dismiss failed downloads
Failed download jobs persist in BullMQ forever with no way to clear them, leaving stale error notifications in Content Explorer and Easy Setup. Adds a dismiss button (X) on failed download cards that removes the job from the queue via a new DELETE endpoint. - Backend: DELETE /api/downloads/jobs/:jobId endpoint - Frontend: X button on failed download cards with immediate refresh Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c1addb6 commit 418f82f

File tree

5 files changed

+38
-2
lines changed

5 files changed

+38
-2
lines changed

admin/app/controllers/downloads_controller.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,9 @@ export default class DownloadsController {
1515
const payload = await request.validateUsing(downloadJobsByFiletypeSchema)
1616
return this.downloadService.listDownloadJobs(payload.params.filetype)
1717
}
18+
19+
async removeJob({ params }: HttpContext) {
20+
await this.downloadService.removeFailedJob(params.jobId)
21+
return { success: true }
22+
}
1823
}

admin/app/services/download_service.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,15 @@ export class DownloadService {
5050
return b.progress - a.progress
5151
})
5252
}
53+
54+
async removeFailedJob(jobId: string): Promise<void> {
55+
for (const queueName of [RunDownloadJob.queue, DownloadModelJob.queue]) {
56+
const queue = this.queueService.getQueue(queueName)
57+
const job = await queue.getJob(jobId)
58+
if (job) {
59+
await job.remove()
60+
return
61+
}
62+
}
63+
}
5364
}

admin/inertia/components/ActiveDownloads.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@ import useDownloads, { useDownloadsProps } from '~/hooks/useDownloads'
22
import HorizontalBarChart from './HorizontalBarChart'
33
import { extractFileName } from '~/lib/util'
44
import StyledSectionHeader from './StyledSectionHeader'
5-
import { IconAlertTriangle } from '@tabler/icons-react'
5+
import { IconAlertTriangle, IconX } from '@tabler/icons-react'
6+
import api from '~/lib/api'
67

78
interface ActiveDownloadProps {
89
filetype?: useDownloadsProps['filetype']
910
withHeader?: boolean
1011
}
1112

1213
const ActiveDownloads = ({ filetype, withHeader = false }: ActiveDownloadProps) => {
13-
const { data: downloads } = useDownloads({ filetype })
14+
const { data: downloads, invalidate } = useDownloads({ filetype })
15+
16+
const handleDismiss = async (jobId: string) => {
17+
await api.removeDownloadJob(jobId)
18+
invalidate()
19+
}
1420

1521
return (
1622
<>
@@ -37,6 +43,13 @@ const ActiveDownloads = ({ filetype, withHeader = false }: ActiveDownloadProps)
3743
Download failed{download.failedReason ? `: ${download.failedReason}` : ''}
3844
</p>
3945
</div>
46+
<button
47+
onClick={() => handleDismiss(download.jobId)}
48+
className="flex-shrink-0 p-1 rounded hover:bg-red-100 transition-colors"
49+
title="Dismiss failed download"
50+
>
51+
<IconX className="w-4 h-4 text-red-400 hover:text-red-600" />
52+
</button>
4053
</div>
4154
) : (
4255
<HorizontalBarChart

admin/inertia/lib/api.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,12 @@ class API {
532532
})()
533533
}
534534

535+
async removeDownloadJob(jobId: string): Promise<void> {
536+
return catchInternal(async () => {
537+
await this.client.delete(`/downloads/jobs/${jobId}`)
538+
})()
539+
}
540+
535541
async runBenchmark(type: BenchmarkType, sync: boolean = false) {
536542
return catchInternal(async () => {
537543
const response = await this.client.post<RunBenchmarkResponse>(

admin/start/routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ router
9292
.group(() => {
9393
router.get('/jobs', [DownloadsController, 'index'])
9494
router.get('/jobs/:filetype', [DownloadsController, 'filetype'])
95+
router.delete('/jobs/:jobId', [DownloadsController, 'removeJob'])
9596
})
9697
.prefix('/api/downloads')
9798

0 commit comments

Comments
 (0)