Compare commits
No commits in common. "47159bb6981c372d0e3ffb2216fa5747b2cb9d57" and "c4f95448c11ba3a51fed67b6e2582be5cde91c99" have entirely different histories.
47159bb698
...
c4f95448c1
|
|
@ -1,16 +1,3 @@
|
||||||
def notify(String giteaUser, String giteaPass, String repositoryUrl, String repositoryName, String prId, String buildStatus) {
|
|
||||||
def status = buildStatus == 'success' ? 'success' : 'failure'
|
|
||||||
def description = buildStatus == 'success' ? 'Build succeeded' : 'Build failed'
|
|
||||||
|
|
||||||
sh """
|
|
||||||
curl -X POST \
|
|
||||||
-u "${giteaUser}:${giteaPass}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"state": "${status}", "description": "${description}", "context": "ci/jenkins"}' \
|
|
||||||
${repositoryUrl}/api/v1/repos/${repositoryName}/statuses/${prId}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent any
|
||||||
environment {
|
environment {
|
||||||
|
|
@ -63,7 +50,6 @@ pipeline {
|
||||||
echo "Attempting to merge PR ${env.CHANGE_ID} into master..."
|
echo "Attempting to merge PR ${env.CHANGE_ID} into master..."
|
||||||
withCredentials([usernamePassword(credentialsId: 'gitea_creds', usernameVariable: 'GITEA_USER', passwordVariable: 'GITEA_PASS')]) {
|
withCredentials([usernamePassword(credentialsId: 'gitea_creds', usernameVariable: 'GITEA_USER', passwordVariable: 'GITEA_PASS')]) {
|
||||||
def prId = env.CHANGE_ID
|
def prId = env.CHANGE_ID
|
||||||
// Merge the PR
|
|
||||||
sh """
|
sh """
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
-u "${GITEA_USER}:${GITEA_PASS}" \
|
-u "${GITEA_USER}:${GITEA_PASS}" \
|
||||||
|
|
@ -72,8 +58,6 @@ pipeline {
|
||||||
http://git.entcor/api/v1/repos/deployer3000/trust-module-frontend/pulls/${prId}/merge
|
http://git.entcor/api/v1/repos/deployer3000/trust-module-frontend/pulls/${prId}/merge
|
||||||
"""
|
"""
|
||||||
echo "PR ${prId} merged successfully into master!"
|
echo "PR ${prId} merged successfully into master!"
|
||||||
// Notify Gitea with the PR status
|
|
||||||
notify(GITEA_USER, GITEA_PASS, GITEA_REPOSITORY_URL, "trust-module-frontend", prId, "success")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { LineChart, XAxis, YAxis, CartesianGrid, Tooltip, Legend, Line, ResponsiveContainer } from 'recharts';
|
import { LineChart, XAxis, YAxis, CartesianGrid, Tooltip, Legend, Line, ResponsiveContainer, Brush } from 'recharts';
|
||||||
|
|
||||||
const LineChartComponent = ({ chartData, metricName, colors, description, onRangeSelect, filteredData }) => {
|
|
||||||
const [selectionStart, setSelectionStart] = useState(null);
|
|
||||||
const [selectionEnd, setSelectionEnd] = useState(null);
|
|
||||||
|
|
||||||
|
const LineChartComponent = ({ chartData, metricName, metricType, colors, description }) => {
|
||||||
// Создаем массив уникальных временных меток
|
// Создаем массив уникальных временных меток
|
||||||
const allTimes = Object.values(chartData)
|
const allTimes = Object.values(chartData)
|
||||||
.flat()
|
.flat()
|
||||||
|
|
@ -21,39 +18,15 @@ const LineChartComponent = ({ chartData, metricName, colors, description, onRang
|
||||||
return point;
|
return point;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Используем отфильтрованные данные, если они есть
|
|
||||||
const displayData = filteredData || data;
|
|
||||||
|
|
||||||
// Обработчик клика на графике
|
|
||||||
const handleClick = (e) => {
|
|
||||||
if (!e || !e.activeLabel) return;
|
|
||||||
|
|
||||||
const clickedTime = e.activeLabel;
|
|
||||||
|
|
||||||
if (!selectionStart) {
|
|
||||||
setSelectionStart(clickedTime);
|
|
||||||
} else if (!selectionEnd) {
|
|
||||||
setSelectionEnd(clickedTime);
|
|
||||||
|
|
||||||
const startIndex = data.findIndex(point => point.time === selectionStart);
|
|
||||||
const endIndex = data.findIndex(point => point.time === clickedTime);
|
|
||||||
|
|
||||||
onRangeSelect({ startIndex, endIndex });
|
|
||||||
|
|
||||||
setSelectionStart(null);
|
|
||||||
setSelectionEnd(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Кастомный Tooltip для отображения значения
|
// Кастомный Tooltip для отображения значения
|
||||||
const CustomTooltip = ({ active, payload, label }) => {
|
const CustomTooltip = ({ active, payload, label }) => {
|
||||||
if (active && payload && payload.length) {
|
if (active && payload && payload.length) {
|
||||||
return (
|
return (
|
||||||
<div className="custom-tooltip" style={{ padding: '10px' }}>
|
<div className="custom-tooltip" style={{ padding: '10px' }}>
|
||||||
<p>{`Время: ${label}`}</p>
|
<p>{`Время: ${label}`}</p> {/* Время из label */}
|
||||||
{payload.map((entry, index) => (
|
{payload.map((entry, index) => (
|
||||||
<p key={index} style={{}}>
|
<p key={index} style={{}}>
|
||||||
{`Значение: ${entry.value}`}
|
{`Значение: ${entry.value}`} {/* Имя и значение из payload */}
|
||||||
</p>
|
</p>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,12 +38,13 @@ const LineChartComponent = ({ chartData, metricName, colors, description, onRang
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<h2>{description}</h2>
|
||||||
<ResponsiveContainer width="100%" height={400}>
|
<ResponsiveContainer width="100%" height={400}>
|
||||||
<LineChart data={displayData} onClick={handleClick}>
|
<LineChart data={data}>
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
<XAxis dataKey="time" />
|
<XAxis dataKey="time" />
|
||||||
<YAxis />
|
<YAxis />
|
||||||
<Tooltip content={<CustomTooltip />} />
|
<Tooltip content={<CustomTooltip />} /> {/* Подключаем кастомный Tooltip */}
|
||||||
<Legend />
|
<Legend />
|
||||||
{Object.keys(chartData).map((key, index) => (
|
{Object.keys(chartData).map((key, index) => (
|
||||||
<Line
|
<Line
|
||||||
|
|
@ -81,6 +55,13 @@ const LineChartComponent = ({ chartData, metricName, colors, description, onRang
|
||||||
name={key}
|
name={key}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
<Brush
|
||||||
|
dataKey="time"
|
||||||
|
height={30}
|
||||||
|
stroke="#8884d8"
|
||||||
|
startIndex={0} // Начальный индекс для Brush
|
||||||
|
endIndex={data.length - 1} // Конечный индекс для Brush (весь диапазон)
|
||||||
|
/>
|
||||||
</LineChart>
|
</LineChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ import DatePicker from 'react-datepicker';
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
import LineChartComponent from './Components/LineChartComponent';
|
import LineChartComponent from './Components/LineChartComponent';
|
||||||
|
|
||||||
const MAX_POINTS = 20;
|
const MAX_POINTS = 20; // Ограничение точек на графике
|
||||||
const COLORS = ['#3e95cd', '#8e5ea2', '#3cba9f', '#e8c3b9', '#c45850'];
|
const COLORS = ['#3e95cd', '#8e5ea2', '#3cba9f', '#e8c3b9', '#c45850']; // Фиксированные цвета для линий
|
||||||
|
|
||||||
|
// Список временных диапазонов и интервалов обновления
|
||||||
const TIME_RANGES = [
|
const TIME_RANGES = [
|
||||||
{ label: '1 минута', value: 60, interval: 3000 },
|
{ label: '1 минута', value: 60, interval: 3000 },
|
||||||
{ label: '5 минут', value: 300, interval: 15000 },
|
{ label: '5 минут', value: 300, interval: 15000 },
|
||||||
|
|
@ -26,12 +28,13 @@ const TIME_RANGES = [
|
||||||
|
|
||||||
const PrometheusChart = ({ metricName }) => {
|
const PrometheusChart = ({ metricName }) => {
|
||||||
const [chartData, setChartData] = useState({});
|
const [chartData, setChartData] = useState({});
|
||||||
const [selectedRange, setSelectedRange] = useState(TIME_RANGES[0]);
|
const [metricType, setMetricType] = useState('');
|
||||||
const [startDate, setStartDate] = useState(new Date());
|
const [metricDescription, setMetricDescription] = useState('');
|
||||||
const [endDate, setEndDate] = useState(new Date());
|
const [selectedRange, setSelectedRange] = useState(TIME_RANGES[0]); // По умолчанию 1 минута
|
||||||
const [useCustomRange, setUseCustomRange] = useState(false);
|
const [startDate, setStartDate] = useState(new Date()); // Начальная дата для кастомного диапазона
|
||||||
const [selectedGraphRange, setSelectedGraphRange] = useState(null); // Выбранный диапазон
|
const [endDate, setEndDate] = useState(new Date()); // Конечная дата для кастомного диапазона
|
||||||
const [filteredData, setFilteredData] = useState(null); // Отфильтрованные данные
|
const [useCustomRange, setUseCustomRange] = useState(false); // Флаг для выбора кастомного диапазона
|
||||||
|
const [brushRange, setBrushRange] = useState({ startIndex: 0, endIndex: 0 }); // Состояние Brush
|
||||||
const intervalRef = useRef(null);
|
const intervalRef = useRef(null);
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
|
|
@ -39,19 +42,24 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
let start, end;
|
let start, end;
|
||||||
|
|
||||||
if (useCustomRange) {
|
if (useCustomRange) {
|
||||||
|
// Используем кастомный диапазон
|
||||||
start = Math.floor(startDate.getTime() / 1000);
|
start = Math.floor(startDate.getTime() / 1000);
|
||||||
end = Math.floor(endDate.getTime() / 1000);
|
end = Math.floor(endDate.getTime() / 1000);
|
||||||
} else {
|
} else {
|
||||||
|
// Используем предустановленный диапазон
|
||||||
end = Math.floor(Date.now() / 1000);
|
end = Math.floor(Date.now() / 1000);
|
||||||
start = end - selectedRange.value;
|
start = end - selectedRange.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Динамический шаг (чем больше диапазон, тем больше шаг)
|
||||||
let step;
|
let step;
|
||||||
const range = end - start;
|
const range = end - start;
|
||||||
if (range <= 3600) step = 5;
|
if (range <= 3600) step = 5; // 1 час и меньше → 5 сек
|
||||||
else if (range <= 21600) step = 30;
|
else if (range <= 21600) step = 30; // 1-6 часов → 30 сек
|
||||||
else if (range <= 86400) step = 120;
|
else if (range <= 86400) step = 120; // 6-24 часа → 2 минуты
|
||||||
else step = 300;
|
else step = 300; // > 24 часов → 5 минут
|
||||||
|
|
||||||
|
console.log(`Запрашиваем данные с шагом ${step} сек`);
|
||||||
|
|
||||||
const response = await axios.get('http://192.168.2.39:3000/metrics', {
|
const response = await axios.get('http://192.168.2.39:3000/metrics', {
|
||||||
params: { metric: metricName, start, end, step },
|
params: { metric: metricName, start, end, step },
|
||||||
|
|
@ -60,10 +68,12 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
const result = response.data;
|
const result = response.data;
|
||||||
let metrics = Array.isArray(result) ? result : result.data || [];
|
let metrics = Array.isArray(result) ? result : result.data || [];
|
||||||
|
|
||||||
if (!Array.isArray(metrics)) {
|
if (!Array.isArray(metrics) || metrics.length === 0) {
|
||||||
|
console.warn('No metrics data available, filling with empty values.');
|
||||||
metrics = [];
|
metrics = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Генерация временных точек с учетом диапазона
|
||||||
const timePoints = [];
|
const timePoints = [];
|
||||||
for (let t = start; t <= end; t += step) {
|
for (let t = start; t <= end; t += step) {
|
||||||
const date = new Date(t * 1000);
|
const date = new Date(t * 1000);
|
||||||
|
|
@ -74,6 +84,7 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
timePoints.push(formattedTime);
|
timePoints.push(formattedTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Обработка данных
|
||||||
const updatedData = {};
|
const updatedData = {};
|
||||||
metrics.forEach(m => {
|
metrics.forEach(m => {
|
||||||
const date = new Date(m.timestamp);
|
const date = new Date(m.timestamp);
|
||||||
|
|
@ -86,6 +97,7 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
updatedData[key][formattedTime] = m.value;
|
updatedData[key][formattedTime] = m.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 3. Заполнение пропусков
|
||||||
const chartData = {};
|
const chartData = {};
|
||||||
Object.keys(updatedData).forEach(key => {
|
Object.keys(updatedData).forEach(key => {
|
||||||
chartData[key] = timePoints.map(time => ({
|
chartData[key] = timePoints.map(time => ({
|
||||||
|
|
@ -95,80 +107,44 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
setChartData(chartData);
|
setChartData(chartData);
|
||||||
|
|
||||||
|
// Устанавливаем Brush на весь диапазон
|
||||||
|
setBrushRange({
|
||||||
|
startIndex: 0,
|
||||||
|
endIndex: timePoints.length - 1,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка при загрузке метрик:', error);
|
console.error('Ошибка при загрузке метрик:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData();
|
fetchData(); // Первоначальная загрузка данных
|
||||||
|
|
||||||
intervalRef.current = setInterval(() => {
|
intervalRef.current = setInterval(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, selectedRange.interval);
|
}, selectedRange.interval); // Обновляем с выбранным интервалом
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (intervalRef.current) {
|
if (intervalRef.current) {
|
||||||
clearInterval(intervalRef.current);
|
clearInterval(intervalRef.current); // Очищаем интервал при размонтировании
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [metricName, selectedRange, useCustomRange, startDate, endDate]);
|
}, [metricName, selectedRange, useCustomRange, startDate, endDate]); // Зависимость от metricName, selectedRange, useCustomRange, startDate и endDate
|
||||||
|
|
||||||
const handleRangeChange = (event) => {
|
const handleRangeChange = (event) => {
|
||||||
const selectedValue = event.target.value;
|
const selectedValue = event.target.value;
|
||||||
const range = TIME_RANGES.find(range => range.value === parseInt(selectedValue, 10));
|
const range = TIME_RANGES.find(range => range.value === parseInt(selectedValue, 10));
|
||||||
setSelectedRange(range);
|
setSelectedRange(range);
|
||||||
setUseCustomRange(false);
|
setUseCustomRange(false); // Переключаемся на предустановленный диапазон
|
||||||
setSelectedGraphRange(null); // Сбрасываем выбранный диапазон
|
|
||||||
setFilteredData(null); // Сбрасываем отфильтрованные данные
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCustomRangeChange = () => {
|
const handleCustomRangeChange = () => {
|
||||||
setUseCustomRange(true);
|
setUseCustomRange(true); // Переключаемся на кастомный диапазон
|
||||||
setSelectedGraphRange(null); // Сбрасываем выбранный диапазон
|
|
||||||
setFilteredData(null); // Сбрасываем отфильтрованные данные
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedGraphRange) {
|
|
||||||
const { startIndex, endIndex } = selectedGraphRange;
|
|
||||||
const allTimes = Object.values(chartData)
|
|
||||||
.flat()
|
|
||||||
.map(point => point.time)
|
|
||||||
.filter((time, index, self) => self.indexOf(time) === index);
|
|
||||||
|
|
||||||
const data = allTimes.map(time => {
|
|
||||||
const point = { time };
|
|
||||||
Object.keys(chartData).forEach(key => {
|
|
||||||
const instanceData = chartData[key].find(p => p.time === time);
|
|
||||||
point[key] = instanceData ? instanceData.value : null;
|
|
||||||
});
|
|
||||||
return point;
|
|
||||||
});
|
|
||||||
|
|
||||||
const filtered = data.slice(startIndex, endIndex + 1);
|
|
||||||
setFilteredData(filtered); // Сохраняем отфильтрованные данные
|
|
||||||
} else {
|
|
||||||
setFilteredData(null); // Сбрасываем фильтрацию, если диапазон не выбран
|
|
||||||
}
|
|
||||||
}, [selectedGraphRange, chartData]);
|
|
||||||
|
|
||||||
if (!Object.keys(chartData).length) return <p>Loading...</p>;
|
if (!Object.keys(chartData).length) return <p>Loading...</p>;
|
||||||
|
|
||||||
const allTimes = Object.values(chartData)
|
|
||||||
.flat()
|
|
||||||
.map(point => point.time)
|
|
||||||
.filter((time, index, self) => self.indexOf(time) === index);
|
|
||||||
|
|
||||||
const data = allTimes.map(time => {
|
|
||||||
const point = { time };
|
|
||||||
Object.keys(chartData).forEach(key => {
|
|
||||||
const instanceData = chartData[key].find(p => p.time === time);
|
|
||||||
point[key] = instanceData ? instanceData.value : null;
|
|
||||||
});
|
|
||||||
return point;
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -208,10 +184,11 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
<LineChartComponent
|
<LineChartComponent
|
||||||
chartData={chartData}
|
chartData={chartData}
|
||||||
metricName={metricName}
|
metricName={metricName}
|
||||||
|
metricType={metricType}
|
||||||
colors={COLORS}
|
colors={COLORS}
|
||||||
description={metricName}
|
description={metricDescription}
|
||||||
onRangeSelect={setSelectedGraphRange}
|
brushRange={brushRange}
|
||||||
filteredData={filteredData}
|
onBrushChange={setBrushRange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ const Dashboard = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>Общий мониторинг</h2>
|
<h2>Общий мониторинг</h2>
|
||||||
|
<ErrorIndicator />
|
||||||
<TreeTable data={treeData.items} /> {/* Используем актуальные данные */}
|
<TreeTable data={treeData.items} /> {/* Используем актуальные данные */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue