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}
+
+ )}
+
+
+ Управление пользователями
+
+
+
+ }
+ onClick={() => setOpenDialog(true)}
+ >
+ Добавить пользователя
+
+
+
+
+
+ {!loading && (
+
+
+
+
+ ID
+ Логин
+ Роль
+ Действия
+
+
+
+ {users.map((user) => (
+
+ {user.id}
+ {user.login}
+ {user.role === 'admin' ? 'Администратор' : 'Пользователь'}
+
+
+ handleDelete(user.id)}
+ color="error"
+ disabled={user.role === 'admin'}
+ >
+
+
+
+
+
+ ))}
+
+
+
+ )}
+
+
+
+ );
+};
+
+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 для новых вкладок */}
+