mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-22 09:39:57 -06:00
Merge pull request #761 from bluewave-labs/feat/clear-checks
Feat/clear checks, resolves #759
This commit is contained in:
@@ -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() {
|
||||
/>
|
||||
<Route
|
||||
path="settings"
|
||||
element={<ProtectedRoute Component={Settings} />}
|
||||
element={<ProtectedRoute Component={SettingsWithAdminProp} />}
|
||||
/>
|
||||
<Route
|
||||
path="account/profile"
|
||||
|
||||
@@ -149,6 +149,26 @@ export const pauseUptimeMonitor = createAsyncThunk(
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteMonitorChecksByTeamId = createAsyncThunk(
|
||||
"monitors/deleteChecksByTeamId",
|
||||
async (data, thunkApi) => {
|
||||
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) => {
|
||||
|
||||
@@ -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 = () => {
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h1">History and monitoring</Typography>
|
||||
<Typography sx={{ mt: theme.spacing(2) }}>
|
||||
Define here for how long you want to keep the data. You can also
|
||||
remove all past data.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<Field
|
||||
type="text"
|
||||
id="history-monitoring"
|
||||
label="The days you want to keep monitoring history."
|
||||
isOptional={true}
|
||||
optionalLabel="0 for infinite"
|
||||
placeholder="90"
|
||||
value=""
|
||||
onChange={() => logger.warn("Disabled")}
|
||||
/>
|
||||
{isAdmin && (
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography>Clear all stats. This is irreversible.</Typography>
|
||||
<Button
|
||||
level="error"
|
||||
label="Clear all stats"
|
||||
sx={{ mt: theme.spacing(4) }}
|
||||
/>
|
||||
<Typography component="h1">History and monitoring</Typography>
|
||||
<Typography sx={{ mt: theme.spacing(2) }}>
|
||||
Define here for how long you want to keep the data. You can also
|
||||
remove all past data.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<Field
|
||||
type="text"
|
||||
id="history-monitoring"
|
||||
label="The days you want to keep monitoring history."
|
||||
isOptional={true}
|
||||
optionalLabel="0 for infinite"
|
||||
placeholder="90"
|
||||
value=""
|
||||
onChange={() => logger.warn("Disabled")}
|
||||
/>
|
||||
<Box>
|
||||
<Typography>Clear all stats. This is irreversible.</Typography>
|
||||
<ButtonSpinner
|
||||
isLoading={isLoading}
|
||||
level="error"
|
||||
label="Clear all stats"
|
||||
onClick={handleClearStats}
|
||||
sx={{ mt: theme.spacing(4) }}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
)}
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h1">About</Typography>
|
||||
@@ -138,4 +166,7 @@ const Settings = () => {
|
||||
);
|
||||
};
|
||||
|
||||
Settings.propTypes = {
|
||||
isAdmin: PropTypes.bool,
|
||||
};
|
||||
export default Settings;
|
||||
|
||||
@@ -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<AxiosResponse>} 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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user