UNPKG

agendash

Version:

Dashboard for Agenda job scheduler

174 lines 6.47 kB
import { Router, static as serveStatic, json, urlencoded } from 'express'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import { AgendashController } from '../AgendashController.js'; import { cspHeader } from '../csp.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); /** * Create Express middleware for Agendash * * @example * ```typescript * import express from 'express'; * import { Agenda } from 'agenda'; * import { createExpressMiddleware } from 'agendash'; * * const app = express(); * const agenda = new Agenda({ db: { address: 'mongodb://localhost/agenda' } }); * * app.use('/dash', createExpressMiddleware(agenda)); * ``` */ export function createExpressMiddleware(agenda, options = {}) { const controller = new AgendashController(agenda); const router = Router(); // Body parsing router.use(json()); router.use(urlencoded({ extended: false })); // CSP header router.use((_req, res, next) => { res.header('Content-Security-Policy', cspHeader); next(); }); // Static files (skip in dev mode when using Vite) if (!options.skipStaticFiles) { router.use('/', serveStatic(join(__dirname, '../../public'))); } // API routes router.get('/api', async (req, res) => { try { const query = req.query; const params = { name: query.job, state: query.state, search: query.q, property: query.property, isObjectId: query.isObjectId, skip: query.skip ? parseInt(query.skip, 10) : 0, limit: query.limit ? parseInt(query.limit, 10) : 50 }; const result = await controller.getJobs(params); res.json(result); } catch (error) { res.status(400).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); router.post('/api/jobs/requeue', async (req, res) => { try { const { jobIds } = req.body; const result = await controller.requeueJobs(jobIds); res.json(result); } catch (error) { res.status(404).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); router.post('/api/jobs/retry', async (req, res) => { try { const { jobIds } = req.body; const result = await controller.retryJobs(jobIds); res.json(result); } catch (error) { res.status(404).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); router.post('/api/jobs/delete', async (req, res) => { try { const { jobIds } = req.body; const result = await controller.deleteJobs(jobIds); res.json(result); } catch (error) { res.status(404).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); router.post('/api/jobs/create', async (req, res) => { try { const options = req.body; const result = await controller.createJob(options); res.json(result); } catch (error) { res.status(400).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); router.post('/api/jobs/pause', async (req, res) => { try { const { jobIds } = req.body; const result = await controller.pauseJobs(jobIds); res.json(result); } catch (error) { res.status(400).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); router.post('/api/jobs/resume', async (req, res) => { try { const { jobIds } = req.body; const result = await controller.resumeJobs(jobIds); res.json(result); } catch (error) { res.status(400).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); router.get('/api/stats', async (req, res) => { try { const fullDetails = req.query.fullDetails === 'true'; const result = await controller.getStats(fullDetails); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); // Logs endpoint router.get('/api/logs', async (req, res) => { try { const query = req.query; const params = { jobId: query.jobId, jobName: query.jobName, level: query.level, event: query.event, from: query.from, to: query.to, limit: query.limit ? parseInt(query.limit, 10) : 50, offset: query.offset ? parseInt(query.offset, 10) : 0, sort: query.sort || 'desc' }; const result = await controller.getLogs(params); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); // SSE endpoint for real-time job state notifications router.get('/api/events', (req, res) => { // Check if state notifications are available if (!controller.hasStateNotifications()) { res.status(501).json({ error: 'State notifications not available. Configure a notification channel that supports state subscriptions.' }); return; } // Set up SSE headers res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering // Send initial connection message res.write('event: connected\ndata: {"connected":true}\n\n'); // Subscribe to state notifications const unsubscribe = controller.createStateStream((notification) => { res.write(`data: ${JSON.stringify(notification)}\n\n`); }); // Clean up on client disconnect req.on('close', () => { unsubscribe(); }); }); return router; } //# sourceMappingURL=express.js.map