Compare commits
No commits in common. "08fde58a30932baa8c93bf3f784fb1309d7ee24f" and "d7c40ee04b7019162b86c72f806c70ceb02e7508" have entirely different histories.
08fde58a30
...
d7c40ee04b
File diff suppressed because it is too large
Load Diff
|
|
@ -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,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class MetricsService {
|
|||
this.subscriptions = new Map();
|
||||
this.pendingRequests = new Map();
|
||||
window.addEventListener('beforeunload', this.cleanupAll.bind(this));
|
||||
window.addEventListener('pagehide', this.cleanupAll.bind(this));
|
||||
window.addEventListener('pagehide', this.cleanupAll.bind(this));
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
this.cleanupAll();
|
||||
|
|
@ -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);
|
||||
|
|
@ -90,7 +91,7 @@ class MetricsService {
|
|||
|
||||
subscribeToMetric(metricKey, callback, interval = 5000, filters = {}) {
|
||||
this.connectWebSocket();
|
||||
|
||||
|
||||
if (!this.subscriptions.has(metricKey)) {
|
||||
this.subscriptions.set(metricKey, []);
|
||||
const [metric] = metricKey.split('?');
|
||||
|
|
@ -100,10 +101,10 @@ class MetricsService {
|
|||
filters
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const callbacks = this.subscriptions.get(metricKey);
|
||||
callbacks.push(callback);
|
||||
|
||||
|
||||
return () => {
|
||||
this.unsubscribeFromMetric(metricKey, callback);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -1,25 +1,65 @@
|
|||
/*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,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogActions,
|
||||
Typography,
|
||||
IconButton
|
||||
} from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
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,38 +68,32 @@ 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,
|
||||
metricsResponse.data,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
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>
|
||||
)}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseModal}>Закрыть</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(JSON.stringify(result, null, 2));
|
||||
alert('Результат скопирован в буфер обмена');
|
||||
}}
|
||||
>
|
||||
Копировать
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
{result && !loading && (
|
||||
<Alert severity="success" sx={{ mt: 1 }}>
|
||||
Анализ завершен! Результат в консоли.
|
||||
</Alert>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Loading…
Reference in New Issue