Merge branch 'bluewave-labs:develop' into Network-tab-implementation-FE
4
.github/scripts/download-translations.js
vendored
@@ -6,7 +6,9 @@ import { URLSearchParams } from "url";
|
||||
// POEditor API information
|
||||
const API_TOKEN = process.env.POEDITOR_API;
|
||||
const PROJECT_ID = process.env.POEDITOR_PROJECT_ID;
|
||||
const LANGUAGES = (process.env.LANGUAGES || "tr,en").split(",");
|
||||
const LANGUAGES = (
|
||||
process.env.LANGUAGES || "ar,zh-tw,cs,en,fi,fr,de,pt-br,ru,es,tr,ja"
|
||||
).split(",");
|
||||
const EXPORT_FORMAT = process.env.EXPORT_FORMAT || "key_value_json";
|
||||
|
||||
// POEditor API endpoint
|
||||
|
||||
2
.github/workflows/poeditor-sync.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
languages:
|
||||
description: "Languages to synchronize (comma separated, e.g.: tr,en,es)"
|
||||
required: false
|
||||
default: "ar,zh-tw,cs,en,fi,fr,de,pt-br,ru,es,tr"
|
||||
default: "ar,zh-tw,cs,en,fi,fr,de,pt-br,ru,es,tr,ja"
|
||||
format:
|
||||
description: "Export format (key_value_json or json)"
|
||||
required: false
|
||||
|
||||
@@ -94,14 +94,16 @@ Feel free to ask questions or share your ideas - we'd love to hear from you!
|
||||
- E-mail, Webhooks, Discord, Telegram, Slack notifications
|
||||
- Scheduled maintenance
|
||||
- JSON query monitoring
|
||||
- Support for multiple languages
|
||||
- Multi-language support for English, German, Japanese, Portuguese (Brazil), Russian, Turkish, Ukrainian, Vietnamese, Chinese (Traditional, Taiwan)
|
||||
|
||||
**Short term roadmap:** ([Milestone 2.2](https://github.com/bluewave-labs/Checkmate/milestone/8))
|
||||
**Short term roadmap:**
|
||||
|
||||
- Better notifications
|
||||
- Network monitoring
|
||||
- ..and a few more features
|
||||
|
||||
If you would like to sponsor an additional feature, [see this page](https://checkmate.so/sponsored-features).
|
||||
|
||||
## Screenshots
|
||||
|
||||
<p>
|
||||
|
||||
35
client/src/Components/ArrowLeft/index.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import LeftArrow from "../../assets/icons/left-arrow.svg?react";
|
||||
import LeftArrowDouble from "../../assets/icons/left-arrow-double.svg?react";
|
||||
import LeftArrowLong from "../../assets/icons/left-arrow-long.svg?react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const ArrowLeft = ({ type, color = "#667085", ...props }) => {
|
||||
if (type === "double") {
|
||||
return (
|
||||
<LeftArrowDouble
|
||||
style={{ color }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
} else if (type === "long") {
|
||||
return (
|
||||
<LeftArrowLong
|
||||
style={{ color }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<LeftArrow
|
||||
style={{ color }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
ArrowLeft.propTypes = {
|
||||
color: PropTypes.string,
|
||||
type: PropTypes.oneOf(["double", "long", "default"]),
|
||||
};
|
||||
export default ArrowLeft;
|
||||
28
client/src/Components/ArrowRight/index.jsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import RightArrow from "../../assets/icons/right-arrow.svg?react";
|
||||
import RightArrowDouble from "../../assets/icons/right-arrow-double.svg?react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const ArrowRight = ({ type, color = "#667085", ...props }) => {
|
||||
if (type === "double") {
|
||||
return (
|
||||
<RightArrowDouble
|
||||
style={{ color }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<RightArrow
|
||||
style={{ color }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
ArrowRight.propTypes = {
|
||||
type: PropTypes.oneOf(["double", "default"]),
|
||||
color: PropTypes.string,
|
||||
};
|
||||
|
||||
export default ArrowRight;
|
||||
@@ -15,7 +15,7 @@ import { useTheme } from "@emotion/react";
|
||||
* <Avatar src="assets/img" first="Alex" last="Holliday" small />
|
||||
*/
|
||||
|
||||
const Avatar = ({ src, small, sx }) => {
|
||||
const Avatar = ({ src, small, sx, onClick = () => {} }) => {
|
||||
const { user } = useSelector((state) => state.auth);
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -31,6 +31,7 @@ const Avatar = ({ src, small, sx }) => {
|
||||
|
||||
return (
|
||||
<MuiAvatar
|
||||
onClick={onClick}
|
||||
alt={`${user?.firstName} ${user?.lastName}`}
|
||||
/* TODO What is the /static/images/avatar/2.jpg ?*/
|
||||
src={src ? src : user?.avatarImage ? image : "/static/images/avatar/2.jpg"}
|
||||
@@ -66,6 +67,7 @@ Avatar.propTypes = {
|
||||
src: PropTypes.string,
|
||||
small: PropTypes.bool,
|
||||
sx: PropTypes.object,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default Avatar;
|
||||
|
||||
@@ -2,8 +2,7 @@ import PropTypes from "prop-types";
|
||||
import { Box, Breadcrumbs as MUIBreadcrumbs } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import ArrowRight from "../../assets/icons/right-arrow.svg?react";
|
||||
|
||||
import ArrowRight from "../ArrowRight";
|
||||
import "./index.css";
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,9 +51,10 @@ const Check = ({ text, noHighlightText, variant = "info", outlined = false }) =>
|
||||
sx={{
|
||||
color:
|
||||
variant === "info"
|
||||
? theme.palette.primary.contrastTextTertiary
|
||||
? theme.palette.primary.contrastTextSecondary
|
||||
: colors[variant],
|
||||
opacity: 0.8,
|
||||
opacity: 0.9,
|
||||
fontWeight: 450,
|
||||
}}
|
||||
>
|
||||
{noHighlightText && <Typography component="span">{noHighlightText}</Typography>}{" "}
|
||||
|
||||
@@ -20,6 +20,11 @@ const ConfigBox = styled(Stack)(({ theme }) => ({
|
||||
borderRightColor: theme.palette.primary.lowContrast,
|
||||
paddingRight: theme.spacing(15),
|
||||
paddingLeft: theme.spacing(15),
|
||||
backgroundColor: theme.palette.tertiary.background,
|
||||
"& :is(h1, h2):first-of-type": {
|
||||
fontWeight: 600,
|
||||
marginBottom: theme.spacing(4),
|
||||
},
|
||||
},
|
||||
"& > div:last-of-type": {
|
||||
flex: 1,
|
||||
|
||||
@@ -17,6 +17,7 @@ const FallbackActionButtons = ({ link, type }) => {
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
sx={{ fontWeight: 700 }}
|
||||
onClick={() => navigate(link)}
|
||||
>
|
||||
{t(`${type}.fallback.actionButton`)}
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Skeleton from "../../assets/Images/create-placeholder.svg?react";
|
||||
import Background from "../../assets/Images/background-grid.svg?react";
|
||||
import SkeletonDark from "../../assets/Images/create-placeholder-dark.svg?react";
|
||||
import OutputAnimation from "../../assets/Animations/output.gif";
|
||||
import DarkmodeOutput from "../../assets/Animations/darkmodeOutput.gif";
|
||||
import { useSelector } from "react-redux";
|
||||
const FallbackBackground = () => {
|
||||
const theme = useTheme();
|
||||
const mode = useSelector((state) => state.ui.mode);
|
||||
return (
|
||||
<>
|
||||
{mode === "light" ? (
|
||||
<Skeleton style={{ zIndex: 1 }} />
|
||||
) : (
|
||||
<SkeletonDark style={{ zIndex: 1 }} />
|
||||
)}
|
||||
<Box
|
||||
component="img"
|
||||
src={mode === "light" ? OutputAnimation : DarkmodeOutput}
|
||||
Background="transparent"
|
||||
alt="Loading animation"
|
||||
sx={{
|
||||
zIndex: 1,
|
||||
border: "none",
|
||||
borderRadius: theme.spacing(8),
|
||||
width: "100%",
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
className="background-pattern-svg"
|
||||
sx={{
|
||||
|
||||
@@ -7,9 +7,9 @@ const FallbackContainer = ({ children, type }) => {
|
||||
return (
|
||||
<Box
|
||||
border={1}
|
||||
borderColor={theme.palette.primary.lowContrast}
|
||||
borderColor={theme.palette.tertiary.border}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.primary.main}
|
||||
backgroundColor={theme.palette.tertiary.background}
|
||||
overflow="hidden"
|
||||
sx={{
|
||||
display: "flex",
|
||||
|
||||
@@ -9,7 +9,7 @@ const FallbackTitle = ({ title }) => {
|
||||
alignSelf="center"
|
||||
component="h1"
|
||||
marginY={theme.spacing(4)}
|
||||
color={theme.palette.primary.contrastTextTertiary}
|
||||
color={theme.palette.primary.contrastText}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Box, Stack } from "@mui/material";
|
||||
import Skeleton from "../../assets/Images/create-placeholder.svg?react";
|
||||
import SkeletonDark from "../../assets/Images/create-placeholder-dark.svg?react";
|
||||
import OutputAnimation from "../../assets/Animations/output.gif";
|
||||
import DarkmodeOutput from "../../assets/Animations/darkmodeOutput.gif";
|
||||
import Background from "../../assets/Images/background-grid.svg?react";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
@@ -37,11 +37,18 @@ const GenericFallback = ({ children }) => {
|
||||
marginTop: "100px",
|
||||
}}
|
||||
>
|
||||
{mode === "light" ? (
|
||||
<Skeleton style={{ zIndex: 1 }} />
|
||||
) : (
|
||||
<SkeletonDark style={{ zIndex: 1 }} />
|
||||
)}
|
||||
<Box
|
||||
component="img"
|
||||
src={mode === "light" ? OutputAnimation : DarkmodeOutput}
|
||||
Background="transparent"
|
||||
alt="Loading animation"
|
||||
sx={{
|
||||
zIndex: 1,
|
||||
border: "none",
|
||||
borderRadius: theme.spacing(8),
|
||||
width: "100%",
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
"& svg g g:last-of-type path": {
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
}
|
||||
} */
|
||||
|
||||
.home-layout aside {
|
||||
/* .home-layout aside {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
max-width: var(--env-var-side-bar-width);
|
||||
}
|
||||
} */
|
||||
|
||||
.home-layout > div {
|
||||
min-height: calc(100vh - var(--env-var-spacing-2) * 2);
|
||||
|
||||
286
client/src/Components/Sidebar/components/authFooter.jsx
Normal file
@@ -0,0 +1,286 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Avatar from "../../Avatar";
|
||||
import ThemeSwitch from "../../ThemeSwitch";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import DotsVertical from "../../../assets/icons/dots-vertical.svg?react";
|
||||
import LogoutSvg from "../../../assets/icons/logout.svg?react";
|
||||
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { clearAuthState } from "../../../Features/Auth/authSlice";
|
||||
import { useDispatch } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const getFilteredAccountMenuItems = (user, items) => {
|
||||
if (!user) return [];
|
||||
|
||||
let filtered = [...items];
|
||||
|
||||
if (user.role?.includes("demo")) {
|
||||
filtered = filtered.filter((item) => item.name !== "Password");
|
||||
}
|
||||
|
||||
if (!user.role?.includes("superadmin")) {
|
||||
filtered = filtered.filter((item) => item.name !== "Team");
|
||||
}
|
||||
|
||||
return filtered;
|
||||
};
|
||||
|
||||
const getRoleDisplayText = (user, t) => {
|
||||
if (!user?.role) return "";
|
||||
|
||||
if (user.role.includes("superadmin")) return t("roles.superAdmin");
|
||||
if (user.role.includes("admin")) return t("roles.admin");
|
||||
if (user.role.includes("user")) return t("roles.teamMember");
|
||||
if (user.role.includes("demo")) return t("roles.demoUser");
|
||||
|
||||
return user.role;
|
||||
};
|
||||
|
||||
const AuthFooter = ({ collapsed, accountMenuItems }) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const authState = useSelector((state) => state.auth);
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
|
||||
const openPopup = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const closePopup = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
dispatch(clearAuthState());
|
||||
navigate("/login");
|
||||
};
|
||||
const renderAccountMenuItems = (user, items) => {
|
||||
const filteredItems = getFilteredAccountMenuItems(user, items);
|
||||
|
||||
return filteredItems.map((item) => (
|
||||
<MenuItem
|
||||
key={item.name}
|
||||
onClick={() => {
|
||||
closePopup();
|
||||
navigate(item.path);
|
||||
}}
|
||||
sx={{
|
||||
gap: theme.spacing(2),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
pl: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
{item.icon}
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
));
|
||||
};
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
height="var(--env-var-side-bar-auth-footer-height)"
|
||||
alignItems="center"
|
||||
py={theme.spacing(4)}
|
||||
px={theme.spacing(8)}
|
||||
gap={theme.spacing(2)}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
boxSizing={"border-box"}
|
||||
>
|
||||
<Avatar
|
||||
small={true}
|
||||
onClick={(e) => collapsed && openPopup(e)}
|
||||
sx={{
|
||||
cursor: collapsed ? "pointer" : "default",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack
|
||||
direction={"row"}
|
||||
alignItems={"center"}
|
||||
gap={theme.spacing(2)}
|
||||
minWidth={0}
|
||||
maxWidth={collapsed ? 0 : "100%"}
|
||||
sx={{
|
||||
opacity: collapsed ? 0 : 1,
|
||||
transition: "opacity 300ms ease, max-width 300ms ease",
|
||||
transitionDelay: collapsed ? "0ms" : "300ms",
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
ml={theme.spacing(2)}
|
||||
sx={{
|
||||
maxWidth: "50%",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
color={theme.palette.primary.contrastText}
|
||||
fontWeight={500}
|
||||
lineHeight={1}
|
||||
fontSize={"var(--env-var-font-size-medium)"}
|
||||
sx={{
|
||||
display: "block",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Typography>
|
||||
<Typography
|
||||
color={theme.palette.primary.contrastText}
|
||||
fontSize={"var(--env-var-font-size-small)"}
|
||||
textOverflow="ellipsis"
|
||||
overflow="hidden"
|
||||
whiteSpace="nowrap"
|
||||
sx={{ textTransform: "capitalize", opacity: 0.8 }}
|
||||
>
|
||||
{getRoleDisplayText(authState.user, t)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
columnGap={theme.spacing(2)}
|
||||
>
|
||||
<ThemeSwitch color={theme.palette.primary.contrastTextTertiary} />
|
||||
<Tooltip
|
||||
title={t("navControls")}
|
||||
disableInteractive
|
||||
>
|
||||
<IconButton
|
||||
sx={{
|
||||
ml: "auto",
|
||||
mr: "-8px",
|
||||
"&:focus": { outline: "none" },
|
||||
alignSelf: "center",
|
||||
padding: "10px",
|
||||
|
||||
"& svg": {
|
||||
width: "22px",
|
||||
height: "22px",
|
||||
},
|
||||
"& svg path": {
|
||||
/* Vertical three dots */
|
||||
stroke: theme.palette.primary.contrastTextTertiary,
|
||||
},
|
||||
}}
|
||||
onClick={(event) => openPopup(event)}
|
||||
>
|
||||
<DotsVertical />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Menu
|
||||
className="sidebar-popup"
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={closePopup}
|
||||
disableScrollLock
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
marginTop: theme.spacing(-4),
|
||||
marginLeft: collapsed ? theme.spacing(2) : 0,
|
||||
},
|
||||
},
|
||||
}}
|
||||
MenuListProps={{
|
||||
sx: {
|
||||
p: 2,
|
||||
"& li": { m: 0 },
|
||||
"& li:has(.MuiBox-root):hover": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
ml: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
{collapsed && (
|
||||
<MenuItem sx={{ cursor: "default", minWidth: "50%" }}>
|
||||
<Box
|
||||
mb={theme.spacing(2)}
|
||||
sx={{
|
||||
minWidth: "50%",
|
||||
maxWidth: "max-content",
|
||||
overflow: "visible",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
fontWeight={500}
|
||||
fontSize={13}
|
||||
sx={{
|
||||
display: "block",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "visible",
|
||||
// wordBreak: "break-word",
|
||||
textOverflow: "clip",
|
||||
}}
|
||||
>
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
textTransform: "capitalize",
|
||||
fontSize: 12,
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "visible",
|
||||
// wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{authState.user?.role}
|
||||
</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
)}
|
||||
{/* TODO Do we need two dividers? */}
|
||||
{collapsed && <Divider />}
|
||||
{/* <Divider /> */}
|
||||
{renderAccountMenuItems(authState.user, accountMenuItems)}
|
||||
<MenuItem
|
||||
onClick={logout}
|
||||
sx={{
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
pl: theme.spacing(4),
|
||||
"& svg path": {
|
||||
stroke: theme.palette.primary.contrastTextTertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<LogoutSvg />
|
||||
{t("menu.logOut", "Log out")}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
AuthFooter.propTypes = {
|
||||
collapsed: PropTypes.bool,
|
||||
accountMenuItems: PropTypes.array,
|
||||
};
|
||||
|
||||
export default AuthFooter;
|
||||
55
client/src/Components/Sidebar/components/collapseButton.jsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import ArrowRight from "../../ArrowRight";
|
||||
import ArrowLeft from "../../ArrowLeft";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSidebar } from "../../../Features/UI/uiSlice";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const CollapseButton = ({ collapsed }) => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const arrowIcon = collapsed ? (
|
||||
<ArrowRight
|
||||
height={theme.spacing(8)}
|
||||
width={theme.spacing(8)}
|
||||
color={theme.palette.primary.contrastTextSecondary}
|
||||
/>
|
||||
) : (
|
||||
<ArrowLeft
|
||||
height={theme.spacing(8)}
|
||||
width={theme.spacing(8)}
|
||||
color={theme.palette.primary.contrastTextSecondary}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<IconButton
|
||||
sx={{
|
||||
position: "absolute",
|
||||
/* TODO 60 is a magic number. if logo chnges size this might break */
|
||||
top: 60,
|
||||
right: 0,
|
||||
transform: `translate(50%, 0)`,
|
||||
backgroundColor: theme.palette.tertiary.main,
|
||||
border: `1px solid ${theme.palette.primary.lowContrast}`,
|
||||
p: theme.spacing(2.5),
|
||||
|
||||
"&:focus": { outline: "none" },
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.primary.lowContrast,
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
dispatch(toggleSidebar());
|
||||
}}
|
||||
>
|
||||
{arrowIcon}
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
CollapseButton.propTypes = {
|
||||
collapsed: PropTypes.bool.isRequired,
|
||||
};
|
||||
export default CollapseButton;
|
||||
67
client/src/Components/Sidebar/components/logo.jsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const Logo = ({ collapsed }) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
pt={theme.spacing(6)}
|
||||
pb={theme.spacing(12)}
|
||||
pl={theme.spacing(8)}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(4)}
|
||||
onClick={() => navigate("/")}
|
||||
sx={{ cursor: "pointer" }}
|
||||
>
|
||||
<Typography
|
||||
pl={theme.spacing("1px")}
|
||||
minWidth={theme.spacing(16)}
|
||||
minHeight={theme.spacing(16)}
|
||||
display={"flex"}
|
||||
justifyContent={"center"}
|
||||
alignItems={"center"}
|
||||
backgroundColor={theme.palette.accent.main}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
color={theme.palette.accent.contrastText}
|
||||
fontSize={18}
|
||||
>
|
||||
C
|
||||
</Typography>
|
||||
<Box
|
||||
overflow={"hidden"}
|
||||
sx={{
|
||||
transition: "opacity 900ms ease, width 900ms ease",
|
||||
opacity: collapsed ? 0 : 1,
|
||||
whiteSpace: "nowrap",
|
||||
width: collapsed ? 0 : "100%",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
<Typography
|
||||
lineHeight={1}
|
||||
mt={theme.spacing(2)}
|
||||
color={theme.palette.accent.contrastText}
|
||||
fontSize={"var(--env-var-font-size-medium-plus)"}
|
||||
sx={{ opacity: 0.8, fontWeight: 500 }}
|
||||
>
|
||||
{t("common.appName")}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
Logo.propTypes = {
|
||||
collapsed: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Logo;
|
||||
97
client/src/Components/Sidebar/components/navItem.jsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const NavItem = ({ item, collapsed, selected, onClick }) => {
|
||||
const theme = useTheme();
|
||||
const iconStroke = selected
|
||||
? theme.palette.primary.contrastText
|
||||
: theme.palette.primary.contrastTextTertiary;
|
||||
|
||||
const buttonBgColor = selected ? theme.palette.secondary.main : "transparent";
|
||||
const buttonBgHoverColor = selected
|
||||
? theme.palette.secondary.main
|
||||
: theme.palette.tertiary.main;
|
||||
const fontWeight = selected ? 600 : 400;
|
||||
return (
|
||||
<Tooltip
|
||||
placement="right"
|
||||
title={collapsed ? item.name : ""}
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, -16],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
backgroundColor: buttonBgColor,
|
||||
"&:hover": {
|
||||
backgroundColor: buttonBgHoverColor,
|
||||
},
|
||||
height: 37,
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
pl: theme.spacing(5),
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: 0,
|
||||
"& svg": {
|
||||
height: 20,
|
||||
width: 20,
|
||||
opacity: 0.81,
|
||||
},
|
||||
"& svg path": {
|
||||
stroke: iconStroke,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{item.icon}
|
||||
</ListItemIcon>
|
||||
<Box
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
transition: "opacity 900ms ease",
|
||||
opacity: collapsed ? 0 : 1,
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="body1"
|
||||
color={theme.palette.primary.contrastText}
|
||||
sx={{
|
||||
fontWeight: fontWeight,
|
||||
opacity: 0.9,
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
</ListItemButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
NavItem.propTypes = {
|
||||
item: PropTypes.object,
|
||||
collapsed: PropTypes.bool,
|
||||
selected: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
export default NavItem;
|
||||
@@ -1,106 +0,0 @@
|
||||
/* TODO */
|
||||
aside .MuiList-root svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
aside span.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
line-height: 1;
|
||||
}
|
||||
aside .MuiStack-root + span.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium-plus);
|
||||
}
|
||||
aside .MuiListSubheader-root {
|
||||
font-size: var(--env-var-font-size-small);
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 2px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
aside p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-small);
|
||||
opacity: 0.8;
|
||||
}
|
||||
aside .MuiListItemButton-root:not(.selected-path) > * {
|
||||
opacity: 0.9;
|
||||
}
|
||||
aside .selected-path > * {
|
||||
opacity: 1;
|
||||
}
|
||||
aside .selected-path span.MuiTypography-root {
|
||||
font-weight: 600;
|
||||
}
|
||||
aside .MuiCollapse-wrapperInner .MuiList-root > .MuiListItemButton-root {
|
||||
position: relative;
|
||||
}
|
||||
aside .MuiCollapse-wrapperInner .MuiList-root svg,
|
||||
aside .MuiList-root .MuiListItemText-root + svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.sidebar-popup li.MuiButtonBase-root:has(.MuiBox-root) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.sidebar-popup svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* TRANSITIONS */
|
||||
aside {
|
||||
flex: 1;
|
||||
transition: max-width 650ms cubic-bezier(0.36, -0.01, 0, 0.77);
|
||||
}
|
||||
.home-layout aside.collapsed {
|
||||
max-width: 64px;
|
||||
}
|
||||
|
||||
aside.expanded .MuiTypography-root,
|
||||
aside.expanded p.MuiTypography-root,
|
||||
aside.expanded .MuiListItemText-root + svg,
|
||||
aside.expanded .MuiAvatar-root + .MuiBox-root + .MuiIconButton-root {
|
||||
visibility: visible;
|
||||
animation: fadeIn 1s ease;
|
||||
}
|
||||
|
||||
aside.collapsed .MuiTypography-root,
|
||||
aside.collapsed p.MuiTypography-root,
|
||||
aside.collapsed .MuiListItemText-root + svg,
|
||||
aside.collapsed .MuiAvatar-root + .MuiBox-root + .MuiIconButton-root {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
aside .MuiListSubheader-root {
|
||||
transition: padding 200ms ease;
|
||||
}
|
||||
|
||||
.sidebar-delay-fade {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
aside.expanded.sidebar-ready .sidebar-delay-fade {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
30% {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.9;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,16 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import {
|
||||
Box,
|
||||
Collapse,
|
||||
Divider,
|
||||
IconButton,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Stack,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import ThemeSwitch from "../ThemeSwitch";
|
||||
import Avatar from "../Avatar";
|
||||
import Stack from "@mui/material/Stack";
|
||||
|
||||
import List from "@mui/material/List";
|
||||
import Logo from "./components/logo";
|
||||
import CollapseButton from "./components/collapseButton";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import NavItem from "./components/navItem";
|
||||
import AuthFooter from "./components/authFooter";
|
||||
|
||||
import StarPrompt from "../StarPrompt";
|
||||
import LockSvg from "../../assets/icons/lock.svg?react";
|
||||
import UserSvg from "../../assets/icons/user.svg?react";
|
||||
import TeamSvg from "../../assets/icons/user-two.svg?react";
|
||||
import LogoutSvg from "../../assets/icons/logout.svg?react";
|
||||
import Support from "../../assets/icons/support.svg?react";
|
||||
import Maintenance from "../../assets/icons/maintenance.svg?react";
|
||||
import Monitors from "../../assets/icons/monitors.svg?react";
|
||||
@@ -28,11 +18,6 @@ import Incidents from "../../assets/icons/incidents.svg?react";
|
||||
import Integrations from "../../assets/icons/integrations.svg?react";
|
||||
import PageSpeed from "../../assets/icons/page-speed.svg?react";
|
||||
import Settings from "../../assets/icons/settings.svg?react";
|
||||
import ArrowDown from "../../assets/icons/down-arrow.svg?react";
|
||||
import ArrowUp from "../../assets/icons/up-arrow.svg?react";
|
||||
import ArrowRight from "../../assets/icons/right-arrow.svg?react";
|
||||
import ArrowLeft from "../../assets/icons/left-arrow.svg?react";
|
||||
import DotsVertical from "../../assets/icons/dots-vertical.svg?react";
|
||||
import ChangeLog from "../../assets/icons/changeLog.svg?react";
|
||||
import Docs from "../../assets/icons/docs.svg?react";
|
||||
import StatusPages from "../../assets/icons/status-pages.svg?react";
|
||||
@@ -40,17 +25,18 @@ import Discussions from "../../assets/icons/discussions.svg?react";
|
||||
import Notifications from "../../assets/icons/notifications.svg?react";
|
||||
import Logs from "../../assets/icons/logs.svg?react";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
// Utils
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { clearAuthState } from "../../Features/Auth/authSlice";
|
||||
import { toggleSidebar } from "../../Features/UI/uiSlice";
|
||||
import { TurnedIn } from "@mui/icons-material";
|
||||
import { rules } from "eslint-plugin-react-refresh";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
const URL_MAP = {
|
||||
support: "https://discord.com/invite/NAb6H3UTjK",
|
||||
discussions: "https://github.com/bluewave-labs/checkmate/discussions",
|
||||
docs: "https://bluewavelabs.gitbook.io/checkmate",
|
||||
changelog: "https://github.com/bluewave-labs/checkmate/releases",
|
||||
};
|
||||
|
||||
const getMenu = (t) => [
|
||||
{ name: t("menu.uptime"), path: "uptime", icon: <Monitors /> },
|
||||
@@ -92,782 +78,94 @@ const getAccountMenuItems = (t) => [
|
||||
{ name: t("menu.team"), path: "account/team", icon: <TeamSvg /> },
|
||||
];
|
||||
|
||||
/* TODO this could be a key in nested Path would be the link */
|
||||
const URL_MAP = {
|
||||
support: "https://discord.com/invite/NAb6H3UTjK",
|
||||
discussions: "https://github.com/bluewave-labs/checkmate/discussions",
|
||||
docs: "https://bluewavelabs.gitbook.io/checkmate",
|
||||
changelog: "https://github.com/bluewave-labs/checkmate/releases",
|
||||
};
|
||||
|
||||
const PATH_MAP = {
|
||||
monitors: "Dashboard",
|
||||
pagespeed: "Dashboard",
|
||||
infrastructure: "Dashboard",
|
||||
account: "Account",
|
||||
settings: "Settings",
|
||||
};
|
||||
|
||||
/**
|
||||
* @component
|
||||
* Sidebar component serves as a sidebar containing a menu.
|
||||
*
|
||||
* @returns {JSX.Element} The JSX element representing the Sidebar component.
|
||||
*/
|
||||
|
||||
function Sidebar() {
|
||||
const Sidebar = () => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const authState = useSelector((state) => state.auth);
|
||||
const navigate = useNavigate();
|
||||
// Redux state
|
||||
const collapsed = useSelector((state) => state.ui.sidebar.collapsed);
|
||||
|
||||
const menu = getMenu(t);
|
||||
const otherMenuItems = getOtherMenuItems(t);
|
||||
const accountMenuItems = getAccountMenuItems(t);
|
||||
const collapsed = useSelector((state) => state.ui.sidebar.collapsed);
|
||||
const [open, setOpen] = useState({ Dashboard: false, Account: false, Other: false });
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [popup, setPopup] = useState();
|
||||
const { user } = useSelector((state) => state.auth);
|
||||
|
||||
const sidebarRef = useRef(null);
|
||||
const [sidebarReady, setSidebarReady] = useState(false);
|
||||
const TRANSITION_DURATION = 200;
|
||||
let menu = getMenu(t);
|
||||
menu = menu.filter((item) => {
|
||||
if (item.path === "logs") {
|
||||
return user.role?.includes("admin") || user.role?.includes("superadmin");
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!collapsed) {
|
||||
setSidebarReady(false);
|
||||
const timeout = setTimeout(() => {
|
||||
setSidebarReady(true);
|
||||
}, TRANSITION_DURATION);
|
||||
return () => clearTimeout(timeout);
|
||||
} else {
|
||||
setSidebarReady(false);
|
||||
}
|
||||
}, [collapsed]);
|
||||
|
||||
const renderAccountMenuItems = () => {
|
||||
let filteredAccountMenuItems = [...accountMenuItems];
|
||||
|
||||
// If the user is in demo mode, remove the "Password" option
|
||||
if (user.role?.includes("demo")) {
|
||||
filteredAccountMenuItems = filteredAccountMenuItems.filter(
|
||||
(item) => item.name !== "Password"
|
||||
);
|
||||
}
|
||||
|
||||
// If the user is NOT a superadmin, remove the "Team" option
|
||||
if (user.role && !user.role.includes("superadmin")) {
|
||||
filteredAccountMenuItems = filteredAccountMenuItems.filter(
|
||||
(item) => item.name !== "Team"
|
||||
);
|
||||
}
|
||||
|
||||
return filteredAccountMenuItems.map((item) => (
|
||||
<MenuItem
|
||||
key={item.name}
|
||||
onClick={() => {
|
||||
closePopup();
|
||||
navigate(item.path);
|
||||
}}
|
||||
sx={{
|
||||
gap: theme.spacing(2),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
pl: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
{item.icon}
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
));
|
||||
};
|
||||
|
||||
const openPopup = (event, id) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
setPopup(id);
|
||||
};
|
||||
const closePopup = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles logging out the user
|
||||
*
|
||||
*/
|
||||
const logout = async () => {
|
||||
// Clear auth state
|
||||
dispatch(clearAuthState());
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const matchedKey = Object.keys(PATH_MAP).find((key) =>
|
||||
location.pathname.includes(key)
|
||||
);
|
||||
|
||||
if (matchedKey) {
|
||||
setOpen((prev) => ({ ...prev, [PATH_MAP[matchedKey]]: true }));
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
const iconColor = theme.palette.primary.contrastTextTertiary;
|
||||
const sidebarClassName = `${collapsed ? "collapsed" : "expanded"} ${sidebarReady ? "sidebar-ready" : ""}`;
|
||||
|
||||
/* TODO refactor this, there are a some ternaries and comments in the return */
|
||||
return (
|
||||
<Stack
|
||||
height="100vh"
|
||||
width={
|
||||
collapsed
|
||||
? "var(--env-var-side-bar-collapsed-width)"
|
||||
: "var(--env-var-side-bar-width)"
|
||||
}
|
||||
component="aside"
|
||||
ref={sidebarRef}
|
||||
className={sidebarClassName}
|
||||
/* TODO general padding should be here */
|
||||
py={theme.spacing(6)}
|
||||
position="sticky"
|
||||
top={0}
|
||||
borderRight={`1px solid ${theme.palette.primary.lowContrast}`}
|
||||
paddingTop={theme.spacing(6)}
|
||||
paddingBottom={theme.spacing(6)}
|
||||
gap={theme.spacing(6)}
|
||||
/* TODO set all style in this sx if possible (when general)
|
||||
This is the top lever for styles
|
||||
*/
|
||||
sx={{
|
||||
position: "relative",
|
||||
borderRight: `1px solid ${theme.palette.primary.lowContrast}`,
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
borderRadius: 0,
|
||||
"& :is(p, span, .MuiListSubheader-root)": {
|
||||
/*
|
||||
Text color for unselected menu items and menu headings
|
||||
Secondary contrast text against main background
|
||||
*/
|
||||
color: theme.palette.primary.contrastTextSecondary,
|
||||
},
|
||||
"& .MuiList-root svg path": {
|
||||
/* Menu Icons */
|
||||
stroke: iconColor,
|
||||
},
|
||||
"& .selected-path": {
|
||||
/* Selected menu item */
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
},
|
||||
"& .MuiListItemIcon-root svg path": {
|
||||
/* Selected menu item icon */
|
||||
stroke: theme.palette.secondary.contrastText,
|
||||
},
|
||||
"& .MuiListItemText-root :is(p, span)": {
|
||||
/* Selected menu item text */
|
||||
color: theme.palette.secondary.contrastText,
|
||||
},
|
||||
},
|
||||
"& .MuiListItemButton-root:not(.selected-path)": {
|
||||
transition: "background-color .3s",
|
||||
" &:hover": {
|
||||
/* Hovered menu item bg color */
|
||||
backgroundColor: theme.palette.tertiary.main,
|
||||
"& :is(p, span)": {
|
||||
/* Hovered menu item text color */
|
||||
color: theme.palette.tertiary.contrastText,
|
||||
},
|
||||
},
|
||||
},
|
||||
transition: "width 650ms cubic-bezier(0.36, -0.01, 0, 0.77)",
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
<CollapseButton collapsed={collapsed} />
|
||||
<Logo collapsed={collapsed} />
|
||||
<List
|
||||
component="nav"
|
||||
aria-labelledby="nested-menu-subheader"
|
||||
disablePadding
|
||||
sx={{
|
||||
position: "absolute",
|
||||
/* TODO 60 is a magic number. if logo chnges size this might break */
|
||||
top: 60,
|
||||
right: 0,
|
||||
transform: `translate(50%, 0)`,
|
||||
backgroundColor: theme.palette.tertiary.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
p: theme.spacing(2.5),
|
||||
"& svg": {
|
||||
width: theme.spacing(8),
|
||||
height: theme.spacing(8),
|
||||
"& path": {
|
||||
/* TODO this should be set at the top level if possible */
|
||||
stroke: theme.palette.primary.contrastTextSecondary,
|
||||
},
|
||||
},
|
||||
"&:focus": { outline: "none" },
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.primary.lowContrast,
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
},
|
||||
px: theme.spacing(6),
|
||||
height: "100%",
|
||||
}}
|
||||
onClick={() => {
|
||||
setOpen((prev) =>
|
||||
Object.fromEntries(Object.keys(prev).map((key) => [key, false]))
|
||||
>
|
||||
{menu.map((item) => {
|
||||
const selected = location.pathname.startsWith(`/${item.path}`);
|
||||
return (
|
||||
<NavItem
|
||||
key={item.path}
|
||||
item={item}
|
||||
collapsed={collapsed}
|
||||
selected={selected}
|
||||
onClick={() => navigate(`/${item.path}`)}
|
||||
/>
|
||||
);
|
||||
dispatch(toggleSidebar());
|
||||
}}
|
||||
>
|
||||
{collapsed ? <ArrowRight /> : <ArrowLeft />}
|
||||
</IconButton>
|
||||
{/* TODO Alignment done using padding. Use single source of truth to that*/}
|
||||
<Stack
|
||||
pt={theme.spacing(6)}
|
||||
pb={theme.spacing(12)}
|
||||
pl={theme.spacing(8)}
|
||||
>
|
||||
{/* TODO Abstract logo into component */}
|
||||
{/* TODO Turn logo into a link */}
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(4)}
|
||||
onClick={() => navigate("/")}
|
||||
sx={{ cursor: "pointer" }}
|
||||
>
|
||||
<Stack
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
minWidth={theme.spacing(16)}
|
||||
minHeight={theme.spacing(16)}
|
||||
pl="1px"
|
||||
fontSize={18}
|
||||
color={theme.palette.accent.contrastText}
|
||||
sx={{
|
||||
position: "relative",
|
||||
backgroundColor: theme.palette.accent.main,
|
||||
color: theme.palette.accent.contrastText,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
C
|
||||
</Stack>
|
||||
<Typography
|
||||
component="span"
|
||||
mt={theme.spacing(2)}
|
||||
sx={{ opacity: 0.8, fontWeight: 500 }}
|
||||
>
|
||||
{t("common.appName")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
overflow: "auto",
|
||||
overflowX: "hidden",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: theme.spacing(2),
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: theme.palette.primary.lowContrast,
|
||||
borderRadius: theme.spacing(4),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<List
|
||||
component="nav"
|
||||
aria-labelledby="nested-menu-subheader"
|
||||
disablePadding
|
||||
sx={{
|
||||
px: theme.spacing(6),
|
||||
height: "100%",
|
||||
/* overflow: "hidden", */
|
||||
}}
|
||||
>
|
||||
{menu.map((item) => {
|
||||
return item.path ? (
|
||||
/* If item has a path */
|
||||
<Tooltip
|
||||
key={item.path}
|
||||
placement="right"
|
||||
title={collapsed ? item.name : ""}
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, -16],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<ListItemButton
|
||||
className={
|
||||
location.pathname.startsWith(`/${item.path}`) ? "selected-path" : ""
|
||||
}
|
||||
onClick={() => navigate(`/${item.path}`)}
|
||||
sx={{
|
||||
height: "37px",
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
pl: theme.spacing(5),
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
||||
<ListItemText>{item.name}</ListItemText>
|
||||
</ListItemButton>
|
||||
</Tooltip>
|
||||
) : collapsed ? (
|
||||
/* TODO Do we ever get here? If item does not have a path and collapsed state is true */
|
||||
<React.Fragment key={item.name}>
|
||||
<Tooltip
|
||||
placement="right"
|
||||
title={collapsed ? item.name : ""}
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, -16],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<ListItemButton
|
||||
className={
|
||||
Boolean(anchorEl) && popup === item.name ? "selected-path" : ""
|
||||
}
|
||||
onClick={(event) => openPopup(event, item.name)}
|
||||
sx={{
|
||||
position: "relative",
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
||||
<ListItemText>{item.name}</ListItemText>
|
||||
</ListItemButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
className="sidebar-popup"
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl) && popup === item.name}
|
||||
onClose={closePopup}
|
||||
disableScrollLock
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
mt: theme.spacing(-2),
|
||||
ml: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
}}
|
||||
MenuListProps={{ sx: { px: 1, py: 2 } }}
|
||||
sx={{
|
||||
ml: theme.spacing(8),
|
||||
/* TODO what is this selection? */
|
||||
"& .selected-path": {
|
||||
backgroundColor: theme.palette.tertiary.main,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{item.nested.map((child) => {
|
||||
if (
|
||||
child.name === "Team" &&
|
||||
authState.user?.role &&
|
||||
!authState.user.role.includes("superadmin")
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
className={
|
||||
location.pathname.includes(child.path) ? "selected-path" : ""
|
||||
}
|
||||
key={child.path}
|
||||
onClick={() => {
|
||||
const url = URL_MAP[child.path];
|
||||
if (url) {
|
||||
window.open(url, "_blank", "noreferrer");
|
||||
} else {
|
||||
navigate(`/${child.path}`);
|
||||
}
|
||||
closePopup();
|
||||
}}
|
||||
sx={{
|
||||
gap: theme.spacing(4),
|
||||
opacity: 0.9,
|
||||
/* TODO this has no effect? */
|
||||
"& svg": {
|
||||
"& path": {
|
||||
stroke: theme.palette.primary.contrastTextTertiary,
|
||||
strokeWidth: 1.1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{child.icon}
|
||||
{child.name}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
/* TODO Do we ever get here? If item does not have a path and collapsed state is false */
|
||||
<React.Fragment key={item.name}>
|
||||
<ListItemButton
|
||||
onClick={() =>
|
||||
setOpen((prev) => ({
|
||||
...Object.fromEntries(Object.keys(prev).map((key) => [key, false])),
|
||||
[item.name]: !prev[item.name],
|
||||
}))
|
||||
}
|
||||
sx={{
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
||||
<ListItemText>{item.name}</ListItemText>
|
||||
{open[`${item.name}`] ? <ArrowUp /> : <ArrowDown />}
|
||||
</ListItemButton>
|
||||
<Collapse
|
||||
in={open[`${item.name}`]}
|
||||
timeout="auto"
|
||||
>
|
||||
<List
|
||||
component="div"
|
||||
disablePadding
|
||||
sx={{ pl: theme.spacing(12) }}
|
||||
>
|
||||
{item.nested.map((child) => {
|
||||
if (
|
||||
child.name === "Team" &&
|
||||
authState.user?.role &&
|
||||
!authState.user.role.includes("superadmin")
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItemButton
|
||||
className={
|
||||
location.pathname.includes(child.path) ? "selected-path" : ""
|
||||
}
|
||||
key={child.path}
|
||||
onClick={() => {
|
||||
const url = URL_MAP[child.path];
|
||||
if (url) {
|
||||
window.open(url, "_blank", "noreferrer");
|
||||
} else {
|
||||
navigate(`/${child.path}`);
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
pl: theme.spacing(4),
|
||||
"&::before": {
|
||||
content: `""`,
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: "-7px",
|
||||
height: "100%",
|
||||
borderLeft: 1,
|
||||
borderLeftColor: theme.palette.primary.lowContrast,
|
||||
},
|
||||
"&:last-child::before": {
|
||||
height: "50%",
|
||||
},
|
||||
"&::after": {
|
||||
content: `""`,
|
||||
position: "absolute",
|
||||
top: "45%",
|
||||
left: "-8px",
|
||||
height: "3px",
|
||||
width: "3px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: theme.palette.primary.lowContrast,
|
||||
},
|
||||
"&.selected-path::after": {
|
||||
/* TODO what is this selector doing? */
|
||||
backgroundColor: theme.palette.primary.contrastTextTertiary,
|
||||
transform: "scale(1.2)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{child.icon}</ListItemIcon>
|
||||
<ListItemText>{child.name}</ListItemText>
|
||||
</ListItemButton>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Collapse>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
|
||||
})}
|
||||
</List>
|
||||
{!collapsed && <StarPrompt />}
|
||||
|
||||
<List
|
||||
component="nav"
|
||||
disablePadding
|
||||
sx={{ px: theme.spacing(6) }}
|
||||
>
|
||||
{otherMenuItems.map((item) => {
|
||||
return item.path ? (
|
||||
<Tooltip
|
||||
const selected = location.pathname.startsWith(`/${item.path}`);
|
||||
|
||||
return (
|
||||
<NavItem
|
||||
key={item.path}
|
||||
placement="right"
|
||||
title={collapsed ? item.name : ""}
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, -16],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<ListItemButton
|
||||
className={
|
||||
location.pathname.startsWith(`/${item.path}`) ? "selected-path" : ""
|
||||
item={item}
|
||||
collapsed={collapsed}
|
||||
selected={selected}
|
||||
onClick={() => {
|
||||
const url = URL_MAP[item.path];
|
||||
if (url) {
|
||||
window.open(url, "_blank", "noreferrer");
|
||||
} else {
|
||||
navigate(`/${item.path}`);
|
||||
}
|
||||
onClick={() => {
|
||||
const url = URL_MAP[item.path];
|
||||
if (url) {
|
||||
window.open(url, "_blank", "noreferrer");
|
||||
} else {
|
||||
navigate(`/${item.path}`);
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
height: "37px",
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
pl: theme.spacing(5),
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon} </ListItemIcon>
|
||||
<ListItemText>{item.name} </ListItemText>{" "}
|
||||
</ListItemButton>
|
||||
</Tooltip>
|
||||
) : null;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
<Divider sx={{ mt: "auto", borderColor: theme.palette.primary.lowContrast }} />
|
||||
<Stack
|
||||
direction="row"
|
||||
height="50px"
|
||||
alignItems="center"
|
||||
py={theme.spacing(4)}
|
||||
px={theme.spacing(8)}
|
||||
gap={theme.spacing(2)}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
>
|
||||
{collapsed ? (
|
||||
<>
|
||||
<Tooltip
|
||||
title="Options"
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, -10],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<IconButton
|
||||
onClick={(event) => openPopup(event, "logout")}
|
||||
sx={{ p: 0, "&:focus": { outline: "none" } }}
|
||||
>
|
||||
<Avatar small={true} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Avatar small={true} />
|
||||
<Box
|
||||
ml={theme.spacing(2)}
|
||||
sx={{ maxWidth: "50%", overflow: "hidden" }}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
fontWeight={500}
|
||||
sx={{
|
||||
display: "block",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Typography>
|
||||
<Typography sx={{ textTransform: "capitalize" }}>
|
||||
{authState.user?.role?.includes("superadmin")
|
||||
? t("roles.superAdmin")
|
||||
: authState.user?.role?.includes("admin")
|
||||
? t("roles.admin")
|
||||
: authState.user?.role?.includes("user")
|
||||
? t("roles.teamMember")
|
||||
: authState.user?.role?.includes("demo")
|
||||
? t("roles.demoUser")
|
||||
: authState.user?.role}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack
|
||||
className="sidebar-delay-fade"
|
||||
flexDirection={"row"}
|
||||
marginLeft={"auto"}
|
||||
columnGap={theme.spacing(2)}
|
||||
>
|
||||
<ThemeSwitch color={iconColor} />
|
||||
<Tooltip
|
||||
title={t("navControls")}
|
||||
disableInteractive
|
||||
>
|
||||
<IconButton
|
||||
sx={{
|
||||
ml: "auto",
|
||||
mr: "-8px",
|
||||
"&:focus": { outline: "none" },
|
||||
alignSelf: "center",
|
||||
padding: "10px",
|
||||
|
||||
"& svg": {
|
||||
width: "22px",
|
||||
height: "22px",
|
||||
},
|
||||
"& svg path": {
|
||||
/* Vertical three dots */
|
||||
stroke: iconColor,
|
||||
},
|
||||
}}
|
||||
onClick={(event) => openPopup(event, "logout")}
|
||||
>
|
||||
<DotsVertical />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
<Menu
|
||||
className="sidebar-popup"
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl) && popup === "logout"}
|
||||
onClose={closePopup}
|
||||
disableScrollLock
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
marginTop: theme.spacing(-4),
|
||||
marginLeft: collapsed ? theme.spacing(2) : 0,
|
||||
},
|
||||
},
|
||||
}}
|
||||
MenuListProps={{
|
||||
sx: {
|
||||
p: 2,
|
||||
"& li": { m: 0 },
|
||||
"& li:has(.MuiBox-root):hover": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
ml: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
{collapsed && (
|
||||
<MenuItem sx={{ cursor: "default", minWidth: "50%" }}>
|
||||
<Box
|
||||
mb={theme.spacing(2)}
|
||||
sx={{
|
||||
minWidth: "50%",
|
||||
maxWidth: "max-content",
|
||||
overflow: "visible",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
fontWeight={500}
|
||||
fontSize={13}
|
||||
sx={{
|
||||
display: "block",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "visible",
|
||||
// wordBreak: "break-word",
|
||||
textOverflow: "clip",
|
||||
}}
|
||||
>
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
textTransform: "capitalize",
|
||||
fontSize: 12,
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "visible",
|
||||
// wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{authState.user?.role}
|
||||
</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
)}
|
||||
{/* TODO Do we need two dividers? */}
|
||||
{collapsed && <Divider />}
|
||||
{/* <Divider /> */}
|
||||
{renderAccountMenuItems()}
|
||||
<MenuItem
|
||||
onClick={logout}
|
||||
sx={{
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
pl: theme.spacing(4),
|
||||
"& svg path": {
|
||||
stroke: iconColor,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<LogoutSvg />
|
||||
{t("menu.logOut", "Log out")}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Stack>
|
||||
<AuthFooter
|
||||
collapsed={collapsed}
|
||||
accountMenuItems={accountMenuItems}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { Box, Button } from "@mui/material";
|
||||
import LeftArrowDouble from "../../../../assets/icons/left-arrow-double.svg?react";
|
||||
import RightArrowDouble from "../../../../assets/icons/right-arrow-double.svg?react";
|
||||
import LeftArrow from "../../../../assets/icons/left-arrow.svg?react";
|
||||
import RightArrow from "../../../../assets/icons/right-arrow.svg?react";
|
||||
import LeftArrow from "../../../ArrowLeft";
|
||||
import RightArrow from "../../../ArrowRight";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
TablePaginationActions.propTypes = {
|
||||
@@ -50,7 +48,7 @@ function TablePaginationActions({ count, page, rowsPerPage, onPageChange }) {
|
||||
disabled={page === 0}
|
||||
aria-label="first page"
|
||||
>
|
||||
<LeftArrowDouble />
|
||||
<LeftArrow type="double" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
@@ -74,7 +72,7 @@ function TablePaginationActions({ count, page, rowsPerPage, onPageChange }) {
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label="last page"
|
||||
>
|
||||
<RightArrowDouble />
|
||||
<RightArrow type="double" />
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -70,6 +70,9 @@ const DataTable = ({
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastTextSecondary,
|
||||
},
|
||||
"& .MuiTableBody-root .MuiTableRow-root:last-child .MuiTableCell-root": {
|
||||
borderBottom: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TableHead>
|
||||
|
||||
@@ -300,6 +300,28 @@ const useCreateMonitor = () => {
|
||||
return [createMonitor, isLoading];
|
||||
};
|
||||
|
||||
const useFetchGlobalSettings = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [globalSettings, setGlobalSettings] = useState(undefined);
|
||||
useEffect(() => {
|
||||
const fetchGlobalSettings = async () => {
|
||||
try {
|
||||
const res = await networkService.getAppSettings();
|
||||
setGlobalSettings(res?.data);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch global settings:", error);
|
||||
createToast({ body: "Failed to load global settings" });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchGlobalSettings();
|
||||
}, []);
|
||||
|
||||
return [globalSettings, isLoading];
|
||||
};
|
||||
|
||||
const useDeleteMonitor = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
@@ -499,6 +521,7 @@ export {
|
||||
useFetchUptimeMonitorById,
|
||||
useFetchHardwareMonitorById,
|
||||
useCreateMonitor,
|
||||
useFetchGlobalSettings,
|
||||
useDeleteMonitor,
|
||||
useUpdateMonitor,
|
||||
usePauseMonitor,
|
||||
|
||||
@@ -7,7 +7,9 @@ import { PasswordEndAdornment } from "../../../Components/Inputs/TextInput/Adorn
|
||||
import { loginCredentials } from "../../../Validation/validation";
|
||||
import TextLink from "../../../Components/TextLink";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
import Logo from "../../../assets/icons/checkmate-icon.svg?react";
|
||||
import Background from "../../../assets/Images/background-grid.svg?react";
|
||||
// Utils
|
||||
import { login } from "../../../Features/Auth/authSlice";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
@@ -92,34 +94,105 @@ const Login = () => {
|
||||
<Stack
|
||||
gap={theme.spacing(10)}
|
||||
minHeight="100vh"
|
||||
position="relative"
|
||||
backgroundColor={theme.palette.primary.main}
|
||||
sx={{ overflow: "hidden" }}
|
||||
>
|
||||
<AuthHeader />
|
||||
<Stack
|
||||
margin="auto"
|
||||
width="100%"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(10)}
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: "0%",
|
||||
transform: "translate(-40%, -40%)",
|
||||
zIndex: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
"& svg g g:last-of-type path": {
|
||||
stroke: theme.palette.primary.lowContrast,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography variant="h1">{t("auth.login.heading")}</Typography>
|
||||
<Background style={{ width: "100%" }} />
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
transform: "translate(45%, 55%)",
|
||||
zIndex: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
"& svg g g:last-of-type path": {
|
||||
stroke: theme.palette.primary.lowContrast,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Background style={{ width: "100%" }} />
|
||||
</Box>
|
||||
<AuthHeader hideLogo={true} />
|
||||
|
||||
<Stack
|
||||
backgroundColor={theme.palette.primary.main}
|
||||
sx={{
|
||||
borderRadius: theme.spacing(8),
|
||||
boxShadow: theme.palette.tertiary.cardShadow,
|
||||
margin: "auto",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(10),
|
||||
padding: theme.spacing(20),
|
||||
zIndex: 1,
|
||||
position: "relative",
|
||||
width: {
|
||||
sm: "60%",
|
||||
md: "50%",
|
||||
lg: "40%",
|
||||
xl: "30%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
mb={theme.spacing(10)}
|
||||
mt={theme.spacing(5)}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: { xs: 60, sm: 80, md: 90 },
|
||||
}}
|
||||
/>
|
||||
<Logo style={{ width: "100%", height: "100%" }} />
|
||||
</Box>
|
||||
<Stack
|
||||
mb={theme.spacing(12)}
|
||||
textAlign="center"
|
||||
>
|
||||
<Typography
|
||||
variant="h1"
|
||||
mb={theme.spacing(2)}
|
||||
>
|
||||
{t("auth.login.welcome")}
|
||||
</Typography>
|
||||
<Typography variant="h1">{t("auth.login.heading")}</Typography>
|
||||
</Stack>
|
||||
<Stack
|
||||
component="form"
|
||||
width="100%"
|
||||
maxWidth={600}
|
||||
alignSelf="center"
|
||||
justifyContent="center"
|
||||
borderRadius={theme.spacing(5)}
|
||||
borderColor={theme.palette.primary.lowContrast}
|
||||
backgroundColor={theme.palette.primary.main}
|
||||
padding={theme.spacing(12)}
|
||||
padding={theme.spacing(8)}
|
||||
gap={theme.spacing(12)}
|
||||
onSubmit={onSubmit}
|
||||
sx={{
|
||||
width: {
|
||||
sm: "80%",
|
||||
md: "70%",
|
||||
lg: "65%",
|
||||
xl: "65%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TextInput
|
||||
type="email"
|
||||
name="email"
|
||||
label={t("auth.common.inputs.email.label")}
|
||||
isRequired={true}
|
||||
placeholder={t("auth.common.inputs.email.placeholder")}
|
||||
autoComplete="email"
|
||||
value={form.email}
|
||||
@@ -131,7 +204,6 @@ const Login = () => {
|
||||
type="password"
|
||||
name="password"
|
||||
label={t("auth.common.inputs.password.label")}
|
||||
isRequired={true}
|
||||
placeholder="••••••••••"
|
||||
autoComplete="current-password"
|
||||
value={form.password}
|
||||
@@ -144,7 +216,7 @@ const Login = () => {
|
||||
variant="contained"
|
||||
color="accent"
|
||||
type="submit"
|
||||
sx={{ width: "30%", alignSelf: "flex-end" }}
|
||||
sx={{ width: "100%", alignSelf: "center", fontWeight: 700 }}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
|
||||
@@ -9,7 +9,7 @@ import ThemeSwitch from "../../../Components/ThemeSwitch";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const AuthHeader = () => {
|
||||
const AuthHeader = ({ hideLogo = false }) => {
|
||||
// Hooks
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
@@ -27,8 +27,12 @@ const AuthHeader = () => {
|
||||
alignItems="center"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>{t("common.appName")}</Typography>
|
||||
{!hideLogo && (
|
||||
<>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>{t("common.appName")}</Typography>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
|
||||
@@ -2,18 +2,22 @@ import PropTypes from "prop-types";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import Background from "../../../../assets/Images/background-grid.svg?react";
|
||||
import MonitorHeartOutlinedIcon from "@mui/icons-material/MonitorHeartOutlined";
|
||||
import TaskAltOutlinedIcon from "@mui/icons-material/TaskAltOutlined";
|
||||
import CancelOutlinedIcon from "@mui/icons-material/CancelOutlined";
|
||||
import WarningAmberRoundedIcon from "@mui/icons-material/WarningAmberRounded";
|
||||
import AlertIcon from "../../../../assets/icons/alert-icon.svg?react";
|
||||
import CheckIcon from "../../../../assets/icons/check-icon.svg?react";
|
||||
import CloseIcon from "../../../../assets/icons/close-icon.svg?react";
|
||||
import WarningIcon from "../../../../assets/icons/warning-icon.svg?react";
|
||||
|
||||
const StatusBox = ({ title, value, status }) => {
|
||||
const theme = useTheme();
|
||||
let sharedStyles = {
|
||||
position: "absolute",
|
||||
right: 8,
|
||||
opacity: 0.5,
|
||||
"& svg path": { stroke: theme.palette.primary.contrastTextTertiary },
|
||||
"& svg": {
|
||||
width: 20,
|
||||
height: 20,
|
||||
opacity: 0.9,
|
||||
"& path": { stroke: theme.palette.primary.contrastTextTertiary, strokeWidth: 1.7 },
|
||||
},
|
||||
};
|
||||
|
||||
let color;
|
||||
@@ -22,28 +26,28 @@ const StatusBox = ({ title, value, status }) => {
|
||||
color = theme.palette.success.lowContrast;
|
||||
icon = (
|
||||
<Box sx={{ ...sharedStyles, top: theme.spacing(6), right: theme.spacing(6) }}>
|
||||
<TaskAltOutlinedIcon fontSize="small" />
|
||||
<CheckIcon />
|
||||
</Box>
|
||||
);
|
||||
} else if (status === "down") {
|
||||
color = theme.palette.error.lowContrast;
|
||||
icon = (
|
||||
<Box sx={{ ...sharedStyles, top: theme.spacing(6), right: theme.spacing(6) }}>
|
||||
<CancelOutlinedIcon fontSize="small" />
|
||||
<CloseIcon />
|
||||
</Box>
|
||||
);
|
||||
} else if (status === "paused") {
|
||||
color = theme.palette.warning.lowContrast;
|
||||
icon = (
|
||||
<Box sx={{ ...sharedStyles, top: theme.spacing(6), right: theme.spacing(6) }}>
|
||||
<WarningAmberRoundedIcon fontSize="small" />
|
||||
<WarningIcon />
|
||||
</Box>
|
||||
);
|
||||
} else {
|
||||
color = theme.palette.accent.main;
|
||||
icon = (
|
||||
<Box sx={{ ...sharedStyles, top: theme.spacing(6), right: theme.spacing(6) }}>
|
||||
<MonitorHeartOutlinedIcon fontSize="small" />
|
||||
<AlertIcon />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,74 +1,51 @@
|
||||
// React, Redux, Router
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
// Utility and Network
|
||||
import { infrastructureMonitorValidation } from "../../../Validation/validation";
|
||||
import { useFetchHardwareMonitorById } from "../../../Hooks/monitorHooks";
|
||||
import { capitalizeFirstLetter } from "../../../Utils/stringUtils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications";
|
||||
import NotificationsConfig from "../../../Components/NotificationConfig";
|
||||
import { useUpdateMonitor, useCreateMonitor } from "../../../Hooks/monitorHooks";
|
||||
|
||||
// MUI
|
||||
import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material";
|
||||
|
||||
//Components
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import Link from "../../../Components/Link";
|
||||
import ConfigBox from "../../../Components/ConfigBox";
|
||||
import Dialog from "../../../Components/Dialog";
|
||||
import FieldWrapper from "../../../Components/Inputs/FieldWrapper";
|
||||
import Link from "../../../Components/Link";
|
||||
import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline";
|
||||
import PlayCircleOutlineRoundedIcon from "@mui/icons-material/PlayCircleOutlineRounded";
|
||||
import PulseDot from "../../../Components/Animated/PulseDot";
|
||||
import Select from "../../../Components/Inputs/Select";
|
||||
import TextInput from "../../../Components/Inputs/TextInput";
|
||||
import { Box, Stack, Tooltip, Typography, Button, ButtonGroup } from "@mui/material";
|
||||
import { CustomThreshold } from "./Components/CustomThreshold";
|
||||
import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import Select from "../../../Components/Inputs/Select";
|
||||
import { CustomThreshold } from "./Components/CustomThreshold";
|
||||
import FieldWrapper from "../../../Components/Inputs/FieldWrapper";
|
||||
|
||||
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;
|
||||
};
|
||||
// Utils
|
||||
import NotificationsConfig from "../../../Components/NotificationConfig";
|
||||
import { capitalizeFirstLetter } from "../../../Utils/stringUtils";
|
||||
import { infrastructureMonitorValidation } from "../../../Validation/validation";
|
||||
import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications";
|
||||
import { useMonitorUtils } from "../../../Hooks/useMonitorUtils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useCreateMonitor,
|
||||
useDeleteMonitor,
|
||||
useFetchGlobalSettings,
|
||||
useFetchHardwareMonitorById,
|
||||
usePauseMonitor,
|
||||
useUpdateMonitor,
|
||||
} from "../../../Hooks/monitorHooks";
|
||||
|
||||
const CreateInfrastructureMonitor = () => {
|
||||
const theme = useTheme();
|
||||
const { user } = useSelector((state) => state.auth);
|
||||
const { monitorId } = useParams();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Determine if we are creating or editing
|
||||
const isCreate = typeof monitorId === "undefined";
|
||||
|
||||
// Fetch monitor details if editing
|
||||
const [monitor, isLoading, networkError] = useFetchHardwareMonitorById({ monitorId });
|
||||
const [notifications, notificationsAreLoading, notificationsError] =
|
||||
useGetNotificationsByTeamId();
|
||||
const [updateMonitor, isUpdating] = useUpdateMonitor();
|
||||
const [createMonitor, isCreating] = useCreateMonitor();
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// State
|
||||
const [errors, setErrors] = useState({});
|
||||
const [https, setHttps] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [updateTrigger, setUpdateTrigger] = useState(false);
|
||||
const [infrastructureMonitor, setInfrastructureMonitor] = useState({
|
||||
url: "",
|
||||
name: "",
|
||||
@@ -86,36 +63,107 @@ const CreateInfrastructureMonitor = () => {
|
||||
secret: "",
|
||||
});
|
||||
|
||||
// Fetch monitor details if editing
|
||||
const { statusColor, pagespeedStatusMsg, determineState } = useMonitorUtils();
|
||||
const [monitor, isLoading] = useFetchHardwareMonitorById({
|
||||
monitorId,
|
||||
updateTrigger,
|
||||
});
|
||||
const [createMonitor, isCreating] = useCreateMonitor();
|
||||
const [deleteMonitor, isDeleting] = useDeleteMonitor();
|
||||
const [globalSettings, globalSettingsLoading] = useFetchGlobalSettings();
|
||||
const [notifications, notificationsAreLoading] = useGetNotificationsByTeamId();
|
||||
const [pauseMonitor, isPausing] = usePauseMonitor();
|
||||
const [updateMonitor, isUpdating] = useUpdateMonitor();
|
||||
|
||||
const FREQUENCIES = [
|
||||
{ _id: 0.25, name: t("time.fifteenSeconds") },
|
||||
{ _id: 0.5, name: t("time.thirtySeconds") },
|
||||
{ _id: 1, name: t("time.oneMinute") },
|
||||
{ _id: 2, name: t("time.twoMinutes") },
|
||||
{ _id: 5, name: t("time.fiveMinutes") },
|
||||
{ _id: 10, name: t("time.tenMinutes") },
|
||||
];
|
||||
const CRUMBS = [
|
||||
{ name: "Infrastructure monitors", path: "/infrastructure" },
|
||||
...(isCreate
|
||||
? [{ name: "Create", path: "/infrastructure/create" }]
|
||||
: [
|
||||
{ name: "Details", path: `/infrastructure/${monitorId}` },
|
||||
{ name: "Configure", path: `/infrastructure/configure/${monitorId}` },
|
||||
]),
|
||||
];
|
||||
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) => {
|
||||
const errorKey = Object.keys(errors).find((key) => key.startsWith(METRIC_PREFIX));
|
||||
return errorKey ? errors[errorKey] : null;
|
||||
};
|
||||
|
||||
// Populate form fields if editing
|
||||
useEffect(() => {
|
||||
if (isCreate || !monitor) return;
|
||||
if (isCreate) {
|
||||
if (globalSettingsLoading) return;
|
||||
|
||||
setInfrastructureMonitor({
|
||||
url: monitor.url.replace(/^https?:\/\//, ""),
|
||||
name: monitor.name || "",
|
||||
notifications: monitor.notifications,
|
||||
interval: monitor.interval / MS_PER_MINUTE,
|
||||
cpu: monitor.thresholds?.usage_cpu !== undefined,
|
||||
usage_cpu: monitor.thresholds?.usage_cpu ? monitor.thresholds.usage_cpu * 100 : "",
|
||||
const gt = globalSettings?.data?.settings?.globalThresholds || {};
|
||||
|
||||
memory: monitor.thresholds?.usage_memory !== undefined,
|
||||
usage_memory: monitor.thresholds?.usage_memory
|
||||
? monitor.thresholds.usage_memory * 100
|
||||
: "",
|
||||
setHttps(false);
|
||||
|
||||
disk: monitor.thresholds?.usage_disk !== undefined,
|
||||
usage_disk: monitor.thresholds?.usage_disk
|
||||
? monitor.thresholds.usage_disk * 100
|
||||
: "",
|
||||
setInfrastructureMonitor({
|
||||
url: "",
|
||||
name: "",
|
||||
notifications: [],
|
||||
interval: 0.25,
|
||||
cpu: gt.cpu !== undefined,
|
||||
usage_cpu: gt.cpu !== undefined ? gt.cpu.toString() : "",
|
||||
memory: gt.memory !== undefined,
|
||||
usage_memory: gt.memory !== undefined ? gt.memory.toString() : "",
|
||||
disk: gt.disk !== undefined,
|
||||
usage_disk: gt.disk !== undefined ? gt.disk.toString() : "",
|
||||
temperature: gt.temperature !== undefined,
|
||||
usage_temperature: gt.temperature !== undefined ? gt.temperature.toString() : "",
|
||||
secret: "",
|
||||
});
|
||||
} else if (monitor) {
|
||||
const { thresholds = {} } = monitor;
|
||||
|
||||
temperature: monitor.thresholds?.usage_temperature !== undefined,
|
||||
usage_temperature: monitor.thresholds?.usage_temperature
|
||||
? monitor.thresholds.usage_temperature * 100
|
||||
: "",
|
||||
secret: monitor.secret || "",
|
||||
});
|
||||
setHttps(monitor.url.startsWith("https"));
|
||||
}, [isCreate, monitor]);
|
||||
setHttps(monitor.url.startsWith("https"));
|
||||
|
||||
setInfrastructureMonitor({
|
||||
url: monitor.url.replace(/^https?:\/\//, ""),
|
||||
name: monitor.name || "",
|
||||
notifications: monitor.notifications || [],
|
||||
interval: monitor.interval / MS_PER_MINUTE,
|
||||
cpu: thresholds.usage_cpu !== undefined,
|
||||
usage_cpu:
|
||||
thresholds.usage_cpu !== undefined
|
||||
? (thresholds.usage_cpu * 100).toString()
|
||||
: "",
|
||||
memory: thresholds.usage_memory !== undefined,
|
||||
usage_memory:
|
||||
thresholds.usage_memory !== undefined
|
||||
? (thresholds.usage_memory * 100).toString()
|
||||
: "",
|
||||
disk: thresholds.usage_disk !== undefined,
|
||||
usage_disk:
|
||||
thresholds.usage_disk !== undefined
|
||||
? (thresholds.usage_disk * 100).toString()
|
||||
: "",
|
||||
temperature: thresholds.usage_temperature !== undefined,
|
||||
usage_temperature:
|
||||
thresholds.usage_temperature !== undefined
|
||||
? (thresholds.usage_temperature * 100).toString()
|
||||
: "",
|
||||
secret: monitor.secret || "",
|
||||
});
|
||||
}
|
||||
}, [isCreate, monitor, globalSettings, globalSettingsLoading]);
|
||||
|
||||
// Handlers
|
||||
const onSubmit = async (event) => {
|
||||
@@ -198,6 +246,10 @@ const CreateInfrastructureMonitor = () => {
|
||||
: await updateMonitor({ monitor: form, redirect: "/infrastructure" });
|
||||
};
|
||||
|
||||
const triggerUpdate = () => {
|
||||
setUpdateTrigger(!updateTrigger);
|
||||
};
|
||||
|
||||
const onChange = (event) => {
|
||||
const { value, name } = event.target;
|
||||
setInfrastructureMonitor({
|
||||
@@ -224,19 +276,26 @@ const CreateInfrastructureMonitor = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handlePause = async () => {
|
||||
await pauseMonitor({ monitorId, triggerUpdate });
|
||||
};
|
||||
|
||||
const handleRemove = async (event) => {
|
||||
event.preventDefault();
|
||||
await deleteMonitor({ monitor, redirect: "/infrastructure" });
|
||||
};
|
||||
|
||||
const isBusy =
|
||||
isLoading ||
|
||||
isUpdating ||
|
||||
isCreating ||
|
||||
isDeleting ||
|
||||
isPausing ||
|
||||
notificationsAreLoading;
|
||||
|
||||
return (
|
||||
<Box className="create-infrastructure-monitor">
|
||||
<Breadcrumbs
|
||||
list={[
|
||||
{ name: "Infrastructure monitors", path: "/infrastructure" },
|
||||
...(isCreate
|
||||
? [{ name: "Create", path: "/infrastructure/create" }]
|
||||
: [
|
||||
{ name: "Details", path: `/infrastructure/${monitorId}` },
|
||||
{ name: "Configure", path: `/infrastructure/configure/${monitorId}` },
|
||||
]),
|
||||
]}
|
||||
/>
|
||||
<Breadcrumbs list={CRUMBS} />
|
||||
<Stack
|
||||
component="form"
|
||||
onSubmit={onSubmit}
|
||||
@@ -245,27 +304,141 @@ const CreateInfrastructureMonitor = () => {
|
||||
gap={theme.spacing(12)}
|
||||
mt={theme.spacing(6)}
|
||||
>
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h1"
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
>
|
||||
{t(isCreate ? "infrastructureCreateYour" : "infrastructureEditYour")}{" "}
|
||||
</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
variant="h2"
|
||||
fontSize="inherit"
|
||||
fontWeight="inherit"
|
||||
>
|
||||
{t("monitor")}
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h1"
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
color={
|
||||
!isCreate ? theme.palette.primary.contrastTextSecondary : undefined
|
||||
}
|
||||
>
|
||||
{!isCreate ? infrastructureMonitor.name : t("createYour") + " "}
|
||||
</Typography>
|
||||
{isCreate ? (
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
fontWeight="inherit"
|
||||
color={theme.palette.primary.contrastTextSecondary}
|
||||
>
|
||||
{t("monitor")}
|
||||
</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Typography>
|
||||
{!isCreate && (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
height="fit-content"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Tooltip
|
||||
title={pagespeedStatusMsg[determineState(monitor)]}
|
||||
disableInteractive
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: { offset: [0, -8] },
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<PulseDot color={statusColor[determineState(monitor)]} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="monitorUrl"
|
||||
>
|
||||
{infrastructureMonitor.url?.replace(/^https?:\/\//, "") || "..."}
|
||||
</Typography>
|
||||
<Typography
|
||||
position="relative"
|
||||
variant="body2"
|
||||
ml={theme.spacing(6)}
|
||||
mt={theme.spacing(1)}
|
||||
sx={{
|
||||
"&:before": {
|
||||
position: "absolute",
|
||||
content: `""`,
|
||||
width: theme.spacing(2),
|
||||
height: theme.spacing(2),
|
||||
borderRadius: "50%",
|
||||
backgroundColor: theme.palette.primary.contrastTextTertiary,
|
||||
opacity: 0.8,
|
||||
left: theme.spacing(-5),
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t("editing")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
</Box>
|
||||
{!isCreate && (
|
||||
<Box
|
||||
alignSelf="flex-end"
|
||||
ml="auto"
|
||||
>
|
||||
<Button
|
||||
onClick={handlePause}
|
||||
loading={isBusy}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
sx={{
|
||||
pl: theme.spacing(4),
|
||||
pr: theme.spacing(6),
|
||||
"& svg": {
|
||||
mr: theme.spacing(2),
|
||||
"& path": {
|
||||
stroke: theme.palette.primary.contrastTextTertiary,
|
||||
strokeWidth: 0.1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{monitor?.isActive ? (
|
||||
<>
|
||||
<PauseCircleOutlineIcon />
|
||||
{t("pause")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlayCircleOutlineRoundedIcon />
|
||||
{t("resume")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
loading={isBusy}
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => setIsOpen(true)}
|
||||
sx={{ ml: theme.spacing(6) }}
|
||||
>
|
||||
{t("remove")}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
<ConfigBox>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
<Stack>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
@@ -347,7 +520,12 @@ const CreateInfrastructureMonitor = () => {
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">{t("notificationConfig.title")}</Typography>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("notificationConfig.title")}
|
||||
</Typography>
|
||||
<Typography component="p">{t("notificationConfig.description")}</Typography>
|
||||
</Box>
|
||||
<NotificationsConfig
|
||||
@@ -424,7 +602,7 @@ const CreateInfrastructureMonitor = () => {
|
||||
label="Check frequency"
|
||||
value={infrastructureMonitor.interval || 15}
|
||||
onChange={onChange}
|
||||
items={SELECT_VALUES}
|
||||
items={FREQUENCIES}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
@@ -436,12 +614,23 @@ const CreateInfrastructureMonitor = () => {
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="accent"
|
||||
loading={isLoading || isUpdating || isCreating || notificationsAreLoading}
|
||||
loading={isBusy}
|
||||
>
|
||||
{t(isCreate ? "infrastructureCreateMonitor" : "infrastructureEditMonitor")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{!isCreate && (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
theme={theme}
|
||||
title={t("deleteDialogTitle")}
|
||||
description={t("deleteDialogDescription")}
|
||||
onCancel={() => setIsOpen(false)}
|
||||
confirmationButtonLabel={t("delete")}
|
||||
onConfirm={handleRemove}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -170,7 +170,10 @@ const CreateNotifications = () => {
|
||||
>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("createNotifications.nameSettings.title")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
@@ -191,7 +194,10 @@ const CreateNotifications = () => {
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("createNotifications.typeSettings.title")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
@@ -210,7 +216,12 @@ const CreateNotifications = () => {
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">{t(TITLE_MAP[type])}</Typography>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t(TITLE_MAP[type])}
|
||||
</Typography>
|
||||
<Typography component="p">{t(DESCRIPTION_MAP[type])}</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(12)}>
|
||||
|
||||
95
client/src/Pages/Settings/SettingsGlobalThresholds.jsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import ConfigBox from "../../Components/ConfigBox";
|
||||
import TextInput from "../../Components/Inputs/TextInput";
|
||||
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { PropTypes } from "prop-types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const SettingsGlobalThresholds = ({
|
||||
isAdmin,
|
||||
HEADING_SX,
|
||||
settingsData,
|
||||
setSettingsData,
|
||||
}) => {
|
||||
const { t } = useTranslation(); // For language translation
|
||||
const theme = useTheme(); // MUI theme access
|
||||
|
||||
// Handles input change and updates parent state
|
||||
const handleChange = (e, min, max) => {
|
||||
const { name, value } = e.target;
|
||||
|
||||
const numValue = parseFloat(value);
|
||||
const isValidNumber =
|
||||
value === "" ||
|
||||
(!isNaN(numValue) && isFinite(numValue) && numValue >= min && numValue <= max);
|
||||
|
||||
if (isValidNumber) {
|
||||
setSettingsData((prev) => ({
|
||||
...prev,
|
||||
settings: {
|
||||
...prev.settings,
|
||||
globalThresholds: {
|
||||
...prev.settings?.globalThresholds,
|
||||
[name]: value,
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Only render this section for admins
|
||||
if (!isAdmin) return null;
|
||||
|
||||
return (
|
||||
<ConfigBox>
|
||||
{/* Header and description */}
|
||||
<Box>
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h2"
|
||||
>
|
||||
{t("settingsPage.globalThresholds.title", "Global Thresholds")}
|
||||
</Typography>
|
||||
<Typography sx={HEADING_SX}>
|
||||
{t(
|
||||
"settingsPage.globalThresholds.description",
|
||||
"Configure global CPU, Memory, Disk, and Temperature thresholds."
|
||||
)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Threshold inputs */}
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
{[
|
||||
["CPU Threshold (%)", "cpu", 1, 100],
|
||||
["Memory Threshold (%)", "memory", 1, 100],
|
||||
["Disk Threshold (%)", "disk", 1, 100],
|
||||
["Temperature Threshold (°C)", "temperature", 1, 150],
|
||||
].map(([label, name, min, max]) => (
|
||||
<TextInput
|
||||
key={name}
|
||||
name={name}
|
||||
label={label}
|
||||
placeholder={`${min} - ${max}`}
|
||||
type="number"
|
||||
value={settingsData?.settings?.globalThresholds?.[name] || ""}
|
||||
onChange={(e) => handleChange(e, min, max)}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
);
|
||||
};
|
||||
|
||||
// Prop types
|
||||
SettingsGlobalThresholds.propTypes = {
|
||||
isAdmin: PropTypes.bool,
|
||||
HEADING_SX: PropTypes.object,
|
||||
settingsData: PropTypes.object,
|
||||
setSettingsData: PropTypes.func,
|
||||
};
|
||||
|
||||
export default SettingsGlobalThresholds;
|
||||
@@ -15,7 +15,12 @@ const SettingsURL = ({ HEADING_SX, handleChange, showURL = false }) => {
|
||||
return (
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h1">{t("settingsPage.urlSettings.title")}</Typography>
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h2"
|
||||
>
|
||||
{t("settingsPage.urlSettings.title")}
|
||||
</Typography>
|
||||
<Typography sx={HEADING_SX}>
|
||||
{t("settingsPage.urlSettings.description")}
|
||||
</Typography>
|
||||
|
||||
@@ -8,6 +8,7 @@ import SettingsPagespeed from "./SettingsPagespeed";
|
||||
import SettingsDemoMonitors from "./SettingsDemoMonitors";
|
||||
import SettingsAbout from "./SettingsAbout";
|
||||
import SettingsEmail from "./SettingsEmail";
|
||||
import SettingsGlobalThresholds from "./SettingsGlobalThresholds";
|
||||
import Button from "@mui/material/Button";
|
||||
// Utils
|
||||
import { settingsValidation } from "../../Validation/validation";
|
||||
@@ -48,6 +49,7 @@ const Settings = () => {
|
||||
setIsApiKeySet,
|
||||
setIsEmailPasswordSet,
|
||||
});
|
||||
|
||||
const [addDemoMonitors, isAddingDemoMonitors] = useAddDemoMonitors();
|
||||
|
||||
const [isSaving, saveError, saveSettings] = useSaveSettings({
|
||||
@@ -149,6 +151,7 @@ const Settings = () => {
|
||||
error.details.forEach((err) => {
|
||||
newErrors[err.path[0]] = err.message;
|
||||
});
|
||||
|
||||
setErrors(newErrors);
|
||||
}
|
||||
saveSettings(settingsData?.settings);
|
||||
@@ -190,6 +193,13 @@ const Settings = () => {
|
||||
handleChange={handleChange}
|
||||
errors={errors}
|
||||
/>
|
||||
<SettingsGlobalThresholds
|
||||
isAdmin={isAdmin}
|
||||
HEADING_SX={HEADING_SX}
|
||||
settingsData={settingsData}
|
||||
setSettingsData={setSettingsData}
|
||||
/>
|
||||
|
||||
<SettingsDemoMonitors
|
||||
isAdmin={isAdmin}
|
||||
HEADER_SX={HEADING_SX}
|
||||
|
||||
@@ -239,7 +239,10 @@ const CreateStatusPage = () => {
|
||||
<Breadcrumbs
|
||||
list={[
|
||||
{ name: t("statusBreadCrumbsStatusPages", "Status"), path: "/status" },
|
||||
{ name: t("statusBreadCrumbsDetails", "Details"), path: `/status/${url}` },
|
||||
{
|
||||
name: t("statusBreadCrumbsDetails", "Details"),
|
||||
path: `/status/uptime/${url}`,
|
||||
},
|
||||
{ name: t("configure", "Configure"), path: `/status/create/${url}` },
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
.configure-monitor button.MuiButtonBase-root {
|
||||
height: var(--env-var-height-2);
|
||||
}
|
||||
|
||||
.configure-monitor .MuiStack-root:has(span.MuiTypography-root.input-error) {
|
||||
position: relative;
|
||||
}
|
||||
.configure-monitor span.MuiTypography-root.input-error {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
}
|
||||
@@ -1,526 +0,0 @@
|
||||
// Components
|
||||
import Box from "@mui/material/Box";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import Switch from "@mui/material/Switch";
|
||||
import ConfigBox from "../../../Components/ConfigBox";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import TextInput from "../../../Components/Inputs/TextInput";
|
||||
import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments";
|
||||
import Select from "../../../Components/Inputs/Select";
|
||||
import Dialog from "../../../Components/Dialog";
|
||||
import PulseDot from "../../../Components/Animated/PulseDot";
|
||||
import Checkbox from "../../../Components/Inputs/Checkbox";
|
||||
|
||||
// Utils
|
||||
import { useParams } from "react-router";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useState } from "react";
|
||||
import { monitorValidation } from "../../../Validation/validation";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PauseOutlinedIcon from "@mui/icons-material/PauseOutlined";
|
||||
import PlayArrowOutlinedIcon from "@mui/icons-material/PlayArrowOutlined";
|
||||
import { useMonitorUtils } from "../../../Hooks/useMonitorUtils";
|
||||
import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications";
|
||||
import {
|
||||
useDeleteMonitor,
|
||||
useUpdateMonitor,
|
||||
usePauseMonitor,
|
||||
useFetchMonitorById,
|
||||
} from "../../../Hooks/monitorHooks";
|
||||
import NotificationsConfig from "../../../Components/NotificationConfig";
|
||||
|
||||
/**
|
||||
* Parses a URL string and returns a URL object.
|
||||
*
|
||||
* @param {string} url - The URL string to parse.
|
||||
* @returns {URL} - The parsed URL object if valid, otherwise an empty string.
|
||||
*/
|
||||
const parseUrl = (url) => {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Configure page displays monitor configurations and allows for editing actions.
|
||||
* @component
|
||||
*/
|
||||
const Configure = () => {
|
||||
const { monitorId } = useParams();
|
||||
|
||||
// Local state
|
||||
const [form, setForm] = useState({
|
||||
ignoreTlsErrors: false,
|
||||
interval: 60000,
|
||||
matchMethod: "equal",
|
||||
expectedValue: "",
|
||||
jsonPath: "",
|
||||
notifications: [],
|
||||
port: "",
|
||||
type: "http",
|
||||
});
|
||||
const [useAdvancedMatching, setUseAdvancedMatching] = useState(false);
|
||||
const [updateTrigger, setUpdateTrigger] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
const triggerUpdate = () => {
|
||||
setUpdateTrigger(!updateTrigger);
|
||||
};
|
||||
|
||||
// Network
|
||||
const [notifications, notificationsAreLoading, notificationsError] =
|
||||
useGetNotificationsByTeamId();
|
||||
const [pauseMonitor, isPausing, pauseError] = usePauseMonitor({});
|
||||
const [deleteMonitor, isDeleting] = useDeleteMonitor();
|
||||
const [updateMonitor, isUpdating] = useUpdateMonitor();
|
||||
const [isLoading] = useFetchMonitorById({
|
||||
monitorId,
|
||||
setMonitor: setForm,
|
||||
updateTrigger,
|
||||
});
|
||||
|
||||
const MS_PER_MINUTE = 60000;
|
||||
const theme = useTheme();
|
||||
|
||||
const matchMethodOptions = [
|
||||
{ _id: "equal", name: "Equal" },
|
||||
{ _id: "include", name: "Include" },
|
||||
{ _id: "regex", name: "Regex" },
|
||||
];
|
||||
|
||||
const frequencies = [
|
||||
{ _id: 1, name: "1 minute" },
|
||||
{ _id: 2, name: "2 minutes" },
|
||||
{ _id: 3, name: "3 minutes" },
|
||||
{ _id: 4, name: "4 minutes" },
|
||||
{ _id: 5, name: "5 minutes" },
|
||||
];
|
||||
|
||||
const expectedValuePlaceholders = {
|
||||
regex: "^(success|ok)$",
|
||||
equal: "success",
|
||||
include: "ok",
|
||||
};
|
||||
|
||||
// Handlers
|
||||
const handlePause = async () => {
|
||||
const res = await pauseMonitor({ monitorId: form?._id, triggerUpdate });
|
||||
if (typeof res !== "undefined") {
|
||||
triggerUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = async (event) => {
|
||||
event.preventDefault();
|
||||
await deleteMonitor({ monitor: form, redirect: "/uptime" });
|
||||
};
|
||||
|
||||
const onChange = (event) => {
|
||||
let { name, value, checked } = event.target;
|
||||
|
||||
if (name === "ignoreTlsErrors") {
|
||||
value = checked;
|
||||
}
|
||||
|
||||
if (name === "useAdvancedMatching") {
|
||||
setForm((prevForm) => {
|
||||
return {
|
||||
...prevForm,
|
||||
matchMethod: "equal",
|
||||
};
|
||||
});
|
||||
setUseAdvancedMatching(!useAdvancedMatching);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === "interval") {
|
||||
value = value * MS_PER_MINUTE;
|
||||
}
|
||||
setForm({ ...form, [name]: value });
|
||||
|
||||
const validation = monitorValidation.validate(
|
||||
{ [name]: value },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
|
||||
setErrors((prev) => {
|
||||
const updatedErrors = { ...prev };
|
||||
if (validation.error) updatedErrors[name] = validation.error.details[0].message;
|
||||
else delete updatedErrors[name];
|
||||
return updatedErrors;
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const toSubmit = {
|
||||
_id: form?._id,
|
||||
url: form?.url,
|
||||
name: form?.name,
|
||||
type: form?.type,
|
||||
matchMethod: form?.matchMethod,
|
||||
expectedValue: form?.expectedValue,
|
||||
jsonPath: form?.jsonPath,
|
||||
interval: form?.interval,
|
||||
teamId: form?.teamId,
|
||||
userId: form?.userId,
|
||||
port: form?.port,
|
||||
ignoreTlsErrors: form?.ignoreTlsErrors,
|
||||
};
|
||||
|
||||
if (!useAdvancedMatching) {
|
||||
toSubmit.matchMethod = "";
|
||||
toSubmit.expectedValue = "";
|
||||
toSubmit.jsonPath = "";
|
||||
}
|
||||
|
||||
const validation = monitorValidation.validate(toSubmit, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
if (validation.error) {
|
||||
const newErrors = {};
|
||||
validation.error.details.forEach((err) => {
|
||||
newErrors[err.path[0]] = err.message;
|
||||
});
|
||||
setErrors(newErrors);
|
||||
createToast({ body: "Please check the form for errors." });
|
||||
return;
|
||||
}
|
||||
|
||||
toSubmit.notifications = form?.notifications;
|
||||
await updateMonitor({ monitor: toSubmit, redirect: "/uptime" });
|
||||
};
|
||||
|
||||
// Parse the URL
|
||||
const parsedUrl = parseUrl(form?.url);
|
||||
const protocol = parsedUrl?.protocol?.replace(":", "") || "";
|
||||
|
||||
const { determineState, statusColor } = useMonitorUtils();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack gap={theme.spacing(10)}>
|
||||
<Breadcrumbs
|
||||
list={[
|
||||
{ name: "uptime", path: "/uptime" },
|
||||
{ name: "details", path: `/uptime/${monitorId}` },
|
||||
{ name: "configure", path: `/uptime/configure/${monitorId}` },
|
||||
]}
|
||||
/>
|
||||
<Stack
|
||||
component="form"
|
||||
onSubmit={onSubmit}
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
gap={theme.spacing(12)}
|
||||
flex={1}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.spacing(12)}
|
||||
>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="monitorName"
|
||||
>
|
||||
{form?.name}
|
||||
</Typography>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
height="fit-content"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Tooltip
|
||||
title={t(`statusMsg.${[determineState(form)]}`)}
|
||||
disableInteractive
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, -8],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<PulseDot color={statusColor[determineState(form)]} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="monitorUrl"
|
||||
>
|
||||
{form?.url?.replace(/^https?:\/\//, "") || "..."}
|
||||
</Typography>
|
||||
<Typography
|
||||
position="relative"
|
||||
variant="body2"
|
||||
ml={theme.spacing(6)}
|
||||
mt={theme.spacing(1)}
|
||||
sx={{
|
||||
"&:before": {
|
||||
position: "absolute",
|
||||
content: `""`,
|
||||
width: 4,
|
||||
height: 4,
|
||||
borderRadius: "50%",
|
||||
backgroundColor: theme.palette.primary.contrastTextTertiary,
|
||||
opacity: 0.8,
|
||||
left: -10,
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t("editing")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box
|
||||
justifyContent="space-between"
|
||||
sx={{
|
||||
alignSelf: "flex-end",
|
||||
ml: "auto",
|
||||
display: "flex",
|
||||
gap: theme.spacing(2),
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
loading={isPausing}
|
||||
startIcon={
|
||||
form?.isActive ? <PauseOutlinedIcon /> : <PlayArrowOutlinedIcon />
|
||||
}
|
||||
onClick={handlePause}
|
||||
>
|
||||
{form?.isActive ? t("pause") : t("resume")}
|
||||
</Button>
|
||||
<Button
|
||||
loading={isLoading}
|
||||
variant="contained"
|
||||
color="error"
|
||||
sx={{ px: theme.spacing(8) }}
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
{t("remove")}
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("settingsGeneralSettings")}
|
||||
</Typography>
|
||||
<Typography component="p">{t("distributedUptimeCreateSelectURL")}</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<TextInput
|
||||
type={form?.type === "http" ? "url" : "text"}
|
||||
https={protocol === "https"}
|
||||
startAdornment={
|
||||
form?.type === "http" && <HttpAdornment https={protocol === "https"} />
|
||||
}
|
||||
id="monitor-url"
|
||||
label={t("urlMonitor")}
|
||||
placeholder="google.com"
|
||||
value={parsedUrl?.host || form?.url || ""}
|
||||
disabled={true}
|
||||
/>
|
||||
<TextInput
|
||||
name="port"
|
||||
type="number"
|
||||
label={t("portToMonitor")}
|
||||
placeholder="5173"
|
||||
value={form?.port || ""}
|
||||
onChange={onChange}
|
||||
error={errors["port"] ? true : false}
|
||||
helperText={errors["port"]}
|
||||
hidden={form?.type !== "port"}
|
||||
/>
|
||||
<TextInput
|
||||
name="name"
|
||||
type="text"
|
||||
label={t("displayName")}
|
||||
isOptional={true}
|
||||
placeholder="Google"
|
||||
value={form?.name || ""}
|
||||
onChange={onChange}
|
||||
error={errors["name"] ? true : false}
|
||||
helperText={errors["name"]}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">{t("notificationConfig.title")}</Typography>
|
||||
<Typography component="p">{t("notificationConfig.description")}</Typography>
|
||||
</Box>
|
||||
<NotificationsConfig
|
||||
notifications={notifications}
|
||||
setMonitor={setForm}
|
||||
setNotifications={form?.notifications}
|
||||
/>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("ignoreTLSError")}
|
||||
</Typography>
|
||||
<Typography component="p">{t("ignoreTLSErrorDescription")}</Typography>
|
||||
</Box>
|
||||
<Stack>
|
||||
<FormControlLabel
|
||||
sx={{ marginLeft: 0 }}
|
||||
control={
|
||||
<Switch
|
||||
name="ignoreTlsErrors"
|
||||
checked={form?.ignoreTlsErrors ?? false}
|
||||
onChange={onChange}
|
||||
sx={{ mr: theme.spacing(2) }}
|
||||
/>
|
||||
}
|
||||
label={t("tlsErrorIgnored")}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("distributedUptimeCreateAdvancedSettings")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<Select
|
||||
name="interval"
|
||||
label={t("checkFrequency")}
|
||||
value={form?.interval / MS_PER_MINUTE || 1}
|
||||
onChange={onChange}
|
||||
items={frequencies}
|
||||
/>
|
||||
<Checkbox
|
||||
name="useAdvancedMatching"
|
||||
label={t("advancedMatching")}
|
||||
isChecked={useAdvancedMatching}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{form?.type === "http" && useAdvancedMatching && (
|
||||
<>
|
||||
<Select
|
||||
name="matchMethod"
|
||||
label={t("matchMethod")}
|
||||
value={form?.matchMethod || "equal"}
|
||||
onChange={onChange}
|
||||
items={matchMethodOptions}
|
||||
/>
|
||||
<Stack>
|
||||
<TextInput
|
||||
type="text"
|
||||
name="expectedValue"
|
||||
label={t("expectedValue")}
|
||||
isOptional={true}
|
||||
placeholder={expectedValuePlaceholders[form?.matchMethod || "equal"]}
|
||||
value={form?.expectedValue}
|
||||
onChange={onChange}
|
||||
error={errors["expectedValue"] ? true : false}
|
||||
helperText={errors["expectedValue"]}
|
||||
/>
|
||||
<Typography
|
||||
component="span"
|
||||
color={theme.palette.primary.contrastTextTertiary}
|
||||
opacity={0.8}
|
||||
>
|
||||
{t("uptimeCreate")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<TextInput
|
||||
name="jsonPath"
|
||||
type="text"
|
||||
label="JSON Path"
|
||||
isOptional={true}
|
||||
placeholder="data.status"
|
||||
value={form?.jsonPath}
|
||||
onChange={onChange}
|
||||
error={errors["jsonPath"] ? true : false}
|
||||
helperText={errors["jsonPath"]}
|
||||
/>
|
||||
<Typography
|
||||
component="span"
|
||||
color={theme.palette.primary.contrastTextTertiary}
|
||||
opacity={0.8}
|
||||
>
|
||||
{t("uptimeCreateJsonPath")}
|
||||
<Typography
|
||||
component="a"
|
||||
href="https://jmespath.org/"
|
||||
target="_blank"
|
||||
color="info"
|
||||
>
|
||||
jmespath.org
|
||||
</Typography>
|
||||
{t("uptimeCreateJsonPathQuery")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-end"
|
||||
mt="auto"
|
||||
>
|
||||
<Button
|
||||
disabled={isDeleting || isUpdating}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="accent"
|
||||
loading={isLoading}
|
||||
sx={{ px: theme.spacing(12) }}
|
||||
>
|
||||
{t("settingsSave")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
theme={theme}
|
||||
title="Do you really want to delete this monitor?"
|
||||
description="Once deleted, this monitor cannot be retrieved."
|
||||
onCancel={() => setIsOpen(false)}
|
||||
confirmationButtonLabel="Delete"
|
||||
onConfirm={handleRemove}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default Configure;
|
||||
@@ -1,4 +1,14 @@
|
||||
//Components
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
FormControlLabel,
|
||||
Stack,
|
||||
Switch,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import TextInput from "../../../Components/Inputs/TextInput";
|
||||
import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments";
|
||||
@@ -6,132 +16,186 @@ import Radio from "../../../Components/Inputs/Radio";
|
||||
import Select from "../../../Components/Inputs/Select";
|
||||
import ConfigBox from "../../../Components/ConfigBox";
|
||||
import NotificationsConfig from "../../../Components/NotificationConfig";
|
||||
import Button from "@mui/material/Button";
|
||||
import ButtonGroup from "@mui/material/ButtonGroup";
|
||||
import Box from "@mui/material/Box";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Switch from "@mui/material/Switch";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import Checkbox from "../../../Components/Inputs/Checkbox";
|
||||
import Dialog from "../../../Components/Dialog";
|
||||
import PulseDot from "../../../Components/Animated/PulseDot";
|
||||
import SkeletonLayout from "./skeleton";
|
||||
|
||||
// Utils
|
||||
import PropTypes from "prop-types";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { monitorValidation } from "../../../Validation/validation";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import {
|
||||
PauseOutlined as PauseOutlinedIcon,
|
||||
PlayArrowOutlined as PlayArrowOutlinedIcon,
|
||||
} from "@mui/icons-material";
|
||||
import { useMonitorUtils } from "../../../Hooks/useMonitorUtils";
|
||||
import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications";
|
||||
import { useCreateMonitor, useFetchMonitorById } from "../../../Hooks/monitorHooks";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {
|
||||
useCreateMonitor,
|
||||
useDeleteMonitor,
|
||||
useUpdateMonitor,
|
||||
usePauseMonitor,
|
||||
useFetchMonitorById,
|
||||
} from "../../../Hooks/monitorHooks";
|
||||
|
||||
const CreateMonitor = () => {
|
||||
// Local state
|
||||
const [errors, setErrors] = useState({});
|
||||
const [https, setHttps] = useState(true);
|
||||
const [useAdvancedMatching, setUseAdvancedMatching] = useState(false);
|
||||
/**
|
||||
* Parses a URL string and returns a URL object.
|
||||
*
|
||||
* @param {string} url - The URL string to parse.
|
||||
* @returns {URL} - The parsed URL object if valid, otherwise an empty string.
|
||||
*/
|
||||
const parseUrl = (url) => {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create page renders monitor creation or configuration views.
|
||||
* @component
|
||||
*/
|
||||
const UptimeCreate = ({ isClone = false }) => {
|
||||
const { monitorId } = useParams();
|
||||
const isCreate = typeof monitorId === "undefined" || isClone;
|
||||
|
||||
// States
|
||||
const [monitor, setMonitor] = useState({
|
||||
url: "",
|
||||
name: "",
|
||||
type: "http",
|
||||
matchMethod: "equal",
|
||||
expectedValue: "",
|
||||
jsonPath: "",
|
||||
notifications: [],
|
||||
interval: 1,
|
||||
interval: 60000,
|
||||
ignoreTlsErrors: false,
|
||||
...(isCreate ? { url: "", name: "" } : { port: undefined }),
|
||||
});
|
||||
|
||||
// Setup
|
||||
const MS_PER_MINUTE = 60000;
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const [notifications, notificationsAreLoading, error] = useGetNotificationsByTeamId();
|
||||
const [createMonitor, isCreating] = useCreateMonitor();
|
||||
const { monitorId } = useParams();
|
||||
|
||||
const formatAndSet = (monitor) => {
|
||||
monitor.interval = monitor.interval / MS_PER_MINUTE;
|
||||
setMonitor(monitor);
|
||||
const [errors, setErrors] = useState({});
|
||||
const [https, setHttps] = useState(true);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [useAdvancedMatching, setUseAdvancedMatching] = useState(false);
|
||||
const [updateTrigger, setUpdateTrigger] = useState(false);
|
||||
const triggerUpdate = () => {
|
||||
setUpdateTrigger(!updateTrigger);
|
||||
};
|
||||
|
||||
// Hooks
|
||||
const [notifications, notificationsAreLoading, notificationsError] =
|
||||
useGetNotificationsByTeamId();
|
||||
const { determineState, statusColor } = useMonitorUtils();
|
||||
// Network
|
||||
const [isLoading] = useFetchMonitorById({
|
||||
monitorId,
|
||||
setMonitor: formatAndSet,
|
||||
setMonitor,
|
||||
updateTrigger: true,
|
||||
});
|
||||
const [createMonitor, isCreating] = useCreateMonitor();
|
||||
const [pauseMonitor, isPausing] = usePauseMonitor({});
|
||||
const [deleteMonitor, isDeleting] = useDeleteMonitor();
|
||||
const [updateMonitor, isUpdating] = useUpdateMonitor();
|
||||
|
||||
const SELECT_VALUES = [
|
||||
{ _id: 1, name: "1 minute" },
|
||||
{ _id: 2, name: "2 minutes" },
|
||||
{ _id: 3, name: "3 minutes" },
|
||||
{ _id: 4, name: "4 minutes" },
|
||||
{ _id: 5, name: "5 minutes" },
|
||||
// Setup
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Constants
|
||||
const MS_PER_MINUTE = 60000;
|
||||
const FREQUENCIES = [
|
||||
{ _id: 1, name: t("time.oneMinute") },
|
||||
{ _id: 2, name: t("time.twoMinutes") },
|
||||
{ _id: 3, name: t("time.threeMinutes") },
|
||||
{ _id: 4, name: t("time.fourMinutes") },
|
||||
{ _id: 5, name: t("time.fiveMinutes") },
|
||||
];
|
||||
const CRUMBS = [
|
||||
{ name: "uptime", path: "/uptime" },
|
||||
...(isCreate
|
||||
? [{ name: "create", path: `/uptime/create` }]
|
||||
: [
|
||||
{ name: "details", path: `/uptime/${monitorId}` },
|
||||
{ name: "configure", path: `/uptime/configure/${monitorId}` },
|
||||
]),
|
||||
];
|
||||
|
||||
const matchMethodOptions = [
|
||||
{ _id: "equal", name: "Equal" },
|
||||
{ _id: "include", name: "Include" },
|
||||
{ _id: "regex", name: "Regex" },
|
||||
{ _id: "equal", name: t("matchMethodOptions.equal") },
|
||||
{ _id: "include", name: t("matchMethodOptions.include") },
|
||||
{ _id: "regex", name: t("matchMethodOptions.regex") },
|
||||
];
|
||||
|
||||
const expectedValuePlaceholders = {
|
||||
regex: "^(success|ok)$",
|
||||
equal: "success",
|
||||
include: "ok",
|
||||
regex: t("matchMethodOptions.regexPlaceholder"),
|
||||
equal: t("matchMethodOptions.equalPlaceholder"),
|
||||
include: t("matchMethodOptions.includePlaceholder"),
|
||||
};
|
||||
|
||||
const monitorTypeMaps = {
|
||||
http: {
|
||||
label: "URL to monitor",
|
||||
placeholder: "google.com",
|
||||
namePlaceholder: "Google",
|
||||
label: t("monitorType.http.label"),
|
||||
placeholder: t("monitorType.http.placeholder"),
|
||||
namePlaceholder: t("monitorType.http.namePlaceholder"),
|
||||
},
|
||||
ping: {
|
||||
label: "IP address to monitor",
|
||||
placeholder: "1.1.1.1",
|
||||
namePlaceholder: "Google",
|
||||
label: t("monitorType.ping.label"),
|
||||
placeholder: t("monitorType.ping.placeholder"),
|
||||
namePlaceholder: t("monitorType.ping.namePlaceholder"),
|
||||
},
|
||||
docker: {
|
||||
label: "Container ID",
|
||||
placeholder: "abc123",
|
||||
namePlaceholder: "My Container",
|
||||
label: t("monitorType.docker.label"),
|
||||
placeholder: t("monitorType.docker.placeholder"),
|
||||
namePlaceholder: t("monitorType.docker.namePlaceholder"),
|
||||
},
|
||||
port: {
|
||||
label: "URL to monitor",
|
||||
placeholder: "localhost",
|
||||
namePlaceholder: "Localhost:5173",
|
||||
label: t("monitorType.port.label"),
|
||||
placeholder: t("monitorType.port.placeholder"),
|
||||
namePlaceholder: t("monitorType.port.namePlaceholder"),
|
||||
},
|
||||
};
|
||||
|
||||
const BREADCRUMBS = [
|
||||
{ name: "uptime", path: "/uptime" },
|
||||
{ name: "create", path: `/uptime/create` },
|
||||
];
|
||||
|
||||
// Handlers
|
||||
|
||||
const onSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
const { notifications, ...rest } = monitor;
|
||||
|
||||
let form = {
|
||||
...rest,
|
||||
url:
|
||||
//prepending protocol for url
|
||||
monitor.type === "http"
|
||||
? `http${https ? "s" : ""}://` + monitor.url
|
||||
: monitor.url,
|
||||
port: monitor.type === "port" ? monitor.port : undefined,
|
||||
name: monitor.name || monitor.url.substring(0, 50),
|
||||
type: monitor.type,
|
||||
interval: monitor.interval * MS_PER_MINUTE,
|
||||
};
|
||||
|
||||
// If not using advanced matching, remove advanced settings
|
||||
let form = {};
|
||||
if (isCreate) {
|
||||
form = {
|
||||
url:
|
||||
monitor.type === "http" && !isClone
|
||||
? `http${https ? "s" : ""}://` + monitor.url
|
||||
: monitor.url,
|
||||
name: monitor.name || monitor.url.substring(0, 50),
|
||||
type: monitor.type,
|
||||
port: monitor.type === "port" ? monitor.port : undefined,
|
||||
interval: monitor.interval,
|
||||
matchMethod: monitor.matchMethod,
|
||||
expectedValue: monitor.expectedValue,
|
||||
jsonPath: monitor.jsonPath,
|
||||
ignoreTlsErrors: monitor.ignoreTlsErrors,
|
||||
};
|
||||
} else {
|
||||
form = {
|
||||
_id: monitor._id,
|
||||
url: monitor.url,
|
||||
name: monitor.name || monitor.url.substring(0, 50),
|
||||
type: monitor.type,
|
||||
matchMethod: monitor.matchMethod,
|
||||
expectedValue: monitor.expectedValue,
|
||||
jsonPath: monitor.jsonPath,
|
||||
interval: monitor.interval,
|
||||
teamId: monitor.teamId,
|
||||
userId: monitor.userId,
|
||||
port: monitor.type === "port" ? monitor.port : undefined,
|
||||
ignoreTlsErrors: monitor.ignoreTlsErrors,
|
||||
};
|
||||
}
|
||||
if (!useAdvancedMatching) {
|
||||
form.matchMethod = undefined;
|
||||
form.expectedValue = undefined;
|
||||
form.jsonPath = undefined;
|
||||
form.matchMethod = isCreate ? undefined : "";
|
||||
form.expectedValue = isCreate ? undefined : "";
|
||||
form.jsonPath = isCreate ? undefined : "";
|
||||
}
|
||||
|
||||
const { error } = monitorValidation.validate(form, {
|
||||
@@ -144,7 +208,7 @@ const CreateMonitor = () => {
|
||||
newErrors[err.path[0]] = err.message;
|
||||
});
|
||||
setErrors(newErrors);
|
||||
createToast({ body: "Please check the form for errors." });
|
||||
createToast({ body: t("checkFormError") });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,15 +218,18 @@ const CreateMonitor = () => {
|
||||
notifications: monitor.notifications,
|
||||
};
|
||||
|
||||
await createMonitor({ monitor: form, redirect: "/uptime" });
|
||||
if (isCreate) {
|
||||
await createMonitor({ monitor: form, redirect: "/uptime" });
|
||||
} else {
|
||||
await updateMonitor({ monitor: form, redirect: "/uptime" });
|
||||
}
|
||||
};
|
||||
|
||||
const onChange = (event) => {
|
||||
const { name, value, checked } = event.target;
|
||||
let { name, value, checked } = event.target;
|
||||
|
||||
let newValue = value;
|
||||
if (name === "ignoreTlsErrors") {
|
||||
newValue = checked;
|
||||
value = checked;
|
||||
}
|
||||
|
||||
if (name === "useAdvancedMatching") {
|
||||
@@ -170,15 +237,14 @@ const CreateMonitor = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedMonitor = {
|
||||
...monitor,
|
||||
[name]: newValue,
|
||||
};
|
||||
if (name === "interval") {
|
||||
value = value * MS_PER_MINUTE;
|
||||
}
|
||||
|
||||
setMonitor(updatedMonitor);
|
||||
setMonitor((prev) => ({ ...prev, [name]: value }));
|
||||
|
||||
const { error } = monitorValidation.validate(
|
||||
{ type: monitor.type, [name]: newValue },
|
||||
{ type: monitor.type, [name]: value },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
|
||||
@@ -188,122 +254,254 @@ const CreateMonitor = () => {
|
||||
}));
|
||||
};
|
||||
|
||||
const handlePause = async () => {
|
||||
await pauseMonitor({ monitorId, triggerUpdate });
|
||||
};
|
||||
|
||||
const handleRemove = async (event) => {
|
||||
event.preventDefault();
|
||||
await deleteMonitor({ monitor, redirect: "/uptime" });
|
||||
};
|
||||
|
||||
const isBusy = isLoading || isCreating || isDeleting || isUpdating || isPausing;
|
||||
const displayInterval = monitor?.interval / MS_PER_MINUTE || 1;
|
||||
const parsedUrl = parseUrl(monitor?.url);
|
||||
const protocol = parsedUrl?.protocol?.replace(":", "") || "";
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCreate || isClone) {
|
||||
if (monitor.matchMethod) {
|
||||
setUseAdvancedMatching(true);
|
||||
} else {
|
||||
setUseAdvancedMatching(false);
|
||||
}
|
||||
}
|
||||
}, [monitor, isCreate]);
|
||||
|
||||
if (Object.keys(monitor).length === 0) {
|
||||
return <SkeletonLayout />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack gap={theme.spacing(10)}>
|
||||
<Breadcrumbs list={BREADCRUMBS} />
|
||||
<Breadcrumbs list={CRUMBS} />
|
||||
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h1"
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
>
|
||||
{t("createYour")}{" "}
|
||||
</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
variant="h2"
|
||||
fontSize="inherit"
|
||||
fontWeight="inherit"
|
||||
>
|
||||
{t("monitor")}
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Stack
|
||||
component="form"
|
||||
noValidate
|
||||
gap={theme.spacing(12)}
|
||||
mt={theme.spacing(6)}
|
||||
onSubmit={onSubmit}
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
gap={theme.spacing(12)}
|
||||
flex={1}
|
||||
>
|
||||
<ConfigBox>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.spacing(12)}
|
||||
>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
component="h1"
|
||||
variant="h1"
|
||||
>
|
||||
{t("distributedUptimeCreateChecks")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
{t("distributedUptimeCreateChecksDescription")}
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
color={
|
||||
!isCreate ? theme.palette.primary.contrastTextSecondary : undefined
|
||||
}
|
||||
>
|
||||
{!isCreate ? monitor.name : t("createYour") + " "}
|
||||
</Typography>
|
||||
{isCreate && (
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
fontWeight="inherit"
|
||||
color={theme.palette.primary.contrastTextSecondary}
|
||||
>
|
||||
{t("monitor")}
|
||||
</Typography>
|
||||
)}
|
||||
</Typography>
|
||||
{!isCreate && (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
height="fit-content"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Tooltip
|
||||
title={t(`statusMsg.${[determineState(monitor)]}`)}
|
||||
disableInteractive
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, -8],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<PulseDot color={statusColor[determineState(monitor)]} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="monitorUrl"
|
||||
>
|
||||
{monitor.url?.replace(/^https?:\/\//, "") || "..."}
|
||||
</Typography>
|
||||
<Typography
|
||||
position="relative"
|
||||
variant="body2"
|
||||
ml={theme.spacing(6)}
|
||||
mt={theme.spacing(1)}
|
||||
sx={{
|
||||
"&:before": {
|
||||
position: "absolute",
|
||||
content: `""`,
|
||||
width: theme.spacing(2),
|
||||
height: theme.spacing(2),
|
||||
borderRadius: "50%",
|
||||
backgroundColor: theme.palette.primary.contrastTextTertiary,
|
||||
opacity: 0.8,
|
||||
left: theme.spacing(-5),
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t("editing")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(12)}>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
{!isCreate && (
|
||||
<Box
|
||||
justifyContent="space-between"
|
||||
sx={{
|
||||
alignSelf: "flex-end",
|
||||
ml: "auto",
|
||||
display: "flex",
|
||||
gap: theme.spacing(2),
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
loading={isBusy}
|
||||
startIcon={
|
||||
monitor?.isActive ? <PauseOutlinedIcon /> : <PlayArrowOutlinedIcon />
|
||||
}
|
||||
onClick={handlePause}
|
||||
>
|
||||
{monitor?.isActive ? t("pause") : t("resume")}
|
||||
</Button>
|
||||
<Button
|
||||
loading={isBusy}
|
||||
variant="contained"
|
||||
color="error"
|
||||
sx={{ px: theme.spacing(8) }}
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
{t("remove")}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
{isCreate && (
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("distributedUptimeCreateChecks")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
{t("distributedUptimeCreateChecksDescription")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(12)}>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
<Radio
|
||||
name="type"
|
||||
title={t("websiteMonitoring")}
|
||||
desc={t("websiteMonitoringDescription")}
|
||||
size="small"
|
||||
value="http"
|
||||
checked={monitor.type === "http"}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{monitor.type === "http" ? (
|
||||
<ButtonGroup sx={{ ml: theme.spacing(16) }}>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={https.toString()}
|
||||
onClick={() => setHttps(true)}
|
||||
>
|
||||
{t("https")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(!https).toString()}
|
||||
onClick={() => setHttps(false)}
|
||||
>
|
||||
{t("http")}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
<Radio
|
||||
name="type"
|
||||
title={t("websiteMonitoring")}
|
||||
desc={t("websiteMonitoringDescription")}
|
||||
title={t("pingMonitoring")}
|
||||
desc={t("pingMonitoringDescription")}
|
||||
size="small"
|
||||
value="http"
|
||||
checked={monitor.type === "http"}
|
||||
value="ping"
|
||||
checked={monitor.type === "ping"}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{monitor.type === "http" ? (
|
||||
<ButtonGroup sx={{ ml: theme.spacing(16) }}>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={https.toString()}
|
||||
onClick={() => setHttps(true)}
|
||||
<Radio
|
||||
name="type"
|
||||
title={t("dockerContainerMonitoring")}
|
||||
desc={t("dockerContainerMonitoringDescription")}
|
||||
size="small"
|
||||
value="docker"
|
||||
checked={monitor.type === "docker"}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<Radio
|
||||
name="type"
|
||||
title={t("portMonitoring")}
|
||||
desc={t("portMonitoringDescription")}
|
||||
size="small"
|
||||
value="port"
|
||||
checked={monitor.type === "port"}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{errors["type"] ? (
|
||||
<Box className="error-container">
|
||||
<Typography
|
||||
component="p"
|
||||
className="input-error"
|
||||
color={theme.palette.error.contrastText}
|
||||
>
|
||||
{t("https")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(!https).toString()}
|
||||
onClick={() => setHttps(false)}
|
||||
>
|
||||
{t("http")}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
{errors["type"]}
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
<Radio
|
||||
name="type"
|
||||
title={t("pingMonitoring")}
|
||||
desc={t("pingMonitoringDescription")}
|
||||
size="small"
|
||||
value="ping"
|
||||
checked={monitor.type === "ping"}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<Radio
|
||||
name="type"
|
||||
title={t("dockerContainerMonitoring")}
|
||||
desc={t("dockerContainerMonitoringDescription")}
|
||||
size="small"
|
||||
value="docker"
|
||||
checked={monitor.type === "docker"}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<Radio
|
||||
name="type"
|
||||
title={t("portMonitoring")}
|
||||
desc={t("portMonitoringDescription")}
|
||||
size="small"
|
||||
value="port"
|
||||
checked={monitor.type === "port"}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{errors["type"] ? (
|
||||
<Box className="error-container">
|
||||
<Typography
|
||||
component="p"
|
||||
className="input-error"
|
||||
color={theme.palette.error.contrastText}
|
||||
>
|
||||
{errors["type"]}
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
</ConfigBox>
|
||||
)}
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
@@ -313,30 +511,39 @@ const CreateMonitor = () => {
|
||||
{t("settingsGeneralSettings")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
{t(`uptimeGeneralInstructions.${monitor.type}`)}
|
||||
{isCreate
|
||||
? t(`uptimeGeneralInstructions.${monitor.type}`)
|
||||
: t("distributedUptimeCreateSelectURL")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(15)}>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<TextInput
|
||||
id="monitor-url"
|
||||
name="url"
|
||||
type={monitor.type === "http" ? "url" : "text"}
|
||||
startAdornment={
|
||||
monitor.type === "http" ? <HttpAdornment https={https} /> : null
|
||||
type={monitor?.type === "http" ? "url" : "text"}
|
||||
label={
|
||||
(monitor.type === "http" || monitor.type === "port") && !isCreate
|
||||
? t("url")
|
||||
: monitorTypeMaps[monitor.type].label || t("urlMonitor")
|
||||
}
|
||||
label={monitorTypeMaps[monitor.type].label || "URL to monitor"}
|
||||
https={https}
|
||||
placeholder={monitorTypeMaps[monitor.type].placeholder || ""}
|
||||
value={monitor.url}
|
||||
onChange={onChange}
|
||||
error={errors["url"] ? true : false}
|
||||
value={parsedUrl?.host + parsedUrl?.pathname || monitor?.url || ""}
|
||||
https={isCreate ? https : protocol === "https"}
|
||||
startAdornment={
|
||||
monitor?.type === "http" && (
|
||||
<HttpAdornment https={isCreate ? https : protocol === "https"} />
|
||||
)
|
||||
}
|
||||
helperText={errors["url"]}
|
||||
onChange={onChange}
|
||||
disabled={!isCreate}
|
||||
/>
|
||||
<TextInput
|
||||
name="port"
|
||||
type="number"
|
||||
label={t("portToMonitor")}
|
||||
placeholder="5173"
|
||||
value={monitor.port}
|
||||
value={monitor.port || ""}
|
||||
onChange={onChange}
|
||||
error={errors["port"] ? true : false}
|
||||
helperText={errors["port"]}
|
||||
@@ -347,8 +554,8 @@ const CreateMonitor = () => {
|
||||
type="text"
|
||||
label={t("displayName")}
|
||||
isOptional={true}
|
||||
placeholder={monitorTypeMaps[monitor.type].namePlaceholder || ""}
|
||||
value={monitor.name}
|
||||
placeholder={monitorTypeMaps[monitor.type].namePlaceholder}
|
||||
value={monitor.name || ""}
|
||||
onChange={onChange}
|
||||
error={errors["name"] ? true : false}
|
||||
helperText={errors["name"]}
|
||||
@@ -357,12 +564,18 @@ const CreateMonitor = () => {
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">{t("notificationConfig.title")}</Typography>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("notificationConfig.title")}
|
||||
</Typography>
|
||||
<Typography component="p">{t("notificationConfig.description")}</Typography>
|
||||
</Box>
|
||||
<NotificationsConfig
|
||||
notifications={notifications}
|
||||
setMonitor={setMonitor}
|
||||
setNotifications={isCreate ? null : monitor.notifications}
|
||||
/>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
@@ -377,7 +590,7 @@ const CreateMonitor = () => {
|
||||
</Box>
|
||||
<Stack>
|
||||
<FormControlLabel
|
||||
sx={{ marginLeft: 0 }}
|
||||
sx={{ marginLeft: theme.spacing(0) }}
|
||||
control={
|
||||
<Switch
|
||||
name="ignoreTlsErrors"
|
||||
@@ -399,34 +612,36 @@ const CreateMonitor = () => {
|
||||
{t("distributedUptimeCreateAdvancedSettings")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(12)}>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<Select
|
||||
name="interval"
|
||||
label="Check frequency"
|
||||
value={monitor.interval || 1}
|
||||
onChange={onChange}
|
||||
items={SELECT_VALUES}
|
||||
/>
|
||||
<Checkbox
|
||||
name="useAdvancedMatching"
|
||||
label={t("advancedMatching")}
|
||||
isChecked={useAdvancedMatching}
|
||||
label={t("checkFrequency")}
|
||||
value={displayInterval}
|
||||
onChange={onChange}
|
||||
items={FREQUENCIES}
|
||||
/>
|
||||
{monitor.type === "http" && (
|
||||
<Checkbox
|
||||
name="useAdvancedMatching"
|
||||
label={t("advancedMatching")}
|
||||
isChecked={useAdvancedMatching}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
{monitor.type === "http" && useAdvancedMatching && (
|
||||
<>
|
||||
<Select
|
||||
name="matchMethod"
|
||||
label="Match Method"
|
||||
label={t("matchMethod")}
|
||||
value={monitor.matchMethod || "equal"}
|
||||
onChange={onChange}
|
||||
items={matchMethodOptions}
|
||||
/>
|
||||
<Stack>
|
||||
<TextInput
|
||||
name="expectedValue"
|
||||
type="text"
|
||||
label="Expected value"
|
||||
name="expectedValue"
|
||||
label={t("expectedValue")}
|
||||
isOptional={true}
|
||||
placeholder={
|
||||
expectedValuePlaceholders[monitor.matchMethod || "equal"]
|
||||
@@ -448,7 +663,7 @@ const CreateMonitor = () => {
|
||||
<TextInput
|
||||
name="jsonPath"
|
||||
type="text"
|
||||
label="JSON Path"
|
||||
label={t("uptimeAdvancedMatching.jsonPath")}
|
||||
isOptional={true}
|
||||
placeholder="data.status"
|
||||
value={monitor.jsonPath}
|
||||
@@ -461,7 +676,7 @@ const CreateMonitor = () => {
|
||||
color={theme.palette.primary.contrastTextTertiary}
|
||||
opacity={0.8}
|
||||
>
|
||||
{t("uptimeCreateJsonPath")}
|
||||
{t("uptimeCreateJsonPath") + " "}
|
||||
<Typography
|
||||
component="a"
|
||||
href="https://jmespath.org/"
|
||||
@@ -470,7 +685,7 @@ const CreateMonitor = () => {
|
||||
>
|
||||
jmespath.org
|
||||
</Typography>
|
||||
{t("uptimeCreateJsonPathQuery")}
|
||||
{" " + t("uptimeCreateJsonPathQuery")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</>
|
||||
@@ -486,14 +701,32 @@ const CreateMonitor = () => {
|
||||
variant="contained"
|
||||
color="accent"
|
||||
disabled={!Object.values(errors).every((value) => value === undefined)}
|
||||
loading={isCreating}
|
||||
loading={isBusy}
|
||||
sx={{ px: theme.spacing(12) }}
|
||||
>
|
||||
{t("createMonitor")}
|
||||
{t("settingsSave")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{!isCreate && (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
theme={theme}
|
||||
title={t("deleteDialogTitle")}
|
||||
description={t("deleteDialogDescription")}
|
||||
onCancel={() => setIsOpen(false)}
|
||||
confirmationButtonLabel={t("delete")}
|
||||
onConfirm={handleRemove}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateMonitor;
|
||||
UptimeCreate.propTypes = {
|
||||
isClone: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default UptimeCreate;
|
||||
|
||||
@@ -14,7 +14,6 @@ import AuthNewPasswordConfirmed from "../Pages/Auth/NewPasswordConfirmed";
|
||||
import Uptime from "../Pages/Uptime/Monitors";
|
||||
import UptimeDetails from "../Pages/Uptime/Details";
|
||||
import UptimeCreate from "../Pages/Uptime/Create";
|
||||
import UptimeConfigure from "../Pages/Uptime/Configure";
|
||||
|
||||
// PageSpeed
|
||||
import PageSpeed from "../Pages/PageSpeed/Monitors";
|
||||
@@ -81,16 +80,20 @@ const Routes = () => {
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/uptime/create/:monitorId?"
|
||||
path="/uptime/create"
|
||||
element={<UptimeCreate />}
|
||||
/>
|
||||
<Route
|
||||
path="/uptime/create/:monitorId"
|
||||
element={<UptimeCreate isClone={true} />}
|
||||
/>
|
||||
<Route
|
||||
path="/uptime/:monitorId/"
|
||||
element={<UptimeDetails />}
|
||||
/>
|
||||
<Route
|
||||
path="/uptime/configure/:monitorId/"
|
||||
element={<UptimeConfigure />}
|
||||
element={<UptimeCreate />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
|
||||
@@ -87,8 +87,10 @@ const newColors = {
|
||||
offBlack: "#131315",
|
||||
gray0: "#FDFDFD",
|
||||
gray10: "#F4F4FF",
|
||||
gray50: "#F9F9F9",
|
||||
gray100: "#F3F3F3",
|
||||
gray200: "#EFEFEF",
|
||||
gray250: "#DADADA",
|
||||
gray500: "#A2A3A3",
|
||||
gray900: "#1c1c1c",
|
||||
blueGray50: "#E8F0FE",
|
||||
@@ -173,7 +175,7 @@ const newSemanticColors = {
|
||||
dark: newColors.blueGray800,
|
||||
},
|
||||
lowContrast: {
|
||||
light: newColors.gray200,
|
||||
light: newColors.gray250,
|
||||
dark: newColors.blueGray600,
|
||||
},
|
||||
},
|
||||
@@ -206,6 +208,18 @@ const newSemanticColors = {
|
||||
light: newColors.blueGray800,
|
||||
dark: newColors.gray100,
|
||||
},
|
||||
background: {
|
||||
light: newColors.gray50,
|
||||
dark: newColors.offBlack,
|
||||
},
|
||||
border: {
|
||||
light: newColors.gray500,
|
||||
dark: newColors.blueGray600,
|
||||
},
|
||||
cardShadow: {
|
||||
light: "0 0 0 1px rgba(0, 0, 0, 0.04), 0 12px 24px rgba(0, 0, 0, 0.08)",
|
||||
dark: "0 2px 10px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.04)",
|
||||
},
|
||||
},
|
||||
success: {
|
||||
main: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { typographyLevels } from "./constants";
|
||||
const fontFamilyPrimary = '"Inter" , sans-serif';
|
||||
import { darken } from "@mui/material/styles";
|
||||
// const fontFamilySecondary = '"Avenir", sans-serif';
|
||||
|
||||
/* TODO take the color out from here */
|
||||
@@ -55,6 +56,20 @@ const baseTheme = (palette) => ({
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
variants: [
|
||||
{
|
||||
props: (props) => props.variant === "contained" && props.color === "accent",
|
||||
style: {
|
||||
backgroundColor: theme.palette.accent.main,
|
||||
color: theme.palette.primary.contrastTextSecondaryDarkBg,
|
||||
letterSpacing: "0.5px",
|
||||
textShadow: "0 0 1px rgba(0, 0, 0, 0.15)",
|
||||
"&:hover": {
|
||||
backgroundColor: darken(theme.palette.accent.darker, 0.05),
|
||||
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.1)`,
|
||||
transition: "all 0.2s ease-in-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
props: (props) => props.color === "accent",
|
||||
style: {
|
||||
@@ -217,16 +232,16 @@ const baseTheme = (palette) => ({
|
||||
},
|
||||
MuiList: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
root: ({ theme }) => ({
|
||||
padding: 0,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiListItemButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
transition: "none",
|
||||
},
|
||||
root: ({ theme }) => ({
|
||||
transition: "background-color .3s",
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiListItemText: {
|
||||
@@ -263,6 +278,7 @@ const baseTheme = (palette) => ({
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
MuiTableHead: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
@@ -566,7 +582,7 @@ const baseTheme = (palette) => ({
|
||||
root: ({ theme }) => ({
|
||||
ml: "auto",
|
||||
"& .MuiButtonBase-root, & .MuiButtonBase-root:hover": {
|
||||
borderColor: theme.palette.primary.contrastBorder,
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
width: "auto",
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
@@ -707,6 +723,14 @@ const baseTheme = (palette) => ({
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
MuiTooltip: {
|
||||
styleOverrides: {
|
||||
tooltip: () => ({
|
||||
fontSize: typographyLevels.m,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 2,
|
||||
|
||||
@@ -302,6 +302,14 @@ const settingsValidation = joi.object({
|
||||
systemEmailIgnoreTLS: joi.boolean(),
|
||||
systemEmailRequireTLS: joi.boolean(),
|
||||
systemEmailRejectUnauthorized: joi.boolean(),
|
||||
globalThresholds: joi
|
||||
.object({
|
||||
cpu: joi.number().min(1).max(100).allow("").optional(),
|
||||
memory: joi.number().min(1).max(100).allow("").optional(),
|
||||
disk: joi.number().min(1).max(100).allow("").optional(),
|
||||
temperature: joi.number().min(1).max(150).allow("").optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
const dayjsValidator = (value, helpers) => {
|
||||
|
||||
BIN
client/src/assets/Animations/darkmodeOutput.gif
Normal file
|
After Width: | Height: | Size: 305 KiB |
BIN
client/src/assets/Animations/output.gif
Normal file
|
After Width: | Height: | Size: 185 KiB |
3
client/src/assets/icons/alert-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.35395 21C10.0591 21.6224 10.9853 22 11.9998 22C13.0142 22 13.9405 21.6224 14.6456 21M17.9998 8C17.9998 6.4087 17.3676 4.88258 16.2424 3.75736C15.1172 2.63214 13.5911 2 11.9998 2C10.4085 2 8.88235 2.63214 7.75713 3.75736C6.63192 4.88258 5.99977 6.4087 5.99977 8C5.99977 11.0902 5.22024 13.206 4.34944 14.6054C3.6149 15.7859 3.24763 16.3761 3.2611 16.5408C3.27601 16.7231 3.31463 16.7926 3.46155 16.9016C3.59423 17 4.19237 17 5.38863 17H18.6109C19.8072 17 20.4053 17 20.538 16.9016C20.6849 16.7926 20.7235 16.7231 20.7384 16.5408C20.7519 16.3761 20.3846 15.7859 19.6501 14.6054C18.7793 13.206 17.9998 11.0902 17.9998 8Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 817 B |
3
client/src/assets/icons/check-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 12L10.5 15L16.5 9M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 332 B |
3
client/src/assets/icons/close-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 9L9 15M9 9L15 15M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 330 B |
@@ -1,3 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 17L13 12L18 7M11 17L6 12L11 7" stroke="#667085" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<path d="M18 17L13 12L18 7M11 17L6 12L11 7" stroke="currentColor" stroke-width="1.8"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 232 B After Width: | Height: | Size: 249 B |
@@ -1,3 +1,4 @@
|
||||
<svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17 7H1M1 7L7 13M1 7L7 1" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<path d="M17 7H1M1 7L7 13M1 7L7 1" stroke="currentColor" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 240 B |
@@ -1,3 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 18L9 12L15 6" stroke="#667085" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 215 B After Width: | Height: | Size: 232 B |
@@ -1,3 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 17L11 12L6 7M13 17L18 12L13 7" stroke="#667085" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<path d="M6 17L11 12L6 7M13 17L18 12L13 7" stroke="currentColor" stroke-width="1.8"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 231 B After Width: | Height: | Size: 248 B |
@@ -1,3 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 18L15 12L9 6" stroke="#667085" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 214 B After Width: | Height: | Size: 231 B |
3
client/src/assets/icons/warning-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 8V12M12 16H12.01M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 330 B |
@@ -28,6 +28,8 @@ html {
|
||||
|
||||
--env-var-nav-bar-height: 70px;
|
||||
--env-var-side-bar-width: 250px;
|
||||
--env-var-side-bar-collapsed-width: 64px;
|
||||
--env-var-side-bar-auth-footer-height: 50px;
|
||||
|
||||
--env-var-spacing-1: 12px;
|
||||
--env-var-spacing-1-plus: 16px;
|
||||
|
||||
969
client/src/locales/ar.json
Normal file
@@ -0,0 +1,969 @@
|
||||
{
|
||||
"submit": "",
|
||||
"title": "",
|
||||
"distributedStatusHeaderText": "",
|
||||
"distributedStatusSubHeaderText": "",
|
||||
"settingsDisabled": "",
|
||||
"settingsSuccessSaved": "",
|
||||
"settingsFailedToSave": "",
|
||||
"settingsStatsCleared": "",
|
||||
"settingsFailedToClearStats": "",
|
||||
"settingsMonitorsDeleted": "",
|
||||
"settingsFailedToDeleteMonitors": "",
|
||||
"starPromptTitle": "",
|
||||
"starPromptDescription": "",
|
||||
"https": "",
|
||||
"http": "",
|
||||
"monitor": "",
|
||||
"aboutus": "",
|
||||
"now": "",
|
||||
"delete": "",
|
||||
"configure": "",
|
||||
"responseTime": "",
|
||||
"ms": "",
|
||||
"bar": "",
|
||||
"area": "",
|
||||
"country": "",
|
||||
"city": "",
|
||||
"response": "",
|
||||
"monitorStatusUp": "",
|
||||
"monitorStatusDown": "",
|
||||
"webhookSendSuccess": "",
|
||||
"webhookSendError": "",
|
||||
"webhookUnsupportedPlatform": "",
|
||||
"distributedRightCategoryTitle": "",
|
||||
"distributedStatusServerMonitors": "",
|
||||
"distributedStatusServerMonitorsDescription": "",
|
||||
"distributedUptimeCreateSelectURL": "",
|
||||
"distributedUptimeCreateChecks": "",
|
||||
"distributedUptimeCreateChecksDescription": "",
|
||||
"distributedUptimeCreateIncidentNotification": "",
|
||||
"distributedUptimeCreateIncidentDescription": "",
|
||||
"distributedUptimeCreateAdvancedSettings": "",
|
||||
"distributedUptimeDetailsNoMonitorHistory": "",
|
||||
"distributedUptimeDetailsStatusHeaderUptime": "",
|
||||
"distributedUptimeDetailsStatusHeaderLastUpdate": "",
|
||||
"notifications": {
|
||||
"enableNotifications": "",
|
||||
"testNotification": "",
|
||||
"addOrEditNotifications": "",
|
||||
"slack": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"discord": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"telegram": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"tokenLabel": "",
|
||||
"tokenPlaceholder": "",
|
||||
"chatIdLabel": "",
|
||||
"chatIdPlaceholder": "",
|
||||
"fieldsRequired": ""
|
||||
},
|
||||
"webhook": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"urlLabel": "",
|
||||
"urlPlaceholder": "",
|
||||
"urlRequired": ""
|
||||
},
|
||||
"testNotificationDevelop": "",
|
||||
"integrationButton": "",
|
||||
"testSuccess": "",
|
||||
"testFailed": "",
|
||||
"unsupportedType": "",
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
"create": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"fetch": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"delete": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"edit": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"test": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
}
|
||||
},
|
||||
"testLocale": "",
|
||||
"add": "",
|
||||
"monitors": "",
|
||||
"distributedUptimeStatusCreateStatusPage": "",
|
||||
"distributedUptimeStatusCreateStatusPageAccess": "",
|
||||
"distributedUptimeStatusCreateStatusPageReady": "",
|
||||
"distributedUptimeStatusBasicInfoHeader": "",
|
||||
"distributedUptimeStatusBasicInfoDescription": "",
|
||||
"distributedUptimeStatusLogoHeader": "",
|
||||
"distributedUptimeStatusLogoDescription": "",
|
||||
"distributedUptimeStatusLogoUploadButton": "",
|
||||
"distributedUptimeStatusStandardMonitorsHeader": "",
|
||||
"distributedUptimeStatusStandardMonitorsDescription": "",
|
||||
"distributedUptimeStatusCreateYour": "",
|
||||
"distributedUptimeStatusEditYour": "",
|
||||
"distributedUptimeStatusPublishedLabel": "",
|
||||
"distributedUptimeStatusCompanyNameLabel": "",
|
||||
"distributedUptimeStatusPageAddressLabel": "",
|
||||
"distributedUptimeStatus30Days": "",
|
||||
"distributedUptimeStatus60Days": "",
|
||||
"distributedUptimeStatus90Days": "",
|
||||
"distributedUptimeStatusPageNotSetUp": "",
|
||||
"distributedUptimeStatusContactAdmin": "",
|
||||
"distributedUptimeStatusPageNotPublic": "",
|
||||
"distributedUptimeStatusPageDeleteDialog": "",
|
||||
"distributedUptimeStatusPageDeleteConfirm": "",
|
||||
"distributedUptimeStatusPageDeleteDescription": "",
|
||||
"distributedUptimeStatusDevices": "",
|
||||
"distributedUptimeStatusUpt": "",
|
||||
"distributedUptimeStatusUptBurned": "",
|
||||
"distributedUptimeStatusUptLogo": "",
|
||||
"incidentsTableNoIncidents": "",
|
||||
"incidentsTablePaginationLabel": "",
|
||||
"incidentsTableMonitorName": "",
|
||||
"incidentsTableStatus": "",
|
||||
"incidentsTableDateTime": "",
|
||||
"incidentsTableStatusCode": "",
|
||||
"incidentsTableMessage": "",
|
||||
"incidentsOptionsHeader": "",
|
||||
"incidentsOptionsHeaderFilterBy": "",
|
||||
"incidentsOptionsHeaderFilterAll": "",
|
||||
"incidentsOptionsHeaderFilterDown": "",
|
||||
"incidentsOptionsHeaderFilterCannotResolve": "",
|
||||
"incidentsOptionsHeaderShow": "",
|
||||
"incidentsOptionsHeaderLastHour": "",
|
||||
"incidentsOptionsHeaderLastDay": "",
|
||||
"incidentsOptionsHeaderLastWeek": "",
|
||||
"incidentsOptionsPlaceholderAllServers": "",
|
||||
"infrastructureCreateYour": "",
|
||||
"infrastructureCreateGeneralSettingsDescription": "",
|
||||
"infrastructureServerRequirement": "",
|
||||
"infrastructureCustomizeAlerts": "",
|
||||
"infrastructureAlertNotificationDescription": "",
|
||||
"infrastructureCreateMonitor": "",
|
||||
"infrastructureProtocol": "",
|
||||
"infrastructureServerUrlLabel": "",
|
||||
"infrastructureDisplayNameLabel": "",
|
||||
"infrastructureAuthorizationSecretLabel": "",
|
||||
"gb": "",
|
||||
"mb": "",
|
||||
"mem": "",
|
||||
"memoryUsage": "",
|
||||
"cpu": "",
|
||||
"cpuUsage": "",
|
||||
"cpuTemperature": "",
|
||||
"diskUsage": "",
|
||||
"used": "",
|
||||
"total": "",
|
||||
"cores": "",
|
||||
"frequency": "",
|
||||
"status": "",
|
||||
"cpuPhysical": "",
|
||||
"cpuLogical": "",
|
||||
"cpuFrequency": "",
|
||||
"avgCpuTemperature": "",
|
||||
"memory": "",
|
||||
"disk": "",
|
||||
"uptime": "",
|
||||
"os": "",
|
||||
"host": "",
|
||||
"actions": "",
|
||||
"integrations": "",
|
||||
"integrationsPrism": "",
|
||||
"integrationsSlack": "",
|
||||
"integrationsSlackInfo": "",
|
||||
"integrationsDiscord": "",
|
||||
"integrationsDiscordInfo": "",
|
||||
"integrationsZapier": "",
|
||||
"integrationsZapierInfo": "",
|
||||
"commonSave": "",
|
||||
"createYour": "",
|
||||
"createMonitor": "",
|
||||
"pause": "",
|
||||
"resume": "",
|
||||
"editing": "",
|
||||
"url": "",
|
||||
"access": "",
|
||||
"timezone": "",
|
||||
"features": "",
|
||||
"administrator": "",
|
||||
"loginHere": "",
|
||||
"displayName": "",
|
||||
"urlMonitor": "",
|
||||
"portToMonitor": "",
|
||||
"websiteMonitoring": "",
|
||||
"websiteMonitoringDescription": "",
|
||||
"pingMonitoring": "",
|
||||
"pingMonitoringDescription": "",
|
||||
"dockerContainerMonitoring": "",
|
||||
"dockerContainerMonitoringDescription": "",
|
||||
"portMonitoring": "",
|
||||
"portMonitoringDescription": "",
|
||||
"createMaintenanceWindow": "",
|
||||
"createMaintenance": "",
|
||||
"editMaintenance": "",
|
||||
"maintenanceWindowName": "",
|
||||
"friendlyNameInput": "",
|
||||
"friendlyNamePlaceholder": "",
|
||||
"maintenanceRepeat": "",
|
||||
"maintenance": "",
|
||||
"duration": "",
|
||||
"addMonitors": "",
|
||||
"window": "",
|
||||
"cancel": "",
|
||||
"message": "",
|
||||
"low": "",
|
||||
"high": "",
|
||||
"statusCode": "",
|
||||
"date&Time": "",
|
||||
"type": "",
|
||||
"statusPageName": "",
|
||||
"publicURL": "",
|
||||
"repeat": "",
|
||||
"edit": "",
|
||||
"createA": "",
|
||||
"remove": "",
|
||||
"maintenanceWindowDescription": "",
|
||||
"startTime": "",
|
||||
"timeZoneInfo": "",
|
||||
"monitorsToApply": "",
|
||||
"nextWindow": "",
|
||||
"notFoundButton": "",
|
||||
"pageSpeedConfigureSettingsDescription": "",
|
||||
"monitorDisplayName": "",
|
||||
"whenNewIncident": "",
|
||||
"notifySMS": "",
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
"deleteDialogDescription": "",
|
||||
"pageSpeedMonitor": "",
|
||||
"shown": "",
|
||||
"ago": "",
|
||||
"companyName": "",
|
||||
"pageSpeedDetailsPerformanceReport": "",
|
||||
"pageSpeedDetailsPerformanceReportCalculator": "",
|
||||
"checkingEvery": "",
|
||||
"statusPageCreateSettings": "",
|
||||
"basicInformation": "",
|
||||
"statusPageCreateBasicInfoDescription": "",
|
||||
"statusPageCreateSelectTimeZoneDescription": "",
|
||||
"statusPageCreateAppearanceDescription": "",
|
||||
"statusPageCreateSettingsCheckboxLabel": "",
|
||||
"statusPageCreateBasicInfoStatusPageAddress": "",
|
||||
"statusPageCreateTabsContent": "",
|
||||
"statusPageCreateTabsContentDescription": "",
|
||||
"statusPageCreateTabsContentFeaturesDescription": "",
|
||||
"showCharts": "",
|
||||
"showUptimePercentage": "",
|
||||
"removeLogo": "",
|
||||
"statusPageStatus": "",
|
||||
"statusPageStatusContactAdmin": "",
|
||||
"statusPageStatusNotPublic": "",
|
||||
"statusPageStatusNoPage": "",
|
||||
"statusPageStatusServiceStatus": "",
|
||||
"deleteStatusPage": "",
|
||||
"deleteStatusPageConfirm": "",
|
||||
"deleteStatusPageDescription": "",
|
||||
"uptimeCreate": "",
|
||||
"uptimeCreateJsonPath": "",
|
||||
"uptimeCreateJsonPathQuery": "",
|
||||
"maintenanceTableActionMenuDialogTitle": "",
|
||||
"infrastructureEditYour": "",
|
||||
"infrastructureEditMonitor": "",
|
||||
"infrastructureMonitorCreated": "",
|
||||
"infrastructureMonitorUpdated": "",
|
||||
"errorInvalidTypeId": "",
|
||||
"errorInvalidFieldId": "",
|
||||
"inviteNoTokenFound": "",
|
||||
"pageSpeedWarning": "",
|
||||
"pageSpeedLearnMoreLink": "",
|
||||
"pageSpeedAddApiKey": "",
|
||||
"update": "",
|
||||
"invalidFileFormat": "",
|
||||
"invalidFileSize": "",
|
||||
"ClickUpload": "",
|
||||
"DragandDrop": "",
|
||||
"MaxSize": "",
|
||||
"SupportedFormats": "",
|
||||
"FirstName": "",
|
||||
"LastName": "",
|
||||
"EmailDescriptionText": "",
|
||||
"YourPhoto": "",
|
||||
"PhotoDescriptionText": "",
|
||||
"save": "",
|
||||
"DeleteDescriptionText": "",
|
||||
"DeleteAccountWarning": "",
|
||||
"DeleteWarningTitle": "",
|
||||
"bulkImport": {
|
||||
"title": "",
|
||||
"selectFileTips": "",
|
||||
"selectFileDescription": "",
|
||||
"selectFile": "",
|
||||
"parsingFailed": "",
|
||||
"uploadSuccess": "",
|
||||
"validationFailed": "",
|
||||
"noFileSelected": "",
|
||||
"fallbackPage": "",
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
},
|
||||
"DeleteAccountTitle": "",
|
||||
"DeleteAccountButton": "",
|
||||
"publicLink": "",
|
||||
"maskedPageSpeedKeyPlaceholder": "",
|
||||
"reset": "",
|
||||
"ignoreTLSError": "",
|
||||
"tlsErrorIgnored": "",
|
||||
"ignoreTLSErrorDescription": "",
|
||||
"createNew": "",
|
||||
"greeting": {
|
||||
"prepend": "",
|
||||
"append": "",
|
||||
"overview": ""
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "",
|
||||
"admin": "",
|
||||
"teamMember": "",
|
||||
"demoUser": ""
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "",
|
||||
"filter": {
|
||||
"all": "",
|
||||
"member": ""
|
||||
},
|
||||
"inviteTeamMember": "",
|
||||
"inviteNewTeamMember": "",
|
||||
"inviteDescription": "",
|
||||
"email": "",
|
||||
"selectRole": "",
|
||||
"inviteLink": "",
|
||||
"cancel": "",
|
||||
"noMembers": "",
|
||||
"getToken": "",
|
||||
"emailToken": "",
|
||||
"table": {
|
||||
"name": "",
|
||||
"email": "",
|
||||
"role": "",
|
||||
"created": ""
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "",
|
||||
"resumed": "",
|
||||
"active": ""
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "",
|
||||
"pagespeed": "",
|
||||
"infrastructure": "",
|
||||
"incidents": "",
|
||||
"statusPages": "",
|
||||
"maintenance": "",
|
||||
"integrations": "",
|
||||
"settings": "",
|
||||
"support": "",
|
||||
"discussions": "",
|
||||
"docs": "",
|
||||
"changelog": "",
|
||||
"profile": "",
|
||||
"password": "",
|
||||
"team": "",
|
||||
"logOut": "",
|
||||
"notifications": "",
|
||||
"logs": ""
|
||||
},
|
||||
"settingsEmailUser": "",
|
||||
"state": "",
|
||||
"statusBreadCrumbsStatusPages": "",
|
||||
"statusBreadCrumbsDetails": "",
|
||||
"commonSaving": "",
|
||||
"navControls": "",
|
||||
"incidentsPageTitle": "",
|
||||
"passwordPanel": {
|
||||
"passwordChangedSuccess": "",
|
||||
"passwordInputIncorrect": "",
|
||||
"currentPassword": "",
|
||||
"enterCurrentPassword": "",
|
||||
"newPassword": "",
|
||||
"enterNewPassword": "",
|
||||
"confirmNewPassword": "",
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
},
|
||||
"emailSent": "",
|
||||
"failedToSendEmail": "",
|
||||
"settingsTestEmailSuccess": "",
|
||||
"settingsTestEmailFailed": "",
|
||||
"settingsTestEmailFailedWithReason": "",
|
||||
"settingsTestEmailUnknownError": "",
|
||||
"statusMsg": {
|
||||
"paused": "",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"pending": ""
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
},
|
||||
"common": {
|
||||
"appName": "",
|
||||
"monitoringAgentName": "",
|
||||
"buttons": {
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"toasts": {
|
||||
"networkError": "",
|
||||
"checkConnection": "",
|
||||
"unknownError": ""
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"common": {
|
||||
"navigation": {
|
||||
"continue": "",
|
||||
"back": ""
|
||||
},
|
||||
"inputs": {
|
||||
"email": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"invalid": ""
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "",
|
||||
"rules": {
|
||||
"length": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"special": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"number": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"uppercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"lowercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"match": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"uppercase": "",
|
||||
"lowercase": "",
|
||||
"number": "",
|
||||
"special": "",
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"passwordConfirm": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"different": ""
|
||||
}
|
||||
},
|
||||
"firstName": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
"lastName": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"pattern": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"validation": ""
|
||||
},
|
||||
"fields": {
|
||||
"password": {
|
||||
"errors": {
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"heading": "",
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": ""
|
||||
},
|
||||
"links": {
|
||||
"forgotPassword": "",
|
||||
"register": "",
|
||||
"forgotPasswordLink": "",
|
||||
"registerLink": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": "",
|
||||
"incorrectPassword": ""
|
||||
},
|
||||
"errors": {
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
},
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": "",
|
||||
"stepThree": ""
|
||||
},
|
||||
"description": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
},
|
||||
"gettingStartedButton": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
},
|
||||
"termsAndPolicies": "",
|
||||
"links": {
|
||||
"login": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": ""
|
||||
}
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "",
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": "",
|
||||
"stepThree": "",
|
||||
"stepFour": ""
|
||||
},
|
||||
"buttons": {
|
||||
"openEmail": "",
|
||||
"resetPassword": ""
|
||||
},
|
||||
"imageAlts": {
|
||||
"passwordKey": "",
|
||||
"email": "",
|
||||
"lock": "",
|
||||
"passwordConfirm": ""
|
||||
},
|
||||
"links": {
|
||||
"login": "",
|
||||
"resend": ""
|
||||
},
|
||||
"toasts": {
|
||||
"sent": "",
|
||||
"emailNotFound": "",
|
||||
"redirect": "",
|
||||
"success": "",
|
||||
"error": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorPages": {
|
||||
"serverUnreachable": {
|
||||
"toasts": {
|
||||
"reconnected": "",
|
||||
"stillUnreachable": ""
|
||||
},
|
||||
"alertBox": "",
|
||||
"description": "",
|
||||
"retryButton": {
|
||||
"default": "",
|
||||
"processing": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"createNotifications": {
|
||||
"title": "",
|
||||
"nameSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"nameLabel": "",
|
||||
"namePlaceholder": ""
|
||||
},
|
||||
"typeSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"typeLabel": ""
|
||||
},
|
||||
"emailSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"emailLabel": "",
|
||||
"emailPlaceholder": ""
|
||||
},
|
||||
"slackSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"pagerdutySettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"integrationKeyLabel": "",
|
||||
"integrationKeyPlaceholder": ""
|
||||
},
|
||||
"discordSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"webhookSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"testNotification": "",
|
||||
"dialogDeleteTitle": "",
|
||||
"dialogDeleteConfirm": ""
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
},
|
||||
"monitorStatus": {
|
||||
"checkingEvery": "",
|
||||
"withCaptureAgent": "",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"paused": ""
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"sendTestNotifications": "",
|
||||
"selectAll": "",
|
||||
"showAdminLoginLink": "",
|
||||
"logsPage": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"tabs": {
|
||||
"queue": "",
|
||||
"logs": "",
|
||||
"diagnostics": ""
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
},
|
||||
"logLevelSelect": {
|
||||
"title": "",
|
||||
"values": {
|
||||
"all": "",
|
||||
"info": "",
|
||||
"warn": "",
|
||||
"error": "",
|
||||
"debug": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"queuePage": {
|
||||
"title": "",
|
||||
"refreshButton": "",
|
||||
"flushButton": "",
|
||||
"jobTable": {
|
||||
"title": "",
|
||||
"idHeader": "",
|
||||
"urlHeader": "",
|
||||
"typeHeader": "",
|
||||
"activeHeader": "",
|
||||
"lockedAtHeader": "",
|
||||
"runCountHeader": "",
|
||||
"failCountHeader": "",
|
||||
"lastRunHeader": "",
|
||||
"lastFinishedAtHeader": "",
|
||||
"lastRunTookHeader": ""
|
||||
},
|
||||
"metricsTable": {
|
||||
"title": "",
|
||||
"metricHeader": "",
|
||||
"valueHeader": ""
|
||||
},
|
||||
"failedJobTable": {
|
||||
"title": "",
|
||||
"monitorIdHeader": "",
|
||||
"monitorUrlHeader": "",
|
||||
"failCountHeader": "",
|
||||
"failedAtHeader": "",
|
||||
"failReasonHeader": ""
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"title": "",
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"monitorActions": {
|
||||
"title": "",
|
||||
"import": "",
|
||||
"export": "",
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"details": ""
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
"labelDevelopedBy": "",
|
||||
"labelVersion": "",
|
||||
"title": ""
|
||||
},
|
||||
"demoMonitorsSettings": {
|
||||
"buttonAddMonitors": "",
|
||||
"description": "",
|
||||
"title": ""
|
||||
},
|
||||
"emailSettings": {
|
||||
"buttonSendTestEmail": "",
|
||||
"description": "",
|
||||
"descriptionTransport": "",
|
||||
"labelAddress": "",
|
||||
"labelConnectionHost": "",
|
||||
"labelHost": "",
|
||||
"labelIgnoreTLS": "",
|
||||
"labelPassword": "",
|
||||
"labelPasswordSet": "",
|
||||
"labelPool": "",
|
||||
"labelPort": "",
|
||||
"labelRejectUnauthorized": "",
|
||||
"labelRequireTLS": "",
|
||||
"labelSecure": "",
|
||||
"labelTLSServername": "",
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": "",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
"labelApiKeySet": "",
|
||||
"labelApiKey": "",
|
||||
"title": ""
|
||||
},
|
||||
"saveButtonLabel": "",
|
||||
"statsSettings": {
|
||||
"clearAllStatsButton": "",
|
||||
"clearAllStatsDescription": "",
|
||||
"clearAllStatsDialogConfirm": "",
|
||||
"clearAllStatsDialogDescription": "",
|
||||
"clearAllStatsDialogTitle": "",
|
||||
"description": "",
|
||||
"labelTTL": "",
|
||||
"labelTTLOptional": "",
|
||||
"title": ""
|
||||
},
|
||||
"systemResetSettings": {
|
||||
"buttonRemoveAllMonitors": "",
|
||||
"description": "",
|
||||
"dialogConfirm": "",
|
||||
"dialogDescription": "",
|
||||
"dialogTitle": "",
|
||||
"title": ""
|
||||
},
|
||||
"timezoneSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"title": ""
|
||||
},
|
||||
"title": "",
|
||||
"uiSettings": {
|
||||
"description": "",
|
||||
"labelLanguage": "",
|
||||
"labelTheme": "",
|
||||
"title": ""
|
||||
},
|
||||
"urlSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "",
|
||||
"failureResolveAll": "",
|
||||
"failureResolveMonitor": ""
|
||||
},
|
||||
"checkFormError": "",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
},
|
||||
"title": "",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
}
|
||||
969
client/src/locales/cs.json
Normal file
@@ -0,0 +1,969 @@
|
||||
{
|
||||
"submit": "Odeslat",
|
||||
"title": "",
|
||||
"distributedStatusHeaderText": "",
|
||||
"distributedStatusSubHeaderText": "",
|
||||
"settingsDisabled": "Vypnuto",
|
||||
"settingsSuccessSaved": "Nastavení bylo úspěšně uloženo",
|
||||
"settingsFailedToSave": "Nepodařilo se uložit nastavení",
|
||||
"settingsStatsCleared": "",
|
||||
"settingsFailedToClearStats": "",
|
||||
"settingsMonitorsDeleted": "",
|
||||
"settingsFailedToDeleteMonitors": "",
|
||||
"starPromptTitle": "",
|
||||
"starPromptDescription": "",
|
||||
"https": "",
|
||||
"http": "",
|
||||
"monitor": "",
|
||||
"aboutus": "",
|
||||
"now": "",
|
||||
"delete": "",
|
||||
"configure": "",
|
||||
"responseTime": "",
|
||||
"ms": "",
|
||||
"bar": "",
|
||||
"area": "",
|
||||
"country": "",
|
||||
"city": "",
|
||||
"response": "",
|
||||
"monitorStatusUp": "",
|
||||
"monitorStatusDown": "",
|
||||
"webhookSendSuccess": "",
|
||||
"webhookSendError": "",
|
||||
"webhookUnsupportedPlatform": "",
|
||||
"distributedRightCategoryTitle": "",
|
||||
"distributedStatusServerMonitors": "",
|
||||
"distributedStatusServerMonitorsDescription": "",
|
||||
"distributedUptimeCreateSelectURL": "",
|
||||
"distributedUptimeCreateChecks": "",
|
||||
"distributedUptimeCreateChecksDescription": "",
|
||||
"distributedUptimeCreateIncidentNotification": "",
|
||||
"distributedUptimeCreateIncidentDescription": "",
|
||||
"distributedUptimeCreateAdvancedSettings": "",
|
||||
"distributedUptimeDetailsNoMonitorHistory": "",
|
||||
"distributedUptimeDetailsStatusHeaderUptime": "",
|
||||
"distributedUptimeDetailsStatusHeaderLastUpdate": "",
|
||||
"notifications": {
|
||||
"enableNotifications": "",
|
||||
"testNotification": "",
|
||||
"addOrEditNotifications": "",
|
||||
"slack": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"discord": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"telegram": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"tokenLabel": "",
|
||||
"tokenPlaceholder": "",
|
||||
"chatIdLabel": "",
|
||||
"chatIdPlaceholder": "",
|
||||
"fieldsRequired": ""
|
||||
},
|
||||
"webhook": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"urlLabel": "",
|
||||
"urlPlaceholder": "",
|
||||
"urlRequired": ""
|
||||
},
|
||||
"testNotificationDevelop": "",
|
||||
"integrationButton": "",
|
||||
"testSuccess": "",
|
||||
"testFailed": "",
|
||||
"unsupportedType": "",
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
"create": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"fetch": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"delete": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"edit": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"test": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
}
|
||||
},
|
||||
"testLocale": "",
|
||||
"add": "",
|
||||
"monitors": "",
|
||||
"distributedUptimeStatusCreateStatusPage": "",
|
||||
"distributedUptimeStatusCreateStatusPageAccess": "",
|
||||
"distributedUptimeStatusCreateStatusPageReady": "",
|
||||
"distributedUptimeStatusBasicInfoHeader": "",
|
||||
"distributedUptimeStatusBasicInfoDescription": "",
|
||||
"distributedUptimeStatusLogoHeader": "",
|
||||
"distributedUptimeStatusLogoDescription": "",
|
||||
"distributedUptimeStatusLogoUploadButton": "",
|
||||
"distributedUptimeStatusStandardMonitorsHeader": "",
|
||||
"distributedUptimeStatusStandardMonitorsDescription": "",
|
||||
"distributedUptimeStatusCreateYour": "",
|
||||
"distributedUptimeStatusEditYour": "",
|
||||
"distributedUptimeStatusPublishedLabel": "",
|
||||
"distributedUptimeStatusCompanyNameLabel": "",
|
||||
"distributedUptimeStatusPageAddressLabel": "",
|
||||
"distributedUptimeStatus30Days": "",
|
||||
"distributedUptimeStatus60Days": "",
|
||||
"distributedUptimeStatus90Days": "",
|
||||
"distributedUptimeStatusPageNotSetUp": "",
|
||||
"distributedUptimeStatusContactAdmin": "",
|
||||
"distributedUptimeStatusPageNotPublic": "",
|
||||
"distributedUptimeStatusPageDeleteDialog": "",
|
||||
"distributedUptimeStatusPageDeleteConfirm": "",
|
||||
"distributedUptimeStatusPageDeleteDescription": "",
|
||||
"distributedUptimeStatusDevices": "",
|
||||
"distributedUptimeStatusUpt": "",
|
||||
"distributedUptimeStatusUptBurned": "",
|
||||
"distributedUptimeStatusUptLogo": "",
|
||||
"incidentsTableNoIncidents": "",
|
||||
"incidentsTablePaginationLabel": "",
|
||||
"incidentsTableMonitorName": "",
|
||||
"incidentsTableStatus": "",
|
||||
"incidentsTableDateTime": "",
|
||||
"incidentsTableStatusCode": "",
|
||||
"incidentsTableMessage": "",
|
||||
"incidentsOptionsHeader": "",
|
||||
"incidentsOptionsHeaderFilterBy": "",
|
||||
"incidentsOptionsHeaderFilterAll": "",
|
||||
"incidentsOptionsHeaderFilterDown": "",
|
||||
"incidentsOptionsHeaderFilterCannotResolve": "",
|
||||
"incidentsOptionsHeaderShow": "",
|
||||
"incidentsOptionsHeaderLastHour": "",
|
||||
"incidentsOptionsHeaderLastDay": "",
|
||||
"incidentsOptionsHeaderLastWeek": "",
|
||||
"incidentsOptionsPlaceholderAllServers": "",
|
||||
"infrastructureCreateYour": "",
|
||||
"infrastructureCreateGeneralSettingsDescription": "",
|
||||
"infrastructureServerRequirement": "",
|
||||
"infrastructureCustomizeAlerts": "",
|
||||
"infrastructureAlertNotificationDescription": "",
|
||||
"infrastructureCreateMonitor": "",
|
||||
"infrastructureProtocol": "",
|
||||
"infrastructureServerUrlLabel": "",
|
||||
"infrastructureDisplayNameLabel": "",
|
||||
"infrastructureAuthorizationSecretLabel": "",
|
||||
"gb": "",
|
||||
"mb": "",
|
||||
"mem": "",
|
||||
"memoryUsage": "",
|
||||
"cpu": "",
|
||||
"cpuUsage": "",
|
||||
"cpuTemperature": "",
|
||||
"diskUsage": "",
|
||||
"used": "",
|
||||
"total": "",
|
||||
"cores": "",
|
||||
"frequency": "",
|
||||
"status": "",
|
||||
"cpuPhysical": "",
|
||||
"cpuLogical": "",
|
||||
"cpuFrequency": "",
|
||||
"avgCpuTemperature": "",
|
||||
"memory": "",
|
||||
"disk": "",
|
||||
"uptime": "",
|
||||
"os": "",
|
||||
"host": "",
|
||||
"actions": "",
|
||||
"integrations": "",
|
||||
"integrationsPrism": "",
|
||||
"integrationsSlack": "",
|
||||
"integrationsSlackInfo": "",
|
||||
"integrationsDiscord": "",
|
||||
"integrationsDiscordInfo": "",
|
||||
"integrationsZapier": "",
|
||||
"integrationsZapierInfo": "",
|
||||
"commonSave": "",
|
||||
"createYour": "",
|
||||
"createMonitor": "",
|
||||
"pause": "",
|
||||
"resume": "",
|
||||
"editing": "",
|
||||
"url": "",
|
||||
"access": "",
|
||||
"timezone": "",
|
||||
"features": "",
|
||||
"administrator": "",
|
||||
"loginHere": "",
|
||||
"displayName": "",
|
||||
"urlMonitor": "",
|
||||
"portToMonitor": "",
|
||||
"websiteMonitoring": "",
|
||||
"websiteMonitoringDescription": "",
|
||||
"pingMonitoring": "",
|
||||
"pingMonitoringDescription": "",
|
||||
"dockerContainerMonitoring": "",
|
||||
"dockerContainerMonitoringDescription": "",
|
||||
"portMonitoring": "",
|
||||
"portMonitoringDescription": "",
|
||||
"createMaintenanceWindow": "",
|
||||
"createMaintenance": "",
|
||||
"editMaintenance": "",
|
||||
"maintenanceWindowName": "",
|
||||
"friendlyNameInput": "",
|
||||
"friendlyNamePlaceholder": "",
|
||||
"maintenanceRepeat": "",
|
||||
"maintenance": "",
|
||||
"duration": "",
|
||||
"addMonitors": "",
|
||||
"window": "",
|
||||
"cancel": "",
|
||||
"message": "",
|
||||
"low": "",
|
||||
"high": "",
|
||||
"statusCode": "",
|
||||
"date&Time": "",
|
||||
"type": "",
|
||||
"statusPageName": "",
|
||||
"publicURL": "",
|
||||
"repeat": "",
|
||||
"edit": "",
|
||||
"createA": "",
|
||||
"remove": "",
|
||||
"maintenanceWindowDescription": "",
|
||||
"startTime": "",
|
||||
"timeZoneInfo": "",
|
||||
"monitorsToApply": "",
|
||||
"nextWindow": "",
|
||||
"notFoundButton": "",
|
||||
"pageSpeedConfigureSettingsDescription": "",
|
||||
"monitorDisplayName": "",
|
||||
"whenNewIncident": "",
|
||||
"notifySMS": "",
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
"deleteDialogDescription": "",
|
||||
"pageSpeedMonitor": "",
|
||||
"shown": "",
|
||||
"ago": "",
|
||||
"companyName": "",
|
||||
"pageSpeedDetailsPerformanceReport": "",
|
||||
"pageSpeedDetailsPerformanceReportCalculator": "",
|
||||
"checkingEvery": "",
|
||||
"statusPageCreateSettings": "",
|
||||
"basicInformation": "",
|
||||
"statusPageCreateBasicInfoDescription": "",
|
||||
"statusPageCreateSelectTimeZoneDescription": "",
|
||||
"statusPageCreateAppearanceDescription": "",
|
||||
"statusPageCreateSettingsCheckboxLabel": "",
|
||||
"statusPageCreateBasicInfoStatusPageAddress": "",
|
||||
"statusPageCreateTabsContent": "",
|
||||
"statusPageCreateTabsContentDescription": "",
|
||||
"statusPageCreateTabsContentFeaturesDescription": "",
|
||||
"showCharts": "",
|
||||
"showUptimePercentage": "",
|
||||
"removeLogo": "",
|
||||
"statusPageStatus": "",
|
||||
"statusPageStatusContactAdmin": "",
|
||||
"statusPageStatusNotPublic": "",
|
||||
"statusPageStatusNoPage": "",
|
||||
"statusPageStatusServiceStatus": "",
|
||||
"deleteStatusPage": "",
|
||||
"deleteStatusPageConfirm": "",
|
||||
"deleteStatusPageDescription": "",
|
||||
"uptimeCreate": "",
|
||||
"uptimeCreateJsonPath": "",
|
||||
"uptimeCreateJsonPathQuery": "",
|
||||
"maintenanceTableActionMenuDialogTitle": "",
|
||||
"infrastructureEditYour": "",
|
||||
"infrastructureEditMonitor": "",
|
||||
"infrastructureMonitorCreated": "",
|
||||
"infrastructureMonitorUpdated": "",
|
||||
"errorInvalidTypeId": "",
|
||||
"errorInvalidFieldId": "",
|
||||
"inviteNoTokenFound": "",
|
||||
"pageSpeedWarning": "",
|
||||
"pageSpeedLearnMoreLink": "",
|
||||
"pageSpeedAddApiKey": "",
|
||||
"update": "",
|
||||
"invalidFileFormat": "",
|
||||
"invalidFileSize": "",
|
||||
"ClickUpload": "",
|
||||
"DragandDrop": "",
|
||||
"MaxSize": "",
|
||||
"SupportedFormats": "",
|
||||
"FirstName": "",
|
||||
"LastName": "",
|
||||
"EmailDescriptionText": "",
|
||||
"YourPhoto": "",
|
||||
"PhotoDescriptionText": "",
|
||||
"save": "",
|
||||
"DeleteDescriptionText": "",
|
||||
"DeleteAccountWarning": "",
|
||||
"DeleteWarningTitle": "",
|
||||
"bulkImport": {
|
||||
"title": "",
|
||||
"selectFileTips": "",
|
||||
"selectFileDescription": "",
|
||||
"selectFile": "",
|
||||
"parsingFailed": "",
|
||||
"uploadSuccess": "",
|
||||
"validationFailed": "",
|
||||
"noFileSelected": "",
|
||||
"fallbackPage": "",
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
},
|
||||
"DeleteAccountTitle": "",
|
||||
"DeleteAccountButton": "",
|
||||
"publicLink": "",
|
||||
"maskedPageSpeedKeyPlaceholder": "",
|
||||
"reset": "",
|
||||
"ignoreTLSError": "",
|
||||
"tlsErrorIgnored": "",
|
||||
"ignoreTLSErrorDescription": "",
|
||||
"createNew": "",
|
||||
"greeting": {
|
||||
"prepend": "",
|
||||
"append": "",
|
||||
"overview": ""
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "",
|
||||
"admin": "",
|
||||
"teamMember": "",
|
||||
"demoUser": ""
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "",
|
||||
"filter": {
|
||||
"all": "",
|
||||
"member": ""
|
||||
},
|
||||
"inviteTeamMember": "",
|
||||
"inviteNewTeamMember": "",
|
||||
"inviteDescription": "",
|
||||
"email": "",
|
||||
"selectRole": "",
|
||||
"inviteLink": "",
|
||||
"cancel": "",
|
||||
"noMembers": "",
|
||||
"getToken": "",
|
||||
"emailToken": "",
|
||||
"table": {
|
||||
"name": "",
|
||||
"email": "",
|
||||
"role": "",
|
||||
"created": ""
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "",
|
||||
"resumed": "",
|
||||
"active": ""
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "",
|
||||
"pagespeed": "",
|
||||
"infrastructure": "",
|
||||
"incidents": "",
|
||||
"statusPages": "",
|
||||
"maintenance": "",
|
||||
"integrations": "",
|
||||
"settings": "",
|
||||
"support": "",
|
||||
"discussions": "",
|
||||
"docs": "",
|
||||
"changelog": "",
|
||||
"profile": "",
|
||||
"password": "",
|
||||
"team": "",
|
||||
"logOut": "",
|
||||
"notifications": "",
|
||||
"logs": ""
|
||||
},
|
||||
"settingsEmailUser": "",
|
||||
"state": "",
|
||||
"statusBreadCrumbsStatusPages": "",
|
||||
"statusBreadCrumbsDetails": "",
|
||||
"commonSaving": "",
|
||||
"navControls": "",
|
||||
"incidentsPageTitle": "",
|
||||
"passwordPanel": {
|
||||
"passwordChangedSuccess": "",
|
||||
"passwordInputIncorrect": "",
|
||||
"currentPassword": "",
|
||||
"enterCurrentPassword": "",
|
||||
"newPassword": "",
|
||||
"enterNewPassword": "",
|
||||
"confirmNewPassword": "",
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
},
|
||||
"emailSent": "",
|
||||
"failedToSendEmail": "",
|
||||
"settingsTestEmailSuccess": "",
|
||||
"settingsTestEmailFailed": "",
|
||||
"settingsTestEmailFailedWithReason": "",
|
||||
"settingsTestEmailUnknownError": "",
|
||||
"statusMsg": {
|
||||
"paused": "",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"pending": ""
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
},
|
||||
"common": {
|
||||
"appName": "Checkmate",
|
||||
"monitoringAgentName": "Capture",
|
||||
"buttons": {
|
||||
"toggleTheme": "Přepnout mezi světlým a tmavým motivem"
|
||||
},
|
||||
"toasts": {
|
||||
"networkError": "Chyba připojení k síti",
|
||||
"checkConnection": "Zkontrolujte prosím své připojení k síti",
|
||||
"unknownError": "Nastala neznámá chyba"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"common": {
|
||||
"navigation": {
|
||||
"continue": "Pokračovat",
|
||||
"back": "Zpět"
|
||||
},
|
||||
"inputs": {
|
||||
"email": {
|
||||
"label": "E-mail",
|
||||
"placeholder": "jan.novak@domena.cz",
|
||||
"errors": {
|
||||
"empty": "Zadejte prosím e-mailovou adresu",
|
||||
"invalid": "Překontrolujte si prosím správnost zadané e-mailové adresy"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "Heslo",
|
||||
"rules": {
|
||||
"length": {
|
||||
"beginning": "Heslo musí být dlouhé alespoň",
|
||||
"highlighted": "8 znaků"
|
||||
},
|
||||
"special": {
|
||||
"beginning": "Heslo musí obsahovat alespoň",
|
||||
"highlighted": "1 speciální znak"
|
||||
},
|
||||
"number": {
|
||||
"beginning": "Heslo musí obsahovat alespoň",
|
||||
"highlighted": "1 číslo"
|
||||
},
|
||||
"uppercase": {
|
||||
"beginning": "Heslo musí obsahovat alespoň",
|
||||
"highlighted": "1 velké písmeno"
|
||||
},
|
||||
"lowercase": {
|
||||
"beginning": "Heslo musí obsahovat alespoň",
|
||||
"highlighted": "1 malé písmeno"
|
||||
},
|
||||
"match": {
|
||||
"beginning": "Obě hesla se",
|
||||
"highlighted": "musí shodovat"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"empty": "Zadejte prosím heslo",
|
||||
"length": "Heslo musí být dlouhé alespoň 8 znaků",
|
||||
"uppercase": "Heslo musí obsahovat alespoň 1 velké písmeno",
|
||||
"lowercase": "Heslo musí obsahovat alespoň 1 malé písmeno",
|
||||
"number": "Heslo musí obsahovat alespoň 1 číslo",
|
||||
"special": "Heslo musí obsahovat alespoň 1 speciální znak",
|
||||
"incorrect": "Zadané heslo neodpovídá tomu, co bylo nastaveno"
|
||||
}
|
||||
},
|
||||
"passwordConfirm": {
|
||||
"label": "Potvrzení hesla",
|
||||
"placeholder": "Pro ověření správnosti zadejte heslo ještě jednou",
|
||||
"errors": {
|
||||
"empty": "Zadejte prosím své heslo ještě jednou, aby mohla být potvrzena jeho správnost (odhalí překlepy)",
|
||||
"different": "Zadaná hesla se liší, takže je nejspíše jedno z nich špatně zapsané"
|
||||
}
|
||||
},
|
||||
"firstName": {
|
||||
"label": "Jméno",
|
||||
"placeholder": "Jan",
|
||||
"errors": {
|
||||
"empty": "Zadejte prosím své křestní jméno",
|
||||
"length": "Jméno se musí vejít do 50 znaků",
|
||||
"pattern": "Součástí jména mohou být pouze písmena, mezery apostrofy nebo spojovníky"
|
||||
}
|
||||
},
|
||||
"lastName": {
|
||||
"label": "Příjmení",
|
||||
"placeholder": "Novák",
|
||||
"errors": {
|
||||
"empty": "Zadejte prosím své příjmení",
|
||||
"length": "Příjmení se musí vejít do 50 znaků",
|
||||
"pattern": "Součástí příjmení mohou být pouze písmena, mezery apostrofy nebo spojovníky"
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"validation": "Nastala chyba při ověřování dat."
|
||||
},
|
||||
"fields": {
|
||||
"password": {
|
||||
"errors": {
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"heading": "Přihlášení",
|
||||
"subheadings": {
|
||||
"stepOne": "Zadejte svůj e-mail",
|
||||
"stepTwo": "Zadejte své heslo"
|
||||
},
|
||||
"links": {
|
||||
"forgotPassword": "Zapomněli jste heslo? <a>Obnovte si ho</a>",
|
||||
"register": "Ještě nemáte účet? <a>Zaregistrujte se</a>",
|
||||
"forgotPasswordLink": "",
|
||||
"registerLink": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": "Vítejte zpět",
|
||||
"incorrectPassword": "Nesprávné heslo"
|
||||
},
|
||||
"errors": {
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
"superAdmin": "Vytvoření superadministrátora",
|
||||
"user": "Registrace"
|
||||
},
|
||||
"subheadings": {
|
||||
"stepOne": "Zadejte své osobní údaje",
|
||||
"stepTwo": "Zadejte svůj e-mail",
|
||||
"stepThree": "Nastavte si heslo"
|
||||
},
|
||||
"description": {
|
||||
"superAdmin": "Pro začátek vytvořte účet superadministrátora",
|
||||
"user": "Zaregistrujte se jako uživatel a požádejte svého superadministrátora, aby vám přidělil oprávnění k hlídačům"
|
||||
},
|
||||
"gettingStartedButton": {
|
||||
"superAdmin": "Vytvořit účet superadministrátora",
|
||||
"user": "Zaregistrovat se jako běžný uživatel"
|
||||
},
|
||||
"termsAndPolicies": "Vytvořením účtu souhlasíte s našimi <a1>Podmínkami použití</a1> (anglicky) a <a2>Podmínkami zpracování osobních údajů</a2> (anglicky).",
|
||||
"links": {
|
||||
"login": "Máte již vytvořený účet? <a>Přihlašte se</a>"
|
||||
},
|
||||
"toasts": {
|
||||
"success": "Vítejte! Váš účet byl úspěšně vytvořen."
|
||||
}
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "Zapomenuté heslo",
|
||||
"subheadings": {
|
||||
"stepOne": "Nebojte se, zašleme vám pokyny pro obnovení hesla.",
|
||||
"stepTwo": "Odkaz pro obnovení hesla byl odeslán na <email/>",
|
||||
"stepThree": "Nové heslo se musí lišit od těch, které jste již použili.",
|
||||
"stepFour": "Heslo bylo úspěšně obnoveno. Klepněte na tlačítko a dojde k čarovnému přihlášení."
|
||||
},
|
||||
"buttons": {
|
||||
"openEmail": "Otevřít e-mailovou aplikaci",
|
||||
"resetPassword": "Obnovit heslo"
|
||||
},
|
||||
"imageAlts": {
|
||||
"passwordKey": "Ikonka přístupového klíče",
|
||||
"email": "Ikonka e-mailu",
|
||||
"lock": "Ikonka zámku",
|
||||
"passwordConfirm": "Ikonka potvrzeného hesla"
|
||||
},
|
||||
"links": {
|
||||
"login": "Vrátit se k <a>Přihlášení</a>",
|
||||
"resend": "Nedorazil vám e-mail? <a>Klepněte pro opětovné zaslání</a>"
|
||||
},
|
||||
"toasts": {
|
||||
"sent": "Instrukce vám dorazí na <email/>.",
|
||||
"emailNotFound": "Nepodařilo se nalézt e-mailovou adresu.",
|
||||
"redirect": "Přesměrování proběhne za <seconds/>...",
|
||||
"success": "Heslo bylo úspěšně obnoveno.",
|
||||
"error": "Obnova hesla se nepodařila. Zkuste to prosím znovu nebo kontaktujte podporu."
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorPages": {
|
||||
"serverUnreachable": {
|
||||
"toasts": {
|
||||
"reconnected": "Podařilo se úspěšně obnovit spojení se serverem",
|
||||
"stillUnreachable": "Server je stále nedosažitelný. Zkuste to prosím později."
|
||||
},
|
||||
"alertBox": "Chyba připojení k serveru",
|
||||
"description": "Nepodařilo se připojit k serveru. Zkontrolujte si prosím internetové připojení a pokud problém přetrvává, ověřte konfiguraci aplikace Checkmate.",
|
||||
"retryButton": {
|
||||
"default": "Zkusit se znovu připojit",
|
||||
"processing": "Probíhá připojování…"
|
||||
}
|
||||
}
|
||||
},
|
||||
"createNotifications": {
|
||||
"title": "",
|
||||
"nameSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"nameLabel": "",
|
||||
"namePlaceholder": ""
|
||||
},
|
||||
"typeSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"typeLabel": ""
|
||||
},
|
||||
"emailSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"emailLabel": "",
|
||||
"emailPlaceholder": ""
|
||||
},
|
||||
"slackSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"pagerdutySettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"integrationKeyLabel": "",
|
||||
"integrationKeyPlaceholder": ""
|
||||
},
|
||||
"discordSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"webhookSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"testNotification": "",
|
||||
"dialogDeleteTitle": "",
|
||||
"dialogDeleteConfirm": ""
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
},
|
||||
"monitorStatus": {
|
||||
"checkingEvery": "",
|
||||
"withCaptureAgent": "",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"paused": ""
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"sendTestNotifications": "",
|
||||
"selectAll": "",
|
||||
"showAdminLoginLink": "",
|
||||
"logsPage": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"tabs": {
|
||||
"queue": "",
|
||||
"logs": "",
|
||||
"diagnostics": ""
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
},
|
||||
"logLevelSelect": {
|
||||
"title": "",
|
||||
"values": {
|
||||
"all": "",
|
||||
"info": "",
|
||||
"warn": "",
|
||||
"error": "",
|
||||
"debug": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"queuePage": {
|
||||
"title": "",
|
||||
"refreshButton": "",
|
||||
"flushButton": "",
|
||||
"jobTable": {
|
||||
"title": "",
|
||||
"idHeader": "",
|
||||
"urlHeader": "",
|
||||
"typeHeader": "",
|
||||
"activeHeader": "",
|
||||
"lockedAtHeader": "",
|
||||
"runCountHeader": "",
|
||||
"failCountHeader": "",
|
||||
"lastRunHeader": "",
|
||||
"lastFinishedAtHeader": "",
|
||||
"lastRunTookHeader": ""
|
||||
},
|
||||
"metricsTable": {
|
||||
"title": "",
|
||||
"metricHeader": "",
|
||||
"valueHeader": ""
|
||||
},
|
||||
"failedJobTable": {
|
||||
"title": "",
|
||||
"monitorIdHeader": "",
|
||||
"monitorUrlHeader": "",
|
||||
"failCountHeader": "",
|
||||
"failedAtHeader": "",
|
||||
"failReasonHeader": ""
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"title": "",
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"monitorActions": {
|
||||
"title": "",
|
||||
"import": "",
|
||||
"export": "",
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"details": ""
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
"labelDevelopedBy": "",
|
||||
"labelVersion": "",
|
||||
"title": ""
|
||||
},
|
||||
"demoMonitorsSettings": {
|
||||
"buttonAddMonitors": "",
|
||||
"description": "",
|
||||
"title": ""
|
||||
},
|
||||
"emailSettings": {
|
||||
"buttonSendTestEmail": "",
|
||||
"description": "",
|
||||
"descriptionTransport": "",
|
||||
"labelAddress": "",
|
||||
"labelConnectionHost": "",
|
||||
"labelHost": "",
|
||||
"labelIgnoreTLS": "",
|
||||
"labelPassword": "",
|
||||
"labelPasswordSet": "",
|
||||
"labelPool": "",
|
||||
"labelPort": "",
|
||||
"labelRejectUnauthorized": "",
|
||||
"labelRequireTLS": "",
|
||||
"labelSecure": "",
|
||||
"labelTLSServername": "",
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": "",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
"labelApiKeySet": "",
|
||||
"labelApiKey": "",
|
||||
"title": ""
|
||||
},
|
||||
"saveButtonLabel": "",
|
||||
"statsSettings": {
|
||||
"clearAllStatsButton": "",
|
||||
"clearAllStatsDescription": "",
|
||||
"clearAllStatsDialogConfirm": "",
|
||||
"clearAllStatsDialogDescription": "",
|
||||
"clearAllStatsDialogTitle": "",
|
||||
"description": "",
|
||||
"labelTTL": "",
|
||||
"labelTTLOptional": "",
|
||||
"title": ""
|
||||
},
|
||||
"systemResetSettings": {
|
||||
"buttonRemoveAllMonitors": "",
|
||||
"description": "",
|
||||
"dialogConfirm": "",
|
||||
"dialogDescription": "",
|
||||
"dialogTitle": "",
|
||||
"title": ""
|
||||
},
|
||||
"timezoneSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"title": ""
|
||||
},
|
||||
"title": "",
|
||||
"uiSettings": {
|
||||
"description": "",
|
||||
"labelLanguage": "",
|
||||
"labelTheme": "",
|
||||
"title": ""
|
||||
},
|
||||
"urlSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "",
|
||||
"failureResolveAll": "",
|
||||
"failureResolveMonitor": ""
|
||||
},
|
||||
"checkFormError": "",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
},
|
||||
"title": "",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
}
|
||||
@@ -154,7 +154,7 @@
|
||||
"incorrect": "The password you provided does not match our records"
|
||||
}
|
||||
},
|
||||
"heading": "Log In",
|
||||
"heading": "Log in to continue",
|
||||
"links": {
|
||||
"forgotPassword": "Forgot password?",
|
||||
"forgotPasswordLink": "Reset password",
|
||||
@@ -168,7 +168,8 @@
|
||||
"toasts": {
|
||||
"incorrectPassword": "Incorrect password",
|
||||
"success": "Welcome back! You're successfully logged in."
|
||||
}
|
||||
},
|
||||
"welcome": "Welcome back to Checkmate!"
|
||||
},
|
||||
"registration": {
|
||||
"description": {
|
||||
@@ -378,6 +379,7 @@
|
||||
"dockerContainerMonitoringDescription": "Check whether your Docker container is running or not.",
|
||||
"duration": "Duration",
|
||||
"edit": "Edit",
|
||||
"editing": "Editing...",
|
||||
"editMaintenance": "Edit maintenance",
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
@@ -397,7 +399,6 @@
|
||||
"validationErrors": "Validation errors"
|
||||
}
|
||||
},
|
||||
"editing": "Editing...",
|
||||
"emailSent": "Email sent successfully",
|
||||
"errorInvalidFieldId": "Invalid field ID provided",
|
||||
"errorInvalidTypeId": "Invalid notification type provided",
|
||||
@@ -453,8 +454,8 @@
|
||||
"incidentsOptionsHeaderShow": "Show:",
|
||||
"incidentsOptionsHeaderTotalIncidents": "Total Incidents",
|
||||
"incidentsOptionsPlaceholderAllServers": "All servers",
|
||||
"incidentsPageActionResolveMonitor": "Resolve monitor incidents",
|
||||
"incidentsPageActionResolveAll": "Resolve all incidents",
|
||||
"incidentsPageActionResolveMonitor": "Resolve monitor incidents",
|
||||
"incidentsPageTitle": "Incidents",
|
||||
"incidentsTableActionResolve": "Resolve",
|
||||
"incidentsTableDateTime": "Date & Time",
|
||||
@@ -476,13 +477,13 @@
|
||||
"infrastructureEditYour": "Edit your",
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"actionButton": "Let's create your first infrastructure monitor!",
|
||||
"checks": [
|
||||
"Track the performance of your servers",
|
||||
"Identify bottlenecks and optimize usage",
|
||||
"Ensure reliability with real-time monitoring"
|
||||
],
|
||||
"title": "An infrastructure monitor is used to:",
|
||||
"actionButton": "Let's create your first infrastructure monitor!"
|
||||
"title": "An infrastructure monitor is used to:"
|
||||
}
|
||||
},
|
||||
"infrastructureMonitorCreated": "Infrastructure monitor created successfully!",
|
||||
@@ -530,19 +531,28 @@
|
||||
"maintenanceTableActionMenuDialogTitle": "Do you really want to remove this maintenance window?",
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"actionButton": "Let's create your first maintenance window!",
|
||||
"checks": [
|
||||
"Mark your maintenance periods",
|
||||
"Eliminate any misunderstandings",
|
||||
"Stop sending alerts in maintenance windows"
|
||||
],
|
||||
"title": "A maintenance window is used to:",
|
||||
"actionButton": "Let's create your first maintenance window!"
|
||||
"title": "A maintenance window is used to:"
|
||||
}
|
||||
},
|
||||
"maintenanceWindowDescription": "Your pings won't be sent during this time frame",
|
||||
"maintenanceWindowName": "Maintenance Window Name",
|
||||
"maskedPageSpeedKeyPlaceholder": "*************************************",
|
||||
"matchMethod": "Match Method",
|
||||
"matchMethodOptions": {
|
||||
"equal": "Equal",
|
||||
"equalPlaceholder": "success",
|
||||
"include": "Include",
|
||||
"includePlaceholder": "ok",
|
||||
"regex": "Regex",
|
||||
"regexPlaceholder": "^(success|ok)$",
|
||||
"text": "Match Method"
|
||||
},
|
||||
"mb": "MB",
|
||||
"mem": "Mem",
|
||||
"memory": "Memory",
|
||||
@@ -582,6 +592,7 @@
|
||||
"failureAddDemoMonitors": "Failed to add demo monitors",
|
||||
"successAddDemoMonitors": "Successfully added demo monitors"
|
||||
},
|
||||
"monitors": "monitors",
|
||||
"monitorState": {
|
||||
"active": "Active",
|
||||
"paused": "Paused",
|
||||
@@ -596,8 +607,29 @@
|
||||
},
|
||||
"monitorStatusDown": "Monitor {name} ({url}) is DOWN and not responding",
|
||||
"monitorStatusUp": "Monitor {name} ({url}) is now UP and responding",
|
||||
"monitors": "monitors",
|
||||
"monitorsToApply": "Monitors to apply maintenance window to",
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "Container ID",
|
||||
"namePlaceholder": "My Container",
|
||||
"placeholder": "abcd1234"
|
||||
},
|
||||
"http": {
|
||||
"label": "URL to monitor",
|
||||
"namePlaceholder": "Google",
|
||||
"placeholder": "google.com"
|
||||
},
|
||||
"ping": {
|
||||
"label": "IP address to monitor",
|
||||
"namePlaceholder": "Google",
|
||||
"placeholder": "1.1.1.1"
|
||||
},
|
||||
"port": {
|
||||
"label": "URL to monitor",
|
||||
"namePlaceholder": "Localhost:5173",
|
||||
"placeholder": "localhost"
|
||||
}
|
||||
},
|
||||
"ms": "ms",
|
||||
"navControls": "Controls",
|
||||
"nextWindow": "Next window",
|
||||
@@ -631,13 +663,13 @@
|
||||
},
|
||||
"enableNotifications": "Enable {{platform}} notifications",
|
||||
"fallback": {
|
||||
"actionButton": "Let's create your first notification channel!",
|
||||
"checks": [
|
||||
"Alert teams about downtime or performance issues",
|
||||
"Let engineers know when incidents happen",
|
||||
"Keep administrators informed of system changes"
|
||||
],
|
||||
"title": "A notification channel is used to:",
|
||||
"actionButton": "Let's create your first notification channel!"
|
||||
"title": "A notification channel is used to:"
|
||||
},
|
||||
"fetch": {
|
||||
"failed": "Failed to fetch notifications",
|
||||
@@ -684,13 +716,13 @@
|
||||
"os": "OS",
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"actionButton": "Let's create your first PageSpeed monitor!",
|
||||
"checks": [
|
||||
"Report on the user experience of a page",
|
||||
"Help analyze webpage speed",
|
||||
"Give suggestions on how the page can be improved"
|
||||
],
|
||||
"title": "A PageSpeed monitor is used to:",
|
||||
"actionButton": "Let's create your first PageSpeed monitor!"
|
||||
"title": "A PageSpeed monitor is used to:"
|
||||
}
|
||||
},
|
||||
"pageSpeedAddApiKey": "to add your API key.",
|
||||
@@ -723,8 +755,8 @@
|
||||
"queuePage": {
|
||||
"failedJobTable": {
|
||||
"failCountHeader": "Fail count",
|
||||
"failReasonHeader": "Fail reason",
|
||||
"failedAtHeader": "Last failed at",
|
||||
"failReasonHeader": "Fail reason",
|
||||
"monitorIdHeader": "Monitor ID",
|
||||
"monitorUrlHeader": "Monitor URL",
|
||||
"title": "Failed jobs"
|
||||
@@ -842,6 +874,10 @@
|
||||
"title": "Display timezone"
|
||||
},
|
||||
"title": "Settings",
|
||||
"globalThresholds": {
|
||||
"title": "Global Thresholds",
|
||||
"description": "Configure global CPU, Memory, Disk, and Temperature thresholds. If a value is provided, it will automatically be enabled for monitoring."
|
||||
},
|
||||
"uiSettings": {
|
||||
"description": "Switch between light and dark mode, or change user interface language.",
|
||||
"labelLanguage": "Language",
|
||||
@@ -865,8 +901,8 @@
|
||||
"settingsTestEmailUnknownError": "Unknown error",
|
||||
"showAdminLoginLink": "Show \"Administrator? Login Here\" link on the status page",
|
||||
"showCharts": "Show charts",
|
||||
"showUptimePercentage": "Show uptime percentage",
|
||||
"shown": "Shown",
|
||||
"showUptimePercentage": "Show uptime percentage",
|
||||
"starPromptDescription": "See the latest releases and help grow the community on GitHub",
|
||||
"starPromptTitle": "Star Checkmate",
|
||||
"startTime": "Start time",
|
||||
@@ -886,6 +922,15 @@
|
||||
"createSuccess": "Status page created successfully",
|
||||
"deleteFailed": "Failed to delete status page",
|
||||
"deleteSuccess": "Status page deleted successfully",
|
||||
"fallback": {
|
||||
"actionButton": "Let's create your first status page!",
|
||||
"checks": [
|
||||
"Monitor and display the health of your services in real time",
|
||||
"Track multiple services and share their status",
|
||||
"Keep users informed about outages and performance"
|
||||
],
|
||||
"title": "A status page is used to:"
|
||||
},
|
||||
"generalSettings": "General settings",
|
||||
"updateSuccess": "Status page updated successfully"
|
||||
},
|
||||
@@ -908,23 +953,6 @@
|
||||
"statusPageStatusNoPage": "There's no status page here.",
|
||||
"statusPageStatusNotPublic": "This status page is not public.",
|
||||
"statusPageStatusServiceStatus": "Service status",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "Status page deleted successfully",
|
||||
"deleteFailed": "Failed to delete status page",
|
||||
"createSuccess": "Status page created successfully",
|
||||
"updateSuccess": "Status page updated successfully",
|
||||
"generalSettings": "General settings",
|
||||
"contents": "Contents",
|
||||
"fallback": {
|
||||
"checks": [
|
||||
"Monitor and display the health of your services in real time",
|
||||
"Track multiple services and share their status",
|
||||
"Keep users informed about outages and performance"
|
||||
],
|
||||
"title": "A status page is used to:",
|
||||
"actionButton": "Let's create your first status page!"
|
||||
}
|
||||
},
|
||||
"submit": "Submit",
|
||||
"teamPanel": {
|
||||
"cancel": "Cancel",
|
||||
@@ -952,22 +980,30 @@
|
||||
"testLocale": "testLocale",
|
||||
"testNotificationsDisabled": "There are no notifications setup for this monitor. You need to add one by clicking 'Configure' button",
|
||||
"time": {
|
||||
"fifteenSeconds": "15 seconds",
|
||||
"fiveMinutes": "5 minutes",
|
||||
"fourMinutes": "4 minutes",
|
||||
"oneDay": "1 day",
|
||||
"oneHour": "1 hour",
|
||||
"oneMinute": "1 minute",
|
||||
"oneWeek": "1 week",
|
||||
"tenMinutes": "10 minutes",
|
||||
"thirtySeconds": "30 seconds",
|
||||
"threeMinutes": "3 minutes",
|
||||
"twentyMinutes": "20 minutes"
|
||||
"twentyMinutes": "20 minutes",
|
||||
"twoMinutes": "2 minutes"
|
||||
},
|
||||
"timeZoneInfo": "All dates and times are in GMT+0 time zone.",
|
||||
"timezone": "Timezone",
|
||||
"timeZoneInfo": "All dates and times are in GMT+0 time zone.",
|
||||
"title": "Title",
|
||||
"tlsErrorIgnored": "TLS/SSL errors ignored",
|
||||
"total": "Total",
|
||||
"type": "Type",
|
||||
"update": "Update",
|
||||
"uptime": "Uptime",
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": "JSON Path"
|
||||
},
|
||||
"uptimeCreate": "The expected value is used to match against response result, and the match determines the status.",
|
||||
"uptimeCreateJsonPath": "This expression will be evaluated against the reponse JSON data and the result will be used to match against the expected value. See",
|
||||
"uptimeCreateJsonPathQuery": "for query language documentation.",
|
||||
@@ -979,14 +1015,14 @@
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"actionButton": "Let's create your first uptime monitor!",
|
||||
"checks": [
|
||||
"Check if websites or servers are online & responsive",
|
||||
"Alert teams about downtime or performance issues",
|
||||
"Monitor HTTP endpoints, pings, containers & ports",
|
||||
"Track historical uptime and reliability trends"
|
||||
],
|
||||
"title": "An uptime monitor is used to:",
|
||||
"actionButton": "Let's create your first uptime monitor!"
|
||||
"title": "An uptime monitor is used to:"
|
||||
}
|
||||
},
|
||||
"url": "URL",
|
||||
|
||||
969
client/src/locales/es.json
Normal file
@@ -0,0 +1,969 @@
|
||||
{
|
||||
"submit": "Enviar",
|
||||
"title": "Titulo",
|
||||
"distributedStatusHeaderText": "Cobertura en tiempo real, para dispositivos reales",
|
||||
"distributedStatusSubHeaderText": "Potenciado por millones de dispositivos alrededor del mundo, vea el rendimiento del sistema por región global, país o ciudad",
|
||||
"settingsDisabled": "Deshabilitado",
|
||||
"settingsSuccessSaved": "Ajustes guardados correctamente",
|
||||
"settingsFailedToSave": "Fallo al guardar los ajustes",
|
||||
"settingsStatsCleared": "Estadísticas eliminadas correctamente",
|
||||
"settingsFailedToClearStats": "Fallo al eliminar las estadísticas",
|
||||
"settingsMonitorsDeleted": "Todos los monitores eliminados correctamente",
|
||||
"settingsFailedToDeleteMonitors": "Fallo al eliminar todos los monitores",
|
||||
"starPromptTitle": "",
|
||||
"starPromptDescription": "Vea las últimas versiones y ayude a incrementar la comunidad en Github",
|
||||
"https": "HTTPS",
|
||||
"http": "HTTP",
|
||||
"monitor": "monitor",
|
||||
"aboutus": "Acerca de",
|
||||
"now": "Ahora",
|
||||
"delete": "Eliminar",
|
||||
"configure": "Configurar",
|
||||
"responseTime": "Tiempo de Respuesta",
|
||||
"ms": "ms",
|
||||
"bar": "Barra",
|
||||
"area": "Área",
|
||||
"country": "PAIS",
|
||||
"city": "CIUDAD",
|
||||
"response": "RESPUESTA",
|
||||
"monitorStatusUp": "El monitor {name} ({url}) está ahora activo y respondiendo",
|
||||
"monitorStatusDown": "El monitor {name} ({url}) está inactivo y no está respondiendo",
|
||||
"webhookSendSuccess": "Notificación webhook enviada correctamente",
|
||||
"webhookSendError": "Error al enviar notificación webhook a {platform}",
|
||||
"webhookUnsupportedPlatform": "Plataforma no soportada: {platform}",
|
||||
"distributedRightCategoryTitle": "Monitor",
|
||||
"distributedStatusServerMonitors": "Monitores de Servidores",
|
||||
"distributedStatusServerMonitorsDescription": "Monitoree el estado de los servidores relacionados",
|
||||
"distributedUptimeCreateSelectURL": "Aquí puede seleccionar la URL del anfitrión, junto con el tipo de monitor.",
|
||||
"distributedUptimeCreateChecks": "Verificaciones a realizar",
|
||||
"distributedUptimeCreateChecksDescription": "Usted siempre puede agregar o eliminar verificaciones luego de añadir su sitio.",
|
||||
"distributedUptimeCreateIncidentNotification": "Notificaciones de incidentes",
|
||||
"distributedUptimeCreateIncidentDescription": "Cuando ocurra un incidente, notificar a usuarios.",
|
||||
"distributedUptimeCreateAdvancedSettings": "Ajustes avanzados",
|
||||
"distributedUptimeDetailsNoMonitorHistory": "No hay historial de verificaciones para este monitor aún.",
|
||||
"distributedUptimeDetailsStatusHeaderUptime": "Tiempo de funcionamiento:",
|
||||
"distributedUptimeDetailsStatusHeaderLastUpdate": "Actualizado por última vez",
|
||||
"notifications": {
|
||||
"enableNotifications": "Habilitar notificaciones para {{platform}}",
|
||||
"testNotification": "Notificación de prueba",
|
||||
"addOrEditNotifications": "Agregar o eliminar notificaciones",
|
||||
"slack": {
|
||||
"label": "Slack",
|
||||
"description": "Para habilitar notificaciones de Slack, cree una aplicación de Slack y habilite los webhooks entrantes. Luego de eso, simplemente introduzca la URL del webhook aquí.",
|
||||
"webhookLabel": "URL del webhook",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"discord": {
|
||||
"label": "Discord",
|
||||
"description": "Para enviar datos a un canal de Discord desde Checkmate a través de notificaciones de Discord utilizando webhooks, puede utilizar la característica de webhooks entrantes de Discord.",
|
||||
"webhookLabel": "URL de Webhooks de Discord",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"telegram": {
|
||||
"label": "Telegram",
|
||||
"description": "Para habilitar las notificaciones de Telegram, cree un bot de Telegram utilizando BotFather, un bot oficial para crear y administrar bots de Telegram. Entonces, obtenga una clave API y un ID de chat y escríbalos aquí.",
|
||||
"tokenLabel": "La clave de su bot",
|
||||
"tokenPlaceholder": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
|
||||
"chatIdLabel": "El ID de su Chat",
|
||||
"chatIdPlaceholder": "-1001234567890",
|
||||
"fieldsRequired": ""
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhooks",
|
||||
"description": "Puede establecer un webhook personalizado para recibir notificaciones cuando ocurra un incidente.",
|
||||
"urlLabel": "URL del webhook",
|
||||
"urlPlaceholder": "https://your-server.com/webhook",
|
||||
"urlRequired": ""
|
||||
},
|
||||
"testNotificationDevelop": "Notificación de prueba 2",
|
||||
"integrationButton": "",
|
||||
"testSuccess": "",
|
||||
"testFailed": "",
|
||||
"unsupportedType": "",
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
"create": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"fetch": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"delete": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"edit": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"test": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
}
|
||||
},
|
||||
"testLocale": "testLocale",
|
||||
"add": "Agregar",
|
||||
"monitors": "monitores",
|
||||
"distributedUptimeStatusCreateStatusPage": "página de estado",
|
||||
"distributedUptimeStatusCreateStatusPageAccess": "",
|
||||
"distributedUptimeStatusCreateStatusPageReady": "",
|
||||
"distributedUptimeStatusBasicInfoHeader": "",
|
||||
"distributedUptimeStatusBasicInfoDescription": "",
|
||||
"distributedUptimeStatusLogoHeader": "",
|
||||
"distributedUptimeStatusLogoDescription": "",
|
||||
"distributedUptimeStatusLogoUploadButton": "",
|
||||
"distributedUptimeStatusStandardMonitorsHeader": "",
|
||||
"distributedUptimeStatusStandardMonitorsDescription": "",
|
||||
"distributedUptimeStatusCreateYour": "",
|
||||
"distributedUptimeStatusEditYour": "",
|
||||
"distributedUptimeStatusPublishedLabel": "",
|
||||
"distributedUptimeStatusCompanyNameLabel": "",
|
||||
"distributedUptimeStatusPageAddressLabel": "",
|
||||
"distributedUptimeStatus30Days": "",
|
||||
"distributedUptimeStatus60Days": "",
|
||||
"distributedUptimeStatus90Days": "",
|
||||
"distributedUptimeStatusPageNotSetUp": "",
|
||||
"distributedUptimeStatusContactAdmin": "",
|
||||
"distributedUptimeStatusPageNotPublic": "",
|
||||
"distributedUptimeStatusPageDeleteDialog": "",
|
||||
"distributedUptimeStatusPageDeleteConfirm": "",
|
||||
"distributedUptimeStatusPageDeleteDescription": "",
|
||||
"distributedUptimeStatusDevices": "",
|
||||
"distributedUptimeStatusUpt": "",
|
||||
"distributedUptimeStatusUptBurned": "",
|
||||
"distributedUptimeStatusUptLogo": "",
|
||||
"incidentsTableNoIncidents": "",
|
||||
"incidentsTablePaginationLabel": "",
|
||||
"incidentsTableMonitorName": "",
|
||||
"incidentsTableStatus": "",
|
||||
"incidentsTableDateTime": "",
|
||||
"incidentsTableStatusCode": "",
|
||||
"incidentsTableMessage": "",
|
||||
"incidentsOptionsHeader": "",
|
||||
"incidentsOptionsHeaderFilterBy": "",
|
||||
"incidentsOptionsHeaderFilterAll": "",
|
||||
"incidentsOptionsHeaderFilterDown": "",
|
||||
"incidentsOptionsHeaderFilterCannotResolve": "",
|
||||
"incidentsOptionsHeaderShow": "",
|
||||
"incidentsOptionsHeaderLastHour": "",
|
||||
"incidentsOptionsHeaderLastDay": "",
|
||||
"incidentsOptionsHeaderLastWeek": "",
|
||||
"incidentsOptionsPlaceholderAllServers": "",
|
||||
"infrastructureCreateYour": "",
|
||||
"infrastructureCreateGeneralSettingsDescription": "",
|
||||
"infrastructureServerRequirement": "",
|
||||
"infrastructureCustomizeAlerts": "",
|
||||
"infrastructureAlertNotificationDescription": "",
|
||||
"infrastructureCreateMonitor": "",
|
||||
"infrastructureProtocol": "",
|
||||
"infrastructureServerUrlLabel": "",
|
||||
"infrastructureDisplayNameLabel": "",
|
||||
"infrastructureAuthorizationSecretLabel": "",
|
||||
"gb": "",
|
||||
"mb": "",
|
||||
"mem": "",
|
||||
"memoryUsage": "",
|
||||
"cpu": "",
|
||||
"cpuUsage": "",
|
||||
"cpuTemperature": "",
|
||||
"diskUsage": "",
|
||||
"used": "",
|
||||
"total": "",
|
||||
"cores": "",
|
||||
"frequency": "",
|
||||
"status": "",
|
||||
"cpuPhysical": "",
|
||||
"cpuLogical": "",
|
||||
"cpuFrequency": "",
|
||||
"avgCpuTemperature": "",
|
||||
"memory": "",
|
||||
"disk": "",
|
||||
"uptime": "",
|
||||
"os": "",
|
||||
"host": "",
|
||||
"actions": "",
|
||||
"integrations": "",
|
||||
"integrationsPrism": "",
|
||||
"integrationsSlack": "",
|
||||
"integrationsSlackInfo": "",
|
||||
"integrationsDiscord": "",
|
||||
"integrationsDiscordInfo": "",
|
||||
"integrationsZapier": "",
|
||||
"integrationsZapierInfo": "",
|
||||
"commonSave": "",
|
||||
"createYour": "",
|
||||
"createMonitor": "",
|
||||
"pause": "",
|
||||
"resume": "",
|
||||
"editing": "",
|
||||
"url": "",
|
||||
"access": "",
|
||||
"timezone": "",
|
||||
"features": "",
|
||||
"administrator": "",
|
||||
"loginHere": "",
|
||||
"displayName": "",
|
||||
"urlMonitor": "",
|
||||
"portToMonitor": "",
|
||||
"websiteMonitoring": "",
|
||||
"websiteMonitoringDescription": "",
|
||||
"pingMonitoring": "",
|
||||
"pingMonitoringDescription": "",
|
||||
"dockerContainerMonitoring": "",
|
||||
"dockerContainerMonitoringDescription": "",
|
||||
"portMonitoring": "",
|
||||
"portMonitoringDescription": "",
|
||||
"createMaintenanceWindow": "",
|
||||
"createMaintenance": "",
|
||||
"editMaintenance": "",
|
||||
"maintenanceWindowName": "",
|
||||
"friendlyNameInput": "",
|
||||
"friendlyNamePlaceholder": "",
|
||||
"maintenanceRepeat": "",
|
||||
"maintenance": "",
|
||||
"duration": "",
|
||||
"addMonitors": "",
|
||||
"window": "",
|
||||
"cancel": "",
|
||||
"message": "",
|
||||
"low": "",
|
||||
"high": "",
|
||||
"statusCode": "",
|
||||
"date&Time": "",
|
||||
"type": "",
|
||||
"statusPageName": "",
|
||||
"publicURL": "",
|
||||
"repeat": "",
|
||||
"edit": "",
|
||||
"createA": "",
|
||||
"remove": "",
|
||||
"maintenanceWindowDescription": "",
|
||||
"startTime": "",
|
||||
"timeZoneInfo": "",
|
||||
"monitorsToApply": "",
|
||||
"nextWindow": "",
|
||||
"notFoundButton": "",
|
||||
"pageSpeedConfigureSettingsDescription": "",
|
||||
"monitorDisplayName": "",
|
||||
"whenNewIncident": "",
|
||||
"notifySMS": "",
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
"deleteDialogDescription": "",
|
||||
"pageSpeedMonitor": "",
|
||||
"shown": "",
|
||||
"ago": "",
|
||||
"companyName": "",
|
||||
"pageSpeedDetailsPerformanceReport": "",
|
||||
"pageSpeedDetailsPerformanceReportCalculator": "",
|
||||
"checkingEvery": "",
|
||||
"statusPageCreateSettings": "",
|
||||
"basicInformation": "",
|
||||
"statusPageCreateBasicInfoDescription": "",
|
||||
"statusPageCreateSelectTimeZoneDescription": "",
|
||||
"statusPageCreateAppearanceDescription": "",
|
||||
"statusPageCreateSettingsCheckboxLabel": "",
|
||||
"statusPageCreateBasicInfoStatusPageAddress": "",
|
||||
"statusPageCreateTabsContent": "",
|
||||
"statusPageCreateTabsContentDescription": "",
|
||||
"statusPageCreateTabsContentFeaturesDescription": "",
|
||||
"showCharts": "",
|
||||
"showUptimePercentage": "",
|
||||
"removeLogo": "",
|
||||
"statusPageStatus": "",
|
||||
"statusPageStatusContactAdmin": "",
|
||||
"statusPageStatusNotPublic": "",
|
||||
"statusPageStatusNoPage": "",
|
||||
"statusPageStatusServiceStatus": "",
|
||||
"deleteStatusPage": "",
|
||||
"deleteStatusPageConfirm": "",
|
||||
"deleteStatusPageDescription": "",
|
||||
"uptimeCreate": "",
|
||||
"uptimeCreateJsonPath": "",
|
||||
"uptimeCreateJsonPathQuery": "",
|
||||
"maintenanceTableActionMenuDialogTitle": "",
|
||||
"infrastructureEditYour": "",
|
||||
"infrastructureEditMonitor": "",
|
||||
"infrastructureMonitorCreated": "",
|
||||
"infrastructureMonitorUpdated": "",
|
||||
"errorInvalidTypeId": "",
|
||||
"errorInvalidFieldId": "",
|
||||
"inviteNoTokenFound": "",
|
||||
"pageSpeedWarning": "",
|
||||
"pageSpeedLearnMoreLink": "",
|
||||
"pageSpeedAddApiKey": "",
|
||||
"update": "",
|
||||
"invalidFileFormat": "",
|
||||
"invalidFileSize": "",
|
||||
"ClickUpload": "",
|
||||
"DragandDrop": "",
|
||||
"MaxSize": "",
|
||||
"SupportedFormats": "",
|
||||
"FirstName": "",
|
||||
"LastName": "",
|
||||
"EmailDescriptionText": "",
|
||||
"YourPhoto": "",
|
||||
"PhotoDescriptionText": "",
|
||||
"save": "",
|
||||
"DeleteDescriptionText": "",
|
||||
"DeleteAccountWarning": "",
|
||||
"DeleteWarningTitle": "",
|
||||
"bulkImport": {
|
||||
"title": "",
|
||||
"selectFileTips": "",
|
||||
"selectFileDescription": "",
|
||||
"selectFile": "",
|
||||
"parsingFailed": "",
|
||||
"uploadSuccess": "",
|
||||
"validationFailed": "",
|
||||
"noFileSelected": "",
|
||||
"fallbackPage": "",
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
},
|
||||
"DeleteAccountTitle": "",
|
||||
"DeleteAccountButton": "",
|
||||
"publicLink": "",
|
||||
"maskedPageSpeedKeyPlaceholder": "",
|
||||
"reset": "",
|
||||
"ignoreTLSError": "",
|
||||
"tlsErrorIgnored": "",
|
||||
"ignoreTLSErrorDescription": "",
|
||||
"createNew": "",
|
||||
"greeting": {
|
||||
"prepend": "",
|
||||
"append": "",
|
||||
"overview": ""
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "",
|
||||
"admin": "",
|
||||
"teamMember": "",
|
||||
"demoUser": ""
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "",
|
||||
"filter": {
|
||||
"all": "",
|
||||
"member": ""
|
||||
},
|
||||
"inviteTeamMember": "",
|
||||
"inviteNewTeamMember": "",
|
||||
"inviteDescription": "",
|
||||
"email": "",
|
||||
"selectRole": "",
|
||||
"inviteLink": "",
|
||||
"cancel": "",
|
||||
"noMembers": "",
|
||||
"getToken": "",
|
||||
"emailToken": "",
|
||||
"table": {
|
||||
"name": "",
|
||||
"email": "",
|
||||
"role": "",
|
||||
"created": ""
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "",
|
||||
"resumed": "",
|
||||
"active": ""
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "",
|
||||
"pagespeed": "",
|
||||
"infrastructure": "",
|
||||
"incidents": "",
|
||||
"statusPages": "",
|
||||
"maintenance": "",
|
||||
"integrations": "",
|
||||
"settings": "",
|
||||
"support": "",
|
||||
"discussions": "",
|
||||
"docs": "",
|
||||
"changelog": "",
|
||||
"profile": "",
|
||||
"password": "",
|
||||
"team": "",
|
||||
"logOut": "",
|
||||
"notifications": "",
|
||||
"logs": ""
|
||||
},
|
||||
"settingsEmailUser": "",
|
||||
"state": "",
|
||||
"statusBreadCrumbsStatusPages": "",
|
||||
"statusBreadCrumbsDetails": "",
|
||||
"commonSaving": "",
|
||||
"navControls": "",
|
||||
"incidentsPageTitle": "",
|
||||
"passwordPanel": {
|
||||
"passwordChangedSuccess": "",
|
||||
"passwordInputIncorrect": "",
|
||||
"currentPassword": "",
|
||||
"enterCurrentPassword": "",
|
||||
"newPassword": "",
|
||||
"enterNewPassword": "",
|
||||
"confirmNewPassword": "",
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
},
|
||||
"emailSent": "",
|
||||
"failedToSendEmail": "",
|
||||
"settingsTestEmailSuccess": "",
|
||||
"settingsTestEmailFailed": "",
|
||||
"settingsTestEmailFailedWithReason": "",
|
||||
"settingsTestEmailUnknownError": "",
|
||||
"statusMsg": {
|
||||
"paused": "",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"pending": ""
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
},
|
||||
"common": {
|
||||
"appName": "",
|
||||
"monitoringAgentName": "",
|
||||
"buttons": {
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"toasts": {
|
||||
"networkError": "",
|
||||
"checkConnection": "",
|
||||
"unknownError": ""
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"common": {
|
||||
"navigation": {
|
||||
"continue": "",
|
||||
"back": ""
|
||||
},
|
||||
"inputs": {
|
||||
"email": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"invalid": ""
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "",
|
||||
"rules": {
|
||||
"length": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"special": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"number": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"uppercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"lowercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"match": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"uppercase": "",
|
||||
"lowercase": "",
|
||||
"number": "",
|
||||
"special": "",
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"passwordConfirm": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"different": ""
|
||||
}
|
||||
},
|
||||
"firstName": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
"lastName": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"pattern": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"validation": ""
|
||||
},
|
||||
"fields": {
|
||||
"password": {
|
||||
"errors": {
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"heading": "",
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": ""
|
||||
},
|
||||
"links": {
|
||||
"forgotPassword": "",
|
||||
"register": "",
|
||||
"forgotPasswordLink": "",
|
||||
"registerLink": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": "",
|
||||
"incorrectPassword": ""
|
||||
},
|
||||
"errors": {
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
},
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": "",
|
||||
"stepThree": ""
|
||||
},
|
||||
"description": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
},
|
||||
"gettingStartedButton": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
},
|
||||
"termsAndPolicies": "",
|
||||
"links": {
|
||||
"login": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": ""
|
||||
}
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "",
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": "",
|
||||
"stepThree": "",
|
||||
"stepFour": ""
|
||||
},
|
||||
"buttons": {
|
||||
"openEmail": "",
|
||||
"resetPassword": ""
|
||||
},
|
||||
"imageAlts": {
|
||||
"passwordKey": "",
|
||||
"email": "",
|
||||
"lock": "",
|
||||
"passwordConfirm": ""
|
||||
},
|
||||
"links": {
|
||||
"login": "",
|
||||
"resend": ""
|
||||
},
|
||||
"toasts": {
|
||||
"sent": "",
|
||||
"emailNotFound": "",
|
||||
"redirect": "",
|
||||
"success": "",
|
||||
"error": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorPages": {
|
||||
"serverUnreachable": {
|
||||
"toasts": {
|
||||
"reconnected": "",
|
||||
"stillUnreachable": ""
|
||||
},
|
||||
"alertBox": "",
|
||||
"description": "",
|
||||
"retryButton": {
|
||||
"default": "",
|
||||
"processing": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"createNotifications": {
|
||||
"title": "",
|
||||
"nameSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"nameLabel": "",
|
||||
"namePlaceholder": ""
|
||||
},
|
||||
"typeSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"typeLabel": ""
|
||||
},
|
||||
"emailSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"emailLabel": "",
|
||||
"emailPlaceholder": ""
|
||||
},
|
||||
"slackSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"pagerdutySettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"integrationKeyLabel": "",
|
||||
"integrationKeyPlaceholder": ""
|
||||
},
|
||||
"discordSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"webhookSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"testNotification": "",
|
||||
"dialogDeleteTitle": "",
|
||||
"dialogDeleteConfirm": ""
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
},
|
||||
"monitorStatus": {
|
||||
"checkingEvery": "",
|
||||
"withCaptureAgent": "",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"paused": ""
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"sendTestNotifications": "",
|
||||
"selectAll": "",
|
||||
"showAdminLoginLink": "",
|
||||
"logsPage": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"tabs": {
|
||||
"queue": "",
|
||||
"logs": "",
|
||||
"diagnostics": ""
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
},
|
||||
"logLevelSelect": {
|
||||
"title": "",
|
||||
"values": {
|
||||
"all": "",
|
||||
"info": "",
|
||||
"warn": "",
|
||||
"error": "",
|
||||
"debug": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"queuePage": {
|
||||
"title": "",
|
||||
"refreshButton": "",
|
||||
"flushButton": "",
|
||||
"jobTable": {
|
||||
"title": "",
|
||||
"idHeader": "",
|
||||
"urlHeader": "",
|
||||
"typeHeader": "",
|
||||
"activeHeader": "",
|
||||
"lockedAtHeader": "",
|
||||
"runCountHeader": "",
|
||||
"failCountHeader": "",
|
||||
"lastRunHeader": "",
|
||||
"lastFinishedAtHeader": "",
|
||||
"lastRunTookHeader": ""
|
||||
},
|
||||
"metricsTable": {
|
||||
"title": "",
|
||||
"metricHeader": "",
|
||||
"valueHeader": ""
|
||||
},
|
||||
"failedJobTable": {
|
||||
"title": "",
|
||||
"monitorIdHeader": "",
|
||||
"monitorUrlHeader": "",
|
||||
"failCountHeader": "",
|
||||
"failedAtHeader": "",
|
||||
"failReasonHeader": ""
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"title": "",
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"monitorActions": {
|
||||
"title": "",
|
||||
"import": "",
|
||||
"export": "",
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"details": ""
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
"labelDevelopedBy": "",
|
||||
"labelVersion": "",
|
||||
"title": ""
|
||||
},
|
||||
"demoMonitorsSettings": {
|
||||
"buttonAddMonitors": "",
|
||||
"description": "",
|
||||
"title": ""
|
||||
},
|
||||
"emailSettings": {
|
||||
"buttonSendTestEmail": "",
|
||||
"description": "",
|
||||
"descriptionTransport": "",
|
||||
"labelAddress": "",
|
||||
"labelConnectionHost": "",
|
||||
"labelHost": "",
|
||||
"labelIgnoreTLS": "",
|
||||
"labelPassword": "",
|
||||
"labelPasswordSet": "",
|
||||
"labelPool": "",
|
||||
"labelPort": "",
|
||||
"labelRejectUnauthorized": "",
|
||||
"labelRequireTLS": "",
|
||||
"labelSecure": "",
|
||||
"labelTLSServername": "",
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": "",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
"labelApiKeySet": "",
|
||||
"labelApiKey": "",
|
||||
"title": ""
|
||||
},
|
||||
"saveButtonLabel": "",
|
||||
"statsSettings": {
|
||||
"clearAllStatsButton": "",
|
||||
"clearAllStatsDescription": "",
|
||||
"clearAllStatsDialogConfirm": "",
|
||||
"clearAllStatsDialogDescription": "",
|
||||
"clearAllStatsDialogTitle": "",
|
||||
"description": "",
|
||||
"labelTTL": "",
|
||||
"labelTTLOptional": "",
|
||||
"title": ""
|
||||
},
|
||||
"systemResetSettings": {
|
||||
"buttonRemoveAllMonitors": "",
|
||||
"description": "",
|
||||
"dialogConfirm": "",
|
||||
"dialogDescription": "",
|
||||
"dialogTitle": "",
|
||||
"title": ""
|
||||
},
|
||||
"timezoneSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"title": ""
|
||||
},
|
||||
"title": "",
|
||||
"uiSettings": {
|
||||
"description": "",
|
||||
"labelLanguage": "",
|
||||
"labelTheme": "",
|
||||
"title": ""
|
||||
},
|
||||
"urlSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "",
|
||||
"failureResolveAll": "",
|
||||
"failureResolveMonitor": ""
|
||||
},
|
||||
"checkFormError": "",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
},
|
||||
"title": "",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
}
|
||||
969
client/src/locales/fi.json
Normal file
@@ -0,0 +1,969 @@
|
||||
{
|
||||
"submit": "Lähetä",
|
||||
"title": "Otsikko",
|
||||
"distributedStatusHeaderText": "Reaaliaikainen kattavuus oikeilla laitteilla",
|
||||
"distributedStatusSubHeaderText": "Miljoonien laitteiden voimin ympäri maailman näet järjestelmän suorituskyvyn maailman alueittain, maittain tai kaupungeittain",
|
||||
"settingsDisabled": "Poistettu käytöstä",
|
||||
"settingsSuccessSaved": "",
|
||||
"settingsFailedToSave": "",
|
||||
"settingsStatsCleared": "",
|
||||
"settingsFailedToClearStats": "",
|
||||
"settingsMonitorsDeleted": "",
|
||||
"settingsFailedToDeleteMonitors": "",
|
||||
"starPromptTitle": "",
|
||||
"starPromptDescription": "",
|
||||
"https": "HTTPS",
|
||||
"http": "HTTP",
|
||||
"monitor": "Seurain",
|
||||
"aboutus": "Tietoja meistä",
|
||||
"now": "Nyt",
|
||||
"delete": "Poista",
|
||||
"configure": "Määritä",
|
||||
"responseTime": "Vasteaika",
|
||||
"ms": "ms",
|
||||
"bar": "Palkki",
|
||||
"area": "Alue",
|
||||
"country": "MAA",
|
||||
"city": "KAUPUNKI",
|
||||
"response": "Vastaus",
|
||||
"monitorStatusUp": "Valvontakohde {name} ({url}) on nyt toiminnassa ja vastaa",
|
||||
"monitorStatusDown": "Valvontakohde {name} ({url}) ei ole toiminnassa, eikä vastaa",
|
||||
"webhookSendSuccess": "Webhook-ilmoitus lähetettiin onnistuneesti",
|
||||
"webhookSendError": "Virhe lähetettäessä webhook-ilmoitusta palveluun {platform}",
|
||||
"webhookUnsupportedPlatform": "Ei tuettu palvelu: {platform}",
|
||||
"distributedRightCategoryTitle": "Valvonta",
|
||||
"distributedStatusServerMonitors": "Palvelin valvonta",
|
||||
"distributedStatusServerMonitorsDescription": "Valvo palvelimien tilaa",
|
||||
"distributedUptimeCreateSelectURL": "Tässä voit valita isäntäkohteen URL-osoitteen ja valvontatyypin.",
|
||||
"distributedUptimeCreateChecks": "Suoritettavat tarkistukset",
|
||||
"distributedUptimeCreateChecksDescription": "Voit aina lisätä tai poistaa tarkistuksia sivuston lisäämisen jälkeen.",
|
||||
"distributedUptimeCreateIncidentNotification": "Häiriöilmoitukset",
|
||||
"distributedUptimeCreateIncidentDescription": "Jos ilmenee häiriö, ilmoita käyttäjille.",
|
||||
"distributedUptimeCreateAdvancedSettings": "Lisäasetukset",
|
||||
"distributedUptimeDetailsNoMonitorHistory": "Tälle valvontakohteelle ei ole vielä tarkistushistoriaa.",
|
||||
"distributedUptimeDetailsStatusHeaderUptime": "",
|
||||
"distributedUptimeDetailsStatusHeaderLastUpdate": "Viimeksi päivitetty",
|
||||
"notifications": {
|
||||
"enableNotifications": "",
|
||||
"testNotification": "Testi-ilmoitus",
|
||||
"addOrEditNotifications": "Lisää tai muokkaa ilmoituksia",
|
||||
"slack": {
|
||||
"label": "Slack",
|
||||
"description": "",
|
||||
"webhookLabel": "Webhookin URL",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"discord": {
|
||||
"label": "Discord",
|
||||
"description": "",
|
||||
"webhookLabel": "Discord Webhookin URL-osoite",
|
||||
"webhookPlaceholder": "https://discord.com/api/webhooks/...",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"telegram": {
|
||||
"label": "Telegram",
|
||||
"description": "",
|
||||
"tokenLabel": "",
|
||||
"tokenPlaceholder": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
|
||||
"chatIdLabel": "",
|
||||
"chatIdPlaceholder": "-1001234567890",
|
||||
"fieldsRequired": ""
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhookit",
|
||||
"description": "",
|
||||
"urlLabel": "Webhookin URL",
|
||||
"urlPlaceholder": "https://your-server.com/webhook",
|
||||
"urlRequired": ""
|
||||
},
|
||||
"testNotificationDevelop": "Testi-ilmoitus 2",
|
||||
"integrationButton": "",
|
||||
"testSuccess": "Testi ilmoituksen lähetys onnistui!",
|
||||
"testFailed": "Testi ilmoituksen lähetys epäonnistui",
|
||||
"unsupportedType": "",
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
"create": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"fetch": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"delete": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"edit": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"test": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
}
|
||||
},
|
||||
"testLocale": "",
|
||||
"add": "Lisää",
|
||||
"monitors": "Seuraimet",
|
||||
"distributedUptimeStatusCreateStatusPage": "",
|
||||
"distributedUptimeStatusCreateStatusPageAccess": "",
|
||||
"distributedUptimeStatusCreateStatusPageReady": "",
|
||||
"distributedUptimeStatusBasicInfoHeader": "",
|
||||
"distributedUptimeStatusBasicInfoDescription": "",
|
||||
"distributedUptimeStatusLogoHeader": "Logo",
|
||||
"distributedUptimeStatusLogoDescription": "",
|
||||
"distributedUptimeStatusLogoUploadButton": "",
|
||||
"distributedUptimeStatusStandardMonitorsHeader": "",
|
||||
"distributedUptimeStatusStandardMonitorsDescription": "",
|
||||
"distributedUptimeStatusCreateYour": "",
|
||||
"distributedUptimeStatusEditYour": "",
|
||||
"distributedUptimeStatusPublishedLabel": "",
|
||||
"distributedUptimeStatusCompanyNameLabel": "Yrityksen nimi",
|
||||
"distributedUptimeStatusPageAddressLabel": "",
|
||||
"distributedUptimeStatus30Days": "30 päivää",
|
||||
"distributedUptimeStatus60Days": "60 päivää",
|
||||
"distributedUptimeStatus90Days": "90 päivää",
|
||||
"distributedUptimeStatusPageNotSetUp": "",
|
||||
"distributedUptimeStatusContactAdmin": "Ota yhteys järjestelmävalvojaasi",
|
||||
"distributedUptimeStatusPageNotPublic": "",
|
||||
"distributedUptimeStatusPageDeleteDialog": "",
|
||||
"distributedUptimeStatusPageDeleteConfirm": "",
|
||||
"distributedUptimeStatusPageDeleteDescription": "",
|
||||
"distributedUptimeStatusDevices": "",
|
||||
"distributedUptimeStatusUpt": "",
|
||||
"distributedUptimeStatusUptBurned": "",
|
||||
"distributedUptimeStatusUptLogo": "UPT logo",
|
||||
"incidentsTableNoIncidents": "",
|
||||
"incidentsTablePaginationLabel": "",
|
||||
"incidentsTableMonitorName": "",
|
||||
"incidentsTableStatus": "Tila",
|
||||
"incidentsTableDateTime": "Päivämäärä ja aika",
|
||||
"incidentsTableStatusCode": "",
|
||||
"incidentsTableMessage": "Viesti",
|
||||
"incidentsOptionsHeader": "",
|
||||
"incidentsOptionsHeaderFilterBy": "",
|
||||
"incidentsOptionsHeaderFilterAll": "Kaikki",
|
||||
"incidentsOptionsHeaderFilterDown": "Alhaalla",
|
||||
"incidentsOptionsHeaderFilterCannotResolve": "",
|
||||
"incidentsOptionsHeaderShow": "Näytä:",
|
||||
"incidentsOptionsHeaderLastHour": "Viime tunti",
|
||||
"incidentsOptionsHeaderLastDay": "Viime päivä",
|
||||
"incidentsOptionsHeaderLastWeek": "Viime viikko",
|
||||
"incidentsOptionsPlaceholderAllServers": "Kaikki palvelimet",
|
||||
"infrastructureCreateYour": "",
|
||||
"infrastructureCreateGeneralSettingsDescription": "",
|
||||
"infrastructureServerRequirement": "",
|
||||
"infrastructureCustomizeAlerts": "",
|
||||
"infrastructureAlertNotificationDescription": "",
|
||||
"infrastructureCreateMonitor": "",
|
||||
"infrastructureProtocol": "Protokolla",
|
||||
"infrastructureServerUrlLabel": "Palvelimen URL-osoite",
|
||||
"infrastructureDisplayNameLabel": "",
|
||||
"infrastructureAuthorizationSecretLabel": "",
|
||||
"gb": "",
|
||||
"mb": "",
|
||||
"mem": "",
|
||||
"memoryUsage": "Muistinkäyttö",
|
||||
"cpu": "",
|
||||
"cpuUsage": "",
|
||||
"cpuTemperature": "",
|
||||
"diskUsage": "",
|
||||
"used": "",
|
||||
"total": "",
|
||||
"cores": "",
|
||||
"frequency": "",
|
||||
"status": "Tila",
|
||||
"cpuPhysical": "",
|
||||
"cpuLogical": "",
|
||||
"cpuFrequency": "",
|
||||
"avgCpuTemperature": "",
|
||||
"memory": "Muisti",
|
||||
"disk": "",
|
||||
"uptime": "",
|
||||
"os": "",
|
||||
"host": "",
|
||||
"actions": "",
|
||||
"integrations": "",
|
||||
"integrationsPrism": "",
|
||||
"integrationsSlack": "Slack",
|
||||
"integrationsSlackInfo": "",
|
||||
"integrationsDiscord": "",
|
||||
"integrationsDiscordInfo": "",
|
||||
"integrationsZapier": "Zapier",
|
||||
"integrationsZapierInfo": "",
|
||||
"commonSave": "Tallenna",
|
||||
"createYour": "",
|
||||
"createMonitor": "",
|
||||
"pause": "",
|
||||
"resume": "",
|
||||
"editing": "",
|
||||
"url": "",
|
||||
"access": "",
|
||||
"timezone": "Aikavyöhyke",
|
||||
"features": "",
|
||||
"administrator": "",
|
||||
"loginHere": "",
|
||||
"displayName": "",
|
||||
"urlMonitor": "",
|
||||
"portToMonitor": "",
|
||||
"websiteMonitoring": "",
|
||||
"websiteMonitoringDescription": "",
|
||||
"pingMonitoring": "",
|
||||
"pingMonitoringDescription": "",
|
||||
"dockerContainerMonitoring": "",
|
||||
"dockerContainerMonitoringDescription": "",
|
||||
"portMonitoring": "",
|
||||
"portMonitoringDescription": "",
|
||||
"createMaintenanceWindow": "",
|
||||
"createMaintenance": "",
|
||||
"editMaintenance": "",
|
||||
"maintenanceWindowName": "",
|
||||
"friendlyNameInput": "",
|
||||
"friendlyNamePlaceholder": "",
|
||||
"maintenanceRepeat": "",
|
||||
"maintenance": "",
|
||||
"duration": "",
|
||||
"addMonitors": "",
|
||||
"window": "",
|
||||
"cancel": "Peruuttaa",
|
||||
"message": "Viesti",
|
||||
"low": "",
|
||||
"high": "",
|
||||
"statusCode": "Tilakoodi",
|
||||
"date&Time": "Päivämäärä ja aika",
|
||||
"type": "",
|
||||
"statusPageName": "",
|
||||
"publicURL": "Julkinen URL-osoite",
|
||||
"repeat": "",
|
||||
"edit": "Muokkaa",
|
||||
"createA": "",
|
||||
"remove": "",
|
||||
"maintenanceWindowDescription": "",
|
||||
"startTime": "",
|
||||
"timeZoneInfo": "",
|
||||
"monitorsToApply": "",
|
||||
"nextWindow": "",
|
||||
"notFoundButton": "",
|
||||
"pageSpeedConfigureSettingsDescription": "",
|
||||
"monitorDisplayName": "",
|
||||
"whenNewIncident": "",
|
||||
"notifySMS": "Ilmoita tekstiviestillä (tulossa pian)",
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
"deleteDialogDescription": "",
|
||||
"pageSpeedMonitor": "",
|
||||
"shown": "",
|
||||
"ago": "sitten",
|
||||
"companyName": "Yrityksen nimi",
|
||||
"pageSpeedDetailsPerformanceReport": "",
|
||||
"pageSpeedDetailsPerformanceReportCalculator": "",
|
||||
"checkingEvery": "",
|
||||
"statusPageCreateSettings": "",
|
||||
"basicInformation": "",
|
||||
"statusPageCreateBasicInfoDescription": "",
|
||||
"statusPageCreateSelectTimeZoneDescription": "",
|
||||
"statusPageCreateAppearanceDescription": "",
|
||||
"statusPageCreateSettingsCheckboxLabel": "",
|
||||
"statusPageCreateBasicInfoStatusPageAddress": "",
|
||||
"statusPageCreateTabsContent": "",
|
||||
"statusPageCreateTabsContentDescription": "",
|
||||
"statusPageCreateTabsContentFeaturesDescription": "",
|
||||
"showCharts": "Näytä kaaviot",
|
||||
"showUptimePercentage": "",
|
||||
"removeLogo": "Poista logo",
|
||||
"statusPageStatus": "",
|
||||
"statusPageStatusContactAdmin": "Ota yhteyttä järjestelmänvalvojaan",
|
||||
"statusPageStatusNotPublic": "",
|
||||
"statusPageStatusNoPage": "",
|
||||
"statusPageStatusServiceStatus": "",
|
||||
"deleteStatusPage": "",
|
||||
"deleteStatusPageConfirm": "",
|
||||
"deleteStatusPageDescription": "",
|
||||
"uptimeCreate": "",
|
||||
"uptimeCreateJsonPath": "",
|
||||
"uptimeCreateJsonPathQuery": "",
|
||||
"maintenanceTableActionMenuDialogTitle": "",
|
||||
"infrastructureEditYour": "Muokkaa omaa",
|
||||
"infrastructureEditMonitor": "",
|
||||
"infrastructureMonitorCreated": "",
|
||||
"infrastructureMonitorUpdated": "",
|
||||
"errorInvalidTypeId": "",
|
||||
"errorInvalidFieldId": "",
|
||||
"inviteNoTokenFound": "",
|
||||
"pageSpeedWarning": "",
|
||||
"pageSpeedLearnMoreLink": "",
|
||||
"pageSpeedAddApiKey": "",
|
||||
"update": "Päivitä",
|
||||
"invalidFileFormat": "",
|
||||
"invalidFileSize": "Tiedoston koko on liian suuri!",
|
||||
"ClickUpload": "",
|
||||
"DragandDrop": "raahaa ja pudota",
|
||||
"MaxSize": "Enimmäiskoko",
|
||||
"SupportedFormats": "Tuetut muodot",
|
||||
"FirstName": "Etunimi",
|
||||
"LastName": "Sukunimi",
|
||||
"EmailDescriptionText": "Tämä on nykyinen sähköpostiosoitteesi — sitä ei voi vaihtaa.",
|
||||
"YourPhoto": "Profiilikuva",
|
||||
"PhotoDescriptionText": "",
|
||||
"save": "Tallenna",
|
||||
"DeleteDescriptionText": "",
|
||||
"DeleteAccountWarning": "",
|
||||
"DeleteWarningTitle": "Haluatko todella poistaa tämän tilin?",
|
||||
"bulkImport": {
|
||||
"title": "",
|
||||
"selectFileTips": "",
|
||||
"selectFileDescription": "",
|
||||
"selectFile": "Valitse tiedosto",
|
||||
"parsingFailed": "",
|
||||
"uploadSuccess": "",
|
||||
"validationFailed": "",
|
||||
"noFileSelected": "Ei valittua tiedostoa",
|
||||
"fallbackPage": "",
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
},
|
||||
"DeleteAccountTitle": "Poista tili",
|
||||
"DeleteAccountButton": "Poista tili",
|
||||
"publicLink": "Julkinen linkki",
|
||||
"maskedPageSpeedKeyPlaceholder": "*************************************",
|
||||
"reset": "Palauta",
|
||||
"ignoreTLSError": "",
|
||||
"tlsErrorIgnored": "",
|
||||
"ignoreTLSErrorDescription": "",
|
||||
"createNew": "Luo uusi",
|
||||
"greeting": {
|
||||
"prepend": "Hei",
|
||||
"append": "",
|
||||
"overview": ""
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "Pääylläpitäjä",
|
||||
"admin": "Ylläpitäjä",
|
||||
"teamMember": "Tiimin jäsen",
|
||||
"demoUser": "Demokäyttäjä"
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "Tiimin jäsenet",
|
||||
"filter": {
|
||||
"all": "Kaikki",
|
||||
"member": "Jäsen"
|
||||
},
|
||||
"inviteTeamMember": "",
|
||||
"inviteNewTeamMember": "",
|
||||
"inviteDescription": "",
|
||||
"email": "Sähköposti",
|
||||
"selectRole": "Valitse rooli",
|
||||
"inviteLink": "Kutsulinkki",
|
||||
"cancel": "Peruuttaa",
|
||||
"noMembers": "",
|
||||
"getToken": "Hae tunniste",
|
||||
"emailToken": "Sähköpostitunnus",
|
||||
"table": {
|
||||
"name": "Nimi",
|
||||
"email": "Sähköposti",
|
||||
"role": "Rooli",
|
||||
"created": "Luotu"
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "Tauota",
|
||||
"resumed": "Jatka",
|
||||
"active": ""
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "",
|
||||
"pagespeed": "",
|
||||
"infrastructure": "Infrastruktuuri",
|
||||
"incidents": "",
|
||||
"statusPages": "Tilasivut",
|
||||
"maintenance": "Huolto",
|
||||
"integrations": "",
|
||||
"settings": "Asetukset",
|
||||
"support": "Tuki",
|
||||
"discussions": "Keskustelut",
|
||||
"docs": "Dokumentit",
|
||||
"changelog": "Muutosloki",
|
||||
"profile": "Profiili",
|
||||
"password": "Salasana",
|
||||
"team": "Tiimi",
|
||||
"logOut": "Kirjaudu ulos",
|
||||
"notifications": "",
|
||||
"logs": ""
|
||||
},
|
||||
"settingsEmailUser": "",
|
||||
"state": "Tila",
|
||||
"statusBreadCrumbsStatusPages": "Tilasivut",
|
||||
"statusBreadCrumbsDetails": "Tiedot",
|
||||
"commonSaving": "Tallennetaan...",
|
||||
"navControls": "Ohjaimet",
|
||||
"incidentsPageTitle": "Häiriöt",
|
||||
"passwordPanel": {
|
||||
"passwordChangedSuccess": "Salasanasi vaihdettiin onnistuneesti.",
|
||||
"passwordInputIncorrect": "",
|
||||
"currentPassword": "Nykyinen salasana",
|
||||
"enterCurrentPassword": "Kirjoita nykyinen salasanasi",
|
||||
"newPassword": "Uusi salasana",
|
||||
"enterNewPassword": "Kirjoita uusi salasana",
|
||||
"confirmNewPassword": "Vahvista uusi salasana",
|
||||
"passwordRequirements": "Uuden salasanan on oltava vähintään 8 merkkiä pitkä ja sen on sisältävä vähintään yksi iso kirjain, yksi pieni kirjain, yksi numero sekä yksi erikoismerkki.",
|
||||
"saving": "Tallennetaan..."
|
||||
},
|
||||
"emailSent": "Sähköpostin lähetys onnistui",
|
||||
"failedToSendEmail": "Sähköpostin lähetys epäonnistui",
|
||||
"settingsTestEmailSuccess": "Testisähköpostin lähetys onnistui",
|
||||
"settingsTestEmailFailed": "Testisähköpostin lähetys epäonnistui",
|
||||
"settingsTestEmailFailedWithReason": "Testisähköpostin lähetys epäonnistui: {{reason}}",
|
||||
"settingsTestEmailUnknownError": "Tuntematon virhe",
|
||||
"statusMsg": {
|
||||
"paused": "Valvonta on keskeytetty.",
|
||||
"up": "Sivusi on ylhäällä.",
|
||||
"down": "Sivusi on alhaalla.",
|
||||
"pending": "Odottaa..."
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
},
|
||||
"common": {
|
||||
"appName": "",
|
||||
"monitoringAgentName": "",
|
||||
"buttons": {
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"toasts": {
|
||||
"networkError": "",
|
||||
"checkConnection": "",
|
||||
"unknownError": ""
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"common": {
|
||||
"navigation": {
|
||||
"continue": "",
|
||||
"back": ""
|
||||
},
|
||||
"inputs": {
|
||||
"email": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"invalid": ""
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "",
|
||||
"rules": {
|
||||
"length": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"special": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"number": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"uppercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"lowercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
},
|
||||
"match": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"uppercase": "",
|
||||
"lowercase": "",
|
||||
"number": "",
|
||||
"special": "",
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"passwordConfirm": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"different": ""
|
||||
}
|
||||
},
|
||||
"firstName": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
"lastName": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"pattern": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"validation": ""
|
||||
},
|
||||
"fields": {
|
||||
"password": {
|
||||
"errors": {
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"heading": "",
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": ""
|
||||
},
|
||||
"links": {
|
||||
"forgotPassword": "",
|
||||
"register": "",
|
||||
"forgotPasswordLink": "",
|
||||
"registerLink": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": "",
|
||||
"incorrectPassword": ""
|
||||
},
|
||||
"errors": {
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
},
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": "",
|
||||
"stepThree": ""
|
||||
},
|
||||
"description": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
},
|
||||
"gettingStartedButton": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
},
|
||||
"termsAndPolicies": "",
|
||||
"links": {
|
||||
"login": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": ""
|
||||
}
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "",
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": "",
|
||||
"stepThree": "",
|
||||
"stepFour": ""
|
||||
},
|
||||
"buttons": {
|
||||
"openEmail": "",
|
||||
"resetPassword": ""
|
||||
},
|
||||
"imageAlts": {
|
||||
"passwordKey": "",
|
||||
"email": "",
|
||||
"lock": "",
|
||||
"passwordConfirm": ""
|
||||
},
|
||||
"links": {
|
||||
"login": "",
|
||||
"resend": ""
|
||||
},
|
||||
"toasts": {
|
||||
"sent": "",
|
||||
"emailNotFound": "",
|
||||
"redirect": "",
|
||||
"success": "",
|
||||
"error": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorPages": {
|
||||
"serverUnreachable": {
|
||||
"toasts": {
|
||||
"reconnected": "",
|
||||
"stillUnreachable": ""
|
||||
},
|
||||
"alertBox": "",
|
||||
"description": "",
|
||||
"retryButton": {
|
||||
"default": "",
|
||||
"processing": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"createNotifications": {
|
||||
"title": "",
|
||||
"nameSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"nameLabel": "",
|
||||
"namePlaceholder": ""
|
||||
},
|
||||
"typeSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"typeLabel": ""
|
||||
},
|
||||
"emailSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"emailLabel": "",
|
||||
"emailPlaceholder": ""
|
||||
},
|
||||
"slackSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"pagerdutySettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"integrationKeyLabel": "",
|
||||
"integrationKeyPlaceholder": ""
|
||||
},
|
||||
"discordSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"webhookSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"testNotification": "",
|
||||
"dialogDeleteTitle": "",
|
||||
"dialogDeleteConfirm": ""
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
},
|
||||
"monitorStatus": {
|
||||
"checkingEvery": "",
|
||||
"withCaptureAgent": "",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"paused": ""
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"sendTestNotifications": "",
|
||||
"selectAll": "",
|
||||
"showAdminLoginLink": "",
|
||||
"logsPage": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"tabs": {
|
||||
"queue": "",
|
||||
"logs": "",
|
||||
"diagnostics": ""
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
},
|
||||
"logLevelSelect": {
|
||||
"title": "",
|
||||
"values": {
|
||||
"all": "",
|
||||
"info": "",
|
||||
"warn": "",
|
||||
"error": "",
|
||||
"debug": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"queuePage": {
|
||||
"title": "",
|
||||
"refreshButton": "",
|
||||
"flushButton": "",
|
||||
"jobTable": {
|
||||
"title": "",
|
||||
"idHeader": "",
|
||||
"urlHeader": "",
|
||||
"typeHeader": "",
|
||||
"activeHeader": "",
|
||||
"lockedAtHeader": "",
|
||||
"runCountHeader": "",
|
||||
"failCountHeader": "",
|
||||
"lastRunHeader": "",
|
||||
"lastFinishedAtHeader": "",
|
||||
"lastRunTookHeader": ""
|
||||
},
|
||||
"metricsTable": {
|
||||
"title": "",
|
||||
"metricHeader": "",
|
||||
"valueHeader": ""
|
||||
},
|
||||
"failedJobTable": {
|
||||
"title": "",
|
||||
"monitorIdHeader": "",
|
||||
"monitorUrlHeader": "",
|
||||
"failCountHeader": "",
|
||||
"failedAtHeader": "",
|
||||
"failReasonHeader": ""
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"title": "",
|
||||
"success": "",
|
||||
"failed": ""
|
||||
},
|
||||
"monitorActions": {
|
||||
"title": "",
|
||||
"import": "",
|
||||
"export": "",
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"details": ""
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
"labelDevelopedBy": "",
|
||||
"labelVersion": "",
|
||||
"title": ""
|
||||
},
|
||||
"demoMonitorsSettings": {
|
||||
"buttonAddMonitors": "",
|
||||
"description": "",
|
||||
"title": ""
|
||||
},
|
||||
"emailSettings": {
|
||||
"buttonSendTestEmail": "",
|
||||
"description": "",
|
||||
"descriptionTransport": "",
|
||||
"labelAddress": "",
|
||||
"labelConnectionHost": "",
|
||||
"labelHost": "",
|
||||
"labelIgnoreTLS": "",
|
||||
"labelPassword": "",
|
||||
"labelPasswordSet": "",
|
||||
"labelPool": "",
|
||||
"labelPort": "",
|
||||
"labelRejectUnauthorized": "",
|
||||
"labelRequireTLS": "",
|
||||
"labelSecure": "",
|
||||
"labelTLSServername": "",
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": "",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
"labelApiKeySet": "",
|
||||
"labelApiKey": "",
|
||||
"title": ""
|
||||
},
|
||||
"saveButtonLabel": "",
|
||||
"statsSettings": {
|
||||
"clearAllStatsButton": "",
|
||||
"clearAllStatsDescription": "",
|
||||
"clearAllStatsDialogConfirm": "",
|
||||
"clearAllStatsDialogDescription": "",
|
||||
"clearAllStatsDialogTitle": "",
|
||||
"description": "",
|
||||
"labelTTL": "",
|
||||
"labelTTLOptional": "",
|
||||
"title": ""
|
||||
},
|
||||
"systemResetSettings": {
|
||||
"buttonRemoveAllMonitors": "",
|
||||
"description": "",
|
||||
"dialogConfirm": "",
|
||||
"dialogDescription": "",
|
||||
"dialogTitle": "",
|
||||
"title": ""
|
||||
},
|
||||
"timezoneSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"title": ""
|
||||
},
|
||||
"title": "",
|
||||
"uiSettings": {
|
||||
"description": "",
|
||||
"labelLanguage": "",
|
||||
"labelTheme": "",
|
||||
"title": ""
|
||||
},
|
||||
"urlSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "",
|
||||
"failureResolveAll": "",
|
||||
"failureResolveMonitor": ""
|
||||
},
|
||||
"checkFormError": "",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
},
|
||||
"title": "",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
}
|
||||
973
client/src/locales/fr.json
Normal file
@@ -0,0 +1,973 @@
|
||||
{
|
||||
"submit": "Envoyer",
|
||||
"title": "Titre",
|
||||
"distributedStatusHeaderText": "Couverture en temps réel, sur des appareils en temps réel",
|
||||
"distributedStatusSubHeaderText": "Alimenté par des millions d'appareils à travers le monde, consultez les performances du système par région, pays ou ville",
|
||||
"settingsDisabled": "Désactivé",
|
||||
"settingsSuccessSaved": "Les paramètres ont bien été sauvegardés",
|
||||
"settingsFailedToSave": "Les paramètres n'ont pas pu être sauvegardés",
|
||||
"settingsStatsCleared": "Les statistiques ont été réinitialisées avec succès",
|
||||
"settingsFailedToClearStats": "Les statistiques n'ont pas pu être réinitialisées",
|
||||
"settingsMonitorsDeleted": "Tous les moniteurs ont bien été supprimés",
|
||||
"settingsFailedToDeleteMonitors": "La suppression de tous les moniteurs a échoué",
|
||||
"starPromptTitle": "Ajouter Checkmate aux favoris",
|
||||
"starPromptDescription": "Voir les dernières versions et aider la communauté à grandir sur Github",
|
||||
"https": "HTTPS",
|
||||
"http": "HTTP",
|
||||
"monitor": "Moniteur",
|
||||
"aboutus": "A propos de nous",
|
||||
"now": "Maintenant",
|
||||
"delete": "Supprimer",
|
||||
"configure": "Configurer",
|
||||
"responseTime": "Temps de réponse",
|
||||
"ms": "ms",
|
||||
"bar": "Graphique",
|
||||
"area": "Zone",
|
||||
"country": "PAYS",
|
||||
"city": "VILLE",
|
||||
"response": "Réponse",
|
||||
"monitorStatusUp": "Moniteur {name} ({url}) est désormais EN LIGNE et répond aux requêtes",
|
||||
"monitorStatusDown": "Moniteur {name} ({url}) est désormais HORS LIGNE et ne répond plus",
|
||||
"webhookSendSuccess": "La notification webhook a bien été envoyée",
|
||||
"webhookSendError": "Une erreur s'est produite lors de l'envoi de la notification webhook sur {platform}",
|
||||
"webhookUnsupportedPlatform": "La plateforme {platform} n'est pas supportée actuellement",
|
||||
"distributedRightCategoryTitle": "Moniteur",
|
||||
"distributedStatusServerMonitors": "Monitoring des serveurs",
|
||||
"distributedStatusServerMonitorsDescription": "Statut des moniteurs relatifs aux serveurs",
|
||||
"distributedUptimeCreateSelectURL": "Ici vous pouvez sélectionner l'URL de l'hôte et le type de moniteur",
|
||||
"distributedUptimeCreateChecks": "Vérifications à effectuer",
|
||||
"distributedUptimeCreateChecksDescription": "Vous pourrez ajouter ou supprimer des vérifications après l'ajout de votre site",
|
||||
"distributedUptimeCreateIncidentNotification": "Notifications d'incidents",
|
||||
"distributedUptimeCreateIncidentDescription": "Quand il y a un incident, notifier les utilisateurs",
|
||||
"distributedUptimeCreateAdvancedSettings": "Paramètres avancés",
|
||||
"distributedUptimeDetailsNoMonitorHistory": "Il n'y a pas encore d'historique de vérification pour ce moniteur",
|
||||
"distributedUptimeDetailsStatusHeaderUptime": "Temps en ligne :",
|
||||
"distributedUptimeDetailsStatusHeaderLastUpdate": "Dernière mise à jour",
|
||||
"notifications": {
|
||||
"enableNotifications": "Activer les notifications sur {{platform}}",
|
||||
"testNotification": "Notification de test",
|
||||
"addOrEditNotifications": "Ajouter ou éditer des notifications",
|
||||
"slack": {
|
||||
"label": "Slack",
|
||||
"description": "Pour activer les notifications Slack, créez une application Slack et activez les webhooks entrants. Ensuite, indiquer l'URL du webhook ici.",
|
||||
"webhookLabel": "URL du webhook",
|
||||
"webhookPlaceholder": "https://hooks.slack.com/services/...",
|
||||
"webhookRequired": "L'URL du webhook Slack est requise"
|
||||
},
|
||||
"discord": {
|
||||
"label": "Discord",
|
||||
"description": "Pour envoyer des données sur un canal Discord depuis Checkmate, utilisez l'intégration Webhook Discord.",
|
||||
"webhookLabel": "URL du webhook Discord",
|
||||
"webhookPlaceholder": "https://discord.com/api/webhooks/...",
|
||||
"webhookRequired": "L'URL du webhook Discord est requise"
|
||||
},
|
||||
"telegram": {
|
||||
"label": "Telegram",
|
||||
"description": "Pour activer les notifications Telegram, créez un bot Telegram en utilisant BotFather, le robot officiel pour créer et gérer ses propres robots. Indiquez ensuite votre clé API et l'ID du chat ici.",
|
||||
"tokenLabel": "Token du robot",
|
||||
"tokenPlaceholder": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
|
||||
"chatIdLabel": "Votre Chat ID",
|
||||
"chatIdPlaceholder": "-1001234567890",
|
||||
"fieldsRequired": "Le token Telegram et l'ID du chat sont requis."
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhooks",
|
||||
"description": "Vous pouvez indiquer un webhook personnalisé pour recevoir des notifications lorsqu'un incident se produit.",
|
||||
"urlLabel": "URL du webhook",
|
||||
"urlPlaceholder": "https://votre-serveur.fr/webhook",
|
||||
"urlRequired": "L'URL du webhook est nécessaire"
|
||||
},
|
||||
"testNotificationDevelop": "Notification de test 2",
|
||||
"integrationButton": "Intégration de notification",
|
||||
"testSuccess": "La notification de test a été envoyée avec succès !",
|
||||
"testFailed": "Échec de l'envoi de la notification de test",
|
||||
"unsupportedType": "Type de notification non supporté",
|
||||
"networkError": "Une erreur réseau s'est produite",
|
||||
"fallback": {
|
||||
"title": "canal de notification",
|
||||
"checks": [
|
||||
"Alertez les équipes en cas de temps d'arrêt ou de problèmes de performances",
|
||||
"Informez les ingénieurs lorsque des incidents se produisent",
|
||||
"Tenez les administrateurs informés des changements apportés au système"
|
||||
],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "Créer un canal de notification",
|
||||
"createTitle": "Canal de notification",
|
||||
"create": {
|
||||
"success": "La notification a été créée avec succès",
|
||||
"failed": "Une erreur s'est produite lors de la création de la notification"
|
||||
},
|
||||
"fetch": {
|
||||
"success": "Les notifications ont été récupérées avec succès",
|
||||
"failed": "Une erreur s'est produite lors de la récupération des notifications"
|
||||
},
|
||||
"delete": {
|
||||
"success": "Notification supprimée avec succès",
|
||||
"failed": "Une erreur s'est produite lors de la suppression de la notification"
|
||||
},
|
||||
"edit": {
|
||||
"success": "Notification modifiée avec succès",
|
||||
"failed": "Une erreur s'est produite lors de la modification de la notification"
|
||||
},
|
||||
"test": {
|
||||
"success": "Notification de test envoyée avec succès",
|
||||
"failed": "Une erreur s'est produite lors de l'envoi de la notification de test"
|
||||
}
|
||||
},
|
||||
"testLocale": "testLocale",
|
||||
"add": "Ajouter",
|
||||
"monitors": "moniteurs",
|
||||
"distributedUptimeStatusCreateStatusPage": "page de statut",
|
||||
"distributedUptimeStatusCreateStatusPageAccess": "Accès",
|
||||
"distributedUptimeStatusCreateStatusPageReady": "Si votre page de statut est prête, vous pouvez l'indiquer en \"Publiée\".",
|
||||
"distributedUptimeStatusBasicInfoHeader": "Informations de base",
|
||||
"distributedUptimeStatusBasicInfoDescription": "Définir le nom de la société et le sous domaine sur lequel votre page de statut pointe.",
|
||||
"distributedUptimeStatusLogoHeader": "Logo",
|
||||
"distributedUptimeStatusLogoDescription": "Ajoutez un logo pour votre page de statut",
|
||||
"distributedUptimeStatusLogoUploadButton": "Téléverser un logo",
|
||||
"distributedUptimeStatusStandardMonitorsHeader": "Moniteurs standards",
|
||||
"distributedUptimeStatusStandardMonitorsDescription": "Ajouter un moniteur standard à votre page de statut",
|
||||
"distributedUptimeStatusCreateYour": "Créer votre",
|
||||
"distributedUptimeStatusEditYour": "Editer votre",
|
||||
"distributedUptimeStatusPublishedLabel": "Publiée et visible publiquement",
|
||||
"distributedUptimeStatusCompanyNameLabel": "Nom de l'entreprise",
|
||||
"distributedUptimeStatusPageAddressLabel": "Adresse de votre page de statut",
|
||||
"distributedUptimeStatus30Days": "30 jours",
|
||||
"distributedUptimeStatus60Days": "60 jours",
|
||||
"distributedUptimeStatus90Days": "90 jours",
|
||||
"distributedUptimeStatusPageNotSetUp": "Aucune page de statut n'est mise en place.",
|
||||
"distributedUptimeStatusContactAdmin": "Veuillez contacter votre administrateur",
|
||||
"distributedUptimeStatusPageNotPublic": "Cette page de statut n'est pas publique.",
|
||||
"distributedUptimeStatusPageDeleteDialog": "Voulez-vous supprimer cette page de statut ?",
|
||||
"distributedUptimeStatusPageDeleteConfirm": "Oui, supprimer la page de statut",
|
||||
"distributedUptimeStatusPageDeleteDescription": "Supprimer votre page de statut est irréversible.",
|
||||
"distributedUptimeStatusDevices": "Équipements",
|
||||
"distributedUptimeStatusUpt": "UPT",
|
||||
"distributedUptimeStatusUptBurned": "",
|
||||
"distributedUptimeStatusUptLogo": "Logo Upt",
|
||||
"incidentsTableNoIncidents": "Aucun incident enregistrée",
|
||||
"incidentsTablePaginationLabel": "incidents",
|
||||
"incidentsTableMonitorName": "Nom du moniteur",
|
||||
"incidentsTableStatus": "Statut",
|
||||
"incidentsTableDateTime": "Date et heure",
|
||||
"incidentsTableStatusCode": "Code de statut",
|
||||
"incidentsTableMessage": "Message",
|
||||
"incidentsOptionsHeader": "Incidents pour :",
|
||||
"incidentsOptionsHeaderFilterBy": "Filtrer par :",
|
||||
"incidentsOptionsHeaderFilterAll": "Tout",
|
||||
"incidentsOptionsHeaderFilterDown": "Hors ligne",
|
||||
"incidentsOptionsHeaderFilterCannotResolve": "Impossible de résoudre",
|
||||
"incidentsOptionsHeaderShow": "Voir:",
|
||||
"incidentsOptionsHeaderLastHour": "Dernière heure",
|
||||
"incidentsOptionsHeaderLastDay": "Dernier jour",
|
||||
"incidentsOptionsHeaderLastWeek": "Dernière semaine",
|
||||
"incidentsOptionsPlaceholderAllServers": "Tous les serveurs",
|
||||
"infrastructureCreateYour": "Créer votre",
|
||||
"infrastructureCreateGeneralSettingsDescription": "Ici, vous pouvez sélectionner l'URL de l'hôte, ainsi que le nom familier et le secret d'autorisation pour vous connecter à l'agent sur le serveur.",
|
||||
"infrastructureServerRequirement": "Le serveur que vous surveillez doit exécuter le",
|
||||
"infrastructureCustomizeAlerts": "Personnaliser les alertes",
|
||||
"infrastructureAlertNotificationDescription": "Envoyer une notification aux utilisateurs lorsque le seuil dépasse un pourcentage spécifié.",
|
||||
"infrastructureCreateMonitor": "Créer un moniteur d'infrastructure",
|
||||
"infrastructureProtocol": "Protocole",
|
||||
"infrastructureServerUrlLabel": "URL du serveur",
|
||||
"infrastructureDisplayNameLabel": "Nom d'affichage",
|
||||
"infrastructureAuthorizationSecretLabel": "Secret d'autorisation",
|
||||
"gb": "GB",
|
||||
"mb": "MB",
|
||||
"mem": "Mém",
|
||||
"memoryUsage": "Utilisation de la mémoire",
|
||||
"cpu": "CPU",
|
||||
"cpuUsage": "Utilisation CPU",
|
||||
"cpuTemperature": "Température CPU",
|
||||
"diskUsage": "Utilisation du disque",
|
||||
"used": "Utilisé",
|
||||
"total": "Total",
|
||||
"cores": "Coeurs",
|
||||
"frequency": "Fréquence",
|
||||
"status": "Statut",
|
||||
"cpuPhysical": "CPU (Physique)",
|
||||
"cpuLogical": "CPU (Logique)",
|
||||
"cpuFrequency": "Fréquence CPU",
|
||||
"avgCpuTemperature": "Température moyenne du CPU",
|
||||
"memory": "Mémoire",
|
||||
"disk": "Disque",
|
||||
"uptime": "Temps en ligne",
|
||||
"os": "OS",
|
||||
"host": "Hôte",
|
||||
"actions": "Actions",
|
||||
"integrations": "Intégrations",
|
||||
"integrationsPrism": "Connecter Prism à votre service préféré.",
|
||||
"integrationsSlack": "Slack",
|
||||
"integrationsSlackInfo": "Connecter à Slack et voir les incidents dans un canal",
|
||||
"integrationsDiscord": "Discord",
|
||||
"integrationsDiscordInfo": "Connecter à Discord et voir les incidents dans un canal",
|
||||
"integrationsZapier": "Zapier",
|
||||
"integrationsZapierInfo": "Envoyer tous les incidents à Zapier",
|
||||
"commonSave": "Sauvegarder",
|
||||
"createYour": "Créez votre",
|
||||
"createMonitor": "Ajouter un moniteur",
|
||||
"pause": "Mettre en pause",
|
||||
"resume": "Reprendre",
|
||||
"editing": "Edition...",
|
||||
"url": "URL",
|
||||
"access": "Accès",
|
||||
"timezone": "Fuseau horaire",
|
||||
"features": "Fonctionnalités",
|
||||
"administrator": "Administrateur ?",
|
||||
"loginHere": "Connexion",
|
||||
"displayName": "Nom d'affichage",
|
||||
"urlMonitor": "URL à suivre",
|
||||
"portToMonitor": "Port du moniteur",
|
||||
"websiteMonitoring": "Monitoring d'un site internet",
|
||||
"websiteMonitoringDescription": "Utiliser HTTP(s) pour vérifier votre site ou votre API.",
|
||||
"pingMonitoring": "Monitoring par ping",
|
||||
"pingMonitoringDescription": "Vérifier si le serveur est disponible ou pas",
|
||||
"dockerContainerMonitoring": "Monitoring d'un container Docker",
|
||||
"dockerContainerMonitoringDescription": "Vérifier si votre container Docker fonctionne ou pas.",
|
||||
"portMonitoring": "Monitoring d'un port",
|
||||
"portMonitoringDescription": "Vérifier si votre port est ouvert ou non.",
|
||||
"createMaintenanceWindow": "Créer une fenêtre de maintenance",
|
||||
"createMaintenance": "Créer une maintenance",
|
||||
"editMaintenance": "Modifier la maintenance",
|
||||
"maintenanceWindowName": "Nom de la fenêtre de maintenance",
|
||||
"friendlyNameInput": "Nom",
|
||||
"friendlyNamePlaceholder": "Maintenance à __ : __ pendant ___ minutes",
|
||||
"maintenanceRepeat": "Répéter la maintenance",
|
||||
"maintenance": "maintenance",
|
||||
"duration": "Durée",
|
||||
"addMonitors": "Ajouter des moniteurs",
|
||||
"window": "fenêtre",
|
||||
"cancel": "Annuler",
|
||||
"message": "Message",
|
||||
"low": "bas",
|
||||
"high": "haut",
|
||||
"statusCode": "Code de statut",
|
||||
"date&Time": "Date et heure",
|
||||
"type": "Type",
|
||||
"statusPageName": "Nom de la page de statut",
|
||||
"publicURL": "URL publique",
|
||||
"repeat": "Répéter",
|
||||
"edit": "Modifier",
|
||||
"createA": "Créer un",
|
||||
"remove": "Supprimer",
|
||||
"maintenanceWindowDescription": "Vos pings ne seront pas envoyés pendant cette fenêtre de temps.",
|
||||
"startTime": "Date de démarrage",
|
||||
"timeZoneInfo": "Toutes les dates et heures sont dans le fuseau GMT+0",
|
||||
"monitorsToApply": "Moniteurs sur lesquels appliquer la fenêtre de maintenance",
|
||||
"nextWindow": "Prochaine fenêtre de maintenance",
|
||||
"notFoundButton": "Aller au tableau de bord principal",
|
||||
"pageSpeedConfigureSettingsDescription": "Vous pouvez sélectionner l'URL de hôte et le type de moniteur.",
|
||||
"monitorDisplayName": "Nom du moniteur",
|
||||
"whenNewIncident": "Lors d'un nouvel incident,",
|
||||
"notifySMS": "Notification par SMS (bientôt)",
|
||||
"notifyEmails": "Envoyer également une notification par email à plusieurs adresses (bientôt).",
|
||||
"seperateEmails": "Vous pouvez ajouter plusieurs adresses emails en les séparant par une virgule",
|
||||
"checkFrequency": "Vérifier la fréquence",
|
||||
"matchMethod": "Méthode de rapprochement",
|
||||
"expectedValue": "Valeur attendue",
|
||||
"deleteDialogTitle": "Voulez-vous vraiment supprimer ce moniteur ?",
|
||||
"deleteDialogDescription": "Une fois supprimé, le moniteur ne peut pas être récupéré.",
|
||||
"pageSpeedMonitor": "Moniteur PageSpeed",
|
||||
"shown": "Visibles",
|
||||
"ago": "depuis",
|
||||
"companyName": "Nom de la société",
|
||||
"pageSpeedDetailsPerformanceReport": "Les valeurs sont estimatives et peuvent varier.",
|
||||
"pageSpeedDetailsPerformanceReportCalculator": "Voir le calculateur",
|
||||
"checkingEvery": "Vérification tous les",
|
||||
"statusPageCreateSettings": "Si votre page de statut est prête, vous pouvez la rendre publique.",
|
||||
"basicInformation": "Informations de base",
|
||||
"statusPageCreateBasicInfoDescription": "Définir le nom de la société et le sous-domaine vers lequel pointe votre page de statut.",
|
||||
"statusPageCreateSelectTimeZoneDescription": "Sélectionnez le fuseau horaire qui sera utilisé pour votre page de statut",
|
||||
"statusPageCreateAppearanceDescription": "Définir le style par défaut de votre page de statut.",
|
||||
"statusPageCreateSettingsCheckboxLabel": "Publiée et visible au public",
|
||||
"statusPageCreateBasicInfoStatusPageAddress": "L'adresse de votre page de statut",
|
||||
"statusPageCreateTabsContent": "Serveurs de votre page de statut",
|
||||
"statusPageCreateTabsContentDescription": "Vous pouvez ajouter autant de moniteurs à votre page de statut que vous le souhaitez. Vous pouvez également les réordonner pour améliorer l'expérience utilisateur.",
|
||||
"statusPageCreateTabsContentFeaturesDescription": "Voir plus de détails sur la page de statut",
|
||||
"showCharts": "Voir les graphiques",
|
||||
"showUptimePercentage": "Voir le pourcentage de temps en ligne",
|
||||
"removeLogo": "Supprimer le logo",
|
||||
"statusPageStatus": "Aucune page de statut publique n'est déployée",
|
||||
"statusPageStatusContactAdmin": "Merci de contacter votre administrateur",
|
||||
"statusPageStatusNotPublic": "Cette page de statut n'est pas piblique",
|
||||
"statusPageStatusNoPage": "Il n'y a aucune page de statut ici",
|
||||
"statusPageStatusServiceStatus": "Statut des services",
|
||||
"deleteStatusPage": "Voulez-vous supprimer cette page de statut ?",
|
||||
"deleteStatusPageConfirm": "Oui, supprimer la page de statut",
|
||||
"deleteStatusPageDescription": "Une fois supprimée, votre page de statut ne pourra plus être récupérée.",
|
||||
"uptimeCreate": "La valeur attendue est utilisée pour comparer le résultat de la réponse et la correspondance détermine le statut du moniteur.",
|
||||
"uptimeCreateJsonPath": "Cette expression sera évaluée par rapport aux données JSON de la réponse et le résultat sera utilisé pour la comparaison avec la valeur attendue. Voir",
|
||||
"uptimeCreateJsonPathQuery": "pour la documentation sur le langage de requête.",
|
||||
"maintenanceTableActionMenuDialogTitle": "Voulez-vous vraiment supprimer cette fenêtre de maintenance ?",
|
||||
"infrastructureEditYour": "Modifier votre",
|
||||
"infrastructureEditMonitor": "Sauvegarder le moniteur d'infrastructure",
|
||||
"infrastructureMonitorCreated": "Le moniteur d'infrastructure a été créé avec succès !",
|
||||
"infrastructureMonitorUpdated": "Le moniteur d'infrastructure a été modifié avec succès !",
|
||||
"errorInvalidTypeId": "Le type de notification fourni est invalide",
|
||||
"errorInvalidFieldId": "Le champ ID fourni est invalide",
|
||||
"inviteNoTokenFound": "Aucun jeton d'invitation fourni",
|
||||
"pageSpeedWarning": "Attention : vous n'avez pas encore ajouté de clé API Google PageSpeed. Sans celle-ci, le moniteur PageSpeed ne fonctionnera pas.",
|
||||
"pageSpeedLearnMoreLink": "Cliquer ici",
|
||||
"pageSpeedAddApiKey": "pour ajouter votre clé API.",
|
||||
"update": "Mettre à jour",
|
||||
"invalidFileFormat": "Format de fichier non supporté !",
|
||||
"invalidFileSize": "Le fichier est trop volumineux !",
|
||||
"ClickUpload": "Cliquez pour téléverser",
|
||||
"DragandDrop": "glisser et déposer",
|
||||
"MaxSize": "Taille maximum",
|
||||
"SupportedFormats": "Formats supportés",
|
||||
"FirstName": "Prénom",
|
||||
"LastName": "Nom",
|
||||
"EmailDescriptionText": "Il s'agit de votre adresse email actuelle : elle ne peut pas être changée.",
|
||||
"YourPhoto": "Photo de profil",
|
||||
"PhotoDescriptionText": "La photo sera affichée sur votre page de profil.",
|
||||
"save": "Enregistrer",
|
||||
"DeleteDescriptionText": "Cela supprimera le compte et toutes les données associées du serveur. Cette opération est irréversible.",
|
||||
"DeleteAccountWarning": "Supprimer votre compte signifie que vous ne pourrez pas vous reconnecter et que toutes vos données seront supprimés. Ceci est irréversible.",
|
||||
"DeleteWarningTitle": "Vous supprimez vraiment ce compte ?",
|
||||
"bulkImport": {
|
||||
"title": "Import en masse",
|
||||
"selectFileTips": "Sélectionnez un fichier CSV pour l'upload",
|
||||
"selectFileDescription": "Vous pouvez télécharger notre <template>modèle</template> ou <sample>exemple</sample>.",
|
||||
"selectFile": "Sélectionner un fichier",
|
||||
"parsingFailed": "L'analyse du fichier a rencontré un problème",
|
||||
"uploadSuccess": "Moniteurs créés avec succès !",
|
||||
"validationFailed": "Echec de la validation",
|
||||
"noFileSelected": "Aucun fichier sélectionné",
|
||||
"fallbackPage": "Importer un fichier pour ajouter une liste de serveurs en masse",
|
||||
"invalidFileType": "Type de fichier invalide",
|
||||
"uploadFailed": "Le téléchargement a échoué"
|
||||
},
|
||||
"DeleteAccountTitle": "Supprimer le compte",
|
||||
"DeleteAccountButton": "Supprimer le compte",
|
||||
"publicLink": "Lien public",
|
||||
"maskedPageSpeedKeyPlaceholder": "*************************************",
|
||||
"reset": "Réinitialiser",
|
||||
"ignoreTLSError": "Ignorer les erreurs TLS/SSL",
|
||||
"tlsErrorIgnored": "Erreurs TLS/SSL ignorées",
|
||||
"ignoreTLSErrorDescription": "Ignorer les erreurs TLS/SSL et continuer à vérifier l'accessibilité du site web",
|
||||
"createNew": "Créer un nouveau",
|
||||
"greeting": {
|
||||
"prepend": "Salut !",
|
||||
"append": "L'après-midi est votre libre, rendons-le épique !",
|
||||
"overview": "Voici un aperçu de vos moniteurs {{type}}."
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "Super admin",
|
||||
"admin": "Admin",
|
||||
"teamMember": "Membre de l'équipe",
|
||||
"demoUser": "Utilisateur de démonstration"
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "Membres de l'équipe",
|
||||
"filter": {
|
||||
"all": "Tous",
|
||||
"member": "Membre"
|
||||
},
|
||||
"inviteTeamMember": "Inviter un membre de l'équipe",
|
||||
"inviteNewTeamMember": "Inviter un nouveau membre d'équipe",
|
||||
"inviteDescription": "Lorsque vous ajoutez un nouveau membre à l'équipe, il aura accès à tous les moniteurs.",
|
||||
"email": "Email",
|
||||
"selectRole": "Sélectionnez un rôle",
|
||||
"inviteLink": "Lien d'invitation",
|
||||
"cancel": "Annuler",
|
||||
"noMembers": "Il n'y a aucun membre d'équipe avec ce rôle",
|
||||
"getToken": "Récupérer un token",
|
||||
"emailToken": "Token de l'email",
|
||||
"table": {
|
||||
"name": "Nom",
|
||||
"email": "Email",
|
||||
"role": "Rôle",
|
||||
"created": "Créée"
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "Pause",
|
||||
"resumed": "Reprendre",
|
||||
"active": "Actif"
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "Temps en ligne",
|
||||
"pagespeed": "Vitesse",
|
||||
"infrastructure": "Infrastructure",
|
||||
"incidents": "Incidents",
|
||||
"statusPages": "Pages de statut",
|
||||
"maintenance": "Maintenance",
|
||||
"integrations": "Intégrations",
|
||||
"settings": "Paramètres",
|
||||
"support": "Support",
|
||||
"discussions": "Discussions",
|
||||
"docs": "Documentation",
|
||||
"changelog": "Changelog",
|
||||
"profile": "Profil",
|
||||
"password": "Mot de passe",
|
||||
"team": "Équipe",
|
||||
"logOut": "Déconnexion",
|
||||
"notifications": "Notifications",
|
||||
"logs": "Journaux"
|
||||
},
|
||||
"settingsEmailUser": "Utilisateur SMTP - Utilisateur pour l'authentification, écrase l'adresse email si spécifiée",
|
||||
"state": "État",
|
||||
"statusBreadCrumbsStatusPages": "Pages de statut",
|
||||
"statusBreadCrumbsDetails": "Détails",
|
||||
"commonSaving": "Sauvegarde...",
|
||||
"navControls": "Contrôle",
|
||||
"incidentsPageTitle": "Incidents",
|
||||
"passwordPanel": {
|
||||
"passwordChangedSuccess": "Votre mot de passe a été modifié avec succès.",
|
||||
"passwordInputIncorrect": "La saisie de votre mot de passe est incorrecte.",
|
||||
"currentPassword": "Mot de passe actuel",
|
||||
"enterCurrentPassword": "Entrez votre mot de passe actuel",
|
||||
"newPassword": "Nouveau mot de passe",
|
||||
"enterNewPassword": "Entrez votre nouveau mot de passe",
|
||||
"confirmNewPassword": "Confirmer le nouveau mot de passe",
|
||||
"passwordRequirements": "Le nouveau mot de passe doit contenir au moins 8 caractères et au moins une lettre majuscule, une lettre minuscule, un chiffre et un caractère spécial.",
|
||||
"saving": "Sauvegarde..."
|
||||
},
|
||||
"emailSent": "Email envoyé avec succès",
|
||||
"failedToSendEmail": "Une erreur s'est produite lors de l'envoi de l'email",
|
||||
"settingsTestEmailSuccess": "Email de test envoyé avec succès",
|
||||
"settingsTestEmailFailed": "Une erreur s'est produite lors de l'envoi de l'email de test",
|
||||
"settingsTestEmailFailedWithReason": "Une erreur s'est produite lors de l'envoi de l'email de test : {{reason}}",
|
||||
"settingsTestEmailUnknownError": "Erreur inconnue",
|
||||
"statusMsg": {
|
||||
"paused": "Le moniteur est en pause.",
|
||||
"up": "Votre site est en ligne.",
|
||||
"down": "Votre site est hors-ligne.",
|
||||
"pending": "En attente..."
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "Entrez l'URL ou l'IP du moniteur (par exemple https://exemple.fr ou 192.168.1.100) et ajoutez un nom familier qui apparaîtra sur le tableau de bord.",
|
||||
"ping": "Entrez l'adresse IP ou le nom d'hôte à tester (par exemple, 192.168.1.100 ou exemple.fr) et ajoutez un nom familier qui apparaîtra sur le tableau de bord.",
|
||||
"docker": "Entrer l'ID Docker du container. Les identifiants Docker doivent être les 64 caractères de l'ID Docker. Vous pouvez utiliser la commande docker inspect <short_id> pour avoir l'ID complet.",
|
||||
"port": "Entrez l'URL ou l'adresse IP du serveur, le numéro de port et un nom d'affichage familier qui apparaîtra sur le tableau de bord."
|
||||
},
|
||||
"common": {
|
||||
"appName": "Checkmate",
|
||||
"monitoringAgentName": "Capture",
|
||||
"buttons": {
|
||||
"toggleTheme": "Afficher le mode clair & sombre"
|
||||
},
|
||||
"toasts": {
|
||||
"networkError": "Erreur de réseau",
|
||||
"checkConnection": "Merci de vérifier votre connexion",
|
||||
"unknownError": "Erreur inconnue"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"common": {
|
||||
"navigation": {
|
||||
"continue": "Continuer",
|
||||
"back": "Retour"
|
||||
},
|
||||
"inputs": {
|
||||
"email": {
|
||||
"label": "Email",
|
||||
"placeholder": "jean.dupont@domaine.fr",
|
||||
"errors": {
|
||||
"empty": "Pour continuer, merci d'indiquer votre adresse email",
|
||||
"invalid": "Merci de vérifier la validité de l'adresse email saisie"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "Mot de passe",
|
||||
"rules": {
|
||||
"length": {
|
||||
"beginning": "Doit contenir au moins",
|
||||
"highlighted": "Longueur de 8 caractères"
|
||||
},
|
||||
"special": {
|
||||
"beginning": "Doit contenir au moins",
|
||||
"highlighted": "un caractère spécial"
|
||||
},
|
||||
"number": {
|
||||
"beginning": "Doit contenir au moins",
|
||||
"highlighted": "un nombre"
|
||||
},
|
||||
"uppercase": {
|
||||
"beginning": "Doit contenir au moins",
|
||||
"highlighted": "un caractère majuscule"
|
||||
},
|
||||
"lowercase": {
|
||||
"beginning": "Doit contenir au moins",
|
||||
"highlighted": "un caractère miniscule"
|
||||
},
|
||||
"match": {
|
||||
"beginning": "Confirmer le mot de passe et le mot de passe",
|
||||
"highlighted": "doit valoir"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"empty": "Merci d'entrer votre mot de passe",
|
||||
"length": "Le mot de passe doit faire 8 caractères minimum",
|
||||
"uppercase": "Le mot de passe doit contenir au minimum une lettre majuscule",
|
||||
"lowercase": "Le mot de passe doit contenir au minimum une lettre minuscule",
|
||||
"number": "Le mot de passe doit contenir au minimum un chiffre",
|
||||
"special": "Le mot de passe doit contenir au minimum un caractère spécial",
|
||||
"incorrect": "Le mot de passe fourni est erroné"
|
||||
}
|
||||
},
|
||||
"passwordConfirm": {
|
||||
"label": "Confirmer le mot de passe",
|
||||
"placeholder": "Entrez à nouveau le mot de passe pour confirmer",
|
||||
"errors": {
|
||||
"empty": "Merci d'entrer votre mot de passe à nouveau pour le confirmer (aide avec la typo)",
|
||||
"different": "Les mots de passe indiqués ne sont pas identiques, l'un d'entre eux est probablement mal écrit"
|
||||
}
|
||||
},
|
||||
"firstName": {
|
||||
"label": "Prénom",
|
||||
"placeholder": "Jean",
|
||||
"errors": {
|
||||
"empty": "Merci d'entrer votre prénom",
|
||||
"length": "Le prénom doit être inférieur à 50 caractères",
|
||||
"pattern": "Le prénom ne peut contenir que des lettres, des espaces, des apostrophes ou des tirets"
|
||||
}
|
||||
},
|
||||
"lastName": {
|
||||
"label": "Nom",
|
||||
"placeholder": "Dupont",
|
||||
"errors": {
|
||||
"empty": "Merci d'entrer votre nom",
|
||||
"length": "Le nom doit être inférieur à 50 caractères",
|
||||
"pattern": "Le nom ne peut contenir que des lettres, des espaces, des apostrophes ou des tirets"
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"validation": "Une erreur s'est produite lors de la validation des données."
|
||||
},
|
||||
"fields": {
|
||||
"password": {
|
||||
"errors": {
|
||||
"incorrect": "Le mot de passe fourni est erroné"
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"heading": "Connexion",
|
||||
"subheadings": {
|
||||
"stepOne": "Entrez votre adresse email",
|
||||
"stepTwo": "Entrez votre mot de passe"
|
||||
},
|
||||
"links": {
|
||||
"forgotPassword": "Mot de passe oublié ?",
|
||||
"register": "Pas encore de compte ?",
|
||||
"forgotPasswordLink": "Réinitialiser le mot de passe",
|
||||
"registerLink": "Inscrivez vous ici"
|
||||
},
|
||||
"toasts": {
|
||||
"success": "Bon retour ! Vous êtes connecté avec succès.",
|
||||
"incorrectPassword": "Mot de passe incorrect"
|
||||
},
|
||||
"errors": {
|
||||
"password": {
|
||||
"incorrect": "Le mot de passe fourni est erroné"
|
||||
}
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
"superAdmin": "Créer un super admin",
|
||||
"user": "Inscription"
|
||||
},
|
||||
"subheadings": {
|
||||
"stepOne": "Entrez votre informations personnelles",
|
||||
"stepTwo": "Entrez votre adresse email",
|
||||
"stepThree": "Créez votre mot de passe"
|
||||
},
|
||||
"description": {
|
||||
"superAdmin": "Créer le compte \"SuperAdmin\" pour commencer",
|
||||
"user": "inscription comme utilisateur et demander à l'administrateur principal l'accès aux moniteurs"
|
||||
},
|
||||
"gettingStartedButton": {
|
||||
"superAdmin": "Créer un compte super admin",
|
||||
"user": "Inscription comme utilisateur normal"
|
||||
},
|
||||
"termsAndPolicies": "En créant un compte, vous comprenez et acceptez les <a1>Conditions d'Utilisation</a1> et la <a2>Politique de Confidentialité</a2>.",
|
||||
"links": {
|
||||
"login": "Déjà inscrit ? <a>Connexion</a>"
|
||||
},
|
||||
"toasts": {
|
||||
"success": "Bienvenue ! Votre compte a été créé avec succès."
|
||||
}
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "Mot de passe oublié ?",
|
||||
"subheadings": {
|
||||
"stepOne": "Pas d'inquiétude, nous allons vous envoyer des informations pour réinitialiser votre mot de passe.",
|
||||
"stepTwo": "Nous avons envoyer un lien de réinitialisation du mot de passe à l'email <email/>",
|
||||
"stepThree": "Votre nouveau mot de passe doit être différent des précédents.",
|
||||
"stepFour": "Votre mot de passe a été réinitialisé avec succès. Cliquez ci-dessous pour vous connecter."
|
||||
},
|
||||
"buttons": {
|
||||
"openEmail": "Ouvrir votre application email",
|
||||
"resetPassword": "Réinitialiser le mot de passe"
|
||||
},
|
||||
"imageAlts": {
|
||||
"passwordKey": "Icône du mot de passe",
|
||||
"email": "Icône de l'email",
|
||||
"lock": "Icône de verrouillage",
|
||||
"passwordConfirm": "Icône de confirmation du mot de passe"
|
||||
},
|
||||
"links": {
|
||||
"login": "Retourner à la page de <a>connexion</a>",
|
||||
"resend": "Vous n'avez pas reçu l'email ? <a>Cliquez ici pour le renvoyer</a>"
|
||||
},
|
||||
"toasts": {
|
||||
"sent": "Instructions envoyées sur l'adresse email <email/>.",
|
||||
"emailNotFound": "L'email n'a pas été trouvé.",
|
||||
"redirect": "Redirection dans <seconds/>...",
|
||||
"success": "Votre mot de passe",
|
||||
"error": "Une erreur s'est produite lors de la réinitialisation du mot de passe. Réessayez ultérieurement ou contactez le support."
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorPages": {
|
||||
"serverUnreachable": {
|
||||
"toasts": {
|
||||
"reconnected": "Connexion rétablie avec le serveur.",
|
||||
"stillUnreachable": "Le serveur est toujours inaccessible. Réessayez ultérieurement."
|
||||
},
|
||||
"alertBox": "Erreur de connexion au serveur",
|
||||
"description": "Nous ne parvenons pas à nous connecter au serveur. Veuillez vérifier votre connexion Internet ou vérifier votre configuration de déploiement si le problème persiste.",
|
||||
"retryButton": {
|
||||
"default": "Réessayer la connexion",
|
||||
"processing": "Connexion..."
|
||||
}
|
||||
}
|
||||
},
|
||||
"createNotifications": {
|
||||
"title": "Créer un canal de notification",
|
||||
"nameSettings": {
|
||||
"title": "Nom",
|
||||
"description": "Une description pour votre intégration.",
|
||||
"nameLabel": "Nom",
|
||||
"namePlaceholder": "e.g. notifications Slack"
|
||||
},
|
||||
"typeSettings": {
|
||||
"title": "Type",
|
||||
"description": "Sélectionnez le type de canal de notification que vous souhaitez créer.",
|
||||
"typeLabel": "Type"
|
||||
},
|
||||
"emailSettings": {
|
||||
"title": "Email",
|
||||
"description": "Adresse email du destinataire",
|
||||
"emailLabel": "Adresse email",
|
||||
"emailPlaceholder": "e.g. jean@exemple.fr"
|
||||
},
|
||||
"slackSettings": {
|
||||
"title": "Slack",
|
||||
"description": "Configurez votre webhook Slack ici",
|
||||
"webhookLabel": "URL du webhook Slack",
|
||||
"webhookPlaceholder": "https://hooks.slack.com/services/..."
|
||||
},
|
||||
"pagerdutySettings": {
|
||||
"title": "PagerDuty",
|
||||
"description": "Configurez l'intégration PagerDuty ici",
|
||||
"integrationKeyLabel": "Clé d'intégration",
|
||||
"integrationKeyPlaceholder": "1234567890"
|
||||
},
|
||||
"discordSettings": {
|
||||
"title": "Discord",
|
||||
"description": "Configurez votre webhook Discord ici",
|
||||
"webhookLabel": "URL du webhook Discord",
|
||||
"webhookPlaceholder": "https://your-server.com/webhook"
|
||||
},
|
||||
"webhookSettings": {
|
||||
"title": "Webhook",
|
||||
"description": "Configurez votre webhook ici",
|
||||
"webhookLabel": "URL du webhook",
|
||||
"webhookPlaceholder": "https://your-server.com/webhook"
|
||||
},
|
||||
"testNotification": "Notification de test",
|
||||
"dialogDeleteTitle": "Voulez-vous vraiment supprimer cette notification ?",
|
||||
"dialogDeleteConfirm": "Supprimer"
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "Notifications",
|
||||
"description": "Sélectionnez les canaux de notification que vous souhaitez utiliser"
|
||||
},
|
||||
"monitorStatus": {
|
||||
"checkingEvery": "Vérifier toutes les {{interval}}",
|
||||
"withCaptureAgent": "avec l'agent Capture {{version}}",
|
||||
"up": "en ligne",
|
||||
"down": "hors ligne",
|
||||
"paused": "en pause"
|
||||
},
|
||||
"advancedMatching": "Filtrage avancé",
|
||||
"sendTestNotifications": "Envoyer une notification de test",
|
||||
"selectAll": "Sélectionner tout",
|
||||
"showAdminLoginLink": "Voir le lien \"Administrateur ? Connectez-vous ici\" sur la page de statut",
|
||||
"logsPage": {
|
||||
"title": "Journaux",
|
||||
"description": "Journaux systèmes - 1000 dernières lignes",
|
||||
"tabs": {
|
||||
"queue": "File d'attente des tâches",
|
||||
"logs": "Journaux des serveurs",
|
||||
"diagnostics": "Diagnostic"
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": "Journaux récupérés avec succès"
|
||||
},
|
||||
"logLevelSelect": {
|
||||
"title": "Niveau de journalisation",
|
||||
"values": {
|
||||
"all": "Tout",
|
||||
"info": "Info",
|
||||
"warn": "Attention",
|
||||
"error": "Erreur",
|
||||
"debug": "Debug"
|
||||
}
|
||||
}
|
||||
},
|
||||
"queuePage": {
|
||||
"title": "File d'attente",
|
||||
"refreshButton": "Rafraîchir",
|
||||
"flushButton": "Vider la file d'attente",
|
||||
"jobTable": {
|
||||
"title": "Tâches actuellement dans la file d'attente",
|
||||
"idHeader": "ID du moniteur",
|
||||
"urlHeader": "URL",
|
||||
"typeHeader": "Type",
|
||||
"activeHeader": "Actif",
|
||||
"lockedAtHeader": "Verrouillé le",
|
||||
"runCountHeader": "Nombre de tentatives",
|
||||
"failCountHeader": "Nombre d'échecs",
|
||||
"lastRunHeader": "Dernière tentative le",
|
||||
"lastFinishedAtHeader": "Dernière finalisation à",
|
||||
"lastRunTookHeader": "Dernière exécution effectuée"
|
||||
},
|
||||
"metricsTable": {
|
||||
"title": "Métriques des files",
|
||||
"metricHeader": "Métrique",
|
||||
"valueHeader": "Valeur"
|
||||
},
|
||||
"failedJobTable": {
|
||||
"title": "Tâches en échec",
|
||||
"monitorIdHeader": "ID du moniteur",
|
||||
"monitorUrlHeader": "URL du moniteur",
|
||||
"failCountHeader": "Nombre de tentatives échouées",
|
||||
"failedAtHeader": "Dernier échec à",
|
||||
"failReasonHeader": "Raison de l'échec"
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"title": "Export des moniteurs",
|
||||
"success": "Les moniteurs ont été exportés avec succès !",
|
||||
"failed": "Une erreur s'est produite lors de l'export des moniteurs"
|
||||
},
|
||||
"monitorActions": {
|
||||
"title": "Export/Import",
|
||||
"import": "Importer des moniteurs",
|
||||
"export": "Exporter des moniteurs",
|
||||
"deleteSuccess": "Le moniteur a été supprimé avec succès",
|
||||
"deleteFailed": "Une erreur s'est produite lors de la suppression du moniteur",
|
||||
"details": "Détails"
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
"labelDevelopedBy": "Développé par Bluewave Labs",
|
||||
"labelVersion": "Version",
|
||||
"title": "A propos"
|
||||
},
|
||||
"demoMonitorsSettings": {
|
||||
"buttonAddMonitors": "Ajouter un moniteur de démonstration",
|
||||
"description": "Ajouter un moniteur à des fins de démonstration.",
|
||||
"title": "Moniteurs de démonstration"
|
||||
},
|
||||
"emailSettings": {
|
||||
"buttonSendTestEmail": "Envoyer un mail de test",
|
||||
"description": "Configurez les paramètres emails pour le système. Il s'agit du système utilisé pour les notifications & alertes.",
|
||||
"descriptionTransport": "Ceci créé un transport SMTP pour NodeMailer",
|
||||
"labelAddress": "Adresse email - utilisée pour la connexion",
|
||||
"labelConnectionHost": "Hôte SMTP - Nom d'hôte à utiliser dans l'introduction HELO/EHLO",
|
||||
"labelHost": "Hôte SMTP - Nom d'hôte ou IP sur lequel se connecter",
|
||||
"labelIgnoreTLS": "Désactiver STARTTLS : ne pas utiliser TLS même si le serveur le supporte",
|
||||
"labelPassword": "Mot de passe SMTP - Mot de passe requis pour l'authentification",
|
||||
"labelPasswordSet": "Le mot de passe est renseigné. Réinitialisez le pour le changer.",
|
||||
"labelPool": "Activer le pool de connexion SMTP afin de réutiliser les connexions existantes pour améliorer la performance",
|
||||
"labelPort": "Port SMTP - Port auquel se connecter",
|
||||
"labelRejectUnauthorized": "Rejeter les certificats non valides : rejeter les connexions avec des certificats auto-signés ou non fiables",
|
||||
"labelRequireTLS": "Forcer STARTTLS : nécessite la mise à niveau TLS, échouer si ce n'est pas supporté",
|
||||
"labelSecure": "Utiliser le SSL (recommandé) : chiffrer la connexion SSL/TLS",
|
||||
"labelTLSServername": "Nom du serveur TLS - Nom d'hôte facultatif pour la validation TLS lorsque l'hôte est une adresse IP",
|
||||
"labelUser": "Utilisateur SMTP - Nom d'utilisateur pour l'authentification, remplace l'adresse email si spécifié",
|
||||
"linkTransport": "Voir les spécifications ici",
|
||||
"placeholderUser": "Laisser vide si non requis",
|
||||
"title": "Email",
|
||||
"toastEmailRequiredFieldsError": "L'adresse email, l'hôte, le port et le mot de passe sont obligatoires."
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "Entrez votre clé API Google PageSpeed pour activer la surveillance Google PageSpeed. Cliquez sur Réinitialiser pour mettre à jour la clé.",
|
||||
"labelApiKeySet": "La clé API est générée. Cliquez sur \"Réinitialiser\" pour la modifier.",
|
||||
"labelApiKey": "Clé API PageSpeed",
|
||||
"title": "Clé API Google PageSpeed"
|
||||
},
|
||||
"saveButtonLabel": "Sauvegarder",
|
||||
"statsSettings": {
|
||||
"clearAllStatsButton": "Supprimer toutes les statistiques",
|
||||
"clearAllStatsDescription": "Supprimer toutes les statistiques. Cette action est irréversible.",
|
||||
"clearAllStatsDialogConfirm": "Oui, supprimer toutes les statistiques",
|
||||
"clearAllStatsDialogDescription": "Une fois supprimés, l'historique et les statistiques du moniteur ne peuvent plus être récupérés.",
|
||||
"clearAllStatsDialogTitle": "Voulez-vous supprimer toutes les statistiques ?",
|
||||
"description": "Définissez la durée pendant laquelle vous souhaitez conserver les données historiques. Vous pouvez également effacer toutes les données existantes.",
|
||||
"labelTTL": "Le nombre de jours dont vous souhaitez garder l'historique.",
|
||||
"labelTTLOptional": "0 pour une durée infinie",
|
||||
"title": "Historique du moniteur"
|
||||
},
|
||||
"systemResetSettings": {
|
||||
"buttonRemoveAllMonitors": "Supprimer tous les moniteurs",
|
||||
"description": "Supprimer toutes les moniteurs du système.",
|
||||
"dialogConfirm": "Oui, supprimer tous les moniteurs",
|
||||
"dialogDescription": "Une fois supprimés, les moniteurs ne peuvent plus être récupérés.",
|
||||
"dialogTitle": "Voulez-vous supprimer tous les moniteurs ?",
|
||||
"title": "Réinitialisation du système"
|
||||
},
|
||||
"timezoneSettings": {
|
||||
"description": "Sélectionnez le fuseau horaire utilisé pour afficher les dates et les heures dans l'application.",
|
||||
"label": "Fuseau horaire d'affichage",
|
||||
"title": "Fuseau horaire d'affichage"
|
||||
},
|
||||
"title": "Paramètres",
|
||||
"uiSettings": {
|
||||
"description": "Changer entre le mode clair et sombre, ou changer le langage de l'interface utilisateur.",
|
||||
"labelLanguage": "Langage",
|
||||
"labelTheme": "Thème",
|
||||
"title": "Apparence"
|
||||
},
|
||||
"urlSettings": {
|
||||
"description": "Affichez l'adresse IP ou l'URL du moniteur sur la page de statut publique. Si cette option est désactivée, seul le nom du moniteur sera affiché afin de protéger les informations sensibles.",
|
||||
"label": "Afficher l'IP/URL sur la page de statut",
|
||||
"selectDisabled": "Désactivé",
|
||||
"selectEnabled": "Activé",
|
||||
"title": "IP/URL du moniteur sur la page de statut"
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": "Sauvegarder"
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "Résolu",
|
||||
"settingsSave": "Sauvegarder",
|
||||
"statusPageCreateAppearanceTitle": "Apparence",
|
||||
"confirmPassword": "Confirmer le mot de passe",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "Une erreur s'est produite lors de l'ajout des moniteurs de démonstration",
|
||||
"successAddDemoMonitors": "Les moniteurs de démonstration ont bien été ajoutés"
|
||||
},
|
||||
"settingsAppearance": "Apparance",
|
||||
"settingsDisplayTimezone": "Fuseau horaire d'affichage",
|
||||
"settingsGeneralSettings": "Paramètres généraux",
|
||||
"incidentsOptionsHeaderTotalIncidents": "Nombre d'incidents",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "La page de statut a été supprimée avec succès",
|
||||
"deleteFailed": "Une erreur s'est produite lors de l'ajout de la page de statut",
|
||||
"createSuccess": "La page de statut a été ajoutée avec succès",
|
||||
"updateSuccess": "La page de statut a été modifiée avec succès",
|
||||
"generalSettings": "Paramètres généraux",
|
||||
"contents": "Contenus",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "Il n'y aucune notification paramétrée pour ce moniteur. Vous pouvez en ajouter une en cliquant sur le bouton 'Configurer'",
|
||||
"incidentsTableResolvedAt": "Résolu le",
|
||||
"incidentsTableActionResolve": "Résoudre",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "Une erreur s'est produite lors de la résolution de l'incident.",
|
||||
"failureResolveAll": "Une erreur s'est produite lors de la résolution des incidents.",
|
||||
"failureResolveMonitor": ""
|
||||
},
|
||||
"checkFormError": "Merci de vérifier les erreurs du formulaire",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "Diagnostic du système",
|
||||
"statsDescription": "Statistiques du système",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "Allocation du tas",
|
||||
"heapAllocationSubtitle": "% de mémoire disponible",
|
||||
"heapUsageTitle": "Utilisation du tas",
|
||||
"heapUsageSubtitle": "% de mémoire disponible",
|
||||
"heapUtilizationTitle": "Utilisation du tas",
|
||||
"heapUtilizationSubtitle": "% d'allocation",
|
||||
"instantCpuUsageTitle": "Utilisation instantanée du CPU",
|
||||
"instantCpuUsageSubtitle": "% de CPU utilisé"
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "Délai de la boucle d'évènements",
|
||||
"uptimeTitle": "Temps en ligne",
|
||||
"usedHeapSizeTitle": "Taille du tas utilisé",
|
||||
"totalHeapSizeTitle": "Taille total du tas",
|
||||
"osMemoryLimitTitle": "Limite de mémoire de l'OS"
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "Utilisez l'API Lighthouse PageSpeed pour suivre votre site web",
|
||||
"time": {
|
||||
"threeMinutes": "3 minutes",
|
||||
"fiveMinutes": "5 minutes",
|
||||
"tenMinutes": "10 minutes",
|
||||
"twentyMinutes": "20 minutes",
|
||||
"oneHour": "1 heure",
|
||||
"oneDay": "1 jour",
|
||||
"oneWeek": "1 semaine"
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
},
|
||||
"title": "",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
"settingsFailedToSave": "Falha ao salvar as configurações",
|
||||
"settingsStatsCleared": "Estatísticas limpas com sucesso",
|
||||
"settingsFailedToClearStats": "Falha ao limpar estatísticas",
|
||||
"settingsFailedToAddDemoMonitors": "Falha ao adicionar monitores de demonstração",
|
||||
"settingsMonitorsDeleted": "Todos os monitores foram excluídos com sucesso",
|
||||
"settingsFailedToDeleteMonitors": "Falha ao excluir todos os monitores",
|
||||
"starPromptTitle": "Star Checkmate",
|
||||
@@ -86,7 +85,8 @@
|
||||
"networkError": "Ocorreu um erro de rede",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""]
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
@@ -331,7 +331,9 @@
|
||||
"uploadSuccess": "Monitores criados com sucesso!",
|
||||
"validationFailed": "Falha na validação",
|
||||
"noFileSelected": "Nenhum arquivo selecionado",
|
||||
"fallbackPage": "Importe um arquivo para enviar uma lista de servidores em massa"
|
||||
"fallbackPage": "Importe um arquivo para enviar uma lista de servidores em massa",
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
},
|
||||
"DeleteAccountTitle": "Remover conta",
|
||||
"DeleteAccountButton": "Remover conta",
|
||||
@@ -432,47 +434,47 @@
|
||||
"pending": "Pendente..."
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
"http": "Insira a URL ou IP para monitorar (ex. https://exemplo.com.br/ ou 192.168.1.100) e adicione uma descrição que aparecerá na dashboard.",
|
||||
"ping": "Insira o endereço IP ou nome de domínio para ping (ex. 192.168.1.100 ou exemplo.com.br) e adicione uma descrição que aparecerá na dashboard.",
|
||||
"docker": "Insira o Docker Id do seu container. Docker Ids devem ser todos os 64 caracteres. Você pode executar docker inspect <short_id> para descobrir o Id completo.",
|
||||
"port": "Insira a URL ou o IP do servidor, aporta e uma descrição que aparecerá na dashboard."
|
||||
},
|
||||
"common": {
|
||||
"appName": "",
|
||||
"monitoringAgentName": "",
|
||||
"appName": "Checkmate",
|
||||
"monitoringAgentName": "Capture",
|
||||
"buttons": {
|
||||
"toggleTheme": ""
|
||||
"toggleTheme": "Trocar claro e escuro"
|
||||
},
|
||||
"toasts": {
|
||||
"networkError": "",
|
||||
"checkConnection": "",
|
||||
"unknownError": ""
|
||||
"networkError": "Erro de rede",
|
||||
"checkConnection": "Por favor, verifique a conexão",
|
||||
"unknownError": "Erro desconhecido"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"common": {
|
||||
"navigation": {
|
||||
"continue": "",
|
||||
"back": ""
|
||||
"continue": "Continuar",
|
||||
"back": "Voltar"
|
||||
},
|
||||
"inputs": {
|
||||
"email": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"label": "Email",
|
||||
"placeholder": "joao.silva@dominio.com.br",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"invalid": ""
|
||||
"empty": "Para continuar, insira seu endereço de email",
|
||||
"invalid": "Por favor, confirme o endereço de email informado"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "",
|
||||
"label": "Senha",
|
||||
"rules": {
|
||||
"length": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Deve ter no mínimo",
|
||||
"highlighted": "8 caracteres de comprimento"
|
||||
},
|
||||
"special": {
|
||||
"beginning": "",
|
||||
"beginning": "Deve conter pelo menos",
|
||||
"highlighted": ""
|
||||
},
|
||||
"number": {
|
||||
@@ -493,9 +495,9 @@
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"uppercase": "",
|
||||
"empty": "Por favor, digite sua senha",
|
||||
"length": "A senha deve ter pelo menos 8 caracteres",
|
||||
"uppercase": "A senha deve conter pelo menos 1 letra maiúscula",
|
||||
"lowercase": "",
|
||||
"number": "",
|
||||
"special": "",
|
||||
@@ -511,50 +513,55 @@
|
||||
}
|
||||
},
|
||||
"firstName": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"label": "Nome",
|
||||
"placeholder": "João",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"pattern": ""
|
||||
"empty": "Por favor, insira seu nome",
|
||||
"length": "Nome deve ter menos de 50 caracteres",
|
||||
"pattern": "Nome deve conter apenas letras, espaços, apóstrofos ou hífens"
|
||||
}
|
||||
},
|
||||
"lastName": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"label": "Sobrenome",
|
||||
"placeholder": "Silva",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"pattern": ""
|
||||
"empty": "Por favor, insira seu sobrenome",
|
||||
"length": "Sobrenome deve ter menos de 50 caracteres",
|
||||
"pattern": "Sobrenome deve conter apenas letras, espaços, apóstrofos ou hífens"
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"validation": ""
|
||||
"validation": "Erro de validação de dados."
|
||||
},
|
||||
"fields": {
|
||||
"password": {
|
||||
"errors": {
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"heading": "",
|
||||
"heading": "Entrar",
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": ""
|
||||
"stepOne": "Insira seu email",
|
||||
"stepTwo": "Insira sua senha"
|
||||
},
|
||||
"links": {
|
||||
"forgotPassword": "",
|
||||
"register": "",
|
||||
"forgotPassword": "Esqueceu sua senha?",
|
||||
"register": "Não tem uma conta?",
|
||||
"forgotPasswordLink": "",
|
||||
"registerLink": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": "",
|
||||
"incorrectPassword": ""
|
||||
"success": "Bem-vindo de volta! Você está autenticado.",
|
||||
"incorrectPassword": "Senha incorreta"
|
||||
},
|
||||
"errors": {
|
||||
"password": {
|
||||
@@ -564,8 +571,8 @@
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
"superAdmin": "Crie um usuário administrador",
|
||||
"user": "Se inscrever"
|
||||
},
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
@@ -604,11 +611,11 @@
|
||||
"passwordKey": "",
|
||||
"email": "",
|
||||
"lock": "",
|
||||
"passwordConfirm": ""
|
||||
"passwordConfirm": "Ícone de confirmação de senha"
|
||||
},
|
||||
"links": {
|
||||
"login": "",
|
||||
"resend": ""
|
||||
"login": "Voltar para o <a>login</a>",
|
||||
"resend": "Não recebeu o email? <a>Clique para reenviar</a>"
|
||||
},
|
||||
"toasts": {
|
||||
"sent": "",
|
||||
@@ -675,7 +682,10 @@
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
}
|
||||
},
|
||||
"testNotification": "",
|
||||
"dialogDeleteTitle": "",
|
||||
"dialogDeleteConfirm": ""
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "",
|
||||
@@ -690,7 +700,6 @@
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"sendTestNotifications": "",
|
||||
"testNotificationsDisabled": "",
|
||||
"selectAll": "",
|
||||
"showAdminLoginLink": "",
|
||||
"logsPage": {
|
||||
@@ -698,7 +707,8 @@
|
||||
"description": "",
|
||||
"tabs": {
|
||||
"queue": "",
|
||||
"logs": ""
|
||||
"logs": "",
|
||||
"diagnostics": ""
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
@@ -753,7 +763,10 @@
|
||||
"monitorActions": {
|
||||
"title": "",
|
||||
"import": "",
|
||||
"export": ""
|
||||
"export": "",
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"details": ""
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
@@ -785,7 +798,8 @@
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": ""
|
||||
"title": "",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
@@ -832,5 +846,124 @@
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "",
|
||||
"failureResolveAll": "",
|
||||
"failureResolveMonitor": ""
|
||||
},
|
||||
"checkFormError": "",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
},
|
||||
"title": "",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
"settingsFailedToSave": "Не удалось сохранить настройки",
|
||||
"settingsStatsCleared": "Статистика успешно очищена",
|
||||
"settingsFailedToClearStats": "Не удалось очистить статистику",
|
||||
"settingsFailedToAddDemoMonitors": "Не удалось добавить демонстрационные мониторы",
|
||||
"settingsMonitorsDeleted": "Успешно удалены все мониторы",
|
||||
"settingsFailedToDeleteMonitors": "Не удалось удалить все мониторы",
|
||||
"starPromptTitle": "Star Checkmate",
|
||||
"starPromptTitle": "",
|
||||
"starPromptDescription": "Ознакомьтесь с последними релизами и помогите развить сообщество на GitHub",
|
||||
"https": "HTTPS",
|
||||
"http": "HTTP",
|
||||
@@ -86,7 +85,8 @@
|
||||
"networkError": "Произошла сетевая ошибка",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""]
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
@@ -331,7 +331,9 @@
|
||||
"uploadSuccess": "Мониторы успешно созданы!",
|
||||
"validationFailed": "Не удалось выполнить проверку",
|
||||
"noFileSelected": "Файл не выбран",
|
||||
"fallbackPage": "Импортируйте файл для массовой загрузки списка серверов"
|
||||
"fallbackPage": "Импортируйте файл для массовой загрузки списка серверов",
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
},
|
||||
"DeleteAccountTitle": "Удалить аккаунт",
|
||||
"DeleteAccountButton": "Удалить аккаунт",
|
||||
@@ -537,6 +539,11 @@
|
||||
"errors": {
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -675,7 +682,10 @@
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
}
|
||||
},
|
||||
"testNotification": "",
|
||||
"dialogDeleteTitle": "",
|
||||
"dialogDeleteConfirm": ""
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "",
|
||||
@@ -690,7 +700,6 @@
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"sendTestNotifications": "",
|
||||
"testNotificationsDisabled": "",
|
||||
"selectAll": "",
|
||||
"showAdminLoginLink": "",
|
||||
"logsPage": {
|
||||
@@ -698,7 +707,8 @@
|
||||
"description": "",
|
||||
"tabs": {
|
||||
"queue": "",
|
||||
"logs": ""
|
||||
"logs": "",
|
||||
"diagnostics": ""
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
@@ -753,7 +763,10 @@
|
||||
"monitorActions": {
|
||||
"title": "",
|
||||
"import": "",
|
||||
"export": ""
|
||||
"export": "",
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"details": ""
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
@@ -785,7 +798,8 @@
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": ""
|
||||
"title": "",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
@@ -832,5 +846,124 @@
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "",
|
||||
"failureResolveAll": "",
|
||||
"failureResolveMonitor": ""
|
||||
},
|
||||
"checkFormError": "",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
},
|
||||
"title": "",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"settingsFailedToSave": "Ayarlar kaydedilemedi",
|
||||
"settingsStatsCleared": "İstatistikler başarıyla temizlendi",
|
||||
"settingsFailedToClearStats": "İstatistikler temizlenemedi",
|
||||
"settingsFailedToAddDemoMonitors": "Demo monitörler eklenemedi",
|
||||
"settingsMonitorsDeleted": "Tüm monitörler başarıyla silindi",
|
||||
"settingsFailedToDeleteMonitors": "Monitörler silinemedi",
|
||||
"starPromptTitle": "Checkmate yıldızla değerlendirin",
|
||||
@@ -86,7 +85,8 @@
|
||||
"networkError": "Ağ hatası oluştu",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""]
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "Bildirim kanalı oluştur",
|
||||
"createTitle": "Bildirim kanalı",
|
||||
@@ -95,16 +95,16 @@
|
||||
"failed": "Bildirim kanalı oluşturulamadı"
|
||||
},
|
||||
"fetch": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Bildirimler başarıyla alındı",
|
||||
"failed": "Bildirimler alınamadı"
|
||||
},
|
||||
"delete": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Bildirim başarıyla silindi",
|
||||
"failed": "Bildirim silinemedi"
|
||||
},
|
||||
"edit": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Bildirim başarıyla güncellendi",
|
||||
"failed": "Bildirim güncellenemedi"
|
||||
},
|
||||
"test": {
|
||||
"success": "Test bildirimi başarıyla gönderildi",
|
||||
@@ -331,7 +331,9 @@
|
||||
"uploadSuccess": "Monitör başarıyla oluşturuldu!",
|
||||
"validationFailed": "Doğrulama başarısız oldu",
|
||||
"noFileSelected": "Hiçbir dosya seçilmedi",
|
||||
"fallbackPage": "Toplu olarak sunucu listesini yüklemek için bir dosyayı içe aktarın"
|
||||
"fallbackPage": "Toplu olarak sunucu listesini yüklemek için bir dosyayı içe aktarın",
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
},
|
||||
"DeleteAccountTitle": "Hesabı sil",
|
||||
"DeleteAccountButton": "Hesabı sil",
|
||||
@@ -340,11 +342,11 @@
|
||||
"reset": "Sıfırla",
|
||||
"ignoreTLSError": "TLS/SSL hatalarını gözardı et",
|
||||
"tlsErrorIgnored": "TLS/SSL hataları gözardı ediliyor",
|
||||
"ignoreTLSErrorDescription": "",
|
||||
"ignoreTLSErrorDescription": "TLS/SSL hatalarını gözardı et ve websitesinin ayakta olduğunu denetle",
|
||||
"createNew": "Yeni oluştur",
|
||||
"greeting": {
|
||||
"prepend": "Merhaba",
|
||||
"append": "",
|
||||
"append": "Öğleden sonra muhteşem geçsin!",
|
||||
"overview": ""
|
||||
},
|
||||
"roles": {
|
||||
@@ -365,28 +367,28 @@
|
||||
"email": "Eposta",
|
||||
"selectRole": "Rolü seçin",
|
||||
"inviteLink": "Davet linki",
|
||||
"cancel": "",
|
||||
"noMembers": "",
|
||||
"cancel": "İptal",
|
||||
"noMembers": "Bu role sahip hiç üye yok",
|
||||
"getToken": "",
|
||||
"emailToken": "",
|
||||
"table": {
|
||||
"name": "",
|
||||
"email": "",
|
||||
"role": "",
|
||||
"created": ""
|
||||
"name": "İsim",
|
||||
"email": "Eposta",
|
||||
"role": "Rol",
|
||||
"created": "Oluşturulma"
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "",
|
||||
"resumed": "",
|
||||
"active": ""
|
||||
"paused": "Durduruldu",
|
||||
"resumed": "Yeniden başlatıldı",
|
||||
"active": "Aktif"
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "",
|
||||
"pagespeed": "",
|
||||
"pagespeed": "Sayfa hızı",
|
||||
"infrastructure": "",
|
||||
"incidents": "",
|
||||
"statusPages": "",
|
||||
"incidents": "Olaylar",
|
||||
"statusPages": "Durum sayfaları",
|
||||
"maintenance": "Bakım",
|
||||
"integrations": "Entegrasyonlar",
|
||||
"settings": "Ayarlar",
|
||||
@@ -399,7 +401,7 @@
|
||||
"team": "Ekip",
|
||||
"logOut": "Çıkış yap",
|
||||
"notifications": "Bildirimler",
|
||||
"logs": ""
|
||||
"logs": "Günlükler"
|
||||
},
|
||||
"settingsEmailUser": "E-posta kullanıcısı – Kimlik doğrulama için kullanıcı adı; belirtilirse e-posta adresinin yerine geçer.",
|
||||
"state": "Durum",
|
||||
@@ -512,11 +514,11 @@
|
||||
},
|
||||
"firstName": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"placeholder": "Jordan",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"length": "",
|
||||
"pattern": ""
|
||||
"empty": "Lütfen adınızı girin",
|
||||
"length": "İsim 50 karakterden az olmalıdır",
|
||||
"pattern": "İsim yalnızca harfler, boşluklar, kesme işaretleri veya tireler içerebilir"
|
||||
}
|
||||
},
|
||||
"lastName": {
|
||||
@@ -525,23 +527,28 @@
|
||||
"errors": {
|
||||
"empty": "Lütfen soyadınızı girin",
|
||||
"length": "Soyadı en çok 50 karakter olmalıdır",
|
||||
"pattern": ""
|
||||
"pattern": "Soyad yalnızca harfler, boşluklar, kesme işaretleri veya tireler içerebilir"
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"validation": ""
|
||||
"validation": "Veriyi işlerken hata oluştu."
|
||||
},
|
||||
"fields": {
|
||||
"password": {
|
||||
"errors": {
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"heading": "",
|
||||
"heading": "Giriş yap",
|
||||
"subheadings": {
|
||||
"stepOne": "Eposta adresinizi girin",
|
||||
"stepTwo": "Parolanızı girin"
|
||||
@@ -580,7 +587,7 @@
|
||||
"superAdmin": "Süper yönetici hesabı oluşturun",
|
||||
"user": ""
|
||||
},
|
||||
"termsAndPolicies": "",
|
||||
"termsAndPolicies": "Bir hesap oluşturarak <a1>Hizmet Şartları</a1> ve <a2>Gizlilik Politikası</a2>'nı kabul etmiş olursunuz.",
|
||||
"links": {
|
||||
"login": "Bir hesabınız mı var? <a>Giriş yapın</a>"
|
||||
},
|
||||
@@ -593,8 +600,8 @@
|
||||
"subheadings": {
|
||||
"stepOne": "Endişelenmeyin, sıfırlamak için gerekli bilgileri göndereceğiz.",
|
||||
"stepTwo": "Parola sıfırlama için gerekli bilgileri <email/> hesabına ilettik.",
|
||||
"stepThree": "",
|
||||
"stepFour": ""
|
||||
"stepThree": "Parolanız daha önce kullandığınız paroladan farklı olmalıdır.",
|
||||
"stepFour": "Parolanız başarıyla sıfırlandı. Buraya tıklayarak giriş yapabilirsiniz."
|
||||
},
|
||||
"buttons": {
|
||||
"openEmail": "Eposta uygulamasını aç",
|
||||
@@ -612,7 +619,7 @@
|
||||
},
|
||||
"toasts": {
|
||||
"sent": "",
|
||||
"emailNotFound": "",
|
||||
"emailNotFound": "Eposta bulunamadı.",
|
||||
"redirect": "",
|
||||
"success": "",
|
||||
"error": ""
|
||||
@@ -675,7 +682,10 @@
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": "https://sunucu-adı.com/webhook\n"
|
||||
}
|
||||
},
|
||||
"testNotification": "Test bildirimi",
|
||||
"dialogDeleteTitle": "Gerçekten bu bildirimi silmek istiyor musunuz?",
|
||||
"dialogDeleteConfirm": "Sil"
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "Bildirimler",
|
||||
@@ -688,20 +698,20 @@
|
||||
"down": "çalışmıyor",
|
||||
"paused": "beklemede"
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"advancedMatching": "Gelişmiş eşleştirme",
|
||||
"sendTestNotifications": "Bildirimleri dene",
|
||||
"testNotificationsDisabled": "",
|
||||
"selectAll": "Tümünü seç",
|
||||
"showAdminLoginLink": "",
|
||||
"logsPage": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "Günlükler",
|
||||
"description": "Sistem günlükleri - son 1000 satır",
|
||||
"tabs": {
|
||||
"queue": "",
|
||||
"logs": ""
|
||||
"queue": "İş kuyruğu",
|
||||
"logs": "Sunucu günlükleri",
|
||||
"diagnostics": "Teşhis"
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
"fetchLogsSuccess": "Günlükler başarıyla alındı"
|
||||
},
|
||||
"logLevelSelect": {
|
||||
"title": "",
|
||||
@@ -723,37 +733,40 @@
|
||||
"idHeader": "",
|
||||
"urlHeader": "",
|
||||
"typeHeader": "",
|
||||
"activeHeader": "",
|
||||
"activeHeader": "Aktif",
|
||||
"lockedAtHeader": "",
|
||||
"runCountHeader": "",
|
||||
"failCountHeader": "",
|
||||
"lastRunHeader": "",
|
||||
"lastFinishedAtHeader": "",
|
||||
"lastFinishedAtHeader": "Son tamamlama",
|
||||
"lastRunTookHeader": ""
|
||||
},
|
||||
"metricsTable": {
|
||||
"title": "",
|
||||
"metricHeader": "",
|
||||
"valueHeader": ""
|
||||
"metricHeader": "Metrik",
|
||||
"valueHeader": "Değer"
|
||||
},
|
||||
"failedJobTable": {
|
||||
"title": "",
|
||||
"monitorIdHeader": "",
|
||||
"monitorUrlHeader": "",
|
||||
"failCountHeader": "",
|
||||
"title": "Tamamlanamayan işler",
|
||||
"monitorIdHeader": "Monitör ID",
|
||||
"monitorUrlHeader": "Monitör URL",
|
||||
"failCountHeader": "Hata sayısı",
|
||||
"failedAtHeader": "",
|
||||
"failReasonHeader": ""
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"title": "",
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"title": "Monitörleri dışarı al",
|
||||
"success": "Monitörler başarıyla dışarıya alındı",
|
||||
"failed": "Monitörleri dışarıya alırken bir hata oluştu"
|
||||
},
|
||||
"monitorActions": {
|
||||
"title": "",
|
||||
"title": "Dışarı/İçeri al",
|
||||
"import": "",
|
||||
"export": ""
|
||||
"export": "",
|
||||
"deleteSuccess": "Monitör başarıyla silindi",
|
||||
"deleteFailed": "Monitör silinemedi",
|
||||
"details": "Detaylar"
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
@@ -784,16 +797,17 @@
|
||||
"labelTLSServername": "",
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": ""
|
||||
"placeholderUser": "Eğer gerekmiyorsa boş bırakabilirsiniz",
|
||||
"title": "Eposta",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
"labelApiKeySet": "",
|
||||
"labelApiKey": "",
|
||||
"title": ""
|
||||
"description": "Google PageSpeed izlemeyi etkinleştirmek için Google PageSpeed API anahtarınızı girin. Anahtarı güncellemek için Sıfırla'ya tıklayın.",
|
||||
"labelApiKeySet": "API key tanımlandı. Sıfırla düğmesine tıklayarak değiştirebilirsiniz.",
|
||||
"labelApiKey": "PageSpeed API anahtarı",
|
||||
"title": "Google PageSpeed API anahtarı"
|
||||
},
|
||||
"saveButtonLabel": "",
|
||||
"saveButtonLabel": "Kaydet",
|
||||
"statsSettings": {
|
||||
"clearAllStatsButton": "",
|
||||
"clearAllStatsDescription": "",
|
||||
@@ -802,35 +816,154 @@
|
||||
"clearAllStatsDialogTitle": "",
|
||||
"description": "",
|
||||
"labelTTL": "",
|
||||
"labelTTLOptional": "",
|
||||
"title": ""
|
||||
"labelTTLOptional": "Sonsuz için 0",
|
||||
"title": "İzleme geçmişi"
|
||||
},
|
||||
"systemResetSettings": {
|
||||
"buttonRemoveAllMonitors": "",
|
||||
"description": "",
|
||||
"dialogConfirm": "",
|
||||
"buttonRemoveAllMonitors": "Tüm monitörleri sil",
|
||||
"description": "Sistemden tüm monitörleri sil",
|
||||
"dialogConfirm": "Evet, tüm monitörleri sil",
|
||||
"dialogDescription": "",
|
||||
"dialogTitle": "",
|
||||
"title": ""
|
||||
"dialogTitle": "Gerçekten tüm monitörleri silmek istiyor musunuz?",
|
||||
"title": "Sistemi sıfırla"
|
||||
},
|
||||
"timezoneSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"title": ""
|
||||
"label": "Görünüm zaman dilimi",
|
||||
"title": "Görünüm zaman dilimi"
|
||||
},
|
||||
"title": "",
|
||||
"title": "Ayarlar",
|
||||
"uiSettings": {
|
||||
"description": "",
|
||||
"labelLanguage": "",
|
||||
"labelTheme": "",
|
||||
"title": ""
|
||||
"description": "Aydınlık ve karanlık mod arasında geçiş yapın ya da kullanıcı arayüzü dilini değiştirin.",
|
||||
"labelLanguage": "Dil",
|
||||
"labelTheme": "Tema modu",
|
||||
"title": "Görünüm"
|
||||
},
|
||||
"urlSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"selectDisabled": "Kapalı",
|
||||
"selectEnabled": "Etkin",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "Durum sayfası başarıyla silindi",
|
||||
"deleteFailed": "Durum sayfası silinemedi",
|
||||
"createSuccess": "Durum sayfası başarıyla oluşturuldu",
|
||||
"updateSuccess": "Durum sayfası başarıyla güncellendi",
|
||||
"generalSettings": "Genel ayarlar",
|
||||
"contents": "İçerikler",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "Bu izleyici için ayarlanmış bir bildirim yok. 'Yapılandır' düğmesine tıklayarak bir bildirim eklemeniz gerekiyor.",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "Çözümle",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "Olay çözümlenirken bir hata oluştu.",
|
||||
"failureResolveAll": "Tüm olayları çözümlerken bir hata oluştu.",
|
||||
"failureResolveMonitor": ""
|
||||
},
|
||||
"checkFormError": "Formda hatalar var. Lütfen kontrol edin.",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "1 saat",
|
||||
"oneDay": "1 gün",
|
||||
"oneWeek": "1 hafta"
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
},
|
||||
"title": "",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"settingsFailedToSave": "",
|
||||
"settingsStatsCleared": "",
|
||||
"settingsFailedToClearStats": "",
|
||||
"settingsFailedToAddDemoMonitors": "",
|
||||
"settingsMonitorsDeleted": "",
|
||||
"settingsFailedToDeleteMonitors": "",
|
||||
"starPromptTitle": "",
|
||||
@@ -86,7 +85,8 @@
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""]
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
@@ -331,7 +331,9 @@
|
||||
"uploadSuccess": "",
|
||||
"validationFailed": "",
|
||||
"noFileSelected": "",
|
||||
"fallbackPage": ""
|
||||
"fallbackPage": "",
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
},
|
||||
"DeleteAccountTitle": "",
|
||||
"DeleteAccountButton": "",
|
||||
@@ -537,6 +539,11 @@
|
||||
"errors": {
|
||||
"incorrect": ""
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -675,7 +682,10 @@
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
}
|
||||
},
|
||||
"testNotification": "",
|
||||
"dialogDeleteTitle": "",
|
||||
"dialogDeleteConfirm": ""
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "",
|
||||
@@ -690,7 +700,6 @@
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"sendTestNotifications": "",
|
||||
"testNotificationsDisabled": "",
|
||||
"selectAll": "",
|
||||
"showAdminLoginLink": "",
|
||||
"logsPage": {
|
||||
@@ -698,7 +707,8 @@
|
||||
"description": "",
|
||||
"tabs": {
|
||||
"queue": "",
|
||||
"logs": ""
|
||||
"logs": "",
|
||||
"diagnostics": ""
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
@@ -753,7 +763,10 @@
|
||||
"monitorActions": {
|
||||
"title": "",
|
||||
"import": "",
|
||||
"export": ""
|
||||
"export": "",
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"details": ""
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
@@ -785,7 +798,8 @@
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": ""
|
||||
"title": "",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
@@ -832,5 +846,124 @@
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "",
|
||||
"failureResolveAll": "",
|
||||
"failureResolveMonitor": ""
|
||||
},
|
||||
"checkFormError": "",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
},
|
||||
"title": "",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import { initializeServices } from "./src/config/services.js";
|
||||
import { initializeControllers } from "./src/config/controllers.js";
|
||||
import { createApp } from "./src/app.js";
|
||||
import { initShutdownListener } from "./shutdown.js";
|
||||
import logger from "./utils/logger.js";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
import SettingsService from "./src/service/system/settingsService.js";
|
||||
import AppSettings from "./src/db/models/AppSettings.js";
|
||||
|
||||
const SERVICE_NAME = "Server";
|
||||
|
||||
const startApp = async () => {
|
||||
// FE path
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const openApiSpec = JSON.parse(fs.readFileSync(path.join(__dirname, "openapi.json"), "utf8"));
|
||||
const frontendPath = path.join(__dirname, "public");
|
||||
// Create services
|
||||
const settingsService = new SettingsService(AppSettings);
|
||||
const appSettings = settingsService.loadSettings();
|
||||
|
||||
// Initialize services
|
||||
const services = await initializeServices(appSettings, settingsService);
|
||||
|
||||
// Initialize controllers
|
||||
const controllers = initializeControllers(services);
|
||||
|
||||
const app = createApp({
|
||||
services,
|
||||
controllers,
|
||||
appSettings,
|
||||
frontendPath,
|
||||
openApiSpec,
|
||||
});
|
||||
|
||||
const port = appSettings.port || 52345;
|
||||
const server = app.listen(port, () => {
|
||||
logger.info({ message: `Server started on port:${port}` });
|
||||
});
|
||||
|
||||
initShutdownListener(server, services);
|
||||
};
|
||||
|
||||
startApp().catch((error) => {
|
||||
logger.error({
|
||||
message: error.message,
|
||||
service: SERVICE_NAME,
|
||||
method: "startApp",
|
||||
stack: error.stack,
|
||||
});
|
||||
process.exit(1);
|
||||
});
|
||||
44
server/package-lock.json
generated
@@ -13,11 +13,12 @@
|
||||
"axios": "^1.7.2",
|
||||
"bcryptjs": "3.0.2",
|
||||
"bullmq": "5.41.2",
|
||||
"compression": "1.8.0",
|
||||
"compression": "1.8.1",
|
||||
"cors": "^2.8.5",
|
||||
"dockerode": "4.0.6",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"express-rate-limit": "8.0.1",
|
||||
"handlebars": "^4.7.8",
|
||||
"helmet": "^8.0.0",
|
||||
"ioredis": "^5.4.2",
|
||||
@@ -2022,16 +2023,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/compression": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
|
||||
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
|
||||
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"compressible": "~2.0.18",
|
||||
"debug": "2.6.9",
|
||||
"negotiator": "~0.6.4",
|
||||
"on-headers": "~1.0.2",
|
||||
"on-headers": "~1.1.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
@@ -3159,6 +3160,24 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express-rate-limit": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.0.1.tgz",
|
||||
"integrity": "sha512-aZVCnybn7TVmxO4BtlmnvX+nuz8qHW124KKJ8dumsBsmv5ZLxE0pYu7S2nwyRBGHHCAzdmnGyrc5U/rksSPO7Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ip-address": "10.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/express-rate-limit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": ">= 4.11"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
@@ -3901,6 +3920,15 @@
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ip-address": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
|
||||
"integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
@@ -5705,9 +5733,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
|
||||
@@ -20,11 +20,12 @@
|
||||
"axios": "^1.7.2",
|
||||
"bcryptjs": "3.0.2",
|
||||
"bullmq": "5.41.2",
|
||||
"compression": "1.8.0",
|
||||
"compression": "1.8.1",
|
||||
"cors": "^2.8.5",
|
||||
"dockerode": "4.0.6",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"express-rate-limit": "8.0.1",
|
||||
"handlebars": "^4.7.8",
|
||||
"helmet": "^8.0.0",
|
||||
"ioredis": "^5.4.2",
|
||||
|
||||
@@ -8,11 +8,13 @@ import languageMiddleware from "./middleware/languageMiddleware.js";
|
||||
import swaggerUi from "swagger-ui-express";
|
||||
import { handleErrors } from "./middleware/handleErrors.js";
|
||||
import { setupRoutes } from "./config/routes.js";
|
||||
export const createApp = ({ services, controllers, appSettings, frontendPath, openApiSpec }) => {
|
||||
const allowedOrigin = appSettings.clientHost;
|
||||
import { generalApiLimiter } from "./middleware/rateLimiter.js";
|
||||
|
||||
export const createApp = ({ services, controllers, envSettings, frontendPath, openApiSpec }) => {
|
||||
const allowedOrigin = envSettings.clientHost;
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(generalApiLimiter);
|
||||
// Static files
|
||||
app.use(express.static(frontendPath));
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { verifyJWT } from "../middleware/verifyJWT.js";
|
||||
import { authApiLimiter } from "../middleware/rateLimiter.js";
|
||||
|
||||
import AuthRoutes from "../routes/authRoute.js";
|
||||
import InviteRoutes from "../routes/inviteRoute.js";
|
||||
@@ -25,7 +26,7 @@ export const setupRoutes = (app, controllers) => {
|
||||
const notificationRoutes = new NotificationRoutes(controllers.notificationController);
|
||||
const diagnosticRoutes = new DiagnosticRoutes(controllers.diagnosticController);
|
||||
|
||||
app.use("/api/v1/auth", authRoutes.getRouter());
|
||||
app.use("/api/v1/auth", authApiLimiter, authRoutes.getRouter());
|
||||
app.use("/api/v1/monitors", verifyJWT, monitorRoutes.getRouter());
|
||||
app.use("/api/v1/settings", verifyJWT, settingsRoutes.getRouter());
|
||||
app.use("/api/v1/checks", verifyJWT, checkRoutes.getRouter());
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import ServiceRegistry from "../service/system/serviceRegistry.js";
|
||||
import logger from "../utils/logger.js";
|
||||
import TranslationService from "../service/system/translationService.js";
|
||||
import StringService from "../service/system/stringService.js";
|
||||
import MongoDB from "../db/mongo/MongoDB.js";
|
||||
@@ -9,7 +8,6 @@ import BufferService from "../service/infrastructure/bufferService.js";
|
||||
import StatusService from "../service/infrastructure/statusService.js";
|
||||
import NotificationUtils from "../service/infrastructure/notificationUtils.js";
|
||||
import NotificationService from "../service/infrastructure/notificationService.js";
|
||||
import RedisService from "../service/data/redisService.js";
|
||||
import ErrorService from "../service/infrastructure/errorService.js";
|
||||
import SuperSimpleQueueHelper from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js";
|
||||
import SuperSimpleQueue from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js";
|
||||
@@ -19,7 +17,6 @@ import DiagnosticService from "../service/business/diagnosticService.js";
|
||||
import InviteService from "../service/business/inviteService.js";
|
||||
import MaintenanceWindowService from "../service/business/maintenanceWindowService.js";
|
||||
import MonitorService from "../service/business/monitorService.js";
|
||||
import IORedis from "ioredis";
|
||||
import papaparse from "papaparse";
|
||||
import axios from "axios";
|
||||
import ping from "ping";
|
||||
@@ -35,19 +32,101 @@ import mjml2html from "mjml";
|
||||
import jwt from "jsonwebtoken";
|
||||
import crypto from "crypto";
|
||||
|
||||
export const initializeServices = async (appSettings, settingsService) => {
|
||||
import { fileURLToPath } from "url";
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
// DB Modules
|
||||
import { NormalizeData, NormalizeDataUptimeDetails } from "../utils/dataUtils.js";
|
||||
import { GenerateAvatarImage } from "../utils/imageProcessing.js";
|
||||
import { ParseBoolean } from "../utils/utils.js";
|
||||
|
||||
// Models
|
||||
import Check from "../db/models/Check.js";
|
||||
import HardwareCheck from "../db/models/HardwareCheck.js";
|
||||
import PageSpeedCheck from "../db/models/PageSpeedCheck.js";
|
||||
import Monitor from "../db/models/Monitor.js";
|
||||
import User from "../db/models/User.js";
|
||||
import InviteToken from "../db/models/InviteToken.js";
|
||||
import StatusPage from "../db/models/StatusPage.js";
|
||||
import Team from "../db/models/Team.js";
|
||||
import MaintenanceWindow from "../db/models/MaintenanceWindow.js";
|
||||
import MonitorStats from "../db/models/MonitorStats.js";
|
||||
import NetworkCheck from "../db/models/NetworkCheck.js";
|
||||
import Notification from "../db/models/Notification.js";
|
||||
import RecoveryToken from "../db/models/RecoveryToken.js";
|
||||
import AppSettings from "../db/models/AppSettings.js";
|
||||
|
||||
import InviteModule from "../db/mongo/modules/inviteModule.js";
|
||||
import CheckModule from "../db/mongo/modules/checkModule.js";
|
||||
import StatusPageModule from "../db/mongo/modules/statusPageModule.js";
|
||||
import UserModule from "../db/mongo/modules/userModule.js";
|
||||
import HardwareCheckModule from "../db/mongo/modules/hardwareCheckModule.js";
|
||||
import MaintenanceWindowModule from "../db/mongo/modules/maintenanceWindowModule.js";
|
||||
import MonitorModule from "../db/mongo/modules/monitorModule.js";
|
||||
import NetworkCheckModule from "../db/mongo/modules/networkCheckModule.js";
|
||||
import NotificationModule from "../db/mongo/modules/notificationModule.js";
|
||||
import PageSpeedCheckModule from "../db/mongo/modules/pageSpeedCheckModule.js";
|
||||
import RecoveryModule from "../db/mongo/modules/recoveryModule.js";
|
||||
import SettingsModule from "../db/mongo/modules/settingsModule.js";
|
||||
|
||||
export const initializeServices = async ({ logger, envSettings, settingsService }) => {
|
||||
const serviceRegistry = new ServiceRegistry({ logger });
|
||||
ServiceRegistry.instance = serviceRegistry;
|
||||
|
||||
const translationService = new TranslationService(logger);
|
||||
await translationService.initialize();
|
||||
|
||||
const stringService = new StringService(translationService);
|
||||
|
||||
// Create DB
|
||||
const db = new MongoDB({ appSettings });
|
||||
const checkModule = new CheckModule({ logger, Check, HardwareCheck, PageSpeedCheck, Monitor, User });
|
||||
const inviteModule = new InviteModule({ InviteToken, crypto, stringService });
|
||||
const statusPageModule = new StatusPageModule({ StatusPage, NormalizeData, stringService });
|
||||
const userModule = new UserModule({ User, Team, GenerateAvatarImage, ParseBoolean, stringService });
|
||||
const hardwareCheckModule = new HardwareCheckModule({ HardwareCheck, Monitor, logger });
|
||||
const maintenanceWindowModule = new MaintenanceWindowModule({ MaintenanceWindow });
|
||||
const monitorModule = new MonitorModule({
|
||||
Monitor,
|
||||
MonitorStats,
|
||||
Check,
|
||||
PageSpeedCheck,
|
||||
HardwareCheck,
|
||||
stringService,
|
||||
fs,
|
||||
path,
|
||||
fileURLToPath,
|
||||
ObjectId,
|
||||
NormalizeData,
|
||||
NormalizeDataUptimeDetails,
|
||||
});
|
||||
const networkCheckModule = new NetworkCheckModule({ NetworkCheck });
|
||||
const notificationModule = new NotificationModule({ Notification, Monitor });
|
||||
const pageSpeedCheckModule = new PageSpeedCheckModule({ PageSpeedCheck });
|
||||
const recoveryModule = new RecoveryModule({ User, RecoveryToken, crypto, stringService });
|
||||
const settingsModule = new SettingsModule({ AppSettings });
|
||||
|
||||
const db = new MongoDB({
|
||||
logger,
|
||||
envSettings,
|
||||
checkModule,
|
||||
inviteModule,
|
||||
statusPageModule,
|
||||
userModule,
|
||||
hardwareCheckModule,
|
||||
maintenanceWindowModule,
|
||||
monitorModule,
|
||||
networkCheckModule,
|
||||
notificationModule,
|
||||
pageSpeedCheckModule,
|
||||
recoveryModule,
|
||||
settingsModule,
|
||||
});
|
||||
|
||||
await db.connect();
|
||||
|
||||
const networkService = new NetworkService(axios, ping, logger, http, Docker, net, stringService, settingsService);
|
||||
const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger);
|
||||
const bufferService = new BufferService({ db, logger });
|
||||
const bufferService = new BufferService({ db, logger, envSettings });
|
||||
const statusService = new StatusService({ db, logger, buffer: bufferService });
|
||||
|
||||
const notificationUtils = new NotificationUtils({
|
||||
@@ -64,7 +143,6 @@ export const initializeServices = async (appSettings, settingsService) => {
|
||||
notificationUtils,
|
||||
});
|
||||
|
||||
const redisService = new RedisService({ Redis: IORedis, logger });
|
||||
const errorService = new ErrorService();
|
||||
|
||||
const superSimpleQueueHelper = new SuperSimpleQueueHelper({
|
||||
@@ -76,7 +154,7 @@ export const initializeServices = async (appSettings, settingsService) => {
|
||||
});
|
||||
|
||||
const superSimpleQueue = await SuperSimpleQueue.create({
|
||||
appSettings,
|
||||
envSettings,
|
||||
db,
|
||||
logger,
|
||||
helper: superSimpleQueueHelper,
|
||||
@@ -135,7 +213,6 @@ export const initializeServices = async (appSettings, settingsService) => {
|
||||
bufferService,
|
||||
statusService,
|
||||
notificationService,
|
||||
redisService,
|
||||
jobQueue: superSimpleQueue,
|
||||
userService,
|
||||
checkService,
|
||||
|
||||
@@ -32,7 +32,7 @@ class MonitorController extends BaseController {
|
||||
}
|
||||
|
||||
async verifyTeamAccess(teamId, monitorId) {
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
const monitor = await this.db.monitorModule.getMonitorById(monitorId);
|
||||
if (!monitor.teamId.equals(teamId)) {
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
@@ -150,7 +150,7 @@ class MonitorController extends BaseController {
|
||||
await getCertificateParamValidation.validateAsync(req.params);
|
||||
|
||||
const { monitorId } = req.params;
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
const monitor = await this.db.monitorModule.getMonitorById(monitorId);
|
||||
const certificate = await fetchMonitorCertificate(sslChecker, monitor);
|
||||
|
||||
return res.success({
|
||||
|
||||
@@ -53,7 +53,7 @@ class NotificationController extends BaseController {
|
||||
body.userId = userId;
|
||||
body.teamId = teamId;
|
||||
|
||||
const notification = await this.db.createNotification(body);
|
||||
const notification = await this.db.notificationModule.createNotification(body);
|
||||
return res.success({
|
||||
msg: "Notification created successfully",
|
||||
data: notification,
|
||||
@@ -70,7 +70,7 @@ class NotificationController extends BaseController {
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const notifications = await this.db.getNotificationsByTeamId(teamId);
|
||||
const notifications = await this.db.notificationModule.getNotificationsByTeamId(teamId);
|
||||
|
||||
return res.success({
|
||||
msg: "Notifications fetched successfully",
|
||||
@@ -88,12 +88,12 @@ class NotificationController extends BaseController {
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const notification = await this.db.getNotificationById(req.params.id);
|
||||
const notification = await this.db.notificationModule.getNotificationById(req.params.id);
|
||||
if (!notification.teamId.equals(teamId)) {
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
|
||||
await this.db.deleteNotificationById(req.params.id);
|
||||
await this.db.notificationModule.deleteNotificationById(req.params.id);
|
||||
return res.success({
|
||||
msg: "Notification deleted successfully",
|
||||
});
|
||||
@@ -104,7 +104,7 @@ class NotificationController extends BaseController {
|
||||
|
||||
getNotificationById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const notification = await this.db.getNotificationById(req.params.id);
|
||||
const notification = await this.db.notificationModule.getNotificationById(req.params.id);
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
@@ -134,13 +134,13 @@ class NotificationController extends BaseController {
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const notification = await this.db.getNotificationById(req.params.id);
|
||||
const notification = await this.db.notificationModule.getNotificationById(req.params.id);
|
||||
|
||||
if (!notification.teamId.equals(teamId)) {
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
|
||||
const editedNotification = await this.db.editNotification(req.params.id, req.body);
|
||||
const editedNotification = await this.db.notificationModule.editNotification(req.params.id, req.body);
|
||||
return res.success({
|
||||
msg: "Notification updated successfully",
|
||||
data: editedNotification,
|
||||
@@ -158,7 +158,7 @@ class NotificationController extends BaseController {
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
const monitor = await this.db.monitorModule.getMonitorById(monitorId);
|
||||
|
||||
if (!monitor.teamId.equals(teamId)) {
|
||||
throw this.errorService.createAuthorizationError();
|
||||
|
||||
@@ -55,7 +55,7 @@ class SettingsController extends BaseController {
|
||||
async (req, res) => {
|
||||
await updateAppSettingsBodyValidation.validateAsync(req.body);
|
||||
|
||||
const updatedSettings = await this.db.updateAppSettings(req.body);
|
||||
const updatedSettings = await this.db.settingsModule.updateAppSettings(req.body);
|
||||
const returnSettings = this.buildAppSettings(updatedSettings);
|
||||
return res.success({
|
||||
msg: this.stringService.updateAppSettings,
|
||||
|
||||
@@ -19,7 +19,7 @@ class StatusPageController extends BaseController {
|
||||
await imageValidation.validateAsync(req.file);
|
||||
|
||||
const { _id, teamId } = req.user;
|
||||
const statusPage = await this.db.createStatusPage({
|
||||
const statusPage = await this.db.statusPageModule.createStatusPage({
|
||||
statusPageData: req.body,
|
||||
image: req.file,
|
||||
userId: _id,
|
||||
@@ -39,7 +39,7 @@ class StatusPageController extends BaseController {
|
||||
await createStatusPageBodyValidation.validateAsync(req.body);
|
||||
await imageValidation.validateAsync(req.file);
|
||||
|
||||
const statusPage = await this.db.updateStatusPage(req.body, req.file);
|
||||
const statusPage = await this.db.statusPageModule.updateStatusPage(req.body, req.file);
|
||||
if (statusPage === null) {
|
||||
throw this.errorService.createNotFoundError(this.stringService.statusPageNotFound);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ class StatusPageController extends BaseController {
|
||||
|
||||
getStatusPage = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const statusPage = await this.db.getStatusPage();
|
||||
const statusPage = await this.db.statusPageModule.getStatusPage();
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageByUrl,
|
||||
data: statusPage,
|
||||
@@ -69,7 +69,7 @@ class StatusPageController extends BaseController {
|
||||
await getStatusPageParamValidation.validateAsync(req.params);
|
||||
await getStatusPageQueryValidation.validateAsync(req.query);
|
||||
|
||||
const statusPage = await this.db.getStatusPageByUrl(req.params.url, req.query.type);
|
||||
const statusPage = await this.db.statusPageModule.getStatusPageByUrl(req.params.url);
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageByUrl,
|
||||
data: statusPage,
|
||||
@@ -82,7 +82,7 @@ class StatusPageController extends BaseController {
|
||||
getStatusPagesByTeamId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const teamId = req.user.teamId;
|
||||
const statusPages = await this.db.getStatusPagesByTeamId(teamId);
|
||||
const statusPages = await this.db.statusPageModule.getStatusPagesByTeamId(teamId);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageByTeamId,
|
||||
|
||||
@@ -65,6 +65,12 @@ const AppSettingsSchema = mongoose.Schema(
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
globalThresholds: {
|
||||
cpu: { type: Number },
|
||||
memory: { type: Number },
|
||||
disk: { type: Number },
|
||||
temperature: { type: Number },
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -74,6 +74,10 @@ const StatusPageSchema = mongoose.Schema(
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
customCSS: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
@@ -103,12 +103,4 @@ UserSchema.methods.comparePassword = async function (submittedPassword) {
|
||||
|
||||
const User = mongoose.model("User", UserSchema);
|
||||
|
||||
User.init().then(() => {
|
||||
logger.info({
|
||||
message: "User model initialized",
|
||||
service: "UserModel",
|
||||
method: "init",
|
||||
});
|
||||
});
|
||||
|
||||
export default User;
|
||||
|
||||
@@ -1,89 +1,39 @@
|
||||
import mongoose from "mongoose";
|
||||
import AppSettings from "../models/AppSettings.js";
|
||||
import logger from "../../utils/logger.js";
|
||||
|
||||
//****************************************
|
||||
// User Operations
|
||||
//****************************************
|
||||
|
||||
import * as userModule from "./modules/userModule.js";
|
||||
|
||||
//****************************************
|
||||
// Invite Token Operations
|
||||
//****************************************
|
||||
|
||||
import * as inviteModule from "./modules/inviteModule.js";
|
||||
|
||||
//****************************************
|
||||
// Recovery Operations
|
||||
//****************************************
|
||||
import * as recoveryModule from "./modules/recoveryModule.js";
|
||||
|
||||
//****************************************
|
||||
// Monitors
|
||||
//****************************************
|
||||
|
||||
import * as monitorModule from "./modules/monitorModule.js";
|
||||
|
||||
//****************************************
|
||||
// Page Speed Checks
|
||||
//****************************************
|
||||
|
||||
import * as pageSpeedCheckModule from "./modules/pageSpeedCheckModule.js";
|
||||
|
||||
//****************************************
|
||||
// Hardware Checks
|
||||
//****************************************
|
||||
import * as hardwareCheckModule from "./modules/hardwareCheckModule.js";
|
||||
|
||||
//****************************************
|
||||
// Checks
|
||||
//****************************************
|
||||
|
||||
import * as checkModule from "./modules/checkModule.js";
|
||||
|
||||
//****************************************
|
||||
// Maintenance Window
|
||||
//****************************************
|
||||
import * as maintenanceWindowModule from "./modules/maintenanceWindowModule.js";
|
||||
|
||||
//****************************************
|
||||
// Notifications
|
||||
//****************************************
|
||||
import * as notificationModule from "./modules/notificationModule.js";
|
||||
|
||||
//****************************************
|
||||
// AppSettings
|
||||
//****************************************
|
||||
import * as settingsModule from "./modules/settingsModule.js";
|
||||
|
||||
//****************************************
|
||||
// Status Page
|
||||
//****************************************
|
||||
import * as statusPageModule from "./modules/statusPageModule.js";
|
||||
|
||||
//****************************************
|
||||
// Diagnostic
|
||||
//****************************************
|
||||
import * as diagnosticModule from "./modules/diagnosticModule.js";
|
||||
|
||||
class MongoDB {
|
||||
static SERVICE_NAME = "MongoDB";
|
||||
|
||||
constructor({ appSettings }) {
|
||||
this.appSettings = appSettings;
|
||||
Object.assign(this, userModule);
|
||||
Object.assign(this, inviteModule);
|
||||
Object.assign(this, recoveryModule);
|
||||
Object.assign(this, monitorModule);
|
||||
Object.assign(this, pageSpeedCheckModule);
|
||||
Object.assign(this, hardwareCheckModule);
|
||||
Object.assign(this, checkModule);
|
||||
Object.assign(this, maintenanceWindowModule);
|
||||
Object.assign(this, notificationModule);
|
||||
Object.assign(this, settingsModule);
|
||||
Object.assign(this, statusPageModule);
|
||||
Object.assign(this, diagnosticModule);
|
||||
constructor({
|
||||
logger,
|
||||
envSettings,
|
||||
checkModule,
|
||||
inviteModule,
|
||||
statusPageModule,
|
||||
userModule,
|
||||
hardwareCheckModule,
|
||||
maintenanceWindowModule,
|
||||
monitorModule,
|
||||
networkCheckModule,
|
||||
notificationModule,
|
||||
pageSpeedCheckModule,
|
||||
recoveryModule,
|
||||
settingsModule,
|
||||
}) {
|
||||
this.logger = logger;
|
||||
this.envSettings = envSettings;
|
||||
this.userModule = userModule;
|
||||
this.inviteModule = inviteModule;
|
||||
this.recoveryModule = recoveryModule;
|
||||
this.pageSpeedCheckModule = pageSpeedCheckModule;
|
||||
this.hardwareCheckModule = hardwareCheckModule;
|
||||
this.checkModule = checkModule;
|
||||
this.maintenanceWindowModule = maintenanceWindowModule;
|
||||
this.monitorModule = monitorModule;
|
||||
this.notificationModule = notificationModule;
|
||||
this.settingsModule = settingsModule;
|
||||
this.statusPageModule = statusPageModule;
|
||||
this.networkCheckModule = networkCheckModule;
|
||||
}
|
||||
|
||||
get serviceName() {
|
||||
@@ -92,9 +42,9 @@ class MongoDB {
|
||||
|
||||
connect = async () => {
|
||||
try {
|
||||
const connectionString = this.appSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db";
|
||||
const connectionString = this.envSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db";
|
||||
await mongoose.connect(connectionString);
|
||||
// If there are no AppSettings, create one
|
||||
// If there are no AppSettings, create one // TODO why is this here?
|
||||
await AppSettings.findOneAndUpdate(
|
||||
{}, // empty filter to match any document
|
||||
{}, // empty update
|
||||
@@ -110,13 +60,13 @@ class MongoDB {
|
||||
await model.syncIndexes();
|
||||
}
|
||||
|
||||
logger.info({
|
||||
this.logger.info({
|
||||
message: "Connected to MongoDB",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "connect",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: this.SERVICE_NAME,
|
||||
method: "connect",
|
||||
@@ -128,12 +78,12 @@ class MongoDB {
|
||||
|
||||
disconnect = async () => {
|
||||
try {
|
||||
logger.info({ message: "Disconnecting from MongoDB" });
|
||||
this.logger.info({ message: "Disconnecting from MongoDB" });
|
||||
await mongoose.disconnect();
|
||||
logger.info({ message: "Disconnected from MongoDB" });
|
||||
this.logger.info({ message: "Disconnected from MongoDB" });
|
||||
return;
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: this.SERVICE_NAME,
|
||||
method: "disconnect",
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
import Check from "../../models/Check.js";
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
import HardwareCheck from "../../models/HardwareCheck.js";
|
||||
import PageSpeedCheck from "../../models/PageSpeedCheck.js";
|
||||
import User from "../../models/User.js";
|
||||
import logger from "../../../utils/logger.js";
|
||||
import { ObjectId } from "mongodb";
|
||||
import { buildChecksSummaryByTeamIdPipeline } from "./checkModuleQueries.js";
|
||||
|
||||
@@ -17,369 +11,296 @@ const dateRangeLookup = {
|
||||
all: undefined,
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a check for a monitor
|
||||
* @async
|
||||
* @param {Object} checkData
|
||||
* @param {string} checkData.monitorId
|
||||
* @param {boolean} checkData.status
|
||||
* @param {number} checkData.responseTime
|
||||
* @param {number} checkData.statusCode
|
||||
* @param {string} checkData.message
|
||||
* @returns {Promise<Check>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
|
||||
const createCheck = async (checkData) => {
|
||||
try {
|
||||
const check = await new Check({ ...checkData }).save();
|
||||
return check;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createCheck";
|
||||
throw error;
|
||||
class CheckModule {
|
||||
constructor({ logger, Check, HardwareCheck, PageSpeedCheck, Monitor, User }) {
|
||||
this.logger = logger;
|
||||
this.Check = Check;
|
||||
this.HardwareCheck = HardwareCheck;
|
||||
this.PageSpeedCheck = PageSpeedCheck;
|
||||
this.Monitor = Monitor;
|
||||
this.User = User;
|
||||
}
|
||||
};
|
||||
|
||||
const createChecks = async (checks) => {
|
||||
try {
|
||||
await Check.insertMany(checks, { ordered: false });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all checks for a monitor
|
||||
* @async
|
||||
* @param {string} monitorId
|
||||
* @returns {Promise<Array<Check>>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getChecksByMonitor = async ({ monitorId, type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => {
|
||||
try {
|
||||
status = status === "true" ? true : status === "false" ? false : undefined;
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
|
||||
const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] };
|
||||
|
||||
// Match
|
||||
const matchStage = {
|
||||
monitorId: new ObjectId(monitorId),
|
||||
...(typeof status !== "undefined" && { status }),
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
createdAt: {
|
||||
$gte: dateRangeLookup[dateRange],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
if (filter !== undefined) {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "down":
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
logger.warn({
|
||||
message: "invalid filter",
|
||||
service: SERVICE_NAME,
|
||||
method: "getChecks",
|
||||
});
|
||||
break;
|
||||
}
|
||||
createChecks = async (checks) => {
|
||||
try {
|
||||
await this.Check.insertMany(checks, { ordered: false });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
//Sort
|
||||
sortOrder = sortOrder === "asc" ? 1 : -1;
|
||||
getChecksByMonitor = async ({ monitorId, type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => {
|
||||
try {
|
||||
status = status === "true" ? true : status === "false" ? false : undefined;
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
|
||||
// Pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] };
|
||||
|
||||
const checkModels = {
|
||||
http: Check,
|
||||
ping: Check,
|
||||
docker: Check,
|
||||
port: Check,
|
||||
pagespeed: PageSpeedCheck,
|
||||
hardware: HardwareCheck,
|
||||
};
|
||||
|
||||
const Model = checkModels[type];
|
||||
|
||||
const checks = await Model.aggregate([
|
||||
{ $match: matchStage },
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
$facet: {
|
||||
summary: [{ $count: "checksCount" }],
|
||||
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
checksCount: {
|
||||
$ifNull: [{ $arrayElemAt: ["$summary.checksCount", 0] }, 0],
|
||||
// Match
|
||||
const matchStage = {
|
||||
monitorId: new ObjectId(monitorId),
|
||||
...(typeof status !== "undefined" && { status }),
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
createdAt: {
|
||||
$gte: dateRangeLookup[dateRange],
|
||||
},
|
||||
checks: {
|
||||
$ifNull: ["$checks", []],
|
||||
}),
|
||||
};
|
||||
|
||||
if (filter !== undefined) {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "down":
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
this.logger.warn({
|
||||
message: "invalid filter",
|
||||
service: SERVICE_NAME,
|
||||
method: "getChecks",
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Sort
|
||||
sortOrder = sortOrder === "asc" ? 1 : -1;
|
||||
|
||||
// Pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
|
||||
const checkModels = {
|
||||
http: this.Check,
|
||||
ping: this.Check,
|
||||
docker: this.Check,
|
||||
port: this.Check,
|
||||
pagespeed: this.PageSpeedCheck,
|
||||
hardware: this.HardwareCheck,
|
||||
};
|
||||
|
||||
const Model = checkModels[type];
|
||||
|
||||
const checks = await Model.aggregate([
|
||||
{ $match: matchStage },
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
$facet: {
|
||||
summary: [{ $count: "checksCount" }],
|
||||
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
return checks[0];
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getChecksByTeam = async ({ sortOrder, dateRange, filter, ack, page, rowsPerPage, teamId }) => {
|
||||
try {
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
|
||||
const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] };
|
||||
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
status: false,
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
createdAt: {
|
||||
$gte: dateRangeLookup[dateRange],
|
||||
{
|
||||
$project: {
|
||||
checksCount: {
|
||||
$ifNull: [{ $arrayElemAt: ["$summary.checksCount", 0] }, 0],
|
||||
},
|
||||
checks: {
|
||||
$ifNull: ["$checks", []],
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
// Add filter to match stage
|
||||
if (filter !== undefined) {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "down":
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
logger.warn({
|
||||
message: "invalid filter",
|
||||
service: SERVICE_NAME,
|
||||
method: "getChecksByTeam",
|
||||
});
|
||||
break;
|
||||
]);
|
||||
return checks[0];
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
getChecksByTeam = async ({ sortOrder, dateRange, filter, ack, page, rowsPerPage, teamId }) => {
|
||||
try {
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
|
||||
const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] };
|
||||
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
status: false,
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
createdAt: {
|
||||
$gte: dateRangeLookup[dateRange],
|
||||
},
|
||||
}),
|
||||
};
|
||||
// Add filter to match stage
|
||||
if (filter !== undefined) {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "down":
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
this.logger.warn({
|
||||
message: "invalid filter",
|
||||
service: SERVICE_NAME,
|
||||
method: "getChecksByTeam",
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sortOrder = sortOrder === "asc" ? 1 : -1;
|
||||
|
||||
// pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
|
||||
const aggregatePipeline = [
|
||||
{ $match: matchStage },
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "hardwarechecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "pagespeedchecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
$facet: {
|
||||
summary: [{ $count: "checksCount" }],
|
||||
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
checksCount: { $arrayElemAt: ["$summary.checksCount", 0] },
|
||||
checks: "$checks",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const checks = await this.Check.aggregate(aggregatePipeline);
|
||||
return checks[0];
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecksByTeam";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
ackCheck = async (checkId, teamId, ack) => {
|
||||
try {
|
||||
const updatedCheck = await this.Check.findOneAndUpdate({ _id: checkId, teamId: teamId }, { $set: { ack, ackAt: new Date() } }, { new: true });
|
||||
|
||||
if (!updatedCheck) {
|
||||
throw new Error("Check not found");
|
||||
}
|
||||
|
||||
return updatedCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "ackCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
ackAllChecks = async (monitorId, teamId, ack, path) => {
|
||||
try {
|
||||
const updatedChecks = await this.Check.updateMany(path === "monitor" ? { monitorId } : { teamId }, { $set: { ack, ackAt: new Date() } });
|
||||
return updatedChecks.modifiedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "ackAllChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
getChecksSummaryByTeamId = async ({ teamId }) => {
|
||||
try {
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
};
|
||||
const checks = await this.Check.aggregate(buildChecksSummaryByTeamIdPipeline({ matchStage }));
|
||||
return checks[0].summary;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecksSummaryByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
deleteChecks = async (monitorId) => {
|
||||
try {
|
||||
const result = await this.Check.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
deleteChecksByTeamId = async (teamId) => {
|
||||
try {
|
||||
// Find all monitor IDs for this team (only get _id field for efficiency)
|
||||
const teamMonitors = await this.Monitor.find({ teamId }, { _id: 1 });
|
||||
const monitorIds = teamMonitors.map((monitor) => monitor._id);
|
||||
|
||||
// Delete all checks for these monitors in one operation
|
||||
const deleteResult = await this.Check.deleteMany({ monitorId: { $in: monitorIds } });
|
||||
|
||||
return deleteResult.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteChecksByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateChecksTTL = async (teamId, ttl) => {
|
||||
try {
|
||||
await this.Check.collection.dropIndex("expiry_1");
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: SERVICE_NAME,
|
||||
method: "updateChecksTTL",
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
|
||||
sortOrder = sortOrder === "asc" ? 1 : -1;
|
||||
|
||||
// pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
try {
|
||||
await this.Check.collection.createIndex(
|
||||
{ expiry: 1 },
|
||||
{ expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary
|
||||
);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateChecksTTL";
|
||||
throw error;
|
||||
}
|
||||
|
||||
const aggregatePipeline = [
|
||||
{ $match: matchStage },
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "hardwarechecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "pagespeedchecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
$facet: {
|
||||
summary: [{ $count: "checksCount" }],
|
||||
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
checksCount: { $arrayElemAt: ["$summary.checksCount", 0] },
|
||||
checks: "$checks",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const checks = await Check.aggregate(aggregatePipeline);
|
||||
return checks[0];
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecksByTeam";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the acknowledgment status of a check
|
||||
* @async
|
||||
* @param {string} checkId - The ID of the check to update
|
||||
* @param {string} teamId - The ID of the team
|
||||
* @param {boolean} ack - The acknowledgment status to set
|
||||
* @returns {Promise<Check>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const ackCheck = async (checkId, teamId, ack) => {
|
||||
try {
|
||||
const updatedCheck = await Check.findOneAndUpdate({ _id: checkId, teamId: teamId }, { $set: { ack, ackAt: new Date() } }, { new: true });
|
||||
|
||||
if (!updatedCheck) {
|
||||
throw new Error("Check not found");
|
||||
// Update user
|
||||
try {
|
||||
await this.User.updateMany({ teamId: teamId }, { checkTTL: ttl });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateChecksTTL";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return updatedCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "ackCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the acknowledgment status of all checks for a monitor or team
|
||||
* @async
|
||||
* @param {string} id - The monitor ID or team ID
|
||||
* @param {boolean} ack - The acknowledgment status to set
|
||||
* @param {string} path - The path type ('monitor' or 'team')
|
||||
* @returns {Promise<number>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const ackAllChecks = async (monitorId, teamId, ack, path) => {
|
||||
try {
|
||||
const updatedChecks = await Check.updateMany(path === "monitor" ? { monitorId } : { teamId }, { $set: { ack, ackAt: new Date() } });
|
||||
return updatedChecks.modifiedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "ackAllChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get checks and summary by team ID
|
||||
* @async
|
||||
* @param {string} teamId
|
||||
* @returns {Promise<Object>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getChecksSummaryByTeamId = async ({ teamId }) => {
|
||||
try {
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
};
|
||||
const checks = await Check.aggregate(buildChecksSummaryByTeamIdPipeline({ matchStage }));
|
||||
return checks[0].summary;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecksSummaryByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete all checks for a monitor
|
||||
* @async
|
||||
* @param {string} monitorId
|
||||
* @returns {number}
|
||||
* @throws {Error}
|
||||
*/
|
||||
|
||||
const deleteChecks = async (monitorId) => {
|
||||
try {
|
||||
const result = await Check.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete all checks for a team
|
||||
* @async
|
||||
* @param {string} monitorId
|
||||
* @returns {number}
|
||||
* @throws {Error}
|
||||
*/
|
||||
|
||||
const deleteChecksByTeamId = async (teamId) => {
|
||||
try {
|
||||
// Find all monitor IDs for this team (only get _id field for efficiency)
|
||||
const teamMonitors = await Monitor.find({ teamId }, { _id: 1 });
|
||||
const monitorIds = teamMonitors.map((monitor) => monitor._id);
|
||||
|
||||
// Delete all checks for these monitors in one operation
|
||||
const deleteResult = await Check.deleteMany({ monitorId: { $in: monitorIds } });
|
||||
|
||||
return deleteResult.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteChecksByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const updateChecksTTL = async (teamId, ttl) => {
|
||||
try {
|
||||
await Check.collection.dropIndex("expiry_1");
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
message: error.message,
|
||||
service: SERVICE_NAME,
|
||||
method: "updateChecksTTL",
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await Check.collection.createIndex(
|
||||
{ expiry: 1 },
|
||||
{ expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary
|
||||
);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateChecksTTL";
|
||||
throw error;
|
||||
}
|
||||
// Update user
|
||||
try {
|
||||
await User.updateMany({ teamId: teamId }, { checkTTL: ttl });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateChecksTTL";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createCheck,
|
||||
createChecks,
|
||||
getChecksByMonitor,
|
||||
getChecksByTeam,
|
||||
ackCheck,
|
||||
ackAllChecks,
|
||||
getChecksSummaryByTeamId,
|
||||
deleteChecks,
|
||||
deleteChecksByTeamId,
|
||||
updateChecksTTL,
|
||||
};
|
||||
export default CheckModule;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
const SERVICE_NAME = "diagnosticModule";
|
||||
import { buildMonitorSummaryByTeamIdPipeline, buildMonitorsByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline } from "./monitorModuleQueries.js";
|
||||
|
||||
const getMonitorsByTeamIdExecutionStats = async (req) => {
|
||||
try {
|
||||
let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
|
||||
limit = parseInt(limit);
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
if (field === undefined) {
|
||||
field = "name";
|
||||
order = "asc";
|
||||
}
|
||||
// Build match stage
|
||||
const matchStage = { teamId: new ObjectId(req.params.teamId) };
|
||||
if (type !== undefined) {
|
||||
matchStage.type = Array.isArray(type) ? { $in: type } : type;
|
||||
}
|
||||
|
||||
const summary = await Monitor.aggregate(buildMonitorSummaryByTeamIdPipeline({ matchStage })).explain("executionStats");
|
||||
|
||||
const monitors = await Monitor.aggregate(buildMonitorsByTeamIdPipeline({ matchStage, field, order })).explain("executionStats");
|
||||
|
||||
const filteredMonitors = await Monitor.aggregate(
|
||||
buildFilteredMonitorsByTeamIdPipeline({
|
||||
matchStage,
|
||||
filter,
|
||||
page,
|
||||
rowsPerPage,
|
||||
field,
|
||||
order,
|
||||
limit,
|
||||
type,
|
||||
})
|
||||
).explain("executionStats");
|
||||
|
||||
return { summary, monitors, filteredMonitors };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMonitorSummaryByTeamIdExecutionStats";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { getMonitorsByTeamIdExecutionStats };
|
||||
@@ -1,70 +1,22 @@
|
||||
import HardwareCheck from "../../models/HardwareCheck.js";
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
import logger from "../../../utils/logger.js";
|
||||
|
||||
const SERVICE_NAME = "hardwareCheckModule";
|
||||
const createHardwareCheck = async (hardwareCheckData) => {
|
||||
try {
|
||||
const { monitorId, status } = hardwareCheckData;
|
||||
const n = (await HardwareCheck.countDocuments({ monitorId })) + 1;
|
||||
const monitor = await Monitor.findById(monitorId);
|
||||
|
||||
if (!monitor) {
|
||||
logger.error({
|
||||
message: "Monitor not found",
|
||||
service: SERVICE_NAME,
|
||||
method: "createHardwareCheck",
|
||||
details: `monitor ID: ${monitorId}`,
|
||||
});
|
||||
return null;
|
||||
class HardwareCheckModule {
|
||||
constructor({ HardwareCheck, Monitor, logger }) {
|
||||
this.HardwareCheck = HardwareCheck;
|
||||
this.Monitor = Monitor;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
createHardwareChecks = async (hardwareChecks) => {
|
||||
try {
|
||||
await this.HardwareCheck.insertMany(hardwareChecks, { ordered: false });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createHardwareChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let newUptimePercentage;
|
||||
if (monitor.uptimePercentage === undefined) {
|
||||
newUptimePercentage = status === true ? 1 : 0;
|
||||
} else {
|
||||
newUptimePercentage = (monitor.uptimePercentage * (n - 1) + (status === true ? 1 : 0)) / n;
|
||||
}
|
||||
|
||||
await Monitor.findOneAndUpdate({ _id: monitorId }, { uptimePercentage: newUptimePercentage });
|
||||
|
||||
const hardwareCheck = await new HardwareCheck({
|
||||
...hardwareCheckData,
|
||||
}).save();
|
||||
return hardwareCheck;
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
message: "Error creating hardware check",
|
||||
service: SERVICE_NAME,
|
||||
method: "createHardwareCheck",
|
||||
stack: error.stack,
|
||||
});
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createHardwareCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const createHardwareChecks = async (hardwareChecks) => {
|
||||
try {
|
||||
await HardwareCheck.insertMany(hardwareChecks, { ordered: false });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createHardwareChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteHardwareChecksByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await HardwareCheck.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteHardwareChecksByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { createHardwareCheck, createHardwareChecks, deleteHardwareChecksByMonitorId };
|
||||
export default HardwareCheckModule;
|
||||
|
||||
@@ -1,89 +1,56 @@
|
||||
import InviteToken from "../../models/InviteToken.js";
|
||||
import crypto from "crypto";
|
||||
import ServiceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
import StringService from "../../../service/system/stringService.js";
|
||||
|
||||
const SERVICE_NAME = "inviteModule";
|
||||
/**
|
||||
* Request an invite token for a user.
|
||||
*
|
||||
* This function deletes any existing invite tokens for the user's email,
|
||||
* generates a new token, saves it, and then returns the new token.
|
||||
*
|
||||
* @param {Object} userData - The user data.
|
||||
* @param {string} userData.email - The user's email.
|
||||
* @param {mongoose.Schema.Types.ObjectId} userData.teamId - The ID of the team.
|
||||
* @param {Array} userData.role - The user's role(s).
|
||||
* @param {Date} [userData.expiry=Date.now] - The expiry date of the token. Defaults to the current date and time.
|
||||
* @returns {Promise<InviteToken>} The invite token.
|
||||
* @throws {Error} If there is an error.
|
||||
*/
|
||||
const requestInviteToken = async (userData) => {
|
||||
try {
|
||||
await InviteToken.deleteMany({ email: userData.email });
|
||||
userData.token = crypto.randomBytes(32).toString("hex");
|
||||
let inviteToken = new InviteToken(userData);
|
||||
await inviteToken.save();
|
||||
return inviteToken;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "requestInviteToken";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves an invite token
|
||||
*
|
||||
* This function searches for an invite token in the database and deletes it.
|
||||
* If the invite token is not found, it throws an error.
|
||||
*
|
||||
* @param {string} token - The invite token to search for.
|
||||
* @returns {Promise<InviteToken>} The invite token data.
|
||||
* @throws {Error} If the invite token is not found or there is another error.
|
||||
*/
|
||||
const getInviteToken = async (token) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const invite = await InviteToken.findOne({
|
||||
token,
|
||||
});
|
||||
if (invite === null) {
|
||||
throw new Error(stringService.authInviteNotFound);
|
||||
class InviteModule {
|
||||
constructor({ InviteToken, crypto, stringService }) {
|
||||
this.InviteToken = InviteToken;
|
||||
this.crypto = crypto;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
requestInviteToken = async (userData) => {
|
||||
try {
|
||||
await this.InviteToken.deleteMany({ email: userData.email });
|
||||
userData.token = this.crypto.randomBytes(32).toString("hex");
|
||||
let inviteToken = new this.InviteToken(userData);
|
||||
await inviteToken.save();
|
||||
return inviteToken;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "requestInviteToken";
|
||||
throw error;
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getInviteToken";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves and deletes an invite token
|
||||
*
|
||||
* This function searches for an invite token in the database and deletes it.
|
||||
* If the invite token is not found, it throws an error.
|
||||
*
|
||||
* @param {string} token - The invite token to search for.
|
||||
* @returns {Promise<InviteToken>} The invite token data.
|
||||
* @throws {Error} If the invite token is not found or there is another error.
|
||||
*/
|
||||
const getInviteTokenAndDelete = async (token) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const invite = await InviteToken.findOneAndDelete({
|
||||
token,
|
||||
});
|
||||
if (invite === null) {
|
||||
throw new Error(stringService.authInviteNotFound);
|
||||
getInviteToken = async (token) => {
|
||||
try {
|
||||
const invite = await this.InviteToken.findOne({
|
||||
token,
|
||||
});
|
||||
if (invite === null) {
|
||||
throw new Error(this.stringService.authInviteNotFound);
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getInviteToken";
|
||||
throw error;
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getInviteTokenAndDelete";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
getInviteTokenAndDelete = async (token) => {
|
||||
try {
|
||||
const invite = await this.InviteToken.findOneAndDelete({
|
||||
token,
|
||||
});
|
||||
if (invite === null) {
|
||||
throw new Error(this.stringService.authInviteNotFound);
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getInviteTokenAndDelete";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { requestInviteToken, getInviteToken, getInviteTokenAndDelete };
|
||||
export default InviteModule;
|
||||
|
||||
@@ -1,192 +1,106 @@
|
||||
import MaintenanceWindow from "../../models/MaintenanceWindow.js";
|
||||
const SERVICE_NAME = "maintenanceWindowModule";
|
||||
|
||||
/**
|
||||
* Asynchronously creates a new MaintenanceWindow document and saves it to the database.
|
||||
* If the maintenance window is a one-time event, the expiry field is set to the same value as the end field.
|
||||
* @async
|
||||
* @function createMaintenanceWindow
|
||||
* @param {Object} maintenanceWindowData - The data for the new MaintenanceWindow document.
|
||||
* @param {mongoose.Schema.Types.ObjectId} maintenanceWindowData.monitorId - The ID of the monitor.
|
||||
* @param {Boolean} maintenanceWindowData.active - Indicates whether the maintenance window is active.
|
||||
* @param {Boolean} maintenanceWindowData.oneTime - Indicates whether the maintenance window is a one-time event.
|
||||
* @param {Date} maintenanceWindowData.start - The start date and time of the maintenance window.
|
||||
* @param {Date} maintenanceWindowData.end - The end date and time of the maintenance window.
|
||||
* @returns {Promise<MaintenanceWindow>} The saved MaintenanceWindow document.
|
||||
* @throws {Error} If there is an error saving the document.
|
||||
*/
|
||||
const createMaintenanceWindow = async (maintenanceWindowData) => {
|
||||
try {
|
||||
const maintenanceWindow = new MaintenanceWindow({
|
||||
...maintenanceWindowData,
|
||||
});
|
||||
class MaintenanceWindowModule {
|
||||
constructor({ MaintenanceWindow }) {
|
||||
this.MaintenanceWindow = MaintenanceWindow;
|
||||
}
|
||||
createMaintenanceWindow = async (maintenanceWindowData) => {
|
||||
try {
|
||||
const maintenanceWindow = new this.MaintenanceWindow({
|
||||
...maintenanceWindowData,
|
||||
});
|
||||
|
||||
// If the maintenance window is a one time window, set the expiry to the end date
|
||||
if (maintenanceWindowData.oneTime) {
|
||||
maintenanceWindow.expiry = maintenanceWindowData.end;
|
||||
// If the maintenance window is a one time window, set the expiry to the end date
|
||||
if (maintenanceWindowData.oneTime) {
|
||||
maintenanceWindow.expiry = maintenanceWindowData.end;
|
||||
}
|
||||
const result = await maintenanceWindow.save();
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createMaintenanceWindow";
|
||||
throw error;
|
||||
}
|
||||
const result = await maintenanceWindow.save();
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createMaintenanceWindow";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getMaintenanceWindowById = async ({ id, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindow = await MaintenanceWindow.findOne({
|
||||
_id: id,
|
||||
teamId: teamId,
|
||||
});
|
||||
return maintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously retrieves all MaintenanceWindow documents associated with a specific team ID.
|
||||
* @async
|
||||
* @function getMaintenanceWindowByUserId
|
||||
* @param {String} teamId - The ID of the team.
|
||||
* @param {Object} query - The request body.
|
||||
* @returns {Promise<Array<MaintenanceWindow>>} An array of MaintenanceWindow documents.
|
||||
* @throws {Error} If there is an error retrieving the documents.
|
||||
*/
|
||||
const getMaintenanceWindowsByTeamId = async (teamId, query) => {
|
||||
try {
|
||||
let { active, page, rowsPerPage, field, order } = query || {};
|
||||
const maintenanceQuery = { teamId };
|
||||
|
||||
if (active !== undefined) maintenanceQuery.active = active;
|
||||
|
||||
const maintenanceWindowCount = await MaintenanceWindow.countDocuments(maintenanceQuery);
|
||||
|
||||
// Pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
};
|
||||
getMaintenanceWindowById = async ({ id, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindow = await this.MaintenanceWindow.findOne({
|
||||
_id: id,
|
||||
teamId: teamId,
|
||||
});
|
||||
return maintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Sorting
|
||||
let sort = {};
|
||||
if (field !== undefined && order !== undefined) {
|
||||
sort[field] = order === "asc" ? 1 : -1;
|
||||
getMaintenanceWindowsByTeamId = async (teamId, query) => {
|
||||
try {
|
||||
let { active, page, rowsPerPage, field, order } = query || {};
|
||||
const maintenanceQuery = { teamId };
|
||||
|
||||
if (active !== undefined) maintenanceQuery.active = active;
|
||||
|
||||
const maintenanceWindowCount = await this.MaintenanceWindow.countDocuments(maintenanceQuery);
|
||||
|
||||
// Pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
|
||||
// Sorting
|
||||
let sort = {};
|
||||
if (field !== undefined && order !== undefined) {
|
||||
sort[field] = order === "asc" ? 1 : -1;
|
||||
}
|
||||
|
||||
const maintenanceWindows = await this.MaintenanceWindow.find(maintenanceQuery).skip(skip).limit(rowsPerPage).sort(sort);
|
||||
|
||||
return { maintenanceWindows, maintenanceWindowCount };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowByUserId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindows = await this.MaintenanceWindow.find({
|
||||
monitorId: monitorId,
|
||||
teamId: teamId,
|
||||
});
|
||||
return maintenanceWindows;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const maintenanceWindows = await MaintenanceWindow.find(maintenanceQuery).skip(skip).limit(rowsPerPage).sort(sort);
|
||||
deleteMaintenanceWindowById = async ({ id, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindow = await this.MaintenanceWindow.findOneAndDelete({ _id: id, teamId });
|
||||
return maintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
return { maintenanceWindows, maintenanceWindowCount };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowByUserId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
editMaintenanceWindowById = async ({ id, body }) => {
|
||||
try {
|
||||
const editedMaintenanceWindow = await this.MaintenanceWindow.findByIdAndUpdate(id, body, { new: true });
|
||||
return editedMaintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously retrieves all MaintenanceWindow documents associated with a specific monitor ID.
|
||||
* @async
|
||||
* @function getMaintenanceWindowsByMonitorId
|
||||
* @param {mongoose.Schema.Types.ObjectId} monitorId - The ID of the monitor.
|
||||
* @returns {Promise<Array<MaintenanceWindow>>} An array of MaintenanceWindow documents.
|
||||
* @throws {Error} If there is an error retrieving the documents.
|
||||
*/
|
||||
const getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindows = await MaintenanceWindow.find({
|
||||
monitorId: monitorId,
|
||||
teamId: teamId,
|
||||
});
|
||||
return maintenanceWindows;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously deletes a MaintenanceWindow document by its ID.
|
||||
* @async
|
||||
* @function deleteMaintenanceWindowById
|
||||
* @param {mongoose.Schema.Types.ObjectId} maintenanceWindowId - The ID of the MaintenanceWindow document to delete.
|
||||
* @returns {Promise<MaintenanceWindow>} The deleted MaintenanceWindow document.
|
||||
* @throws {Error} If there is an error deleting the document.
|
||||
*/
|
||||
const deleteMaintenanceWindowById = async ({ id, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindow = await MaintenanceWindow.findOneAndDelete({ _id: id, teamId });
|
||||
return maintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously deletes all MaintenanceWindow documents associated with a specific monitor ID.
|
||||
* @async
|
||||
* @function deleteMaintenanceWindowByMonitorId
|
||||
* @param {mongoose.Schema.Types.ObjectId} monitorId - The ID of the monitor.
|
||||
* @returns {Promise<Object>} The result of the delete operation. This object contains information about the operation, such as the number of documents deleted.
|
||||
* @throws {Error} If there is an error deleting the documents.
|
||||
* @example
|
||||
*/
|
||||
const deleteMaintenanceWindowByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await MaintenanceWindow.deleteMany({ monitorId: monitorId });
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteMaintenanceWindowByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously deletes all MaintenanceWindow documents associated with a specific user ID.
|
||||
* @async
|
||||
* @function deleteMaintenanceWindowByUserId
|
||||
* @param {String} userId - The ID of the user.
|
||||
* @returns {Promise<Object>} The result of the delete operation. This object contains information about the operation, such as the number of documents deleted.
|
||||
* @throws {Error} If there is an error deleting the documents.
|
||||
* @example
|
||||
*/
|
||||
const deleteMaintenanceWindowByUserId = async (userId) => {
|
||||
try {
|
||||
const result = await MaintenanceWindow.deleteMany({ userId: userId });
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteMaintenanceWindowByUserId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const editMaintenanceWindowById = async ({ id, body, teamId }) => {
|
||||
try {
|
||||
const editedMaintenanceWindow = await MaintenanceWindow.findByIdAndUpdate(id, body, { new: true });
|
||||
return editedMaintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createMaintenanceWindow,
|
||||
getMaintenanceWindowById,
|
||||
getMaintenanceWindowsByTeamId,
|
||||
getMaintenanceWindowsByMonitorId,
|
||||
deleteMaintenanceWindowById,
|
||||
deleteMaintenanceWindowByMonitorId,
|
||||
deleteMaintenanceWindowByUserId,
|
||||
editMaintenanceWindowById,
|
||||
};
|
||||
export default MaintenanceWindowModule;
|
||||
|
||||
@@ -1,44 +1,30 @@
|
||||
import NetworkCheck from "../../models/NetworkCheck.js";
|
||||
|
||||
const SERVICE_NAME = "networkCheckModule";
|
||||
|
||||
/**
|
||||
* Creates and saves a new network check document to the database.
|
||||
* @async
|
||||
* @param {object} networkCheckData - The data for the new network check. This should conform to the NetworkCheckSchema.
|
||||
* @param {string} networkCheckData.monitorId - The ID of the monitor associated with this check.
|
||||
* @returns {Promise<object>} A promise that resolves to the newly created network check document.
|
||||
* @throws {Error} Throws an error if the database operation fails.
|
||||
*/
|
||||
const createNetworkCheck = async (networkCheckData) => {
|
||||
try {
|
||||
const networkCheck = await new NetworkCheck(networkCheckData);
|
||||
await networkCheck.save();
|
||||
return networkCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createNetworkCheck";
|
||||
throw error;
|
||||
class NetworkCheckModule {
|
||||
constructor({ NetworkCheck }) {
|
||||
this.NetworkCheck = NetworkCheck;
|
||||
}
|
||||
};
|
||||
createNetworkCheck = async (networkCheckData) => {
|
||||
try {
|
||||
const networkCheck = await new this.NetworkCheck(networkCheckData);
|
||||
await networkCheck.save();
|
||||
return networkCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createNetworkCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNetworkChecksByMonitorId = async (monitorId, limit = 100) => {
|
||||
try {
|
||||
const networkChecks = await this.NetworkCheck.find({ monitorId }).sort({ createdAt: -1 }).limit(limit);
|
||||
return networkChecks;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNetworkChecksByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of network checks for a specific monitor, sorted by most recent.
|
||||
* @async
|
||||
* @param {string} monitorId - The ID of the monitor to retrieve checks for.
|
||||
* @param {number} [limit=100] - The maximum number of checks to return. Defaults to 100.
|
||||
* @returns {Promise<Array<object>>} A promise that resolves to an array of network check documents.
|
||||
* @throws {Error} Throws an error if the database operation fails.
|
||||
*/
|
||||
const getNetworkChecksByMonitorId = async (monitorId, limit = 100) => {
|
||||
try {
|
||||
const networkChecks = await NetworkCheck.find({ monitorId }).sort({ createdAt: -1 }).limit(limit);
|
||||
return networkChecks;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNetworkChecksByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { createNetworkCheck, getNetworkChecksByMonitorId };
|
||||
export default NetworkCheckModule;
|
||||
|
||||
@@ -1,125 +1,100 @@
|
||||
import Notification from "../../models/Notification.js";
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
// import Notification from "../../models/Notification.js";
|
||||
// import Monitor from "../../models/Monitor.js";
|
||||
const SERVICE_NAME = "notificationModule";
|
||||
/**
|
||||
* Creates a new notification.
|
||||
* @param {Object} notificationData - The data for the new notification.
|
||||
* @param {mongoose.Types.ObjectId} notificationData.monitorId - The ID of the monitor.
|
||||
* @param {string} notificationData.type - The type of the notification (e.g., "email", "sms").
|
||||
* @param {string} [notificationData.address] - The address for the notification (if applicable).
|
||||
* @param {string} [notificationData.phone] - The phone number for the notification (if applicable).
|
||||
* @returns {Promise<Object>} The created notification.
|
||||
* @throws Will throw an error if the notification cannot be created.
|
||||
*/
|
||||
const createNotification = async (notificationData) => {
|
||||
try {
|
||||
const notification = await new Notification({ ...notificationData }).save();
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createNotification";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getNotificationsByTeamId = async (teamId) => {
|
||||
try {
|
||||
const notifications = await Notification.find({ teamId });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByTeamId";
|
||||
throw error;
|
||||
class NotificationModule {
|
||||
constructor({ Notification, Monitor }) {
|
||||
this.Notification = Notification;
|
||||
this.Monitor = Monitor;
|
||||
}
|
||||
};
|
||||
|
||||
const getNotificationsByIds = async (notificationIds) => {
|
||||
try {
|
||||
const notifications = await Notification.find({ _id: { $in: notificationIds } });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByIds";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves notifications by monitor ID.
|
||||
* @param {mongoose.Types.ObjectId} monitorId - The ID of the monitor.
|
||||
* @returns {Promise<Array<Object>>} An array of notifications.
|
||||
* @throws Will throw an error if the notifications cannot be retrieved.
|
||||
*/
|
||||
const getNotificationsByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const notifications = await Notification.find({ monitorId });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteNotificationsByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await Notification.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteNotificationsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteNotificationById = async (id) => {
|
||||
try {
|
||||
const notification = await Notification.findById(id);
|
||||
if (!notification) {
|
||||
throw new Error("Notification not found");
|
||||
createNotification = async (notificationData) => {
|
||||
try {
|
||||
const notification = await new this.Notification({ ...notificationData }).save();
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createNotification";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNotificationsByTeamId = async (teamId) => {
|
||||
try {
|
||||
const notifications = await this.Notification.find({ teamId });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNotificationsByIds = async (notificationIds) => {
|
||||
try {
|
||||
const notifications = await this.Notification.find({ _id: { $in: notificationIds } });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByIds";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNotificationsByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const notifications = await this.Notification.find({ monitorId });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
deleteNotificationsByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await this.Notification.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteNotificationsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
deleteNotificationById = async (id) => {
|
||||
try {
|
||||
const notification = await this.Notification.findById(id);
|
||||
if (!notification) {
|
||||
throw new Error("Notification not found");
|
||||
}
|
||||
|
||||
const result = await Notification.findByIdAndDelete(id);
|
||||
await Monitor.updateMany({ notifications: id }, { $pull: { notifications: id } });
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteNotificationById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
const result = await this.Notification.findByIdAndDelete(id);
|
||||
await this.Monitor.updateMany({ notifications: id }, { $pull: { notifications: id } });
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteNotificationById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNotificationById = async (id) => {
|
||||
try {
|
||||
const notification = await this.Notification.findById(id);
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
editNotification = async (id, notificationData) => {
|
||||
try {
|
||||
const notification = await this.Notification.findByIdAndUpdate(id, notificationData, {
|
||||
new: true,
|
||||
});
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editNotification";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const getNotificationById = async (id) => {
|
||||
try {
|
||||
const notification = await Notification.findById(id);
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const editNotification = async (id, notificationData) => {
|
||||
try {
|
||||
const notification = await Notification.findByIdAndUpdate(id, notificationData, {
|
||||
new: true,
|
||||
});
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editNotification";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createNotification,
|
||||
getNotificationsByTeamId,
|
||||
getNotificationsByIds,
|
||||
getNotificationsByMonitorId,
|
||||
deleteNotificationsByMonitorId,
|
||||
deleteNotificationById,
|
||||
getNotificationById,
|
||||
editNotification,
|
||||
};
|
||||
export default NotificationModule;
|
||||
|
||||
@@ -1,57 +1,31 @@
|
||||
import PageSpeedCheck from "../../models/PageSpeedCheck.js";
|
||||
// import PageSpeedCheck from "../../models/PageSpeedCheck.js";
|
||||
const SERVICE_NAME = "pageSpeedCheckModule";
|
||||
/**
|
||||
* Create a PageSpeed check for a monitor
|
||||
* @async
|
||||
* @param {Object} pageSpeedCheckData
|
||||
* @param {string} pageSpeedCheckData.monitorId
|
||||
* @param {number} pageSpeedCheckData.accessibility
|
||||
* @param {number} pageSpeedCheckData.bestPractices
|
||||
* @param {number} pageSpeedCheckData.seo
|
||||
* @param {number} pageSpeedCheckData.performance
|
||||
* @returns {Promise<PageSpeedCheck>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const createPageSpeedCheck = async (pageSpeedCheckData) => {
|
||||
try {
|
||||
const pageSpeedCheck = await new PageSpeedCheck({
|
||||
...pageSpeedCheckData,
|
||||
}).save();
|
||||
return pageSpeedCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createPageSpeedCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
const createPageSpeedChecks = async (pageSpeedChecks) => {
|
||||
try {
|
||||
await PageSpeedCheck.insertMany(pageSpeedChecks, { ordered: false });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createPageSpeedCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete all PageSpeed checks for a monitor
|
||||
* @async
|
||||
* @param {string} monitorId
|
||||
* @returns {number}
|
||||
* @throws {Error}
|
||||
*/
|
||||
|
||||
const deletePageSpeedChecksByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await PageSpeedCheck.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deletePageSpeedChecksByMonitorId";
|
||||
throw error;
|
||||
class PageSpeedCheckModule {
|
||||
constructor({ PageSpeedCheck }) {
|
||||
this.PageSpeedCheck = PageSpeedCheck;
|
||||
}
|
||||
};
|
||||
|
||||
export { createPageSpeedCheck, createPageSpeedChecks, deletePageSpeedChecksByMonitorId };
|
||||
createPageSpeedChecks = async (pageSpeedChecks) => {
|
||||
try {
|
||||
await this.PageSpeedCheck.insertMany(pageSpeedChecks, { ordered: false });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createPageSpeedCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
deletePageSpeedChecksByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await this.PageSpeedCheck.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deletePageSpeedChecksByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
export default PageSpeedCheckModule;
|
||||
|
||||
@@ -1,86 +1,80 @@
|
||||
import UserModel from "../../models/User.js";
|
||||
import RecoveryToken from "../../models/RecoveryToken.js";
|
||||
import crypto from "crypto";
|
||||
import serviceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
import StringService from "../../../service/system/stringService.js";
|
||||
|
||||
const SERVICE_NAME = "recoveryModule";
|
||||
|
||||
/**
|
||||
* Request a recovery token
|
||||
* @async
|
||||
* @param {string} email
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const requestRecoveryToken = async (email) => {
|
||||
try {
|
||||
// Delete any existing tokens
|
||||
await RecoveryToken.deleteMany({ email });
|
||||
let recoveryToken = new RecoveryToken({
|
||||
email,
|
||||
token: crypto.randomBytes(32).toString("hex"),
|
||||
});
|
||||
await recoveryToken.save();
|
||||
return recoveryToken;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "requestRecoveryToken";
|
||||
throw error;
|
||||
class RecoveryModule {
|
||||
constructor({ User, RecoveryToken, crypto, stringService }) {
|
||||
this.User = User;
|
||||
this.RecoveryToken = RecoveryToken;
|
||||
this.crypto = crypto;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
};
|
||||
|
||||
const validateRecoveryToken = async (candidateToken) => {
|
||||
const stringService = serviceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const recoveryToken = await RecoveryToken.findOne({
|
||||
token: candidateToken,
|
||||
});
|
||||
if (recoveryToken !== null) {
|
||||
requestRecoveryToken = async (email) => {
|
||||
try {
|
||||
// Delete any existing tokens
|
||||
await this.RecoveryToken.deleteMany({ email });
|
||||
let recoveryToken = new this.RecoveryToken({
|
||||
email,
|
||||
token: this.crypto.randomBytes(32).toString("hex"),
|
||||
});
|
||||
await recoveryToken.save();
|
||||
return recoveryToken;
|
||||
} else {
|
||||
throw new Error(stringService.dbTokenNotFound);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "requestRecoveryToken";
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "validateRecoveryToken";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const resetPassword = async (password, candidateToken) => {
|
||||
const stringService = serviceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const newPassword = password;
|
||||
|
||||
// Validate token again
|
||||
const recoveryToken = await validateRecoveryToken(candidateToken);
|
||||
const user = await UserModel.findOne({ email: recoveryToken.email });
|
||||
|
||||
if (user === null) {
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
};
|
||||
validateRecoveryToken = async (candidateToken) => {
|
||||
try {
|
||||
const recoveryToken = await this.RecoveryToken.findOne({
|
||||
token: candidateToken,
|
||||
});
|
||||
if (recoveryToken !== null) {
|
||||
return recoveryToken;
|
||||
} else {
|
||||
throw new Error(this.stringService.dbTokenNotFound);
|
||||
}
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "validateRecoveryToken";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const match = await user.comparePassword(newPassword);
|
||||
if (match === true) {
|
||||
throw new Error(stringService.dbResetPasswordBadMatch);
|
||||
resetPassword = async (password, candidateToken) => {
|
||||
try {
|
||||
const newPassword = password;
|
||||
|
||||
// Validate token again
|
||||
const recoveryToken = await this.validateRecoveryToken(candidateToken);
|
||||
const user = await this.User.findOne({ email: recoveryToken.email });
|
||||
|
||||
if (user === null) {
|
||||
throw new Error(this.stringService.dbUserNotFound);
|
||||
}
|
||||
|
||||
const match = await user.comparePassword(newPassword);
|
||||
|
||||
if (match === true) {
|
||||
throw new Error("Password cannot be the same as the old password");
|
||||
}
|
||||
|
||||
user.password = newPassword;
|
||||
await user.save();
|
||||
await this.RecoveryToken.deleteMany({ email: recoveryToken.email });
|
||||
// Fetch the user again without the password
|
||||
const userWithoutPassword = await this.User.findOne({
|
||||
email: recoveryToken.email,
|
||||
})
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return userWithoutPassword;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "resetPassword";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
user.password = newPassword;
|
||||
await user.save();
|
||||
await RecoveryToken.deleteMany({ email: recoveryToken.email });
|
||||
// Fetch the user again without the password
|
||||
const userWithoutPassword = await UserModel.findOne({
|
||||
email: recoveryToken.email,
|
||||
})
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return userWithoutPassword;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "resetPassword";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { requestRecoveryToken, validateRecoveryToken, resetPassword };
|
||||
export default RecoveryModule;
|
||||
|
||||
@@ -1,41 +1,36 @@
|
||||
import AppSettings from "../../models/AppSettings.js";
|
||||
// import AppSettings from "../../models/AppSettings.js";
|
||||
const SERVICE_NAME = "SettingsModule";
|
||||
|
||||
const getAppSettings = async () => {
|
||||
try {
|
||||
const settings = AppSettings.findOne();
|
||||
return settings;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getSettings";
|
||||
throw error;
|
||||
class SettingsModule {
|
||||
constructor({ AppSettings }) {
|
||||
this.AppSettings = AppSettings;
|
||||
}
|
||||
};
|
||||
|
||||
const updateAppSettings = async (newSettings) => {
|
||||
try {
|
||||
const update = { $set: { ...newSettings } };
|
||||
updateAppSettings = async (newSettings) => {
|
||||
try {
|
||||
const update = { $set: { ...newSettings } };
|
||||
|
||||
if (newSettings.pagespeedApiKey === "") {
|
||||
update.$unset = { pagespeedApiKey: "" };
|
||||
delete update.$set.pagespeedApiKey;
|
||||
if (newSettings.pagespeedApiKey === "") {
|
||||
update.$unset = { pagespeedApiKey: "" };
|
||||
delete update.$set.pagespeedApiKey;
|
||||
}
|
||||
|
||||
if (newSettings.systemEmailPassword === "") {
|
||||
update.$unset = { systemEmailPassword: "" };
|
||||
delete update.$set.systemEmailPassword;
|
||||
}
|
||||
|
||||
await this.AppSettings.findOneAndUpdate({}, update, {
|
||||
upsert: true,
|
||||
});
|
||||
const settings = await this.AppSettings.findOne().select("-__v -_id -createdAt -updatedAt -singleton").lean();
|
||||
return settings;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateAppSettings";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (newSettings.systemEmailPassword === "") {
|
||||
update.$unset = { systemEmailPassword: "" };
|
||||
delete update.$set.systemEmailPassword;
|
||||
}
|
||||
|
||||
await AppSettings.findOneAndUpdate({}, update, {
|
||||
upsert: true,
|
||||
});
|
||||
const settings = await AppSettings.findOne().select("-__v -_id -createdAt -updatedAt -singleton").lean();
|
||||
return settings;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateAppSettings";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { getAppSettings, updateAppSettings };
|
||||
export default SettingsModule;
|
||||
|
||||
@@ -1,292 +1,287 @@
|
||||
import StatusPage from "../../models/StatusPage.js";
|
||||
import { NormalizeData } from "../../../utils/dataUtils.js";
|
||||
import ServiceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
import StringService from "../../../service/system/stringService.js";
|
||||
// import StatusPage from "../../models/StatusPage.js";
|
||||
// import { NormalizeData } from "../../../utils/dataUtils.js";
|
||||
// import ServiceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
// import StringService from "../../../service/system/stringService.js";
|
||||
|
||||
const SERVICE_NAME = "statusPageModule";
|
||||
|
||||
const createStatusPage = async ({ statusPageData, image, userId, teamId }) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const statusPage = new StatusPage({
|
||||
...statusPageData,
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
if (image) {
|
||||
statusPage.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
}
|
||||
await statusPage.save();
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
if (error?.code === 11000) {
|
||||
// Handle duplicate URL errors
|
||||
error.status = 400;
|
||||
error.message = stringService.statusPageUrlNotUnique;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createStatusPage";
|
||||
throw error;
|
||||
class StatusPageModule {
|
||||
constructor({ StatusPage, NormalizeData, stringService }) {
|
||||
this.StatusPage = StatusPage;
|
||||
this.NormalizeData = NormalizeData;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
};
|
||||
|
||||
const updateStatusPage = async (statusPageData, image) => {
|
||||
try {
|
||||
if (image) {
|
||||
statusPageData.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
} else {
|
||||
statusPageData.logo = null;
|
||||
}
|
||||
|
||||
if (statusPageData.deleteSubmonitors === "true") {
|
||||
statusPageData.subMonitors = [];
|
||||
}
|
||||
const statusPage = await StatusPage.findOneAndUpdate({ url: statusPageData.url }, statusPageData, {
|
||||
new: true,
|
||||
});
|
||||
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusPageByUrl = async (url, type) => {
|
||||
// TODO This is deprecated, can remove and have controller call getStatusPage
|
||||
try {
|
||||
return getStatusPage(url);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPageByUrl";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusPagesByTeamId = async (teamId) => {
|
||||
try {
|
||||
const statusPages = await StatusPage.find({ teamId });
|
||||
return statusPages;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPagesByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusPage = async (url) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const preliminaryStatusPage = await StatusPage.findOne({ url });
|
||||
if (!preliminaryStatusPage) {
|
||||
const error = new Error(stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
createStatusPage = async ({ statusPageData, image, userId, teamId }) => {
|
||||
try {
|
||||
const statusPage = new this.StatusPage({
|
||||
...statusPageData,
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
if (image) {
|
||||
statusPage.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
}
|
||||
await statusPage.save();
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
if (error?.code === 11000) {
|
||||
// Handle duplicate URL errors
|
||||
error.status = 400;
|
||||
error.message = this.stringService.statusPageUrlNotUnique;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
if (!preliminaryStatusPage.monitors || preliminaryStatusPage.monitors.length === 0) {
|
||||
const { _id, color, companyName, isPublished, logo, originalMonitors, showCharts, showUptimePercentage, timezone, showAdminLoginLink, url } =
|
||||
preliminaryStatusPage;
|
||||
return {
|
||||
statusPage: {
|
||||
_id,
|
||||
color,
|
||||
companyName,
|
||||
isPublished,
|
||||
logo,
|
||||
originalMonitors,
|
||||
showCharts,
|
||||
showUptimePercentage,
|
||||
timezone,
|
||||
showAdminLoginLink,
|
||||
url,
|
||||
},
|
||||
monitors: [],
|
||||
};
|
||||
updateStatusPage = async (statusPageData, image) => {
|
||||
try {
|
||||
if (image) {
|
||||
statusPageData.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
} else {
|
||||
statusPageData.logo = null;
|
||||
}
|
||||
|
||||
if (statusPageData.deleteSubmonitors === "true") {
|
||||
statusPageData.subMonitors = [];
|
||||
}
|
||||
const statusPage = await this.StatusPage.findOneAndUpdate({ url: statusPageData.url }, statusPageData, {
|
||||
new: true,
|
||||
});
|
||||
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const statusPageQuery = await StatusPage.aggregate([
|
||||
{ $match: { url: url } },
|
||||
{
|
||||
$set: {
|
||||
originalMonitors: "$monitors",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "monitors",
|
||||
localField: "monitors",
|
||||
foreignField: "_id",
|
||||
as: "monitors",
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: {
|
||||
path: "$monitors",
|
||||
preserveNullAndEmptyArrays: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "maintenancewindows",
|
||||
let: { monitorId: "$monitors._id" },
|
||||
pipeline: [{ $match: { $expr: { $eq: ["$monitorId", "$$monitorId"] } } }],
|
||||
as: "monitors.maintenanceWindows",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "checks",
|
||||
let: { monitorId: "$monitors._id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
{ $limit: 25 },
|
||||
],
|
||||
as: "monitors.checks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
"monitors.orderIndex": {
|
||||
$indexOfArray: ["$originalMonitors", "$monitors._id"],
|
||||
},
|
||||
"monitors.isMaintenance": {
|
||||
$reduce: {
|
||||
input: "$monitors.maintenanceWindows",
|
||||
initialValue: false,
|
||||
in: {
|
||||
$or: [
|
||||
"$$value",
|
||||
{
|
||||
$and: [{ $eq: ["$$this.active", true] }, { $lte: ["$$this.start", "$$NOW"] }, { $gte: ["$$this.end", "$$NOW"] }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ $match: { "monitors.orderIndex": { $ne: -1 } } },
|
||||
{ $sort: { "monitors.orderIndex": 1 } },
|
||||
getStatusPageByUrl = async (url) => {
|
||||
// TODO This is deprecated, can remove and have controller call getStatusPage
|
||||
try {
|
||||
return this.getStatusPage(url);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPageByUrl";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id",
|
||||
statusPage: { $first: "$$ROOT" },
|
||||
monitors: { $push: "$monitors" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
getStatusPagesByTeamId = async (teamId) => {
|
||||
try {
|
||||
const statusPages = await this.StatusPage.find({ teamId });
|
||||
return statusPages;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPagesByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
getStatusPage = async (url) => {
|
||||
try {
|
||||
const preliminaryStatusPage = await this.StatusPage.findOne({ url });
|
||||
if (!preliminaryStatusPage) {
|
||||
const error = new Error(this.stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!preliminaryStatusPage.monitors || preliminaryStatusPage.monitors.length === 0) {
|
||||
const { _id, color, companyName, isPublished, logo, originalMonitors, showCharts, showUptimePercentage, timezone, showAdminLoginLink, url } =
|
||||
preliminaryStatusPage;
|
||||
return {
|
||||
statusPage: {
|
||||
_id: 1,
|
||||
color: 1,
|
||||
companyName: 1,
|
||||
isPublished: 1,
|
||||
logo: 1,
|
||||
originalMonitors: 1,
|
||||
showCharts: 1,
|
||||
showUptimePercentage: 1,
|
||||
timezone: 1,
|
||||
showAdminLoginLink: 1,
|
||||
url: 1,
|
||||
_id,
|
||||
color,
|
||||
companyName,
|
||||
isPublished,
|
||||
logo,
|
||||
originalMonitors,
|
||||
showCharts,
|
||||
showUptimePercentage,
|
||||
timezone,
|
||||
showAdminLoginLink,
|
||||
url,
|
||||
},
|
||||
monitors: {
|
||||
_id: 1,
|
||||
userId: 1,
|
||||
teamId: 1,
|
||||
name: 1,
|
||||
description: 1,
|
||||
status: 1,
|
||||
type: 1,
|
||||
ignoreTlsErrors: 1,
|
||||
jsonPath: 1,
|
||||
expectedValue: 1,
|
||||
matchMethod: 1,
|
||||
url: 1,
|
||||
port: 1,
|
||||
isActive: 1,
|
||||
interval: 1,
|
||||
uptimePercentage: 1,
|
||||
notifications: 1,
|
||||
secret: 1,
|
||||
thresholds: 1,
|
||||
alertThreshold: 1,
|
||||
cpuAlertThreshold: 1,
|
||||
memoryAlertThreshold: 1,
|
||||
diskAlertThreshold: 1,
|
||||
tempAlertThreshold: 1,
|
||||
checks: 1,
|
||||
isMaintenance: 1,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
monitors: [],
|
||||
};
|
||||
}
|
||||
|
||||
const statusPageQuery = await this.StatusPage.aggregate([
|
||||
{ $match: { url: url } },
|
||||
{
|
||||
$set: {
|
||||
originalMonitors: "$monitors",
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
if (!statusPageQuery.length) {
|
||||
const error = new Error(stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
{
|
||||
$lookup: {
|
||||
from: "monitors",
|
||||
localField: "monitors",
|
||||
foreignField: "_id",
|
||||
as: "monitors",
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: {
|
||||
path: "$monitors",
|
||||
preserveNullAndEmptyArrays: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "maintenancewindows",
|
||||
let: { monitorId: "$monitors._id" },
|
||||
pipeline: [{ $match: { $expr: { $eq: ["$monitorId", "$$monitorId"] } } }],
|
||||
as: "monitors.maintenanceWindows",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "checks",
|
||||
let: { monitorId: "$monitors._id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
{ $limit: 25 },
|
||||
],
|
||||
as: "monitors.checks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
"monitors.orderIndex": {
|
||||
$indexOfArray: ["$originalMonitors", "$monitors._id"],
|
||||
},
|
||||
"monitors.isMaintenance": {
|
||||
$reduce: {
|
||||
input: "$monitors.maintenanceWindows",
|
||||
initialValue: false,
|
||||
in: {
|
||||
$or: [
|
||||
"$$value",
|
||||
{
|
||||
$and: [{ $eq: ["$$this.active", true] }, { $lte: ["$$this.start", "$$NOW"] }, { $gte: ["$$this.end", "$$NOW"] }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ $match: { "monitors.orderIndex": { $ne: -1 } } },
|
||||
{ $sort: { "monitors.orderIndex": 1 } },
|
||||
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id",
|
||||
statusPage: { $first: "$$ROOT" },
|
||||
monitors: { $push: "$monitors" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
statusPage: {
|
||||
_id: 1,
|
||||
color: 1,
|
||||
companyName: 1,
|
||||
isPublished: 1,
|
||||
logo: 1,
|
||||
originalMonitors: 1,
|
||||
showCharts: 1,
|
||||
showUptimePercentage: 1,
|
||||
timezone: 1,
|
||||
showAdminLoginLink: 1,
|
||||
url: 1,
|
||||
},
|
||||
monitors: {
|
||||
_id: 1,
|
||||
userId: 1,
|
||||
teamId: 1,
|
||||
name: 1,
|
||||
description: 1,
|
||||
status: 1,
|
||||
type: 1,
|
||||
ignoreTlsErrors: 1,
|
||||
jsonPath: 1,
|
||||
expectedValue: 1,
|
||||
matchMethod: 1,
|
||||
url: 1,
|
||||
port: 1,
|
||||
isActive: 1,
|
||||
interval: 1,
|
||||
uptimePercentage: 1,
|
||||
notifications: 1,
|
||||
secret: 1,
|
||||
thresholds: 1,
|
||||
alertThreshold: 1,
|
||||
cpuAlertThreshold: 1,
|
||||
memoryAlertThreshold: 1,
|
||||
diskAlertThreshold: 1,
|
||||
tempAlertThreshold: 1,
|
||||
checks: 1,
|
||||
isMaintenance: 1,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
if (!statusPageQuery.length) {
|
||||
const error = new Error(this.stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const { statusPage, monitors } = statusPageQuery[0];
|
||||
|
||||
const normalizedMonitors = monitors.map((monitor) => {
|
||||
return {
|
||||
...monitor,
|
||||
checks: this.NormalizeData(monitor.checks, 10, 100),
|
||||
};
|
||||
});
|
||||
|
||||
return { statusPage, monitors: normalizedMonitors };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPageByUrl";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const { statusPage, monitors } = statusPageQuery[0];
|
||||
deleteStatusPage = async (url) => {
|
||||
try {
|
||||
await this.StatusPage.deleteOne({ url });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
deleteStatusPagesByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
await this.StatusPage.deleteMany({ monitors: { $in: [monitorId] } });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteStatusPageByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const normalizedMonitors = monitors.map((monitor) => {
|
||||
return {
|
||||
...monitor,
|
||||
checks: NormalizeData(monitor.checks, 10, 100),
|
||||
};
|
||||
});
|
||||
|
||||
return { statusPage, monitors: normalizedMonitors };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPageByUrl";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteStatusPage = async (url) => {
|
||||
try {
|
||||
await StatusPage.deleteOne({ url });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteStatusPagesByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
await StatusPage.deleteMany({ monitors: { $in: [monitorId] } });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteStatusPageByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createStatusPage,
|
||||
updateStatusPage,
|
||||
getStatusPagesByTeamId,
|
||||
getStatusPage,
|
||||
getStatusPageByUrl,
|
||||
deleteStatusPage,
|
||||
deleteStatusPagesByMonitorId,
|
||||
};
|
||||
export default StatusPageModule;
|
||||
|
||||
@@ -1,261 +1,171 @@
|
||||
import UserModel from "../../models/User.js";
|
||||
import TeamModel from "../../models/Team.js";
|
||||
import { GenerateAvatarImage } from "../../../utils/imageProcessing.js";
|
||||
|
||||
const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key
|
||||
import { ParseBoolean } from "../../../utils/utils.js";
|
||||
import ServiceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
import StringService from "../../../service/system/stringService.js";
|
||||
const SERVICE_NAME = "userModule";
|
||||
const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key
|
||||
|
||||
const checkSuperadmin = async () => {
|
||||
const superAdmin = await UserModel.findOne({ role: "superadmin" });
|
||||
if (superAdmin !== null) {
|
||||
return true;
|
||||
class UserModule {
|
||||
constructor({ User, Team, GenerateAvatarImage, ParseBoolean, stringService }) {
|
||||
this.User = User;
|
||||
this.Team = Team;
|
||||
this.GenerateAvatarImage = GenerateAvatarImage;
|
||||
this.ParseBoolean = ParseBoolean;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert a User
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const insertUser = async (userData, imageFile, generateAvatarImage = GenerateAvatarImage) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
if (imageFile) {
|
||||
// 1. Save the full size image
|
||||
userData.profileImage = {
|
||||
data: imageFile.buffer,
|
||||
contentType: imageFile.mimetype,
|
||||
};
|
||||
checkSuperadmin = async () => {
|
||||
try {
|
||||
const superAdmin = await this.User.findOne({ role: "superadmin" });
|
||||
if (superAdmin !== null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "checkSuperadmin";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await generateAvatarImage(imageFile);
|
||||
userData.avatarImage = avatar;
|
||||
insertUser = async (userData, imageFile) => {
|
||||
try {
|
||||
if (imageFile) {
|
||||
// 1. Save the full size image
|
||||
userData.profileImage = {
|
||||
data: imageFile.buffer,
|
||||
contentType: imageFile.mimetype,
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await this.GenerateAvatarImage(imageFile);
|
||||
userData.avatarImage = avatar;
|
||||
}
|
||||
|
||||
// Handle creating team if superadmin
|
||||
if (userData.role.includes("superadmin")) {
|
||||
const team = new this.Team({
|
||||
email: userData.email,
|
||||
});
|
||||
userData.teamId = team._id;
|
||||
userData.checkTTL = 60 * 60 * 24 * 30;
|
||||
await team.save();
|
||||
}
|
||||
|
||||
const newUser = new this.User(userData);
|
||||
await newUser.save();
|
||||
return await this.User.findOne({ _id: newUser._id }).select("-password").select("-profileImage"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
if (error.code === DUPLICATE_KEY_CODE) {
|
||||
error.message = this.stringService.dbUserExists;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "insertUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getUserByEmail = async (email) => {
|
||||
try {
|
||||
// Need the password to be able to compare, removed .select()
|
||||
// We can strip the hash before returning the user
|
||||
const user = await this.User.findOne({ email: email }).select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error(this.stringService.dbUserNotFound);
|
||||
}
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserByEmail";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateUser = async ({ userId, user, file }) => {
|
||||
if (!userId) {
|
||||
throw new Error("No user in request");
|
||||
}
|
||||
|
||||
// Handle creating team if superadmin
|
||||
if (userData.role.includes("superadmin")) {
|
||||
const team = new TeamModel({
|
||||
email: userData.email,
|
||||
});
|
||||
userData.teamId = team._id;
|
||||
userData.checkTTL = 60 * 60 * 24 * 30;
|
||||
await team.save();
|
||||
try {
|
||||
const candidateUser = { ...user };
|
||||
|
||||
if (this.ParseBoolean(candidateUser.deleteProfileImage) === true) {
|
||||
candidateUser.profileImage = null;
|
||||
candidateUser.avatarImage = null;
|
||||
} else if (file) {
|
||||
// 1. Save the full size image
|
||||
candidateUser.profileImage = {
|
||||
data: file.buffer,
|
||||
contentType: file.mimetype,
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await this.GenerateAvatarImage(file);
|
||||
candidateUser.avatarImage = avatar;
|
||||
}
|
||||
|
||||
const updatedUser = await this.User.findByIdAndUpdate(
|
||||
userId,
|
||||
candidateUser,
|
||||
{ new: true } // Returns updated user instead of pre-update user
|
||||
)
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateUser";
|
||||
throw error;
|
||||
}
|
||||
|
||||
const newUser = new UserModel(userData);
|
||||
await newUser.save();
|
||||
return await UserModel.findOne({ _id: newUser._id }).select("-password").select("-profileImage"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
if (error.code === DUPLICATE_KEY_CODE) {
|
||||
error.message = stringService.dbUserExists;
|
||||
};
|
||||
deleteUser = async (userId) => {
|
||||
try {
|
||||
const deletedUser = await this.User.findByIdAndDelete(userId);
|
||||
if (!deletedUser) {
|
||||
throw new Error(this.stringService.dbUserNotFound);
|
||||
}
|
||||
return deletedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteUser";
|
||||
throw error;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "insertUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get User by Email
|
||||
* Gets a user by Email. Not sure if we'll ever need this except for login.
|
||||
* If not needed except for login, we can move password comparison here
|
||||
* Throws error if user not found
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getUserByEmail = async (email) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
// Need the password to be able to compare, removed .select()
|
||||
// We can strip the hash before returning the user
|
||||
const user = await UserModel.findOne({ email: email }).select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
getAllUsers = async () => {
|
||||
try {
|
||||
const users = await this.User.find().select("-password").select("-profileImage");
|
||||
return users;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getAllUsers";
|
||||
throw error;
|
||||
}
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserByEmail";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a user by ID
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
getUserById = async (roles, userId) => {
|
||||
try {
|
||||
if (!roles.includes("superadmin")) {
|
||||
throw new Error("User is not a superadmin");
|
||||
}
|
||||
|
||||
const updateUser = async ({ userId, user, file }) => {
|
||||
if (!userId) {
|
||||
throw new Error("No user in request");
|
||||
}
|
||||
const user = await this.User.findById(userId).select("-password").select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
try {
|
||||
const candidateUser = { ...user };
|
||||
|
||||
if (ParseBoolean(candidateUser.deleteProfileImage) === true) {
|
||||
candidateUser.profileImage = null;
|
||||
candidateUser.avatarImage = null;
|
||||
} else if (file) {
|
||||
// 1. Save the full size image
|
||||
candidateUser.profileImage = {
|
||||
data: file.buffer,
|
||||
contentType: file.mimetype,
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await GenerateAvatarImage(file);
|
||||
candidateUser.avatarImage = avatar;
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// ******************************************
|
||||
// End handling profile image
|
||||
// ******************************************
|
||||
|
||||
const updatedUser = await UserModel.findByIdAndUpdate(
|
||||
userId,
|
||||
candidateUser,
|
||||
{ new: true } // Returns updated user instead of pre-update user
|
||||
)
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a user by ID
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const deleteUser = async (userId) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const deletedUser = await UserModel.findByIdAndDelete(userId);
|
||||
if (!deletedUser) {
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
editUserById = async (userId, user) => {
|
||||
try {
|
||||
await this.User.findByIdAndUpdate(userId, user, { new: true }).select("-password").select("-profileImage");
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editUserById";
|
||||
throw error;
|
||||
}
|
||||
return deletedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user by ID
|
||||
* @async
|
||||
* @param {string} teamId
|
||||
* @returns {void}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const deleteTeam = async (teamId) => {
|
||||
try {
|
||||
await TeamModel.findByIdAndDelete(teamId);
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteTeam";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteAllOtherUsers = async () => {
|
||||
try {
|
||||
await UserModel.deleteMany({ role: { $ne: "superadmin" } });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteAllOtherUsers";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getAllUsers = async () => {
|
||||
try {
|
||||
const users = await UserModel.find().select("-password").select("-profileImage");
|
||||
return users;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getAllUsers";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const logoutUser = async (userId) => {
|
||||
try {
|
||||
await UserModel.updateOne({ _id: userId }, { $unset: { authToken: null } });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "logoutUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getUserById = async (roles, userId) => {
|
||||
try {
|
||||
if (!roles.includes("superadmin")) {
|
||||
throw new Error("User is not a superadmin");
|
||||
}
|
||||
|
||||
const user = await UserModel.findById(userId).select("-password").select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const editUserById = async (userId, user) => {
|
||||
try {
|
||||
await UserModel.findByIdAndUpdate(userId, user, { new: true }).select("-password").select("-profileImage");
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editUserById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
checkSuperadmin,
|
||||
insertUser,
|
||||
getUserByEmail,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
deleteTeam,
|
||||
deleteAllOtherUsers,
|
||||
getAllUsers,
|
||||
logoutUser,
|
||||
getUserById,
|
||||
editUserById,
|
||||
};
|
||||
export default UserModule;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
import Check from "../../models/Check.js";
|
||||
import logger from "../../../utils/logger.js";
|
||||
import { logger } from "../../../utils/logger.js";
|
||||
|
||||
const generateRandomUrl = () => {
|
||||
const domains = ["example.com", "test.org", "demo.net", "sample.io", "mock.dev"];
|
||||
|
||||
@@ -2,15 +2,16 @@ import { initializeServices } from "./config/services.js";
|
||||
import { initializeControllers } from "./config/controllers.js";
|
||||
import { createApp } from "./app.js";
|
||||
import { initShutdownListener } from "./shutdown.js";
|
||||
import logger from "./utils/logger.js";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
import Logger from "./utils/logger.js";
|
||||
import SettingsService from "./service/system/settingsService.js";
|
||||
import AppSettings from "./db/models/AppSettings.js";
|
||||
|
||||
const SERVICE_NAME = "Server";
|
||||
let logger;
|
||||
|
||||
const startApp = async () => {
|
||||
// FE path
|
||||
@@ -18,12 +19,16 @@ const startApp = async () => {
|
||||
const __dirname = path.dirname(__filename);
|
||||
const openApiSpec = JSON.parse(fs.readFileSync(path.join(__dirname, "../openapi.json"), "utf8"));
|
||||
const frontendPath = path.join(__dirname, "public");
|
||||
|
||||
// Create services
|
||||
const settingsService = new SettingsService(AppSettings);
|
||||
const appSettings = settingsService.loadSettings();
|
||||
const envSettings = settingsService.loadSettings();
|
||||
|
||||
// Create logger
|
||||
logger = new Logger({ envSettings });
|
||||
|
||||
// Initialize services
|
||||
const services = await initializeServices(appSettings, settingsService);
|
||||
const services = await initializeServices({ logger, envSettings, settingsService });
|
||||
|
||||
// Initialize controllers
|
||||
const controllers = initializeControllers(services);
|
||||
@@ -31,12 +36,12 @@ const startApp = async () => {
|
||||
const app = createApp({
|
||||
services,
|
||||
controllers,
|
||||
appSettings,
|
||||
envSettings,
|
||||
frontendPath,
|
||||
openApiSpec,
|
||||
});
|
||||
|
||||
const port = appSettings.port || 52345;
|
||||
const port = envSettings.port || 52345;
|
||||
const server = app.listen(port, () => {
|
||||
logger.info({ message: `Server started on port:${port}` });
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import logger from "../utils/logger.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
import ServiceRegistry from "../service/system/serviceRegistry.js";
|
||||
import StringService from "../service/system/stringService.js";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import logger from "../utils/logger.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
|
||||
const languageMiddleware = (stringService, translationService) => async (req, res, next) => {
|
||||
try {
|
||||
|
||||
17
server/src/middleware/rateLimiter.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import rateLimit from "express-rate-limit";
|
||||
|
||||
export const generalApiLimiter = rateLimit({
|
||||
window: 60 * 1000,
|
||||
limit: 600,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
ipv6Subnet: 64,
|
||||
});
|
||||
|
||||
export const authApiLimiter = rateLimit({
|
||||
window: 60 * 1000,
|
||||
limit: 15,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
ipv6Subnet: 64,
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import logger from "../utils/logger.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
import ServiceRegistry from "../service/system/serviceRegistry.js";
|
||||
import StringService from "../service/system/stringService.js";
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
@@ -23,7 +23,7 @@ class CheckService {
|
||||
throw this.errorService.createBadRequestError("No team ID in request");
|
||||
}
|
||||
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
const monitor = await this.db.monitorModule.getMonitorById(monitorId);
|
||||
|
||||
if (!monitor) {
|
||||
throw this.errorService.createNotFoundError("Monitor not found");
|
||||
@@ -34,7 +34,7 @@ class CheckService {
|
||||
}
|
||||
|
||||
let { type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status } = query;
|
||||
const result = await this.db.getChecksByMonitor({
|
||||
const result = await this.db.checkModule.getChecksByMonitor({
|
||||
monitorId,
|
||||
type,
|
||||
sortOrder,
|
||||
@@ -55,7 +55,7 @@ class CheckService {
|
||||
throw this.errorService.createBadRequestError("No team ID in request");
|
||||
}
|
||||
|
||||
const checkData = await this.db.getChecksByTeam({
|
||||
const checkData = await this.db.checkModule.getChecksByTeam({
|
||||
sortOrder,
|
||||
dateRange,
|
||||
filter,
|
||||
@@ -72,7 +72,7 @@ class CheckService {
|
||||
throw this.errorService.createBadRequestError("No team ID in request");
|
||||
}
|
||||
|
||||
const summary = await this.db.getChecksSummaryByTeamId({ teamId });
|
||||
const summary = await this.db.checkModule.getChecksSummaryByTeamId({ teamId });
|
||||
return summary;
|
||||
};
|
||||
|
||||
@@ -85,7 +85,7 @@ class CheckService {
|
||||
throw this.errorService.createBadRequestError("No team ID in request");
|
||||
}
|
||||
|
||||
const updatedCheck = await this.db.ackCheck(checkId, teamId, ack);
|
||||
const updatedCheck = await this.db.checkModule.ackCheck(checkId, teamId, ack);
|
||||
return updatedCheck;
|
||||
};
|
||||
|
||||
@@ -95,7 +95,7 @@ class CheckService {
|
||||
throw this.errorService.createBadRequestError("No monitor ID in request");
|
||||
}
|
||||
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
const monitor = await this.db.monitorModule.getMonitorById(monitorId);
|
||||
if (!monitor) {
|
||||
throw this.errorService.createNotFoundError("Monitor not found");
|
||||
}
|
||||
@@ -105,7 +105,7 @@ class CheckService {
|
||||
}
|
||||
}
|
||||
|
||||
const updatedChecks = await this.db.ackAllChecks(monitorId, teamId, ack, path);
|
||||
const updatedChecks = await this.db.checkModule.ackAllChecks(monitorId, teamId, ack, path);
|
||||
return updatedChecks;
|
||||
};
|
||||
|
||||
@@ -118,7 +118,7 @@ class CheckService {
|
||||
throw this.errorService.createBadRequestError("No team ID in request");
|
||||
}
|
||||
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
const monitor = await this.db.monitorModule.getMonitorById(monitorId);
|
||||
|
||||
if (!monitor) {
|
||||
throw this.errorService.createNotFoundError("Monitor not found");
|
||||
@@ -128,7 +128,7 @@ class CheckService {
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
|
||||
const deletedCount = await this.db.deleteChecks(monitorId);
|
||||
const deletedCount = await this.db.checkModule.deleteChecks(monitorId);
|
||||
return deletedCount;
|
||||
};
|
||||
deleteChecksByTeamId = async ({ teamId }) => {
|
||||
@@ -136,14 +136,14 @@ class CheckService {
|
||||
throw this.errorService.createBadRequestError("No team ID in request");
|
||||
}
|
||||
|
||||
const deletedCount = await this.db.deleteChecksByTeamId(teamId);
|
||||
const deletedCount = await this.db.checkModule.deleteChecksByTeamId(teamId);
|
||||
return deletedCount;
|
||||
};
|
||||
|
||||
updateChecksTTL = async ({ teamId, ttl }) => {
|
||||
const SECONDS_PER_DAY = 86400;
|
||||
const newTTL = parseInt(ttl, 10) * SECONDS_PER_DAY;
|
||||
await this.db.updateChecksTTL(teamId, newTTL);
|
||||
await this.db.checkModule.updateChecksTTL(teamId, newTTL);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ class InviteService {
|
||||
|
||||
getInviteToken = async ({ invite, teamId }) => {
|
||||
invite.teamId = teamId;
|
||||
const inviteToken = await this.db.requestInviteToken(invite);
|
||||
const inviteToken = await this.db.inviteModule.requestInviteToken(invite);
|
||||
return inviteToken;
|
||||
};
|
||||
|
||||
sendInviteEmail = async ({ inviteRequest, firstName }) => {
|
||||
const inviteToken = await this.db.requestInviteToken({ ...inviteRequest });
|
||||
const inviteToken = await this.db.inviteModule.requestInviteToken({ ...inviteRequest });
|
||||
const { clientHost } = this.settingsService.getSettings();
|
||||
|
||||
const html = await this.emailService.buildEmail("employeeActivationTemplate", {
|
||||
@@ -36,7 +36,7 @@ class InviteService {
|
||||
};
|
||||
|
||||
verifyInviteToken = async ({ inviteToken }) => {
|
||||
const invite = await this.db.getInviteToken(inviteToken);
|
||||
const invite = await this.db.inviteModule.getInviteToken(inviteToken);
|
||||
return invite;
|
||||
};
|
||||
}
|
||||
|
||||