UNPKG

ah-resque-ui

Version:

A resque administration website for actionhero

222 lines (196 loc) 5.66 kB
import React, { useState, useEffect } from "react"; import useInterval from "../hooks/useInterval"; import { Row, Col } from "react-bootstrap"; import { Link } from "react-router-dom"; import Page from "../layouts/page"; import BumpChart from "../components/bumpChart"; const pollingInterval = 2000; const maxSampleLength = 13; function timeFormatter(time) { return ( ("0" + time.getHours()).slice(-2) + ":" + ("0" + time.getMinutes()).slice(-2) + ":" + ("0" + time.getSeconds()).slice(-2) ); } function OverviewPage({ client }) { const [data, setData] = useState({ queues: {}, workers: {}, stats: {}, counts: { failed: 0, queues: 0, workers: 0 }, samples: [], }); useEffect(() => { loadData(); }, []); useInterval(() => { loadData(); }, [pollingInterval]); async function loadData() { let queues = {}; let workers = {}; let stats = {}; let counts = { failed: 0, queues: 0, workers: 0 }; let response = await client.action( {}, "/api/1/resque/resqueDetails", "GET" ); queues = response.resqueDetails.queues || {}; workers = response.resqueDetails.workers || {}; stats = response.resqueDetails.stats || {}; counts.queues = Object.keys(response.resqueDetails.queues).length || 0; counts.workers = Object.keys(response.resqueDetails.workers).length || 0; response = await client.action( {}, "/api/1/resque/resqueFailedCount", "GET" ); counts.failed = response.failedCount; Object.keys(workers).forEach((workerName) => { const worker = workers[workerName]; if (typeof worker === "string") { workers[workerName] = { status: worker, statusString: worker, }; } else { worker.delta = Math.round( (new Date().getTime() - new Date(worker.run_at).getTime()) / 1000 ); worker.statusString = "working on " + worker.queue + "#" + worker.payload.class + " for " + worker.delta + "s"; } }); const samples = data.samples; const time = timeFormatter(new Date()); for (const name in queues) { let found = false; samples.forEach((sample) => { if (sample.id === name) { found = true; } }); if (!found) { samples.push({ id: name, data: [] }); } for (const i in samples) { if (samples[i].id === name) { samples[i].data.push({ x: time, y: queues[name].length }); if (samples[i].data.length > maxSampleLength) { samples[i].data.shift(); } } } } setData({ queues, stats, counts, workers, samples }); } const chartData = []; data.samples.forEach((series) => { chartData.push(series); }); return ( <Page client={client}> <h1>Resque Overview</h1> <Row> <Col md={12} style={{ height: 450 }}> <BumpChart data={chartData} /> </Col> </Row> <Row> <Col md={2}> <h3>Stats:</h3> <table className="table table-hover"> <tbody> {Object.keys(data.stats).map((k) => { const v = data.stats[k]; if (k.indexOf(":") > 0) { return null; } return ( <tr key={k}> <td>{k}</td> <td>{v}</td> </tr> ); })} </tbody> </table> </Col> <Col md={4}> <h2>Queues ({data.counts.queues})</h2> <table className="table table-hover "> <thead> <tr> <th>Queue Name</th> <th>Jobs</th> </tr> </thead> <tbody> <tr className="table-warning"> <td> <strong> <Link to="/failed">failed</Link> </strong> </td> <td> <strong>{data.counts.failed || 0}</strong> </td> </tr> {Object.keys(data.queues).map((q) => { return ( <tr key={q}> <td> <Link to={`/queue/${q}`}>{q}</Link> </td> <td>{data.queues[q].length}</td> </tr> ); })} </tbody> </table> </Col> <Col md={5}> <h2>Workers ({data.counts.workers})</h2> <table className="table table-striped table-hover "> <thead> <tr> <th>Worker Name</th> <th>Status</th> </tr> </thead> <tbody> {Object.keys(data.workers).map((name) => { const worker = data.workers[name]; return ( <tr key={name}> <td> <span className={worker.delta > 0 ? "text-success" : ""}> {name} </span> </td> <td> <span className={worker.delta > 0 ? "text-info" : ""}> {worker.statusString} </span> </td> </tr> ); })} </tbody> </table> </Col> </Row> </Page> ); } export default OverviewPage;