delete all
This commit is contained in:
1 parent
6c04f46e8a
commit
ee04ac2b98
7 files changed
+115
-8
No files matched your search
@@ -37,6 +37,8 @@ export const HistogramResponseTime = ({
|
||||
data = normalized;
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
|
||||
const chartHeight = typeof height === "number" ? `${height}px` : height;
|
||||
const gridGap = gap ?? theme.spacing(0.5);
|
||||
|
||||
@@ -85,7 +87,9 @@ export const HistogramResponseTime = ({
|
||||
);
|
||||
|
||||
return isPlaceholder ? (
|
||||
<Box key={index}>{bar}</Box>
|
||||
<Box key={index} sx={{ height: "100%" }}>
|
||||
{bar}
|
||||
</Box>
|
||||
) : (
|
||||
<HeatmapResponseTimeTooltip key={index} check={check}>
|
||||
{bar}
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Box from "@mui/material/Box";
|
||||
import { BasePage, ConfigBox } from "@/components/design-elements";
|
||||
import { AutoComplete, Select, LanguageSelector } from "@/components/inputs";
|
||||
import { RadioWithDescription } from "@/components/inputs/RadioInput";
|
||||
import {
|
||||
AutoComplete,
|
||||
Select,
|
||||
LanguageSelector,
|
||||
Button,
|
||||
RadioWithDescription,
|
||||
Dialog,
|
||||
} from "@/components/inputs";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { SettingsForm } from "@/pages/settings/SettingsForm";
|
||||
import DummyChart from "@/pages/settings/DummyChart";
|
||||
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { setTimezone, setMode, setChartType } from "@/features/uiSlice";
|
||||
import { useAppSelector, useAppDispatch } from "@/hooks/AppHooks";
|
||||
import timezones from "@/utils/timezones.json";
|
||||
import type { ITimeZone } from "@/types/timezone";
|
||||
import type { SelectChangeEvent } from "@mui/material/Select";
|
||||
import { useGet, usePatch, usePost } from "@/hooks/UseApi";
|
||||
import { useGet, usePatch, usePost, useDelete } from "@/hooks/UseApi";
|
||||
import type { ApiResponse } from "@/hooks/UseApi";
|
||||
import { z } from "zod";
|
||||
import { systemSettingsSchema } from "@/validation/zod";
|
||||
import DummyChart from "./DummyChart";
|
||||
|
||||
const SettingsPage = () => {
|
||||
type FormValues = z.infer<typeof systemSettingsSchema>;
|
||||
@@ -26,8 +34,13 @@ const SettingsPage = () => {
|
||||
|
||||
const orgPermissions = user?.org?.permissions || [];
|
||||
const hasMaster = orgPermissions.includes("master");
|
||||
const hasDelete =
|
||||
orgPermissions.includes("monitors.*") ||
|
||||
orgPermissions.includes("monitors.delete") ||
|
||||
hasMaster;
|
||||
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const timezone = useAppSelector((state) => state.ui.timezone);
|
||||
const mode = useAppSelector((state) => state.ui.mode);
|
||||
@@ -42,6 +55,7 @@ const SettingsPage = () => {
|
||||
|
||||
const { post, loading: posting } = usePost<Partial<FormValues>, any>();
|
||||
const { patch, loading: patching } = usePatch<Partial<FormValues>, any>();
|
||||
const { deleteFn, loading: isDeleting } = useDelete();
|
||||
|
||||
const { response, loading } = useGet<ApiResponse<any>>(
|
||||
hasMaster ? "/settings" : null
|
||||
@@ -56,6 +70,15 @@ const SettingsPage = () => {
|
||||
const onTest = async (data: Partial<FormValues>) => {
|
||||
await post("/settings/test-transport", data);
|
||||
};
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
|
||||
const handleOpen = () => setIsDialogOpen(true);
|
||||
const handleCancel = () => setIsDialogOpen(false);
|
||||
const handleConfirm = async () => {
|
||||
await deleteFn("/monitors");
|
||||
setIsDialogOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<BasePage>
|
||||
<ConfigBox
|
||||
@@ -123,6 +146,28 @@ const SettingsPage = () => {
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
{hasDelete && (
|
||||
<ConfigBox
|
||||
title="Monitors"
|
||||
subtitle="Monitor related settings"
|
||||
rightContent={
|
||||
<Stack gap={theme.spacing(4)}>
|
||||
<Stack gap={theme.spacing(4)}>
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={handleOpen}
|
||||
>
|
||||
Remove all monitors
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{hasMaster && (
|
||||
<SettingsForm
|
||||
onSubmit={onSubmit}
|
||||
@@ -131,6 +176,17 @@ const SettingsPage = () => {
|
||||
loading={posting || loading || patching}
|
||||
/>
|
||||
)}
|
||||
<Dialog
|
||||
open={isDialogOpen}
|
||||
title={t("settings.removeAllMonitors.title", "Delete all monitors?")}
|
||||
content={t(
|
||||
"settings.removeAllMonitors.description",
|
||||
"This action cannot be undone. Are you sure you want to remove all monitors?"
|
||||
)}
|
||||
onConfirm={handleConfirm}
|
||||
onCancel={handleCancel}
|
||||
loading={isDeleting}
|
||||
/>
|
||||
</BasePage>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ export const registerSchema = z
|
||||
});
|
||||
|
||||
const urlRegex =
|
||||
/^(https?:\/\/)?(localhost|[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3})(:\d{1,5})?(\/.*)?$/;
|
||||
/^(https?:\/\/)?(localhost|([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|[a-zA-Z0-9-]+|(\d{1,3}\.){3}\d{1,3})(:\d{1,5})?(\/.*)?$/;
|
||||
|
||||
const durationSchema = z
|
||||
.string()
|
||||
|
||||
@@ -15,6 +15,11 @@ export interface IMonitorController {
|
||||
togglePause(req: Request, res: Response, next: NextFunction): Promise<void>;
|
||||
update(req: Request, res: Response, next: NextFunction): Promise<void>;
|
||||
delete(req: Request, res: Response, next: NextFunction): Promise<void>;
|
||||
deleteAllInOrg(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<void>;
|
||||
testNotifications(
|
||||
req: Request,
|
||||
res: Response,
|
||||
@@ -287,6 +292,28 @@ class MonitorController {
|
||||
}
|
||||
};
|
||||
|
||||
deleteAllInOrg = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const tokenizedUser = req.user;
|
||||
if (!tokenizedUser) {
|
||||
return res.status(401).json({ message: "Unauthorized" });
|
||||
}
|
||||
|
||||
const orgId = tokenizedUser.orgId;
|
||||
if (!orgId) {
|
||||
throw new ApiError("No organization ID", 400);
|
||||
}
|
||||
|
||||
await this.monitorService.deleteAllInOrg(orgId);
|
||||
|
||||
res.status(200).json({
|
||||
message: "All monitors in organization deleted successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
testNotifications = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Router } from "express";
|
||||
import { MonitorController } from "@/controllers/index.js";
|
||||
import { verifyToken } from "@/middleware/VerifyToken.js";
|
||||
import { verifyTeamPermission } from "@/middleware/VerifyPermission.js";
|
||||
import {
|
||||
verifyOrgPermission,
|
||||
verifyTeamPermission,
|
||||
} from "@/middleware/VerifyPermission.js";
|
||||
import { addUserContext } from "@/middleware/AddUserContext.js";
|
||||
import { PERMISSIONS } from "@/services/business/AuthService.js";
|
||||
import { validateBody, validateQuery } from "@/middleware/validation.js";
|
||||
@@ -109,6 +112,13 @@ class MonitorRoutes {
|
||||
verifyTeamPermission([PERMISSIONS.monitors.delete]),
|
||||
this.controller.delete
|
||||
);
|
||||
this.router.delete(
|
||||
"/",
|
||||
verifyToken,
|
||||
addUserContext,
|
||||
verifyOrgPermission([PERMISSIONS.monitors.delete]),
|
||||
this.controller.deleteAllInOrg
|
||||
);
|
||||
};
|
||||
|
||||
getRouter() {
|
||||
|
||||
@@ -59,6 +59,7 @@ export interface IMonitorService {
|
||||
updateData: Partial<IMonitor>
|
||||
) => Promise<IMonitor>;
|
||||
delete: (teamId: string, monitorId: string) => Promise<boolean>;
|
||||
deleteAllInOrg: (orgId: string) => Promise<boolean>;
|
||||
export: (teamId: string) => Promise<Array<Record<string, unknown>>>;
|
||||
import: (
|
||||
orgId: string,
|
||||
@@ -665,6 +666,15 @@ class MonitorService implements IMonitorService {
|
||||
return true;
|
||||
}
|
||||
|
||||
async deleteAllInOrg(orgId: string) {
|
||||
const montiors = await Monitor.find({ orgId });
|
||||
for (const monitor of montiors) {
|
||||
await this.jobQueue.deleteJob(monitor);
|
||||
}
|
||||
const deleted = await Monitor.deleteMany({ orgId });
|
||||
return deleted.acknowledged;
|
||||
}
|
||||
|
||||
export = async (teamId: string) => {
|
||||
return Monitor.find({ teamId })
|
||||
.select("name url port type interval n secret -_id")
|
||||
|
||||
@@ -45,7 +45,7 @@ export const inviteSchema = z.object({
|
||||
});
|
||||
|
||||
const urlRegex =
|
||||
/^(https?:\/\/)?(localhost|[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3})(:\d{1,5})?(\/.*)?$/;
|
||||
/^(https?:\/\/)?(localhost|([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|[a-zA-Z0-9-]+|(\d{1,3}\.){3}\d{1,3})(:\d{1,5})?(\/.*)?$/;
|
||||
|
||||
export const monitorSchema = z
|
||||
.object({
|
||||
|
||||
Reference in new issue
Block a user