diff --git a/client/src/Components/RoleProtectedRoute/index.jsx b/client/src/Components/RoleProtectedRoute/index.jsx new file mode 100644 index 000000000..c7b7dfc12 --- /dev/null +++ b/client/src/Components/RoleProtectedRoute/index.jsx @@ -0,0 +1,35 @@ +import { Navigate } from "react-router-dom"; +import { useSelector } from "react-redux"; +import PropTypes from "prop-types"; + +/** + * ProtectedRoute is a wrapper component that ensures only authenticated users + * can access the wrapped content. It checks authentication status (e.g., from Redux or Context). + * If the user is authenticated, it renders the children; otherwise, it redirects to the login page. + * + * @param {Object} props - The props passed to the ProtectedRoute component. + * @param {React.ReactNode} props.children - The children to render if the user is authenticated. + * @returns {React.ReactElement} The children wrapped in a protected route or a redirect to the login page. + */ + +const RoleProtectedRoute = ({ roles, children }) => { + const authState = useSelector((state) => state.auth); + const userRoles = authState?.user?.role || []; + const canAccess = userRoles.some((role) => roles.includes(role)); + + return canAccess ? ( + children + ) : ( + + ); +}; + +RoleProtectedRoute.propTypes = { + children: PropTypes.element.isRequired, + roles: PropTypes.array, +}; + +export default RoleProtectedRoute; diff --git a/client/src/Components/Sidebar/index.jsx b/client/src/Components/Sidebar/index.jsx index e9f0d3ad7..219ceff1e 100644 --- a/client/src/Components/Sidebar/index.jsx +++ b/client/src/Components/Sidebar/index.jsx @@ -49,6 +49,8 @@ import { useDispatch, useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import { clearAuthState } from "../../Features/Auth/authSlice"; import { toggleSidebar } from "../../Features/UI/uiSlice"; +import { TurnedIn } from "@mui/icons-material"; +import { rules } from "eslint-plugin-react-refresh"; const getMenu = (t) => [ { name: t("menu.uptime"), path: "uptime", icon: }, @@ -121,7 +123,6 @@ function Sidebar() { const { t } = useTranslation(); const authState = useSelector((state) => state.auth); - const menu = getMenu(t); const otherMenuItems = getOtherMenuItems(t); const accountMenuItems = getAccountMenuItems(t); const collapsed = useSelector((state) => state.ui.sidebar.collapsed); @@ -133,6 +134,13 @@ function Sidebar() { const sidebarRef = useRef(null); const [sidebarReady, setSidebarReady] = useState(false); const TRANSITION_DURATION = 200; + let menu = getMenu(t); + menu = menu.filter((item) => { + if (item.path === "logs") { + return user.role?.includes("admin") || user.role?.includes("superadmin"); + } + return true; + }); useEffect(() => { if (!collapsed) { diff --git a/client/src/Routes/index.jsx b/client/src/Routes/index.jsx index 60df27211..965ed2676 100644 --- a/client/src/Routes/index.jsx +++ b/client/src/Routes/index.jsx @@ -48,6 +48,7 @@ import Settings from "../Pages/Settings"; import Maintenance from "../Pages/Maintenance"; import ProtectedRoute from "../Components/ProtectedRoute"; +import RoleProtectedRoute from "../Components/RoleProtectedRoute"; import CreateNewMaintenanceWindow from "../Pages/Maintenance/CreateMaintenance"; import withAdminCheck from "../Components/HOC/withAdminCheck"; import BulkImport from "../Pages/Uptime/BulkImport"; @@ -190,7 +191,11 @@ const Routes = () => { } + element={ + + + + } /> diff --git a/client/vite.config.js b/client/vite.config.js index 20943eeb4..838cc5142 100644 --- a/client/vite.config.js +++ b/client/vite.config.js @@ -4,14 +4,8 @@ import svgr from "vite-plugin-svgr"; import { execSync } from "child_process"; export default defineConfig(({ mode }) => { - let version = process.env.VITE_APP_VERSION; - if (!version || version === "unknown") { - try { - version = execSync("git describe --tags --abbrev=0").toString().trim(); - } catch { - version = "unknown"; - } - } + const env = loadEnv(mode, process.cwd(), ""); + let version = 2.2; return { base: "/",