diff --git a/Client/src/App.jsx b/Client/src/App.jsx
index 88e1e7019..783f28330 100644
--- a/Client/src/App.jsx
+++ b/Client/src/App.jsx
@@ -39,6 +39,7 @@ function App() {
const DetailsWithAdminProp = withAdminProp(Details);
const PageSpeedWithAdminProp = withAdminProp(PageSpeed);
const MaintenanceWithAdminProp = withAdminProp(Maintenance);
+ const SettingsWithAdminProp = withAdminProp(Settings);
const mode = useSelector((state) => state.ui.mode);
@@ -90,7 +91,7 @@ function App() {
/>
}
+ element={}
/>
{
+ try {
+ const { authToken, teamId } = data;
+ const res = await networkService.deleteChecksByTeamId(authToken, teamId);
+ 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);
+ }
+ }
+);
+
const uptimeMonitorsSlice = createSlice({
name: "uptimeMonitors",
initialState,
@@ -256,6 +276,24 @@ const uptimeMonitorsSlice = createSlice({
: "Failed to delete uptime monitor";
})
// *****************************************************
+ // Delete Monitor checks by Team ID
+ // *****************************************************
+ .addCase(deleteMonitorChecksByTeamId.pending, (state) => {
+ state.isLoading = true;
+ })
+ .addCase(deleteMonitorChecksByTeamId.fulfilled, (state, action) => {
+ state.isLoading = false;
+ state.success = action.payload.success;
+ state.msg = action.payload.msg;
+ })
+ .addCase(deleteMonitorChecksByTeamId.rejected, (state, action) => {
+ state.isLoading = false;
+ state.success = false;
+ state.msg = action.payload
+ ? action.payload.msg
+ : "Failed to delete monitor checks";
+ })
+ // *****************************************************
// Pause Monitor
// *****************************************************
.addCase(pauseUptimeMonitor.pending, (state) => {
diff --git a/Client/src/Pages/Settings/index.jsx b/Client/src/Pages/Settings/index.jsx
index d908e33c1..c0295b572 100644
--- a/Client/src/Pages/Settings/index.jsx
+++ b/Client/src/Pages/Settings/index.jsx
@@ -1,14 +1,38 @@
import { useTheme } from "@emotion/react";
import { Box, Stack, styled, Typography } from "@mui/material";
import Button from "../../Components/Button";
+import ButtonSpinner from "../../Components/ButtonSpinner";
import Field from "../../Components/Inputs/Field";
import Link from "../../Components/Link";
import Select from "../../Components/Inputs/Select";
import { logger } from "../../Utils/Logger";
import "./index.css";
-
-const Settings = () => {
+import { useDispatch, useSelector } from "react-redux";
+import { createToast } from "../../Utils/toastUtils";
+import { deleteMonitorChecksByTeamId } from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
+import PropTypes from "prop-types";
+const Settings = ({ isAdmin }) => {
const theme = useTheme();
+ const { user, authToken } = useSelector((state) => state.auth);
+ const { isLoading } = useSelector((state) => state.uptimeMonitors);
+
+ const dispatch = useDispatch();
+ const handleClearStats = async () => {
+ try {
+ const action = await dispatch(
+ deleteMonitorChecksByTeamId({ teamId: user.teamId, authToken })
+ );
+
+ if (deleteMonitorChecksByTeamId.fulfilled.match(action)) {
+ createToast({ body: "Stats cleared successfully" });
+ } else {
+ createToast({ body: "Failed to clear stats" });
+ }
+ } catch (error) {
+ logger.error(error);
+ createToast({ body: "Failed to clear stats" });
+ }
+ };
const ConfigBox = styled("div")({
display: "flex",
@@ -79,35 +103,39 @@ const Settings = () => {
/>
-
-
- History and monitoring
-
- Define here for how long you want to keep the data. You can also
- remove all past data.
-
-
-
- logger.warn("Disabled")}
- />
+ {isAdmin && (
+
- Clear all stats. This is irreversible.
-
+ History and monitoring
+
+ Define here for how long you want to keep the data. You can also
+ remove all past data.
+
-
-
+
+ logger.warn("Disabled")}
+ />
+
+ Clear all stats. This is irreversible.
+
+
+
+
+ )}
About
@@ -138,4 +166,7 @@ const Settings = () => {
);
};
+Settings.propTypes = {
+ isAdmin: PropTypes.bool,
+};
export default Settings;
diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js
index 74bf8460b..31a23b5b1 100644
--- a/Client/src/Utils/NetworkService.js
+++ b/Client/src/Utils/NetworkService.js
@@ -184,6 +184,25 @@ class NetworkService {
},
});
}
+
+ /**
+ * ************************************
+ * Deletes all checks for all monitor by teamID
+ * ************************************
+ *
+ * @async
+ * @param {string} authToken - The authorization token to be used in the request header.
+ * @param {string} monitorId - The ID of the monitor to be deleted.
+ * @returns {Promise} The response from the axios DELETE request.
+ */
+ async deleteChecksByTeamId(authToken, teamId) {
+ return this.axiosInstance.delete(`/checks/team/${teamId}`, {
+ headers: {
+ Authorization: `Bearer ${authToken}`,
+ "Content-Type": "application/json",
+ },
+ });
+ }
/**
* ************************************
* Pauses a single monitor by its ID
diff --git a/Server/controllers/checkController.js b/Server/controllers/checkController.js
index f553dabcb..85bce0b54 100644
--- a/Server/controllers/checkController.js
+++ b/Server/controllers/checkController.js
@@ -6,6 +6,7 @@ const {
getTeamChecksParamValidation,
getTeamChecksQueryValidation,
deleteChecksParamValidation,
+ deleteChecksByTeamIdParamValidation,
} = require("../validation/joi");
const { successMessages } = require("../utils/messages");
const SERVICE_NAME = "check";
@@ -112,9 +113,34 @@ const deleteChecks = async (req, res, next) => {
}
};
+const deleteChecksByTeamId = async (req, res, next) => {
+ try {
+ await deleteChecksByTeamIdParamValidation.validateAsync(req.params);
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "deleteChecksByTeam";
+ error.status = 422;
+ next(error);
+ }
+
+ try {
+ const deletedCount = await req.db.deleteChecksByTeamId(req.params.teamId);
+ return res.status(200).json({
+ success: true,
+ msg: successMessages.CHECK_DELETE,
+ data: { deletedCount },
+ });
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "deleteChecksByTeamId";
+ next(error);
+ }
+};
+
module.exports = {
createCheck,
getChecks,
getTeamChecks,
deleteChecks,
+ deleteChecksByTeamId,
};
diff --git a/Server/db/mongo/MongoDB.js b/Server/db/mongo/MongoDB.js
index 1b675e7e5..912bfb438 100644
--- a/Server/db/mongo/MongoDB.js
+++ b/Server/db/mongo/MongoDB.js
@@ -94,6 +94,7 @@ const {
getChecks,
getTeamChecks,
deleteChecks,
+ deleteChecksByTeamId,
} = require("./modules/checkModule");
//****************************************
@@ -155,6 +156,7 @@ module.exports = {
getChecks,
getTeamChecks,
deleteChecks,
+ deleteChecksByTeamId,
createAlert,
getAlertsByUserId,
getAlertsByMonitorId,
diff --git a/Server/db/mongo/modules/checkModule.js b/Server/db/mongo/modules/checkModule.js
index 56a40d2ad..7736ca3c1 100644
--- a/Server/db/mongo/modules/checkModule.js
+++ b/Server/db/mongo/modules/checkModule.js
@@ -170,7 +170,7 @@ const getTeamChecks = async (req) => {
};
/**
- * Delete all checks for a user
+ * Delete all checks for a monitor
* @async
* @param {string} monitorId
* @returns {number}
@@ -185,10 +185,40 @@ const deleteChecks = async (monitorId) => {
throw error;
}
};
+
+/**
+ * Delete all checks for a team
+ * @async
+ * @param {string} monitorId
+ * @returns {number}
+ * @throws {Error}
+ */
+
+const deleteChecksByTeamId = async (teamId) => {
+ try {
+ const teamMonitors = await Monitor.find({ teamId: teamId });
+ let totalDeletedCount = 0;
+
+ await Promise.all(
+ teamMonitors.map(async (monitor) => {
+ const result = await Check.deleteMany({ monitorId: monitor._id });
+ totalDeletedCount += result.deletedCount;
+ monitor.status = true;
+ await monitor.save();
+ })
+ );
+
+ return totalDeletedCount;
+ } catch (error) {
+ throw error;
+ }
+};
+
module.exports = {
createCheck,
getChecksCount,
getChecks,
getTeamChecks,
deleteChecks,
+ deleteChecksByTeamId,
};
diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js
index 562b91263..649a68ccf 100644
--- a/Server/routes/checkRoute.js
+++ b/Server/routes/checkRoute.js
@@ -1,6 +1,7 @@
const router = require("express").Router();
const checkController = require("../controllers/checkController");
const { verifyOwnership } = require("../middleware/verifyOwnership");
+const { isAllowed } = require("../middleware/isAllowed");
const Monitor = require("../models/Monitor");
router.post(
@@ -19,4 +20,10 @@ router.delete(
checkController.deleteChecks
);
+router.delete(
+ "/team/:teamId",
+ isAllowed(["admin", "superadmin"]),
+ checkController.deleteChecksByTeamId
+);
+
module.exports = router;
diff --git a/Server/validation/joi.js b/Server/validation/joi.js
index 2a7ff173d..ccc8da9f6 100644
--- a/Server/validation/joi.js
+++ b/Server/validation/joi.js
@@ -292,6 +292,10 @@ const deleteChecksParamValidation = joi.object({
monitorId: joi.string().required(),
});
+const deleteChecksByTeamIdParamValidation = joi.object({
+ teamId: joi.string().required(),
+});
+
//****************************************
// PageSpeedCheckValidation
//****************************************
@@ -372,6 +376,7 @@ module.exports = {
getTeamChecksParamValidation,
getTeamChecksQueryValidation,
deleteChecksParamValidation,
+ deleteChecksByTeamIdParamValidation,
deleteUserParamValidation,
getPageSpeedCheckParamValidation,
createPageSpeedCheckParamValidation,