Some checks failed
ci/woodpecker/push/ci Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
129 lines
3.3 KiB
TypeScript
129 lines
3.3 KiB
TypeScript
import {
|
|
Controller,
|
|
Get,
|
|
HttpCode,
|
|
HttpStatus,
|
|
Inject,
|
|
NotFoundException,
|
|
Optional,
|
|
Param,
|
|
Post,
|
|
Query,
|
|
UseGuards,
|
|
} from '@nestjs/common';
|
|
import { AdminGuard } from './admin.guard.js';
|
|
import { QueueService } from '../queue/queue.service.js';
|
|
import type { JobDto, JobListDto, JobStatus, QueueListDto } from '../queue/queue-admin.dto.js';
|
|
|
|
@Controller('api/admin/jobs')
|
|
@UseGuards(AdminGuard)
|
|
export class AdminJobsController {
|
|
constructor(
|
|
@Optional()
|
|
@Inject(QueueService)
|
|
private readonly queueService: QueueService | null,
|
|
) {}
|
|
|
|
/**
|
|
* GET /api/admin/jobs
|
|
* List jobs across all queues. Optional ?status=active|completed|failed|waiting|delayed
|
|
*/
|
|
@Get()
|
|
async listJobs(@Query('status') status?: string): Promise<JobListDto> {
|
|
if (!this.queueService) {
|
|
return { jobs: [], total: 0 };
|
|
}
|
|
|
|
const validStatuses: JobStatus[] = ['active', 'completed', 'failed', 'waiting', 'delayed'];
|
|
const normalised = status as JobStatus | undefined;
|
|
|
|
if (normalised && !validStatuses.includes(normalised)) {
|
|
return { jobs: [], total: 0 };
|
|
}
|
|
|
|
const jobs: JobDto[] = await this.queueService.listJobs(normalised);
|
|
return { jobs, total: jobs.length };
|
|
}
|
|
|
|
/**
|
|
* POST /api/admin/jobs/:id/retry
|
|
* Retry a specific failed job. The id is "<queue>__<bullmq-job-id>".
|
|
*/
|
|
@Post(':id/retry')
|
|
@HttpCode(HttpStatus.OK)
|
|
async retryJob(@Param('id') id: string): Promise<{ ok: boolean; message: string }> {
|
|
if (!this.queueService) {
|
|
throw new NotFoundException('Queue service is not available');
|
|
}
|
|
|
|
const result = await this.queueService.retryJob(id);
|
|
if (!result.ok) {
|
|
throw new NotFoundException(result.message);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* GET /api/admin/jobs/queues
|
|
* Return status for all managed queues.
|
|
*/
|
|
@Get('queues')
|
|
async listQueues(): Promise<QueueListDto> {
|
|
if (!this.queueService) {
|
|
return { queues: [] };
|
|
}
|
|
|
|
const health = await this.queueService.getHealthStatus();
|
|
const queues = Object.entries(health.queues).map(([name, stats]) => ({
|
|
name,
|
|
waiting: stats.waiting,
|
|
active: stats.active,
|
|
completed: stats.completed,
|
|
failed: stats.failed,
|
|
delayed: 0,
|
|
paused: stats.paused,
|
|
}));
|
|
|
|
return { queues };
|
|
}
|
|
|
|
/**
|
|
* POST /api/admin/jobs/queues/:name/pause
|
|
* Pause the named queue.
|
|
*/
|
|
@Post('queues/:name/pause')
|
|
@HttpCode(HttpStatus.OK)
|
|
async pauseQueue(@Param('name') name: string): Promise<{ ok: boolean; message: string }> {
|
|
if (!this.queueService) {
|
|
throw new NotFoundException('Queue service is not available');
|
|
}
|
|
|
|
const result = await this.queueService.pauseQueue(name);
|
|
if (!result.ok) {
|
|
throw new NotFoundException(result.message);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* POST /api/admin/jobs/queues/:name/resume
|
|
* Resume the named queue.
|
|
*/
|
|
@Post('queues/:name/resume')
|
|
@HttpCode(HttpStatus.OK)
|
|
async resumeQueue(@Param('name') name: string): Promise<{ ok: boolean; message: string }> {
|
|
if (!this.queueService) {
|
|
throw new NotFoundException('Queue service is not available');
|
|
}
|
|
|
|
const result = await this.queueService.resumeQueue(name);
|
|
if (!result.ok) {
|
|
throw new NotFoundException(result.message);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|