Merge pull request #761 from bluewave-labs/feat/clear-checks

Feat/clear checks, resolves #759
This commit is contained in:
Alexander Holliday
2024-08-30 11:48:38 -07:00
committed by GitHub
9 changed files with 190 additions and 31 deletions

View File

@@ -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"

View File

@@ -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) => {

View File

@@ -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;

View File

@@ -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

View File

@@ -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,
};

View File

@@ -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,

View File

@@ -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,
};

View File

@@ -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;

View File

@@ -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,