Check the monitor URL resolution before adding it
This commit is contained in:
1 parent
4a1b290416
commit
ebf6a0ceb7
5 files changed
+161
-50
No files matched your search
@@ -31,6 +31,30 @@ export const createUptimeMonitor = createAsyncThunk(
|
||||
}
|
||||
);
|
||||
|
||||
export const checkEndpointResolution = createAsyncThunk(
|
||||
"monitors/checkEnpoint",
|
||||
async(data, thunkApi) => {
|
||||
try{
|
||||
const { authToken, monitorURL } = data;
|
||||
|
||||
const res = await networkService.checkEndpointResolution({
|
||||
authToken: authToken,
|
||||
monitorURL: monitorURL,
|
||||
})
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
return thunkApi.rejectWithValue(error.response.data);
|
||||
}
|
||||
const payload = {
|
||||
status: false,
|
||||
msg: error.message ? error.message : "Unknown error",
|
||||
};
|
||||
return thunkApi.rejectWithValue(payload);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const getUptimeMonitorById = createAsyncThunk(
|
||||
"monitors/getMonitorById",
|
||||
async (data, thunkApi) => {
|
||||
@@ -271,6 +295,24 @@ const uptimeMonitorsSlice = createSlice({
|
||||
: "Failed to create uptime monitor";
|
||||
})
|
||||
// *****************************************************
|
||||
// Resolve Endpoint
|
||||
// *****************************************************
|
||||
.addCase(checkEndpointResolution.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
})
|
||||
.addCase(checkEndpointResolution.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = action.payload.success;
|
||||
state.msg = action.payload.msg;
|
||||
})
|
||||
.addCase(checkEndpointResolution.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = false;
|
||||
state.msg = action.payload
|
||||
? action.payload.msg
|
||||
: "Failed to check endpoint resolution";
|
||||
})
|
||||
// *****************************************************
|
||||
// Get Monitor By Id
|
||||
// *****************************************************
|
||||
.addCase(getUptimeMonitorById.pending, (state) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Box, Button, ButtonGroup, Stack, Typography } from "@mui/material";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { monitorValidation } from "../../../Validation/validation";
|
||||
import { createUptimeMonitor } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import { checkEndpointResolution } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
@@ -124,58 +125,55 @@ const CreateMonitor = () => {
|
||||
|
||||
const handleCreateMonitor = async (event) => {
|
||||
event.preventDefault();
|
||||
//obj to submit
|
||||
let form = {
|
||||
url:
|
||||
//preprending protocol for url
|
||||
monitor.type === "http"
|
||||
? `http${https ? "s" : ""}://` + monitor.url
|
||||
: monitor.url,
|
||||
name: monitor.name === "" ? monitor.url : monitor.name,
|
||||
type: monitor.type,
|
||||
interval: monitor.interval * MS_PER_MINUTE,
|
||||
};
|
||||
|
||||
const formattedUrl = monitor.type === "http"
|
||||
? `http${https ? "s" : ""}://` + monitor.url
|
||||
: monitor.url;
|
||||
const { error } = monitorValidation.validate(form, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
try {
|
||||
// Check if the URL resolves by sending a request
|
||||
const response = await fetch(formattedUrl, { method: 'GET', mode: 'no-cors' });
|
||||
if (!response) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
//obj to submit
|
||||
let form = {
|
||||
url:formattedUrl,
|
||||
name: monitor.name === "" ? monitor.url : monitor.name,
|
||||
type: monitor.type,
|
||||
interval: monitor.interval * MS_PER_MINUTE,
|
||||
};
|
||||
|
||||
const { error } = monitorValidation.validate(form, {
|
||||
abortEarly: false,
|
||||
if (error) {
|
||||
const newErrors = {};
|
||||
error.details.forEach((err) => {
|
||||
newErrors[err.path[0]] = err.message;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
const newErrors = {};
|
||||
error.details.forEach((err) => {
|
||||
newErrors[err.path[0]] = err.message;
|
||||
});
|
||||
setErrors(newErrors);
|
||||
createToast({ body: "Error validation data." });
|
||||
} else {
|
||||
form = {
|
||||
...form,
|
||||
description: form.name,
|
||||
teamId: user.teamId,
|
||||
userId: user._id,
|
||||
notifications: monitor.notifications,
|
||||
};
|
||||
const action = await dispatch(
|
||||
createUptimeMonitor({ authToken, monitor: form })
|
||||
);
|
||||
if (action.meta.requestStatus === "fulfilled") {
|
||||
createToast({ body: "Monitor created successfully!" });
|
||||
navigate("/monitors");
|
||||
} else {
|
||||
createToast({ body: "Failed to create monitor." });
|
||||
}
|
||||
setErrors(newErrors);
|
||||
createToast({ body: "Error validation data." });
|
||||
} else {
|
||||
const checkEndpointAction = await dispatch(
|
||||
checkEndpointResolution({ authToken, monitorURL: form.url })
|
||||
)
|
||||
if (checkEndpointAction.meta.requestStatus === "rejected") {
|
||||
createToast({ body: "The endpoint you entered doesn't resolve. Check the URL again." });
|
||||
setErrors({ url: "The entered URL is not reachable." });
|
||||
return;
|
||||
}
|
||||
|
||||
form = {
|
||||
...form,
|
||||
description: form.name,
|
||||
teamId: user.teamId,
|
||||
userId: user._id,
|
||||
notifications: monitor.notifications,
|
||||
};
|
||||
const action = await dispatch(
|
||||
createUptimeMonitor({ authToken, monitor: form })
|
||||
);
|
||||
if (action.meta.requestStatus === "fulfilled") {
|
||||
createToast({ body: "Monitor created successfully!" });
|
||||
navigate("/monitors");
|
||||
} else {
|
||||
createToast({ body: "Failed to create monitor." });
|
||||
}
|
||||
} catch (error) {
|
||||
createToast({ body: "The endpoint you entered doesn't resolve. Check the URL again." });
|
||||
setErrors({ url: "The entered URL is not reachable." });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -387,7 +385,7 @@ const CreateMonitor = () => {
|
||||
onClick={handleCreateMonitor}
|
||||
disabled={Object.keys(errors).length !== 0 && true}
|
||||
>
|
||||
{monitor.type === "http" ? "Create Monitor" : "Check Endpoint"}
|
||||
Create Monitor
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -395,4 +393,4 @@ const CreateMonitor = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateMonitor;
|
||||
export default CreateMonitor;
|
||||
@@ -89,6 +89,32 @@ class NetworkService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* ************************************
|
||||
* Check the endpoint resolution
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {Object} config - The configuration object.
|
||||
* @param {string} config.authToken - The authorization token to be used in the request header.
|
||||
* @param {Object} config.monitorURL - The monitor url to be sent in the request body.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*/
|
||||
async checkEndpointResolution(config) {
|
||||
const { authToken, monitorURL } = config;
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (monitorURL) params.append("monitorURL", monitorURL);
|
||||
|
||||
return this.axiosInstance.get(`/monitors/check-endpoint/url?${params.toString()}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* ************************************
|
||||
|
||||
@@ -20,6 +20,7 @@ const jwt = require("jsonwebtoken");
|
||||
const { getTokenFromHeaders } = require("../utils/utils");
|
||||
const logger = require("../utils/logger");
|
||||
const { handleError, handleValidationError } = require("./controllerUtils");
|
||||
const dns = require('dns');
|
||||
|
||||
/**
|
||||
* Returns all monitors
|
||||
@@ -262,6 +263,43 @@ const createMonitor = async (req, res, next) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the endpoint can be resolved and adds it to the job queue.
|
||||
* @async
|
||||
* @param {Object} req - The Express request object.
|
||||
* @property {Object} req.query - The query parameters of the request.
|
||||
* @param {Object} res - The Express response object.
|
||||
* @param {function} next - The next middleware function.
|
||||
* @returns {Object} The response object with a success status, a message, and the resolution result.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
|
||||
const checkEndpointResolution = async (req, res, next) => {
|
||||
try {
|
||||
let { monitorURL } = req.query;
|
||||
|
||||
// Remove the protocol (http:// or https://) if present
|
||||
monitorURL = monitorURL.replace(/^https?:\/\//, '');
|
||||
// Remove the trailing slash if it exists
|
||||
monitorURL = monitorURL.endsWith('/') ? monitorURL.slice(0, -1) : monitorURL;
|
||||
|
||||
dns.setServers(['8.8.8.8', '1.1.1.1']);// Google, Cloudfare
|
||||
dns.resolve(monitorURL, (error) => {
|
||||
console.log("Inside .resolve");
|
||||
if (error) {
|
||||
next(handleError(error, SERVICE_NAME, "checkEndpointResolution"));
|
||||
return ;
|
||||
}
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: `URL resolved successfully`,
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "checkEndpointResolution"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a monitor by its ID and also deletes associated checks, alerts, and notifications.
|
||||
* @async
|
||||
@@ -475,6 +513,7 @@ module.exports = {
|
||||
getMonitorsAndSummaryByTeamId,
|
||||
getMonitorsByTeamId,
|
||||
createMonitor,
|
||||
checkEndpointResolution,
|
||||
deleteMonitor,
|
||||
deleteAllMonitors,
|
||||
editMonitor,
|
||||
|
||||
@@ -18,6 +18,12 @@ router.post(
|
||||
monitorController.createMonitor
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/check-endpoint/url",
|
||||
isAllowed(["admin", "superadmin"]),
|
||||
monitorController.checkEndpointResolution
|
||||
)
|
||||
|
||||
router.delete(
|
||||
"/:monitorId",
|
||||
isAllowed(["admin", "superadmin"]),
|
||||
|
||||
Reference in new issue
Block a user