UNPKG

@sidequest/dashboard

Version:

@sidequest/dashboard is the web dashboard for Sidequest, a distributed background job queue system.

161 lines (157 loc) 5.65 kB
'use strict'; var core = require('@sidequest/core'); var engine = require('@sidequest/engine'); var express = require('express'); function createJobsRouter(backend) { const jobsRouter = express.Router(); jobsRouter.get("/", async (req, res) => { const { status, start, end, queue, class: jobClass } = req.query; const time = typeof req.query.time === "string" && req.query.time.trim() ? req.query.time : "any"; const pageSize = req.query.pageSize ? parseInt(req.query.pageSize, 10) : 30; const page = req.query.page ? Math.max(parseInt(req.query.page, 10), 1) : 1; const offset = (page - 1) * pageSize; const filters = { limit: pageSize, offset: offset, queue: typeof queue === "string" && queue.trim() ? queue : undefined, jobClass: typeof jobClass === "string" && jobClass.trim() ? "%" + jobClass + "%" : undefined, state: status, }; filters.timeRange = computeTimeRange(time, start, end); const timeRangeStrings = filters.timeRange ? { from: filters.timeRange.from.toISOString(), to: (filters.timeRange.to ?? new Date()).toISOString(), } : undefined; const [jobs, queues, nextPageJobs] = await Promise.all([ backend?.listJobs(filters), backend?.getQueuesFromJobs(), backend?.listJobs({ ...filters, limit: 1, offset: page * pageSize }), ]); const isHtmx = req.get("hx-request"); if (isHtmx) { res.render("partials/jobs-table", { jobs, pagination: { page, pageSize, hasNextPage: nextPageJobs.length > 0, }, layout: false, }); } else { res.render("pages/jobs", { title: "Jobs", jobs, queues, filters: { status: status ?? "", time: time ?? "", queue: queue ?? "", class: jobClass ?? "", start: start ?? timeRangeStrings?.from ?? "", end: end ?? timeRangeStrings?.to ?? "", }, pagination: { page, pageSize, hasNextPage: nextPageJobs.length > 0, }, }); } }); jobsRouter.get("/:id", async (req, res) => { const jobId = parseInt(req.params.id); const job = await backend?.getJob(jobId); const isHtmx = req.get("hx-request"); if (job) { if (isHtmx) { res.render("partials/job-view", { title: `Job #${job.id}`, job, layout: false, }); } else { res.render("pages/job", { title: `Job #${job.id}`, job, }); } } else { res.status(404).send("Job not found!"); } }); jobsRouter.patch("/:id/run", async (req, res) => { const jobId = parseInt(req.params.id); const job = await backend?.getJob(jobId); if (job) { if (job.state === "canceled") { await engine.JobTransitioner.apply(backend, job, new core.RerunTransition()); } else { await engine.JobTransitioner.apply(backend, job, new core.SnoozeTransition(0)); } res.header("HX-Trigger", "jobChanged").status(200).end(); } else { res.status(404).end(); } }); jobsRouter.patch("/:id/cancel", async (req, res) => { const jobId = parseInt(req.params.id); const job = await backend?.getJob(jobId); if (job) { await engine.JobTransitioner.apply(backend, job, new core.CancelTransition()); res.header("HX-Trigger", "jobChanged").status(200).end(); } else { res.status(404).end(); } }); jobsRouter.patch("/:id/rerun", async (req, res) => { const jobId = parseInt(req.params.id); const job = await backend?.getJob(jobId); if (job) { await engine.JobTransitioner.apply(backend, job, new core.RerunTransition()); res.header("HX-Trigger", "jobChanged").status(200).end(); } else { res.status(404).end(); } }); function computeTimeRange(time, start, end) { if (typeof time !== "string") return undefined; const now = Date.now(); const minutesMap = { "5m": 5, "15m": 15, "30m": 30, "1h": 60, "4h": 240, "12h": 720, "24h": 1440, "2d": 2880, "7d": 10080, "30d": 43200, }; if (minutesMap[time]) { return { from: new Date(now - minutesMap[time] * 60_000) }; } if (time === "custom" && typeof start === "string" && typeof end === "string") { const fromDate = new Date(start); const toDate = new Date(end); if (!isNaN(fromDate.getTime()) && !isNaN(toDate.getTime())) { return { from: fromDate, to: toDate }; } } return undefined; } return ["/jobs", jobsRouter]; } exports.createJobsRouter = createJobsRouter; //# sourceMappingURL=jobs.cjs.map