diff --git a/client/src/App.jsx b/client/src/App.jsx index da83f9061..194a5bf5b 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -7,7 +7,6 @@ import lightTheme from "./Utils/Theme/lightTheme"; import darkTheme from "./Utils/Theme/darkTheme"; import { CssBaseline, GlobalStyles } from "@mui/material"; import { logger } from "./Utils/Logger"; // Import the logger -import { networkService } from "./main"; import { Routes } from "./Routes"; import AppLayout from "@/Components/v1/Layouts/AppLayout"; @@ -18,7 +17,6 @@ function App() { useEffect(() => { return () => { logger.cleanup(); - networkService.cleanup(); }; }, []); diff --git a/client/src/Hooks/useSendTestEmail.js b/client/src/Hooks/useSendTestEmail.js deleted file mode 100644 index 7223a489a..000000000 --- a/client/src/Hooks/useSendTestEmail.js +++ /dev/null @@ -1,60 +0,0 @@ -import { useState } from "react"; -import { networkService } from "../main.jsx"; -import { useSelector } from "react-redux"; -import { createToast } from "../Utils/toastUtils.jsx"; -import { useTranslation } from "react-i18next"; - -const useSendTestEmail = () => { - const [isSending, setIsSending] = useState(false); - const [error, setError] = useState(null); - const user = useSelector((state) => state.auth.user); - const { t } = useTranslation(); - - /** - * Send a test email with optional email configuration - * @param {Object} emailConfig - Optional email configuration parameters - */ - const sendTestEmail = async (emailConfig = null) => { - try { - setIsSending(true); - setError(null); - - // Send the test email with or without configuration - const response = await networkService.sendTestEmail({ - to: user.email, - emailConfig, - }); - - if (typeof response?.data?.data?.messageId !== "undefined") { - createToast({ - body: t("settingsTestEmailSuccess", "Test email sent successfully"), - }); - } else { - throw new Error( - response?.data?.error || - t("settingsTestEmailFailed", "Failed to send test email") - ); - } - } catch (error) { - createToast({ - body: t("failedToSendEmail"), - }); - setError(error); - createToast({ - body: t( - "settingsTestEmailFailedWithReason", - "Failed to send test email: {{reason}}", - { - reason: error.message || t("settingsTestEmailUnknownError", "Unknown error"), - } - ), - variant: "error", - }); - } finally { - setIsSending(false); - } - }; - return [isSending, error, sendTestEmail]; -}; - -export { useSendTestEmail }; diff --git a/client/src/Pages/ServerUnreachable.jsx b/client/src/Pages/ServerUnreachable.jsx index bfbbfa90a..5051ee95f 100644 --- a/client/src/Pages/ServerUnreachable.jsx +++ b/client/src/Pages/ServerUnreachable.jsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { Box, Typography, Button, Stack } from "@mui/material"; import { useTheme } from "@emotion/react"; import { useNavigate } from "react-router-dom"; -import { networkService } from "@/Utils/NetworkService.js"; +import { get } from "@/Utils/ApiClient"; import Alert from "@/Components/v1/Alert/index.jsx"; import { createToast } from "@/Utils/toastUtils.jsx"; import { useTranslation } from "react-i18next"; @@ -24,7 +24,7 @@ const ServerUnreachable = () => { try { // Try to connect to the backend with a simple API call // We'll use any lightweight endpoint that doesn't require authentication - await networkService.axiosInstance.get("/health", { timeout: 5000 }); + await get("/health", { timeout: 5000 }); // If successful, show toast and navigate to login page createToast({ diff --git a/client/src/Pages/Settings/SettingsEmail.jsx b/client/src/Pages/Settings/SettingsEmail.jsx index 6f0b02137..859ff604c 100644 --- a/client/src/Pages/Settings/SettingsEmail.jsx +++ b/client/src/Pages/Settings/SettingsEmail.jsx @@ -12,7 +12,8 @@ import { PropTypes } from "prop-types"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { PasswordEndAdornment } from "@/Components/v1/Inputs/TextInput/Adornments/index.jsx"; -import { useSendTestEmail } from "@/Hooks/useSendTestEmail.js"; +import { usePost } from "@/Hooks/UseApi"; +import { useSelector } from "react-redux"; import { createToast } from "@/Utils/toastUtils.jsx"; const SettingsEmail = ({ @@ -48,7 +49,8 @@ const SettingsEmail = ({ const [password, setPassword] = useState(""); // Network - const [isSending, , sendTestEmail] = useSendTestEmail(); // Using empty placeholder for unused error variable + const { post: sendTestEmailFn, loading: isSending } = usePost(); + const user = useSelector((state) => state.auth.user); // Handlers const handlePasswordChange = (e) => { @@ -94,7 +96,10 @@ const SettingsEmail = ({ } // Send test email with current form values - sendTestEmail(emailConfig); + sendTestEmailFn("/settings/test-email", { + to: user.email, + ...emailConfig, + }); }; if (!isAdmin) { @@ -297,6 +302,14 @@ const SettingsEmail = ({ +
+						{JSON.stringify({
+							systemEmailHost,
+							systemEmailAddress,
+							systemEmailPassword,
+						})}
+					
+ {systemEmailHost && systemEmailPort && diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js deleted file mode 100644 index 7f6a8e455..000000000 --- a/client/src/Utils/NetworkService.js +++ /dev/null @@ -1,1187 +0,0 @@ -import axios from "axios"; -import i18next from "i18next"; -const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL; -const FALLBACK_BASE_URL = "http://localhost:5000/api/v1"; -import { clearAuthState } from "../Features/Auth/authSlice"; -class NetworkService { - constructor(store, dispatch, navigate) { - this.store = store; - this.dispatch = dispatch; - this.navigate = navigate; - let baseURL = BASE_URL; - this.axiosInstance = axios.create(); - this.setBaseUrl(baseURL); - this.unsubscribe = store.subscribe(() => { - const state = store.getState(); - if (BASE_URL !== undefined) { - baseURL = BASE_URL; - } else if (state?.settings?.apiBaseUrl ?? null) { - baseURL = state.settings.apiBaseUrl; - } else { - baseURL = FALLBACK_BASE_URL; - } - this.setBaseUrl(baseURL); - }); - this.axiosInstance.interceptors.request.use( - (config) => { - const currentLanguage = i18next.language || "en"; - - const { authToken } = store.getState().auth; - - config.headers = { - Authorization: `Bearer ${authToken}`, - "Accept-Language": currentLanguage === "gb" ? "en" : currentLanguage, - ...config.headers, - }; - - return config; - }, - (error) => { - return Promise.reject(error); - } - ); - this.axiosInstance.interceptors.response.use( - (response) => response, - (error) => { - // Handle network errors (server unreachable) - if (error.code === "ERR_NETWORK") { - // Navigate to server unreachable page - navigate("/server-unreachable"); - // Return an empty resolved promise to stop the error propagation - return Promise.reject(error); - } - - // Handle authentication errors - - if (error.response && error.response.status === 401) { - dispatch(clearAuthState()); - navigate("/login"); - } else if (error.request && !error.response) { - return Promise.reject(error); - } - return Promise.reject(error); - } - ); - } - - setBaseUrl = (url) => { - this.axiosInstance.defaults.baseURL = url; - }; - - cleanup() { - if (this.unsubscribe) { - this.unsubscribe(); - } - } - - /** - * Fetch the games associated with a monitor - * - * @async - * @returns {Promise} The response from the axios GET request. - */ - async getMonitorGames() { - return this.axiosInstance.get(`/monitors/games`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * - * ************************************ - * Create a new monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.monitorId - The monitor ID to be sent in the param. - * @returns {Promise} The response from the axios GET request. - */ - - async getMonitorById(config) { - return this.axiosInstance.get(`/monitors/${config.monitorId}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * - * ************************************ - * Create a new monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {Object} config.monitor - The monitor object to be sent in the request body. - * @returns {Promise} The response from the axios POST request. - */ - async createMonitor(config) { - const { monitor } = config; - - return this.axiosInstance.post(`/monitors`, monitor); - } - - /** - * ************************************ - * Get all uptime monitors for a Team - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.teamId - The ID of the team whose monitors are to be retrieved. - * @param {number} [config.limit] - The maximum number of checks to retrieve. 0 for all, -1 for none - * @param {Array} [config.types] - The types of monitors to retrieve. - * @param {number} [config.page] - The page number for pagination. - * @param {number} [config.rowsPerPage] - The number of rows per page for pagination. - * @param {string} [config.filter] - The filter to apply to the monitors. - * @param {string} [config.field] - The field to sort by. - * @param {string} [config.order] - The order in which to sort the field. - * @returns {Promise} The response from the axios GET request. - */ - - async getMonitorsByTeamId(config) { - const { types, filter } = config ?? {}; - const params = new URLSearchParams(); - - if (types) { - types.forEach((type) => { - params.append("type", type); - }); - } - if (filter !== undefined && filter !== null && filter !== "") - params.append("filter", filter); - - return this.axiosInstance.get(`/monitors/team?${params.toString()}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * ************************************ - * Get stats for a monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.monitorId - The ID of the monitor whose statistics are to be retrieved. - * @param {string} config.sortOrder - The order in which to sort the retrieved statistics. - * @param {number} config.limit - The maximum number of statistics to retrieve. - * @param {string} config.dateRange - The date range for which to retrieve statistics. - * @param {number} config.numToDisplay - The number of checks to display. - * @param {boolean} config.normalize - Whether to normalize the retrieved statistics. - * @returns {Promise} The response from the axios GET request. - */ - async getStatsByMonitorId(config) { - const params = new URLSearchParams(); - if (config.sortOrder) params.append("sortOrder", config.sortOrder); - if (config.limit) params.append("limit", config.limit); - if (config.dateRange) params.append("dateRange", config.dateRange); - if (config.numToDisplay) params.append("numToDisplay", config.numToDisplay); - if (config.normalize) params.append("normalize", config.normalize); - - return this.axiosInstance.get( - `/monitors/stats/${config.monitorId}?${params.toString()}` - ); - } - - async getHardwareDetailsByMonitorId(config) { - const params = new URLSearchParams(); - if (config.dateRange) params.append("dateRange", config.dateRange); - - return this.axiosInstance.get( - `/monitors/hardware/details/${config.monitorId}?${params.toString()}` - ); - } - async getPageSpeedDetailsByMonitorId(config) { - const params = new URLSearchParams(); - if (config.dateRange) params.append("dateRange", config.dateRange); - - return this.axiosInstance.get( - `/monitors/pagespeed/details/${config.monitorId}?${params.toString()}` - ); - } - async getUptimeDetailsById(config) { - const params = new URLSearchParams(); - if (config.dateRange) params.append("dateRange", config.dateRange); - if (config.normalize) params.append("normalize", config.normalize); - - return this.axiosInstance.get( - `/monitors/uptime/details/${config.monitorId}?${params.toString()}` - ); - } - - /** - * ************************************ - * Updates a single monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.monitorId - The ID of the monitor to be updated. - * @param {Object} config.updatedFields - The fields to be updated for the monitor. - * @returns {Promise} The response from the axios PUT request. - */ - async updateMonitor(config) { - const { monitorId, updatedFields } = config; - const payload = updatedFields; - return this.axiosInstance.patch(`/monitors/${monitorId}`, payload, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * ************************************ - * Deletes a single monitor by its ID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.monitorId - The ID of the monitor to be deleted. - * @returns {Promise} The response from the axios DELETE request. - */ - async deleteMonitorById(config) { - return this.axiosInstance.delete(`/monitors/${config.monitorId}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * ************************************ - * Deletes all checks for all monitor by teamID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.teamId - The team ID of the monitors to be deleted. - * @returns {Promise} The response from the axios DELETE request. - */ - async deleteChecksByTeamId(config) { - return this.axiosInstance.delete(`/checks/team`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - /** - * ************************************ - * Pauses a single monitor by its ID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.monitorId - The ID of the monitor to be paused. - * @returns {Promise} The response from the axios POST request. - */ - async pauseMonitorById(config) { - return this.axiosInstance.post( - `/monitors/pause/${config.monitorId}`, - {}, - { - headers: { - "Content-Type": "application/json", - }, - } - ); - } - - /** - * ************************************ - * Adds demo monitors - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @returns {Promise} The response from the axios POST request. - */ - async addDemoMonitors(config) { - return this.axiosInstance.post( - `/monitors/demo`, - {}, - { - headers: { - "Content-Type": "application/json", - }, - } - ); - } - - /** - * ************************************ - * Deletes all monitors for a team by team ID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @returns {Promise} The response from the axios DELETE request. - */ - async deleteAllMonitors(config) { - return this.axiosInstance.delete(`/monitors/`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * ************************************ - * Gets the certificate expiry for a monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.monitorId - The ID of the monitor whose certificate expiry is to be retrieved. - * @returns {Promise} The response from the axios GET request. - * - */ - async getCertificateExpiry(config) { - return this.axiosInstance.get(`/monitors/certificate/${config.monitorId}`); - } - - async registerUser(user, token) { - return this.axiosInstance.post(`/auth/register`, { user, token }); - } - - /** - * ************************************ - * Logs in a user - * ************************************ - * - * @async - * @param {Object} form - The form data for the user to be logged in. - * @returns {Promise} The response from the axios POST request. - * - */ - async loginUser(form) { - return this.axiosInstance.post(`/auth/login`, form); - } - - /** - * ************************************ - * Updates a user - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.userId - The ID of the user to be updated. - * @param {Object} config.form - The form data for the user to be updated. - * @returns {Promise} The response from the axios PATCH request. - * - */ - async updateUser(config) { - return this.axiosInstance.patch(`/auth/user`, config.form); - } - - /** - * ************************************ - * Deletes a user - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.userId - The ID of the user to be deleted. - * - **/ - async deleteUser() { - return this.axiosInstance.delete(`/auth/user`); - } - - /** - * ************************************ - * Forgot password request - * ************************************ - * - * @async - * @param {Object} form - The form data for the password recovery request. - * @returns {Promise} The response from the axios POST request. - * - */ - async forgotPassword(form) { - return this.axiosInstance.post(`/auth/recovery/request`, form); - } - - /** - * ************************************ - * Validates a recovery token - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.recoveryToken - The recovery token to be validated. - * @returns {Promise} The response from the axios POST request. - * - */ - async validateRecoveryToken(config) { - return this.axiosInstance.post("/auth/recovery/validate", { - recoveryToken: config.recoveryToken, - }); - } - - /** - * ************************************ - * Requests password recovery - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.recoveryToken - The token for recovery request. - * @param {Object} config.form - The form data for the password recovery request. - * @returns {Promise} The response from the axios POST request. - * - */ - async setNewPassword(config) { - return this.axiosInstance.post("/auth/recovery/reset", { - ...config.form, - recoveryToken: config.recoveryToken, - }); - } - - /** - * ************************************ - * Checks if an admin user exists - * ************************************ - * - * @async - * @returns {Promise} The response from the axios GET request. - * - */ - async doesSuperAdminExist() { - return this.axiosInstance.get("/auth/users/superadmin"); - } - - /** - * ************************************ - * Get all users - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @returns {Promise} The response from the axios GET request. - * - */ - async getAllUsers(config) { - return this.axiosInstance.get("/auth/users"); - } - - /** - * ************************************ - * Requests an invitation token - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.email - The email of the user to be invited. - * @param {string} config.role - The role of the user to be invited. - * @returns {Promise} The response from the axios POST request. - * - */ - async requestInvitationToken(config) { - return this.axiosInstance.post(`/invite`, { email: config.email, role: config.role }); - } - /** - * ************************************ - * Sends an invitation token - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.email - The email of the user to be invited. - * @param {string} config.role - The role of the user to be invited. - * @returns {Promise} The response from the axios POST request. - * - */ - async sendInvitationToken(config) { - return this.axiosInstance.post(`/invite/send`, { - email: config.email, - role: config.role, - }); - } - - /** - * ************************************ - * Verifies an invitation token - * ************************************ - * - * @async - * @param {string} token - The invitation token to be verified. - * @returns {Promise} The response from the axios POST request. - * - */ - async verifyInvitationToken(token) { - return this.axiosInstance.post(`/invite/verify`, { - token, - }); - } - - /** - * ************************************ - * Get all checks for a given monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.monitorId - The ID of the monitor. - * @param {string} config.sortOrder - The order in which to sort the checks. - * @param {number} config.limit - The maximum number of checks to retrieve. - * @param {string} config.dateRange - The range of dates for which to retrieve checks. - * @param {string} config.filter - The filter to apply to the checks. - * @param {boolean} config.ack - The acknowledgment status to apply to the checks. - * @param {number} config.page - The page number to retrieve in a paginated list. - * @param {number} config.rowsPerPage - The number of rows per page in a paginated list. - * @returns {Promise} The response from the axios GET request. - * - */ - - getChecksByMonitor = async (config) => { - const params = new URLSearchParams(); - if (config.type) params.append("type", config.type); - if (config.sortOrder) params.append("sortOrder", config.sortOrder); - if (config.limit) params.append("limit", config.limit); - if (config.dateRange) params.append("dateRange", config.dateRange); - if (config.filter) params.append("filter", config.filter); - if (config.ack !== undefined) params.append("ack", config.ack); - if (config.page) params.append("page", config.page); - if (config.rowsPerPage) params.append("rowsPerPage", config.rowsPerPage); - if (config.status !== undefined) params.append("status", config.status); - - return this.axiosInstance.get(`/checks/${config.monitorId}?${params.toString()}`); - }; - - /** - * ************************************ - * Get all checks for a given user - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.userId - The ID of the user. - * @param {string} config.sortOrder - The order in which to sort the checks. - * @param {number} config.limit - The maximum number of checks to retrieve. - * @param {string} config.dateRange - The range of dates for which to retrieve checks. - * @param {string} config.filter - The filter to apply to the checks. - * @param {boolean} config.ack - The acknowledgment status to apply to the checks. - * @param {number} config.page - The page number to retrieve in a paginated list. - * @param {number} config.rowsPerPage - The number of rows per page in a paginated list. - * @returns {Promise} The response from the axios GET request. - * - */ - getChecksByTeam = async (config) => { - const params = new URLSearchParams(); - if (config.sortOrder) params.append("sortOrder", config.sortOrder); - if (config.limit) params.append("limit", config.limit); - if (config.dateRange) params.append("dateRange", config.dateRange); - if (config.filter) params.append("filter", config.filter); - if (config.ack !== undefined) params.append("ack", config.ack); - if (config.page) params.append("page", config.page); - if (config.rowsPerPage) params.append("rowsPerPage", config.rowsPerPage); - return this.axiosInstance.get(`/checks/team?${params.toString()}`); - }; - - /** - * ************************************ - * Get summary of checks by team ID - * ************************************ - * - * @async - * @returns {Promise} The response from the axios GET request. - */ - getChecksAndSummaryByTeamId = async ({ dateRange }) => { - return this.axiosInstance.get(`/checks/team/summary?dateRange=${dateRange}`); - }; - - /** - * ************************************ - * Update the status of a check - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.checkId - The ID of the check to update. - * @param {boolean} config.ack - The acknowledgment to update the check to. - * @returns {Promise} The response from the axios PUT request. - * - */ - async updateCheckStatus(config) { - return this.axiosInstance.patch(`/checks/check/${config.checkId}`, { - ack: config.ack, - }); - } - - /** - * ************************************ - * Update the status of all checks for a given monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.monitorId - The ID of the monitor. - * @param {boolean} config.ack - The acknowledgment to update the checks to. - * @returns {Promise} The response from the axios PUT request. - * - */ - async updateMonitorChecksStatus(config) { - return this.axiosInstance.patch(`/checks/monitor/${config.monitorId}`, { - ack: config.ack, - }); - } - - /** - * ************************************ - * Update the status of all checks for a given team - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {boolean} config.ack - The acknowledgment to update the checks to. - * @returns {Promise} The response from the axios PUT request. - * - */ - async updateAllChecksStatus(config) { - return this.axiosInstance.patch(`/checks/team/`, { - ack: config.ack, - }); - } - - /** - * ************************************ - * Get all checks for a given user - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {number} config.ttl - TTL for checks - * @returns {Promise} The response from the axios GET request. - * - */ - async updateChecksTTL(config) { - return this.axiosInstance.patch( - `/checks/team/ttl`, - { ttl: config.ttl }, - { - headers: { - "Content-Type": "application/json", - }, - } - ); - } - - /** - * ************************************ - * Get app settings - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @returns {Promise} The response from the axios GET request. - * - */ - - async getAppSettings(config) { - return this.axiosInstance.get("/settings", { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * - * ************************************ - * Create a new monitor - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {Object} config.settings - The monitor object to be sent in the request body. - * @returns {Promise} The response from the axios POST request. - */ - async updateAppSettings(config) { - return this.axiosInstance.patch(`/settings`, config.settings, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * ************************************ - * Test a notification integration - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.platform - The notification platform (slack, discord, telegram, webhook). - * @param {Object} config.payload - The payload with configuration for the notification. - * @returns {Promise} The response from the axios POST request. - */ - async testNotification(config) { - return this.axiosInstance.post("/notifications/test", config.notification, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async testAllNotifications(config) { - const { monitorId } = config; - return this.axiosInstance.post("/notifications/test/all", { - monitorId, - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * ************************************ - * Creates a maintenance window - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {Object} config.maintenanceWindow - The maintenance window object to be sent in the request body. - * @returns {Promise} The response from the axios POST request. - * - */ - - async createMaintenanceWindow(config) { - return this.axiosInstance.post(`/maintenance-window`, config.maintenanceWindow, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * ************************************ - * Edits a maintenance window - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {Object} config.maintenanceWindowId - The maintenance window id. - * @param {Object} config.maintenanceWindow - The maintenance window object to be sent in the request body. - * @returns {Promise} The response from the axios POST request. - * - */ - - async editMaintenanceWindow(config) { - return this.axiosInstance.patch( - `/maintenance-window/${config.maintenanceWindowId}`, - config.maintenanceWindow, - { - headers: { - "Content-Type": "application/json", - }, - } - ); - } - - /** - * ************************************ - * Get maintenance window by id - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} [config.maintenanceWindowId] - The id of the maintenance window to delete. - * @returns {Promise} The response from the axios POST request. - * - */ - - async getMaintenanceWindowById(config) { - const { maintenanceWindowId } = config; - return this.axiosInstance.get(`/maintenance-window/${maintenanceWindowId}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * ************************************ - * Get maintenance windows by teamId - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} [config.active] - The status of the maintenance windows to retrieve. - * @param {number} [config.page] - The page number for pagination. - * @param {number} [config.rowsPerPage] - The number of rows per page for pagination. - * @param {string} [config.field] - The field to sort by. - * @param {string} [config.order] - The order in which to sort the field. - * @returns {Promise} The response from the axios POST request. - * - */ - - async getMaintenanceWindowsByTeamId(config) { - const { active, page, rowsPerPage, field, order } = config; - const params = new URLSearchParams(); - - if (active) params.append("status", active); - if (page) params.append("page", page); - if (rowsPerPage) params.append("rowsPerPage", rowsPerPage); - if (field) params.append("field", field); - if (order) params.append("order", order); - - return this.axiosInstance.get(`/maintenance-window/team?${params.toString()}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - /** - * ************************************ - * Delete maintenance window by id - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} [config.maintenanceWindowId] - The id of the maintenance window to delete. - * @returns {Promise} The response from the axios POST request. - * - */ - - async deleteMaintenanceWindow(config) { - const { maintenanceWindowId } = config; - return this.axiosInstance.delete(`/maintenance-window/${maintenanceWindowId}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - /** - * ************************************ - * Fetcher github latest release version - * ************************************ - * - * @async - * @returns {Promise} The response from the axios GET request. - * - */ - async fetchGithubLatestRelease() { - return this.axiosInstance.get( - "https://api.github.com/repos/bluewave-labs/checkmate/releases/latest", - { - headers: { - Authorization: null, // No authorization header for this request - }, - } - ); - } - - async getStatusPage() { - return this.axiosInstance.get(`/status-page`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async getStatusPageByUrl(config) { - const { url, type } = config; - const params = new URLSearchParams(); - params.append("type", type); - - return this.axiosInstance.get(`/status-page/${url}?${params.toString()}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async getStatusPagesByTeamId(config) { - return this.axiosInstance.get(`/status-page/team`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async createStatusPage(config) { - const { form, isCreate, id } = config; - - const fd = new FormData(); - fd.append("type", form.type); - form.isPublished !== undefined && fd.append("isPublished", form.isPublished); - form.companyName && fd.append("companyName", form.companyName); - form.url && fd.append("url", form.url); - form.timezone && fd.append("timezone", form.timezone); - form.color && fd.append("color", form.color); - if (form.showCharts !== undefined) { - fd.append("showCharts", String(form.showCharts)); - } - if (form.showUptimePercentage !== undefined) { - fd.append("showUptimePercentage", String(form.showUptimePercentage)); - } - if (form.showAdminLoginLink !== undefined) { - fd.append("showAdminLoginLink", String(form.showAdminLoginLink)); - } - form.monitors && - form.monitors.forEach((monitorId) => { - fd.append("monitors[]", monitorId); - }); - // Handle subMonitors, even if it's an empty array - if (form.subMonitors && form.subMonitors.length > 0) { - form.subMonitors.forEach((monitorId) => { - fd.append("subMonitors[]", monitorId); - }); - } else { - fd.append("deleteSubmonitors", true); - } - - if (form?.logo?.src && form?.logo?.src !== "") { - const imageResult = await axios.get(form.logo.src, { - responseType: "blob", - }); - fd.append("logo", imageResult.data); - // Cleanup blob - if (form.logo.src.startsWith("blob:")) { - URL.revokeObjectURL(form.logo.src); - } - } - - if (isCreate) { - return this.axiosInstance.post(`/status-page`, fd, {}); - } - - return this.axiosInstance.patch(`/status-page/${id}`, fd, {}); - } - - async deleteStatusPage(config) { - const { url } = config; - const encodedUrl = encodeURIComponent(url); - - return this.axiosInstance.delete(`/status-page/${encodedUrl}`, {}); - } - - // ************************************ - // Create bulk monitors - // ************************************ - - async createBulkMonitors(formData) { - const response = await this.axiosInstance.post(`/monitors/bulk`, formData, { - headers: { - "Content-Type": "multipart/form-data", - }, - }); - return response.data; - } - - // ************************************ - // Fetch monitors with summary by TeamID - // ************************************ - async getMonitorsWithSummaryByTeamId(config) { - const { types } = config; - const params = new URLSearchParams(); - - if (types) { - types.forEach((type) => { - params.append("type", type); - }); - } - - return this.axiosInstance.get(`/monitors/team/summary?${params.toString()}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - // ************************************ - // Fetch monitors with checks by TeamID - // ************************************ - async getMonitorsWithChecksByTeamId(config) { - const { limit, types, page, rowsPerPage, filter, field, order } = config; - const params = new URLSearchParams(); - - if (limit) params.append("limit", limit); - if (types) { - types.forEach((type) => { - params.append("type", type); - }); - } - if (page) params.append("page", page); - if (rowsPerPage) params.append("rowsPerPage", rowsPerPage); - if (filter) params.append("filter", filter); - if (field) params.append("field", field); - if (order) params.append("order", order); - - return this.axiosInstance.get(`/monitors/team/with-checks?${params.toString()}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - // ************************************ - // Send test email - // ************************************ - async sendTestEmail(config) { - // Extract recipient and email configuration - const { to, emailConfig } = config; - - // If emailConfig is provided, use the new endpoint with direct parameters - if (emailConfig) { - return this.axiosInstance.post(`/settings/test-email`, { - to, - systemEmailHost: emailConfig.systemEmailHost, - systemEmailPort: emailConfig.systemEmailPort, - systemEmailAddress: emailConfig.systemEmailAddress, - systemEmailPassword: emailConfig.systemEmailPassword, - systemEmailSecure: emailConfig.systemEmailSecure, - systemEmailPool: emailConfig.systemEmailPool, - systemEmailIgnoreTLS: emailConfig.systemEmailIgnoreTLS, - systemEmailRequireTLS: emailConfig.systemEmailRequireTLS, - systemEmailRejectUnauthorized: emailConfig.systemEmailRejectUnauthorized, - systemEmailTLSServername: emailConfig.systemEmailTLSServername, - ...(emailConfig.systemEmailUser && { - systemEmailUser: emailConfig.systemEmailUser, - }), - }); - } - - // Fallback to original behavior for backward compatibility - return this.axiosInstance.post(`/settings/test-email`, { to }); - } - - async createNotification(config) { - const { notification } = config; - return this.axiosInstance.post(`/notifications`, notification); - } - - async getNotificationsByTeamId(config) { - return this.axiosInstance.get(`/notifications/team`); - } - - async deleteNotificationById(config) { - const { id } = config; - return this.axiosInstance.delete(`/notifications/${id}`); - } - - async getNotificationById(config) { - const { id } = config; - return this.axiosInstance.get(`/notifications/${id}`); - } - - async editNotification(config) { - const { id, notification } = config; - return this.axiosInstance.patch(`/notifications/${id}`, notification); - } - - async getQueueData() { - return this.axiosInstance.get(`/queue/all-metrics`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async flushQueue() { - return this.axiosInstance.post(`/queue/flush`); - } - - async exportMonitors() { - const response = await this.axiosInstance.get("/monitors/export", { - responseType: "blob", - }); - return response; - } - - async getLogs() { - return this.axiosInstance.get(`/logs`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async getDiagnostics() { - return this.axiosInstance.get(`/diagnostic/system`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async getUserById(config) { - const userId = config?.userId; - return this.axiosInstance.get(`auth/users/${userId}`, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async editUser(config) { - const { userId, user } = config; - return this.axiosInstance.patch(`auth/users/${userId}`, user, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async changePasswordByAdmin(config) { - const { userId, passwordForm } = config; - return this.axiosInstance.patch(`auth/users/${userId}/password`, passwordForm, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - async fetchJson() { - return this.axiosInstance.get("/monitors/export/json"); - } - - getIncidentsByTeam = async (config) => { - const params = new URLSearchParams(); - if (config.monitorId) params.append("monitorId", config.monitorId); - if (config.status !== undefined) params.append("status", config.status); - if (config.resolutionType) params.append("resolutionType", config.resolutionType); - if (config.sortOrder) params.append("sortOrder", config.sortOrder); - if (config.dateRange) params.append("dateRange", config.dateRange); - if (config.page) params.append("page", config.page); - if (config.rowsPerPage) params.append("rowsPerPage", config.rowsPerPage); - return this.axiosInstance.get(`/incidents/team?${params.toString()}`); - }; - - getIncidentById = async (incidentId) => { - return this.axiosInstance.get(`/incidents/${incidentId}`); - }; - - resolveIncidentManually = async (incidentId, options = {}) => { - const body = {}; - if (options.comment) body.comment = options.comment; - return this.axiosInstance.patch(`/incidents/${incidentId}/resolve`, body); - }; - - getIncidentSummary = async (config = {}) => { - const params = new URLSearchParams(); - if (config.limit) params.append("limit", config.limit); - const queryString = params.toString(); - return this.axiosInstance.get( - `/incidents/team/summary${queryString ? `?${queryString}` : ""}` - ); - }; -} - -export default NetworkService; - -let networkService; - -export const setNetworkService = (service) => { - networkService = service; -}; -export { networkService }; diff --git a/client/src/Utils/NetworkServiceProvider.jsx b/client/src/Utils/NetworkServiceProvider.jsx deleted file mode 100644 index 73c3a2f94..000000000 --- a/client/src/Utils/NetworkServiceProvider.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import { useDispatch } from "react-redux"; -import { useNavigate } from "react-router-dom"; -import { setNetworkService } from "./NetworkService"; -import NetworkService, { networkService } from "./NetworkService"; -import { store } from "../store"; - -const NetworkServiceProvider = ({ children }) => { - const dispatch = useDispatch(); - const navigate = useNavigate(); - if (!networkService) { - setNetworkService(new NetworkService(store, dispatch, navigate)); - } - return children; -}; - -export default NetworkServiceProvider; diff --git a/client/src/main.jsx b/client/src/main.jsx index 5cfbe6f33..ad35d0303 100644 --- a/client/src/main.jsx +++ b/client/src/main.jsx @@ -5,9 +5,6 @@ import { BrowserRouter as Router } from "react-router-dom"; import { Provider } from "react-redux"; import { persistor, store } from "./store"; import { PersistGate } from "redux-persist/integration/react"; -import NetworkServiceProvider from "./Utils/NetworkServiceProvider.jsx"; -import { networkService } from "./Utils/NetworkService"; -export { networkService }; import I18nLoader from "./Components/v1/I18nLoader"; import { initApiClient } from "./Utils/ApiClient"; @@ -21,9 +18,7 @@ ReactDOM.createRoot(document.getElementById("root")).render( > - - - +