From 60694258f26893a71c70fff0381154b68878089e Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Thu, 17 Apr 2025 16:19:22 -0400 Subject: [PATCH 01/13] Add backend reachability check method --- src/Utils/NetworkService.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/Utils/NetworkService.js b/src/Utils/NetworkService.js index 7c4ffba9f..7ac76f89e 100644 --- a/src/Utils/NetworkService.js +++ b/src/Utils/NetworkService.js @@ -504,6 +504,41 @@ class NetworkService { return this.axiosInstance.get("/auth/users/superadmin"); } + /** + * ************************************ + * Checks if the backend server is reachable + * ************************************ + * + * @async + * @returns {Promise} True if backend is reachable, false otherwise + */ + async checkBackendReachability() { + try { + // Create a temporary instance that doesn't follow redirects and has a short timeout + const tempInstance = axios.create({ + timeout: 5000, + maxRedirects: 0, + baseURL: this.axiosInstance.defaults.baseURL, + }); + + // Make a simple GET request to the root endpoint + await tempInstance.get('/', { + headers: { + Authorization: undefined // Prevent sending the auth token + } + }); + + return true; + } catch (error) { + // If we get a successful HTTP status or redirect, the backend is reachable + if (error.response) { + return true; // Server responded with any status code, so it's reachable + } + console.error("Backend connection failed:", error); + return false; + } + } + /** * ************************************ * Get all users From b404ff3022d5203e13d34f2e2a85802e8d318f8f Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Thu, 17 Apr 2025 16:36:53 -0400 Subject: [PATCH 02/13] some modification --- src/Utils/NetworkService.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Utils/NetworkService.js b/src/Utils/NetworkService.js index 7ac76f89e..583b65089 100644 --- a/src/Utils/NetworkService.js +++ b/src/Utils/NetworkService.js @@ -516,7 +516,6 @@ class NetworkService { try { // Create a temporary instance that doesn't follow redirects and has a short timeout const tempInstance = axios.create({ - timeout: 5000, maxRedirects: 0, baseURL: this.axiosInstance.defaults.baseURL, }); From 402361ce97bb71621192f599acd81041c2600aa6 Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Mon, 21 Apr 2025 10:57:49 -0400 Subject: [PATCH 03/13] error message in login page and translation --- client/src/Pages/Auth/Login/Login.jsx | 118 ++++++++++++++++++++++---- client/src/locales/gb.json | 8 ++ client/src/locales/ru.json | 8 ++ client/src/locales/tr.json | 8 ++ 4 files changed, 127 insertions(+), 15 deletions(-) diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx index 9db56ae49..52663a479 100644 --- a/client/src/Pages/Auth/Login/Login.jsx +++ b/client/src/Pages/Auth/Login/Login.jsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; -import { Box, Stack, Typography } from "@mui/material"; +import { Box, Stack, Typography, Alert, Button } from "@mui/material"; import { useTheme } from "@emotion/react"; import { credentials } from "../../../Validation/validation"; import { login } from "../../../Features/Auth/authSlice"; @@ -16,6 +16,7 @@ import PasswordStep from "./Components/PasswordStep"; import ThemeSwitch from "../../../Components/ThemeSwitch"; import ForgotPasswordLabel from "./Components/ForgotPasswordLabel"; import LanguageSelector from "../../../Components/LanguageSelector"; +import { useTranslation } from "react-i18next"; const DEMO = import.meta.env.VITE_APP_DEMO; @@ -27,6 +28,7 @@ const Login = () => { const dispatch = useDispatch(); const navigate = useNavigate(); const theme = useTheme(); + const { t } = useTranslation(); const authState = useSelector((state) => state.auth); const { authToken } = authState; @@ -42,22 +44,81 @@ const Login = () => { }); const [errors, setErrors] = useState({}); const [step, setStep] = useState(0); + const [backendReachable, setBackendReachable] = useState(true); + const [isCheckingConnection, setIsCheckingConnection] = useState(false); useEffect(() => { if (authToken) { navigate("/uptime"); return; } - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - navigate("/register"); + + // Check backend connectivity first + const handleRetry = async () => { + setIsCheckingConnection(true); + try { + const isReachable = await networkService.checkBackendReachability(); + setBackendReachable(isReachable); + + // If backend is now reachable, proceed with admin check + if (isReachable) { + networkService + .doesSuperAdminExist() + .then((response) => { + if (response.data.data === false) { + navigate("/register"); + } + }) + .catch((error) => { + logger.error(error); + }); + createToast({ + body: t("backendReconnected"), + }); + } else { + createToast({ + body: t("backendStillUnreachable"), + }); } - }) - .catch((error) => { - logger.error(error); - }); + } catch (error) { + logger.error("Error checking backend connectivity:", error); + setBackendReachable(false); + createToast({ + body: t("backendConnectionError"), + }); + } finally { + setIsCheckingConnection(false); + } + }; + + const checkConnectivity = async () => { + setIsCheckingConnection(true); + try { + const isReachable = await networkService.checkBackendReachability(); + setBackendReachable(isReachable); + + // Only proceed with admin check if backend is reachable + if (isReachable) { + networkService + .doesSuperAdminExist() + .then((response) => { + if (response.data.data === false) { + navigate("/register"); + } + }) + .catch((error) => { + logger.error(error); + }); + } + } catch (error) { + logger.error("Error checking backend connectivity:", error); + setBackendReachable(false); + } finally { + setIsCheckingConnection(false); + } + }; + + checkConnectivity(); }, [authToken, navigate]); const handleChange = (event) => { @@ -82,6 +143,14 @@ 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( { email: form.email }, @@ -208,7 +277,24 @@ const Login = () => { }, }} > - {step === 0 ? ( + {!backendReachable ? ( + + + {t("backendUnreachable")} + + + {t("backendUnreachableMessage")} + + + + ) : step === 0 ? ( { /> ) )} - + {backendReachable && ( + + )} ); diff --git a/client/src/locales/gb.json b/client/src/locales/gb.json index 343386bec..3bfb07fcf 100644 --- a/client/src/locales/gb.json +++ b/client/src/locales/gb.json @@ -99,6 +99,14 @@ "settingsFailedToAddDemoMonitors": "Failed to add demo monitors", "settingsMonitorsDeleted": "Successfully deleted all monitors", "settingsFailedToDeleteMonitors": "Failed to delete all monitors", + "backendUnreachable": "Server Connection Error", + "backendUnreachableMessage": "We're unable to connect to the server. Please check your internet connection or contact your administrator if the problem persists.", + "backendUnreachableError": "Cannot connect to the server. Please try again later.", + "retryConnection": "Retry Connection", + "retryingConnection": "Connecting...", + "backendReconnected": "Successfully reconnected to the server.", + "backendStillUnreachable": "Server is still unreachable. Please try again later.", + "backendConnectionError": "Error connecting to the server. Please check your network connection.", "starPromptTitle": "Star Checkmate", "starPromptDescription": "See the latest releases and help grow the community on GitHub", "https": "HTTPS", diff --git a/client/src/locales/ru.json b/client/src/locales/ru.json index 5d0f649de..4845830ef 100644 --- a/client/src/locales/ru.json +++ b/client/src/locales/ru.json @@ -99,6 +99,14 @@ "settingsFailedToAddDemoMonitors": "Не удалось добавить демонстрационные мониторы", "settingsMonitorsDeleted": "Успешно удалены все мониторы", "settingsFailedToDeleteMonitors": "Не удалось удалить все мониторы", + "backendUnreachable": "Ошибка подключения к серверу", + "backendUnreachableMessage": "Мы не можем подключиться к серверу. Пожалуйста, проверьте ваше интернет-соединение или обратитесь к администратору, если проблема не устраняется.", + "backendUnreachableError": "Невозможно подключиться к серверу. Пожалуйста, повторите попытку позже.", + "retryConnection": "Повторить подключение", + "retryingConnection": "Подключение...", + "backendReconnected": "Успешно восстановлено подключение к серверу.", + "backendStillUnreachable": "Сервер по-прежнему недоступен. Пожалуйста, повторите попытку позже.", + "backendConnectionError": "Ошибка подключения к серверу. Пожалуйста, проверьте ваше сетевое подключение.", "starPromptTitle": "Star Checkmate", "starPromptDescription": "Ознакомьтесь с последними релизами и помогите развить сообщество на GitHub", "https": "HTTPS", diff --git a/client/src/locales/tr.json b/client/src/locales/tr.json index 1b759a29d..11d79fabc 100644 --- a/client/src/locales/tr.json +++ b/client/src/locales/tr.json @@ -99,6 +99,14 @@ "settingsFailedToAddDemoMonitors": "Demo monitörler eklenemedi", "settingsMonitorsDeleted": "Tüm monitörler başarıyla silindi", "settingsFailedToDeleteMonitors": "Monitörler silinemedi", + "backendUnreachable": "Sunucu Bağlantı Hatası", + "backendUnreachableMessage": "Sunucuya bağlanamıyoruz. Lütfen internet bağlantınızı kontrol edin veya sorun devam ederse yöneticinize başvurun.", + "backendUnreachableError": "Sunucuya bağlanılamıyor. Lütfen daha sonra tekrar deneyin.", + "retryConnection": "Bağlantıyı Yeniden Dene", + "retryingConnection": "Bağlanıyor...", + "backendReconnected": "Sunucuya başarıyla yeniden bağlandı.", + "backendStillUnreachable": "Sunucu hala erişilemez durumda. Lütfen daha sonra tekrar deneyin.", + "backendConnectionError": "Sunucuya bağlanırken hata oluştu. Lütfen ağ bağlantınızı kontrol edin.", "starPromptTitle": "Checkmate yıldızla değerlendirin", "starPromptDescription": "En son sürümleri görün ve GitHub'daki topluluğun büyümesine yardımcı olun", "https": "HTTPS", From b4f2c2c1c2d6bfd6c5ef6e989560d8c69c2cb676 Mon Sep 17 00:00:00 2001 From: Mohadeseh Safari <139602878+mohicody@users.noreply.github.com> Date: Mon, 21 Apr 2025 11:05:42 -0400 Subject: [PATCH 04/13] Delete client/src/Pages/Auth/Login/Login.jsx --- client/src/Pages/Auth/Login/Login.jsx | 326 -------------------------- 1 file changed, 326 deletions(-) delete mode 100644 client/src/Pages/Auth/Login/Login.jsx diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx deleted file mode 100644 index 52663a479..000000000 --- a/client/src/Pages/Auth/Login/Login.jsx +++ /dev/null @@ -1,326 +0,0 @@ -import { useState, useEffect } from "react"; -import { useNavigate } from "react-router-dom"; -import { Box, Stack, Typography, Alert, Button } 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"; -import ThemeSwitch from "../../../Components/ThemeSwitch"; -import ForgotPasswordLabel from "./Components/ForgotPasswordLabel"; -import LanguageSelector from "../../../Components/LanguageSelector"; -import { useTranslation } from "react-i18next"; - -const DEMO = import.meta.env.VITE_APP_DEMO; - -/** - * Displays the login page. - */ - -const Login = () => { - const dispatch = useDispatch(); - const navigate = useNavigate(); - const theme = useTheme(); - const { t } = useTranslation(); - - const authState = useSelector((state) => state.auth); - const { authToken } = authState; - - const idMap = { - "login-email-input": "email", - "login-password-input": "password", - }; - - const [form, setForm] = useState({ - email: DEMO !== undefined ? "uptimedemo@demo.com" : "", - password: DEMO !== undefined ? "Demouser1!" : "", - }); - const [errors, setErrors] = useState({}); - const [step, setStep] = useState(0); - const [backendReachable, setBackendReachable] = useState(true); - const [isCheckingConnection, setIsCheckingConnection] = useState(false); - - useEffect(() => { - if (authToken) { - navigate("/uptime"); - return; - } - - // Check backend connectivity first - const handleRetry = async () => { - setIsCheckingConnection(true); - try { - const isReachable = await networkService.checkBackendReachability(); - setBackendReachable(isReachable); - - // If backend is now reachable, proceed with admin check - if (isReachable) { - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - navigate("/register"); - } - }) - .catch((error) => { - logger.error(error); - }); - createToast({ - body: t("backendReconnected"), - }); - } else { - createToast({ - body: t("backendStillUnreachable"), - }); - } - } catch (error) { - logger.error("Error checking backend connectivity:", error); - setBackendReachable(false); - createToast({ - body: t("backendConnectionError"), - }); - } finally { - setIsCheckingConnection(false); - } - }; - - const checkConnectivity = async () => { - setIsCheckingConnection(true); - try { - const isReachable = await networkService.checkBackendReachability(); - setBackendReachable(isReachable); - - // Only proceed with admin check if backend is reachable - if (isReachable) { - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - navigate("/register"); - } - }) - .catch((error) => { - logger.error(error); - }); - } - } catch (error) { - logger.error("Error checking backend connectivity:", error); - setBackendReachable(false); - } finally { - setIsCheckingConnection(false); - } - }; - - checkConnectivity(); - }, [authToken, navigate]); - - const handleChange = (event) => { - const { value, id } = event.target; - const name = idMap[id]; - const lowerCasedValue = name === idMap["login-email-input"]? value?.toLowerCase()||value : value - setForm((prev) => ({ - ...prev, - [name]: lowerCasedValue, - })); - - const { error } = credentials.validate({ [name]: lowerCasedValue }, { abortEarly: false }); - - setErrors((prev) => { - const prevErrors = { ...prev }; - if (error) prevErrors[name] = error.details[0].message; - else delete prevErrors[name]; - return prevErrors; - }); - }; - - 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( - { email: form.email }, - { abortEarly: false } - ); - if (error) { - setErrors((prev) => ({ ...prev, email: error.details[0].message })); - createToast({ body: error.details[0].message }); - } else { - setStep(1); - } - } else if (step === 1) { - const { error } = credentials.validate(form, { abortEarly: false }); - - if (error) { - // validation errors - const newErrors = {}; - error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - createToast({ - body: - error.details && error.details.length > 0 - ? error.details[0].message - : "Error validating data.", - }); - } else { - const action = await dispatch(login(form)); - if (action.payload.success) { - navigate("/uptime"); - createToast({ - body: "Welcome back! You're successfully logged in.", - }); - } else { - if (action.payload) { - if (action.payload.msg === "Incorrect password") - setErrors({ - password: "The password you provided does not match our records", - }); - // dispatch errors - createToast({ - body: action.payload.msg, - }); - } else { - // unknown errors - createToast({ - body: "Unknown error.", - }); - } - } - } - } - }; - - return ( - - - - - - - - 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), - }, - }, - }} - > - {!backendReachable ? ( - - - {t("backendUnreachable")} - - - {t("backendUnreachableMessage")} - - - - ) : step === 0 ? ( - - ) : ( - step === 1 && ( - setStep(0)} - /> - ) - )} - {backendReachable && ( - - )} - - - ); -}; - -export default Login; From 0508fde86cf70dd4188cf0371744c47f829d00d8 Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Mon, 21 Apr 2025 13:57:26 -0400 Subject: [PATCH 05/13] modify login page --- client/src/Pages/Auth/Login/Login.jsx | 144 +++++++++++++------------- 1 file changed, 73 insertions(+), 71 deletions(-) diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx index 52663a479..7eaf4d441 100644 --- a/client/src/Pages/Auth/Login/Login.jsx +++ b/client/src/Pages/Auth/Login/Login.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useCallback } from "react"; import { useNavigate } from "react-router-dom"; import { Box, Stack, Typography, Alert, Button } from "@mui/material"; import { useTheme } from "@emotion/react"; @@ -44,82 +44,69 @@ 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); + // 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); + + if (isReachable) { + networkService + .doesSuperAdminExist() + .then((response) => { + if (response.data.data === false) { + navigate("/register"); + } + }) + .catch((error) => { + logger.error(error); + }); + + // Only show reconnection toast if this was a retry attempt + if (isRetry) { + createToast({ + body: t("backendReconnected"), + }); + } + } else if (isRetry) { + // Only show still unreachable toast if this was a retry attempt + createToast({ + body: t("backendStillUnreachable"), + }); + } + } catch (error) { + logger.error("Error checking backend connectivity:", error); + setBackendReachable(false); + + if (isRetry) { + createToast({ + body: t("backendConnectionError"), + }); + } + } finally { + setIsCheckingConnection(false); + } + }, [navigate, t]); + + // Function to handle retry button click + const handleRetry = () => checkConnectivity(true); + useEffect(() => { if (authToken) { navigate("/uptime"); return; } - - // Check backend connectivity first - const handleRetry = async () => { - setIsCheckingConnection(true); - try { - const isReachable = await networkService.checkBackendReachability(); - setBackendReachable(isReachable); - - // If backend is now reachable, proceed with admin check - if (isReachable) { - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - navigate("/register"); - } - }) - .catch((error) => { - logger.error(error); - }); - createToast({ - body: t("backendReconnected"), - }); - } else { - createToast({ - body: t("backendStillUnreachable"), - }); - } - } catch (error) { - logger.error("Error checking backend connectivity:", error); - setBackendReachable(false); - createToast({ - body: t("backendConnectionError"), - }); - } finally { - setIsCheckingConnection(false); - } - }; - - const checkConnectivity = async () => { - setIsCheckingConnection(true); - try { - const isReachable = await networkService.checkBackendReachability(); - setBackendReachable(isReachable); - - // Only proceed with admin check if backend is reachable - if (isReachable) { - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - navigate("/register"); - } - }) - .catch((error) => { - logger.error(error); - }); - } - } catch (error) { - logger.error("Error checking backend connectivity:", error); - setBackendReachable(false); - } finally { - setIsCheckingConnection(false); - } - }; - + + // Initial connectivity check checkConnectivity(); - }, [authToken, navigate]); + }, [authToken, navigate, checkConnectivity]); const handleChange = (event) => { const { value, id } = event.target; @@ -142,7 +129,7 @@ const Login = () => { const handleSubmit = async (event) => { event.preventDefault(); - + // Check backend connectivity before proceeding if (!backendReachable) { createToast({ @@ -279,7 +266,21 @@ const Login = () => { > {!backendReachable ? ( - + {t("backendUnreachable")} @@ -287,9 +288,10 @@ const Login = () => { From c0adbb5bd7625bcac807a23813b725599e4309a6 Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Mon, 21 Apr 2025 16:06:44 -0400 Subject: [PATCH 06/13] modify login --- client/src/Pages/Auth/Login/Login.jsx | 30 +++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx index 7eaf4d441..0440d14b6 100644 --- a/client/src/Pages/Auth/Login/Login.jsx +++ b/client/src/Pages/Auth/Login/Login.jsx @@ -58,16 +58,24 @@ const Login = () => { setBackendReachable(isReachable); if (isReachable) { - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - navigate("/register"); - } - }) - .catch((error) => { - logger.error(error); - }); + // Only check if super admin exists if not already attempting to log in + // This allows users to log in even if system thinks no super admin exists + if (window.location.pathname === "/" || window.location.pathname === "") { + networkService + .doesSuperAdminExist() + .then((response) => { + if (response.data.data === false) { + // Add a toast notification instead of forcing redirect + createToast({ + body: t("noSuperAdminMessage"), + type: "info" + }); + } + }) + .catch((error) => { + logger.error(error); + }); + } // Only show reconnection toast if this was a retry attempt if (isRetry) { @@ -93,7 +101,7 @@ const Login = () => { } finally { setIsCheckingConnection(false); } - }, [navigate, t]); + }, [t]); // Removed navigate since we no longer use it within this function // Function to handle retry button click const handleRetry = () => checkConnectivity(true); From 4b92683c7268dc67534c31740ed2d4dd3f6fdcce Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Mon, 21 Apr 2025 22:00:28 -0400 Subject: [PATCH 07/13] some modification --- client/src/Pages/Auth/Login/Login.jsx | 49 ++++++++++++++++++++++----- client/src/locales/gb.json | 4 ++- client/src/locales/ru.json | 2 ++ client/src/locales/tr.json | 2 ++ 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx index 0440d14b6..a5eec8898 100644 --- a/client/src/Pages/Auth/Login/Login.jsx +++ b/client/src/Pages/Auth/Login/Login.jsx @@ -48,6 +48,7 @@ const Login = () => { // 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 @@ -100,6 +101,7 @@ const Login = () => { } } finally { setIsCheckingConnection(false); + setInitialCheckComplete(true); } }, [t]); // Removed navigate since we no longer use it within this function @@ -272,8 +274,13 @@ const Login = () => { }, }} > - {!backendReachable ? ( - + {!initialCheckComplete ? ( + + {/* Show loading state while doing initial connectivity check */} + {t("retryingConnection")} + + ) : !backendReachable ? ( + { > {t("backendUnreachable")} - + {t("backendUnreachableMessage")} @@ -323,10 +334,32 @@ const Login = () => { ) )} {backendReachable && ( - + <> + + + {/* Registration link */} + + + {t("doNotHaveAccount")} + + navigate("/register")} + > + {t("registerHere")} + + + )} diff --git a/client/src/locales/gb.json b/client/src/locales/gb.json index 3bfb07fcf..e24362dc8 100644 --- a/client/src/locales/gb.json +++ b/client/src/locales/gb.json @@ -1,5 +1,7 @@ { "dontHaveAccount": "Don't have account", + "doNotHaveAccount": "Do not have an account?", + "registerHere": "Register here", "email": "E-mail", "forgotPassword": "Forgot Password", "password": "password", @@ -102,7 +104,7 @@ "backendUnreachable": "Server Connection Error", "backendUnreachableMessage": "We're unable to connect to the server. Please check your internet connection or contact your administrator if the problem persists.", "backendUnreachableError": "Cannot connect to the server. Please try again later.", - "retryConnection": "Retry Connection", + "retryConnection": "Retry connection", "retryingConnection": "Connecting...", "backendReconnected": "Successfully reconnected to the server.", "backendStillUnreachable": "Server is still unreachable. Please try again later.", diff --git a/client/src/locales/ru.json b/client/src/locales/ru.json index 4845830ef..33a7c615c 100644 --- a/client/src/locales/ru.json +++ b/client/src/locales/ru.json @@ -1,5 +1,7 @@ { "dontHaveAccount": "Нет аккаунта", + "doNotHaveAccount": "У вас нет учетной записи?", + "registerHere": "Зарегистрироваться здесь", "email": "Почта", "forgotPassword": "Забыли пароль", "password": "пароль", diff --git a/client/src/locales/tr.json b/client/src/locales/tr.json index 11d79fabc..767384b9d 100644 --- a/client/src/locales/tr.json +++ b/client/src/locales/tr.json @@ -1,5 +1,7 @@ { "dontHaveAccount": "Hesabınız yok mu", + "doNotHaveAccount": "Hesabınız yok mu?", + "registerHere": "Buradan kayıt olun", "email": "E-posta", "forgotPassword": "Parolamı unuttum", "password": "Parola", From eec11f303c6aeb8657f719a1fa2302db6ccab0d3 Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Wed, 23 Apr 2025 23:28:35 -0400 Subject: [PATCH 08/13] applied the feedback --- .../Pages/Auth/Login/Components/EmailStep.jsx | 9 ++ .../Auth/Login/Components/PasswordStep.jsx | 19 +++- client/src/Pages/Auth/Login/Login.jsx | 87 +++++++++++-------- 3 files changed, 80 insertions(+), 35 deletions(-) diff --git a/client/src/Pages/Auth/Login/Components/EmailStep.jsx b/client/src/Pages/Auth/Login/Components/EmailStep.jsx index ec5a4b2db..d50212e85 100644 --- a/client/src/Pages/Auth/Login/Components/EmailStep.jsx +++ b/client/src/Pages/Auth/Login/Components/EmailStep.jsx @@ -70,8 +70,17 @@ const EmailStep = ({ form, errors, onSubmit, onChange }) => { color="accent" type="submit" disabled={errors.email && true} + className="dashboard-style-button" sx={{ width: "30%", + px: theme.spacing(6), + borderRadius: `${theme.shape.borderRadius}px !important`, + '&.MuiButtonBase-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, + '&.MuiButton-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, "&.Mui-focusVisible": { outline: `2px solid ${theme.palette.primary.main}`, outlineOffset: `2px`, diff --git a/client/src/Pages/Auth/Login/Components/PasswordStep.jsx b/client/src/Pages/Auth/Login/Components/PasswordStep.jsx index 0b8142161..a57246e23 100644 --- a/client/src/Pages/Auth/Login/Components/PasswordStep.jsx +++ b/client/src/Pages/Auth/Login/Components/PasswordStep.jsx @@ -75,8 +75,16 @@ const PasswordStep = ({ form, errors, onSubmit, onChange, onBack }) => { variant="outlined" color="info" onClick={onBack} + className="dashboard-style-button" sx={{ px: theme.spacing(5), + borderRadius: `${theme.shape.borderRadius}px !important`, + '&.MuiButtonBase-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, + '&.MuiButton-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, "& svg.MuiSvgIcon-root": { mr: theme.spacing(3), }, @@ -95,13 +103,22 @@ const PasswordStep = ({ form, errors, onSubmit, onChange, onBack }) => { type="submit" loading={authState.isLoading} disabled={errors.password && true} + className="dashboard-style-button" sx={{ width: "30%", + px: theme.spacing(4), + borderRadius: `${theme.shape.borderRadius}px !important`, + '&.MuiButtonBase-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, + '&.MuiButton-root': { + borderRadius: `${theme.shape.borderRadius}px !important` + }, "&.Mui-focusVisible": { outline: `2px solid ${theme.palette.primary.main}`, outlineOffset: `2px`, - boxShadow: `none`, }, + boxShadow: `none`, }} > {t("continue")} diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx index a5eec8898..bbf14be8b 100644 --- a/client/src/Pages/Auth/Login/Login.jsx +++ b/client/src/Pages/Auth/Login/Login.jsx @@ -1,6 +1,7 @@ import { useState, useEffect, useCallback } from "react"; import { useNavigate } from "react-router-dom"; -import { Box, Stack, Typography, Alert, Button } from "@mui/material"; +import { Box, Stack, Typography, Button } from "@mui/material"; +import Alert from "../../../Components/Alert"; import { useTheme } from "@emotion/react"; import { credentials } from "../../../Validation/validation"; import { login } from "../../../Features/Auth/authSlice"; @@ -281,39 +282,52 @@ const Login = () => { ) : !backendReachable ? ( - - {t("backendUnreachable")} - - - {t("backendUnreachableMessage")} - - + gap: theme.spacing(3) + } + }}> + + + + + {t("backendUnreachableMessage")} + + + + + ) : step === 0 ? ( { component="span" color={theme.palette.accent.main} ml={theme.spacing(2)} - sx={{ userSelect: "none" }} + sx={{ + cursor: 'pointer', + '&:hover': { + color: theme.palette.accent.darker + } + }} onClick={() => navigate("/register")} > {t("registerHere")} From 720f60cbb7ff894711625adcf5fb8c6f3fcf10cd Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Sun, 27 Apr 2025 12:57:38 -0400 Subject: [PATCH 09/13] Improve backend connectivity check with early returns and Remove unrelated super admin check to focus on backend reachability --- client/src/Pages/Auth/Login/Login.jsx | 35 ++++++++------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/client/src/Pages/Auth/Login/Login.jsx b/client/src/Pages/Auth/Login/Login.jsx index bbf14be8b..0e5c5dcaf 100644 --- a/client/src/Pages/Auth/Login/Login.jsx +++ b/client/src/Pages/Auth/Login/Login.jsx @@ -59,36 +59,21 @@ const Login = () => { const isReachable = await networkService.checkBackendReachability(); setBackendReachable(isReachable); - if (isReachable) { - // Only check if super admin exists if not already attempting to log in - // This allows users to log in even if system thinks no super admin exists - if (window.location.pathname === "/" || window.location.pathname === "") { - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - // Add a toast notification instead of forcing redirect - createToast({ - body: t("noSuperAdminMessage"), - type: "info" - }); - } - }) - .catch((error) => { - logger.error(error); - }); - } - - // Only show reconnection toast if this was a retry attempt + // Early return if backend is not reachable + if (isReachable === false) { + // Show toast only on retry attempts if (isRetry) { createToast({ - body: t("backendReconnected"), + body: t("backendStillUnreachable"), }); } - } else if (isRetry) { - // Only show still unreachable toast if this was a retry attempt + return; + } + + // Show reconnection toast if this was a retry attempt and backend is now reachable + if (isRetry) { createToast({ - body: t("backendStillUnreachable"), + body: t("backendReconnected"), }); } } catch (error) { From ce6789791e4540e0ddff71f144f7b73d099f7bd9 Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Thu, 1 May 2025 14:17:22 -0400 Subject: [PATCH 10/13] changed error message --- client/src/locales/gb.json | 2 +- client/src/locales/ru.json | 2 +- client/src/locales/tr.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/locales/gb.json b/client/src/locales/gb.json index e24362dc8..aaecdef98 100644 --- a/client/src/locales/gb.json +++ b/client/src/locales/gb.json @@ -102,7 +102,7 @@ "settingsMonitorsDeleted": "Successfully deleted all monitors", "settingsFailedToDeleteMonitors": "Failed to delete all monitors", "backendUnreachable": "Server Connection Error", - "backendUnreachableMessage": "We're unable to connect to the server. Please check your internet connection or contact your administrator if the problem persists.", + "backendUnreachableMessage": "We're unable to connect to the server. Please check your internet connection or verify your deployment configuration if the problem persists.", "backendUnreachableError": "Cannot connect to the server. Please try again later.", "retryConnection": "Retry connection", "retryingConnection": "Connecting...", diff --git a/client/src/locales/ru.json b/client/src/locales/ru.json index 33a7c615c..5b2d6aa0c 100644 --- a/client/src/locales/ru.json +++ b/client/src/locales/ru.json @@ -102,7 +102,7 @@ "settingsMonitorsDeleted": "Успешно удалены все мониторы", "settingsFailedToDeleteMonitors": "Не удалось удалить все мониторы", "backendUnreachable": "Ошибка подключения к серверу", - "backendUnreachableMessage": "Мы не можем подключиться к серверу. Пожалуйста, проверьте ваше интернет-соединение или обратитесь к администратору, если проблема не устраняется.", + "backendUnreachableMessage": "Мы не можем подключиться к серверу. Пожалуйста, проверьте ваше интернет-соединение или проверьте конфигурацию развертывания, если проблема не устраняется.", "backendUnreachableError": "Невозможно подключиться к серверу. Пожалуйста, повторите попытку позже.", "retryConnection": "Повторить подключение", "retryingConnection": "Подключение...", diff --git a/client/src/locales/tr.json b/client/src/locales/tr.json index 767384b9d..e2c7b3827 100644 --- a/client/src/locales/tr.json +++ b/client/src/locales/tr.json @@ -102,7 +102,7 @@ "settingsMonitorsDeleted": "Tüm monitörler başarıyla silindi", "settingsFailedToDeleteMonitors": "Monitörler silinemedi", "backendUnreachable": "Sunucu Bağlantı Hatası", - "backendUnreachableMessage": "Sunucuya bağlanamıyoruz. Lütfen internet bağlantınızı kontrol edin veya sorun devam ederse yöneticinize başvurun.", + "backendUnreachableMessage": "Sunucuya bağlanamıyoruz. Lütfen internet bağlantınızı kontrol edin veya sorun devam ederse dağıtım yapılandırmanızı doğrulayın.", "backendUnreachableError": "Sunucuya bağlanılamıyor. Lütfen daha sonra tekrar deneyin.", "retryConnection": "Bağlantıyı Yeniden Dene", "retryingConnection": "Bağlanıyor...", From 791714caeb125c5bd8ea6246733579fb176f21cf Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Sun, 4 May 2025 22:54:55 -0400 Subject: [PATCH 11/13] 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()); From 8e495d12513b9e6a3a6cfb53212f04c3b512bdc1 Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Sun, 4 May 2025 23:16:34 -0400 Subject: [PATCH 12/13] remove "the backend server is reachable" --- client/src/Utils/NetworkService.js | 34 +----------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index 8a06fc9da..90f35d7e3 100644 --- a/client/src/Utils/NetworkService.js +++ b/client/src/Utils/NetworkService.js @@ -513,39 +513,7 @@ class NetworkService { return this.axiosInstance.get("/auth/users/superadmin"); } - /** - * ************************************ - * Checks if the backend server is reachable - * ************************************ - * - * @async - * @returns {Promise} True if backend is reachable, false otherwise - */ - async checkBackendReachability() { - try { - // Create a temporary instance that doesn't follow redirects and has a short timeout - const tempInstance = axios.create({ - maxRedirects: 0, - baseURL: this.axiosInstance.defaults.baseURL, - }); - - // Make a simple GET request to the root endpoint - await tempInstance.get('/', { - headers: { - Authorization: undefined // Prevent sending the auth token - } - }); - - return true; - } catch (error) { - // If we get a successful HTTP status or redirect, the backend is reachable - if (error.response) { - return true; // Server responded with any status code, so it's reachable - } - console.error("Backend connection failed:", error); - return false; - } - } + /** * ************************************ From 8e8f35b44752f19e3029b10c591755c087997cdc Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Tue, 6 May 2025 23:27:10 -0400 Subject: [PATCH 13/13] simplify styling in ServerUnreachable component --- client/src/Pages/ServerUnreachable.jsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/client/src/Pages/ServerUnreachable.jsx b/client/src/Pages/ServerUnreachable.jsx index b635a8460..aaa61cbc4 100644 --- a/client/src/Pages/ServerUnreachable.jsx +++ b/client/src/Pages/ServerUnreachable.jsx @@ -33,7 +33,6 @@ const ServerUnreachable = () => { 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"), }); @@ -46,15 +45,6 @@ const ServerUnreachable = () => {