mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-17 07:10:08 -06:00
Merge branch 'develop' into fix/safe-data-access
This commit is contained in:
@@ -19,6 +19,7 @@ import ProgressUpload from "../../ProgressBars";
|
||||
import { formatBytes } from "../../../Utils/fileUtils";
|
||||
import { clearUptimeMonitorState } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
|
||||
/**
|
||||
* ProfilePanel component displays a form for editing user profile information
|
||||
@@ -256,7 +257,7 @@ const ProfilePanel = () => {
|
||||
placeholder="Enter your email"
|
||||
autoComplete="email"
|
||||
// TODO - add onChange
|
||||
onChange={() => console.log("Disabled.")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
// error={errors[idToName["edit-email"]]}
|
||||
disabled={true}
|
||||
/>
|
||||
|
||||
@@ -16,7 +16,7 @@ import { useEffect, useState } from "react";
|
||||
import EditSvg from "../../../assets/icons/edit.svg?react";
|
||||
import Field from "../../Inputs/Field";
|
||||
import { credentials } from "../../../Validation/validation";
|
||||
import axiosInstance from "../../../Utils/axiosConfig";
|
||||
import { networkService } from "../../../main";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { useSelector } from "react-redux";
|
||||
import BasicTable from "../../BasicTable";
|
||||
@@ -52,10 +52,7 @@ const TeamPanel = () => {
|
||||
useEffect(() => {
|
||||
const fetchTeam = async () => {
|
||||
try {
|
||||
const response = await axiosInstance.get("/auth/users", {
|
||||
headers: { Authorization: `Bearer ${authToken}` },
|
||||
});
|
||||
|
||||
const response = await networkService.getAllUsers(authToken);
|
||||
setMembers(response.data.data);
|
||||
} catch (error) {
|
||||
createToast({
|
||||
@@ -179,13 +176,10 @@ const TeamPanel = () => {
|
||||
setErrors((prev) => ({ ...prev, email: error.details[0].message }));
|
||||
} else
|
||||
try {
|
||||
await axiosInstance.post(
|
||||
"/auth/invite",
|
||||
{
|
||||
email: toInvite.email,
|
||||
role: toInvite.role,
|
||||
},
|
||||
{ headers: { Authorization: `Bearer ${authToken}` } }
|
||||
await networkService.requestInvitationToken(
|
||||
authToken,
|
||||
toInvite.email,
|
||||
toInvite.role
|
||||
);
|
||||
|
||||
closeInviteModal();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import axiosInstance from "../../Utils/axiosConfig";
|
||||
import { networkService } from "../../main";
|
||||
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
|
||||
@@ -14,7 +14,7 @@ export const register = createAsyncThunk(
|
||||
"auth/register",
|
||||
async (form, thunkApi) => {
|
||||
try {
|
||||
const res = await axiosInstance.post("/auth/register", form);
|
||||
const res = await networkService.registerUser(form);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response.data) {
|
||||
@@ -31,7 +31,7 @@ export const register = createAsyncThunk(
|
||||
|
||||
export const login = createAsyncThunk("auth/login", async (form, thunkApi) => {
|
||||
try {
|
||||
const res = await axiosInstance.post(`/auth/login`, form);
|
||||
const res = await networkService.loginUser(form);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -51,16 +51,13 @@ export const update = createAsyncThunk(
|
||||
const { authToken: token, localData: form } = data;
|
||||
const user = jwtDecode(token);
|
||||
try {
|
||||
//1.5s delay to show loading spinner
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
|
||||
const fd = new FormData();
|
||||
form.firstName && fd.append("firstName", form.firstName);
|
||||
form.lastName && fd.append("lastName", form.lastName);
|
||||
form.password && fd.append("password", form.password);
|
||||
form.newPassword && fd.append("newPassword", form.newPassword);
|
||||
if (form.file && form.file !== "") {
|
||||
const imageResult = await axiosInstance.get(form.file, {
|
||||
const imageResult = await networkService.get(form.file, {
|
||||
responseType: "blob",
|
||||
baseURL: "",
|
||||
});
|
||||
@@ -69,12 +66,8 @@ export const update = createAsyncThunk(
|
||||
form.deleteProfileImage &&
|
||||
fd.append("deleteProfileImage", form.deleteProfileImage);
|
||||
|
||||
const res = await axiosInstance.put(`/auth/user/${user._id}`, fd, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
const res = await networkService.updateUser(token, user._id, fd);
|
||||
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -95,9 +88,7 @@ export const deleteUser = createAsyncThunk(
|
||||
const user = jwtDecode(data);
|
||||
|
||||
try {
|
||||
const res = await axiosInstance.delete(`/auth/user/${user._id}`, {
|
||||
headers: { Authorization: `Bearer ${data}` },
|
||||
});
|
||||
const res = await networkService.deleteUser(data, user._id);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -116,7 +107,7 @@ export const forgotPassword = createAsyncThunk(
|
||||
"auth/forgotPassword",
|
||||
async (form, thunkApi) => {
|
||||
try {
|
||||
const res = await axiosInstance.post("/auth/recovery/request", form);
|
||||
const res = await networkService.forgotPassword(form);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response.data) {
|
||||
@@ -136,13 +127,8 @@ export const setNewPassword = createAsyncThunk(
|
||||
async (data, thunkApi) => {
|
||||
const { token, form } = data;
|
||||
try {
|
||||
await axiosInstance.post("/auth/recovery/validate", {
|
||||
recoveryToken: token,
|
||||
});
|
||||
const res = await axiosInstance.post("/auth/recovery/reset", {
|
||||
...form,
|
||||
recoveryToken: token,
|
||||
});
|
||||
await networkService.validateRecoveryToken(token);
|
||||
const res = await networkService.setNewPassword(token, form);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response.data) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import axiosInstance from "../../Utils/axiosConfig";
|
||||
import { networkService } from "../../main";
|
||||
const initialState = {
|
||||
isLoading: false,
|
||||
monitors: [],
|
||||
@@ -13,32 +13,7 @@ export const createPageSpeed = createAsyncThunk(
|
||||
async (data, thunkApi) => {
|
||||
try {
|
||||
const { authToken, monitor } = data;
|
||||
|
||||
const res = await axiosInstance.post(`/monitors`, monitor, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
return thunkApi.rejectWithValue(error.response.data);
|
||||
}
|
||||
const payload = {
|
||||
status: false,
|
||||
msg: error.message ? error.message : "Unknown error",
|
||||
};
|
||||
return thunkApi.rejectWithValue(payload);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const getPageSpeedMonitors = createAsyncThunk(
|
||||
"pageSpeedMonitors/getPageSpeedMonitors",
|
||||
async (token, thunkApi) => {
|
||||
try {
|
||||
const res = await axiosInstance.get("/monitors");
|
||||
const res = await networkService.createMonitor(authToken, monitor);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -58,14 +33,16 @@ export const getPageSpeedByUserId = createAsyncThunk(
|
||||
async (token, thunkApi) => {
|
||||
const user = jwtDecode(token);
|
||||
try {
|
||||
const res = await axiosInstance.get(
|
||||
`/monitors/user/${user._id}?limit=25&type=pagespeed&sortOrder=desc`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
const res = await networkService.getMonitorsByUserId(
|
||||
token,
|
||||
user._id,
|
||||
25,
|
||||
["pagespeed"],
|
||||
null,
|
||||
"desc",
|
||||
false
|
||||
);
|
||||
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -91,15 +68,10 @@ export const updatePageSpeed = createAsyncThunk(
|
||||
interval: monitor.interval,
|
||||
// notifications: monitor.notifications,
|
||||
};
|
||||
const res = await axiosInstance.put(
|
||||
`/monitors/${monitor._id}`,
|
||||
updatedFields,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
const res = await networkService.updateMonitor(
|
||||
authToken,
|
||||
monitor._id,
|
||||
updatedFields
|
||||
);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
@@ -120,12 +92,10 @@ export const deletePageSpeed = createAsyncThunk(
|
||||
async (data, thunkApi) => {
|
||||
try {
|
||||
const { authToken, monitor } = data;
|
||||
const res = await axiosInstance.delete(`/monitors/${monitor._id}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const res = await networkService.deleteMonitorById(
|
||||
authToken,
|
||||
monitor._id
|
||||
);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -153,25 +123,6 @@ const pageSpeedMonitorSlice = createSlice({
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
// *****************************************************
|
||||
// All Monitors
|
||||
// *****************************************************
|
||||
.addCase(getPageSpeedMonitors.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
})
|
||||
.addCase(getPageSpeedMonitors.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = action.payload.success;
|
||||
state.msg = action.payload.msg;
|
||||
state.monitors = action.payload.data;
|
||||
})
|
||||
.addCase(getPageSpeedMonitors.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = false;
|
||||
state.msg = action.payload
|
||||
? action.payload.msg
|
||||
: "Getting montiors failed";
|
||||
})
|
||||
// *****************************************************
|
||||
// Monitors by userId
|
||||
// *****************************************************
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import axiosInstance from "../../Utils/axiosConfig";
|
||||
import { networkService } from "../../main";
|
||||
const initialState = {
|
||||
isLoading: false,
|
||||
monitors: [],
|
||||
@@ -13,32 +13,7 @@ export const createUptimeMonitor = createAsyncThunk(
|
||||
async (data, thunkApi) => {
|
||||
try {
|
||||
const { authToken, monitor } = data;
|
||||
|
||||
const res = await axiosInstance.post(`/monitors`, monitor, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
return thunkApi.rejectWithValue(error.response.data);
|
||||
}
|
||||
const payload = {
|
||||
status: false,
|
||||
msg: error.message ? error.message : "Unknown error",
|
||||
};
|
||||
return thunkApi.rejectWithValue(payload);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const getUptimeMonitors = createAsyncThunk(
|
||||
"monitors/getMonitors",
|
||||
async (token, thunkApi) => {
|
||||
try {
|
||||
const res = await axiosInstance.get("/monitors");
|
||||
const res = await networkService.createMonitor(authToken, monitor);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -58,13 +33,14 @@ export const getUptimeMonitorsByUserId = createAsyncThunk(
|
||||
async (token, thunkApi) => {
|
||||
const user = jwtDecode(token);
|
||||
try {
|
||||
const res = await axiosInstance.get(
|
||||
`/monitors/user/${user._id}?limit=25&type=http&type=ping&sortOrder=desc&normalize=true`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
const res = await networkService.getMonitorsByUserId(
|
||||
token,
|
||||
user._id,
|
||||
25,
|
||||
["http", "ping"],
|
||||
null,
|
||||
"desc",
|
||||
true
|
||||
);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
@@ -91,15 +67,10 @@ export const updateUptimeMonitor = createAsyncThunk(
|
||||
interval: monitor.interval,
|
||||
notifications: monitor.notifications,
|
||||
};
|
||||
const res = await axiosInstance.put(
|
||||
`/monitors/${monitor._id}`,
|
||||
updatedFields,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
const res = await networkService.updateMonitor(
|
||||
authToken,
|
||||
monitor._id,
|
||||
updatedFields
|
||||
);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
@@ -120,12 +91,10 @@ export const deleteUptimeMonitor = createAsyncThunk(
|
||||
async (data, thunkApi) => {
|
||||
try {
|
||||
const { authToken, monitor } = data;
|
||||
const res = await axiosInstance.delete(`/monitors/${monitor._id}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const res = await networkService.deleteMonitorById(
|
||||
authToken,
|
||||
monitor._id
|
||||
);
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -153,25 +122,6 @@ const uptimeMonitorsSlice = createSlice({
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
// *****************************************************
|
||||
// All Monitors
|
||||
// *****************************************************
|
||||
.addCase(getUptimeMonitors.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
})
|
||||
.addCase(getUptimeMonitors.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = action.payload.success;
|
||||
state.msg = action.payload.msg;
|
||||
state.monitors = action.payload.data;
|
||||
})
|
||||
.addCase(getUptimeMonitors.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = false;
|
||||
state.msg = action.payload
|
||||
? action.payload.msg
|
||||
: "Getting uptime monitors failed";
|
||||
})
|
||||
// *****************************************************
|
||||
// Monitors by userId
|
||||
// *****************************************************
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import axiosInstance from "../Utils/axiosConfig";
|
||||
|
||||
import { logger } from "../Utils/Logger";
|
||||
import { networkService } from "../main";
|
||||
|
||||
const withAdminCheck = (WrappedComponent) => {
|
||||
const WithAdminCheck = (props) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
axiosInstance
|
||||
.get("/auth/users/admin")
|
||||
networkService
|
||||
.doesAdminExist()
|
||||
.then((response) => {
|
||||
if (response.data.data === true) {
|
||||
navigate("/login");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
logger.error(error);
|
||||
});
|
||||
}, [navigate]);
|
||||
return <WrappedComponent {...props} isAdmin={true} />;
|
||||
|
||||
@@ -7,14 +7,14 @@ import { login } from "../../Features/Auth/authSlice";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { createToast } from "../../Utils/toastUtils";
|
||||
import Button from "../../Components/Button";
|
||||
import axiosInstance from "../../Utils/axiosConfig";
|
||||
import { networkService } from "../../main";
|
||||
import Field from "../../Components/Inputs/Field";
|
||||
import background from "../../assets/Images/background_pattern_decorative.png";
|
||||
import Logo from "../../assets/icons/bwu-icon.svg?react";
|
||||
import Mail from "../../assets/icons/mail.svg?react";
|
||||
import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { logger } from "../../Utils/Logger";
|
||||
import "./index.css";
|
||||
|
||||
/**
|
||||
@@ -276,15 +276,15 @@ const Login = () => {
|
||||
navigate("/monitors");
|
||||
return;
|
||||
}
|
||||
axiosInstance
|
||||
.get("/auth/users/admin")
|
||||
networkService
|
||||
.doesAdminExist()
|
||||
.then((response) => {
|
||||
if (response.data.data === false) {
|
||||
navigate("/register");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
logger.error(error);
|
||||
});
|
||||
}, [authToken, navigate]);
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@ import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
|
||||
import Check from "../../../Components/Check/Check";
|
||||
import Button from "../../../Components/Button";
|
||||
import Field from "../../../Components/Inputs/Field";
|
||||
import axiosInstance from "../../../Utils/axiosConfig";
|
||||
import { networkService } from "../../../main";
|
||||
import "../index.css";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
|
||||
/**
|
||||
* Displays the initial landing page.
|
||||
@@ -397,14 +398,11 @@ const Register = ({ isAdmin }) => {
|
||||
const fetchInvite = async () => {
|
||||
if (token !== undefined) {
|
||||
try {
|
||||
const res = await axiosInstance.post(`/auth/invite/verify`, {
|
||||
token,
|
||||
});
|
||||
const res = await networkService.verifyInvitationToken(token);
|
||||
const { role, email } = res.data.data;
|
||||
console.log(role);
|
||||
setForm({ ...form, email, role });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.error(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,8 +18,9 @@ import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import axiosInstance from "../../../Utils/axiosConfig";
|
||||
import { networkService } from "../../../main";
|
||||
import { StatusLabel } from "../../../Components/Label";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
|
||||
const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
|
||||
const { authToken, user } = useSelector((state) => state.auth);
|
||||
@@ -43,21 +44,34 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let url = `/checks/${selectedMonitor}?sortOrder=desc&filter=${filter}&page=${paginationController.page}&rowsPerPage=${paginationController.rowsPerPage}`;
|
||||
|
||||
let res;
|
||||
if (selectedMonitor === "0") {
|
||||
url = `/checks/user/${user._id}?sortOrder=desc&filter=${filter}&page=${paginationController.page}&rowsPerPage=${paginationController.rowsPerPage}`;
|
||||
res = await networkService.getChecksByUser(
|
||||
authToken,
|
||||
user._id,
|
||||
"desc",
|
||||
null,
|
||||
null,
|
||||
filter,
|
||||
paginationController.page,
|
||||
paginationController.rowsPerPage
|
||||
);
|
||||
} else {
|
||||
res = await networkService.getChecksByMonitor(
|
||||
authToken,
|
||||
selectedMonitor,
|
||||
"desc",
|
||||
null,
|
||||
null,
|
||||
filter,
|
||||
paginationController.page,
|
||||
paginationController.rowsPerPage
|
||||
);
|
||||
}
|
||||
|
||||
const res = await axiosInstance.get(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
});
|
||||
setChecks(res.data.data.checks);
|
||||
setChecksCount(res.data.data.checksCount);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.error(error);
|
||||
}
|
||||
};
|
||||
fetchPage();
|
||||
@@ -72,7 +86,6 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
|
||||
]);
|
||||
|
||||
const handlePageChange = (_, newPage) => {
|
||||
console.log(newPage);
|
||||
setPaginationController({
|
||||
...paginationController,
|
||||
page: newPage - 1, // 0-indexed
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { ButtonGroup, Stack, Skeleton, Typography } from "@mui/material";
|
||||
import Button from "../../Components/Button";
|
||||
import axiosInstance from "../../Utils/axiosConfig";
|
||||
import { networkService } from "../../main";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Select from "../../Components/Inputs/Select";
|
||||
import IncidentTable from "./IncidentTable";
|
||||
@@ -48,13 +48,14 @@ const Incidents = () => {
|
||||
useEffect(() => {
|
||||
const fetchMonitors = async () => {
|
||||
setLoading(true);
|
||||
const res = await axiosInstance.get(
|
||||
`/monitors/user/${authState.user._id}?status=false&limit=1`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authState.authToken}`,
|
||||
},
|
||||
}
|
||||
const res = await networkService.getMonitorsByUserId(
|
||||
authState.authToken,
|
||||
authState.user._id,
|
||||
1,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// Reduce to a lookup object for 0(1) lookup
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
import Checkbox from "../../../Components/Inputs/Checkbox";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import "./index.css";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
|
||||
/**
|
||||
* Parses a URL string and returns a URL object.
|
||||
@@ -108,13 +109,13 @@ const Configure = () => {
|
||||
useEffect(() => {
|
||||
const data = monitors.find((monitor) => monitor._id === monitorId);
|
||||
if (!data) {
|
||||
console.error("Error fetching monitor of id: " + monitorId);
|
||||
navigate("/not-found");
|
||||
logger.error("Error fetching monitor of id: " + monitorId);
|
||||
navigate("/not-found", { replace: true });
|
||||
}
|
||||
setMonitor({
|
||||
...data,
|
||||
});
|
||||
}, [monitorId, authToken, monitors]);
|
||||
}, [monitorId, authToken, monitors, navigate]);
|
||||
|
||||
const handleChange = (event, name) => {
|
||||
let { value, id } = event.target;
|
||||
@@ -337,7 +338,7 @@ const Configure = () => {
|
||||
label="Notify via SMS (coming soon)"
|
||||
isChecked={false}
|
||||
value=""
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
isDisabled={true}
|
||||
/>
|
||||
<Checkbox
|
||||
@@ -356,7 +357,7 @@ const Configure = () => {
|
||||
label="Also notify via email to multiple addresses (coming soon)"
|
||||
isChecked={false}
|
||||
value=""
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
isDisabled={true}
|
||||
/>
|
||||
{monitor?.notifications?.some(
|
||||
@@ -368,7 +369,7 @@ const Configure = () => {
|
||||
type="text"
|
||||
placeholder="name@gmail.com"
|
||||
value=""
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
/>
|
||||
<Typography mt={theme.gap.small}>
|
||||
You can separate multiple emails with a comma
|
||||
|
||||
@@ -13,6 +13,7 @@ import Select from "../../../Components/Inputs/Select";
|
||||
import Checkbox from "../../../Components/Inputs/Checkbox";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
|
||||
const CreateMonitor = () => {
|
||||
const MS_PER_MINUTE = 60000;
|
||||
@@ -267,7 +268,7 @@ const CreateMonitor = () => {
|
||||
label="Notify via SMS (coming soon)"
|
||||
isChecked={false}
|
||||
value=""
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
isDisabled={true}
|
||||
/>
|
||||
<Checkbox
|
||||
@@ -284,7 +285,7 @@ const CreateMonitor = () => {
|
||||
label="Also notify via email to multiple addresses (coming soon)"
|
||||
isChecked={false}
|
||||
value=""
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
isDisabled={true}
|
||||
/>
|
||||
{monitor.notifications.some(
|
||||
@@ -296,7 +297,7 @@ const CreateMonitor = () => {
|
||||
type="text"
|
||||
placeholder="name@gmail.com"
|
||||
value=""
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
/>
|
||||
<Typography mt={theme.gap.small}>
|
||||
You can separate multiple emails with a comma
|
||||
|
||||
@@ -13,10 +13,11 @@ import {
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import axiosInstance from "../../../../Utils/axiosConfig";
|
||||
import { networkService } from "../../../../main";
|
||||
import { StatusLabel } from "../../../../Components/Label";
|
||||
import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
|
||||
import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded";
|
||||
import { logger } from "../../../../Utils/Logger";
|
||||
|
||||
const PaginationTable = ({ monitorId, dateRange }) => {
|
||||
const { authToken } = useSelector((state) => state.auth);
|
||||
@@ -37,18 +38,20 @@ const PaginationTable = ({ monitorId, dateRange }) => {
|
||||
useEffect(() => {
|
||||
const fetchPage = async () => {
|
||||
try {
|
||||
const res = await axiosInstance.get(
|
||||
`/checks/${monitorId}?sortOrder=desc&dateRange=${dateRange}&page=${paginationController.page}&rowsPerPage=${paginationController.rowsPerPage}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
}
|
||||
const res = await networkService.getChecksByMonitor(
|
||||
authToken,
|
||||
monitorId,
|
||||
"desc",
|
||||
null,
|
||||
dateRange,
|
||||
null,
|
||||
paginationController.page,
|
||||
paginationController.rowsPerPage
|
||||
);
|
||||
setChecks(res.data.data.checks);
|
||||
setChecksCount(res.data.data.checksCount);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.error(error);
|
||||
}
|
||||
};
|
||||
fetchPage();
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
|
||||
import { Box, Skeleton, Stack, Typography, useTheme } from "@mui/material";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import axiosInstance from "../../../Utils/axiosConfig";
|
||||
import { networkService } from "../../../main";
|
||||
import MonitorDetailsAreaChart from "../../../Components/Charts/MonitorDetailsAreaChart";
|
||||
import ButtonGroup from "@mui/material/ButtonGroup";
|
||||
import Button from "../../../Components/Button";
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
} from "../../../Utils/timeUtils";
|
||||
import "./index.css";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
|
||||
const StatBox = ({ title, value }) => {
|
||||
return (
|
||||
@@ -120,19 +121,19 @@ const DetailsPage = () => {
|
||||
|
||||
const fetchMonitor = useCallback(async () => {
|
||||
try {
|
||||
const res = await axiosInstance.get(
|
||||
`/monitors/stats/${monitorId}?dateRange=${dateRange}&numToDisplay=50&normalize=true`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
}
|
||||
const res = await networkService.getStatsByMonitorId(
|
||||
authToken,
|
||||
monitorId,
|
||||
null,
|
||||
null,
|
||||
dateRange,
|
||||
50,
|
||||
true
|
||||
);
|
||||
setMonitor(res?.data?.data ?? {});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
navigate("/not-found");
|
||||
logger.error(error);
|
||||
navigate("/not-found", { replace: true });
|
||||
}
|
||||
}, [authToken, monitorId, navigate, dateRange]);
|
||||
|
||||
@@ -143,18 +144,15 @@ const DetailsPage = () => {
|
||||
useEffect(() => {
|
||||
const fetchCertificate = async () => {
|
||||
try {
|
||||
const res = await axiosInstance.get(
|
||||
`/monitors/certificate/${monitorId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
setCertificateExpiry(res?.data?.data?.certificateDate ?? "N/A");
|
||||
const res = await networkService.getCertificateExpiry(
|
||||
authToken,
|
||||
monitorId
|
||||
);
|
||||
setCertificateExpiry(res?.data?.data?.certificateDate ?? "N/A");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
};
|
||||
fetchCertificate();
|
||||
}, [authToken, monitorId]);
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
|
||||
import Settings from "../../assets/icons/settings-bold.svg?react";
|
||||
import PropTypes from "prop-types";
|
||||
import { logger } from "../../Utils/Logger";
|
||||
|
||||
const ActionsMenu = ({ monitor }) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
@@ -36,7 +37,6 @@ const ActionsMenu = ({ monitor }) => {
|
||||
const dispatch = useDispatch();
|
||||
const theme = useTheme();
|
||||
const authState = useSelector((state) => state.auth);
|
||||
|
||||
const handleRemove = async (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
@@ -18,6 +18,7 @@ import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline";
|
||||
import GreenCheck from "../../../assets/icons/checkbox-green.svg?react";
|
||||
import RedCheck from "../../../assets/icons/checkbox-red.svg?react";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
@@ -92,8 +93,8 @@ const PageSpeedConfigure = () => {
|
||||
useEffect(() => {
|
||||
const data = monitors.find((monitor) => monitor._id === monitorId);
|
||||
if (!data) {
|
||||
console.error("Error fetching pagespeed monitor of id: " + monitorId);
|
||||
navigate("/not-found");
|
||||
logger.error("Error fetching pagespeed monitor of id: " + monitorId);
|
||||
navigate("/not-found", { replace: true });
|
||||
}
|
||||
setMonitor({
|
||||
...data,
|
||||
@@ -278,7 +279,7 @@ const PageSpeedConfigure = () => {
|
||||
id="notify-emails-list"
|
||||
placeholder="notifications@gmail.com"
|
||||
value=""
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
error=""
|
||||
/>
|
||||
<Typography mt={theme.gap.small}>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { createToast } from "../../../Utils/toastUtils";
|
||||
import { createPageSpeed } from "../../../Features/PageSpeedMonitor/pageSpeedMonitorSlice";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import "./index.css";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
|
||||
const CreatePageSpeed = () => {
|
||||
const theme = useTheme();
|
||||
@@ -175,7 +176,7 @@ const CreatePageSpeed = () => {
|
||||
id="notify-emails-list"
|
||||
placeholder="notifications@gmail.com"
|
||||
value=""
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
error=""
|
||||
/>
|
||||
<Typography mt={theme.gap.small}>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
formatDuration,
|
||||
formatDurationRounded,
|
||||
} from "../../../Utils/timeUtils";
|
||||
import axiosInstance from "../../../Utils/axiosConfig";
|
||||
import { networkService } from "../../../main";
|
||||
import Button from "../../../Components/Button";
|
||||
import SettingsIcon from "../../../assets/icons/settings-bold.svg?react";
|
||||
import LastCheckedIcon from "../../../assets/icons/calendar-check.svg?react";
|
||||
@@ -21,6 +21,7 @@ import PageSpeedLineChart from "../../../Components/Charts/PagespeedLineChart";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import "./index.css";
|
||||
import PropTypes from "prop-types";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
|
||||
const StatBox = ({ icon, title, value }) => {
|
||||
const theme = useTheme();
|
||||
@@ -198,20 +199,20 @@ const PageSpeedDetails = () => {
|
||||
useEffect(() => {
|
||||
const fetchMonitor = async () => {
|
||||
try {
|
||||
const res = await axiosInstance.get(
|
||||
`/monitors/stats/${monitorId}?sortOrder=desc&limit=50`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
}
|
||||
const res = await networkService.getStatsByMonitorId(
|
||||
authToken,
|
||||
monitorId,
|
||||
"desc",
|
||||
50,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
setMonitor(res?.data?.data ?? {});
|
||||
setAudits(res?.data?.data?.checks?.[0]?.audits ?? []);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
navigate("/not-found");
|
||||
logger.error(logger);
|
||||
navigate("/not-found", { replace: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import Button from "../../Components/Button";
|
||||
import Field from "../../Components/Inputs/Field";
|
||||
import Link from "../../Components/Link";
|
||||
import Select from "../../Components/Inputs/Select";
|
||||
|
||||
import { logger } from "../../Utils/Logger";
|
||||
import "./index.css";
|
||||
|
||||
const Settings = () => {
|
||||
@@ -40,14 +40,14 @@ const Settings = () => {
|
||||
id="display-timezone"
|
||||
label="Display timezone"
|
||||
value="est"
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
items={[{ _id: "est", name: "America / Toronto" }]}
|
||||
/>
|
||||
<Select
|
||||
id="server-timezone"
|
||||
label="Server timezone"
|
||||
value="est"
|
||||
onChange={() => console.log("disabled")}
|
||||
onChange={() => logger.warn("disabled")}
|
||||
items={[{ _id: "est", name: "America / Toronto" }]}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -74,7 +74,7 @@ const Settings = () => {
|
||||
optionalLabel="0 for infinite"
|
||||
placeholder="90"
|
||||
value=""
|
||||
onChange={() => console.log("Disabled")}
|
||||
onChange={() => logger.warn("Disabled")}
|
||||
/>
|
||||
<Box>
|
||||
<Typography>Clear all stats. This is irreversible.</Typography>
|
||||
|
||||
30
Client/src/Utils/Logger.js
Normal file
30
Client/src/Utils/Logger.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const LOG_LEVEL = import.meta.env.VITE_APP_LOG_LEVEL;
|
||||
class Logger {
|
||||
constructor(logLevel) {
|
||||
const NO_OP = () => {};
|
||||
|
||||
if (logLevel === "none") {
|
||||
this.error = NO_OP;
|
||||
this.warn = NO_OP;
|
||||
this.log = NO_OP;
|
||||
return;
|
||||
}
|
||||
|
||||
this.error = console.error.bind(console);
|
||||
|
||||
if (logLevel === "error") {
|
||||
this.warn = NO_OP;
|
||||
this.log = NO_OP;
|
||||
return;
|
||||
}
|
||||
this.warn = console.warn.bind(console);
|
||||
|
||||
if (logLevel === "warn") {
|
||||
this.log = NO_OP;
|
||||
return;
|
||||
}
|
||||
this.log = console.log.bind(console);
|
||||
}
|
||||
}
|
||||
|
||||
export const logger = new Logger(LOG_LEVEL);
|
||||
434
Client/src/Utils/NetworkService.js
Normal file
434
Client/src/Utils/NetworkService.js
Normal file
@@ -0,0 +1,434 @@
|
||||
import axios from "axios";
|
||||
import { clearAuthState } from "../Features/Auth/authSlice";
|
||||
const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL;
|
||||
|
||||
class NetworkService {
|
||||
constructor(store) {
|
||||
this.store = store;
|
||||
this.axiosInstance = axios.create({ baseURL: BASE_URL });
|
||||
this.axiosInstance.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
console.error(error);
|
||||
if (error.response && error.response.status === 401) {
|
||||
console.log("Invalid token revoked");
|
||||
networkService;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* ************************************
|
||||
* Create a new monitor
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {Object} monitor - The monitor object to be sent in the request body.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*/
|
||||
async createMonitor(authToken, monitor) {
|
||||
return this.axiosInstance.post(`/monitors`, monitor, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Get all uptime monitors for a user
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {string} userId - The ID of the user whose monitors are to be retrieved.
|
||||
* @param {number} [limit] - The maximum number of monitors to retrieve.
|
||||
* @param {Array<string>} [types] - The types of monitors to retrieve.
|
||||
* @param {string} [status] - The status of the monitors to retrieve.
|
||||
* @param {string} [sortOrder] - The order in which to sort the retrieved monitors.
|
||||
* @param {boolean} [normalize] - Whether to normalize the retrieved monitors.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios GET request.
|
||||
*/
|
||||
async getMonitorsByUserId(
|
||||
authToken,
|
||||
userId,
|
||||
limit,
|
||||
types,
|
||||
status,
|
||||
sortOrder,
|
||||
normalize
|
||||
) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (limit) params.append("limit", limit);
|
||||
if (types) {
|
||||
types.forEach((type) => {
|
||||
params.append("type", type);
|
||||
});
|
||||
}
|
||||
if (status) params.append("status", status);
|
||||
if (sortOrder) params.append("sortOrder", sortOrder);
|
||||
if (normalize) params.append("normalize", normalize);
|
||||
|
||||
return this.axiosInstance.get(
|
||||
`/monitors/user/${userId}?${params.toString()}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Get stats for a monitor
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {string} monitorId - The ID of the monitor whose statistics are to be retrieved.
|
||||
* @param {string} [sortOrder] - The order in which to sort the retrieved statistics.
|
||||
* @param {number} [limit] - The maximum number of statistics to retrieve.
|
||||
* @param {string} [dateRange] - The date range for which to retrieve statistics.
|
||||
* @param {number} [numToDisplay] - The number of statistics to display.
|
||||
* @param {boolean} [normalize] - Whether to normalize the retrieved statistics.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios GET request.
|
||||
*/
|
||||
async getStatsByMonitorId(
|
||||
authToken,
|
||||
monitorId,
|
||||
sortOrder,
|
||||
limit,
|
||||
dateRange,
|
||||
numToDisplay,
|
||||
normalize
|
||||
) {
|
||||
const params = new URLSearchParams();
|
||||
if (sortOrder) params.append("sortOrder", sortOrder);
|
||||
if (limit) params.append("limit", limit);
|
||||
if (dateRange) params.append("dateRange", dateRange);
|
||||
if (numToDisplay) params.append("numToDisplay", numToDisplay);
|
||||
if (normalize) params.append("normalize", normalize);
|
||||
|
||||
return this.axiosInstance.get(
|
||||
`/monitors/stats/${monitorId}?${params.toString()}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Updates a single monitor
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {string} monitorId - The ID of the monitor to be updated.
|
||||
* @param {Object} updatedFields - The fields to be updated for the monitor.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios PUT request.
|
||||
*/
|
||||
async updateMonitor(authToken, monitorId, updatedFields) {
|
||||
return this.axiosInstance.put(`/monitors/${monitorId}`, updatedFields, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Deletes a single monitor by its ID
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {string} monitorId - The ID of the monitor to be deleted.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios DELETE request.
|
||||
*/
|
||||
async deleteMonitorById(authToken, monitorId) {
|
||||
return this.axiosInstance.delete(`/monitors/${monitorId}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Gets the certificate expiry for a monitor
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {string} monitorId - The ID of the monitor whose certificate expiry is to be retrieved.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios GET request.
|
||||
*
|
||||
*/
|
||||
async getCertificateExpiry(authToken, monitorId) {
|
||||
return this.axiosInstance.get(`/monitors/certificate/${monitorId}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Registers a new user
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {Object} form - The form data for the new user to be registered.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*/
|
||||
async registerUser(form) {
|
||||
return this.axiosInstance.post(`/auth/register`, form);
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Logs in a user
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {Object} form - The form data for the user to be logged in.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*
|
||||
*/
|
||||
async loginUser(form) {
|
||||
return this.axiosInstance.post(`/auth/login`, form);
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Updates a user
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {string} userId - The ID of the user to be updated.
|
||||
* @param {Object} form - The form data for the user to be updated.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios PUT request.
|
||||
*
|
||||
*/
|
||||
async updateUser(authToken, userId, form) {
|
||||
return this.axiosInstance.put(`/auth/user/${userId}`, form, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Forgot password request
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {Object} form - The form data for the password recovery request.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*
|
||||
*/
|
||||
async forgotPassword(form) {
|
||||
return this.axiosInstance.post(`/auth/recovery/request`, form);
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Validates a recovery token
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} recoveryToken - The recovery token to be validated.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*
|
||||
*/
|
||||
async validateRecoveryToken(recoveryToken) {
|
||||
return this.axiosInstance.post("/auth/recovery/validate", {
|
||||
recoveryToken,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Requests password recovery
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {Object} form - The form data for the password recovery request.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*
|
||||
*/
|
||||
async setNewPassword(recoveryToken, form) {
|
||||
return this.axiosInstance.post("/auth/recovery/reset", {
|
||||
...form,
|
||||
recoveryToken,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Checks if an admin user exists
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios GET request.
|
||||
*
|
||||
*/
|
||||
async doesAdminExist() {
|
||||
return this.axiosInstance.get("/auth/users/admin");
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Get all users
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios GET request.
|
||||
*
|
||||
*/
|
||||
async getAllUsers(authToken) {
|
||||
return this.axiosInstance.get("/auth/users", {
|
||||
headers: { Authorization: `Bearer ${authToken}` },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Requests an invitation token
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {string} email - The email of the user to be invited.
|
||||
* @param {string} role - The role of the user to be invited.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*
|
||||
*/
|
||||
async requestInvitationToken(authToken, email, role) {
|
||||
return this.axiosInstance.post(
|
||||
`/auth/invite`,
|
||||
{ email, role },
|
||||
{
|
||||
headers: { Authorization: `Bearer ${authToken}` },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Verifies an invitation token
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} token - The invitation token to be verified.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*
|
||||
*/
|
||||
async verifyInvitationToken(token) {
|
||||
return this.axiosInstance.post(`/auth/invite/verify`, {
|
||||
token,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Get all checks for a given monitor
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {string} monitorId - The ID of the monitor.
|
||||
* @param {string} sortOrder - The order in which to sort the checks.
|
||||
* @param {number} limit - The maximum number of checks to retrieve.
|
||||
* @param {string} dateRange - The range of dates for which to retrieve checks.
|
||||
* @param {string} filter - The filter to apply to the checks.
|
||||
* @param {number} page - The page number to retrieve in a paginated list.
|
||||
* @param {number} rowsPerPage - The number of rows per page in a paginated list.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios GET request.
|
||||
*
|
||||
*/
|
||||
|
||||
async getChecksByMonitor(
|
||||
authToken,
|
||||
monitorId,
|
||||
sortOrder,
|
||||
limit,
|
||||
dateRange,
|
||||
filter,
|
||||
page,
|
||||
rowsPerPage
|
||||
) {
|
||||
const params = new URLSearchParams();
|
||||
if (sortOrder) params.append("sortOrder", sortOrder);
|
||||
if (limit) params.append("limit", limit);
|
||||
if (dateRange) params.append("dateRange", dateRange);
|
||||
if (filter) params.append("filter", filter);
|
||||
if (page) params.append("page", page);
|
||||
if (rowsPerPage) params.append("rowsPerPage", rowsPerPage);
|
||||
|
||||
return this.axiosInstance.get(`/checks/${monitorId}?${params.toString()}`, {
|
||||
headers: { Authorization: `Bearer ${authToken}` },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Get all checks for a given user
|
||||
* ************************************
|
||||
*
|
||||
* @async
|
||||
* @param {string} authToken - The authorization token to be used in the request header.
|
||||
* @param {string} userId - The ID of the user.
|
||||
* @param {string} sortOrder - The order in which to sort the checks.
|
||||
* @param {number} limit - The maximum number of checks to retrieve.
|
||||
* @param {string} dateRange - The range of dates for which to retrieve checks.
|
||||
* @param {string} filter - The filter to apply to the checks.
|
||||
* @param {number} page - The page number to retrieve in a paginated list.
|
||||
* @param {number} rowsPerPage - The number of rows per page in a paginated list.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios GET request.
|
||||
*
|
||||
*/
|
||||
async getChecksByUser(
|
||||
authToken,
|
||||
userId,
|
||||
sortOrder,
|
||||
limit,
|
||||
dateRange,
|
||||
filter,
|
||||
page,
|
||||
rowsPerPage
|
||||
) {
|
||||
const params = new URLSearchParams();
|
||||
if (sortOrder) params.append("sortOrder", sortOrder);
|
||||
if (limit) params.append("limit", limit);
|
||||
if (dateRange) params.append("dateRange", dateRange);
|
||||
if (filter) params.append("filter", filter);
|
||||
if (page) params.append("page", page);
|
||||
if (rowsPerPage) params.append("rowsPerPage", rowsPerPage);
|
||||
return this.axiosInstance.get(
|
||||
`/checks/user/${userId}?${params.toString()}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${authToken}` },
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NetworkService;
|
||||
@@ -1,27 +0,0 @@
|
||||
import axios from "axios";
|
||||
import { clearAuthState } from "../Features/Auth/authSlice";
|
||||
const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL;
|
||||
|
||||
let store;
|
||||
|
||||
export const injectStore = (s) => {
|
||||
store = s;
|
||||
};
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: BASE_URL,
|
||||
});
|
||||
|
||||
axiosInstance.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
console.error(error);
|
||||
if (error.response && error.response.status === 401) {
|
||||
console.log("Invalid token revoked");
|
||||
store.dispatch(clearAuthState());
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default axiosInstance;
|
||||
@@ -8,10 +8,8 @@ import { ThemeProvider } from "@mui/material";
|
||||
import { Provider } from "react-redux";
|
||||
import { persistor, store } from "./store";
|
||||
import { PersistGate } from "redux-persist/integration/react";
|
||||
import { injectStore } from "./Utils/axiosConfig.js";
|
||||
|
||||
injectStore(store);
|
||||
|
||||
import NetworkService from "./Utils/NetworkService.js";
|
||||
export const networkService = new NetworkService(store);
|
||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
|
||||
13
README.md
13
README.md
@@ -22,7 +22,7 @@ BlueWave Uptime is an open source server monitoring application used to track th
|
||||
- [x] Ping monitoring
|
||||
- [x] Incidents at a glance
|
||||
- [x] Page speed monitoring
|
||||
- [x] E-mail notifications
|
||||
- [x] E-mail notifications
|
||||
- [ ] Scheduled maintenance (in the works)
|
||||
|
||||
**Roadmap (short term):**
|
||||
@@ -62,8 +62,7 @@ Made with [contrib.rocks](https://contrib.rocks).
|
||||
|
||||
[](https://star-history.com/#bluewave-labs/bluewave-uptime&Date)
|
||||
|
||||
|
||||
Also check other developer and contributor-friendly projects of BlueWave:
|
||||
Also check other developer and contributor-friendly projects of BlueWave:
|
||||
|
||||
- [BlueWave HRM](https://github.com/bluewave-labs/bluewave-hrm)
|
||||
- [BlueWave Onboarding](https://github.com/bluewave-labs/bluewave-onboarding)
|
||||
@@ -191,6 +190,7 @@ SYSTEM_EMAIL_PASSWORD=<system_email_password>
|
||||
|
||||
```
|
||||
VITE_APP_API_BASE_URL="http://localhost:5000/api/v1"
|
||||
VITE_APP_API_LOG_LEVEL="debug"
|
||||
```
|
||||
|
||||
4. In the `Docker` directory run `docker compose up` to run the `docker-compose.yaml` file and start all four images.
|
||||
@@ -211,9 +211,10 @@ That's it, the application is ready to use on port 80.
|
||||
|
||||
##### Environmental Variables <a id="env-vars-client"></a>
|
||||
|
||||
| ENV Variable Name | Required/Optional | Type | Description | Accepted Values |
|
||||
| --------------------- | ----------------- | -------- | ------------------ | --------------- |
|
||||
| VITE_APP_API_BASE_URL | Required | `string` | Base URL of server | {host}/api/v1 |
|
||||
| ENV Variable Name | Required/Optional | Type | Description | Accepted Values |
|
||||
| --------------------- | ----------------- | -------- | ------------------ | ---------------------------------- |
|
||||
| VITE_APP_API_BASE_URL | Required | `string` | Base URL of server | {host}/api/v1 |
|
||||
| VITE_APP_LOG_LEVEL | Optional | `string` | Log level | `"none"`\|`"error"` \| `"warn"` \| |
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
@@ -450,6 +450,7 @@ const deleteUserController = async (req, res, next) => {
|
||||
}
|
||||
|
||||
// 1. Find all the monitors associated with the user id
|
||||
|
||||
const monitors = await req.db.getMonitorsByUserId({
|
||||
params: { userId: _id },
|
||||
});
|
||||
@@ -459,6 +460,7 @@ const deleteUserController = async (req, res, next) => {
|
||||
await Promise.all(
|
||||
monitors.map(async (monitor) => {
|
||||
await req.jobQueue.deleteJob(monitor);
|
||||
|
||||
await req.db.deleteChecks(monitor._id);
|
||||
await req.db.deleteAlertByMonitorId(monitor._id);
|
||||
await req.db.deletePageSpeedChecksByMonitorId(monitor._id);
|
||||
|
||||
@@ -297,7 +297,7 @@ const getMonitorById = async (monitorId) => {
|
||||
*/
|
||||
const getMonitorsByUserId = async (req, res) => {
|
||||
try {
|
||||
let { limit, type, status, sortOrder, normalize } = req.query;
|
||||
let { limit, type, status, sortOrder, normalize } = req.query || {};
|
||||
const monitorQuery = { userId: req.params.userId };
|
||||
|
||||
if (type !== undefined) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const UserModel = require("../../../models/user");
|
||||
const RecoveryToken = require("../../../models/RecoveryToken");
|
||||
const crypto = require("crypto");
|
||||
const { errorMessages } = require("../../../utils/messages");
|
||||
|
||||
@@ -19,7 +19,7 @@ const JobQueue = require("./service/jobQueue");
|
||||
const NetworkService = require("./service/networkService");
|
||||
const EmailService = require("./service/emailService");
|
||||
const PageSpeedService = require("./service/pageSpeedService");
|
||||
|
||||
const SERVICE_NAME = "Server";
|
||||
let cleaningUp = false;
|
||||
|
||||
// Need to wrap server setup in a function to handle async nature of JobQueue
|
||||
|
||||
@@ -15,12 +15,12 @@ router.get(
|
||||
checkController.getChecks
|
||||
);
|
||||
|
||||
router.get("/user/:userId", checkController.getUserChecks);
|
||||
|
||||
router.delete(
|
||||
"/:monitorId",
|
||||
verifyOwnership(Monitor, "monitorId"),
|
||||
checkController.deleteChecks
|
||||
);
|
||||
|
||||
router.get("/user/:userId", checkController.getUserChecks);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user