diff --git a/Client/package-lock.json b/Client/package-lock.json index fe3d684f5..e4a8fd7a8 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -13,6 +13,7 @@ "@fontsource/roboto": "^5.0.13", "@mui/icons-material": "^5.15.17", "@mui/material": "^5.15.16", + "@mui/x-charts": "^7.5.1", "@mui/x-data-grid": "7.3.2", "@mui/x-date-pickers": "7.3.2", "dayjs": "1.11.11", @@ -989,6 +990,44 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/@mui/x-charts": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.5.1.tgz", + "integrity": "sha512-8vMQyJL13cGXMbp+gm2QdABzFuOpP4hxEa/o6nL3ZpVNp7hja6h9F5bc0FNGR8h/eRN+4+swDnrEebnR0w4cbA==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "@mui/base": "^5.0.0-beta.40", + "@mui/system": "^5.15.14", + "@mui/utils": "^5.15.14", + "@react-spring/rafz": "^9.7.3", + "@react-spring/web": "^9.7.3", + "clsx": "^2.1.1", + "d3-color": "^3.1.0", + "d3-delaunay": "^6.0.4", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.2.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, "node_modules/@mui/x-data-grid": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.3.2.tgz", @@ -1123,6 +1162,71 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-spring/animated": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", + "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", + "dependencies": { + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", + "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.3.tgz", + "integrity": "sha512-9vzW1zJPcC4nS3aCV+GgcsK/WLaB520Iyvm55ARHfM5AuyBqycjvh1wbmWmgCyJuX4VPoWigzemq1CaaeRSHhQ==" + }, + "node_modules/@react-spring/shared": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", + "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", + "dependencies": { + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", + "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==" + }, + "node_modules/@react-spring/web": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz", + "integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/core": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@remix-run/router": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", @@ -1674,6 +1778,111 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -1787,6 +1996,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2739,6 +2956,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -3805,6 +4030,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/rollup": { "version": "4.17.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", diff --git a/Client/package.json b/Client/package.json index cca88fb31..213119f38 100644 --- a/Client/package.json +++ b/Client/package.json @@ -15,6 +15,7 @@ "@fontsource/roboto": "^5.0.13", "@mui/icons-material": "^5.15.17", "@mui/material": "^5.15.16", + "@mui/x-charts": "^7.5.1", "@mui/x-data-grid": "7.3.2", "@mui/x-date-pickers": "7.3.2", "dayjs": "1.11.11", diff --git a/Client/vite.config.js.timestamp-1714807482127-bba3249973ca6.mjs b/Client/src/Components/DashboardProgressBars/index.css similarity index 100% rename from Client/vite.config.js.timestamp-1714807482127-bba3249973ca6.mjs rename to Client/src/Components/DashboardProgressBars/index.css diff --git a/Client/src/Components/DashboardProgressBars/index.jsx b/Client/src/Components/DashboardProgressBars/index.jsx new file mode 100644 index 000000000..8215a0d17 --- /dev/null +++ b/Client/src/Components/DashboardProgressBars/index.jsx @@ -0,0 +1,19 @@ +import { BarChart } from '@mui/x-charts/BarChart'; + +const ChartsOverviewDemo = () => { + return ( + + ); +}; + +export default ChartsOverviewDemo; diff --git a/Client/vite.config.js.timestamp-1714807494135-d5aa25dec011.mjs b/Client/src/Components/DashboardSettingsIcon/index.css similarity index 100% rename from Client/vite.config.js.timestamp-1714807494135-d5aa25dec011.mjs rename to Client/src/Components/DashboardSettingsIcon/index.css diff --git a/Client/src/Components/DashboardSettingsIcon/index.jsx b/Client/src/Components/DashboardSettingsIcon/index.jsx new file mode 100644 index 000000000..e1f1d59da --- /dev/null +++ b/Client/src/Components/DashboardSettingsIcon/index.jsx @@ -0,0 +1,78 @@ +import { useState } from 'react'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import Menu from '@mui/material/Menu'; +import Tooltip from '@mui/material/Tooltip'; +import MenuItem from '@mui/material/MenuItem'; +import Typography from '@mui/material/Typography'; +import SvgIcon from '@mui/material/SvgIcon'; +import SettingsIcon from '@mui/icons-material/Settings'; +import { useTheme } from '@mui/material/styles'; + +const settings = ['Open Site', 'Detailed View', 'Incidents', 'Configure', 'Remove']; + +/** + * DashboardSettings component + * + * A component that provides a settings menu accessible from an icon. + * + * @component + * @example + * return ( + * + * ) + */ +function DashboardSettings() { + const theme = useTheme(); + const [anchorElUser, setAnchorElUser] = useState(null); + + /** + * Handles opening the user menu. + * + * @param {React.MouseEvent} event - The event triggered by clicking the user menu button. + */ + const handleOpenUserMenu = (event) => { + setAnchorElUser(event.currentTarget); + }; + + /** + * Handles closing the user menu. + */ + const handleCloseUserMenu = () => { + setAnchorElUser(null); + }; + + return ( + + + + + + + + {settings.map((setting) => ( + + {setting} + + ))} + + + ); +} + +export default DashboardSettings; diff --git a/Client/src/Layouts/HomeLayout/index.jsx b/Client/src/Layouts/HomeLayout/index.jsx index 454f1452a..ed1d06fe6 100644 --- a/Client/src/Layouts/HomeLayout/index.jsx +++ b/Client/src/Layouts/HomeLayout/index.jsx @@ -1,8 +1,10 @@ -import React from 'react' import NavBar from '../../Components/NavBar' import { Outlet } from 'react-router' import Home from '../../Pages/Home' import './index.css' +import ChartsOverviewDemo from '../../Components/DashboardProgressBars' +import DashboardSettings from '../../Components/DashboardSettingsIcon' + const HomeLayout = () => { return ( @@ -10,6 +12,8 @@ const HomeLayout = () => { + + ) } diff --git a/Client/vite.config.js.timestamp-1714807476723-44482001f0038.mjs b/Client/vite.config.js.timestamp-1714807476723-44482001f0038.mjs deleted file mode 100644 index f54c5cbfe..000000000 --- a/Client/vite.config.js.timestamp-1714807476723-44482001f0038.mjs +++ /dev/null @@ -1,10 +0,0 @@ -// vite.config.js -import { defineConfig } from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/vite/dist/node/index.js"; -import react from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/@vitejs/plugin-react/dist/index.mjs"; -var vite_config_default = defineConfig({ - plugins: [react()] -}); -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJjOlxcXFxVc2Vyc1xcXFxjb3NtaVxcXFxPbmVEcml2ZVxcXFxEZXNrdG9wXFxcXEJsdWVXYXZlXFxcXFVwdGltZS1Nb25pdG9yXFxcXGJsdWV3YXZlLXVwdGltZVxcXFxDbGllbnRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcImM6XFxcXFVzZXJzXFxcXGNvc21pXFxcXE9uZURyaXZlXFxcXERlc2t0b3BcXFxcQmx1ZVdhdmVcXFxcVXB0aW1lLU1vbml0b3JcXFxcYmx1ZXdhdmUtdXB0aW1lXFxcXENsaWVudFxcXFx2aXRlLmNvbmZpZy5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vYzovVXNlcnMvY29zbWkvT25lRHJpdmUvRGVza3RvcC9CbHVlV2F2ZS9VcHRpbWUtTW9uaXRvci9ibHVld2F2ZS11cHRpbWUvQ2xpZW50L3ZpdGUuY29uZmlnLmpzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xyXG5cclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcclxuICBwbHVnaW5zOiBbcmVhY3QoKV0sXHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBOGEsU0FBUyxvQkFBb0I7QUFDM2MsT0FBTyxXQUFXO0FBR2xCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFDbkIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/Client/vite.config.js.timestamp-1714807479464-cc660fde72923.mjs b/Client/vite.config.js.timestamp-1714807479464-cc660fde72923.mjs deleted file mode 100644 index f54c5cbfe..000000000 --- a/Client/vite.config.js.timestamp-1714807479464-cc660fde72923.mjs +++ /dev/null @@ -1,10 +0,0 @@ -// vite.config.js -import { defineConfig } from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/vite/dist/node/index.js"; -import react from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/@vitejs/plugin-react/dist/index.mjs"; -var vite_config_default = defineConfig({ - plugins: [react()] -}); -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJjOlxcXFxVc2Vyc1xcXFxjb3NtaVxcXFxPbmVEcml2ZVxcXFxEZXNrdG9wXFxcXEJsdWVXYXZlXFxcXFVwdGltZS1Nb25pdG9yXFxcXGJsdWV3YXZlLXVwdGltZVxcXFxDbGllbnRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcImM6XFxcXFVzZXJzXFxcXGNvc21pXFxcXE9uZURyaXZlXFxcXERlc2t0b3BcXFxcQmx1ZVdhdmVcXFxcVXB0aW1lLU1vbml0b3JcXFxcYmx1ZXdhdmUtdXB0aW1lXFxcXENsaWVudFxcXFx2aXRlLmNvbmZpZy5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vYzovVXNlcnMvY29zbWkvT25lRHJpdmUvRGVza3RvcC9CbHVlV2F2ZS9VcHRpbWUtTW9uaXRvci9ibHVld2F2ZS11cHRpbWUvQ2xpZW50L3ZpdGUuY29uZmlnLmpzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xyXG5cclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcclxuICBwbHVnaW5zOiBbcmVhY3QoKV0sXHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBOGEsU0FBUyxvQkFBb0I7QUFDM2MsT0FBTyxXQUFXO0FBR2xCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFDbkIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/Client/vite.config.js.timestamp-1714807487371-43b917cc75e26.mjs b/Client/vite.config.js.timestamp-1714807487371-43b917cc75e26.mjs deleted file mode 100644 index f54c5cbfe..000000000 --- a/Client/vite.config.js.timestamp-1714807487371-43b917cc75e26.mjs +++ /dev/null @@ -1,10 +0,0 @@ -// vite.config.js -import { defineConfig } from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/vite/dist/node/index.js"; -import react from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/@vitejs/plugin-react/dist/index.mjs"; -var vite_config_default = defineConfig({ - plugins: [react()] -}); -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJjOlxcXFxVc2Vyc1xcXFxjb3NtaVxcXFxPbmVEcml2ZVxcXFxEZXNrdG9wXFxcXEJsdWVXYXZlXFxcXFVwdGltZS1Nb25pdG9yXFxcXGJsdWV3YXZlLXVwdGltZVxcXFxDbGllbnRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcImM6XFxcXFVzZXJzXFxcXGNvc21pXFxcXE9uZURyaXZlXFxcXERlc2t0b3BcXFxcQmx1ZVdhdmVcXFxcVXB0aW1lLU1vbml0b3JcXFxcYmx1ZXdhdmUtdXB0aW1lXFxcXENsaWVudFxcXFx2aXRlLmNvbmZpZy5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vYzovVXNlcnMvY29zbWkvT25lRHJpdmUvRGVza3RvcC9CbHVlV2F2ZS9VcHRpbWUtTW9uaXRvci9ibHVld2F2ZS11cHRpbWUvQ2xpZW50L3ZpdGUuY29uZmlnLmpzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xyXG5cclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcclxuICBwbHVnaW5zOiBbcmVhY3QoKV0sXHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBOGEsU0FBUyxvQkFBb0I7QUFDM2MsT0FBTyxXQUFXO0FBR2xCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFDbkIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/Client/vite.config.js.timestamp-1714807590920-36497efd2dbd1.mjs b/Client/vite.config.js.timestamp-1714807590920-36497efd2dbd1.mjs deleted file mode 100644 index f54c5cbfe..000000000 --- a/Client/vite.config.js.timestamp-1714807590920-36497efd2dbd1.mjs +++ /dev/null @@ -1,10 +0,0 @@ -// vite.config.js -import { defineConfig } from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/vite/dist/node/index.js"; -import react from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/@vitejs/plugin-react/dist/index.mjs"; -var vite_config_default = defineConfig({ - plugins: [react()] -}); -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJjOlxcXFxVc2Vyc1xcXFxjb3NtaVxcXFxPbmVEcml2ZVxcXFxEZXNrdG9wXFxcXEJsdWVXYXZlXFxcXFVwdGltZS1Nb25pdG9yXFxcXGJsdWV3YXZlLXVwdGltZVxcXFxDbGllbnRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcImM6XFxcXFVzZXJzXFxcXGNvc21pXFxcXE9uZURyaXZlXFxcXERlc2t0b3BcXFxcQmx1ZVdhdmVcXFxcVXB0aW1lLU1vbml0b3JcXFxcYmx1ZXdhdmUtdXB0aW1lXFxcXENsaWVudFxcXFx2aXRlLmNvbmZpZy5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vYzovVXNlcnMvY29zbWkvT25lRHJpdmUvRGVza3RvcC9CbHVlV2F2ZS9VcHRpbWUtTW9uaXRvci9ibHVld2F2ZS11cHRpbWUvQ2xpZW50L3ZpdGUuY29uZmlnLmpzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xyXG5cclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcclxuICBwbHVnaW5zOiBbcmVhY3QoKV0sXHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBOGEsU0FBUyxvQkFBb0I7QUFDM2MsT0FBTyxXQUFXO0FBR2xCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFDbkIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/Client/vite.config.js.timestamp-1714807965878-e54134a7db9a9.mjs b/Client/vite.config.js.timestamp-1714807965878-e54134a7db9a9.mjs deleted file mode 100644 index f54c5cbfe..000000000 --- a/Client/vite.config.js.timestamp-1714807965878-e54134a7db9a9.mjs +++ /dev/null @@ -1,10 +0,0 @@ -// vite.config.js -import { defineConfig } from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/vite/dist/node/index.js"; -import react from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/@vitejs/plugin-react/dist/index.mjs"; -var vite_config_default = defineConfig({ - plugins: [react()] -}); -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJjOlxcXFxVc2Vyc1xcXFxjb3NtaVxcXFxPbmVEcml2ZVxcXFxEZXNrdG9wXFxcXEJsdWVXYXZlXFxcXFVwdGltZS1Nb25pdG9yXFxcXGJsdWV3YXZlLXVwdGltZVxcXFxDbGllbnRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcImM6XFxcXFVzZXJzXFxcXGNvc21pXFxcXE9uZURyaXZlXFxcXERlc2t0b3BcXFxcQmx1ZVdhdmVcXFxcVXB0aW1lLU1vbml0b3JcXFxcYmx1ZXdhdmUtdXB0aW1lXFxcXENsaWVudFxcXFx2aXRlLmNvbmZpZy5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vYzovVXNlcnMvY29zbWkvT25lRHJpdmUvRGVza3RvcC9CbHVlV2F2ZS9VcHRpbWUtTW9uaXRvci9ibHVld2F2ZS11cHRpbWUvQ2xpZW50L3ZpdGUuY29uZmlnLmpzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xyXG5cclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcclxuICBwbHVnaW5zOiBbcmVhY3QoKV0sXHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBOGEsU0FBUyxvQkFBb0I7QUFDM2MsT0FBTyxXQUFXO0FBR2xCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFDbkIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/Client/vite.config.js.timestamp-1714807967318-789fa77657938.mjs b/Client/vite.config.js.timestamp-1714807967318-789fa77657938.mjs deleted file mode 100644 index f54c5cbfe..000000000 --- a/Client/vite.config.js.timestamp-1714807967318-789fa77657938.mjs +++ /dev/null @@ -1,10 +0,0 @@ -// vite.config.js -import { defineConfig } from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/vite/dist/node/index.js"; -import react from "file:///c:/Users/cosmi/OneDrive/Desktop/BlueWave/Uptime-Monitor/bluewave-uptime/Client/node_modules/@vitejs/plugin-react/dist/index.mjs"; -var vite_config_default = defineConfig({ - plugins: [react()] -}); -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJjOlxcXFxVc2Vyc1xcXFxjb3NtaVxcXFxPbmVEcml2ZVxcXFxEZXNrdG9wXFxcXEJsdWVXYXZlXFxcXFVwdGltZS1Nb25pdG9yXFxcXGJsdWV3YXZlLXVwdGltZVxcXFxDbGllbnRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcImM6XFxcXFVzZXJzXFxcXGNvc21pXFxcXE9uZURyaXZlXFxcXERlc2t0b3BcXFxcQmx1ZVdhdmVcXFxcVXB0aW1lLU1vbml0b3JcXFxcYmx1ZXdhdmUtdXB0aW1lXFxcXENsaWVudFxcXFx2aXRlLmNvbmZpZy5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vYzovVXNlcnMvY29zbWkvT25lRHJpdmUvRGVza3RvcC9CbHVlV2F2ZS9VcHRpbWUtTW9uaXRvci9ibHVld2F2ZS11cHRpbWUvQ2xpZW50L3ZpdGUuY29uZmlnLmpzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xyXG5cclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcclxuICBwbHVnaW5zOiBbcmVhY3QoKV0sXHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBOGEsU0FBUyxvQkFBb0I7QUFDM2MsT0FBTyxXQUFXO0FBR2xCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFDbkIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/README.md b/README.md index 44a30ea94..dfc57064d 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,33 @@ BlueWave uptime monitoring application - Clone this repository to your local machine 1. [Installation (Client)](#client) -1. [Installation (Server)](#server) -1. [Configuration(Server)](#config-server) -1. [Endpoints](#endpoints) +2. [Installation (Server)](#server) +3. [Configuration(Server)](#config-server) +4. [Endpoints](#endpoints) + ###### Auth + - POST [/api/v1/auth/register](#post-register) + - POST [/api/v1/auth/login](#post-login) + - POST [/api/v1/auth/user/{userId}](#post-auth-user-edit-id) + ###### Monitors + - GET [/api/v1/monitors](#get-monitors) + - GET [/api/v1/monitor/{id}](#get-monitor-id) + - GET [/api/v1/monitors/user/{userId}](#get-monitors-user-userid) + - POST [/api/v1/monitors](#post-monitors) + - POST [/api/v1/monitors/delete/{monitorId}](#post-monitors-del-id) + - POST [/api/v1/monitors/edit/{monitorId}](#post-monitors-edit-id) + ###### Checks + - POST [/api/v1/checks/{monitorId}](#post-checks) + - GET [/api/v1/checks/{monitorId}](#get-checks) + - POST [/api/v1/checks/delete/{monitorId}](#delete-checks) + ###### Alerts + - POST [/api/v1/alerts/{monitorId}](#create-alert) + - GET [/api/v1/alerts/user/{userId}](#get-alerts-user-id) + - GET [/api/v1/alerts/monitor/{monitorId}](#get-alerts-monitor-id) + - GET [/api/v1/alerts/{alertId}](#get-alert-alert-id) + - POST [/api/v1/alerts/edit/{alertId}](#edit-alert) + - POST [/api/v1/alerts/delete/{alertId}](#delete-alert) +5. [Error Handling](#error-handling) +6. [Contributors](#contributors) --- @@ -108,10 +132,44 @@ Example: ---- +
+Check + +| Name | Type | Notes | +| ------------ | --------- | ----------------------------------------------- | +| monitorId | `string` | Unique ID for the monitor | +| status | `boolean` | Indicates the service is Up or Down | +| responseTime | `integer` | Indicates the response time of the service (ms) | +| statusCode | `integer` | Status Code returned from the service | +| message | `string` | Message returned from the service | +| updatedAt | `Date` | Last time the check was updated | +| CreatedAt | `Date` | When the check was created | + +
-POST /api/v1/auth/register +Alert + +| Name | Type | Notes | +| ----------------- | --------- | ------------------------------------------------- | +| checkId | `string` | Unique ID for the associated check | +| monitorId | `string` | Unique ID for the associated monitor | +| userId | `string` | Unique ID for the associated user | +| status | `boolean` | Indicates the service is Up or Down | +| message | `string` | Message for the user about the down service | +| notifiedStatus | `boolean` | Indicates whether the user is notified | +| acknowledgeStatus | `boolean` | Indicates whether the user acknowledged the alert | +| updatedAt | `Date` | Last time the alert was updated | +| CreatedAt | `Date` | When the alert was created | + +
+ +--- + +###### Auth + +
+POST /api/v1/auth/register ##### Method/Headers @@ -161,10 +219,8 @@ curl --request POST \
---- -
-POST /api/v1/auth/login +POST /api/v1/auth/login ##### Method/Headers @@ -210,10 +266,71 @@ curl --request POST \
+
+POST/api/v1/auth/user/{userId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +##### Body + +> | Name | Type | Notes | +> | ------------- | -------- | ----------- | +> | firstname | `string` | | +> | lastname | `string` | | +> | profilePicUrl | `string` | | +> | password | `string` | Min 8 chars | + +###### Response Payload + +> | Type | Notes | +> | ------ | ------------------------ | +> | `User` | Returns the updated user | + +##### Sample CURL request + +``` +curl --request POST \ + --url http://localhost:5000/api/v1/auth/user/6654d156634754f789e1f10e \ + --header 'Authorization: ' \ + --header 'Content-Type: application/json' \ + --data '{ + "firstname": "First Name", + "lastname: "Last Name" +}' +``` + +###### Sample Response + +```json +{ + "success": true, + "msg": "User updated", + "data": { + "_id": "6654d156634754f789e1f10e", + "firstname": "First Name", + "lastname": "Last Name", + "email": "me@gmail.com", + "isActive": true, + "isVerified": false, + "createdAt": "2024-05-27T18:30:46.358Z", + "updatedAt": "2024-05-27T19:21:51.747Z", + "__v": 0 + } +} +``` + +
+ --- +###### Monitors +
-GET /api/v1/monitors +GET /api/v1/monitors ##### Method/Headers @@ -223,9 +340,9 @@ curl --request POST \ ##### Response Payload -> | Type | Notes | -> | ---------------- | ----------------- | -> | `Array[Monitor]` | Array of monitors | +> | Type | Notes | +> | ---------------- | --------------------- | +> | `Array` | Array of all monitors | ###### Sample cURL Request @@ -272,10 +389,8 @@ curl --request GET \
---- -
-GET /api/v1/monitor/{id} +GET /api/v1/monitor/{id} ###### Method/Headers @@ -320,10 +435,8 @@ curl --request GET \
---- -
-GET /api/v1/monitors/user/{userId} +GET /api/v1/monitors/user/{userId} ###### Method/Headers @@ -333,9 +446,9 @@ curl --request GET \ ###### Response Payload -> | Type | Notes | -> | ---------------- | ------------------------------------------------------------------ | -> | `Array[Monitor]` | Array of monitors created by user with userId specified in request | +> | Type | Notes | +> | ---------------- | ------------------------------------------------------- | +> | `Array` | Array of monitors created by user with specified UserID | ###### Sample cURL Request @@ -382,10 +495,8 @@ curl --request GET \
---- -
-POST/api/v1/monitors +POST/api/v1/monitors ###### Method/Headers @@ -395,9 +506,9 @@ curl --request GET \ ###### Response Payload -> | Type | Notes | -> | --------- | --------------------------------- | -> | `Monitor` | Newly created monitor is returned | +> | Type | Notes | +> | --------- | ------------------------------- | +> | `Monitor` | Returns newly created `Monitor` | ##### Sample CURL request @@ -437,10 +548,8 @@ curl --request POST \
---- -
-POST/api/v1/monitors/delete/{monitorId} +POST/api/v1/monitors/delete/{monitorId} ###### Method/Headers @@ -474,10 +583,8 @@ curl --request POST \ ##
---- -
-POST/api/v1/monitors/edit/{monitorId} +POST/api/v1/monitors/edit/{monitorId} ###### Method/Headers @@ -531,6 +638,321 @@ curl --request POST \ } ``` +
+ +--- + +###### Checks + +
+POST/api/v1/checks/{monitorId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +###### Response Payload + +> | Type | Notes | +> | ---- | ------------------- | +> | None | No payload returned | + +###### Body + +##### Sample CURL request + +``` + +``` + +###### Sample Response + +```json + +``` + +
+ +
+GET/api/v1/checks/{monitorId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +###### Response Payload + +> | Type | Notes | +> | --------------- | ------------------------ | +> | `Array` | Array of `Check` objects | + +##### Sample CURL request + +``` + +``` + +###### Sample Response + +```json + +``` + +
+ +
+POST/api/v1/checks/delete/{monitorId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +###### Response Payload + +> | Type | Notes | +> | ---- | ------------------- | +> | None | No payload returned | + +###### Body + +##### Sample CURL request + +``` + +``` + +###### Sample Response + +```json + +``` + +
+ +--- + +###### Alerts + +
+POST/api/v1/alerts/{monitorId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +###### Response Payload + +> | Type | Notes | +> | ------- | ----------------------------- | +> | `Alert` | Returns newly created `Alert` | + +###### Body + +##### Sample CURL request + +``` + +``` + +###### Sample Response + +```json + +``` + +
+ +
+GET/api/v1/alerts/user/{userId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | GET | + +###### Response Payload + +> | Type | Notes | +> | -------------- | --------------------------------------- | +> | `Array` | Returns all `Alert` created by a `User` | + +##### Sample CURL request + +``` + +``` + +###### Sample Response + +```json + +``` + +
+
+GET/api/v1/alerts/monitor/{monitorId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | GET | + +###### Response Payload + +> | Type | Notes | +> | -------------- | -------------------------------------------------------------- | +> | `Array` | Returns an array of `Alert` belonging to a specified `Monitor` | + +##### Sample CURL request + +``` + +``` + +###### Sample Response + +```json + +``` + +
+ +
+GET/api/v1/alerts/{alertId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | GET | + +###### Response Payload + +> | Type | Notes | +> | ------- | ------------------------- | +> | `Alert` | Returns specified `Alert` | + +##### Sample CURL request + +``` + +``` + +###### Sample Response + +```json + +``` + +
+ +
+POST/api/v1/alerts/edit/{alertId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +###### Response Payload + +> | Type | Notes | +> | ------- | -------------------- | +> | `Alert` | Returns edited alert | + +###### Body + +##### Sample CURL request + +``` + +``` + +###### Sample Response + +```json + +``` + +
+
+POST/api/v1/alerts/delete/{alertId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +###### Response Payload + +> | Type | Notes | +> | ---- | ------------------- | +> | None | No payload returned | + +###### Body + +##### Sample CURL request + +``` + +``` + +###### Sample Response + +```json + +``` + +
+ +--- + +### Error handling {#error-handling} + +Errors are returned in a standard format: + +`{"success": false, "msg": "No token provided"}` + +Errors are handled by error handling middleware and should be thrown with the following parameters + +| Name | Type | Default | Notes | +| ------- | --------- | ---------------------- | ------------------------------------ | +| status | `integer` | 500 | Standard HTTP codes | +| message | `string` | "Something went wrong" | An error message | +| service | `string` | "Unknown Service" | Name of service that threw the error | + +Example: + +``` +const myRoute = async(req, res, next) => { + try{ + const result = myRiskyOperationHere(); + } + catch(error){ + error.status = 404 + error.message = "Resource not found" + error.service = service name + next(error) + return; + } +} + +``` + +Errors should not be handled at the controller level and should be left to the middleware to handle. + ## Contributors diff --git a/Server/controllers/alertController.js b/Server/controllers/alertController.js new file mode 100644 index 000000000..91bb56971 --- /dev/null +++ b/Server/controllers/alertController.js @@ -0,0 +1,139 @@ +const express = require("express"); +const logger = require("../utils/logger"); +require("dotenv").config(); +const { + createAlertParamValidation, + createAlertBodyValidation, + getAlertsByUserIdParamValidation, + getAlertsByMonitorIdParamValidation, + getAlertByIdParamValidation, + editAlertParamValidation, + editAlertBodyValidation, + deleteAlertParamValidation, +} = require("../validation/joi"); + +const SERVICE_NAME = "alerts"; + +/** + * Creates an alert for a monitor + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const createAlert = async (req, res, next) => { + try { + await createAlertParamValidation.validateAsync(req.params); + await createAlertBodyValidation.validateAsync(req.body); + } catch (error) { + error.status = 422; + error.message = error.details[0].message; + error.service = SERVICE_NAME; + next(error); + return; + } + + try { + req.db.createAlert(req, res); + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } +}; + +/** + * Gets all alerts for a user + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const getAlertsByUserId = async (req, res, next) => { + try { + await getAlertsByUserIdParamValidation.validateAsync(req.params); + } catch (error) { + error.status = 422; + error.message = error.details[0].message; + error.service = SERVICE_NAME; + next(error); + return; + } + + try { + req.db.getAlertsByUserId(req, res); + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } +}; + +const getAlertsByMonitorId = async (req, res, next) => { + try { + await getAlertsByMonitorIdParamValidation.validateAsync(req.params); + res.status(200).json({ + success: true, + msg: "Get Alerts By MonitorID", + data: req.params.monitorId, + }); + } catch (error) { + error.status = 422; + res.status(200).json({ success: true, msg: "Edit alert" }); + + error.message = error.details[0].message; + error.service = SERVICE_NAME; + next(error); + return; + } +}; +const getAlertById = async (req, res, next) => { + try { + await getAlertByIdParamValidation.validateAsync(req.params); + res.status(200).json({ + success: true, + msg: "Get Alert By alertID", + data: req.params.alertId, + }); + } catch (error) { + error.status = 422; + error.message = error.details[0].message; + error.service = SERVICE_NAME; + next(error); + return; + } +}; +const editAlert = async (req, res, next) => { + try { + await editAlertParamValidation(req.params); + await editAlertBodyValidation(req.body); + res.status(200).json({ success: true, msg: "Edit alert" }); + } catch (error) { + error.status = 422; + error.message = error.details[0].message; + error.service = SERVICE_NAME; + next(error); + return; + } +}; +const deleteAlert = async (req, res, next) => { + try { + await deleteAlertParamValidation(req.params); + res.status(200).json({ success: true, msg: "Delete alert" }); + } catch (error) { + error.status = 422; + error.message = error.details[0].message; + error.service = SERVICE_NAME; + next(error); + return; + } +}; + +module.exports = { + createAlert, + getAlertsByUserId, + getAlertsByMonitorId, + getAlertById, + editAlert, + deleteAlert, +}; diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 1285726e1..257048937 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -1,5 +1,10 @@ const express = require("express"); -const { registerValidation, loginValidation } = require("../validation/joi"); +const { + registerValidation, + loginValidation, + editUserParamValidation, + editUserBodyValidation, +} = require("../validation/joi"); const logger = require("../utils/logger"); require("dotenv").config(); var jwt = require("jsonwebtoken"); @@ -12,7 +17,7 @@ const SERVICE_NAME = "auth"; * @returns {String} */ const issueToken = (payload) => { - return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "1h" }); + return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "99d" }); }; /** @@ -21,35 +26,32 @@ const issueToken = (payload) => { * @param {express.Response} res * @returns {{success: Boolean, msg: String}} */ -const registerController = async (req, res) => { +const registerController = async (req, res, next) => { // joi validation try { await registerValidation.validateAsync(req.body); } catch (error) { - return res - .status(400) - .json({ success: false, msg: error.details[0].message }); + error.status = 422; + error.service = SERVICE_NAME; + error.message = error.details[0].message; + next(error); + return; } // Check if the user exists try { const isUser = await req.db.getUserByEmail(req, res); if (isUser) { - logger.error("User already exists", { - service: SERVICE_NAME, - userId: isUser._id, - }); - return res - .status(400) - .json({ success: false, msg: "User already exists" }); + throw new Error("User already exists"); } } catch (error) { - logger.error(error.message, { service: SERVICE_NAME }); - return res.status(500).json({ success: false, msg: error.message }); + error.service = SERVICE_NAME; + next(error); + return; } + // Create a new user try { - // Create a new user const newUser = await req.db.insertUser(req, res); // TODO: Send an email to user // Will add this later @@ -63,8 +65,8 @@ const registerController = async (req, res) => { .status(200) .json({ success: true, msg: "User created", data: token }); } catch (error) { - logger.error(error.message, { service: SERVICE_NAME }); - return res.status(500).json({ success: false, msg: error.message }); + error.service = SERVICE_NAME; + next(error); } }; @@ -76,7 +78,7 @@ const registerController = async (req, res) => { * @returns {Promise} * @throws {Error} */ -const loginController = async (req, res) => { +const loginController = async (req, res, next) => { try { // Validate input await loginValidation.validateAsync(req.body); @@ -104,10 +106,42 @@ const loginController = async (req, res) => { .status(200) .json({ success: true, msg: "Found user", data: token }); } catch (error) { + error.status = 500; // Anything else should be an error - logger.error(error.message, { service: SERVICE_NAME }); - return res.status(500).json({ success: false, msg: error.message }); + next(error); } }; -module.exports = { registerController, loginController }; +const userEditController = async (req, res, next) => { + try { + await editUserParamValidation.validateAsync(req.params); + await editUserBodyValidation.validateAsync(req.body); + } catch (error) { + error.status = 422; + error.service = SERVICE_NAME; + error.message = error.details[0].message; + next(error); + return; + } + + if (req.params.userId !== req.user._id.toString()) { + const error = new Error("Unauthorized access"); + error.status = 401; + error.service = SERVICE_NAME; + next(error); + return; + } + + try { + const updatedUser = await req.db.updateUser(req, res); + res + .status(200) + .json({ success: true, msg: "User updated", data: updatedUser }); + } catch (error) { + error.service = SERVICE_NAME; + next(error); + return; + } +}; + +module.exports = { registerController, loginController, userEditController }; diff --git a/Server/controllers/checkController.js b/Server/controllers/checkController.js new file mode 100644 index 000000000..ef1a1a071 --- /dev/null +++ b/Server/controllers/checkController.js @@ -0,0 +1,37 @@ +var jwt = require("jsonwebtoken"); +const SERVICE_NAME = "check"; + +const createCheck = (req, res, next) => { + try { + req.db.createCheck(req, res); + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } +}; + +const getChecks = (req, res, next) => { + try { + req.db.getChecks(req, res); + // Return all checks for a monitor + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } +}; + +const deleteChecks = (req, res, next) => { + try { + req.db.deleteChecks(req, res); + // Delete all checks for a monitor + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } +}; + +module.exports = { + createCheck, + getChecks, + deleteChecks, +}; diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index f004f98f3..250dc55b8 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -4,7 +4,7 @@ const { monitorValidation, } = require("../validation/joi"); -const logger = require("../utils/logger"); +const SERVICE_NAME = "monitorController"; /** * Returns all monitors @@ -14,13 +14,13 @@ const logger = require("../utils/logger"); * @returns {Promise} * @throws {Error} */ -const getAllMonitors = async (req, res) => { +const getAllMonitors = async (req, res, next) => { try { const monitors = await req.db.getAllMonitors(); return res.json({ success: true, msg: "Monitors found", data: monitors }); } catch (error) { - logger.error(error.message, { service: "monitor" }); - return res.status(500).json({ success: false, msg: error.message }); + error.service = SERVICE_NAME; + next(error); } }; @@ -32,20 +32,28 @@ const getAllMonitors = async (req, res) => { * @returns {Promise} * @throws {Error} */ -const getMonitorById = async (req, res) => { - const { error } = getMonitorByIdValidation.validate(req.params); - if (error) { - return res - .status(422) - .json({ success: false, msg: error.details[0].message }); +const getMonitorById = async (req, res, next) => { + try { + await getMonitorByIdValidation.validateAsync(req.params); + } catch (error) { + error.status = 422; + error.message = error.details[0].message; + next(error); + return; } try { const monitor = await req.db.getMonitorById(req, res); + if (!monitor) { + const error = new Error("Monitor not found"); + error.status = 404; + throw error; + } + return res.json({ success: true, msg: "Monitor found", data: monitor }); } catch (error) { - logger.error(error.message, { service: "monitor" }); - return res.status(500).json({ success: false, msg: error.message }); + error.service = SERVICE_NAME; + next(error); } }; @@ -57,7 +65,7 @@ const getMonitorById = async (req, res) => { * @returns {Promise} * @throws {Error} */ -const getMonitorsByUserId = async (req, res) => { +const getMonitorsByUserId = async (req, res, next) => { const { error } = getMonitorsByUserIdValidation.validate(req.params); if (error) { return res @@ -68,18 +76,21 @@ const getMonitorsByUserId = async (req, res) => { try { const userId = req.params.userId; const monitors = await req.db.getMonitorsByUserId(req, res); - logger.info(`Monitors for user ${userId} found`, { - service: "monitor", - userId: userId, - }); + + if (monitors && monitors.length === 0) { + const err = new Error("No monitors found"); + err.status = 404; + throw err; + } + return res.json({ success: true, msg: `Monitors for user ${userId} found`, data: monitors, }); } catch (error) { - logger.error(error.message, { service: "monitor" }); - return res.status(500).json({ success: false, msg: error.message }); + error.service = SERVICE_NAME; + next(error); } }; @@ -92,12 +103,15 @@ const getMonitorsByUserId = async (req, res) => { * @throws {Error} */ -const createMonitor = async (req, res) => { - const { error } = monitorValidation.validate(req.body); - if (error) { - return res - .status(422) - .json({ success: false, msg: error.details[0].message }); +const createMonitor = async (req, res, next) => { + try { + await monitorValidation.validateAsync(req.body); + } catch (error) { + error.status = 422; + error.service = SERVICE_NAME; + error.message = error.details[0].message; + next(error); + return; } try { @@ -106,8 +120,8 @@ const createMonitor = async (req, res) => { .status(201) .json({ success: true, msg: "Monitor created", data: monitor }); } catch (error) { - logger.error(error.message, { service: "monitor" }); - return res.status(500).json({ success: false, msg: error.message }); + error.service = SERVICE_NAME; + next(error); } }; @@ -120,15 +134,19 @@ const createMonitor = async (req, res) => { * @throws {Error} */ -const deleteMonitor = async (req, res) => { - const { error } = getMonitorByIdValidation.validate(req.params); - if (error) { - return res - .status(422) - .json({ success: false, msg: error.details[0].message }); - } +const deleteMonitor = async (req, res, next) => { try { - const monitor = await req.db.deleteMonitor(req, res); + await getMonitorByIdValidation.validateAsync(req.params); + } catch (error) { + error.status = 422; + error.service = SERVICE_NAME; + error.message = error.details[0].message; + next(error); + return; + } + + try { + const monitor = await req.db.deleteMonitor(req, res, next); /** * TODO * We should remove all checks and alerts associated with this monitor @@ -137,8 +155,8 @@ const deleteMonitor = async (req, res) => { */ return res.status(200).json({ success: true, msg: "Monitor deleted" }); } catch (error) { - logger.error(error.message, { service: "monitor" }); - return res.status(500).json({ success: false, msg: error.message }); + error.service = SERVICE_NAME; + next(error); } }; @@ -150,19 +168,16 @@ const deleteMonitor = async (req, res) => { * @returns {Promise} * @throws {Error} */ -const editMonitor = async (req, res) => { - let paramError = getMonitorByIdValidation.validate(req.params); - if (paramError.error) { - return res - .status(422) - .json({ success: false, msg: paramError.error.details[0].message }); - } - - let { error } = monitorValidation.validate(req.body); - if (error) { - return res - .status(422) - .json({ success: false, msg: error.details[0].message }); +const editMonitor = async (req, res, next) => { + try { + await getMonitorByIdValidation.validateAsync(req.params); + await monitorValidation.validateAsync(req.body); + } catch (error) { + error.status = 422; + error.service = SERVICE_NAME; + error.message = error.details[0].message; + next(error); + return; } try { @@ -171,8 +186,8 @@ const editMonitor = async (req, res) => { .status(200) .json({ success: true, msg: "Monitor edited", data: editedMonitor }); } catch (error) { - logger.error(error.message, { service: "monitor" }); - return res.status(500).json({ success: false, msg: error.message }); + error.service = SERVICE_NAME; + next(error); } }; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index 8fcc1dced..32d5d33f7 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -2,6 +2,10 @@ const Monitor = require("../models/Monitor"); const mongoose = require("mongoose"); const UserModel = require("../models/user"); +const verifyId = (userId, monitorId) => { + return userId.toString() === monitorId.toString(); +}; + const connect = async () => { try { await mongoose.connect(process.env.DB_CONNECTION_STRING); @@ -12,6 +16,10 @@ const connect = async () => { } }; +//**************************************** +// Users +//**************************************** + /** * Insert a User * @async @@ -51,6 +59,35 @@ const getUserByEmail = async (req, res) => { } }; +//**************************************** +// Monitors +//**************************************** + +/** + * Update a user by ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ + +const updateUser = async (req, res) => { + const candidateUserId = req.params.userId; + const candidateUser = req.body; + + try { + const updatedUser = await UserModel.findByIdAndUpdate( + candidateUserId, + candidateUser, + { new: true } // Returns updated user instead of pre-update user + ).select("-password"); + return updatedUser; + } catch (error) { + throw error; + } +}; + /** * Get all monitors * @async @@ -113,6 +150,7 @@ const getMonitorsByUserId = async (req, res) => { const createMonitor = async (req, res) => { try { const monitor = new Monitor({ ...req.body }); + monitor.userId = req.user._id; await monitor.save(); return monitor; } catch (error) { @@ -164,14 +202,204 @@ const editMonitor = async (req, res) => { } }; +//**************************************** +// Checks +//**************************************** + +/** + * Create a check for a monitor + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @throws {Error} + */ + +const createCheck = async (req, res) => { + try { + // Create check with monitor id + res + .status(200) + .json({ success: true, msg: "Create check", data: req.params.monitorId }); + } catch (error) { + throw error; + } +}; + +/** + * Get all checks for a monitor + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @throws {Error} + */ + +const getChecks = async (req, res) => { + try { + // TODO get all checks for a monitor from DB + res + .status(200) + .json({ success: true, msg: "Get checks", data: req.params.monitorId }); + } catch (error) { + throw error; + } +}; + +/** + * Delete all checks for a monitor + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @throws {Error} + */ + +const deleteChecks = async (req, res) => { + try { + // TODO Delete all checks for a monitor + res.status(200).json({ + success: true, + msg: "Deleted checks", + data: req.params.monitorId, + }); + } catch (error) { + throw error; + } +}; + +//**************************************** +// Alerts +//**************************************** + +/** + * Creates an Alert associated with a Monitor and User + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {>} + * @throws {Error} + */ +const createAlert = async (req, res) => { + try { + res + .status(200) + .json({ success: true, msg: "Create Alert", data: req.params.monitorId }); + } catch (error) { + throw error; + } +}; + +/** + * Gets all alerts a User has set + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {>} + * @throws {Error} + */ +const getAlertsByUserId = async (req, res) => { + try { + res.status(200).json({ + success: true, + msg: "Get Alerts By UserID", + data: req.params.userId, + }); + } catch (error) { + throw error; + } +}; + +/** + * Gets all alerts a for an associated Monitor + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {>} + * @throws {Error} + */ +const getAlertsByMonitorId = async (req, res) => { + try { + res.status(200).json({ + success: true, + msg: "Get Alerts By MonitorID", + data: req.params.monitorId, + }); + } catch (error) { + throw error; + } +}; + +/** + * Gets an alert with specified ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const getAlertById = async (req, res) => { + try { + res.status(200).json({ + success: true, + msg: "Get Alert By alertID", + data: req.params.alertId, + }); + } catch (error) { + throw error; + } +}; + +/** + * Returns an edited monitor with the specified ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise>} + * @throws {Error} + */ +const editAlert = async (req, res) => { + try { + res + .status(200) + .json({ success: true, msg: "Edit alert", data: req.params.alertId }); + } catch (error) { + throw error; + } +}; + +/** + * Deletes a monitor with the specified ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @throws {Error} + */ +const deleteAlert = async (req, res) => { + try { + res + .status(200) + .json({ success: true, msg: "Delete alert", data: req.params.alertId }); + } catch (error) { + throw error; + } +}; + module.exports = { connect, insertUser, getUserByEmail, + updateUser, getAllMonitors, getMonitorById, getMonitorsByUserId, createMonitor, deleteMonitor, editMonitor, + createCheck, + getChecks, + deleteChecks, + createAlert, + getAlertsByUserId, + getAlertsByMonitorId, + getAlertById, + editAlert, + deleteAlert, }; diff --git a/Server/index.js b/Server/index.js index 5068c98d8..96f7553fd 100644 --- a/Server/index.js +++ b/Server/index.js @@ -3,10 +3,13 @@ const helmet = require("helmet"); const cors = require("cors"); const authRouter = require("./routes/authRoute"); const monitorRouter = require("./routes/monitorRoute"); +const checkRouter = require("./routes/checkRoute"); +const alertRouter = require("./routes/alertRoute"); const { connectDbAndRunServer } = require("./configs/db"); require("dotenv").config(); const logger = require("./utils/logger"); -var { verifyJWT } = require("./middleware/verifyJWT"); +const { verifyJWT } = require("./middleware/verifyJWT"); +const { handleErrors } = require("./middleware/handleErrors"); // const { sendEmail } = require('./utils/sendEmail') @@ -61,6 +64,8 @@ app.use((req, res, next) => { //routes app.use("/api/v1/auth", authRouter); app.use("/api/v1/monitors", verifyJWT, monitorRouter); +app.use("/api/v1/checks", verifyJWT, checkRouter); +app.use("/api/v1/alerts", verifyJWT, alertRouter); // Testing email service // app.use('/sendEmail', async (req, res) => { @@ -79,4 +84,10 @@ app.use("/api/v1/healthy", (req, res) => { } }); +/** + * Error handler middleware + * Should be called last + */ +app.use(handleErrors); + connectDbAndRunServer(app, db); diff --git a/Server/middleware/handleErrors.js b/Server/middleware/handleErrors.js new file mode 100644 index 000000000..a20db8571 --- /dev/null +++ b/Server/middleware/handleErrors.js @@ -0,0 +1,12 @@ +const logger = require("../utils/logger"); + +const handleErrors = (error, req, res, next) => { + const status = error.status || 500; + const message = error.message || "Something went wrong"; + const service = error.service || "Unknown service"; + + logger.error(error.message, { service: service }); + res.status(status).json({ success: false, msg: message }); +}; + +module.exports = { handleErrors }; diff --git a/Server/middleware/verifyJWT.js b/Server/middleware/verifyJWT.js index 53464c606..2e8895623 100644 --- a/Server/middleware/verifyJWT.js +++ b/Server/middleware/verifyJWT.js @@ -15,8 +15,11 @@ const verifyJWT = (req, res, next) => { const token = req.headers["authorization"]; // Make sure a token is provided if (!token) { - logger.error("No token provided", { service: SERVICE_NAME }); - return res.status(401).json({ success: false, msg: "No token provided" }); + const error = new Error("No token provided"); + error.status = 401; + error.service = SERVICE_NAME; + next(error); + return; } // Make sure it is properly formatted if (token.startsWith(TOKEN_PREFIX)) { @@ -32,10 +35,9 @@ const verifyJWT = (req, res, next) => { next(); }); } else { - logger.error("Invalid token format", { service: SERVICE_NAME }); - return res - .status(400) - .json({ success: false, msg: "Invalid token format" }); + error.status = 400; + error.service = SERVICE_NAME; + next(error); } }; diff --git a/Server/middleware/verifyOwnership.js b/Server/middleware/verifyOwnership.js new file mode 100644 index 000000000..409303fa4 --- /dev/null +++ b/Server/middleware/verifyOwnership.js @@ -0,0 +1,35 @@ +const logger = require("../utils/logger"); +const SERVICE_NAME = "verifyOwnership"; + +const verifyOwnership = (Model, paramName) => { + return async (req, res, next) => { + const userId = req.user._id; + const documentId = req.params[paramName]; + try { + const doc = await Model.findById(documentId); + //If the document is not found, return a 404 error + if (!doc) { + logger.error("Document not found", { + service: SERVICE_NAME, + }); + const error = new Error("Document not found"); + error.status = 404; + throw error; + } + + // If the userID does not match the document's userID, return a 403 error + if (userId.toString() !== doc.userId.toString()) { + console.log("boom"); + const error = new Error("Unauthorized access"); + error.status = 403; + throw error; + } + next(); + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } + }; +}; + +module.exports = { verifyOwnership }; diff --git a/Server/models/Alert.js b/Server/models/Alert.js new file mode 100644 index 000000000..758a7eba2 --- /dev/null +++ b/Server/models/Alert.js @@ -0,0 +1,38 @@ +const mongoose = require('mongoose') + +const AlertSchema = mongoose.Schema( + { + checkId: { + type: mongoose.Schema.Types.ObjectId, + ref: 'Check', + immutable: true, + }, + monitorId: { + type: mongoose.Schema.Types.ObjectId, + ref: 'Monitor', + immutable: true, + }, + userId: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User', + immutable: true, + }, + status: { + type: Boolean, + }, + message: { + type: String, + }, + notifiedStatus: { + type: Boolean, + }, + acknowledgeStatus: { + type: Boolean, + } + }, + { + timestamps: true, + } +); + +module.exports = mongoose.model('Alert', AlertSchema); diff --git a/Server/models/Check.js b/Server/models/Check.js new file mode 100644 index 000000000..eedc2a28e --- /dev/null +++ b/Server/models/Check.js @@ -0,0 +1,28 @@ +const mongoose = require('mongoose') + +const CheckSchema = mongoose.Schema( + { + monitorId: { + type: mongoose.Schema.Types.ObjectId, + ref: 'Monitor', + immutable: true, + }, + status: { + type: Boolean, + }, + responseTime: { + type: Number, // milliseconds + }, + statusCode: { + type: Number, // 200, ... , 500 + }, + message: { + type: String, + } + }, + { + timestamps: true, + } +); + +module.exports = mongoose.model('Check', CheckSchema); diff --git a/Server/models/user.js b/Server/models/user.js index 02d0d53b3..7f3945354 100644 --- a/Server/models/user.js +++ b/Server/models/user.js @@ -1,44 +1,41 @@ const mongoose = require("mongoose"); const bcrypt = require("bcrypt"); -const UserSchema = mongoose.Schema({ - firstname: { - type: String, - required: true, +const UserSchema = mongoose.Schema( + { + firstname: { + type: String, + required: true, + }, + lastname: { + type: String, + required: true, + }, + email: { + type: String, + required: true, + unique: true, + }, + password: { + type: String, + required: true, + }, + profilePicUrl: { + type: String, + }, + isActive: { + type: Boolean, + default: true, + }, + isVerified: { + type: Boolean, + default: false, + }, }, - lastname: { - type: String, - required: true, - }, - email: { - type: String, - required: true, - unique: true, - }, - password: { - type: String, - required: true, - }, - profilePicUrl: { - type: String, - }, - isActive: { - type: Boolean, - default: true, - }, - isVerified: { - type: Boolean, - default: false, - }, - updated_at: { - type: Date, - default: Date.now, - }, - created_at: { - type: Date, - default: Date.now, - }, -}); + { + timestamps: true, + } +); UserSchema.pre("save", async function (next) { if (!this.isModified("password")) { @@ -50,7 +47,6 @@ UserSchema.pre("save", async function (next) { }); UserSchema.methods.comparePassword = function (submittedPassword) { - console.log(submittedPassword, this.password); return bcrypt.compare(submittedPassword, this.password); }; diff --git a/Server/routes/alertRoute.js b/Server/routes/alertRoute.js new file mode 100644 index 000000000..be252e8c4 --- /dev/null +++ b/Server/routes/alertRoute.js @@ -0,0 +1,27 @@ +const router = require("express").Router(); +const alertController = require("../controllers/alertController"); +const { verifyOwnership } = require("../middleware/verifyOwnership"); +const Alert = require("../models/Alert"); + +// Create alert +router.post("/:monitorId", alertController.createAlert); +// Get all alerts for a user +router.get("/user/:userId", alertController.getAlertsByUserId); +// Get all alerts for a monitor +router.get("/monitor/:monitorId", alertController.getAlertsByMonitorId); +// Get a single alert +router.get("/:alertId", alertController.getAlertById); +// Edit +router.post( + "/edit/:alertId", + verifyOwnership(Alert, "alertId"), + alertController.editAlert +); +//Delete +router.post( + "/delete/:alertId", + verifyOwnership(Alert, "alertId"), + alertController.deleteAlert +); + +module.exports = router; diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index 48a173b92..637e4bf5e 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -1,10 +1,14 @@ const router = require("express").Router(); +const { verifyJWT } = require("../middleware/verifyJWT"); + const { registerController, loginController, + userEditController, } = require("../controllers/authController"); router.post("/register", registerController); router.post("/login", loginController); +router.post("/user/:userId", verifyJWT, userEditController); module.exports = router; diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js new file mode 100644 index 000000000..6569dd49a --- /dev/null +++ b/Server/routes/checkRoute.js @@ -0,0 +1,24 @@ +const router = require("express").Router(); +const checkController = require("../controllers/checkController"); +const { verifyOwnership } = require("../middleware/verifyOwnership"); +const Monitor = require("../models/Monitor"); + +router.post( + "/:monitorId", + verifyOwnership(Monitor, "monitorId"), + checkController.createCheck +); + +router.get( + "/:monitorId", + verifyOwnership(Monitor, "monitorId"), + checkController.getChecks +); + +router.post( + "/delete/:monitorId", + verifyOwnership(Monitor, "monitorId"), + checkController.deleteChecks +); + +module.exports = router; diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index 5f2ce4bb5..ffdce1d4d 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -1,11 +1,21 @@ const router = require("express").Router(); const monitorController = require("../controllers/monitorController"); +const { verifyOwnership } = require("../middleware/verifyOwnership"); +const Monitor = require("../models/Monitor"); router.get("/", monitorController.getAllMonitors); router.get("/:monitorId", monitorController.getMonitorById); router.get("/user/:userId", monitorController.getMonitorsByUserId); router.post("/", monitorController.createMonitor); -router.post("/delete/:monitorId", monitorController.deleteMonitor); -router.post("/edit/:monitorId", monitorController.editMonitor); +router.post( + "/delete/:monitorId", + verifyOwnership(Monitor, "monitorId"), + monitorController.deleteMonitor +); +router.post( + "/edit/:monitorId", + verifyOwnership(Monitor, "monitorId"), + monitorController.editMonitor +); module.exports = router; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index ecc19253e..01a0d0ada 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -1,5 +1,4 @@ const joi = require("joi"); -const user = require("../models/user"); const loginValidation = joi.object({ email: joi.string().email().required(), @@ -30,10 +29,71 @@ const monitorValidation = joi.object({ interval: joi.number(), }); +const editUserParamValidation = joi.object({ + userId: joi.string().required(), +}); + +const editUserBodyValidation = joi.object({ + firstname: joi.string(), + lastname: joi.string(), + profilePicUrl: joi.string(), +}); + +const createAlertParamValidation = joi.object({ + monitorId: joi.string().required(), +}); + +const createAlertBodyValidation = joi.object({ + checkId: joi.string().required(), + monitorId: joi.string().required(), + userId: joi.string().required(), + status: joi.boolean(), + message: joi.string(), + notifiedStatus: joi.boolean(), + acknowledgeStatus: joi.boolean(), +}); + +const getAlertsByUserIdParamValidation = joi.object({ + userId: joi.string().required(), +}); + +const getAlertsByMonitorIdParamValidation = joi.object({ + monitorId: joi.string().required(), +}); + +const getAlertByIdParamValidation = joi.object({ + alertId: joi.string().required(), +}); + +const editAlertParamValidation = joi.object({ + alertId: joi.string().required(), +}); + +const editAlertBodyValidation = joi.object({ + status: joi.boolean(), + message: joi.string(), + notifiedStatus: joi.boolean(), + acknowledgeStatus: joi.boolean(), +}); + +const deleteAlertParamValidation = joi.object({ + alertId: joi.string().required(), +}); + module.exports = { loginValidation, registerValidation, getMonitorByIdValidation, getMonitorsByUserIdValidation, monitorValidation, + editUserParamValidation, + editUserBodyValidation, + createAlertParamValidation, + createAlertBodyValidation, + getAlertsByUserIdParamValidation, + getAlertsByMonitorIdParamValidation, + getAlertByIdParamValidation, + editAlertParamValidation, + editAlertBodyValidation, + deleteAlertParamValidation, }; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f71a78c1f..000000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "bluewave-uptime", - "lockfileVersion": 3, - "requires": true, - "packages": {} -}