diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 64d8d8ef1..402f5b15e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,4 +1,4 @@ -**(when sending your PR, only remove this line. Do not remove any lines below. Check all the relevant items before sending your PR)** +**(Please remove this line only before submitting your PR. Ensure that all relevant items are checked before submission.)** ## Describe your changes @@ -8,10 +8,10 @@ Briefly describe the changes you made and their purpose. Mention the issue number(s) this PR addresses (e.g., #123). -## Please ensure all items are checked off before requesting a review: +## Please ensure all items are checked off before requesting a review. "Checked off" means you need to add an "x" character between brackets so they turn into checkmarks. -- [ ] I deployed the application locally. -- [ ] I have performed a self-review and testing of my code. +- [ ] (Do not skip this or your PR will be closed) I deployed the application locally. +- [ ] (Do not skip this or your PR will be closed) I have performed a self-review and testing of my code. - [ ] I have included the issue # in the PR. - [ ] I have labelled the PR correctly. - [ ] The issue I am working on is assigned to me. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fa2ccf3f9..d96c99d0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,9 @@ -# Contributing to Uptime Manager application +# Contributing to Checkmate First off, thanks for taking the time to contribute! ❤️ -All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how the Uptime Manager project handles them. Please make sure to read the relevant section before making your contribution. It will make it much easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 +All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how the Checkmate project handles them. Please make sure to read the relevant section before making your contribution. It will make it much easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 > And if you like the project, but don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: > - Star the project @@ -77,7 +77,7 @@ Once it's filed: This section guides you through submitting an enhancement suggestion for the application, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community understand your enhancements and find related suggestions. - Make sure that you are using the latest version. -- Make sure you run the code locally. The Uptime Manager [documentation](https://bluewavelabs.gitbook.io/uptime-manager) covers it. +- Make sure you run the code locally. The Checkmate [documentation](https://bluewavelabs.gitbook.io/checkmate) covers it. - Find out if the functionality is already covered, maybe by an individual configuration. - Perform a [search](/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. diff --git a/Client/package-lock.json b/Client/package-lock.json index f58aa9d46..abbc57323 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -15,8 +15,8 @@ "@mui/lab": "6.0.0-beta.24", "@mui/material": "6.4.1", "@mui/x-charts": "^7.5.1", - "@mui/x-data-grid": "7.24.0", - "@mui/x-date-pickers": "7.24.0", + "@mui/x-data-grid": "7.24.1", + "@mui/x-date-pickers": "7.24.1", "@reduxjs/toolkit": "2.5.0", "axios": "^1.7.4", "dayjs": "1.11.13", @@ -1390,15 +1390,15 @@ "license": "MIT" }, "node_modules/@mui/x-charts": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.24.0.tgz", - "integrity": "sha512-X/zN0GxPHJC24QckFrtEbIK0HYoGwZgpNC5CV9cvRGvHphDFuNZk9xg9fJwIg1i3PPZHXRO3E+L/ee6fJ9ufhw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.24.1.tgz", + "integrity": "sha512-OdTS/nXaANPe4AoUFIDD4LlID8kK/00q+uqVOCkVClEvFQeAkj3pBaghdS4hY7rVqsCgsm+yOStQVJa9G2MR+Q==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", "@mui/x-charts-vendor": "7.20.0", - "@mui/x-internals": "7.24.0", + "@mui/x-internals": "7.24.1", "@react-spring/rafz": "^9.7.5", "@react-spring/web": "^9.7.5", "clsx": "^2.1.1", @@ -1448,14 +1448,14 @@ } }, "node_modules/@mui/x-data-grid": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.24.0.tgz", - "integrity": "sha512-goYTKDp+e+dXw7E+WndWUhWXTjX3aTqN8W2dCKhXnmE9Gu8dFwG6Azl7GK9l2m5YHGuqYmpWqcSG9etLdwYaVg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.24.1.tgz", + "integrity": "sha512-4sYTbMwsDotuTd2Cwa2JGTPXPWQs8RGJvocAKnIsNOzNdZNMrikE//HO35snriK8s4dauAApY7RVbeisjpVT+A==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.24.0", + "@mui/x-internals": "7.24.1", "clsx": "^2.1.1", "prop-types": "^15.8.1", "reselect": "^5.1.1" @@ -1485,14 +1485,14 @@ } }, "node_modules/@mui/x-date-pickers": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.24.0.tgz", - "integrity": "sha512-oBM9Yp2H3tJ7qoHB4APQJYxZG4rz6JD4CwLzbzD9o3r+E1HGpGSLhwK3rDEz9VEjbOq8893Z2TGYLLWoyjeFXQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.24.1.tgz", + "integrity": "sha512-ykQugMQHuQKBk3kViW/r0PpubtHQOlrd54bgbdafgTMCeM2VpXvv4zimzOu5IGnM6wEN8hupC7EXZbkrT6x46w==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.24.0", + "@mui/x-internals": "7.24.1", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -1551,9 +1551,9 @@ } }, "node_modules/@mui/x-internals": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.24.0.tgz", - "integrity": "sha512-lYa/XLltxNMY8YAFDopIHrXda2EAoqMCilyGMuPMz+WTG+b+StlUKqtj8cgFPQ/sa5dQ2fR7R3KJdjLREKUrlQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.24.1.tgz", + "integrity": "sha512-9BvJzpLJnS9BDphvkiv6v0QOLxbnu8jhwcexFjtCQ2ZyxtVuVsWzGZ2npT9sGOil7+eaFDmWnJtea/tgrPvSwQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", diff --git a/Client/package.json b/Client/package.json index 247e1fe72..006bdd081 100644 --- a/Client/package.json +++ b/Client/package.json @@ -18,8 +18,8 @@ "@mui/lab": "6.0.0-beta.24", "@mui/material": "6.4.1", "@mui/x-charts": "^7.5.1", - "@mui/x-data-grid": "7.24.0", - "@mui/x-date-pickers": "7.24.0", + "@mui/x-data-grid": "7.24.1", + "@mui/x-date-pickers": "7.24.1", "@reduxjs/toolkit": "2.5.0", "axios": "^1.7.4", "dayjs": "1.11.13", diff --git a/Client/src/Components/HttpStatusLabel/index.jsx b/Client/src/Components/HttpStatusLabel/index.jsx index ca9f70fe8..52676ae7c 100644 --- a/Client/src/Components/HttpStatusLabel/index.jsx +++ b/Client/src/Components/HttpStatusLabel/index.jsx @@ -16,7 +16,7 @@ import { BaseLabel } from "../Label"; const DEFAULT_CODE = 9999; // Default code for unknown status const handleStatusCode = (status) => { - if (status >= 100 && status < 600) { + if (status) { return status; } return DEFAULT_CODE; diff --git a/Client/src/Components/Inputs/Search/index.jsx b/Client/src/Components/Inputs/Search/index.jsx index bbc1ab614..1dff6b532 100644 --- a/Client/src/Components/Inputs/Search/index.jsx +++ b/Client/src/Components/Inputs/Search/index.jsx @@ -104,6 +104,14 @@ const Search = ({ "& .MuiOutlinedInput-root": { paddingY: 0, }, + "& .MuiAutocomplete-tag": { + // CAIO_REVIEW + color: theme.palette.primary.contrastText, + backgroundColor: theme.palette.primary.lowContrast, + }, + "& .MuiChip-deleteIcon": { + color: theme.palette.primary.contrastText, // CAIO_REVIEW + }, }} /> {error && ( @@ -165,6 +173,7 @@ const Search = ({ "& .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true'], & .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true'].Mui-focused, & .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true']:hover": { backgroundColor: theme.palette.primary.lowContrast, + color: "red", }, "& li.MuiAutocomplete-option:hover:not([aria-selected='true'])": { color: theme.palette.primary.main, diff --git a/Client/src/Components/Table/TablePagination/index.jsx b/Client/src/Components/Table/TablePagination/index.jsx index 66707494a..61e89232f 100644 --- a/Client/src/Components/Table/TablePagination/index.jsx +++ b/Client/src/Components/Table/TablePagination/index.jsx @@ -5,11 +5,11 @@ import { TablePaginationActions } from "./Actions"; import SelectorVertical from "../../../assets/icons/selector-vertical.svg?react"; Pagination.propTypes = { - monitorCount: PropTypes.number.isRequired, // Total number of items for pagination. + itemCount: PropTypes.number.isRequired, // Total number of items for pagination. page: PropTypes.number.isRequired, // Current page index. rowsPerPage: PropTypes.number.isRequired, // Number of rows displayed per page. handleChangePage: PropTypes.func.isRequired, // Function to handle page changes. - handleChangeRowsPerPage: PropTypes.func.isRequired, // Function to handle changes in rows per page. + handleChangeRowsPerPage: PropTypes.func, // Function to handle changes in rows per page. }; const ROWS_PER_PAGE_OPTIONS = [5, 10, 15, 25]; @@ -18,6 +18,7 @@ const ROWS_PER_PAGE_OPTIONS = [5, 10, 15, 25]; * Pagination component for table navigation with customized styling and behavior. * * @param {object} props - Component properties. + * @param {string} props.paginationLabel - Label for the pagination. * @param {number} props.monitorCount - Total number of monitors to paginate. * @param {number} props.page - Current page index (0-based). * @param {number} props.rowsPerPage - Number of rows to display per page. @@ -26,7 +27,8 @@ const ROWS_PER_PAGE_OPTIONS = [5, 10, 15, 25]; * @returns {JSX.Element} The Pagination component. */ function Pagination({ - monitorCount, + paginationLabel, + itemCount, page, rowsPerPage, handleChangePage, @@ -35,7 +37,7 @@ function Pagination({ const theme = useTheme(); const start = page * rowsPerPage + 1; - const end = Math.min(page * rowsPerPage + rowsPerPage, monitorCount); + const end = Math.min(page * rowsPerPage + rowsPerPage, itemCount); const range = `${start} - ${end}`; return ( @@ -51,11 +53,11 @@ function Pagination({ variant="body2" sx={{ opacity: 0.7 }} > - Showing {range} of {monitorCount} monitor(s) + Showing {range} of {itemCount} {paginationLabel} { ) : ( data.map((row) => { + const key = row.id || row._id || Math.random(); return ( config?.onRowClick(row)} + onClick={config?.onRowClick ? () => config.onRowClick(row) : null} > {headers.map((header, index) => { return ( diff --git a/Client/src/Pages/Incidents/IncidentTable/index.jsx b/Client/src/Pages/Incidents/IncidentTable/index.jsx index b89246cb8..ae9a3ae2a 100644 --- a/Client/src/Pages/Incidents/IncidentTable/index.jsx +++ b/Client/src/Pages/Incidents/IncidentTable/index.jsx @@ -1,8 +1,6 @@ import PropTypes from "prop-types"; -import { Pagination, PaginationItem, Typography, Box } from "@mui/material"; +import { Typography, Box } from "@mui/material"; -import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded"; -import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded"; import { useState, useEffect } from "react"; import { useSelector } from "react-redux"; import { networkService } from "../../../main"; @@ -16,7 +14,9 @@ import { HttpStatusLabel } from "../../../Components/HttpStatusLabel"; import { Empty } from "./Empty/Empty"; import { IncidentSkeleton } from "./Skeleton/Skeleton"; import DataTable from "../../../Components/Table"; -const IncidentTable = ({ monitors, selectedMonitor, filter }) => { +import Pagination from "../../../Components/Table/TablePagination"; + +const IncidentTable = ({ monitors, selectedMonitor, filter, dateRange }) => { const uiTimezone = useSelector((state) => state.ui.timezone); const theme = useTheme(); @@ -24,19 +24,10 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => { const mode = useSelector((state) => state.ui.mode); const [checks, setChecks] = useState([]); const [checksCount, setChecksCount] = useState(0); - const [paginationController, setPaginationController] = useState({ - page: 0, - rowsPerPage: 14, - }); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); const [isLoading, setIsLoading] = useState(false); - useEffect(() => { - setPaginationController((prevPaginationController) => ({ - ...prevPaginationController, - page: 0, - })); - }, [filter, selectedMonitor]); - useEffect(() => { const fetchPage = async () => { if (!monitors || Object.keys(monitors).length === 0) { @@ -51,10 +42,10 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => { teamId: user.teamId, sortOrder: "desc", limit: null, - dateRange: null, + dateRange, filter: filter, - page: paginationController.page, - rowsPerPage: paginationController.rowsPerPage, + page: page, + rowsPerPage: rowsPerPage, }); } else { res = await networkService.getChecksByMonitor({ @@ -62,12 +53,13 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => { monitorId: selectedMonitor, sortOrder: "desc", limit: null, - dateRange: null, + dateRange, filter: filter, - page: paginationController.page, - rowsPerPage: paginationController.rowsPerPage, + page, + rowsPerPage, }); } + setChecks(res.data.data.checks); setChecksCount(res.data.data.checksCount); } catch (error) { @@ -77,21 +69,14 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => { } }; fetchPage(); - }, [ - authToken, - user, - monitors, - selectedMonitor, - filter, - paginationController.page, - paginationController.rowsPerPage, - ]); + }, [authToken, user, monitors, selectedMonitor, filter, page, rowsPerPage, dateRange]); const handlePageChange = (_, newPage) => { - setPaginationController({ - ...paginationController, - page: newPage - 1, // 0-indexed - }); + setPage(newPage); + }; + + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(event.target.value); }; const headers = [ @@ -134,28 +119,6 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => { { id: "message", content: "Message", render: (row) => row.message }, ]; - let paginationComponent = <>; - if (checksCount > paginationController.rowsPerPage) { - paginationComponent = ( - ( - - )} - sx={{ mt: "auto" }} - /> - ); - } - let sharedStyles = { border: 1, borderColor: theme.palette.primary.lowContrast, @@ -198,8 +161,14 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => { headers={headers} data={checks} /> - - {paginationComponent} + )} @@ -210,6 +179,7 @@ IncidentTable.propTypes = { monitors: PropTypes.object.isRequired, selectedMonitor: PropTypes.string.isRequired, filter: PropTypes.string.isRequired, + dateRange: PropTypes.string.isRequired, }; export default IncidentTable; diff --git a/Client/src/Pages/Incidents/index.css b/Client/src/Pages/Incidents/index.css deleted file mode 100644 index ddbb44e6d..000000000 --- a/Client/src/Pages/Incidents/index.css +++ /dev/null @@ -1,11 +0,0 @@ -.incidents h1.MuiTypography-root { - font-size: var(--env-var-font-size-large); - font-weight: 600; -} -.incidents button.MuiButtonBase-root, -.incidents p.MuiTypography-root { - font-size: var(--env-var-font-size-medium); -} -.incidents button.MuiButtonBase-root { - min-height: 34px; -} diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx index 86f5afc5f..8305ec672 100644 --- a/Client/src/Pages/Incidents/index.jsx +++ b/Client/src/Pages/Incidents/index.jsx @@ -8,7 +8,6 @@ import { useTheme } from "@emotion/react"; import Select from "../../Components/Inputs/Select"; import IncidentTable from "./IncidentTable"; import SkeletonLayout from "./skeleton"; -import "./index.css"; const Incidents = () => { const theme = useTheme(); @@ -21,6 +20,7 @@ const Incidents = () => { // TODO do something with these filters const [filter, setFilter] = useState("all"); + const [dateRange, setDateRange] = useState("hour"); useEffect(() => { const fetchMonitors = async () => { @@ -77,62 +77,132 @@ const Incidents = () => { <> - - Incidents for - - + + - - - + + + + + + - Cannot Resolve - - + Show: + + + + + + + + )} diff --git a/Client/src/Pages/Infrastructure/index.jsx b/Client/src/Pages/Infrastructure/index.jsx index 08636a05e..fbfae6a3c 100644 --- a/Client/src/Pages/Infrastructure/index.jsx +++ b/Client/src/Pages/Infrastructure/index.jsx @@ -13,7 +13,7 @@ import { Box, Button, IconButton, Stack } from "@mui/material"; import Breadcrumbs from "../../Components/Breadcrumbs"; import { StatusLabel } from "../../Components/Label"; import { Heading } from "../../Components/Heading"; -import { Pagination } from "../../Components/Table/TablePagination/index.jsx"; +import Pagination from "../../Components/Table/TablePagination/index.jsx"; // import { getInfrastructureMonitorsByTeamId } from "../../Features/InfrastructureMonitors/infrastructureMonitorsSlice"; import { networkService } from "../../Utils/NetworkService.js"; import CustomGauge from "../../Components/Charts/CustomGauge/index.jsx"; @@ -150,7 +150,7 @@ function Infrastructure() { const monitorsAsRows = monitors.map((monitor) => { const processor = - ((monitor.checks[0]?.cpu?.usage_frequency ?? 0) / 1000).toFixed(2) + " GHz"; + ((monitor.checks[0]?.cpu?.frequency ?? 0) / 1000).toFixed(2) + " GHz"; const cpu = (monitor?.checks[0]?.cpu.usage_percent ?? 0) * 100; const mem = (monitor?.checks[0]?.memory.usage_percent ?? 0) * 100; const disk = (monitor?.checks[0]?.disk[0]?.usage_percent ?? 0) * 100; @@ -269,7 +269,8 @@ function Infrastructure() { /> { backgroundColor: theme.palette.accent.light, // Hover background }, "&.Mui-disabled": { - color: theme.palette.primary.lowContrast, // Disabled day color + color: theme.palette.primary.ContrastTextTertiary, // Disabled day color }, }, "& .MuiDayCalendar-weekDayLabel": { diff --git a/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx b/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx index 775d3d954..9bdce5887 100644 --- a/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx +++ b/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx @@ -1,34 +1,16 @@ import PropTypes from "prop-types"; -import { - TableContainer, - Table, - TableHead, - TableRow, - TableCell, - TableBody, - Paper, - Box, - TablePagination, - Stack, - Typography, - Button, -} from "@mui/material"; +import { Box } from "@mui/material"; +import DataTable from "../../../Components/Table"; +import Pagination from "../../../Components/Table/TablePagination"; import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded"; import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded"; import ActionsMenu from "./ActionsMenu"; -import { useState, useEffect, memo, useCallback, useRef } from "react"; +import { memo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { useTheme } from "@emotion/react"; -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 SelectorVertical from "../../../assets/icons/selector-vertical.svg?react"; import { formatDurationRounded } from "../../../Utils/timeUtils"; import { StatusLabel } from "../../../Components/Label"; import { setRowsPerPage } from "../../../Features/UI/uiSlice"; import dayjs from "dayjs"; - /** * Component for pagination actions (first, previous, next, last). * @@ -41,65 +23,6 @@ import dayjs from "dayjs"; * * @returns {JSX.Element} Pagination actions component. */ -const TablePaginationActions = (props) => { - const { count, page, rowsPerPage, onPageChange } = props; - const handleFirstPageButtonClick = (event) => { - onPageChange(event, 0); - }; - const handleBackButtonClick = (event) => { - onPageChange(event, page - 1); - }; - const handleNextButtonClick = (event) => { - onPageChange(event, page + 1); - }; - const handleLastPageButtonClick = (event) => { - onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); - }; - - return ( - - - - - - - ); -}; - -TablePaginationActions.propTypes = { - count: PropTypes.number.isRequired, - page: PropTypes.number.isRequired, - rowsPerPage: PropTypes.number.isRequired, - onPageChange: PropTypes.func.isRequired, -}; const MaintenanceTable = ({ page, @@ -111,13 +34,102 @@ const MaintenanceTable = ({ updateCallback, }) => { const { rowsPerPage } = useSelector((state) => state.ui.maintenance); - const theme = useTheme(); + console.log(rowsPerPage); const dispatch = useDispatch(); const handleChangePage = (event, newPage) => { setPage(newPage); }; + const handleChangeRowsPerPage = (event) => { + dispatch( + setRowsPerPage({ + value: parseInt(event.target.value, 10), + table: "maintenance", + }) + ); + setPage(0); + }; + + const headers = [ + { + id: "name", + content: ( + handleSort("name")}> + Maintenance Window Name + + {sort.order === "asc" ? ( + + ) : ( + + )} + + + ), + render: (row) => row.name, + }, + { + id: "status", + content: ( + handleSort("status")}> + {" "} + Status + + {sort.order === "asc" ? ( + + ) : ( + + )} + + + ), + render: (row) => { + const status = row.active ? "up" : "paused"; + const text = row.active ? "active" : "paused"; + + return ( + + ); + }, + }, + { + id: "nextWindow", + content: "Next window", + render: (row) => { + return getTimeToNextWindow(row.start, row.end, row.repeat); + }, + }, + { + id: "repeat", + content: "Repeat", + render: (row) => { + return row.repeat === 0 ? "N/A" : formatDurationRounded(row.repeat); + }, + }, + { + id: "actions", + content: "Actions", + render: (row) => ( + + ), + }, + ]; + const getTimeToNextWindow = (startTime, endTime, repeat) => { //1. Advance time closest to next window as possible const now = dayjs(); @@ -153,26 +165,6 @@ const MaintenanceTable = ({ } }; - const handleChangeRowsPerPage = (event) => { - dispatch( - setRowsPerPage({ - value: parseInt(event.target.value, 10), - table: "maintenance", - }) - ); - setPage(0); - }; - - /** - * Helper function to calculate the range of displayed rows. - * @returns {string} - */ - const getRange = () => { - let start = page * rowsPerPage + 1; - let end = Math.min(page * rowsPerPage + rowsPerPage, maintenanceWindowCount); - return `${start} - ${end}`; - }; - const handleSort = async (field) => { let order = ""; if (sort.field !== field) { @@ -183,165 +175,21 @@ const MaintenanceTable = ({ setSort({ field, order }); }; + console.log(handleChangePage); + return ( <> - - - - - handleSort("name")} - > - - Maintenance Window Name - - {sort.order === "asc" ? ( - - ) : ( - - )} - - - - handleSort("status")} - > - {" "} - - {" "} - Status - - {sort.order === "asc" ? ( - - ) : ( - - )} - - - - Next Window - Repeat - Actions - - - - {maintenanceWindows.map((maintenanceWindow) => { - const text = maintenanceWindow.active ? "active" : "paused"; - const status = maintenanceWindow.active ? "up" : "paused"; - return ( - - {maintenanceWindow.name} - - - - - {getTimeToNextWindow( - maintenanceWindow.start, - maintenanceWindow.end, - maintenanceWindow.repeat - )} - - - {maintenanceWindow.repeat === 0 - ? "N/A" - : formatDurationRounded(maintenanceWindow.repeat)} - - - - - - ); - })} - -
-
- - - Showing {getRange()} of {maintenanceWindowCount} maintenance window(s) - - - `Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}` - } - slotProps={{ - select: { - MenuProps: { - keepMounted: true, - disableScrollLock: true, - PaperProps: { - className: "pagination-dropdown", - sx: { - mt: 0, - mb: theme.spacing(2), - }, - }, - transformOrigin: { vertical: "bottom", horizontal: "left" }, - anchorOrigin: { vertical: "top", horizontal: "left" }, - sx: { mt: theme.spacing(-2) }, - }, - inputProps: { id: "pagination-dropdown" }, - IconComponent: SelectorVertical, - sx: { - ml: theme.spacing(4), - mr: theme.spacing(12), - minWidth: theme.spacing(20), - textAlign: "left", - "&.Mui-focused > div": { - backgroundColor: theme.palette.primary.main, - }, - }, - }, - }} - sx={{ - mt: theme.spacing(6), - color: theme.palette.primary.contrastTextSecondary, - "& svg path": { - stroke: theme.palette.primary.contrastTextTertiary, - strokeWidth: 1.3, - }, - "& .MuiSelect-select": { - border: 1, - borderColor: theme.palette.primary.lowContrast, - borderRadius: theme.shape.borderRadius, - }, - }} - /> - + + ); }; diff --git a/Client/src/Pages/Uptime/CreateUptime/index.jsx b/Client/src/Pages/Uptime/CreateUptime/index.jsx index afd7d93fa..762d8e764 100644 --- a/Client/src/Pages/Uptime/CreateUptime/index.jsx +++ b/Client/src/Pages/Uptime/CreateUptime/index.jsx @@ -108,16 +108,16 @@ const CreateMonitor = () => { } if (monitor.type === "http") { - const checkEndpointAction = await dispatch( - checkEndpointResolution({ authToken, monitorURL: form.url }) - ); - if (checkEndpointAction.meta.requestStatus === "rejected") { - createToast({ - body: "The endpoint you entered doesn't resolve. Check the URL again.", - }); - setErrors({ url: "The entered URL is not reachable." }); - return; - } + // const checkEndpointAction = await dispatch( + // checkEndpointResolution({ authToken, monitorURL: form.url }) + // ); + // if (checkEndpointAction.meta.requestStatus === "rejected") { + // createToast({ + // body: "The endpoint you entered doesn't resolve. Check the URL again.", + // }); + // setErrors({ url: "The entered URL is not reachable." }); + // return; + // } } form = { diff --git a/Client/src/Pages/Uptime/Home/index.jsx b/Client/src/Pages/Uptime/Home/index.jsx index df59185fb..446702ebb 100644 --- a/Client/src/Pages/Uptime/Home/index.jsx +++ b/Client/src/Pages/Uptime/Home/index.jsx @@ -5,7 +5,7 @@ import SkeletonLayout from "./skeleton"; import Fallback from "./fallback"; import StatusBox from "./StatusBox"; import UptimeDataTable from "./UptimeDataTable"; -import { Pagination } from "../../../Components/Table/TablePagination"; +import Pagination from "../../../Components/Table/TablePagination"; // Utils import { useTheme } from "@emotion/react"; @@ -225,7 +225,8 @@ const UptimeMonitors = () => { triggerUpdate={triggerUpdate} /> = 224.0.0.0 diff --git a/Server/controllers/checkController.js b/Server/controllers/checkController.js index 8988df6d8..ae6b96dbd 100644 --- a/Server/controllers/checkController.js +++ b/Server/controllers/checkController.js @@ -44,7 +44,7 @@ class CheckController { } }; - getChecks = async (req, res, next) => { + getChecksByMonitor = async (req, res, next) => { try { await getChecksParamValidation.validateAsync(req.params); await getChecksQueryValidation.validateAsync(req.query); @@ -54,19 +54,18 @@ class CheckController { } try { - const checks = await this.db.getChecks(req); - const checksCount = await this.db.getChecksCount(req); + const result = await this.db.getChecksByMonitor(req); return res.success({ msg: successMessages.CHECK_GET, - data: { checksCount, checks }, + data: result, }); } catch (error) { next(handleError(error, SERVICE_NAME, "getChecks")); } }; - getTeamChecks = async (req, res, next) => { + getChecksByTeam = async (req, res, next) => { try { await getTeamChecksParamValidation.validateAsync(req.params); await getTeamChecksQueryValidation.validateAsync(req.query); @@ -75,7 +74,7 @@ class CheckController { return; } try { - const checkData = await this.db.getTeamChecks(req); + const checkData = await this.db.getChecksByTeam(req); return res.success({ msg: successMessages.CHECK_GET, diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index 097172854..86c0fb840 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -20,7 +20,7 @@ import { getTokenFromHeaders } from "../utils/utils.js"; import logger from "../utils/logger.js"; import { handleError, handleValidationError } from "./controllerUtils.js"; import axios from "axios"; - +import seedDb from "../db/mongo/utils/seedDb.js"; const SERVICE_NAME = "monitorController"; class MonitorController { @@ -495,6 +495,17 @@ class MonitorController { next(handleError(error, SERVICE_NAME, "getMonitorsByTeamId")); } }; + + seedDb = async (req, res, next) => { + try { + const token = getTokenFromHeaders(req.headers); + const { jwtSecret } = this.settingsService.getSettings(); + const { _id, teamId } = jwt.verify(token, jwtSecret); + await seedDb(_id, teamId); + } catch (error) { + next(handleError(error, SERVICE_NAME, "seedDb")); + } + }; } export default MonitorController; diff --git a/Server/db/models/Check.js b/Server/db/models/Check.js index 1946f2c59..158749986 100644 --- a/Server/db/models/Check.js +++ b/Server/db/models/Check.js @@ -12,6 +12,13 @@ const BaseCheckSchema = mongoose.Schema({ immutable: true, index: true, }, + + teamId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Team", + immutable: true, + index: true, + }, /** * Status of the check (true for up, false for down). * @@ -69,6 +76,8 @@ const CheckSchema = mongoose.Schema({ ...BaseCheckSchema.obj }, { timestamps: tr CheckSchema.index({ createdAt: 1 }); CheckSchema.index({ monitorId: 1, createdAt: 1 }); CheckSchema.index({ monitorId: 1, createdAt: -1 }); +CheckSchema.index({ teamId: 1, createdAt: -1 }); +CheckSchema.index({ teamId: 1 }); export default mongoose.model("Check", CheckSchema); export { BaseCheckSchema }; diff --git a/Server/db/mongo/modules/checkModule.js b/Server/db/mongo/modules/checkModule.js index 0a552012d..7965e7c30 100644 --- a/Server/db/mongo/modules/checkModule.js +++ b/Server/db/mongo/modules/checkModule.js @@ -2,11 +2,15 @@ import Check from "../../models/Check.js"; import Monitor from "../../models/Monitor.js"; import User from "../../models/User.js"; import logger from "../../../utils/logger.js"; +import { ObjectId } from "mongodb"; + const SERVICE_NAME = "checkModule"; const dateRangeLookup = { + hour: new Date(new Date().setHours(new Date().getHours() - 1)), day: new Date(new Date().setDate(new Date().getDate() - 1)), week: new Date(new Date().setDate(new Date().getDate() - 7)), month: new Date(new Date().setMonth(new Date().getMonth() - 1)), + all: undefined, }; /** @@ -56,41 +60,6 @@ const createCheck = async (checkData) => { } }; -const getChecksCount = async (req) => { - const monitorId = req.params.monitorId; - const dateRange = req.query.dateRange; - const filter = req.query.filter; - // Build query - const checksQuery = { monitorId: monitorId }; - // Filter checks by "day", "week", or "month" - if (dateRange !== undefined) { - checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] }; - } - - if (filter !== undefined) { - checksQuery.status = false; - switch (filter) { - case "all": - break; - case "down": - break; - case "resolve": - checksQuery.statusCode = 5000; - break; - default: - logger.warn({ - message: "invalid filter", - service: SERVICE_NAME, - method: "getChecksCount", - }); - break; - } - } - - const count = await Check.countDocuments(checksQuery); - return count; -}; - /** * Get all checks for a monitor * @async @@ -98,33 +67,31 @@ const getChecksCount = async (req) => { * @returns {Promise>} * @throws {Error} */ - -const getChecks = async (req) => { +const getChecksByMonitor = async (req) => { try { const { monitorId } = req.params; - let { sortOrder, limit, dateRange, filter, page, rowsPerPage } = req.query; - // Default limit to 0 if not provided - limit = limit === undefined ? 0 : limit; + let { sortOrder, dateRange, filter, page, rowsPerPage } = req.query; + page = parseInt(page); + rowsPerPage = parseInt(rowsPerPage); + // Match + const matchStage = { + monitorId: ObjectId.createFromHexString(monitorId), + status: false, + ...(dateRangeLookup[dateRange] && { + createdAt: { + $gte: dateRangeLookup[dateRange], + }, + }), + }; - // Default sort order is newest -> oldest - sortOrder = sortOrder === "asc" ? 1 : -1; - - // Build query - const checksQuery = { monitorId: monitorId }; - // Filter checks by "day", "week", or "month" - if (dateRange !== undefined) { - checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] }; - } - // Filter checks by status if (filter !== undefined) { - checksQuery.status = false; switch (filter) { case "all": break; case "down": break; case "resolve": - checksQuery.statusCode = 5000; + matchStage.statusCode = 5000; break; default: logger.warn({ @@ -136,16 +103,33 @@ const getChecks = async (req) => { } } - // Need to skip and limit here + //Sort + sortOrder = sortOrder === "asc" ? 1 : -1; + + // Pagination let skip = 0; if (page && rowsPerPage) { skip = page * rowsPerPage; } - const checks = await Check.find(checksQuery) - .skip(skip) - .limit(rowsPerPage) - .sort({ createdAt: sortOrder }); - return checks; + + const checks = await Check.aggregate([ + { $match: matchStage }, + { $sort: { createdAt: sortOrder } }, + { + $facet: { + summary: [{ $count: "checksCount" }], + checks: [{ $skip: skip }, { $limit: rowsPerPage }], + }, + }, + { + $project: { + checksCount: { $arrayElemAt: ["$summary.checksCount", 0] }, + checks: "$checks", + }, + }, + ]); + + return checks[0]; } catch (error) { error.service = SERVICE_NAME; error.method = "getChecks"; @@ -153,64 +137,71 @@ const getChecks = async (req) => { } }; -const getTeamChecks = async (req) => { +const getChecksByTeam = async (req) => { try { + let { sortOrder, dateRange, filter, page, rowsPerPage } = req.query; + page = parseInt(page); + rowsPerPage = parseInt(rowsPerPage); const { teamId } = req.params; - let { sortOrder, limit, dateRange, filter, page, rowsPerPage } = req.query; - - // Get monitorIDs - const userMonitors = await Monitor.find({ teamId: teamId }).select("_id"); - - //Build check query - // Default limit to 0 if not provided - limit = limit === undefined ? 0 : limit; - // Default sort order is newest -> oldest - sortOrder = sortOrder === "asc" ? 1 : -1; - - const checksQuery = { monitorId: { $in: userMonitors } }; - + const matchStage = { + teamId: ObjectId.createFromHexString(teamId), + status: false, + ...(dateRangeLookup[dateRange] && { + createdAt: { + $gte: dateRangeLookup[dateRange], + }, + }), + }; + // Add filter to match stage if (filter !== undefined) { - checksQuery.status = false; switch (filter) { case "all": break; case "down": - checksQuery.status = false; break; case "resolve": - checksQuery.statusCode = 5000; + matchStage.statusCode = 5000; break; default: logger.warn({ message: "invalid filter", service: SERVICE_NAME, - method: "getTeamChecks", + method: "getChecksByTeam", }); break; } } - if (dateRange !== undefined) { - checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] }; - } + sortOrder = sortOrder === "asc" ? 1 : -1; - // Skip and limit for pagination + // pagination let skip = 0; if (page && rowsPerPage) { skip = page * rowsPerPage; } - const checksCount = await Check.countDocuments(checksQuery); + const checks = await Check.aggregate([ + { $match: matchStage }, + { $sort: { createdAt: sortOrder } }, - const checks = await Check.find(checksQuery) - .skip(skip) - .limit(rowsPerPage) - .sort({ createdAt: sortOrder }) - .select(["monitorId", "status", "responseTime", "statusCode", "message"]); - return { checksCount, checks }; + { + $facet: { + summary: [{ $count: "checksCount" }], + checks: [{ $skip: skip }, { $limit: rowsPerPage }], + }, + }, + { + $project: { + checksCount: { $arrayElemAt: ["$summary.checksCount", 0] }, + checks: "$checks", + }, + }, + ]); + + return checks[0]; } catch (error) { error.service = SERVICE_NAME; - error.method = "getTeamChecks"; + error.method = "getChecksByTeam"; throw error; } }; @@ -298,9 +289,8 @@ const updateChecksTTL = async (teamId, ttl) => { export { createCheck, - getChecksCount, - getChecks, - getTeamChecks, + getChecksByMonitor, + getChecksByTeam, deleteChecks, deleteChecksByTeamId, updateChecksTTL, diff --git a/Server/db/mongo/utils/seedDb.js b/Server/db/mongo/utils/seedDb.js new file mode 100644 index 000000000..cd4abb8d5 --- /dev/null +++ b/Server/db/mongo/utils/seedDb.js @@ -0,0 +1,57 @@ +import Monitor from "../../models/Monitor.js"; +import Check from "../../models/Check.js"; + +const generateRandomUrl = () => { + const domains = ["example.com", "test.org", "demo.net", "sample.io", "mock.dev"]; + const paths = ["api", "status", "health", "ping", "check"]; + return `https://${domains[Math.floor(Math.random() * domains.length)]}/${paths[Math.floor(Math.random() * paths.length)]}`; +}; + +const generateChecks = (monitorId, teamId, count) => { + const checks = []; + const endTime = new Date(Date.now() - 10 * 60 * 1000); // 10 minutes ago + const startTime = new Date(endTime - count * 60 * 1000); // count minutes before endTime + + for (let i = 0; i < count; i++) { + const timestamp = new Date(startTime.getTime() + i * 60 * 1000); + const status = Math.random() > 0.05; // 95% chance of being up + + checks.push({ + monitorId, + teamId, + status, + responseTime: Math.floor(Math.random() * 1000), // Random response time between 0-1000ms + createdAt: timestamp, + updatedAt: timestamp, + }); + } + + return checks; +}; + +const seedDb = async (userId, teamId) => { + try { + console.log("Deleting all monitors and checks"); + await Monitor.deleteMany({}); + await Check.deleteMany({}); + console.log("Adding monitors"); + for (let i = 0; i < 300; i++) { + const monitor = await Monitor.create({ + name: `Monitor ${i}`, + url: generateRandomUrl(), + type: "http", + userId, + teamId, + interval: 60000, + active: false, + }); + console.log(`Adding monitor and checks for monitor ${i}`); + const checks = generateChecks(monitor._id, teamId, 10000); + await Check.insertMany(checks); + } + } catch (error) { + console.error(error); + } +}; + +export default seedDb; diff --git a/Server/package-lock.json b/Server/package-lock.json index 2659edbab..d5a5ea747 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "axios": "^1.7.2", "bcrypt": "5.1.1", - "bullmq": "5.35.0", + "bullmq": "5.37.0", "cors": "^2.8.5", "dockerode": "4.0.4", "dotenv": "^16.4.5", @@ -324,9 +324,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", + "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", "dev": true, "license": "MIT", "engines": { @@ -1748,9 +1748,9 @@ } }, "node_modules/bullmq": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.35.0.tgz", - "integrity": "sha512-4wOFPv5oVkcK5A11cwsk5Zzc7Hz7xFe2rHIQfsg0UmWjtwTj/nc7+vPjcMGML6jdl7WejuHXnMeop4uxxo/3WA==", + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.37.0.tgz", + "integrity": "sha512-h/wf979+9uROyYpB8oTE44Py6JERCluCSd+ZFpCZlPsYh+wxAkqrfHsHHHKBgsNJp9odWLIY4SG+280EXzXiCQ==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -3071,9 +3071,9 @@ } }, "node_modules/eslint": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", - "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", + "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", "dev": true, "license": "MIT", "dependencies": { @@ -3082,7 +3082,7 @@ "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.18.0", + "@eslint/js": "9.19.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -5848,9 +5848,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz", - "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", + "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", "license": "MIT-0", "engines": { "node": ">=6.0.0" diff --git a/Server/package.json b/Server/package.json index c4ef0958f..9d92a2010 100644 --- a/Server/package.json +++ b/Server/package.json @@ -18,7 +18,7 @@ "dependencies": { "axios": "^1.7.2", "bcrypt": "5.1.1", - "bullmq": "5.35.0", + "bullmq": "5.37.0", "cors": "^2.8.5", "dockerode": "4.0.4", "dotenv": "^16.4.5", diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js index 289e0bc2f..bab45e4d4 100644 --- a/Server/routes/checkRoute.js +++ b/Server/routes/checkRoute.js @@ -11,7 +11,7 @@ class CheckRoutes { } initRoutes() { - this.router.get("/:monitorId", this.checkController.getChecks); + this.router.get("/:monitorId", this.checkController.getChecksByMonitor); this.router.post( "/:monitorId", verifyOwnership(Monitor, "monitorId"), @@ -23,7 +23,7 @@ class CheckRoutes { this.checkController.deleteChecks ); - this.router.get("/team/:teamId", this.checkController.getTeamChecks); + this.router.get("/team/:teamId", this.checkController.getChecksByTeam); this.router.delete( "/team/:teamId", diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index 92b529417..964bbee7a 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -75,6 +75,8 @@ class MonitorRoutes { isAllowed(["admin", "superadmin"]), this.monitorController.addDemoMonitors ); + + this.router.post("/seed", isAllowed(["superadmin"]), this.monitorController.seedDb); } getRouter() { diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index 5a9ab335c..419785540 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -5,6 +5,7 @@ const QUEUE_LOOKUP = { hardware: "hardware", http: "uptime", ping: "uptime", + port: "uptime", docker: "uptime", pagespeed: "pagespeed", }; diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 03bd6fef9..54edf39f9 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -131,6 +131,7 @@ class NetworkService { const httpResponse = { monitorId: job.data._id, + teamId: job.data.teamId, type: job.data.type, responseTime, payload: response?.data, diff --git a/Server/service/statusService.js b/Server/service/statusService.js index 5138b73ce..cca443e9e 100644 --- a/Server/service/statusService.js +++ b/Server/service/statusService.js @@ -83,10 +83,11 @@ class StatusService { * @returns {Object} The check object. */ buildCheck = (networkResponse) => { - const { monitorId, type, status, responseTime, code, message, payload } = + const { monitorId, teamId, type, status, responseTime, code, message, payload } = networkResponse; const check = { monitorId, + teamId, status, statusCode: code, responseTime, diff --git a/Server/utils/demoMonitors.json b/Server/utils/demoMonitors.json index ffce7d226..3efea8d0e 100644 --- a/Server/utils/demoMonitors.json +++ b/Server/utils/demoMonitors.json @@ -1,1271 +1,22 @@ [ { - "name": "0to255", - "url": "https://0to255.com" + "name": "Google", + "url": "https://www.google.com" }, { - "name": "10015.io", - "url": "https://10015.io" + "name": "Facebook", + "url": "https://www.facebook.com" }, { - "name": "3DIcons", - "url": "https://3dicons.co" + "name": "Yahoo", + "url": "https://www.yahoo.com" }, { - "name": "About.me", - "url": "https://about.me" + "name": "Amazon", + "url": "https://www.amazon.com" }, { - "name": "Alias", - "url": "https://alias.co" - }, - { - "name": "All About Berlin", - "url": "https://allaboutberlin.com" - }, - { - "name": "All Acronyms", - "url": "https://allacronyms.com" - }, - { - "name": "All You Can Read ", - "url": "https://allyoucanread.com" - }, - { - "name": "AllTrails", - "url": "https://alltrails.com" - }, - { - "name": "Anotepad", - "url": "https://anotepad.com" - }, - { - "name": "AnswerSocrates", - "url": "https://answersocrates.com" - }, - { - "name": "AnswerThePublic ", - "url": "https://answerthepublic.com" - }, - { - "name": "Apollo ", - "url": "https://apollo.io" - }, - { - "name": "ArrayList", - "url": "https://arraylist.org" - }, - { - "name": "Ask Difference", - "url": "https://askdifference.com" - }, - { - "name": "Audd.io", - "url": "https://audd.io" - }, - { - "name": "Audiocheck", - "url": "https://audiocheck.net" - }, - { - "name": "Audionautix", - "url": "https://audionautix.com" - }, - { - "name": "Authentic Jobs", - "url": "https://authenticjobs.com" - }, - { - "name": "Behind the Name", - "url": "https://behindthename.com" - }, - { - "name": "Bilim Terimleri", - "url": "https://terimler.org" - }, - { - "name": "BitBof", - "url": "https://bitbof.com" - }, - { - "name": "Blank Page", - "url": "https://blank.page" - }, - { - "name": "Bonanza", - "url": "https://bonanza.com" - }, - { - "name": "BookCrossing", - "url": "https://bookcrossing.com" - }, - { - "name": "Browse AI", - "url": "https://browse.ai" - }, - { - "name": "Bubbl.us", - "url": "https://bubbl.us" - }, - { - "name": "Business Model Toolbox", - "url": "https://bmtoolbox.net" - }, - { - "name": "ByClickDownloader", - "url": "https://byclickdownloader.com" - }, - { - "name": "Calligraphr", - "url": "https://calligraphr.com" - }, - { - "name": "CertificateClaim", - "url": "https://certificateclaim.com" - }, - { - "name": "Chosic", - "url": "https://chosic.com" - }, - { - "name": "ClipDrop", - "url": "https://clipdrop.co" - }, - { - "name": "CloudConvert", - "url": "https://cloudconvert.com" - }, - { - "name": "CodingFont", - "url": "https://codingfont.com" - }, - { - "name": "Color Hunt", - "url": "https://colorhunt.co" - }, - { - "name": "ColorHexa", - "url": "https://colorhexa.com" - }, - { - "name": "Conversion-Tool", - "url": "https://conversion-tool.com" - }, - { - "name": "Cool Startup Jobs", - "url": "https://coolstartupjobs.com" - }, - { - "name": "Coroflot", - "url": "https://coroflot.com" - }, - { - "name": "Corrupt-a-File", - "url": "https://corrupt-a-file.net" - }, - { - "name": "Couchsurfing", - "url": "https://couchsurfing.com" - }, - { - "name": "Countries Been", - "url": "https://countriesbeen.com" - }, - { - "name": "Country Code", - "url": "https://countrycode.org" - }, - { - "name": "Creately", - "url": "https://creately.com" - }, - { - "name": "Creately ", - "url": "https://creately.com" - }, - { - "name": "Crossfade.io", - "url": "https://crossfade.io" - }, - { - "name": "Crunchbase", - "url": "https://crunchbase.com" - }, - { - "name": "CVmkr", - "url": "https://cvwizard.com" - }, - { - "name": "Daily Remote", - "url": "https://dailyremote.com" - }, - { - "name": "David Li", - "url": "https://david.li" - }, - { - "name": "DemandHunt", - "url": "https://demandhunt.com" - }, - { - "name": "Designify", - "url": "https://designify.com" - }, - { - "name": "Diff Checker", - "url": "https://diffchecker.com" - }, - { - "name": "DifferenceBetween.info", - "url": "https://differencebetween.info" - }, - { - "name": "Digital Glossary", - "url": "https://digital-glossary.com" - }, - { - "name": "Dimensions", - "url": "https://dimensions.com" - }, - { - "name": "Discoverify Music", - "url": "https://discoverifymusic.com" - }, - { - "name": "discu.eu", - "url": "https://discu.eu" - }, - { - "name": "Do It Yourself", - "url": "https://doityourself.com" - }, - { - "name": "draw.io", - "url": "https://drawio.com" - }, - { - "name": "Drumeo", - "url": "https://drumeo.com" - }, - { - "name": "Dummies", - "url": "https://dummies.com" - }, - { - "name": "Easel.ly", - "url": "https://easel.ly" - }, - { - "name": "Educalingo", - "url": "https://educalingo.com" - }, - { - "name": "Emoji Combos", - "url": "https://emojicombos.com" - }, - { - "name": "EquityBee", - "url": "https://equitybee.com" - }, - { - "name": "EquityZen", - "url": "https://equityzen.com" - }, - { - "name": "Escape Room Tips", - "url": "https://escaperoomtips.com" - }, - { - "name": "Every Noise", - "url": "https://everynoise.com" - }, - { - "name": "Every Time Zone", - "url": "https://everytimezone.com" - }, - { - "name": "Excalideck", - "url": "https://excalideck.com" - }, - { - "name": "Excalidraw", - "url": "https://excalidraw.com" - }, - { - "name": "Extract pics", - "url": "https://extract.pics" - }, - { - "name": "EZGIF", - "url": "https://ezgif.com" - }, - { - "name": "FactSlides", - "url": "https://factslides.com" - }, - { - "name": "FIGR ", - "url": "https://figr.app" - }, - { - "name": "Fine Dictionary", - "url": "https://finedictionary.com" - }, - { - "name": "Fiverr", - "url": "https://fiverr.com" - }, - { - "name": "Fix It Club", - "url": "https://fixitclub.com" - }, - { - "name": "Flightradar24", - "url": "https://flightradar24.com" - }, - { - "name": "FlowCV ", - "url": "https://flowcv.com" - }, - { - "name": "Font Squirrel", - "url": "https://fontsquirrel.com" - }, - { - "name": "FontAwesome", - "url": "https://fontawesome.com" - }, - { - "name": "Fontello ", - "url": "https://fontello.com" - }, - { - "name": "Form to Chatbot", - "url": "https://formtochatbot.com" - }, - { - "name": "Founder Resources", - "url": "https://founderresources.io" - }, - { - "name": "Franz", - "url": "https://meetfranz.com" - }, - { - "name": "Fraze It", - "url": "https://fraze.it" - }, - { - "name": "Freecycle", - "url": "https://freecycle.org" - }, - { - "name": "FreeType", - "url": "https://freetype.org" - }, - { - "name": "FutureM", - "url": "https://futureme.org" - }, - { - "name": "Generated.Photos", - "url": "https://generated.photos" - }, - { - "name": "Get Human", - "url": "https://gethuman.com" - }, - { - "name": "Go Bento", - "url": "https://gobento.com" - }, - { - "name": "Good CV", - "url": "https://goodcv.com" - }, - { - "name": "Grammar Monster", - "url": "https://grammar-monster.com" - }, - { - "name": "Grammar Book", - "url": "https://grammarbook.com" - }, - { - "name": "Gummy Search", - "url": "https://gummysearch.com" - }, - { - "name": "Gumroad", - "url": "https://gumroad.com" - }, - { - "name": "HealthIcons", - "url": "https://healthicons.org" - }, - { - "name": "HexColor", - "url": "https://hexcolor.co" - }, - { - "name": "Hidden Life Radio", - "url": "https://hiddenliferadio.com" - }, - { - "name": "Hired", - "url": "https://lhh.com" - }, - { - "name": "Honey", - "url": "https://joinhoney.com" - }, - { - "name": "HowStuffWorks", - "url": "https://howstuffworks.com" - }, - { - "name": "HugeIcons Pro", - "url": "https://hugeicons.com" - }, - { - "name": "Humble Bundle", - "url": "https://humblebundle.com" - }, - { - "name": "I Have No TV", - "url": "https://ihavenotv.com" - }, - { - "name": "I Miss the Office", - "url": "https://imisstheoffice.eu" - }, - { - "name": "IcoMoon", - "url": "https://icomoon.io" - }, - { - "name": "Iconfinder", - "url": "https://iconfinder.com" - }, - { - "name": "Icon Packs", - "url": "https://iconpacks.net" - }, - { - "name": "Iconshock", - "url": "https://iconshock.com" - }, - { - "name": "Iconz Design", - "url": "https://iconz.design" - }, - { - "name": "iFixit", - "url": "https://ifixit.com" - }, - { - "name": "IFTTT", - "url": "https://ifttt.com" - }, - { - "name": "Illlustrations", - "url": "https://illlustrations.co" - }, - { - "name": "Illustration Kit", - "url": "https://illustrationkit.com" - }, - { - "name": "IMSDB", - "url": "https://imsdb.com" - }, - { - "name": "Incompetech", - "url": "https://incompetech.com" - }, - { - "name": "Incredibox", - "url": "https://incredibox.com" - }, - { - "name": "InnerBod", - "url": "https://innerbody.com" - }, - { - "name": "Instructables", - "url": "https://instructables.com" - }, - { - "name": "Integromat", - "url": "https://make.com" - }, - { - "name": "Investopedia", - "url": "https://investopedia.com" - }, - { - "name": "Japanese Wiki Corpus", - "url": "https://japanesewiki.com" - }, - { - "name": "Jitter.Video", - "url": "https://jitter.video" - }, - { - "name": "Jobspresso", - "url": "https://jobspresso.co" - }, - { - "name": "JPEG-Optimizer", - "url": "https://jpeg-optimizer.com" - }, - { - "name": "JS Remotely", - "url": "https://jsremotely.com" - }, - { - "name": "JScreenFix", - "url": "https://jscreenfix.com" - }, - { - "name": "JSON Resume", - "url": "https://jsonresume.io" - }, - { - "name": "Just Join", - "url": "https://justjoin.it" - }, - { - "name": "Just the Recipe", - "url": "https://justtherecipe.com" - }, - { - "name": "JustRemote", - "url": "https://justremote.co" - }, - { - "name": "JustWatch", - "url": "https://justwatch.com" - }, - { - "name": "Kanopy", - "url": "https://kanopy.com" - }, - { - "name": "Kassellabs", - "url": "https://kassellabs.io" - }, - { - "name": "Key Differences", - "url": "https://keydifferences.com" - }, - { - "name": "Keybase", - "url": "https://keybase.io" - }, - { - "name": "KeyValues", - "url": "https://keyvalues.com" - }, - { - "name": "KHInsider", - "url": "https://khinsider.com" - }, - { - "name": "Killed by Google", - "url": "https://killedbygoogle.com" - }, - { - "name": "Kimovil", - "url": "https://kimovil.com" - }, - { - "name": "Lalal.ai", - "url": "https://www.lalal.ai" - }, - { - "name": "Learn Anything", - "url": "https://learn-anything.xyz" - }, - { - "name": "LendingTree", - "url": "https://lendingtree.com" - }, - { - "name": "Lightyear.fm", - "url": "https://lightyear.fm" - }, - { - "name": "LittleSis", - "url": "https://littlesis.org" - }, - { - "name": "Looria", - "url": "https://looria.com" - }, - { - "name": "Lucidchart", - "url": "https://lucidchart.com" - }, - { - "name": "Lunar", - "url": "https://lunar.fyi" - }, - { - "name": "Manuals Lib", - "url": "https://manualslib.com" - }, - { - "name": "Map Crunch", - "url": "https://mapcrunch.com" - }, - { - "name": "Masterworks", - "url": "https://masterworks.com" - }, - { - "name": "MediaFire", - "url": "https://mediafire.com" - }, - { - "name": "Mixlr", - "url": "https://mixlr.com" - }, - { - "name": "Moises AI", - "url": "https://moises.ai" - }, - { - "name": "Money", - "url": "https://money.com" - }, - { - "name": "Mountain Project", - "url": "https://mountainproject.com" - }, - { - "name": "Movie Map", - "url": "https://movie-map.com" - }, - { - "name": "Movie Sounds", - "url": "https://movie-sounds.org" - }, - { - "name": "MP3Cut", - "url": "https://mp3cut.net" - }, - { - "name": "Murmel", - "url": "https://murmel.social" - }, - { - "name": "Muscle Wiki", - "url": "https://musclewiki.com" - }, - { - "name": "Music-Map", - "url": "https://music-map.com" - }, - { - "name": "MusicTheory.net", - "url": "https://musictheory.net" - }, - { - "name": "MyFonts", - "url": "https://myfonts.com" - }, - { - "name": "MyFridgeFood", - "url": "https://myfridgefood.com" - }, - { - "name": "Nameberry", - "url": "https://nameberry.com" - }, - { - "name": "Namechk", - "url": "https://namechk.com" - }, - { - "name": "Ncase", - "url": "https://ncase.me" - }, - { - "name": "News in Levels", - "url": "https://newsinlevels.com" - }, - { - "name": "Noisli", - "url": "https://noisli.com" - }, - { - "name": "Notes.io", - "url": "https://notes.io" - }, - { - "name": "Novoresume", - "url": "https://novoresume.com" - }, - { - "name": "Ocoya", - "url": "https://ocoya.com" - }, - { - "name": "Old Computers Museum", - "url": "https://oldcomputers.net" - }, - { - "name": "Online Tone Generator", - "url": "https://onlinetonegenerator.com" - }, - { - "name": "Online-Convert", - "url": "https://online-convert.com" - }, - { - "name": "OnlineConversion", - "url": "https://onlineconversion.com" - }, - { - "name": "Online OCR", - "url": "https://onlineocr.net" - }, - { - "name": "OpenWeatherMap", - "url": "https://openweathermap.org" - }, - { - "name": "OrgPad", - "url": "https://orgpad.com" - }, - { - "name": "Passport Index", - "url": "https://passportindex.org" - }, - { - "name": "PDF Candy", - "url": "https://pdfcandy.com" - }, - { - "name": "PDF2DOC", - "url": "https://pdf2doc.com" - }, - { - "name": "PDFescape", - "url": "https://pdfescape.com" - }, - { - "name": "PfpMaker", - "url": "https://pfpmaker.com" - }, - { - "name": "PIDGI Wiki ", - "url": "https://pidgi.net" - }, - { - "name": "PimEyes", - "url": "https://pimeyes.com" - }, - { - "name": "Pipl ", - "url": "https://pipl.com" - }, - { - "name": "PixelBazaar", - "url": "https://pixelbazaar.com" - }, - { - "name": "PixelPaper", - "url": "https://pixelpaper.io" - }, - { - "name": "Ponly", - "url": "https://ponly.com" - }, - { - "name": "PowerToFly", - "url": "https://powertofly.com" - }, - { - "name": "Pretzel Rocks", - "url": "https://pretzel.rocks" - }, - { - "name": "PrintIt", - "url": "https://printit.work" - }, - { - "name": "Prismatext", - "url": "https://prismatext.com" - }, - { - "name": "Puffin Maps", - "url": "https://puffinmaps.com" - }, - { - "name": "Puzzle Loop ", - "url": "https://puzzle-loop.com" - }, - { - "name": "QuoteMaster", - "url": "https://quotemaster.org" - }, - { - "name": "Radio Garden", - "url": "https://radio.garden" - }, - { - "name": "Radiooooo", - "url": "https://radiooooo.com" - }, - { - "name": "Radiosondy", - "url": "https://radiosondy.info" - }, - { - "name": "Rainy Mood", - "url": "https://rainymood.com" - }, - { - "name": "Random Street View", - "url": "https://randomstreetview.com" - }, - { - "name": "Rap4Ever", - "url": "https://rap4all.com" - }, - { - "name": "RareFilm", - "url": "https://rarefilm.net" - }, - { - "name": "Rattibha", - "url": "https://rattibha.com" - }, - { - "name": "Reddit List ", - "url": "https://redditlist.com" - }, - { - "name": "RedditSearch.io", - "url": "https://redditsearch.io" - }, - { - "name": "Reelgood", - "url": "https://reelgood.com" - }, - { - "name": "Reface", - "url": "https://reface.ai" - }, - { - "name": "Rejected.us", - "url": "https://rejected.us" - }, - { - "name": "Relanote", - "url": "https://relanote.com" - }, - { - "name": "Remote Leaf", - "url": "https://remoteleaf.com" - }, - { - "name": "Remote OK", - "url": "https://remoteok.com" - }, - { - "name": "Remote Starter Kit ", - "url": "https://remotestarterkit.com" - }, - { - "name": "Remote.co", - "url": "https://remote.co" - }, - { - "name": "Remote Base ", - "url": "https://remotebase.com" - }, - { - "name": "Remote Bear", - "url": "https://remotebear.io" - }, - { - "name": "Remove.bg", - "url": "https://remove.bg" - }, - { - "name": "Respresso", - "url": "https://respresso.io" - }, - { - "name": "Reveddit", - "url": "https://reveddit.com" - }, - { - "name": "Rhymer", - "url": "https://rhymer.com" - }, - { - "name": "RhymeZone", - "url": "https://rhymezone.com" - }, - { - "name": "Ribbet", - "url": "https://ribbet.com" - }, - { - "name": "Roadmap.sh", - "url": "https://roadmap.sh" - }, - { - "name": "Roadtrippers", - "url": "https://roadtrippers.com" - }, - { - "name": "RxResu.me", - "url": "https://rxresu.me" - }, - { - "name": "SchemeColor", - "url": "https://schemecolor.com" - }, - { - "name": "Screenshot.Guru", - "url": "https://screenshot.guru" - }, - { - "name": "SeatGuru", - "url": "https://seatguru.com" - }, - { - "name": "Sessions", - "url": "https://sessions.us" - }, - { - "name": "Shottr", - "url": "https://shottr.cc" - }, - { - "name": "Signature Maker", - "url": "https://signature-maker.net" - }, - { - "name": "Skip The Drive", - "url": "https://skipthedrive.com" - }, - { - "name": "Slowby", - "url": "https://slowby.travel" - }, - { - "name": "Small World", - "url": "https://smallworld.kiwi" - }, - { - "name": "SmallPDF", - "url": "https://smallpdf.com" - }, - { - "name": "Social Image Maker", - "url": "https://socialimagemaker.io" - }, - { - "name": "Social Sizes", - "url": "https://socialsizes.io" - }, - { - "name": "SoundLove", - "url": "https://soundlove.se" - }, - { - "name": "Spline", - "url": "https://spline.design" - }, - { - "name": "Starkey Comics", - "url": "https://starkeycomics.com" - }, - - { - "name": "Statista", - "url": "https://statista.com" - }, - { - "name": "Stolen Camera Finder", - "url": "https://stolencamerafinder.com" - }, - { - "name": "Strobe.Cool", - "url": "https://strobe.cool" - }, - { - "name": "Sumo", - "url": "https://sumo.app" - }, - { - "name": "SuperMeme AI", - "url": "https://supermeme.ai" - }, - { - "name": "Synthesia", - "url": "https://synthesia.io" - }, - { - "name": "TablerIcons", - "url": "https://tablericons.com" - }, - { - "name": "Tango", - "url": "https://tango.us" - }, - { - "name": "TasteDive", - "url": "https://tastedive.com" - }, - { - "name": "TechSpecs", - "url": "https://techspecs.io" - }, - { - "name": "Teoria", - "url": "https://teoria.com" - }, - { - "name": "Text Faces", - "url": "https://textfac.es" - }, - { - "name": "The Balance Money", - "url": "https://thebalancemoney.com" - }, - { - "name": "The Punctuation Guide", - "url": "https://thepunctuationguide.com" - }, - { - "name": "This to That", - "url": "https://thistothat.com" - }, - { - "name": "This vs That", - "url": "https://thisvsthat.io" - }, - { - "name": "ThreadReaderApp ", - "url": "https://threadreaderapp.com" - }, - { - "name": "Thumbly", - "url": "https://tokee.ai" - }, - { - "name": "Tiii.me", - "url": "https://tiii.me" - }, - { - "name": "TikTok Video Downloader", - "url": "https://ttvdl.com" - }, - { - "name": "Time and Date", - "url": "https://timeanddate.com" - }, - { - "name": "Time.is", - "url": "https://time.is" - }, - { - "name": "Title Case", - "url": "https://titlecase.com" - }, - { - "name": "Toaster Central", - "url": "https://toastercentral.com" - }, - { - "name": "Tongue-Twister ", - "url": "https://tongue-twister.net" - }, - { - "name": "TradingView", - "url": "https://tradingview.com" - }, - { - "name": "Transparent Textures", - "url": "https://transparenttextures.com" - }, - { - "name": "Tubi TV", - "url": "https://tubitv.com" - }, - { - "name": "Tunefind", - "url": "https://tunefind.com" - }, - { - "name": "TuneMyMusic", - "url": "https://tunemymusic.com" - }, - { - "name": "Tweepsmap", - "url": "https://fedica.com" - }, - { - "name": "Two Peas and Their Pod", - "url": "https://twopeasandtheirpod.com" - }, - { - "name": "Typatone", - "url": "https://typatone.com" - }, - { - "name": "Under Glass", - "url": "https://underglass.io" - }, - { - "name": "UniCorner", - "url": "https://unicorner.news" - }, - { - "name": "Unita", - "url": "https://unita.co" - }, - { - "name": "UnitConverters", - "url": "https://unitconverters.net" - }, - { - "name": "Unreadit", - "url": "https://unreadit.com" - }, - { - "name": "Unscreen", - "url": "https://unscreen.com" - }, - { - "name": "UnTools ", - "url": "https://untools.co" - }, - { - "name": "Upwork", - "url": "https://upwork.com" - }, - { - "name": "UTF8 Icons", - "url": "https://utf8icons.com" - }, - { - "name": "Vector Magic", - "url": "https://vectormagic.com" - }, - { - "name": "Virtual Vacation", - "url": "https://virtualvacation.us" - }, - { - "name": "Virtual Vocations", - "url": "https://virtualvocations.com" - }, - { - "name": "Visiwig", - "url": "https://visiwig.com" - }, - { - "name": "Visual CV", - "url": "https://visualcv.com" - }, - { - "name": "Vocus.io", - "url": "https://vocus.io" - }, - { - "name": "Voscreen", - "url": "https://voscreen.com" - }, - { - "name": "Wanderprep", - "url": "https://wanderprep.com" - }, - { - "name": "Warmshowers", - "url": "https:/warmshowers.org" - }, - { - "name": "Watch Documentaries", - "url": "https://watchdocumentaries.com" - }, - { - "name": "We Work Remotely", - "url": "https://weworkremotely.com" - }, - { - "name": "Web2PDFConvert", - "url": "https://web2pdfconvert.com" - }, - { - "name": "Welcome to My Garden", - "url": "https://welcometomygarden.org" - }, - { - "name": "When2meet ", - "url": "https://when2meet.com" - }, - { - "name": "Where's George", - "url": "https://wheresgeorge.com" - }, - { - "name": "Where's Willy", - "url": "https://whereswilly.com" - }, - { - "name": "WikiHow", - "url": "https://wikihow.com" - }, - { - "name": "Windy", - "url": "https://www.windy.com" - }, - { - "name": "WonderHowTo", - "url": "https://wonderhowto.com" - }, - { - "name": "Working Nomads", - "url": "https://workingnomads.com" - }, - { - "name": "Wormhole", - "url": "https://wormhole.app" - }, - { - "name": "Y Combinator Jobs", - "url": "https://ycombinator.com" - }, - { - "name": "Yes Promo", - "url": "https://yespromo.me" - }, - { - "name": "YouGlish", - "url": "https://youglish.com" - }, - { - "name": "Zamzar", - "url": "https://zamzar.com" - }, - { - "name": "Zippyshare", - "url": "https://zippyshare.com" - }, - { - "name": "Zoom Earth", - "url": "https://zoom.earth" - }, - { - "name": "Zoom.it", - "url": "https://zoom.it" + "name": "Apple", + "url": "https://www.apple.com" } ] diff --git a/Server/utils/demoMonitorsOld.json b/Server/utils/demoMonitorsOld.json new file mode 100644 index 000000000..ffce7d226 --- /dev/null +++ b/Server/utils/demoMonitorsOld.json @@ -0,0 +1,1271 @@ +[ + { + "name": "0to255", + "url": "https://0to255.com" + }, + { + "name": "10015.io", + "url": "https://10015.io" + }, + { + "name": "3DIcons", + "url": "https://3dicons.co" + }, + { + "name": "About.me", + "url": "https://about.me" + }, + { + "name": "Alias", + "url": "https://alias.co" + }, + { + "name": "All About Berlin", + "url": "https://allaboutberlin.com" + }, + { + "name": "All Acronyms", + "url": "https://allacronyms.com" + }, + { + "name": "All You Can Read ", + "url": "https://allyoucanread.com" + }, + { + "name": "AllTrails", + "url": "https://alltrails.com" + }, + { + "name": "Anotepad", + "url": "https://anotepad.com" + }, + { + "name": "AnswerSocrates", + "url": "https://answersocrates.com" + }, + { + "name": "AnswerThePublic ", + "url": "https://answerthepublic.com" + }, + { + "name": "Apollo ", + "url": "https://apollo.io" + }, + { + "name": "ArrayList", + "url": "https://arraylist.org" + }, + { + "name": "Ask Difference", + "url": "https://askdifference.com" + }, + { + "name": "Audd.io", + "url": "https://audd.io" + }, + { + "name": "Audiocheck", + "url": "https://audiocheck.net" + }, + { + "name": "Audionautix", + "url": "https://audionautix.com" + }, + { + "name": "Authentic Jobs", + "url": "https://authenticjobs.com" + }, + { + "name": "Behind the Name", + "url": "https://behindthename.com" + }, + { + "name": "Bilim Terimleri", + "url": "https://terimler.org" + }, + { + "name": "BitBof", + "url": "https://bitbof.com" + }, + { + "name": "Blank Page", + "url": "https://blank.page" + }, + { + "name": "Bonanza", + "url": "https://bonanza.com" + }, + { + "name": "BookCrossing", + "url": "https://bookcrossing.com" + }, + { + "name": "Browse AI", + "url": "https://browse.ai" + }, + { + "name": "Bubbl.us", + "url": "https://bubbl.us" + }, + { + "name": "Business Model Toolbox", + "url": "https://bmtoolbox.net" + }, + { + "name": "ByClickDownloader", + "url": "https://byclickdownloader.com" + }, + { + "name": "Calligraphr", + "url": "https://calligraphr.com" + }, + { + "name": "CertificateClaim", + "url": "https://certificateclaim.com" + }, + { + "name": "Chosic", + "url": "https://chosic.com" + }, + { + "name": "ClipDrop", + "url": "https://clipdrop.co" + }, + { + "name": "CloudConvert", + "url": "https://cloudconvert.com" + }, + { + "name": "CodingFont", + "url": "https://codingfont.com" + }, + { + "name": "Color Hunt", + "url": "https://colorhunt.co" + }, + { + "name": "ColorHexa", + "url": "https://colorhexa.com" + }, + { + "name": "Conversion-Tool", + "url": "https://conversion-tool.com" + }, + { + "name": "Cool Startup Jobs", + "url": "https://coolstartupjobs.com" + }, + { + "name": "Coroflot", + "url": "https://coroflot.com" + }, + { + "name": "Corrupt-a-File", + "url": "https://corrupt-a-file.net" + }, + { + "name": "Couchsurfing", + "url": "https://couchsurfing.com" + }, + { + "name": "Countries Been", + "url": "https://countriesbeen.com" + }, + { + "name": "Country Code", + "url": "https://countrycode.org" + }, + { + "name": "Creately", + "url": "https://creately.com" + }, + { + "name": "Creately ", + "url": "https://creately.com" + }, + { + "name": "Crossfade.io", + "url": "https://crossfade.io" + }, + { + "name": "Crunchbase", + "url": "https://crunchbase.com" + }, + { + "name": "CVmkr", + "url": "https://cvwizard.com" + }, + { + "name": "Daily Remote", + "url": "https://dailyremote.com" + }, + { + "name": "David Li", + "url": "https://david.li" + }, + { + "name": "DemandHunt", + "url": "https://demandhunt.com" + }, + { + "name": "Designify", + "url": "https://designify.com" + }, + { + "name": "Diff Checker", + "url": "https://diffchecker.com" + }, + { + "name": "DifferenceBetween.info", + "url": "https://differencebetween.info" + }, + { + "name": "Digital Glossary", + "url": "https://digital-glossary.com" + }, + { + "name": "Dimensions", + "url": "https://dimensions.com" + }, + { + "name": "Discoverify Music", + "url": "https://discoverifymusic.com" + }, + { + "name": "discu.eu", + "url": "https://discu.eu" + }, + { + "name": "Do It Yourself", + "url": "https://doityourself.com" + }, + { + "name": "draw.io", + "url": "https://drawio.com" + }, + { + "name": "Drumeo", + "url": "https://drumeo.com" + }, + { + "name": "Dummies", + "url": "https://dummies.com" + }, + { + "name": "Easel.ly", + "url": "https://easel.ly" + }, + { + "name": "Educalingo", + "url": "https://educalingo.com" + }, + { + "name": "Emoji Combos", + "url": "https://emojicombos.com" + }, + { + "name": "EquityBee", + "url": "https://equitybee.com" + }, + { + "name": "EquityZen", + "url": "https://equityzen.com" + }, + { + "name": "Escape Room Tips", + "url": "https://escaperoomtips.com" + }, + { + "name": "Every Noise", + "url": "https://everynoise.com" + }, + { + "name": "Every Time Zone", + "url": "https://everytimezone.com" + }, + { + "name": "Excalideck", + "url": "https://excalideck.com" + }, + { + "name": "Excalidraw", + "url": "https://excalidraw.com" + }, + { + "name": "Extract pics", + "url": "https://extract.pics" + }, + { + "name": "EZGIF", + "url": "https://ezgif.com" + }, + { + "name": "FactSlides", + "url": "https://factslides.com" + }, + { + "name": "FIGR ", + "url": "https://figr.app" + }, + { + "name": "Fine Dictionary", + "url": "https://finedictionary.com" + }, + { + "name": "Fiverr", + "url": "https://fiverr.com" + }, + { + "name": "Fix It Club", + "url": "https://fixitclub.com" + }, + { + "name": "Flightradar24", + "url": "https://flightradar24.com" + }, + { + "name": "FlowCV ", + "url": "https://flowcv.com" + }, + { + "name": "Font Squirrel", + "url": "https://fontsquirrel.com" + }, + { + "name": "FontAwesome", + "url": "https://fontawesome.com" + }, + { + "name": "Fontello ", + "url": "https://fontello.com" + }, + { + "name": "Form to Chatbot", + "url": "https://formtochatbot.com" + }, + { + "name": "Founder Resources", + "url": "https://founderresources.io" + }, + { + "name": "Franz", + "url": "https://meetfranz.com" + }, + { + "name": "Fraze It", + "url": "https://fraze.it" + }, + { + "name": "Freecycle", + "url": "https://freecycle.org" + }, + { + "name": "FreeType", + "url": "https://freetype.org" + }, + { + "name": "FutureM", + "url": "https://futureme.org" + }, + { + "name": "Generated.Photos", + "url": "https://generated.photos" + }, + { + "name": "Get Human", + "url": "https://gethuman.com" + }, + { + "name": "Go Bento", + "url": "https://gobento.com" + }, + { + "name": "Good CV", + "url": "https://goodcv.com" + }, + { + "name": "Grammar Monster", + "url": "https://grammar-monster.com" + }, + { + "name": "Grammar Book", + "url": "https://grammarbook.com" + }, + { + "name": "Gummy Search", + "url": "https://gummysearch.com" + }, + { + "name": "Gumroad", + "url": "https://gumroad.com" + }, + { + "name": "HealthIcons", + "url": "https://healthicons.org" + }, + { + "name": "HexColor", + "url": "https://hexcolor.co" + }, + { + "name": "Hidden Life Radio", + "url": "https://hiddenliferadio.com" + }, + { + "name": "Hired", + "url": "https://lhh.com" + }, + { + "name": "Honey", + "url": "https://joinhoney.com" + }, + { + "name": "HowStuffWorks", + "url": "https://howstuffworks.com" + }, + { + "name": "HugeIcons Pro", + "url": "https://hugeicons.com" + }, + { + "name": "Humble Bundle", + "url": "https://humblebundle.com" + }, + { + "name": "I Have No TV", + "url": "https://ihavenotv.com" + }, + { + "name": "I Miss the Office", + "url": "https://imisstheoffice.eu" + }, + { + "name": "IcoMoon", + "url": "https://icomoon.io" + }, + { + "name": "Iconfinder", + "url": "https://iconfinder.com" + }, + { + "name": "Icon Packs", + "url": "https://iconpacks.net" + }, + { + "name": "Iconshock", + "url": "https://iconshock.com" + }, + { + "name": "Iconz Design", + "url": "https://iconz.design" + }, + { + "name": "iFixit", + "url": "https://ifixit.com" + }, + { + "name": "IFTTT", + "url": "https://ifttt.com" + }, + { + "name": "Illlustrations", + "url": "https://illlustrations.co" + }, + { + "name": "Illustration Kit", + "url": "https://illustrationkit.com" + }, + { + "name": "IMSDB", + "url": "https://imsdb.com" + }, + { + "name": "Incompetech", + "url": "https://incompetech.com" + }, + { + "name": "Incredibox", + "url": "https://incredibox.com" + }, + { + "name": "InnerBod", + "url": "https://innerbody.com" + }, + { + "name": "Instructables", + "url": "https://instructables.com" + }, + { + "name": "Integromat", + "url": "https://make.com" + }, + { + "name": "Investopedia", + "url": "https://investopedia.com" + }, + { + "name": "Japanese Wiki Corpus", + "url": "https://japanesewiki.com" + }, + { + "name": "Jitter.Video", + "url": "https://jitter.video" + }, + { + "name": "Jobspresso", + "url": "https://jobspresso.co" + }, + { + "name": "JPEG-Optimizer", + "url": "https://jpeg-optimizer.com" + }, + { + "name": "JS Remotely", + "url": "https://jsremotely.com" + }, + { + "name": "JScreenFix", + "url": "https://jscreenfix.com" + }, + { + "name": "JSON Resume", + "url": "https://jsonresume.io" + }, + { + "name": "Just Join", + "url": "https://justjoin.it" + }, + { + "name": "Just the Recipe", + "url": "https://justtherecipe.com" + }, + { + "name": "JustRemote", + "url": "https://justremote.co" + }, + { + "name": "JustWatch", + "url": "https://justwatch.com" + }, + { + "name": "Kanopy", + "url": "https://kanopy.com" + }, + { + "name": "Kassellabs", + "url": "https://kassellabs.io" + }, + { + "name": "Key Differences", + "url": "https://keydifferences.com" + }, + { + "name": "Keybase", + "url": "https://keybase.io" + }, + { + "name": "KeyValues", + "url": "https://keyvalues.com" + }, + { + "name": "KHInsider", + "url": "https://khinsider.com" + }, + { + "name": "Killed by Google", + "url": "https://killedbygoogle.com" + }, + { + "name": "Kimovil", + "url": "https://kimovil.com" + }, + { + "name": "Lalal.ai", + "url": "https://www.lalal.ai" + }, + { + "name": "Learn Anything", + "url": "https://learn-anything.xyz" + }, + { + "name": "LendingTree", + "url": "https://lendingtree.com" + }, + { + "name": "Lightyear.fm", + "url": "https://lightyear.fm" + }, + { + "name": "LittleSis", + "url": "https://littlesis.org" + }, + { + "name": "Looria", + "url": "https://looria.com" + }, + { + "name": "Lucidchart", + "url": "https://lucidchart.com" + }, + { + "name": "Lunar", + "url": "https://lunar.fyi" + }, + { + "name": "Manuals Lib", + "url": "https://manualslib.com" + }, + { + "name": "Map Crunch", + "url": "https://mapcrunch.com" + }, + { + "name": "Masterworks", + "url": "https://masterworks.com" + }, + { + "name": "MediaFire", + "url": "https://mediafire.com" + }, + { + "name": "Mixlr", + "url": "https://mixlr.com" + }, + { + "name": "Moises AI", + "url": "https://moises.ai" + }, + { + "name": "Money", + "url": "https://money.com" + }, + { + "name": "Mountain Project", + "url": "https://mountainproject.com" + }, + { + "name": "Movie Map", + "url": "https://movie-map.com" + }, + { + "name": "Movie Sounds", + "url": "https://movie-sounds.org" + }, + { + "name": "MP3Cut", + "url": "https://mp3cut.net" + }, + { + "name": "Murmel", + "url": "https://murmel.social" + }, + { + "name": "Muscle Wiki", + "url": "https://musclewiki.com" + }, + { + "name": "Music-Map", + "url": "https://music-map.com" + }, + { + "name": "MusicTheory.net", + "url": "https://musictheory.net" + }, + { + "name": "MyFonts", + "url": "https://myfonts.com" + }, + { + "name": "MyFridgeFood", + "url": "https://myfridgefood.com" + }, + { + "name": "Nameberry", + "url": "https://nameberry.com" + }, + { + "name": "Namechk", + "url": "https://namechk.com" + }, + { + "name": "Ncase", + "url": "https://ncase.me" + }, + { + "name": "News in Levels", + "url": "https://newsinlevels.com" + }, + { + "name": "Noisli", + "url": "https://noisli.com" + }, + { + "name": "Notes.io", + "url": "https://notes.io" + }, + { + "name": "Novoresume", + "url": "https://novoresume.com" + }, + { + "name": "Ocoya", + "url": "https://ocoya.com" + }, + { + "name": "Old Computers Museum", + "url": "https://oldcomputers.net" + }, + { + "name": "Online Tone Generator", + "url": "https://onlinetonegenerator.com" + }, + { + "name": "Online-Convert", + "url": "https://online-convert.com" + }, + { + "name": "OnlineConversion", + "url": "https://onlineconversion.com" + }, + { + "name": "Online OCR", + "url": "https://onlineocr.net" + }, + { + "name": "OpenWeatherMap", + "url": "https://openweathermap.org" + }, + { + "name": "OrgPad", + "url": "https://orgpad.com" + }, + { + "name": "Passport Index", + "url": "https://passportindex.org" + }, + { + "name": "PDF Candy", + "url": "https://pdfcandy.com" + }, + { + "name": "PDF2DOC", + "url": "https://pdf2doc.com" + }, + { + "name": "PDFescape", + "url": "https://pdfescape.com" + }, + { + "name": "PfpMaker", + "url": "https://pfpmaker.com" + }, + { + "name": "PIDGI Wiki ", + "url": "https://pidgi.net" + }, + { + "name": "PimEyes", + "url": "https://pimeyes.com" + }, + { + "name": "Pipl ", + "url": "https://pipl.com" + }, + { + "name": "PixelBazaar", + "url": "https://pixelbazaar.com" + }, + { + "name": "PixelPaper", + "url": "https://pixelpaper.io" + }, + { + "name": "Ponly", + "url": "https://ponly.com" + }, + { + "name": "PowerToFly", + "url": "https://powertofly.com" + }, + { + "name": "Pretzel Rocks", + "url": "https://pretzel.rocks" + }, + { + "name": "PrintIt", + "url": "https://printit.work" + }, + { + "name": "Prismatext", + "url": "https://prismatext.com" + }, + { + "name": "Puffin Maps", + "url": "https://puffinmaps.com" + }, + { + "name": "Puzzle Loop ", + "url": "https://puzzle-loop.com" + }, + { + "name": "QuoteMaster", + "url": "https://quotemaster.org" + }, + { + "name": "Radio Garden", + "url": "https://radio.garden" + }, + { + "name": "Radiooooo", + "url": "https://radiooooo.com" + }, + { + "name": "Radiosondy", + "url": "https://radiosondy.info" + }, + { + "name": "Rainy Mood", + "url": "https://rainymood.com" + }, + { + "name": "Random Street View", + "url": "https://randomstreetview.com" + }, + { + "name": "Rap4Ever", + "url": "https://rap4all.com" + }, + { + "name": "RareFilm", + "url": "https://rarefilm.net" + }, + { + "name": "Rattibha", + "url": "https://rattibha.com" + }, + { + "name": "Reddit List ", + "url": "https://redditlist.com" + }, + { + "name": "RedditSearch.io", + "url": "https://redditsearch.io" + }, + { + "name": "Reelgood", + "url": "https://reelgood.com" + }, + { + "name": "Reface", + "url": "https://reface.ai" + }, + { + "name": "Rejected.us", + "url": "https://rejected.us" + }, + { + "name": "Relanote", + "url": "https://relanote.com" + }, + { + "name": "Remote Leaf", + "url": "https://remoteleaf.com" + }, + { + "name": "Remote OK", + "url": "https://remoteok.com" + }, + { + "name": "Remote Starter Kit ", + "url": "https://remotestarterkit.com" + }, + { + "name": "Remote.co", + "url": "https://remote.co" + }, + { + "name": "Remote Base ", + "url": "https://remotebase.com" + }, + { + "name": "Remote Bear", + "url": "https://remotebear.io" + }, + { + "name": "Remove.bg", + "url": "https://remove.bg" + }, + { + "name": "Respresso", + "url": "https://respresso.io" + }, + { + "name": "Reveddit", + "url": "https://reveddit.com" + }, + { + "name": "Rhymer", + "url": "https://rhymer.com" + }, + { + "name": "RhymeZone", + "url": "https://rhymezone.com" + }, + { + "name": "Ribbet", + "url": "https://ribbet.com" + }, + { + "name": "Roadmap.sh", + "url": "https://roadmap.sh" + }, + { + "name": "Roadtrippers", + "url": "https://roadtrippers.com" + }, + { + "name": "RxResu.me", + "url": "https://rxresu.me" + }, + { + "name": "SchemeColor", + "url": "https://schemecolor.com" + }, + { + "name": "Screenshot.Guru", + "url": "https://screenshot.guru" + }, + { + "name": "SeatGuru", + "url": "https://seatguru.com" + }, + { + "name": "Sessions", + "url": "https://sessions.us" + }, + { + "name": "Shottr", + "url": "https://shottr.cc" + }, + { + "name": "Signature Maker", + "url": "https://signature-maker.net" + }, + { + "name": "Skip The Drive", + "url": "https://skipthedrive.com" + }, + { + "name": "Slowby", + "url": "https://slowby.travel" + }, + { + "name": "Small World", + "url": "https://smallworld.kiwi" + }, + { + "name": "SmallPDF", + "url": "https://smallpdf.com" + }, + { + "name": "Social Image Maker", + "url": "https://socialimagemaker.io" + }, + { + "name": "Social Sizes", + "url": "https://socialsizes.io" + }, + { + "name": "SoundLove", + "url": "https://soundlove.se" + }, + { + "name": "Spline", + "url": "https://spline.design" + }, + { + "name": "Starkey Comics", + "url": "https://starkeycomics.com" + }, + + { + "name": "Statista", + "url": "https://statista.com" + }, + { + "name": "Stolen Camera Finder", + "url": "https://stolencamerafinder.com" + }, + { + "name": "Strobe.Cool", + "url": "https://strobe.cool" + }, + { + "name": "Sumo", + "url": "https://sumo.app" + }, + { + "name": "SuperMeme AI", + "url": "https://supermeme.ai" + }, + { + "name": "Synthesia", + "url": "https://synthesia.io" + }, + { + "name": "TablerIcons", + "url": "https://tablericons.com" + }, + { + "name": "Tango", + "url": "https://tango.us" + }, + { + "name": "TasteDive", + "url": "https://tastedive.com" + }, + { + "name": "TechSpecs", + "url": "https://techspecs.io" + }, + { + "name": "Teoria", + "url": "https://teoria.com" + }, + { + "name": "Text Faces", + "url": "https://textfac.es" + }, + { + "name": "The Balance Money", + "url": "https://thebalancemoney.com" + }, + { + "name": "The Punctuation Guide", + "url": "https://thepunctuationguide.com" + }, + { + "name": "This to That", + "url": "https://thistothat.com" + }, + { + "name": "This vs That", + "url": "https://thisvsthat.io" + }, + { + "name": "ThreadReaderApp ", + "url": "https://threadreaderapp.com" + }, + { + "name": "Thumbly", + "url": "https://tokee.ai" + }, + { + "name": "Tiii.me", + "url": "https://tiii.me" + }, + { + "name": "TikTok Video Downloader", + "url": "https://ttvdl.com" + }, + { + "name": "Time and Date", + "url": "https://timeanddate.com" + }, + { + "name": "Time.is", + "url": "https://time.is" + }, + { + "name": "Title Case", + "url": "https://titlecase.com" + }, + { + "name": "Toaster Central", + "url": "https://toastercentral.com" + }, + { + "name": "Tongue-Twister ", + "url": "https://tongue-twister.net" + }, + { + "name": "TradingView", + "url": "https://tradingview.com" + }, + { + "name": "Transparent Textures", + "url": "https://transparenttextures.com" + }, + { + "name": "Tubi TV", + "url": "https://tubitv.com" + }, + { + "name": "Tunefind", + "url": "https://tunefind.com" + }, + { + "name": "TuneMyMusic", + "url": "https://tunemymusic.com" + }, + { + "name": "Tweepsmap", + "url": "https://fedica.com" + }, + { + "name": "Two Peas and Their Pod", + "url": "https://twopeasandtheirpod.com" + }, + { + "name": "Typatone", + "url": "https://typatone.com" + }, + { + "name": "Under Glass", + "url": "https://underglass.io" + }, + { + "name": "UniCorner", + "url": "https://unicorner.news" + }, + { + "name": "Unita", + "url": "https://unita.co" + }, + { + "name": "UnitConverters", + "url": "https://unitconverters.net" + }, + { + "name": "Unreadit", + "url": "https://unreadit.com" + }, + { + "name": "Unscreen", + "url": "https://unscreen.com" + }, + { + "name": "UnTools ", + "url": "https://untools.co" + }, + { + "name": "Upwork", + "url": "https://upwork.com" + }, + { + "name": "UTF8 Icons", + "url": "https://utf8icons.com" + }, + { + "name": "Vector Magic", + "url": "https://vectormagic.com" + }, + { + "name": "Virtual Vacation", + "url": "https://virtualvacation.us" + }, + { + "name": "Virtual Vocations", + "url": "https://virtualvocations.com" + }, + { + "name": "Visiwig", + "url": "https://visiwig.com" + }, + { + "name": "Visual CV", + "url": "https://visualcv.com" + }, + { + "name": "Vocus.io", + "url": "https://vocus.io" + }, + { + "name": "Voscreen", + "url": "https://voscreen.com" + }, + { + "name": "Wanderprep", + "url": "https://wanderprep.com" + }, + { + "name": "Warmshowers", + "url": "https:/warmshowers.org" + }, + { + "name": "Watch Documentaries", + "url": "https://watchdocumentaries.com" + }, + { + "name": "We Work Remotely", + "url": "https://weworkremotely.com" + }, + { + "name": "Web2PDFConvert", + "url": "https://web2pdfconvert.com" + }, + { + "name": "Welcome to My Garden", + "url": "https://welcometomygarden.org" + }, + { + "name": "When2meet ", + "url": "https://when2meet.com" + }, + { + "name": "Where's George", + "url": "https://wheresgeorge.com" + }, + { + "name": "Where's Willy", + "url": "https://whereswilly.com" + }, + { + "name": "WikiHow", + "url": "https://wikihow.com" + }, + { + "name": "Windy", + "url": "https://www.windy.com" + }, + { + "name": "WonderHowTo", + "url": "https://wonderhowto.com" + }, + { + "name": "Working Nomads", + "url": "https://workingnomads.com" + }, + { + "name": "Wormhole", + "url": "https://wormhole.app" + }, + { + "name": "Y Combinator Jobs", + "url": "https://ycombinator.com" + }, + { + "name": "Yes Promo", + "url": "https://yespromo.me" + }, + { + "name": "YouGlish", + "url": "https://youglish.com" + }, + { + "name": "Zamzar", + "url": "https://zamzar.com" + }, + { + "name": "Zippyshare", + "url": "https://zippyshare.com" + }, + { + "name": "Zoom Earth", + "url": "https://zoom.earth" + }, + { + "name": "Zoom.it", + "url": "https://zoom.it" + } +] diff --git a/Server/validation/joi.js b/Server/validation/joi.js index d30235048..8f366a113 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -131,7 +131,7 @@ const getMonitorByIdQueryValidation = joi.object({ status: joi.boolean(), sortOrder: joi.string().valid("asc", "desc"), limit: joi.number(), - dateRange: joi.string().valid("day", "week", "month"), + dateRange: joi.string().valid("hour", "day", "week", "month", "all"), numToDisplay: joi.number(), normalize: joi.boolean(), }); @@ -166,7 +166,7 @@ const getMonitorStatsByIdQueryValidation = joi.object({ status: joi.string(), limit: joi.number(), sortOrder: joi.string().valid("asc", "desc"), - dateRange: joi.string().valid("day", "week", "month", "all"), + dateRange: joi.string().valid("hour", "day", "week", "month", "all"), numToDisplay: joi.number(), normalize: joi.boolean(), }); @@ -217,7 +217,7 @@ const getHardwareDetailsByIdParamValidation = joi.object({ }); const getHardwareDetailsByIdQueryValidation = joi.object({ - dateRange: joi.string().valid("day", "week", "month"), + dateRange: joi.string().valid("hour", "day", "week", "month", "all"), }); //**************************************** @@ -288,7 +288,7 @@ const getChecksParamValidation = joi.object({ const getChecksQueryValidation = joi.object({ sortOrder: joi.string().valid("asc", "desc"), limit: joi.number(), - dateRange: joi.string().valid("day", "week", "month"), + dateRange: joi.string().valid("hour", "day", "week", "month", "all"), filter: joi.string().valid("all", "down", "resolve"), page: joi.number(), rowsPerPage: joi.number(), @@ -301,7 +301,7 @@ const getTeamChecksParamValidation = joi.object({ const getTeamChecksQueryValidation = joi.object({ sortOrder: joi.string().valid("asc", "desc"), limit: joi.number(), - dateRange: joi.string().valid("day", "week", "month"), + dateRange: joi.string().valid("hour", "day", "week", "month", "all"), filter: joi.string().valid("all", "down", "resolve"), page: joi.number(), rowsPerPage: joi.number(), diff --git a/guides/.gitbook/assets/Screenshot 2024-10-10 at 10.37.36 PM.png b/guides/.gitbook/assets/Screenshot 2024-10-10 at 10.37.36 PM.png new file mode 100644 index 000000000..31dbacbe0 Binary files /dev/null and b/guides/.gitbook/assets/Screenshot 2024-10-10 at 10.37.36 PM.png differ diff --git a/guides/users-guide/maintenance-mode.md b/guides/users-guide/maintenance-mode.md new file mode 100644 index 000000000..7881dbfd8 --- /dev/null +++ b/guides/users-guide/maintenance-mode.md @@ -0,0 +1,35 @@ +--- +icon: clock-nine +--- + +# Maintenance mode + +The Maintenance mode allows you to manage and schedule maintenance periods for monitored URLs. During a maintenance window, no pings or HTTP requests are sent to the specified URLs, preventing downtime alerts during scheduled maintenance activities. + +### Creating a new maintenance window + +When you click the **"Create Maintenance Window"** button, you will see a page similar to this: + +
+ +Here's how to set up a new maintenance window: + +**General settings:** + +* **Maintenance repeat**: Select how often the maintenance window should repeat (e.g., don't repeat, daily, weekly). +* **Date**: Select the date the maintenance window should start. +* **Start time**: Set the time of day the window begins. +* **Duration**: Specify how long the maintenance window should last (in seconds, minutes, or hours). + +**Monitor related settings:** + +* **Friendly Name**: Provide a descriptive name for the maintenance window (e.g., "Maintenance at 10:37 PM for 30 minutes"). +* **Add Monitors**: Select the specific monitors (URLs) for which this maintenance window will apply by typing and selecting from the list. + +Once all fields are filled, click **Create maintenance** to save the new window. + +### Editing a maintenance window + +* To edit an existing window, click the gear icon under **Actions** in the table view. +* Update the required fields and save your changes. + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..f71a78c1f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "bluewave-uptime", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}