Compare commits

..

22 Commits

Author SHA1 Message Date
SovietSpiderCat 08fde58a30 added proxy 2025-08-01 11:49:10 +03:00
SovietSpiderCat 6f0b15427a Merge branch 'redisign' of http://git.enode/deployer3000/trust-module-frontend into redisign 2025-07-31 11:15:43 +03:00
deployer3000 8fcace10b1 Merge pull request 'rc' (#52) from rc into main
Reviewed-on: http://git.enode/deployer3000/trust-module-frontend/pulls/52
2025-07-21 13:20:26 +03:00
deployer3000 c99b6add47 Merge pull request 'rc' (#50) from rc into main 2025-07-04 15:12:00 +03:00
deployer3000 9f8d0072c2 Merge pull request 'rc' (#47) from rc into main 2025-06-11 15:09:57 +03:00
deployer3000 a931fd3ea4 Merge pull request 'rc' (#45) from rc into main 2025-06-06 14:39:35 +03:00
deployer3000 350f375015 Merge pull request 'rc' (#43) from rc into main 2025-06-03 13:11:48 +03:00
deployer3000 c55806d180 Merge pull request 'rc' (#41) from rc into main 2025-05-28 14:36:53 +03:00
deployer3000 dd0aa2d706 Merge pull request 'rc' (#37) from rc into main 2025-04-10 15:06:45 +03:00
deployer3000 f69443d051 Merge pull request 'rc' (#27) from rc into main 2025-03-26 13:30:17 +03:00
deployer3000 88b7b8af77 Merge pull request 'login modal ip changed' (#24) from rc into main 2025-03-26 10:07:46 +03:00
deployer3000 b7f6a6c386 Merge pull request 'rc' (#23) from rc into main 2025-03-26 09:24:45 +03:00
deployer3000 f5b92715ef Merge pull request 'rc' (#22) from rc into main 2025-03-19 17:01:39 +03:00
deployer3000 2713142c7d Merge pull request 'rc' (#20) from rc into main 2025-03-19 16:25:45 +03:00
deployer3000 3c60e9d144 Merge pull request 'rc' (#18) from rc into main 2025-03-12 21:26:27 +03:00
deployer3000 a8ca18edc9 Merge pull request 'rc' (#16) from rc into main 2025-03-10 17:51:26 +03:00
deployer3000 b7e5941500 Merge pull request 'DEBUG info 2' (#15) from rc into main 2025-03-10 17:44:02 +03:00
deployer3000 3d63dbe94c Merge pull request 'DEBUG info' (#14) from rc into main 2025-03-10 17:41:06 +03:00
deployer3000 ff10808dd8 Merge pull request 'Notify after PR 2' (#13) from rc into main 2025-03-10 17:21:41 +03:00
deployer3000 92d1236587 Merge pull request 'Notify after PR' (#12) from rc into main
test-org/trust-module-frontend/pipeline/pr-main Build succeeded Details
2025-03-10 17:13:07 +03:00
deployer3000 47159bb698 Merge pull request 'rc' (#10) from rc into main 2025-03-10 15:31:12 +03:00
deployer3000 c4f95448c1 Merge pull request 'rc' (#6) from rc into main 2025-03-10 12:47:39 +03:00
10 changed files with 16410 additions and 509 deletions

16721
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(`${import.meta.env.VITE_BACK_URL}/api/auth/logout`, null, {
await axios.post(`/api/auth/logout`, null, {
withCredentials: true,
});

View File

@ -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,7 +42,6 @@ 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);
@ -91,7 +90,7 @@ class MetricsService {
subscribeToMetric(metricKey, callback, interval = 5000, filters = {}) {
this.connectWebSocket();
if (!this.subscriptions.has(metricKey)) {
this.subscriptions.set(metricKey, []);
const [metric] = metricKey.split('?');
@ -101,10 +100,10 @@ class MetricsService {
filters
});
}
const callbacks = this.subscriptions.get(metricKey);
callbacks.push(callback);
return () => {
this.unsubscribeFromMetric(metricKey, callback);
};

View File

@ -115,7 +115,7 @@ const MetricRangeEditor = ({ onSave }) => {
const loadRanges = useCallback(async () => {
try {
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(
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(`${import.meta.env.VITE_BACK_URL}/api/ranges/update`, ranges);
await axios.post(`/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(`${import.meta.env.VITE_BACK_URL}/api/menu/full`, {
const response = await axios.get(`/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(`${import.meta.env.VITE_BACK_URL}/api/menu/check-updates`, {
const response = await axios.get(`/api/menu/check-updates`, {
headers: { 'If-Modified-Since': lastModified }
});
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);
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(
`${import.meta.env.VITE_BACK_URL}/api/menu/${updatedItem.id}`,
`/api/menu/${updatedItem.id}`,
updatedItem,
{
headers: {

View File

@ -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 { 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';
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);
@ -68,32 +28,38 @@ 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("Детали ошибки 422:", err.response?.data);
setError(err.response?.data?.message || JSON.stringify(err.response?.data) || "Ошибка валидации данных");
console.error("Детали ошибки:", err.response?.data);
setError(err.response?.data?.message || JSON.stringify(err.response?.data)) || "Ошибка при анализе данных";
} finally {
setLoading(false);
}
};
const handleCloseModal = () => {
setOpenModal(false);
};
return (
@ -121,11 +87,63 @@ const AIAnalysisButton = ({ onAnalysisComplete }) => {
</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>
);
};

View File

@ -24,7 +24,8 @@ const LoginModal = ({ onLogin, onClose }) => {
e.preventDefault();
try {
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 },
{
withCredentials: true,

View File

@ -3,7 +3,8 @@ import axios from "axios"
export const checkAuth = async () => {
try {
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,
headers: {

View File

@ -44,12 +44,12 @@ const MetricsAnalyzer = () => {
setError(null);
// 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);
// 2. Преобразуем и отправляем на анализ
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,
headers: {
'Content-Type': 'application/json',

View File

@ -7,5 +7,16 @@ 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,
}
}
}
});