Merge branch 'bluewave-labs:master' into forgot-password
This commit is contained in:
22 files changed
+817
-139
No files matched your search
Generated
+230
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,19 @@
|
||||
import { BarChart } from '@mui/x-charts/BarChart';
|
||||
|
||||
const ChartsOverviewDemo = () => {
|
||||
return (
|
||||
<BarChart
|
||||
series={[
|
||||
{ data: [35, 44, 24, 34] },
|
||||
{ data: [51, 6, 49, 30] },
|
||||
{ data: [15, 25, 30, 50] },
|
||||
{ data: [60, 50, 15, 25] },
|
||||
]}
|
||||
height={290}
|
||||
xAxis={[{ data: ['Q1', 'Q2', 'Q3', 'Q4'], scaleType: 'band' }]}
|
||||
margin={{ top: 10, bottom: 30, left: 40, right: 10 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChartsOverviewDemo;
|
||||
Whitespace-only changes.
@@ -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 (
|
||||
* <DashboardSettings />
|
||||
* )
|
||||
*/
|
||||
function DashboardSettings() {
|
||||
const theme = useTheme();
|
||||
const [anchorElUser, setAnchorElUser] = useState(null);
|
||||
|
||||
/**
|
||||
* Handles opening the user menu.
|
||||
*
|
||||
* @param {React.MouseEvent<HTMLElement>} 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 (
|
||||
<Box sx={{ flexGrow: 0 }}>
|
||||
<Tooltip title="Open settings">
|
||||
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
||||
<SvgIcon component={SettingsIcon} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
sx={{ mt: theme.spacing(5.5) }}
|
||||
id="menu-appbar"
|
||||
anchorEl={anchorElUser}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={Boolean(anchorElUser)}
|
||||
onClose={handleCloseUserMenu}
|
||||
>
|
||||
{settings.map((setting) => (
|
||||
<MenuItem key={setting} onClick={handleCloseUserMenu}>
|
||||
<Typography textAlign="center">{setting}</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashboardSettings;
|
||||
@@ -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 = () => {
|
||||
<NavBar />
|
||||
<Home />
|
||||
<Outlet />
|
||||
<ChartsOverviewDemo />
|
||||
<DashboardSettings />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,9 +7,20 @@ 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)
|
||||
- <code>POST</code> [/api/v1/auth/register](#post-register)
|
||||
- <code>POST</code> [/api/v1/auth/login](#post-login)
|
||||
- <code>POST</code> [/api/v1/auth/user/{userId}](#post-auth-user-edit-id)
|
||||
- <code>GET</code> [/api/v1/monitors](#get-monitors)
|
||||
- <code>GET</code> [/api/v1/monitor/{id}](#get-monitor-id)
|
||||
- <code>GET</code> [/api/v1/monitors/user/{userId}](#get-monitors-user-userid)
|
||||
- <code>POST</code> [/api/v1/monitors](#post-monitors)
|
||||
- <code>POST</code> [/api/v1/monitors/delete/{monitorId}](#post-monitors-del-id)
|
||||
- <code>POST</code> [/api/v1/monitors/edit/{monitorId}](#post-monitors-edit-id)
|
||||
5. [Error Handling](#error-handling)
|
||||
6. [Contributors](#contributors)
|
||||
|
||||
---
|
||||
|
||||
@@ -108,10 +119,39 @@ Example:
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><code>Check</code></summary>
|
||||
|
||||
| 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 |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><code>Alert</code></summary>
|
||||
|
||||
| Name | Type | Notes |
|
||||
| ----------- | --------- | --------------------------------------------------|
|
||||
| checkId | `string` | Unique ID for the check |
|
||||
| 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 |
|
||||
|
||||
</details>
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>POST</code> <b>/api/v1/auth/register</b></summary>
|
||||
<summary id='post-register'><code>POST</code> <b>/api/v1/auth/register</b></summary>
|
||||
|
||||
##### Method/Headers
|
||||
|
||||
@@ -164,7 +204,7 @@ curl --request POST \
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>POST</code> <b>/api/v1/auth/login</b></summary>
|
||||
<summary id='post-login'><code>POST</code> <b>/api/v1/auth/login</b></summary>
|
||||
|
||||
##### Method/Headers
|
||||
|
||||
@@ -213,7 +253,68 @@ curl --request POST \
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>GET</code> <b>/api/v1/monitors</b></summary>
|
||||
<summary id='post-auth-user-edit-id'><code>POST</code><b>/api/v1/auth/user/{userId}</b></summary>
|
||||
|
||||
###### 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: <bearer_token>' \
|
||||
--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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary id='get-monitors'><code>GET</code> <b>/api/v1/monitors</b></summary>
|
||||
|
||||
##### Method/Headers
|
||||
|
||||
@@ -275,7 +376,7 @@ curl --request GET \
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>GET</code> <b>/api/v1/monitor/{id}</b></summary>
|
||||
<summary id='get-monitor-id'><code>GET</code> <b>/api/v1/monitor/{id}</b></summary>
|
||||
|
||||
###### Method/Headers
|
||||
|
||||
@@ -323,7 +424,7 @@ curl --request GET \
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>GET</code> <b>/api/v1/monitors/user/{userId}</b></summary>
|
||||
<summary id='get-monitors-user-userid'><code>GET</code> <b>/api/v1/monitors/user/{userId}</b></summary>
|
||||
|
||||
###### Method/Headers
|
||||
|
||||
@@ -385,7 +486,7 @@ curl --request GET \
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>POST</code><b>/api/v1/monitors</b></summary>
|
||||
<summary id='post-monitors'><code>POST</code><b>/api/v1/monitors</b></summary>
|
||||
|
||||
###### Method/Headers
|
||||
|
||||
@@ -440,7 +541,7 @@ curl --request POST \
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>POST</code><b>/api/v1/monitors/delete/{monitorId}</b></summary>
|
||||
<summary id='post-monitors-del-id'><code>POST</code><b>/api/v1/monitors/delete/{monitorId}</b></summary>
|
||||
|
||||
###### Method/Headers
|
||||
|
||||
@@ -477,7 +578,7 @@ curl --request POST \
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>POST</code><b>/api/v1/monitors/edit/{monitorId}</b></summary>
|
||||
<summary id='post-monitors-edit-id'><code>POST</code><b>/api/v1/monitors/edit/{monitorId}</b></summary>
|
||||
|
||||
###### Method/Headers
|
||||
|
||||
@@ -531,6 +632,44 @@ curl --request POST \
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### 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
|
||||
|
||||
<a href="https://github.com/bluewave-labs/bluewave-uptime/graphs/contributors">
|
||||
|
||||
@@ -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");
|
||||
@@ -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 = 400;
|
||||
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<Express.Response>}
|
||||
* @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 };
|
||||
@@ -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<Express.Response>}
|
||||
* @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<Express.Response>}
|
||||
* @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<Express.Response>}
|
||||
* @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<Express.Response>}
|
||||
* @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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -51,6 +55,31 @@ const getUserByEmail = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a user by ID
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @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 +142,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) {
|
||||
@@ -168,6 +198,7 @@ module.exports = {
|
||||
connect,
|
||||
insertUser,
|
||||
getUserByEmail,
|
||||
updateUser,
|
||||
getAllMonitors,
|
||||
getMonitorById,
|
||||
getMonitorsByUserId,
|
||||
|
||||
+8
-1
@@ -6,7 +6,8 @@ const monitorRouter = require("./routes/monitorRoute");
|
||||
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')
|
||||
|
||||
@@ -79,4 +80,10 @@ app.use("/api/v1/healthy", (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Error handler middleware
|
||||
* Should be called last
|
||||
*/
|
||||
app.use(handleErrors);
|
||||
|
||||
connectDbAndRunServer(app, db);
|
||||
@@ -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 };
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 };
|
||||
@@ -0,0 +1,28 @@
|
||||
const mongoose = require('mongoose')
|
||||
|
||||
const AlertSchema = mongoose.Schema(
|
||||
{
|
||||
checkId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'Check',
|
||||
immutable: true,
|
||||
},
|
||||
status: {
|
||||
type: Boolean,
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
},
|
||||
notifiedStatus: {
|
||||
type: Boolean,
|
||||
},
|
||||
acknowledgeStatus: {
|
||||
type: Boolean,
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
module.exports = mongoose.model('Alert', AlertSchema);
|
||||
@@ -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);
|
||||
+34
-38
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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,22 @@ 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(),
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
loginValidation,
|
||||
registerValidation,
|
||||
getMonitorByIdValidation,
|
||||
getMonitorsByUserIdValidation,
|
||||
monitorValidation,
|
||||
editUserParamValidation,
|
||||
editUserBodyValidation,
|
||||
};
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "bluewave-uptime",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
Reference in new issue
Block a user