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: "/",