diff --git a/src/App.jsx b/src/App.jsx
index 798e186..2406af5 100755
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -22,23 +22,75 @@ function App() {
useEffect(() => {
const verifyAuth = async () => {
try {
+ const savedToken = localStorage.getItem('access_token');
+
+ // Если есть токен, но нет пользователя - делаем запрос к серверу
+ if (savedToken && !localStorage.getItem('user')) {
+ const authStatus = await checkAuth();
+ handleAuthResponse(authStatus);
+ return;
+ }
+
+ // Если есть сохраненный пользователь
+ const savedUser = JSON.parse(localStorage.getItem('user'));
+ if (savedUser && savedToken) {
+ // Если у сохраненного пользователя нет роли - запрашиваем свежие данные
+ if (!savedUser.role) {
+ const authStatus = await checkAuth();
+ handleAuthResponse(authStatus);
+ } else {
+ setAuthState({
+ isAuthenticated: true,
+ isLoading: false,
+ user: savedUser
+ });
+ setShowLoginModal(false);
+ }
+ return;
+ }
+
+ // Стандартная проверка авторизации
const authStatus = await checkAuth();
- setAuthState({
- isAuthenticated: authStatus.isAuthenticated,
- isLoading: false,
- user: authStatus.user || null
- });
- setShowLoginModal(!authStatus.isAuthenticated);
+ handleAuthResponse(authStatus);
} catch (error) {
console.error('Auth verification error:', error);
- setAuthState({
- isAuthenticated: false,
- isLoading: false,
- user: null
- });
- setShowLoginModal(true);
+ handleAuthFailure();
}
};
+
+
+ const handleAuthResponse = (authStatus) => {
+ if (authStatus.isAuthenticated && authStatus.user?.role) {
+ const userToSave = {
+ id: authStatus.user.id,
+ login: authStatus.user.login,
+ role: authStatus.user.role
+ };
+
+ console.log('Saving user:', userToSave);
+ localStorage.setItem('user', JSON.stringify(userToSave));
+
+ setAuthState({
+ isAuthenticated: true,
+ isLoading: false,
+ user: userToSave
+ });
+ setShowLoginModal(false);
+ } else {
+ handleAuthFailure();
+ }
+ };
+ const handleAuthFailure = () => {
+ localStorage.removeItem('user');
+ localStorage.removeItem('access_token');
+ setAuthState({
+ isAuthenticated: false,
+ isLoading: false,
+ user: null
+ });
+ setShowLoginModal(true);
+ };
+
verifyAuth();
}, []);
@@ -46,29 +98,33 @@ function App() {
setAuthState({
isAuthenticated: true,
isLoading: false,
- user: userData
+ user: {
+ id: userData.id,
+ login: userData.login,
+ role: userData.role
+ }
});
setShowLoginModal(false);
};
const handleLogout = async () => {
- try {
- await axios.post(`${import.meta.env.VITE_BACK_URL}/api/auth/logout`, null, {
- withCredentials: true, // чтобы отправлялись куки
- });
-
- localStorage.removeItem('access_token');
- setAuthState({
- isAuthenticated: false,
- isLoading: false,
- user: null,
- });
- setShowLoginModal(true);
- } catch (error) {
- console.error('Logout failed:', error);
- }
-};
+ try {
+ await axios.post(`${import.meta.env.VITE_BACK_URL}/api/auth/logout`, null, {
+ withCredentials: true,
+ });
+ localStorage.removeItem('access_token');
+ localStorage.removeItem('user');
+ setAuthState({
+ isAuthenticated: false,
+ isLoading: false,
+ user: null,
+ });
+ setShowLoginModal(true);
+ } catch (error) {
+ console.error('Logout failed:', error);
+ }
+ };
// Полноэкранный лоадер во время проверки авторизации
if (authState.isLoading) {
return (
diff --git a/src/Components/Layout/Dashboard.jsx b/src/Components/Layout/Dashboard.jsx
index e0c2c42..48ac303 100755
--- a/src/Components/Layout/Dashboard.jsx
+++ b/src/Components/Layout/Dashboard.jsx
@@ -37,7 +37,7 @@ const Content = styled(Box)(({ theme }) => ({
color: theme.palette.custom.modalText,
}));
-const Dashboard = ({ isDarkMode, setIsDarkMode }) => {
+const Dashboard = ({ isDarkMode, setIsDarkMode, user }) => {
const { tabs, activeTab, handleOpenTab, handleCloseTab, setActiveTab } = useTabs("Главная");
const [tabContent, setTabContent] = useState({});
const [treeData1, setTreeData1] = useState(menuData);
@@ -91,11 +91,11 @@ const Dashboard = ({ isDarkMode, setIsDarkMode }) => {
filters: item.filters,
title: item.title,
description: item.description,
- ranges: item.ranges,
+ ranges: item.ranges,
context: {
device: item.filters?.device,
source_id: item.filters?.source_id,
- parent: item
+ parent: item
}
}}
/>
@@ -111,13 +111,13 @@ const Dashboard = ({ isDarkMode, setIsDarkMode }) => {
type: item.metric ? 'metric' : 'menuItem',
metric: item.metric,
filters: item.filters,
- ranges: item.ranges
+ ranges: item.ranges
};
handleOpenTab(newTab);
} else {
setActiveTab(tabId);
}
-};
+ };
// Вспомогательная функция для получения всех дочерних элементов
const getAllChildren = (node) => {
@@ -138,6 +138,7 @@ const Dashboard = ({ isDarkMode, setIsDarkMode }) => {
isDarkMode={isDarkMode}
setIsDarkMode={setIsDarkMode}
onMenuSelect={handleMenuSelect}
+ user={user}
/>
{/* Основной контент */}
diff --git a/src/Components/Layout/SidebarMenu.jsx b/src/Components/Layout/SidebarMenu.jsx
index d592dc9..3c07130 100644
--- a/src/Components/Layout/SidebarMenu.jsx
+++ b/src/Components/Layout/SidebarMenu.jsx
@@ -21,7 +21,8 @@ const SidebarMenu = ({
isDarkMode,
setIsDarkMode,
onSelectItem,
- forceRefreshMenu
+ forceRefreshMenu,
+ user
}) => {
const [collapsed, setCollapsed] = useState(false);
const { sidebarWidth, startResizing } = useSidebarResize(290);
@@ -148,6 +149,7 @@ const SidebarMenu = ({
isDarkMode={isDarkMode}
setIsDarkMode={setIsDarkMode}
forceRefreshMenu={forceRefreshMenu}
+ user={user}
/>
{!collapsed && (
diff --git a/src/Components/Layout/SidebarMenuComponents/SidebarFooter.jsx b/src/Components/Layout/SidebarMenuComponents/SidebarFooter.jsx
index d9546ff..9268f9e 100644
--- a/src/Components/Layout/SidebarMenuComponents/SidebarFooter.jsx
+++ b/src/Components/Layout/SidebarMenuComponents/SidebarFooter.jsx
@@ -1,4 +1,3 @@
-// components/SidebarMenuComponents/SidebarFooter.jsx
import React, { useState } from "react";
import { Brightness4, Brightness7 } from "@mui/icons-material";
import { IconButton, Tooltip } from "@mui/material";
@@ -12,6 +11,7 @@ import {
Button
} from "@mui/material";
import SettingsModal from "../SettingsModal";
+import { RoleBasedRender } from "../../UI/RoleBasedRender";
const FooterList = styled(List)(({ theme }) => ({
backgroundColor: theme.palette.custom.sidebar,
@@ -30,7 +30,13 @@ const FooterListItem = styled(ListItem)(({ theme }) => ({
alignItems: 'center'
}));
-const SidebarFooter = ({ collapsed, isDarkMode, setIsDarkMode, forceRefreshMenu }) => {
+const SidebarFooter = ({
+ collapsed,
+ isDarkMode,
+ setIsDarkMode,
+ forceRefreshMenu,
+ user
+}) => {
const [settingsOpen, setSettingsOpen] = useState(false);
const handleSettingsOpen = () => {
@@ -40,7 +46,11 @@ const SidebarFooter = ({ collapsed, isDarkMode, setIsDarkMode, forceRefreshMenu
const handleSettingsClose = () => {
setSettingsOpen(false);
};
-
+ console.log('SidebarFooter user with role:', {
+ ...user,
+ hasRole: 'role' in user,
+ roleValue: user?.role
+ });
return (
<>
@@ -56,26 +66,29 @@ const SidebarFooter = ({ collapsed, isDarkMode, setIsDarkMode, forceRefreshMenu
)}
- {!collapsed && (
-
- )}
+ >
+
+
+ )}
+
@@ -98,9 +111,14 @@ const SidebarFooter = ({ collapsed, isDarkMode, setIsDarkMode, forceRefreshMenu
-
+ {/* Используем RoleBasedRender для модального окна */}
+
+
+
>
);
};
diff --git a/src/Components/Layout/SidebarMenuWrapper.jsx b/src/Components/Layout/SidebarMenuWrapper.jsx
index ddce8fa..462c2ce 100644
--- a/src/Components/Layout/SidebarMenuWrapper.jsx
+++ b/src/Components/Layout/SidebarMenuWrapper.jsx
@@ -3,7 +3,7 @@ import SidebarMenu from './SidebarMenu';
import { Box, CircularProgress, Typography } from '@mui/material';
import axios from 'axios';
-const SidebarMenuWrapper = ({ isDarkMode, setIsDarkMode, onMenuSelect }) => {
+const SidebarMenuWrapper = ({ isDarkMode, setIsDarkMode, onMenuSelect, user }) => {
const [menuData, setMenuData] = useState(null);
const [lastModified, setLastModified] = useState(null);
const [loading, setLoading] = useState(true);
@@ -177,6 +177,7 @@ const SidebarMenuWrapper = ({ isDarkMode, setIsDarkMode, onMenuSelect }) => {
onCloseEditModal={() => setEditModalOpen(false)}
onSaveChanges={handleSaveChanges}
forceRefreshMenu={forceRefreshMenu}
+ user={user}
/>
);
};
diff --git a/src/Components/UI/LoginModal.jsx b/src/Components/UI/LoginModal.jsx
index f76b7b3..0b308e3 100755
--- a/src/Components/UI/LoginModal.jsx
+++ b/src/Components/UI/LoginModal.jsx
@@ -1,21 +1,27 @@
import React, { useState } from "react";
import Modal from "./Modal";
import "../../Style/LoginModal.css";
-import Logo from '../../assets/images/logo.svg?react';
-import TextField from '@mui/material/TextField';
+import {
+ TextField,
+ IconButton,
+ Button,
+ Typography,
+ InputAdornment
+} from "@mui/material";
+import {
+ Visibility,
+ VisibilityOff
+} from "@mui/icons-material";
import axios from 'axios';
const LoginModal = ({ onLogin, onClose }) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
- const [showPassword, setShowPassword] = React.useState(false);
-
- const handleClickShowPassword = () => setShowPassword((show) => !show);
+ const [showPassword, setShowPassword] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
-
try {
const { data } = await axios.post(
`${import.meta.env.VITE_BACK_URL}/api/auth/login`,
@@ -28,16 +34,33 @@ const LoginModal = ({ onLogin, onClose }) => {
}
);
+ console.log('Login response:', data);
+
if (data.success) {
+ if (!data.user?.role) {
+ console.error('Role missing in response:', data);
+ throw new Error('Роль пользователя не получена');
+ }
+
+ const userData = {
+ id: data.user.id,
+ login: data.user.login,
+ role: data.user.role
+ };
+
localStorage.setItem('access_token', data.access_token);
- onLogin(data.user);
+ localStorage.setItem('user', JSON.stringify(userData));
+
+ console.log('User data saved:', userData);
+
+ onLogin(userData);
onClose();
} else {
- setError(data.message || "Неверный логин или пароль");
+ setError(data.message || 'Ошибка авторизации');
}
} catch (err) {
- console.error('Ошибка при отправке запроса:', err);
- setError(err.response?.data?.message || "Ошибка при подключении к серверу");
+ console.error('Login error:', err);
+ setError(err.response?.data?.message || err.message || 'Ошибка при входе');
}
};
@@ -52,8 +75,8 @@ const LoginModal = ({ onLogin, onClose }) => {
variant="filled"
margin="normal"
required
+ value={username}
onChange={(e) => setUsername(e.target.value)}
- size="normal"
/>
{
margin="normal"
required
type={showPassword ? 'text' : 'password'}
+ value={password}
onChange={(e) => setPassword(e.target.value)}
- size="normal"
+ InputProps={{
+ endAdornment: (
+
+ setShowPassword(!showPassword)}
+ edge="end"
+ sx={{
+ marginRight: '-12px',
+ alignSelf: 'flex-end'
+ }}
+ >
+ {showPassword ? : }
+
+
+ )
+ }}
/>
- {error && {error}
}
- Войти
+ {error && (
+
+ {error}
+
+ )}
+
+
+ Войти
+
);
diff --git a/src/Components/UI/RoleBasedRender.jsx b/src/Components/UI/RoleBasedRender.jsx
new file mode 100644
index 0000000..afdec19
--- /dev/null
+++ b/src/Components/UI/RoleBasedRender.jsx
@@ -0,0 +1,15 @@
+import React from 'react';
+
+export const RoleBasedRender = ({ user, allowedRoles, children }) => {
+ console.log('RoleBasedRender check:', {
+ user,
+ hasRole: user?.role,
+ allowedRoles,
+ hasAccess: user && allowedRoles.includes(user.role)
+ });
+
+ if (!user || !allowedRoles.includes(user.role)) {
+ return null;
+ }
+ return children;
+};
\ No newline at end of file
diff --git a/src/Components/UI/auth.jsx b/src/Components/UI/auth.jsx
index c0beca8..76f0b4c 100644
--- a/src/Components/UI/auth.jsx
+++ b/src/Components/UI/auth.jsx
@@ -1,5 +1,3 @@
-import axios from 'axios';
-
export const checkAuth = async () => {
try {
const { data } = await axios.get(
@@ -12,7 +10,20 @@ export const checkAuth = async () => {
}
);
- return data;
+ console.log('Auth check response:', data);
+
+ if (!data.user) {
+ return { isAuthenticated: false };
+ }
+
+ return {
+ isAuthenticated: data.isAuthenticated,
+ user: {
+ id: data.user.id,
+ login: data.user.login,
+ role: data.user.role
+ }
+ };
} catch (err) {
console.error('Auth check failed:', err);
return { isAuthenticated: false };