// React, Redux, Router import { useTheme } from "@emotion/react"; import { useNavigate } from "react-router-dom"; import { useState } from "react"; import { useSelector, useDispatch } from "react-redux"; // Utility and Network import { infrastructureMonitorValidation } from "../../../Validation/validation"; import { createInfrastructureMonitor } from "../../../Features/InfrastructureMonitors/infrastructureMonitorsSlice"; import { capitalizeFirstLetter } from "../../../Utils/stringUtils"; // MUI import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material"; import LoadingButton from "@mui/lab/LoadingButton"; //Components import Breadcrumbs from "../../../Components/Breadcrumbs"; import Link from "../../../Components/Link"; import { ConfigBox } from "../../Monitors/styled"; import TextInput from "../../../Components/Inputs/TextInput"; import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments"; import { createToast } from "../../../Utils/toastUtils"; import Checkbox from "../../../Components/Inputs/Checkbox"; import Select from "../../../Components/Inputs/Select"; import { CustomThreshold } from "./CustomThreshold"; const SELECT_VALUES = [ { _id: 0.25, name: "15 seconds" }, { _id: 0.5, name: "30 seconds" }, { _id: 1, name: "1 minute" }, { _id: 2, name: "2 minutes" }, { _id: 5, name: "5 minutes" }, { _id: 10, name: "10 minutes" }, ]; const METRICS = ["cpu", "memory", "disk", "temperature"]; const METRIC_PREFIX = "usage_"; const MS_PER_MINUTE = 60000; const hasAlertError = (errors) => { return Object.keys(errors).filter((k) => k.startsWith(METRIC_PREFIX)).length > 0; }; const getAlertError = (errors) => { return Object.keys(errors).find((key) => key.startsWith(METRIC_PREFIX)) ? errors[Object.keys(errors).find((key) => key.startsWith(METRIC_PREFIX))] : null; }; const CreateInfrastructureMonitor = () => { const theme = useTheme(); const { user, authToken } = useSelector((state) => state.auth); const monitorState = useSelector((state) => state.infrastructureMonitor); const dispatch = useDispatch(); const navigate = useNavigate(); // State const [errors, setErrors] = useState({}); const [https, setHttps] = useState(false); const [infrastructureMonitor, setInfrastructureMonitor] = useState({ url: "", name: "", notifications: [], interval: 0.25, cpu: false, usage_cpu: "", memory: false, usage_memory: "", disk: false, usage_disk: "", temperature: false, usage_temperature: "", secret: "", }); // Handlers const handleCreateInfrastructureMonitor = async (event) => { event.preventDefault(); // Build the form let form = { url: `http${https ? "s" : ""}://` + infrastructureMonitor.url, name: infrastructureMonitor.name === "" ? infrastructureMonitor.url : infrastructureMonitor.name, interval: infrastructureMonitor.interval * MS_PER_MINUTE, cpu: infrastructureMonitor.cpu, ...(infrastructureMonitor.cpu ? { usage_cpu: infrastructureMonitor.usage_cpu } : {}), memory: infrastructureMonitor.memory, ...(infrastructureMonitor.memory ? { usage_memory: infrastructureMonitor.usage_memory } : {}), disk: infrastructureMonitor.disk, ...(infrastructureMonitor.disk ? { usage_disk: infrastructureMonitor.usage_disk } : {}), temperature: infrastructureMonitor.temperature, ...(infrastructureMonitor.temperature ? { usage_temperature: infrastructureMonitor.usage_temperature } : {}), secret: infrastructureMonitor.secret, }; const { error } = infrastructureMonitorValidation.validate(form, { abortEarly: false, }); if (error) { const newErrors = {}; error.details.forEach((err) => { newErrors[err.path[0]] = err.message; }); setErrors(newErrors); createToast({ body: "Please check the form for errors." }); return; } // Build the thresholds for the form const { cpu, usage_cpu, memory, usage_memory, disk, usage_disk, temperature, usage_temperature, ...rest } = form; const thresholds = { ...(cpu ? { usage_cpu: usage_cpu / 100 } : {}), ...(memory ? { usage_memory: usage_memory / 100 } : {}), ...(disk ? { usage_disk: usage_disk / 100 } : {}), ...(temperature ? { usage_temperature: usage_temperature / 100 } : {}), }; form = { ...rest, description: form.name, teamId: user.teamId, userId: user._id, type: "hardware", notifications: infrastructureMonitor.notifications, thresholds, }; const action = await dispatch( createInfrastructureMonitor({ authToken, monitor: form }) ); if (action.meta.requestStatus === "fulfilled") { createToast({ body: "Infrastructure monitor created successfully!" }); navigate("/infrastructure"); } else { createToast({ body: "Failed to create monitor." }); } }; const handleChange = (event) => { const { value, name } = event.target; setInfrastructureMonitor({ ...infrastructureMonitor, [name]: value, }); const { error } = infrastructureMonitorValidation.validate( { [name]: value }, { abortEarly: false } ); setErrors((prev) => ({ ...prev, ...(error ? { [name]: error.details[0].message } : { [name]: undefined }), })); }; const handleCheckboxChange = (event) => { const { name } = event.target; const { checked } = event.target; setInfrastructureMonitor({ ...infrastructureMonitor, [name]: checked, }); }; const handleNotifications = (event, type) => { const { value } = event.target; let notifications = [...infrastructureMonitor.notifications]; const notificationExists = notifications.some((notification) => { if (notification.type === type && notification.address === value) { return true; } return false; }); if (notificationExists) { notifications = notifications.filter((notification) => { if (notification.type === type && notification.address === value) { return false; } return true; }); } else { notifications.push({ type, address: value }); } setInfrastructureMonitor((prev) => ({ ...prev, notifications, })); }; return ( Create your{" "} monitor General settings Here you can select the URL of the host, together with the friendly name and authorization secret to connect to the server agent. The server you are monitoring must be running the{" "} } placeholder={"localhost:59232/api/v1/metrics"} label="Server URL" https={https} value={infrastructureMonitor.url} onChange={handleChange} error={errors["url"] ? true : false} helperText={errors["url"]} /> Protocol Incident notifications When there is an incident, notify users. When there is a new incident, notification.type === "email" )} value={user?.email} onChange={(event) => handleNotifications(event, "email")} /> Customize alerts Send a notification to user(s) when thresholds exceed a specified percentage. {METRICS.map((metric) => { return ( ); })} {/* Error text */} {hasAlertError(errors) && ( {getAlertError(errors)} )} Advanced settings