Merge branch 'develop' into feat/logger

This commit is contained in:
Alexander Holliday
2024-08-19 19:02:30 -07:00
committed by GitHub
20 changed files with 568 additions and 267 deletions

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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
// *****************************************************

View File

@@ -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
// *****************************************************

View File

@@ -1,15 +1,16 @@
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");

View File

@@ -7,7 +7,7 @@ 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";
@@ -276,8 +276,8 @@ const Login = () => {
navigate("/monitors");
return;
}
axiosInstance
.get("/auth/users/admin")
networkService
.doesAdminExist()
.then((response) => {
if (response.data.data === false) {
navigate("/register");

View File

@@ -15,7 +15,7 @@ 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";
@@ -398,9 +398,7 @@ 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;
setForm({ ...form, email, role });
} catch (error) {

View File

@@ -18,7 +18,7 @@ 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";
@@ -44,17 +44,30 @@ 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) {

View File

@@ -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

View File

@@ -13,7 +13,7 @@ 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";
@@ -38,13 +38,15 @@ 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);

View File

@@ -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";
@@ -122,13 +122,14 @@ 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) {
@@ -143,13 +144,9 @@ const DetailsPage = () => {
useEffect(() => {
const fetchCertificate = async () => {
const res = await axiosInstance.get(
`/monitors/certificate/${monitorId}`,
{
headers: {
Authorization: `Bearer ${authToken}`,
},
}
const res = await networkService.getCertificateExpiry(
authToken,
monitorId
);
setCertificateExpiry(res.data.data.certificateDate);
};

View File

@@ -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";
@@ -199,15 +199,15 @@ 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) {

View 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;

View File

@@ -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;

View File

@@ -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}>

View File

@@ -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);

View File

@@ -294,7 +294,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) {

View File

@@ -1,3 +1,4 @@
const UserModel = require("../../../models/user");
const RecoveryToken = require("../../../models/RecoveryToken");
const crypto = require("crypto");
const { errorMessages } = require("../../../utils/messages");

View File

@@ -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

View File

@@ -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;