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 { 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 "__". */ @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 { 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; } }