diff --git a/Client/src/Pages/Monitors/Configure/index.jsx b/Client/src/Pages/Monitors/Configure/index.jsx
index c34be3a2f..12f97ab1e 100644
--- a/Client/src/Pages/Monitors/Configure/index.jsx
+++ b/Client/src/Pages/Monitors/Configure/index.jsx
@@ -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,
});
diff --git a/Client/src/Pages/Monitors/Details/index.jsx b/Client/src/Pages/Monitors/Details/index.jsx
index 852f602e7..bbfba0b43 100644
--- a/Client/src/Pages/Monitors/Details/index.jsx
+++ b/Client/src/Pages/Monitors/Details/index.jsx
@@ -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: (
-
- ),
- },
- { id: idx + 1, data: new Date(check.createdAt).toLocaleString() },
- { id: idx + 2, data: check.statusCode },
- ],
- };
- }),
- };
+ return {
+ id: check._id,
+ data: [
+ {
+ id: idx,
+ data: (
+
+ ),
+ },
+ {
+ 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={}
+ img={
+
+ }
onClick={() => navigate(`/monitors/configure/${monitorId}`)}
sx={{
ml: "auto",
diff --git a/Client/src/Pages/Monitors/index.css b/Client/src/Pages/Monitors/index.css
index a9317d5da..180bac0b8 100644
--- a/Client/src/Pages/Monitors/index.css
+++ b/Client/src/Pages/Monitors/index.css
@@ -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;
diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx
index 145faaec3..3c1d7bc64 100644
--- a/Client/src/Pages/Monitors/index.jsx
+++ b/Client/src/Pages/Monitors/index.jsx
@@ -41,16 +41,26 @@ import {
const Host = ({ params }) => {
return (
- {
event.stopPropagation();
+ window.open(params.url, "_blank", "noreferrer");
+ }}
+ sx={{
+ "&:focus": {
+ outline: "none",
+ },
+ mr: "3px",
}}
>
-
-
+
+
{params.title}
diff --git a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
index f3b10e174..cb82310c4 100644
--- a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
+++ b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
@@ -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({});
diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/Client/src/Pages/PageSpeed/Details/index.jsx
index 1af0fbba1..ad3b66abc 100644
--- a/Client/src/Pages/PageSpeed/Details/index.jsx
+++ b/Client/src/Pages/PageSpeed/Details/index.jsx
@@ -400,7 +400,7 @@ const PageSpeedDetails = () => {
{pieData?.map((pie) => (
+
diff --git a/Server/controllers/queueController.js b/Server/controllers/queueController.js
index 813fa9808..cb3e132af 100644
--- a/Server/controllers/queueController.js
+++ b/Server/controllers/queueController.js
@@ -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;
diff --git a/Server/index.js b/Server/index.js
index e92a5bb45..1eb5d886b 100644
--- a/Server/index.js
+++ b/Server/index.js
@@ -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");
diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js
index 3fdb91710..12c151abd 100644
--- a/Server/service/jobQueue.js
+++ b/Server/service/jobQueue.js
@@ -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,