Merge branch 'develop' of https://github.com/bluewave-labs/bluewave-uptime into caio/infrastructurePage

This commit is contained in:
Caio Cabral
2024-11-26 09:40:13 -05:00
15 changed files with 942 additions and 556 deletions

967
Client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,9 +14,9 @@
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.15.17",
"@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "^5.16.7",
"@mui/icons-material": "6.1.8",
"@mui/lab": "6.0.0-beta.16",
"@mui/material": "6.1.8",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.22.3",
"@mui/x-date-pickers": "7.22.3",

View File

@@ -41,6 +41,7 @@ import { logger } from "./Utils/Logger"; // Import the logger
import { networkService } from "./main";
import { Infrastructure } from "./Pages/Infrastructure";
import InfrastructureDetails from "./Pages/Infrastructure/Details";
import Test from "./Pages/test";
function App() {
const AdminCheckedRegister = withAdminCheck(Register);
const MonitorsWithAdminProp = withAdminProp(Monitors);
@@ -89,6 +90,11 @@ function App() {
path="/"
element={<HomeLayout />}
>
<Route
path="/test"
element={<Test />}
/>
<Route
exact
path="/"

View File

@@ -4,6 +4,10 @@
.field-infrastructure-alert{
max-width: var(--env-var-width-4);
}
.field-infrastructure-alert .MuiInputBase-root:has(input) {
/* height: var(--env-var-height-2); */
min-width: unset;
}
@@ -20,7 +24,7 @@
padding-right: var(--env-var-spacing-1-minus);
}
.field .MuiInputBase-root:has(input) {
height: var(--env-var-height-2);
/* height: var(--env-var-height-2); */
}
.field .MuiInputBase-root:has(.MuiInputAdornment-root) {
padding-right: var(--env-var-spacing-1-minus);

View File

@@ -0,0 +1,60 @@
import { Stack, Typography, InputAdornment, IconButton } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { useState } from "react";
import PropTypes from "prop-types";
import VisibilityOff from "@mui/icons-material/VisibilityOff";
import Visibility from "@mui/icons-material/Visibility";
export const HttpAdornment = ({ https }) => {
const theme = useTheme();
return (
<Stack
direction="row"
alignItems="center"
height="100%"
sx={{
borderRight: `solid 1px ${theme.palette.border.dark}`,
backgroundColor: theme.palette.background.accent,
pl: theme.spacing(6),
}}
>
<Typography
component="h5"
paddingRight={"var(--env-var-spacing-1-minus)"}
color={theme.palette.text.secondary}
sx={{ lineHeight: 1, opacity: 0.8 }}
>
{https ? "https" : "http"}://
</Typography>
</Stack>
);
};
export const PasswordEndAdornment = ({ fieldType, setFieldType }) => {
const theme = useTheme();
return (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={() => setFieldType(fieldType === "password" ? "text" : "password")}
sx={{
color: theme.palette.border.dark,
padding: theme.spacing(1),
"&:focus-visible": {
outline: `2px solid ${theme.palette.primary.main}`,
outlineOffset: `2px`,
},
"& .MuiTouchRipple-root": {
pointerEvents: "none",
display: "none",
},
}}
>
{fieldType === "password" ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
);
};
HttpAdornment.propTypes = {
https: PropTypes.bool.isRequired,
};

View File

@@ -0,0 +1,130 @@
import { Stack, TextField, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import { forwardRef, useState, cloneElement } from "react";
import PropTypes from "prop-types";
const getSx = (theme, type, maxWidth) => {
const sx = {
maxWidth: maxWidth,
};
if (type === "url") {
return {
...sx,
"& .MuiInputBase-root": { padding: 0 },
"& .MuiStack-root": {
borderTopLeftRadius: theme.shape.borderRadius,
borderBottomLeftRadius: theme.shape.borderRadius,
},
};
}
return sx;
};
const Required = () => {
const theme = useTheme();
return (
<Typography
component="span"
ml={theme.spacing(1)}
color={theme.palette.error.main}
>
*
</Typography>
);
};
const Optional = ({ optionalLabel }) => {
const theme = useTheme();
return (
<Typography
component="span"
fontSize="inherit"
fontWeight={400}
ml={theme.spacing(2)}
sx={{ opacity: 0.6 }}
>
{optionalLabel || "(optional)"}
</Typography>
);
};
Optional.propTypes = {
optionalLabel: PropTypes.string,
};
const TextInput = forwardRef(
(
{
type,
value,
placeholder,
isRequired,
isOptional,
optionalLabel,
onChange,
error = false,
helperText = null,
startAdornment = null,
endAdornment = null,
label = null,
maxWidth = "100%",
},
ref
) => {
const [fieldType, setFieldType] = useState(type);
const theme = useTheme();
return (
<Stack>
<Typography
component="h3"
fontSize={"var(--env-var-font-size-medium)"}
color={theme.palette.text.secondary}
fontWeight={500}
>
{label}
{isRequired && <Required />}
{isOptional && <Optional optionalLabel={optionalLabel} />}
</Typography>
<TextField
type={fieldType}
value={value}
placeholder={placeholder}
onChange={onChange}
error={error}
helperText={helperText}
inputRef={ref}
sx={getSx(theme, type, maxWidth)}
slotProps={{
input: {
startAdornment: startAdornment,
endAdornment: endAdornment
? cloneElement(endAdornment, { fieldType, setFieldType })
: null,
},
}}
/>
</Stack>
);
}
);
TextInput.displayName = "TextInput";
TextInput.propTypes = {
type: PropTypes.string,
value: PropTypes.string,
placeholder: PropTypes.string,
isRequired: PropTypes.bool,
isOptional: PropTypes.bool,
optionalLabel: PropTypes.string,
onChange: PropTypes.func,
error: PropTypes.bool,
helperText: PropTypes.string,
startAdornment: PropTypes.node,
endAdornment: PropTypes.node,
label: PropTypes.string,
maxWidth: PropTypes.string,
};
export default TextInput;

View File

@@ -83,7 +83,7 @@ const menu = [
const URL_MAP = {
support: "https://github.com/bluewave-labs/bluewave-uptime/issues",
docs: "https://bluewavelabs.gitbook.io/uptime-manager",
docs: "https://bluewavelabs.gitbook.io/checkmate",
changelog: "https://github.com/bluewave-labs/bluewave-uptime/releases",
};
@@ -207,14 +207,14 @@ function Sidebar() {
userSelect: "none",
}}
>
BU
C
</Stack>
<Typography
component="span"
mt={theme.spacing(2)}
sx={{ opacity: 0.8, fontWeight: 500 }}
>
BlueWave Uptime
Checkmate
</Typography>
</Stack>
<IconButton

View File

@@ -10,7 +10,8 @@ import {
import { useNavigate } from "react-router-dom";
import { useTheme } from "@emotion/react";
import { createToast } from "../../../Utils/toastUtils";
import { logger } from "../../../Utils/Logger";
import Link from "../../../Components/Link";
import { ConfigBox } from "../../Monitors/styled";
import Field from "../../../Components/Inputs/Field";
import Select from "../../../Components/Inputs/Select";
@@ -233,11 +234,21 @@ const CreateInfrastructureMonitor = () => {
</Typography>
<ConfigBox>
<Box>
<Typography component="h2">General settings</Typography>
<Typography component="p">
Here you can select the URL of the host, together with the friendly name and
authorization secret to connect to the server agent.
</Typography>
<Stack gap={theme.spacing(6)}>
<Typography component="h2">General settings</Typography>
<Typography component="p">
Here you can select the URL of the host, together with the friendly name
and authorization secret to connect to the server agent.
</Typography>
<Typography component="p">
The server you are monitoring must be running the{" "}
<Link
level="primary"
url="https://github.com/bluewave-labs/checkmate-agent"
label="Checkmate Monitoring Agent"
/>
</Typography>
</Stack>
</Box>
<Stack gap={theme.spacing(15)}>
<Field
@@ -297,7 +308,8 @@ const CreateInfrastructureMonitor = () => {
<Box>
<Typography component="h2">Customize alerts</Typography>
<Typography component="p">
Send a notification to user(s) when thresholds exceed a specified percentage.
Send a notification to user(s) when thresholds exceed a specified
percentage.
</Typography>
</Box>
<Stack gap={theme.spacing(6)}>

View File

@@ -348,14 +348,14 @@ const Settings = ({ isAdmin }) => {
<Typography component="h1">About</Typography>
</Box>
<Box>
<Typography component="h2">BlueWave Uptime {version}</Typography>
<Typography component="h2">Checkmate {version}</Typography>
<Typography sx={{ mt: theme.spacing(2), mb: theme.spacing(6), opacity: 0.6 }}>
Developed by Bluewave Labs.
</Typography>
<Link
level="secondary"
url="https://github.com/bluewave-labs/bluewave-uptime"
label="https://github.com/bluewave-labs/bluewave-uptime"
url="https://github.com/bluewave-labs/checkmate"
label="https://github.com/bluewave-labs/checkmate"
/>
</Box>
</ConfigBox>

192
Client/src/Pages/test.jsx Normal file
View File

@@ -0,0 +1,192 @@
import { Stack, Typography } from "@mui/material";
import Field from "../Components/Inputs/Field";
import TextInput from "../Components/Inputs/TextInput";
import { useState, useEffect, useRef } from "react";
import { HttpAdornment } from "../Components/Inputs/TextInput/Adornments";
import { PasswordEndAdornment } from "../Components/Inputs/TextInput/Adornments";
const Test = () => {
const [originalValue, setOriginalValue] = useState("");
const [originalError, setOriginalError] = useState("");
const [newValue, setNewValue] = useState("");
const [newError, setNewError] = useState("");
const [thresholdValue, setThresholdValue] = useState(20);
const [thresholdError, setThresholdError] = useState("");
const [thresholdValue2, setThresholdValue2] = useState(20);
const [thresholdError2, setThresholdError2] = useState("");
const inputRef = useRef(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
const checkError = (value) => {
if (value !== "clear") {
return "This is an error";
}
return "";
};
const checkThresholdError = (value) => {
if (value !== 99) {
return "This is a threshold error";
}
return "";
};
const checkThresholdError2 = (value) => {
if (value !== 99) {
return "This is a threshold error 2";
}
return "";
};
const handleOriginalValue = (e) => {
setOriginalError(checkError(e.target.value));
setOriginalValue(e.target.value);
};
const handleNewValue = (e) => {
setNewError(checkError(e.target.value));
setNewValue(e.target.value);
};
const handleThresholdValue = (e) => {
const parsedVal = parseInt(e.target.value);
setThresholdError(checkThresholdError(parsedVal));
setThresholdValue(parsedVal);
};
const handleThresholdValue2 = (e) => {
const parsedVal = parseInt(e.target.value);
setThresholdError2(checkThresholdError2(parsedVal));
setThresholdValue2(parsedVal);
};
return (
<Stack
gap={8}
direction="column"
border="1px dashed blue"
padding="1rem"
>
<Typography>
This is a test page for the TextInput component. It is a rationalized Input
component.
</Typography>
<Typography>Type anything for an error.</Typography>
<Typography>Typing "clear" will clear the error for text based input</Typography>
<Typography>Typing "99" will clear the error for threshold based input</Typography>
<Field
id="original-field"
onChange={handleOriginalValue}
type="text"
value={originalValue}
error={originalError}
/>
<TextInput
value={newValue}
onChange={handleNewValue}
error={newError !== ""}
helperText={newError}
/>
<Field
type={"url"}
id="monitor-url"
label={"URL to monitor"}
https={true}
placeholder={""}
value={originalValue}
onChange={handleOriginalValue}
error={originalError}
/>
<TextInput
type={"url"}
id="monitor-url"
label={"URL to monitor"}
placeholder={""}
value={newValue}
startAdornment={<HttpAdornment https={true} />}
onChange={handleNewValue}
error={newError !== ""}
helperText={newError}
/>
<Field
type="password"
id="login-password-input"
label="Password"
isRequired={true}
placeholder="••••••••••"
autoComplete="current-password"
value={originalValue}
onChange={handleOriginalValue}
error={originalError}
ref={inputRef}
/>
<TextInput
type="password"
id="login-password-input"
label="Password"
isRequired={true}
placeholder="••••••••••"
autoComplete="current-password"
value={newValue}
endAdornment={<PasswordEndAdornment />}
onChange={handleNewValue}
error={newError !== ""}
helperText={newError}
ref={inputRef}
/>
<Field
id="ttl"
label="The days you want to keep monitoring history."
isOptional={true}
optionalLabel="0 for infinite"
value={originalValue}
onChange={handleOriginalValue}
error={originalError}
/>
<TextInput
id="ttl"
label="The days you want to keep monitoring history."
isOptional={true}
optionalLabel="0 for infinite"
value={newValue}
onChange={handleNewValue}
error={newError !== ""}
helperText={newError}
/>
<Typography>Short field for threshold. Easily show/hide error text</Typography>
<TextInput
maxWidth="var(--env-var-width-4)"
id="threshold"
type="number"
value={thresholdValue.toString()}
onChange={handleThresholdValue}
error={thresholdError !== ""}
/>
<TextInput
maxWidth="var(--env-var-width-4)"
id="threshold"
type="number"
value={thresholdValue2.toString()}
onChange={handleThresholdValue2}
error={thresholdError2 !== ""}
/>
<Typography sx={{ color: "red" }}>
{thresholdError} {thresholdError2}
</Typography>
</Stack>
);
};
export default Test;

View File

@@ -197,6 +197,43 @@ const baseTheme = (palette) => ({
}),
},
},
MuiTextField: {
styleOverrides: {
root: ({ theme }) => ({
"& fieldset": {
borderColor: theme.palette.border.dark,
borderRadius: theme.shape.borderRadius,
},
"& .MuiInputBase-input": {
height: "var(--env-var-height-2)",
fontSize: "var(--env-var-font-size-medium)",
fontWeight: 400,
color: palette.text.secondary, // or any color from your palette
},
"& .MuiInputBase-input.MuiOutlinedInput-input": {
padding: "0 var(--env-var-spacing-1-minus) !important",
},
"& .MuiOutlinedInput-root": {
borderRadius: 4,
},
"& .MuiOutlinedInput-notchedOutline": {
borderRadius: 4,
},
"& .MuiFormHelperText-root": {
color: palette.error.main,
opacity: 0.8,
fontSize: "var(--env-var-font-size-medium)",
marginLeft: 0,
},
"& .MuiFormHelperText-root.Mui-error": {
opacity: 0.8,
fontSize: "var(--env-var-font-size-medium)",
color: palette.error.main,
},
}),
},
},
},
shape: {
borderRadius: 2,

View File

@@ -6,26 +6,26 @@
![](https://img.shields.io/github/issues-pr/bluewave-labs/bluewave-uptime)
![](https://img.shields.io/github/issues/bluewave-labs/bluewave-uptime)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">BlueWave Uptime</a></h1>
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>An open source server monitoring application</strong></p>
![Dashboard-dark](https://github.com/user-attachments/assets/db875138-164f-453c-a75e-889f88747578)
(yes, we have a light theme as well, but this looks better on readme.md)
BlueWave Uptime is an open source server monitoring application used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.
Checkmate is an open source uptime manager, server & Docker monitoring tool used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.
## Demo
See [BlueWave Uptime](https://uptime-demo.bluewavelabs.ca/) in action. The username is uptimedemo@demo.com and the password is Demouser1!
See [Checkmate](https://uptime-demo.bluewavelabs.ca/) in action. The username is uptimedemo@demo.com and the password is Demouser1!
## User's guide
Usage instructions can be found [here](https://bluewavelabs.gitbook.io/uptime-manager).
Usage instructions can be found [here](https://bluewavelabs.gitbook.io/checkmate).
## Installation
See installation instructions in [Uptime Manager documentation portal](https://bluewavelabs.gitbook.io/uptime-manager/quickstart).
See installation instructions in [Checkmate documentation portal](https://bluewavelabs.gitbook.io/checkmate/quickstart).
## Questions & ideas
@@ -44,7 +44,7 @@ We've just launched our [Discussions](https://github.com/bluewave-labs/bluewave-
**Roadmap (short term):**
We are actively developing **infrastructure monitoring** features for Uptime Manager, which will include comprehensive monitoring of memory, disk usage, and CPU performance. Our goal is to build a lightweight agent that runs on Linux servers, continuously collecting and transmitting health metrics to Uptime Manager, where the data will be visualized for real-time insights.
We are actively developing **infrastructure monitoring** features for Checkmate, which will include comprehensive monitoring of memory, disk usage, and CPU performance. Our goal is to build a lightweight agent that runs on Linux servers, continuously collecting and transmitting health metrics to Checkmate, where the data will be visualized for real-time insights.
Additionally, we will introduce **Docker monitoring** to track the performance and health of containerized environments.

View File

@@ -17,7 +17,7 @@
"dotenv": "^16.4.5",
"express": "^4.19.2",
"handlebars": "^4.7.8",
"helmet": "^7.1.0",
"helmet": "^8.0.0",
"joi": "^17.13.1",
"jsonwebtoken": "9.0.2",
"mailersend": "^2.2.0",
@@ -79,9 +79,10 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz",
"integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==",
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@@ -997,9 +998,9 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"version": "1.7.8",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz",
"integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -1787,9 +1788,10 @@
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -2808,12 +2810,12 @@
}
},
"node_modules/helmet": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz",
"integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-8.0.0.tgz",
"integrity": "sha512-VyusHLEIIO5mjQPUI1wpOAEu+wl6Q0998jzTxqUYGE45xCIcAxy3MsbEK/yyJUJ3ADeMoB6MornPH6GMWAf+Pw==",
"license": "MIT",
"engines": {
"node": ">=16.0.0"
"node": ">=18.0.0"
}
},
"node_modules/html-escaper": {

View File

@@ -20,7 +20,7 @@
"dotenv": "^16.4.5",
"express": "^4.19.2",
"handlebars": "^4.7.8",
"helmet": "^7.1.0",
"helmet": "^8.0.0",
"joi": "^17.13.1",
"jsonwebtoken": "9.0.2",
"mailersend": "^2.2.0",

View File

@@ -144,8 +144,10 @@ const getMonitorsAndSummaryByTeamIdQueryValidation = joi.object({
type: joi
.alternatives()
.try(
joi.string().valid("http", "ping", "pagespeed", "docker"),
joi.array().items(joi.string().valid("http", "ping", "pagespeed", "docker"))
joi.string().valid("http", "ping", "pagespeed", "docker", "hardware"),
joi
.array()
.items(joi.string().valid("http", "ping", "pagespeed", "docker", "hardware"))
),
});
@@ -161,8 +163,10 @@ const getMonitorsByTeamIdQueryValidation = joi.object({
type: joi
.alternatives()
.try(
joi.string().valid("http", "ping", "pagespeed", "docker"),
joi.array().items(joi.string().valid("http", "ping", "pagespeed", "docker"))
joi.string().valid("http", "ping", "pagespeed", "docker", "hardware"),
joi
.array()
.items(joi.string().valid("http", "ping", "pagespeed", "docker", "hardware"))
),
page: joi.number(),
rowsPerPage: joi.number(),