Compare commits
22 Commits
d7c40ee04b
...
08fde58a30
| Author | SHA1 | Date |
|---|---|---|
|
|
08fde58a30 | |
|
|
6f0b15427a | |
|
|
8fcace10b1 | |
|
|
c99b6add47 | |
|
|
9f8d0072c2 | |
|
|
a931fd3ea4 | |
|
|
350f375015 | |
|
|
c55806d180 | |
|
|
dd0aa2d706 | |
|
|
f69443d051 | |
|
|
88b7b8af77 | |
|
|
b7f6a6c386 | |
|
|
f5b92715ef | |
|
|
2713142c7d | |
|
|
3c60e9d144 | |
|
|
a8ca18edc9 | |
|
|
b7e5941500 | |
|
|
3d63dbe94c | |
|
|
ff10808dd8 | |
|
|
92d1236587 | |
|
|
47159bb698 | |
|
|
c4f95448c1 |
File diff suppressed because it is too large
Load Diff
|
|
@ -110,7 +110,7 @@ function App() {
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
try {
|
try {
|
||||||
await axios.post(`${import.meta.env.VITE_BACK_URL}/api/auth/logout`, null, {
|
await axios.post(`/api/auth/logout`, null, {
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ class MetricsService {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('metrics-data', ({ metric, data, requestId }) => {
|
this.socket.on('metrics-data', ({ metric, data, requestId }) => {
|
||||||
console.log('Incoming metric update:', metric);
|
|
||||||
if (requestId && this.pendingRequests.has(requestId)) {
|
if (requestId && this.pendingRequests.has(requestId)) {
|
||||||
const { resolve } = this.pendingRequests.get(requestId);
|
const { resolve } = this.pendingRequests.get(requestId);
|
||||||
resolve(data);
|
resolve(data);
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ const MetricRangeEditor = ({ onSave }) => {
|
||||||
const loadRanges = useCallback(async () => {
|
const loadRanges = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const res = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/ranges/list`);
|
const res = await axios.get(`/api/ranges/list`);
|
||||||
setRanges(
|
setRanges(
|
||||||
Object.entries(res.data).map(([name, r]) => ({
|
Object.entries(res.data).map(([name, r]) => ({
|
||||||
name,
|
name,
|
||||||
|
|
@ -184,7 +184,7 @@ const MetricRangeEditor = ({ onSave }) => {
|
||||||
const saveChanges = useCallback(async () => {
|
const saveChanges = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
await axios.post(`${import.meta.env.VITE_BACK_URL}/api/ranges/update`, ranges);
|
await axios.post(`/api/ranges/update`, ranges);
|
||||||
setHasChanges(false);
|
setHasChanges(false);
|
||||||
setSuccess(true);
|
setSuccess(true);
|
||||||
setTimeout(() => setSuccess(false), 3000);
|
setTimeout(() => setSuccess(false), 3000);
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ const SidebarMenuWrapper = ({ isDarkMode, setIsDarkMode, onMenuSelect, user }) =
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const headers = lastModified ? { 'If-Modified-Since': lastModified } : {};
|
const headers = lastModified ? { 'If-Modified-Since': lastModified } : {};
|
||||||
|
|
||||||
const response = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/menu/full`, {
|
const response = await axios.get(`/api/menu/full`, {
|
||||||
headers,
|
headers,
|
||||||
validateStatus: status => status === 200 || status === 304
|
validateStatus: status => status === 200 || status === 304
|
||||||
});
|
});
|
||||||
|
|
@ -78,13 +78,13 @@ const SidebarMenuWrapper = ({ isDarkMode, setIsDarkMode, onMenuSelect, user }) =
|
||||||
const checkForUpdates = async () => {
|
const checkForUpdates = async () => {
|
||||||
try {
|
try {
|
||||||
setBackgroundLoading(true);
|
setBackgroundLoading(true);
|
||||||
const response = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/menu/check-updates`, {
|
const response = await axios.get(`/api/menu/check-updates`, {
|
||||||
headers: { 'If-Modified-Since': lastModified }
|
headers: { 'If-Modified-Since': lastModified }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data.hasUpdates) {
|
if (response.data.hasUpdates) {
|
||||||
// Если есть обновления, загружаем их в фоне
|
// Если есть обновления, загружаем их в фоне
|
||||||
const updateResponse = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/menu/full`);
|
const updateResponse = await axios.get(`/api/menu/full`);
|
||||||
setMenuData(updateResponse.data);
|
setMenuData(updateResponse.data);
|
||||||
setLastModified(updateResponse.headers['last-modified']);
|
setLastModified(updateResponse.headers['last-modified']);
|
||||||
|
|
||||||
|
|
@ -108,7 +108,7 @@ const SidebarMenuWrapper = ({ isDarkMode, setIsDarkMode, onMenuSelect, user }) =
|
||||||
const handleSaveChanges = async (updatedItem) => {
|
const handleSaveChanges = async (updatedItem) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.put(
|
const response = await axios.put(
|
||||||
`${import.meta.env.VITE_BACK_URL}/api/menu/${updatedItem.id}`,
|
`/api/menu/${updatedItem.id}`,
|
||||||
updatedItem,
|
updatedItem,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1,25 @@
|
||||||
/*import React, { useState } from 'react';
|
|
||||||
import { Button, CircularProgress, Alert, Box } from '@mui/material';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
const AIAnalysisButton = ({ onAnalysisComplete }) => {
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [error, setError] = useState(null);
|
|
||||||
const [result, setResult] = useState(null);
|
|
||||||
|
|
||||||
const handleAnalyze = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await axios.post('/api/clickhouse/send-to-ai');
|
|
||||||
setResult(response.data);
|
|
||||||
if (onAnalysisComplete) {
|
|
||||||
onAnalysisComplete(response.data);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setError(err.response?.data?.message || err.message);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={handleAnalyze}
|
|
||||||
disabled={loading}
|
|
||||||
startIcon={loading ? <CircularProgress size={20} /> : null}
|
|
||||||
sx={{
|
|
||||||
minWidth: '180px',
|
|
||||||
backgroundColor: '#4caf50',
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor: '#388e3c',
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{loading ? 'Analyzing...' : 'AI Analysis'}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{error && (
|
|
||||||
<Alert severity="error" sx={{ mt: 1 }}>{error}</Alert>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AIAnalysisButton; */
|
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Button, CircularProgress, Alert, Box } from '@mui/material';
|
import {
|
||||||
|
Button,
|
||||||
|
CircularProgress,
|
||||||
|
Alert,
|
||||||
|
Box,
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogActions,
|
||||||
|
Typography,
|
||||||
|
IconButton
|
||||||
|
} from '@mui/material';
|
||||||
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const AIAnalysisButton = ({ onAnalysisComplete }) => {
|
const AIAnalysisButton = ({ onAnalysisComplete }) => {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [result, setResult] = useState(null);
|
const [result, setResult] = useState(null);
|
||||||
|
const [openModal, setOpenModal] = useState(false);
|
||||||
|
|
||||||
const handleAnalyze = async () => {
|
const handleAnalyze = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
@ -68,9 +28,12 @@ const AIAnalysisButton = ({ onAnalysisComplete }) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Получаем данные из ClickHouse
|
// 1. Получаем данные из ClickHouse
|
||||||
|
console.log('Запрашиваем данные из ClickHouse...');
|
||||||
const metricsResponse = await axios.get('/api/clickhouse');
|
const metricsResponse = await axios.get('/api/clickhouse');
|
||||||
|
console.log('Получены данные из ClickHouse:', metricsResponse.data);
|
||||||
|
|
||||||
// 2. Отправляем в AI API
|
// 2. Отправляем в AI API
|
||||||
|
console.log('Отправляем данные в AI API:', metricsResponse.data);
|
||||||
const aiResponse = await axios.post(
|
const aiResponse = await axios.post(
|
||||||
'/ai-api/api/metrics/rest',
|
'/ai-api/api/metrics/rest',
|
||||||
metricsResponse.data,
|
metricsResponse.data,
|
||||||
|
|
@ -80,20 +43,23 @@ const AIAnalysisButton = ({ onAnalysisComplete }) => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
console.log('Ответ от AI API:', aiResponse.data);
|
||||||
|
|
||||||
setResult(aiResponse.data);
|
setResult(aiResponse.data);
|
||||||
|
setOpenModal(true);
|
||||||
if (onAnalysisComplete) {
|
if (onAnalysisComplete) {
|
||||||
onAnalysisComplete(aiResponse.data);
|
onAnalysisComplete(aiResponse.data);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Детали ошибки 422:", err.response?.data);
|
console.error("Детали ошибки:", err.response?.data);
|
||||||
setError(err.response?.data?.message || JSON.stringify(err.response?.data) || "Ошибка валидации данных");
|
setError(err.response?.data?.message || JSON.stringify(err.response?.data)) || "Ошибка при анализе данных";
|
||||||
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setOpenModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -121,11 +87,63 @@ const AIAnalysisButton = ({ onAnalysisComplete }) => {
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{result && !loading && (
|
|
||||||
<Alert severity="success" sx={{ mt: 1 }}>
|
|
||||||
Анализ завершен! Результат в консоли.
|
{/* Модальное окно с результатом */}
|
||||||
</Alert>
|
<Dialog
|
||||||
|
open={openModal}
|
||||||
|
onClose={handleCloseModal}
|
||||||
|
fullWidth={true}
|
||||||
|
maxWidth="lg"
|
||||||
|
scroll="paper"
|
||||||
|
>
|
||||||
|
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
Результат AI-анализа
|
||||||
|
<IconButton
|
||||||
|
aria-label="close"
|
||||||
|
onClick={handleCloseModal}
|
||||||
|
sx={{
|
||||||
|
color: (theme) => theme.palette.grey[500],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
{result ? (
|
||||||
|
<>
|
||||||
|
<Typography variant="h6" gutterBottom>Данные анализа:</Typography>
|
||||||
|
<Box
|
||||||
|
component="pre"
|
||||||
|
sx={{
|
||||||
|
p: 2,
|
||||||
|
bgcolor: '#f5f5f5',
|
||||||
|
borderRadius: 1,
|
||||||
|
overflow: 'auto',
|
||||||
|
maxHeight: '60vh',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
wordWrap: 'break-word'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{JSON.stringify(result, null, 2)}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<DialogContentText>Нет данных для отображения</DialogContentText>
|
||||||
)}
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleCloseModal}>Закрыть</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(JSON.stringify(result, null, 2));
|
||||||
|
alert('Результат скопирован в буфер обмена');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Копировать
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ const LoginModal = ({ onLogin, onClose }) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post(
|
const { data } = await axios.post(
|
||||||
`${import.meta.env.VITE_BACK_URL}/api/auth/login`,
|
//`${import.meta.env.VITE_BACK_URL}/api/auth/login`,
|
||||||
|
'/api/auth/login',
|
||||||
{ login: username, password },
|
{ login: username, password },
|
||||||
{
|
{
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ import axios from "axios"
|
||||||
export const checkAuth = async () => {
|
export const checkAuth = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(
|
const { data } = await axios.get(
|
||||||
`${import.meta.env.VITE_BACK_URL}/api/auth/check`,
|
//`${import.meta.env.VITE_BACK_URL}/api/auth/check`,
|
||||||
|
'/api/auth/check',
|
||||||
{
|
{
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,12 @@ const MetricsAnalyzer = () => {
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
// 1. Сначала загружаем метрики
|
// 1. Сначала загружаем метрики
|
||||||
const metricsResponse = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/metrics/all-values`);
|
const metricsResponse = await axios.get(`/api/metrics/all-values`);
|
||||||
setMetrics(metricsResponse.data);
|
setMetrics(metricsResponse.data);
|
||||||
|
|
||||||
// 2. Преобразуем и отправляем на анализ
|
// 2. Преобразуем и отправляем на анализ
|
||||||
const requestData = transformMetricsForAnalysis(metricsResponse.data);
|
const requestData = transformMetricsForAnalysis(metricsResponse.data);
|
||||||
const analysisResponse = await axios.get(`${import.meta.env.VITE_BACK_URL}:5134/api/metrics/rest`, {
|
const analysisResponse = await axios.get(`:5134/api/metrics/rest`, {
|
||||||
data: requestData,
|
data: requestData,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,16 @@ export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
host: true,
|
host: true,
|
||||||
allowedHosts: ['dev.msf.enode', 'demo-msf.kis-npo.ru'],
|
allowedHosts: ['dev.msf.enode', 'demo-msf.kis-npo.ru'],
|
||||||
|
proxy: {
|
||||||
|
'/ai-api': {
|
||||||
|
target: 'http://192.168.2.39:5134',
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/ai-api/, ''),
|
||||||
|
},
|
||||||
|
'/api': {
|
||||||
|
target: 'http://192.168.2.39:3000',
|
||||||
|
changeOrigin: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Loading…
Reference in New Issue