Compare commits

..

No commits in common. "08fde58a30932baa8c93bf3f784fb1309d7ee24f" and "d7c40ee04b7019162b86c72f806c70ceb02e7508" have entirely different histories.

10 changed files with 495 additions and 16396 deletions

16693
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -110,7 +110,7 @@ function App() {
const handleLogout = async () => {
try {
await axios.post(`/api/auth/logout`, null, {
await axios.post(`${import.meta.env.VITE_BACK_URL}/api/auth/logout`, null, {
withCredentials: true,
});

View File

@ -42,6 +42,7 @@ class MetricsService {
});
this.socket.on('metrics-data', ({ metric, data, requestId }) => {
console.log('Incoming metric update:', metric);
if (requestId && this.pendingRequests.has(requestId)) {
const { resolve } = this.pendingRequests.get(requestId);
resolve(data);

View File

@ -115,7 +115,7 @@ const MetricRangeEditor = ({ onSave }) => {
const loadRanges = useCallback(async () => {
try {
setLoading(true);
const res = await axios.get(`/api/ranges/list`);
const res = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/ranges/list`);
setRanges(
Object.entries(res.data).map(([name, r]) => ({
name,
@ -184,7 +184,7 @@ const MetricRangeEditor = ({ onSave }) => {
const saveChanges = useCallback(async () => {
try {
setLoading(true);
await axios.post(`/api/ranges/update`, ranges);
await axios.post(`${import.meta.env.VITE_BACK_URL}/api/ranges/update`, ranges);
setHasChanges(false);
setSuccess(true);
setTimeout(() => setSuccess(false), 3000);

View File

@ -44,7 +44,7 @@ const SidebarMenuWrapper = ({ isDarkMode, setIsDarkMode, onMenuSelect, user }) =
setLoading(true);
const headers = lastModified ? { 'If-Modified-Since': lastModified } : {};
const response = await axios.get(`/api/menu/full`, {
const response = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/menu/full`, {
headers,
validateStatus: status => status === 200 || status === 304
});
@ -78,13 +78,13 @@ const SidebarMenuWrapper = ({ isDarkMode, setIsDarkMode, onMenuSelect, user }) =
const checkForUpdates = async () => {
try {
setBackgroundLoading(true);
const response = await axios.get(`/api/menu/check-updates`, {
const response = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/menu/check-updates`, {
headers: { 'If-Modified-Since': lastModified }
});
if (response.data.hasUpdates) {
// Если есть обновления, загружаем их в фоне
const updateResponse = await axios.get(`/api/menu/full`);
const updateResponse = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/menu/full`);
setMenuData(updateResponse.data);
setLastModified(updateResponse.headers['last-modified']);
@ -108,7 +108,7 @@ const SidebarMenuWrapper = ({ isDarkMode, setIsDarkMode, onMenuSelect, user }) =
const handleSaveChanges = async (updatedItem) => {
try {
const response = await axios.put(
`/api/menu/${updatedItem.id}`,
`${import.meta.env.VITE_BACK_URL}/api/menu/${updatedItem.id}`,
updatedItem,
{
headers: {

View File

@ -1,25 +1,65 @@
import React, { useState } from 'react';
import {
Button,
CircularProgress,
Alert,
Box,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
Typography,
IconButton
} from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
/*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 { 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 [openModal, setOpenModal] = useState(false);
const handleAnalyze = async () => {
setLoading(true);
@ -28,12 +68,9 @@ const AIAnalysisButton = ({ onAnalysisComplete }) => {
try {
// 1. Получаем данные из ClickHouse
console.log('Запрашиваем данные из ClickHouse...');
const metricsResponse = await axios.get('/api/clickhouse');
console.log('Получены данные из ClickHouse:', metricsResponse.data);
// 2. Отправляем в AI API
console.log('Отправляем данные в AI API:', metricsResponse.data);
const aiResponse = await axios.post(
'/ai-api/api/metrics/rest',
metricsResponse.data,
@ -43,23 +80,20 @@ const AIAnalysisButton = ({ onAnalysisComplete }) => {
},
}
);
console.log('Ответ от AI API:', aiResponse.data);
setResult(aiResponse.data);
setOpenModal(true);
if (onAnalysisComplete) {
onAnalysisComplete(aiResponse.data);
}
} catch (err) {
console.error("Детали ошибки:", err.response?.data);
setError(err.response?.data?.message || JSON.stringify(err.response?.data)) || "Ошибка при анализе данных";
console.error("Детали ошибки 422:", err.response?.data);
setError(err.response?.data?.message || JSON.stringify(err.response?.data) || "Ошибка валидации данных");
} finally {
setLoading(false);
}
};
const handleCloseModal = () => {
setOpenModal(false);
};
return (
@ -87,63 +121,11 @@ const AIAnalysisButton = ({ onAnalysisComplete }) => {
</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>
{result && !loading && (
<Alert severity="success" sx={{ mt: 1 }}>
Анализ завершен! Результат в консоли.
</Alert>
)}
</DialogContent>
<DialogActions>
<Button onClick={handleCloseModal}>Закрыть</Button>
<Button
onClick={() => {
navigator.clipboard.writeText(JSON.stringify(result, null, 2));
alert('Результат скопирован в буфер обмена');
}}
>
Копировать
</Button>
</DialogActions>
</Dialog>
</Box>
);
};

View File

@ -24,8 +24,7 @@ const LoginModal = ({ onLogin, onClose }) => {
e.preventDefault();
try {
const { data } = await axios.post(
//`${import.meta.env.VITE_BACK_URL}/api/auth/login`,
'/api/auth/login',
`${import.meta.env.VITE_BACK_URL}/api/auth/login`,
{ login: username, password },
{
withCredentials: true,

View File

@ -3,8 +3,7 @@ import axios from "axios"
export const checkAuth = async () => {
try {
const { data } = await axios.get(
//`${import.meta.env.VITE_BACK_URL}/api/auth/check`,
'/api/auth/check',
`${import.meta.env.VITE_BACK_URL}/api/auth/check`,
{
withCredentials: true,
headers: {

View File

@ -44,12 +44,12 @@ const MetricsAnalyzer = () => {
setError(null);
// 1. Сначала загружаем метрики
const metricsResponse = await axios.get(`/api/metrics/all-values`);
const metricsResponse = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/metrics/all-values`);
setMetrics(metricsResponse.data);
// 2. Преобразуем и отправляем на анализ
const requestData = transformMetricsForAnalysis(metricsResponse.data);
const analysisResponse = await axios.get(`:5134/api/metrics/rest`, {
const analysisResponse = await axios.get(`${import.meta.env.VITE_BACK_URL}:5134/api/metrics/rest`, {
data: requestData,
headers: {
'Content-Type': 'application/json',

View File

@ -7,16 +7,5 @@ export default defineConfig({
server: {
host: true,
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,
}
}
}
});