diff --git a/Dockerfile b/Dockerfile index d5d7d39..467553f 100755 --- a/Dockerfile +++ b/Dockerfile @@ -8,4 +8,8 @@ RUN npm install --verbose COPY vite.config.js eslint.config.js ./ COPY . . +ENV HOST=0.0.0.0 + +EXPOSE 5173 + ENTRYPOINT ["npm", "run", "dev"] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b7cb31c..d76ff97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "chartjs-adapter-date-fns": "^3.0.0", "chartjs-chart-box-and-violin-plot": "^4.0.0", "d3": "^7.9.0", + "esbuild": "^0.25.8", "react": "^18.3.1", "react-chartjs-2": "^5.0.0", "react-datepicker": "^8.1.0", @@ -40,7 +41,7 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", - "vite": "^6.0.5" + "vite": "^7.1.0" } }, "node_modules/@alloc/quick-lru": { @@ -8880,9 +8881,10 @@ }, "node_modules/esbuild": { "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz", "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -18642,16 +18644,17 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "4.9.5", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { @@ -18903,22 +18906,23 @@ } }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/vite/-/vite-7.1.0.tgz", + "integrity": "sha512-3jdAy3NhBJYsa/lCFcnRfbK4kNkO/bhijFCnv5ByUQk/eekYagoV2yQSISUrhpV+5JiY5hmwOh7jNnQ68dFMuQ==", + "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -18927,14 +18931,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" diff --git a/package.json b/package.json index d107aba..ca982a3 100755 --- a/package.json +++ b/package.json @@ -10,27 +10,28 @@ "preview": "vite preview" }, "dependencies": { - "chartjs-adapter-date-fns": "^3.0.0", - "recharts": "^2.15.1", - "d3": "^7.9.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "chart.js": "^4.0.0", - "chartjs-chart-box-and-violin-plot": "^4.0.0", - "react-chartjs-2": "^5.0.0", - "axios": "^1.7.9", - "react-datepicker": "^8.1.0", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/material": "^6.4.7", "@mui/icons-material": "^6.4.8", - "reactflow": "^11.11.4", - "vite-plugin-svgr": "^4.3.0", - "react-scripts": "^5.0.1", - "socket.io-client": "^4.8.1", + "@mui/material": "^6.4.7", "antd": "^5.24.7", + "axios": "^1.7.9", + "chart.js": "^4.0.0", + "chartjs-adapter-date-fns": "^3.0.0", + "chartjs-chart-box-and-violin-plot": "^4.0.0", + "d3": "^7.9.0", + "esbuild": "^0.25.8", + "react": "^18.3.1", + "react-chartjs-2": "^5.0.0", + "react-datepicker": "^8.1.0", + "react-dom": "^18.3.1", + "react-scripts": "^5.0.1", + "react-virtualized-auto-sizer": "1.0.26", "react-window": "1.8.11", - "react-virtualized-auto-sizer": "1.0.26" + "reactflow": "^11.11.4", + "recharts": "^2.15.1", + "socket.io-client": "^4.8.1", + "vite-plugin-svgr": "^4.3.0" }, "devDependencies": { "@eslint/js": "^9.17.0", @@ -42,6 +43,6 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", - "vite": "^6.0.5" + "vite": "^7.1.0" } -} \ No newline at end of file +} diff --git a/src/Charts2/Components/metricsService.jsx b/src/Charts2/Components/metricsService.jsx index 7cf4d81..0a6139a 100644 --- a/src/Charts2/Components/metricsService.jsx +++ b/src/Charts2/Components/metricsService.jsx @@ -21,7 +21,7 @@ class MetricsService { } console.log('Connecting WebSocket...'); - this.socket = io(`${this.baseUrl.replace('http', 'ws')}/api/metrics-ws`, { + this.socket = io(`${this.baseUrl.replace('http', 'ws')}/ws`, { transports: ['websocket'], withCredentials: true, }); @@ -152,6 +152,6 @@ class MetricsService { } // Создаем экземпляр сервиса -const metricsService = new MetricsService(import.meta.env.VITE_BACK_URL); +const metricsService = new MetricsService(); export default metricsService; \ No newline at end of file diff --git a/src/Components/Layout/SettingsComponents/UserManagement.jsx b/src/Components/Layout/SettingsComponents/UserManagement.jsx new file mode 100644 index 0000000..3247251 --- /dev/null +++ b/src/Components/Layout/SettingsComponents/UserManagement.jsx @@ -0,0 +1,227 @@ +import React, { useState, useEffect } from 'react'; +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Button, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Select, + MenuItem, + FormControl, + InputLabel, + IconButton, + Typography, + Box, + CircularProgress, + Alert, + Snackbar, + Divider, + Tooltip +} from '@mui/material'; +import { Add, Delete } from '@mui/icons-material'; +import axios from 'axios'; + +const UserManagement = () => { + const [users, setUsers] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(''); + const [openDialog, setOpenDialog] = useState(false); + const [newUser, setNewUser] = useState({ + login: '', + password: '', + role: 'user' + }); + + useEffect(() => { + fetchUsers(); + }, []); + + const fetchUsers = async () => { + setLoading(true); + try { + const response = await axios.get('/api/auth/users', { + withCredentials: true + }); + setUsers(response.data); + setError(''); + } catch (err) { + setError('Не удалось загрузить пользователей'); + console.error(err); + } finally { + setLoading(false); + } + }; + + const handleInputChange = (e) => { + const { name, value } = e.target; + setNewUser(prev => ({ ...prev, [name]: value })); + }; + + const handleRoleChange = (e) => { + setNewUser(prev => ({ ...prev, role: e.target.value })); + }; + + const handleSubmit = async () => { + try { + await axios.post('/api/auth/users', newUser, { + withCredentials: true + }); + setOpenDialog(false); + setNewUser({ + login: '', + password: '', + role: 'user' + }); + setSuccess('Пользователь успешно создан'); + fetchUsers(); + } catch (err) { + setError(err.response?.data?.message || 'Не удалось создать пользователя'); + console.error(err); + } + }; + + const handleDelete = async (id) => { + try { + await axios.delete(`/api/auth/users/${id}`, { + withCredentials: true + }); + setSuccess('Пользователь успешно удален'); + fetchUsers(); + } catch (err) { + setError(err.response?.data?.message || 'Не удалось удалить пользователя'); + console.error(err); + } + }; + + return ( + + {loading && ( + + + + )} + + {error && ( + setError('')}> + {error} + + )} + + {success && ( + setSuccess('')}> + {success} + + )} + + + Управление пользователями + + + + + + + + + {!loading && ( + + + + + ID + Логин + Роль + Действия + + + + {users.map((user) => ( + + {user.id} + {user.login} + {user.role === 'admin' ? 'Администратор' : 'Пользователь'} + + + handleDelete(user.id)} + color="error" + disabled={user.role === 'admin'} + > + + + + + + ))} + +
+
+ )} + + setOpenDialog(false)}> + Добавить нового пользователя + + + + + + Роль + + + + + + + + + +
+ ); +}; + +export default UserManagement; \ No newline at end of file diff --git a/src/Components/Layout/SettingsModal.jsx b/src/Components/Layout/SettingsModal.jsx index c61340c..54d7b04 100644 --- a/src/Components/Layout/SettingsModal.jsx +++ b/src/Components/Layout/SettingsModal.jsx @@ -20,6 +20,7 @@ import { import CloseIcon from '@mui/icons-material/Close'; import SaveIcon from '@mui/icons-material/Save'; import MetricRangeEditor from './SettingsComponents/MetricRangeEditor'; +import UserManagement from './SettingsComponents/UserManagement'; const Transition = React.forwardRef(function Transition(props, ref) { return ; @@ -147,6 +148,7 @@ const SettingsModal = ({ open, onClose, onMenuUpdate }) => { + {/* Добавляйте новые вкладки здесь */} @@ -161,9 +163,14 @@ const SettingsModal = ({ open, onClose, onMenuUpdate }) => { + + + + {/* Добавляйте новые TabPanel для новых вкладок */} +