From 791714caeb125c5bd8ea6246733579fb176f21cf Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Sun, 4 May 2025 22:54:55 -0400 Subject: [PATCH] using axios response interceptor --- client/src/Pages/Auth/Login/Login.jsx | 185 +++++-------------------- client/src/Pages/ServerUnreachable.jsx | 171 +++++++++++++++++++++++ client/src/Routes/index.jsx | 7 + client/src/Utils/NetworkService.js | 9 ++ 4 files changed, 220 insertions(+), 152 deletions(-) create mode 100644 client/src/Pages/ServerUnreachable.jsx diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx index 0e5c5dcaf..e43346640 100644 --- a/client/src/Pages/Auth/Login/Login.jsx +++ b/client/src/Pages/Auth/Login/Login.jsx @@ -1,16 +1,13 @@ -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; -import { Box, Stack, Typography, Button } from "@mui/material"; -import Alert from "../../../Components/Alert"; +import { Box, Stack, Typography } from "@mui/material"; import { useTheme } from "@emotion/react"; import { credentials } from "../../../Validation/validation"; import { login } from "../../../Features/Auth/authSlice"; import { useDispatch, useSelector } from "react-redux"; import { createToast } from "../../../Utils/toastUtils"; -import { networkService } from "../../../main"; import Background from "../../../assets/Images/background-grid.svg?react"; import Logo from "../../../assets/icons/checkmate-icon.svg?react"; -import { logger } from "../../../Utils/Logger"; import "../index.css"; import EmailStep from "./Components/EmailStep"; import PasswordStep from "./Components/PasswordStep"; @@ -46,63 +43,13 @@ const Login = () => { const [errors, setErrors] = useState({}); const [step, setStep] = useState(0); - // State variables for backend connectivity status and loading state - const [backendReachable, setBackendReachable] = useState(true); - const [isCheckingConnection, setIsCheckingConnection] = useState(false); - const [initialCheckComplete, setInitialCheckComplete] = useState(false); - // Function to check if the backend server is reachable and handle connectivity status - // Wrapped in useCallback to prevent recreation on each render - const checkConnectivity = useCallback(async (isRetry = false) => { - setIsCheckingConnection(true); - try { - const isReachable = await networkService.checkBackendReachability(); - setBackendReachable(isReachable); - - // Early return if backend is not reachable - if (isReachable === false) { - // Show toast only on retry attempts - if (isRetry) { - createToast({ - body: t("backendStillUnreachable"), - }); - } - return; - } - - // Show reconnection toast if this was a retry attempt and backend is now reachable - if (isRetry) { - createToast({ - body: t("backendReconnected"), - }); - } - } catch (error) { - logger.error("Error checking backend connectivity:", error); - setBackendReachable(false); - - if (isRetry) { - createToast({ - body: t("backendConnectionError"), - }); - } - } finally { - setIsCheckingConnection(false); - setInitialCheckComplete(true); - } - }, [t]); // Removed navigate since we no longer use it within this function - - // Function to handle retry button click - const handleRetry = () => checkConnectivity(true); useEffect(() => { if (authToken) { navigate("/uptime"); - return; } - - // Initial connectivity check - checkConnectivity(); - }, [authToken, navigate, checkConnectivity]); + }, [authToken, navigate]); const handleChange = (event) => { const { value, id } = event.target; @@ -125,14 +72,6 @@ const Login = () => { const handleSubmit = async (event) => { event.preventDefault(); - - // Check backend connectivity before proceeding - if (!backendReachable) { - createToast({ - body: t("backendUnreachableError"), - }); - return; - } if (step === 0) { const { error } = credentials.validate( @@ -260,61 +199,7 @@ const Login = () => { }, }} > - {!initialCheckComplete ? ( - - {/* Show loading state while doing initial connectivity check */} - {t("retryingConnection")} - - ) : !backendReachable ? ( - - - - - - - {t("backendUnreachableMessage")} - - - - - - - ) : step === 0 ? ( + {step === 0 ? ( { /> ) )} - {backendReachable && ( - <> - - - {/* Registration link */} - - - {t("doNotHaveAccount")} - - navigate("/register")} - > - {t("registerHere")} - - - - )} + + + {/* Registration link */} + + + {t("doNotHaveAccount")} + + navigate("/register")} + > + {t("registerHere")} + + ); diff --git a/client/src/Pages/ServerUnreachable.jsx b/client/src/Pages/ServerUnreachable.jsx new file mode 100644 index 000000000..b635a8460 --- /dev/null +++ b/client/src/Pages/ServerUnreachable.jsx @@ -0,0 +1,171 @@ +import React, { useState } from "react"; +import { Box, Typography, Button, Stack } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import { useNavigate } from "react-router"; +import { networkService } from "../Utils/NetworkService"; +import Alert from "../Components/Alert"; +import { createToast } from "../Utils/toastUtils"; +import { useTranslation } from "react-i18next"; +import Background from "../assets/Images/background-grid.svg?react"; +import Logo from "../assets/icons/checkmate-icon.svg?react"; +import ThemeSwitch from "../Components/ThemeSwitch"; +import LanguageSelector from "../Components/LanguageSelector"; + +const ServerUnreachable = () => { + const theme = useTheme(); + const navigate = useNavigate(); + const { t } = useTranslation(); + + // State for tracking connection check status + const [isCheckingConnection, setIsCheckingConnection] = useState(false); + + const handleRetry = React.useCallback(async () => { + setIsCheckingConnection(true); + 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 }); + + // If successful, show toast and navigate to login page + createToast({ + body: t("backendReconnected", "Connection to server restored"), + }); + navigate("/login"); + } catch (error) { + // If still unreachable, stay on this page and show toast + console.error("Server still unreachable:", error); + createToast({ + body: t("backendStillUnreachable", "Server is still unreachable"), + }); + } finally { + setIsCheckingConnection(false); + } + }, [navigate, t]); + + return ( + + + + + + {/* Header with logo */} + + + + Checkmate + + + + + + + .MuiStack-root": { + border: 1, + borderRadius: theme.spacing(5), + borderColor: theme.palette.primary.lowContrast, + backgroundColor: theme.palette.primary.main, + padding: { + xs: theme.spacing(12), + sm: theme.spacing(20), + }, + }, + }} + > + + + + + + + {t("backendUnreachableMessage", "The Checkmate server is not responding. Please check your deployment configuration or try again later.")} + + + + + + + + + ); +}; + +export default ServerUnreachable; diff --git a/client/src/Routes/index.jsx b/client/src/Routes/index.jsx index 82d7af7ca..90be1282e 100644 --- a/client/src/Routes/index.jsx +++ b/client/src/Routes/index.jsx @@ -36,6 +36,9 @@ import DistributedUptimeDetails from "../Pages/DistributedUptime/Details"; import CreateDistributedUptimeStatus from "../Pages/DistributedUptimeStatus/Create"; import DistributedUptimeStatus from "../Pages/DistributedUptimeStatus/Status"; +// Server Status +import ServerUnreachable from "../Pages/ServerUnreachable"; + // Incidents import Incidents from "../Pages/Incidents"; @@ -279,6 +282,10 @@ const Routes = () => { element={} /> + } + /> } diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index 583b65089..8a06fc9da 100644 --- a/client/src/Utils/NetworkService.js +++ b/client/src/Utils/NetworkService.js @@ -45,6 +45,15 @@ class NetworkService { 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()); dispatch(clearUptimeMonitorState());