/** * Queue Admin API * * Admin-Endpunkt für Queue-Monitoring und -Verwaltung. * Nur für Super-Admins zugänglich. */ import { getPayload } from 'payload' import config from '@payload-config' import { NextRequest, NextResponse } from 'next/server' import { getQueuesStatus, isQueueAvailable, getQueue, QUEUE_NAMES, type QueueStatus, } from '@/lib/queue' import { createSafeLogger } from '@/lib/security' const logger = createSafeLogger('API:AdminQueues') interface UserWithAdmin { id: number isSuperAdmin?: boolean } /** * GET /api/admin/queues * * Gibt den Status aller Queues zurück. * Nur für Super-Admins zugänglich. */ export async function GET(req: NextRequest) { try { const payload = await getPayload({ config }) // Authentifizierung prüfen const { user } = await payload.auth({ headers: req.headers }) if (!user) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const typedUser = user as UserWithAdmin // Nur Super-Admins haben Zugriff if (!typedUser.isSuperAdmin) { return NextResponse.json( { error: 'Forbidden - Super admin access required' }, { status: 403 }, ) } // Queue-Verfügbarkeit prüfen const available = await isQueueAvailable() if (!available) { return NextResponse.json( { available: false, error: 'Queue system unavailable (Redis connection failed)', queues: [], }, { status: 503 }, ) } // Queue-Status abrufen const statuses = await getQueuesStatus() return NextResponse.json({ available: true, timestamp: new Date().toISOString(), queues: statuses, summary: { totalWaiting: statuses.reduce((sum: number, q: QueueStatus) => sum + q.waiting, 0), totalActive: statuses.reduce((sum: number, q: QueueStatus) => sum + q.active, 0), totalCompleted: statuses.reduce((sum: number, q: QueueStatus) => sum + q.completed, 0), totalFailed: statuses.reduce((sum: number, q: QueueStatus) => sum + q.failed, 0), totalDelayed: statuses.reduce((sum: number, q: QueueStatus) => sum + q.delayed, 0), }, }) } catch (error) { logger.error('admin-queues error', error) return NextResponse.json( { error: error instanceof Error ? error.message : 'Unknown error' }, { status: 500 }, ) } } /** * POST /api/admin/queues * * Queue-Aktionen ausführen (pause, resume, clean, etc.) * Nur für Super-Admins zugänglich. */ export async function POST(req: NextRequest) { try { const payload = await getPayload({ config }) // Authentifizierung prüfen const { user } = await payload.auth({ headers: req.headers }) if (!user) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const typedUser = user as UserWithAdmin // Nur Super-Admins haben Zugriff if (!typedUser.isSuperAdmin) { return NextResponse.json( { error: 'Forbidden - Super admin access required' }, { status: 403 }, ) } const body = await req.json() const { action, queueName } = body if (!action || !queueName) { return NextResponse.json( { error: 'Missing required fields: action, queueName' }, { status: 400 }, ) } // Queue-Namen validieren const validQueues = Object.values(QUEUE_NAMES) if (!validQueues.includes(queueName)) { return NextResponse.json( { error: `Invalid queue name. Valid: ${validQueues.join(', ')}` }, { status: 400 }, ) } const queue = getQueue(queueName) switch (action) { case 'pause': await queue.pause() logger.info(`Queue ${queueName} paused by user ${typedUser.id}`) return NextResponse.json({ success: true, message: `Queue ${queueName} paused` }) case 'resume': await queue.resume() logger.info(`Queue ${queueName} resumed by user ${typedUser.id}`) return NextResponse.json({ success: true, message: `Queue ${queueName} resumed` }) case 'clean': const { status = 'completed', grace = 0 } = body const cleanedCount = await queue.clean(grace, 1000, status) logger.info(`Queue ${queueName} cleaned (${status}): ${cleanedCount.length} jobs`) return NextResponse.json({ success: true, message: `Cleaned ${cleanedCount.length} ${status} jobs from ${queueName}`, count: cleanedCount.length, }) case 'drain': await queue.drain() logger.info(`Queue ${queueName} drained by user ${typedUser.id}`) return NextResponse.json({ success: true, message: `Queue ${queueName} drained` }) default: return NextResponse.json( { error: `Unknown action: ${action}. Valid: pause, resume, clean, drain` }, { status: 400 }, ) } } catch (error) { logger.error('admin-queues action error', error) return NextResponse.json( { error: error instanceof Error ? error.message : 'Unknown error' }, { status: 500 }, ) } }