mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-05 01:10:36 -06:00
@@ -62,6 +62,10 @@ const Configure = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const data = monitors.find((monitor) => monitor._id === monitorId);
|
||||
if (!data) {
|
||||
console.error("Error fetching monitor of id: " + monitorId);
|
||||
navigate("/not-found");
|
||||
}
|
||||
setMonitor({
|
||||
...data,
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ import Button from "../../../Components/Button";
|
||||
import WestRoundedIcon from "@mui/icons-material/WestRounded";
|
||||
import GreenCheck from "../../../assets/icons/checkbox-green.svg?react";
|
||||
import RedCheck from "../../../assets/icons/checkbox-red.svg?react";
|
||||
import SettingsIcon from "../../../assets/icons/settings.svg?react";
|
||||
import SettingsIcon from "../../../assets/icons/settings-bold.svg?react";
|
||||
import {
|
||||
formatDuration,
|
||||
formatDurationRounded,
|
||||
@@ -43,45 +43,53 @@ const DetailsPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMonitor = async () => {
|
||||
const res = await axiosInstance.get(
|
||||
`/monitors/${monitorId}?sortOrder=asc`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
setMonitor(res.data.data);
|
||||
const data = {
|
||||
cols: [
|
||||
{ id: 1, name: "Status" },
|
||||
{ id: 2, name: "Date & Time" },
|
||||
{ id: 3, name: "Message" },
|
||||
],
|
||||
rows: res.data.data.checks.map((check, idx) => {
|
||||
const status = check.status === true ? "up" : "down";
|
||||
try {
|
||||
const res = await axiosInstance.get(
|
||||
`/monitors/${monitorId}?sortOrder=asc`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
setMonitor(res.data.data);
|
||||
const data = {
|
||||
cols: [
|
||||
{ id: 1, name: "Status" },
|
||||
{ id: 2, name: "Date & Time" },
|
||||
{ id: 3, name: "Message" },
|
||||
],
|
||||
rows: res.data.data.checks.map((check, idx) => {
|
||||
const status = check.status === true ? "up" : "down";
|
||||
|
||||
return {
|
||||
id: check._id,
|
||||
data: [
|
||||
{
|
||||
id: idx,
|
||||
data: (
|
||||
<StatusLabel
|
||||
status={status}
|
||||
text={status}
|
||||
customStyles={{ textTransform: "capitalize" }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{ id: idx + 1, data: new Date(check.createdAt).toLocaleString() },
|
||||
{ id: idx + 2, data: check.statusCode },
|
||||
],
|
||||
};
|
||||
}),
|
||||
};
|
||||
return {
|
||||
id: check._id,
|
||||
data: [
|
||||
{
|
||||
id: idx,
|
||||
data: (
|
||||
<StatusLabel
|
||||
status={status}
|
||||
text={status}
|
||||
customStyles={{ textTransform: "capitalize" }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: idx + 1,
|
||||
data: new Date(check.createdAt).toLocaleString(),
|
||||
},
|
||||
{ id: idx + 2, data: check.statusCode },
|
||||
],
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
setData(data);
|
||||
setData(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching monitor of id: " + monitorId);
|
||||
navigate("/not-found");
|
||||
}
|
||||
};
|
||||
fetchMonitor();
|
||||
}, [monitorId, authToken]);
|
||||
@@ -195,7 +203,11 @@ const DetailsPage = () => {
|
||||
level="tertiary"
|
||||
label="Configure"
|
||||
animate="rotate90"
|
||||
img={<SettingsIcon />}
|
||||
img={
|
||||
<SettingsIcon
|
||||
style={{ width: theme.gap.mlplus, height: theme.gap.mlplus }}
|
||||
/>
|
||||
}
|
||||
onClick={() => navigate(`/monitors/configure/${monitorId}`)}
|
||||
sx={{
|
||||
ml: "auto",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
.monitors h2.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
}
|
||||
.monitors .MuiStack-root > button {
|
||||
.monitors .MuiStack-root > button:not(.MuiIconButton-root) {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
height: var(--env-var-height-2);
|
||||
min-width: fit-content;
|
||||
|
||||
@@ -41,16 +41,26 @@ import {
|
||||
const Host = ({ params }) => {
|
||||
return (
|
||||
<Stack direction="row" alignItems="center" className="host">
|
||||
<a
|
||||
href={params.url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
<IconButton
|
||||
aria-label="monitor link"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
window.open(params.url, "_blank", "noreferrer");
|
||||
}}
|
||||
sx={{
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
mr: "3px",
|
||||
}}
|
||||
>
|
||||
<OpenInNewPage />
|
||||
</a>
|
||||
<OpenInNewPage
|
||||
style={{
|
||||
marginTop: "-1px",
|
||||
marginRight: "-1px",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
<Box>
|
||||
{params.title}
|
||||
<Typography component="span" sx={{ color: params.percentageColor }}>
|
||||
|
||||
@@ -22,18 +22,19 @@ const CreatePageSpeed = () => {
|
||||
const { user, authToken } = useSelector((state) => state.auth);
|
||||
|
||||
const frequencies = [
|
||||
{ _id: 1, name: "1 minute" },
|
||||
{ _id: 3, name: "3 minutes" },
|
||||
{ _id: 5, name: "5 minutes" },
|
||||
{ _id: 10, name: "10 minutes" },
|
||||
{ _id: 20, name: "20 minutes" },
|
||||
{ _id: 60, name: "1 hour" },
|
||||
{ _id: 1440, name: "1 day" },
|
||||
{ _id: 2880, name: "2 days" },
|
||||
{ _id: 4320, name: "3 days" },
|
||||
{ _id: 7200, name: "5 days" },
|
||||
{ _id: 10080, name: "1 week" },
|
||||
];
|
||||
|
||||
const [form, setForm] = useState({
|
||||
name: "",
|
||||
url: "",
|
||||
interval: 1,
|
||||
interval: 3,
|
||||
});
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
|
||||
@@ -400,7 +400,7 @@ const PageSpeedDetails = () => {
|
||||
{pieData?.map((pie) => (
|
||||
<PieValueLabel
|
||||
key={pie.id}
|
||||
value={pie.data[0].value}
|
||||
value={Math.round(pie.data[0].value * 10) / 10}
|
||||
startAngle={pie.startAngle}
|
||||
endAngle={pie.endAngle}
|
||||
color={pie.data[0].color}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="16" height="16" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 5L13 1M13 1H9M13 1L7.66667 6.33333M5.66667 2.33333H4.2C3.0799 2.33333 2.51984 2.33333 2.09202 2.55132C1.71569 2.74307 1.40973 3.04903 1.21799 3.42535C1 3.85318 1 4.41323 1 5.53333V9.8C1 10.9201 1 11.4802 1.21799 11.908C1.40973 12.2843 1.71569 12.5903 2.09202 12.782C2.51984 13 3.0799 13 4.2 13H8.46667C9.58677 13 10.1468 13 10.5746 12.782C10.951 12.5903 11.2569 12.2843 11.4487 11.908C11.6667 11.4802 11.6667 10.9201 11.6667 9.8V8.33333" stroke="#667085" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 620 B After Width: | Height: | Size: 620 B |
@@ -4,7 +4,7 @@ const SERVICE_NAME = "JobQueue";
|
||||
|
||||
const getJobs = async (req, res, next) => {
|
||||
try {
|
||||
const jobs = await req.jobQueue.getJobs();
|
||||
const jobs = await req.jobQueue.getJobStats();
|
||||
return res.status(200).json({ jobs });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
|
||||
@@ -19,6 +19,8 @@ const JobQueue = require("./service/jobQueue");
|
||||
const EmailService = require("./service/emailService");
|
||||
const PageSpeedService = require("./service/pageSpeedService");
|
||||
|
||||
let cleaningUp = false;
|
||||
|
||||
// Need to wrap server setup in a function to handle async nature of JobQueue
|
||||
const startApp = async () => {
|
||||
// **************************
|
||||
@@ -117,6 +119,11 @@ const startApp = async () => {
|
||||
const pageSpeedService = new PageSpeedService();
|
||||
|
||||
const cleanup = async () => {
|
||||
if (cleaningUp) {
|
||||
console.log("Already cleaning up");
|
||||
return;
|
||||
}
|
||||
cleaningUp = true;
|
||||
console.log("Shutting down gracefully");
|
||||
await jobQueue.obliterate();
|
||||
console.log("Finished cleanup");
|
||||
|
||||
@@ -124,6 +124,8 @@ class JobQueue {
|
||||
const load = jobs.length / this.workers.length;
|
||||
return { jobs, load };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -143,8 +145,10 @@ class JobQueue {
|
||||
async scaleWorkers(workerStats) {
|
||||
if (this.workers.length === 0) {
|
||||
// There are no workers, need to add one
|
||||
const worker = this.createWorker();
|
||||
this.workers.push(worker);
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const worker = this.createWorker();
|
||||
this.workers.push(worker);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -168,15 +172,17 @@ class JobQueue {
|
||||
const excessCapacity = workerCapacity - workerStats.jobs.length;
|
||||
// Calculate how many workers to remove
|
||||
const workersToRemove = Math.floor(excessCapacity / JOBS_PER_WORKER);
|
||||
for (let i = 0; i < workersToRemove; i++) {
|
||||
const worker = this.workers.pop();
|
||||
try {
|
||||
await worker.close();
|
||||
} catch (error) {
|
||||
// Catch the error instead of throwing it
|
||||
logger.error(errorMessages.JOB_QUEUE_WORKER_CLOSE, {
|
||||
service: SERVICE_NAME,
|
||||
});
|
||||
if (this.workers.length > 5) {
|
||||
for (let i = 0; i < workersToRemove; i++) {
|
||||
const worker = this.workers.pop();
|
||||
try {
|
||||
await worker.close();
|
||||
} catch (error) {
|
||||
// Catch the error instead of throwing it
|
||||
logger.error(errorMessages.JOB_QUEUE_WORKER_CLOSE, {
|
||||
service: SERVICE_NAME,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -196,6 +202,25 @@ class JobQueue {
|
||||
const jobs = await this.queue.getRepeatableJobs();
|
||||
return jobs;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getJobStats() {
|
||||
try {
|
||||
const jobs = await this.queue.getJobs();
|
||||
const ret = await Promise.all(
|
||||
jobs.map(async (job) => {
|
||||
const state = await job.getState();
|
||||
return { url: job.data.url, state };
|
||||
})
|
||||
);
|
||||
return { jobs: ret, workers: this.workers.length };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -210,6 +235,7 @@ class JobQueue {
|
||||
*/
|
||||
async addJob(jobName, payload) {
|
||||
try {
|
||||
console.log("Adding job", payload.url);
|
||||
// Execute job immediately
|
||||
await this.queue.add(jobName, payload);
|
||||
|
||||
@@ -221,6 +247,7 @@ class JobQueue {
|
||||
const workerStats = await this.getWorkerStats();
|
||||
await this.scaleWorkers(workerStats);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -264,9 +291,12 @@ class JobQueue {
|
||||
await this.queue.removeRepeatableByKey(job.key);
|
||||
await this.queue.remove(job.id);
|
||||
}
|
||||
this.workers.forEach(async (worker) => {
|
||||
await worker.close();
|
||||
});
|
||||
await Promise.all(
|
||||
this.workers.map(async (worker) => {
|
||||
await worker.close();
|
||||
})
|
||||
);
|
||||
|
||||
await this.queue.obliterate();
|
||||
logger.info(successMessages.JOB_QUEUE_OBLITERATE, {
|
||||
service: SERVICE_NAME,
|
||||
|
||||
Reference in New Issue
Block a user