mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
feat(dashboard): richer System History & Xray Metrics charts
- Collect disk read/write and network packet-rate metrics on the host sampler - Sparkline: optional 2nd/3rd overlaid series with a colored legend - System History: merge Bandwidth (up/down), Disk I/O (read/write) and Load (1m/5m/15m) into single multi-line tabs - Add a descriptive per-chart title and mobile-only tab icons to both modals - Localize every chart title and tab label across all 13 languages
This commit is contained in:
parent
a4dae566ce
commit
4b11c54206
21 changed files with 591 additions and 36 deletions
|
|
@ -32,3 +32,28 @@
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sparkline-legend {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
right: 8px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
background: color-mix(in srgb, var(--ant-color-bg-elevated) 88%, transparent);
|
||||||
|
border: 1px solid var(--ant-color-border-secondary);
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 16px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sparkline-legend .extrema-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,13 @@ const DEFAULT_MAX_COLOR = '#fa541c';
|
||||||
|
|
||||||
interface SparklineProps {
|
interface SparklineProps {
|
||||||
data: number[];
|
data: number[];
|
||||||
|
data2?: number[];
|
||||||
|
data3?: number[];
|
||||||
|
stroke2?: string;
|
||||||
|
stroke3?: string;
|
||||||
|
name1?: string;
|
||||||
|
name2?: string;
|
||||||
|
name3?: string;
|
||||||
labels?: (string | number)[];
|
labels?: (string | number)[];
|
||||||
height?: number;
|
height?: number;
|
||||||
stroke?: string;
|
stroke?: string;
|
||||||
|
|
@ -56,11 +63,20 @@ interface SparklineProps {
|
||||||
interface ChartPoint {
|
interface ChartPoint {
|
||||||
index: number;
|
index: number;
|
||||||
value: number;
|
value: number;
|
||||||
|
value2: number;
|
||||||
|
value3: number;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Sparkline({
|
export default function Sparkline({
|
||||||
data,
|
data,
|
||||||
|
data2 = [],
|
||||||
|
data3 = [],
|
||||||
|
stroke2 = '#722ed1',
|
||||||
|
stroke3 = '#a0d911',
|
||||||
|
name1,
|
||||||
|
name2,
|
||||||
|
name3,
|
||||||
labels = [],
|
labels = [],
|
||||||
height = 80,
|
height = 80,
|
||||||
stroke = '#008771',
|
stroke = '#008771',
|
||||||
|
|
@ -85,28 +101,39 @@ export default function Sparkline({
|
||||||
const reactId = useId();
|
const reactId = useId();
|
||||||
const safeId = reactId.replace(/[^a-zA-Z0-9]/g, '');
|
const safeId = reactId.replace(/[^a-zA-Z0-9]/g, '');
|
||||||
const gradId = `spkGrad-${safeId}`;
|
const gradId = `spkGrad-${safeId}`;
|
||||||
|
const gradId2 = `spkGrad2-${safeId}`;
|
||||||
|
const gradId3 = `spkGrad3-${safeId}`;
|
||||||
|
const hasSeries2 = data2.length > 0;
|
||||||
|
const hasSeries3 = data3.length > 0;
|
||||||
|
const multiSeries = hasSeries2 || hasSeries3;
|
||||||
|
|
||||||
const points = useMemo<ChartPoint[]>(() => {
|
const points = useMemo<ChartPoint[]>(() => {
|
||||||
const n = Math.min(data.length, maxPoints);
|
const n = Math.min(data.length, maxPoints);
|
||||||
if (n === 0) return [];
|
if (n === 0) return [];
|
||||||
const sliceStart = data.length - n;
|
const sliceStart = data.length - n;
|
||||||
const labelStart = Math.max(0, labels.length - n);
|
const labelStart = Math.max(0, labels.length - n);
|
||||||
|
const slice2Start = data2.length - n;
|
||||||
|
const slice3Start = data3.length - n;
|
||||||
return data.slice(sliceStart).map((value, i) => ({
|
return data.slice(sliceStart).map((value, i) => ({
|
||||||
index: i,
|
index: i,
|
||||||
value: Number(value) || 0,
|
value: Number(value) || 0,
|
||||||
|
value2: data2.length ? Number(data2[slice2Start + i]) || 0 : 0,
|
||||||
|
value3: data3.length ? Number(data3[slice3Start + i]) || 0 : 0,
|
||||||
label: String(labels[labelStart + i] ?? i + 1),
|
label: String(labels[labelStart + i] ?? i + 1),
|
||||||
}));
|
}));
|
||||||
}, [data, labels, maxPoints]);
|
}, [data, data2, data3, labels, maxPoints]);
|
||||||
|
|
||||||
const yDomain = useMemo<[number, number]>(() => {
|
const yDomain = useMemo<[number, number]>(() => {
|
||||||
if (valueMax != null) return [valueMin, valueMax];
|
if (valueMax != null) return [valueMin, valueMax];
|
||||||
let max = valueMin;
|
let max = valueMin;
|
||||||
for (const p of points) {
|
for (const p of points) {
|
||||||
if (Number.isFinite(p.value) && p.value > max) max = p.value;
|
if (Number.isFinite(p.value) && p.value > max) max = p.value;
|
||||||
|
if (hasSeries2 && Number.isFinite(p.value2) && p.value2 > max) max = p.value2;
|
||||||
|
if (hasSeries3 && Number.isFinite(p.value3) && p.value3 > max) max = p.value3;
|
||||||
}
|
}
|
||||||
if (max <= valueMin) max = valueMin + 1;
|
if (max <= valueMin) max = valueMin + 1;
|
||||||
return [valueMin, max * 1.1];
|
return [valueMin, max * 1.1];
|
||||||
}, [points, valueMin, valueMax]);
|
}, [points, valueMin, valueMax, hasSeries2, hasSeries3]);
|
||||||
|
|
||||||
const yTicks = useMemo(() => {
|
const yTicks = useMemo(() => {
|
||||||
if (!showAxes) return undefined;
|
if (!showAxes) return undefined;
|
||||||
|
|
@ -129,7 +156,7 @@ export default function Sparkline({
|
||||||
const fmtTooltip = tooltipFormatter ?? yFormatter;
|
const fmtTooltip = tooltipFormatter ?? yFormatter;
|
||||||
|
|
||||||
const extremaPoints = useMemo(() => {
|
const extremaPoints = useMemo(() => {
|
||||||
if (!extrema?.show || points.length < 2) return null;
|
if (!extrema?.show || multiSeries || points.length < 2) return null;
|
||||||
let minIdx = 0;
|
let minIdx = 0;
|
||||||
let maxIdx = 0;
|
let maxIdx = 0;
|
||||||
for (let i = 1; i < points.length; i++) {
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
|
@ -138,7 +165,17 @@ export default function Sparkline({
|
||||||
}
|
}
|
||||||
if (minIdx === maxIdx) return null;
|
if (minIdx === maxIdx) return null;
|
||||||
return { min: points[minIdx], max: points[maxIdx], minIdx, maxIdx };
|
return { min: points[minIdx], max: points[maxIdx], minIdx, maxIdx };
|
||||||
}, [points, extrema?.show]);
|
}, [points, extrema?.show, multiSeries]);
|
||||||
|
|
||||||
|
const legendItems = useMemo(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
|
{ name: name1, color: stroke },
|
||||||
|
{ name: name2, color: stroke2 },
|
||||||
|
{ name: name3, color: stroke3 },
|
||||||
|
].filter((s, i) => s.name && (i === 0 ? multiSeries : i === 1 ? hasSeries2 : hasSeries3)),
|
||||||
|
[name1, name2, name3, stroke, stroke2, stroke3, multiSeries, hasSeries2, hasSeries3],
|
||||||
|
);
|
||||||
|
|
||||||
const fmtExtrema = extrema?.formatter ?? yFormatter;
|
const fmtExtrema = extrema?.formatter ?? yFormatter;
|
||||||
const minColor = extrema?.minColor ?? DEFAULT_MIN_COLOR;
|
const minColor = extrema?.minColor ?? DEFAULT_MIN_COLOR;
|
||||||
|
|
@ -156,6 +193,13 @@ export default function Sparkline({
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{legendItems.length > 0 && (
|
||||||
|
<div className="sparkline-legend" aria-hidden="true">
|
||||||
|
{legendItems.map((s) => (
|
||||||
|
<span key={s.name} className="extrema-item" style={{ color: s.color }}>● {s.name}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<ResponsiveContainer width="100%" height={height} className="sparkline-svg">
|
<ResponsiveContainer width="100%" height={height} className="sparkline-svg">
|
||||||
<AreaChart
|
<AreaChart
|
||||||
data={points}
|
data={points}
|
||||||
|
|
@ -171,6 +215,14 @@ export default function Sparkline({
|
||||||
<stop offset="0%" stopColor={stroke} stopOpacity={fillOpacity} />
|
<stop offset="0%" stopColor={stroke} stopOpacity={fillOpacity} />
|
||||||
<stop offset="100%" stopColor={stroke} stopOpacity={0} />
|
<stop offset="100%" stopColor={stroke} stopOpacity={0} />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
|
<linearGradient id={gradId2} x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0%" stopColor={stroke2} stopOpacity={fillOpacity} />
|
||||||
|
<stop offset="100%" stopColor={stroke2} stopOpacity={0} />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id={gradId3} x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0%" stopColor={stroke3} stopOpacity={fillOpacity} />
|
||||||
|
<stop offset="100%" stopColor={stroke3} stopOpacity={0} />
|
||||||
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
{showGrid && (
|
{showGrid && (
|
||||||
<CartesianGrid stroke="rgba(128, 128, 140, 0.35)" strokeDasharray="3 4" vertical={false} />
|
<CartesianGrid stroke="rgba(128, 128, 140, 0.35)" strokeDasharray="3 4" vertical={false} />
|
||||||
|
|
@ -209,9 +261,9 @@ export default function Sparkline({
|
||||||
}}
|
}}
|
||||||
labelStyle={{ color: 'var(--ant-color-text-tertiary)', marginBottom: 4, fontSize: 11 }}
|
labelStyle={{ color: 'var(--ant-color-text-tertiary)', marginBottom: 4, fontSize: 11 }}
|
||||||
itemStyle={{ color: 'var(--ant-color-text)', padding: 0, fontWeight: 500 }}
|
itemStyle={{ color: 'var(--ant-color-text)', padding: 0, fontWeight: 500 }}
|
||||||
formatter={(v) => [fmtTooltip(Number(v) || 0), '']}
|
formatter={(v, name) => [fmtTooltip(Number(v) || 0), multiSeries && typeof name === 'string' ? name : '']}
|
||||||
labelFormatter={(label) => (tooltipLabelFormatter ? tooltipLabelFormatter(String(label)) : String(label))}
|
labelFormatter={(label) => (tooltipLabelFormatter ? tooltipLabelFormatter(String(label)) : String(label))}
|
||||||
separator=""
|
separator={multiSeries ? ': ' : ''}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{referenceLines?.map((rl, idx) => (
|
{referenceLines?.map((rl, idx) => (
|
||||||
|
|
@ -256,6 +308,7 @@ export default function Sparkline({
|
||||||
<Area
|
<Area
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey="value"
|
dataKey="value"
|
||||||
|
name={multiSeries ? name1 : undefined}
|
||||||
stroke={stroke}
|
stroke={stroke}
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
fill={`url(#${gradId})`}
|
fill={`url(#${gradId})`}
|
||||||
|
|
@ -263,6 +316,32 @@ export default function Sparkline({
|
||||||
activeDot={showMarker ? { r: markerRadius, fill: stroke, strokeWidth: 0 } : false}
|
activeDot={showMarker ? { r: markerRadius, fill: stroke, strokeWidth: 0 } : false}
|
||||||
isAnimationActive={false}
|
isAnimationActive={false}
|
||||||
/>
|
/>
|
||||||
|
{hasSeries2 && (
|
||||||
|
<Area
|
||||||
|
type="monotone"
|
||||||
|
dataKey="value2"
|
||||||
|
name={name2}
|
||||||
|
stroke={stroke2}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
fill={`url(#${gradId2})`}
|
||||||
|
dot={false}
|
||||||
|
activeDot={showMarker ? { r: markerRadius, fill: stroke2, strokeWidth: 0 } : false}
|
||||||
|
isAnimationActive={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{hasSeries3 && (
|
||||||
|
<Area
|
||||||
|
type="monotone"
|
||||||
|
dataKey="value3"
|
||||||
|
name={name3}
|
||||||
|
stroke={stroke3}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
fill={`url(#${gradId3})`}
|
||||||
|
dot={false}
|
||||||
|
activeDot={showMarker ? { r: markerRadius, fill: stroke3, strokeWidth: 0 } : false}
|
||||||
|
isAnimationActive={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import {
|
||||||
|
|
||||||
import { HttpUtil } from '@/utils';
|
import { HttpUtil } from '@/utils';
|
||||||
import { pauseAnimationsUntilLeave, useTheme } from '@/hooks/useTheme';
|
import { pauseAnimationsUntilLeave, useTheme } from '@/hooks/useTheme';
|
||||||
|
import { useAllSettings } from '@/api/queries/useAllSettings';
|
||||||
import './AppSidebar.css';
|
import './AppSidebar.css';
|
||||||
|
|
||||||
const SIDEBAR_COLLAPSED_KEY = 'isSidebarCollapsed';
|
const SIDEBAR_COLLAPSED_KEY = 'isSidebarCollapsed';
|
||||||
|
|
@ -121,6 +122,8 @@ export default function AppSidebar() {
|
||||||
const { isDark, isUltra, toggleTheme, toggleUltra } = useTheme();
|
const { isDark, isUltra, toggleTheme, toggleUltra } = useTheme();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { pathname, hash } = useLocation();
|
const { pathname, hash } = useLocation();
|
||||||
|
const { allSetting } = useAllSettings();
|
||||||
|
const showSubFormats = !!(allSetting.subJsonEnable || allSetting.subClashEnable);
|
||||||
|
|
||||||
const [collapsed, setCollapsed] = useState<boolean>(() => readCollapsed());
|
const [collapsed, setCollapsed] = useState<boolean>(() => readCollapsed());
|
||||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||||
|
|
@ -143,13 +146,18 @@ export default function AppSidebar() {
|
||||||
const navItems = useMemo(() => tabs.filter((tab) => tab.icon !== 'logout'), [tabs]);
|
const navItems = useMemo(() => tabs.filter((tab) => tab.icon !== 'logout'), [tabs]);
|
||||||
const utilItems = useMemo(() => tabs.filter((tab) => tab.icon === 'logout'), [tabs]);
|
const utilItems = useMemo(() => tabs.filter((tab) => tab.icon === 'logout'), [tabs]);
|
||||||
|
|
||||||
const settingsChildren = useMemo<NonNullable<MenuProps['items']>>(() => [
|
const settingsChildren = useMemo<NonNullable<MenuProps['items']>>(() => {
|
||||||
{ key: '/settings#general', icon: <SettingOutlined />, label: t('pages.settings.panelSettings') },
|
const children: NonNullable<MenuProps['items']> = [
|
||||||
{ key: '/settings#security', icon: <SafetyOutlined />, label: t('pages.settings.securitySettings') },
|
{ key: '/settings#general', icon: <SettingOutlined />, label: t('pages.settings.panelSettings') },
|
||||||
{ key: '/settings#telegram', icon: <MessageOutlined />, label: t('pages.settings.TGBotSettings') },
|
{ key: '/settings#security', icon: <SafetyOutlined />, label: t('pages.settings.securitySettings') },
|
||||||
{ key: '/settings#subscription', icon: <CloudServerOutlined />, label: t('pages.settings.subSettings') },
|
{ key: '/settings#telegram', icon: <MessageOutlined />, label: t('pages.settings.TGBotSettings') },
|
||||||
{ key: '/settings#subscription-formats', icon: <CodeOutlined />, label: 'Sub Formats' },
|
{ key: '/settings#subscription', icon: <CloudServerOutlined />, label: t('pages.settings.subSettings') },
|
||||||
], [t]);
|
];
|
||||||
|
if (showSubFormats) {
|
||||||
|
children.push({ key: '/settings#subscription-formats', icon: <CodeOutlined />, label: 'Sub Formats' });
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}, [t, showSubFormats]);
|
||||||
|
|
||||||
const xrayChildren = useMemo<NonNullable<MenuProps['items']>>(() => [
|
const xrayChildren = useMemo<NonNullable<MenuProps['items']>>(() => [
|
||||||
{ key: '/xray#basic', icon: <SettingOutlined />, label: t('pages.xray.basicTemplate') },
|
{ key: '/xray#basic', icon: <SettingOutlined />, label: t('pages.xray.basicTemplate') },
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,13 @@
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.history-chart-title {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
}
|
||||||
|
|
||||||
.cpu-chart-wrap {
|
.cpu-chart-wrap {
|
||||||
margin: 8px 8px 16px;
|
margin: 8px 8px 16px;
|
||||||
padding: 16px 18px 18px;
|
padding: 16px 18px 18px;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,16 @@
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Modal, Select, Tabs } from 'antd';
|
import { Modal, Select, Tabs } from 'antd';
|
||||||
|
import {
|
||||||
|
DashboardOutlined,
|
||||||
|
DatabaseOutlined,
|
||||||
|
DeploymentUnitOutlined,
|
||||||
|
GlobalOutlined,
|
||||||
|
HddOutlined,
|
||||||
|
LineChartOutlined,
|
||||||
|
TeamOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
import { HttpUtil, SizeFormatter } from '@/utils';
|
import { HttpUtil, SizeFormatter } from '@/utils';
|
||||||
import { Sparkline } from '@/components/viz';
|
import { Sparkline } from '@/components/viz';
|
||||||
|
|
@ -17,26 +27,38 @@ interface SystemHistoryModalProps {
|
||||||
interface MetricDef {
|
interface MetricDef {
|
||||||
key: string;
|
key: string;
|
||||||
tab: string;
|
tab: string;
|
||||||
|
tabKey?: string;
|
||||||
|
title: string;
|
||||||
|
icon: ReactNode;
|
||||||
valueMax: number | null;
|
valueMax: number | null;
|
||||||
unit: string;
|
unit: string;
|
||||||
stroke: string;
|
stroke: string;
|
||||||
|
key2?: string;
|
||||||
|
stroke2?: string;
|
||||||
|
name1?: string;
|
||||||
|
name2?: string;
|
||||||
|
key3?: string;
|
||||||
|
stroke3?: string;
|
||||||
|
name3?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const METRICS: MetricDef[] = [
|
const METRICS: MetricDef[] = [
|
||||||
{ key: 'cpu', tab: 'CPU', valueMax: 100, unit: '%', stroke: '' },
|
{ key: 'cpu', tab: 'CPU', title: 'pages.index.historyTitleCpu', icon: <DashboardOutlined />, valueMax: 100, unit: '%', stroke: '' },
|
||||||
{ key: 'mem', tab: 'RAM', valueMax: 100, unit: '%', stroke: '#7c4dff' },
|
{ key: 'mem', tab: 'RAM', title: 'pages.index.historyTitleMem', icon: <DatabaseOutlined />, valueMax: 100, unit: '%', stroke: '#7c4dff' },
|
||||||
{ key: 'netUp', tab: 'Net Up', valueMax: null, unit: 'B/s', stroke: '#1890ff' },
|
{ key: 'netUp', tab: 'Bandwidth', tabKey: 'pages.index.historyTabBandwidth', title: 'pages.index.historyTitleNetwork', icon: <GlobalOutlined />, valueMax: null, unit: 'B/s', stroke: '#1890ff', key2: 'netDown', stroke2: '#13c2c2', name1: 'Up', name2: 'Down' },
|
||||||
{ key: 'netDown', tab: 'Net Down', valueMax: null, unit: 'B/s', stroke: '#13c2c2' },
|
{ key: 'pktUp', tab: 'Packets', tabKey: 'pages.index.historyTabPackets', title: 'pages.index.historyTitlePackets', icon: <DeploymentUnitOutlined />, valueMax: null, unit: 'pkt/s', stroke: '#2f54eb', key2: 'pktDown', stroke2: '#36cfc9', name1: 'Up', name2: 'Down' },
|
||||||
{ key: 'online', tab: 'Online', valueMax: null, unit: '', stroke: '#52c41a' },
|
{ key: 'diskRead', tab: 'Disk I/O', tabKey: 'pages.index.historyTabDisk', title: 'pages.index.historyTitleDisk', icon: <HddOutlined />, valueMax: null, unit: 'B/s', stroke: '#eb2f96', key2: 'diskWrite', stroke2: '#722ed1', name1: 'Read', name2: 'Write' },
|
||||||
{ key: 'load1', tab: 'Load 1m', valueMax: null, unit: '', stroke: '#fa8c16' },
|
{ key: 'online', tab: 'Online', tabKey: 'pages.index.historyTabOnline', title: 'pages.index.historyTitleOnline', icon: <TeamOutlined />, valueMax: null, unit: '', stroke: '#52c41a' },
|
||||||
{ key: 'load5', tab: 'Load 5m', valueMax: null, unit: '', stroke: '#f5222d' },
|
{ key: 'load1', tab: 'Load', tabKey: 'pages.index.historyTabLoad', title: 'pages.index.historyTitleLoad', icon: <LineChartOutlined />, valueMax: null, unit: '', stroke: '#fa8c16', key2: 'load5', stroke2: '#f5222d', name1: '1m', name2: '5m', key3: 'load15', stroke3: '#a0d911', name3: '15m' },
|
||||||
{ key: 'load15', tab: 'Load 15m', valueMax: null, unit: '', stroke: '#a0d911' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function unitFormatter(unit: string, activeKey: string): (v: number) => string {
|
function unitFormatter(unit: string, activeKey: string): (v: number) => string {
|
||||||
if (unit === 'B/s') {
|
if (unit === 'B/s') {
|
||||||
return (v) => `${SizeFormatter.sizeFormat(Math.max(0, Number(v) || 0)).replace(/\.\d+/, '')}/s`;
|
return (v) => `${SizeFormatter.sizeFormat(Math.max(0, Number(v) || 0)).replace(/\.\d+/, '')}/s`;
|
||||||
}
|
}
|
||||||
|
if (unit === 'pkt/s') {
|
||||||
|
return (v) => `${Math.round(Math.max(0, Number(v) || 0)).toLocaleString()}/s`;
|
||||||
|
}
|
||||||
if (unit === '%') {
|
if (unit === '%') {
|
||||||
return (v) => `${Number(v).toFixed(1)}%`;
|
return (v) => `${Number(v).toFixed(1)}%`;
|
||||||
}
|
}
|
||||||
|
|
@ -69,6 +91,8 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
|
||||||
const [activeKey, setActiveKey] = useState('cpu');
|
const [activeKey, setActiveKey] = useState('cpu');
|
||||||
const [bucket, setBucket] = useState(2);
|
const [bucket, setBucket] = useState(2);
|
||||||
const [points, setPoints] = useState<number[]>([]);
|
const [points, setPoints] = useState<number[]>([]);
|
||||||
|
const [points2, setPoints2] = useState<number[]>([]);
|
||||||
|
const [points3, setPoints3] = useState<number[]>([]);
|
||||||
const [labels, setLabels] = useState<string[]>([]);
|
const [labels, setLabels] = useState<string[]>([]);
|
||||||
const [timestamps, setTimestamps] = useState<number[]>([]);
|
const [timestamps, setTimestamps] = useState<number[]>([]);
|
||||||
|
|
||||||
|
|
@ -116,15 +140,32 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
|
||||||
setLabels(labs);
|
setLabels(labs);
|
||||||
setPoints(vals);
|
setPoints(vals);
|
||||||
setTimestamps(tss);
|
setTimestamps(tss);
|
||||||
|
|
||||||
|
const fetchAligned = async (key?: string): Promise<number[]> => {
|
||||||
|
if (!key) return [];
|
||||||
|
const m = await HttpUtil.get(`/panel/api/server/history/${key}/${bucket}`);
|
||||||
|
if (m?.success && Array.isArray(m.obj)) {
|
||||||
|
const byTs = new Map<number, number>();
|
||||||
|
for (const p of m.obj) byTs.set(Number(p.t) || 0, Number(p.v) || 0);
|
||||||
|
return tss.map((ts) => byTs.get(ts) ?? 0);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
setPoints2(await fetchAligned(activeMetric.key2));
|
||||||
|
setPoints3(await fetchAligned(activeMetric.key3));
|
||||||
} else {
|
} else {
|
||||||
setLabels([]);
|
setLabels([]);
|
||||||
setPoints([]);
|
setPoints([]);
|
||||||
|
setPoints2([]);
|
||||||
|
setPoints3([]);
|
||||||
setTimestamps([]);
|
setTimestamps([]);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to fetch history bucket', e);
|
console.error('Failed to fetch history bucket', e);
|
||||||
setLabels([]);
|
setLabels([]);
|
||||||
setPoints([]);
|
setPoints([]);
|
||||||
|
setPoints2([]);
|
||||||
|
setPoints3([]);
|
||||||
setTimestamps([]);
|
setTimestamps([]);
|
||||||
}
|
}
|
||||||
}, [activeMetric, bucket]);
|
}, [activeMetric, bucket]);
|
||||||
|
|
@ -168,12 +209,26 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
|
||||||
onChange={setActiveKey}
|
onChange={setActiveKey}
|
||||||
size="small"
|
size="small"
|
||||||
className="history-tabs"
|
className="history-tabs"
|
||||||
items={METRICS.map((m) => ({ key: m.key, label: m.tab }))}
|
items={METRICS.map((m) => {
|
||||||
|
const tabLabel = m.tabKey ? t(m.tabKey) : m.tab;
|
||||||
|
return {
|
||||||
|
key: m.key,
|
||||||
|
label: isMobile ? <span title={tabLabel} aria-label={tabLabel}>{m.icon}</span> : tabLabel,
|
||||||
|
};
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="cpu-chart-wrap">
|
<div className="cpu-chart-wrap">
|
||||||
|
{activeMetric?.title && <div className="history-chart-title">{t(activeMetric.title)}</div>}
|
||||||
<Sparkline
|
<Sparkline
|
||||||
data={points}
|
data={points}
|
||||||
|
data2={activeMetric?.key2 ? points2 : undefined}
|
||||||
|
data3={activeMetric?.key3 ? points3 : undefined}
|
||||||
|
stroke2={activeMetric?.stroke2}
|
||||||
|
stroke3={activeMetric?.stroke3}
|
||||||
|
name1={activeMetric?.name1}
|
||||||
|
name2={activeMetric?.name2}
|
||||||
|
name3={activeMetric?.name3}
|
||||||
labels={labels}
|
labels={labels}
|
||||||
height={260}
|
height={260}
|
||||||
stroke={strokeColor}
|
stroke={strokeColor}
|
||||||
|
|
@ -189,7 +244,7 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
|
||||||
valueMax={activeMetric?.valueMax ?? null}
|
valueMax={activeMetric?.valueMax ?? null}
|
||||||
yFormatter={yFormatter}
|
yFormatter={yFormatter}
|
||||||
tooltipLabelFormatter={tooltipLabelFormatter}
|
tooltipLabelFormatter={tooltipLabelFormatter}
|
||||||
extrema={{ show: true, formatter: yFormatter }}
|
extrema={{ show: !activeMetric?.key2, formatter: yFormatter }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Alert, Modal, Select, Tabs, Tag } from 'antd';
|
import { Alert, Modal, Select, Tabs, Tag } from 'antd';
|
||||||
|
import {
|
||||||
|
BlockOutlined,
|
||||||
|
CloudServerOutlined,
|
||||||
|
DatabaseOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
EyeOutlined,
|
||||||
|
PauseCircleOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
import { HttpUtil, Msg, SizeFormatter } from '@/utils';
|
import { HttpUtil, Msg, SizeFormatter } from '@/utils';
|
||||||
import { Sparkline } from '@/components/viz';
|
import { Sparkline } from '@/components/viz';
|
||||||
|
|
@ -17,6 +26,9 @@ interface XrayMetricsModalProps {
|
||||||
interface MetricDef {
|
interface MetricDef {
|
||||||
key: string;
|
key: string;
|
||||||
tab: string;
|
tab: string;
|
||||||
|
tabKey: string;
|
||||||
|
title: string;
|
||||||
|
icon: ReactNode;
|
||||||
unit: 'B' | 'ns' | 'ms' | '';
|
unit: 'B' | 'ns' | 'ms' | '';
|
||||||
stroke: string;
|
stroke: string;
|
||||||
}
|
}
|
||||||
|
|
@ -36,12 +48,12 @@ interface ObservatoryTag {
|
||||||
}
|
}
|
||||||
|
|
||||||
const METRICS: MetricDef[] = [
|
const METRICS: MetricDef[] = [
|
||||||
{ key: 'xrAlloc', tab: 'Heap', unit: 'B', stroke: '#7c4dff' },
|
{ key: 'xrAlloc', tab: 'Heap', tabKey: 'pages.index.xrayTabHeap', title: 'pages.index.xrayTitleHeap', icon: <DatabaseOutlined />, unit: 'B', stroke: '#7c4dff' },
|
||||||
{ key: 'xrSys', tab: 'Sys', unit: 'B', stroke: '#1890ff' },
|
{ key: 'xrSys', tab: 'Sys', tabKey: 'pages.index.xrayTabSys', title: 'pages.index.xrayTitleSys', icon: <CloudServerOutlined />, unit: 'B', stroke: '#1890ff' },
|
||||||
{ key: 'xrHeapObjects', tab: 'Objects', unit: '', stroke: '#13c2c2' },
|
{ key: 'xrHeapObjects', tab: 'Objects', tabKey: 'pages.index.xrayTabObjects', title: 'pages.index.xrayTitleObjects', icon: <BlockOutlined />, unit: '', stroke: '#13c2c2' },
|
||||||
{ key: 'xrNumGC', tab: 'GC Count', unit: '', stroke: '#fa8c16' },
|
{ key: 'xrNumGC', tab: 'GC Count', tabKey: 'pages.index.xrayTabGcCount', title: 'pages.index.xrayTitleGcCount', icon: <DeleteOutlined />, unit: '', stroke: '#fa8c16' },
|
||||||
{ key: 'xrPauseNs', tab: 'GC Pause', unit: 'ns', stroke: '#f5222d' },
|
{ key: 'xrPauseNs', tab: 'GC Pause', tabKey: 'pages.index.xrayTabGcPause', title: 'pages.index.xrayTitleGcPause', icon: <PauseCircleOutlined />, unit: 'ns', stroke: '#f5222d' },
|
||||||
{ key: OBS_KEY, tab: 'Observatory', unit: 'ms', stroke: '#52c41a' },
|
{ key: OBS_KEY, tab: 'Observatory', tabKey: 'pages.index.xrayTabObservatory', title: 'pages.index.xrayTitleObservatory', icon: <EyeOutlined />, unit: 'ms', stroke: '#52c41a' },
|
||||||
];
|
];
|
||||||
|
|
||||||
function unitFormatter(unit: string): (v: number) => string {
|
function unitFormatter(unit: string): (v: number) => string {
|
||||||
|
|
@ -299,7 +311,13 @@ export default function XrayMetricsModal({ open, onClose }: XrayMetricsModalProp
|
||||||
onChange={setActiveKey}
|
onChange={setActiveKey}
|
||||||
size="small"
|
size="small"
|
||||||
className="history-tabs"
|
className="history-tabs"
|
||||||
items={METRICS.map((m) => ({ key: m.key, label: m.tab }))}
|
items={METRICS.map((m) => {
|
||||||
|
const tabLabel = m.tabKey ? t(m.tabKey) : m.tab;
|
||||||
|
return {
|
||||||
|
key: m.key,
|
||||||
|
label: isMobile ? <span title={tabLabel} aria-label={tabLabel}>{m.icon}</span> : tabLabel,
|
||||||
|
};
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isObservatory && (
|
{isObservatory && (
|
||||||
|
|
@ -353,6 +371,7 @@ export default function XrayMetricsModal({ open, onClose }: XrayMetricsModalProp
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="cpu-chart-wrap">
|
<div className="cpu-chart-wrap">
|
||||||
|
{activeMetric?.title && <div className="history-chart-title">{t(activeMetric.title)}</div>}
|
||||||
<Sparkline
|
<Sparkline
|
||||||
data={points}
|
data={points}
|
||||||
labels={labels}
|
labels={labels}
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ var (
|
||||||
// status sample. Exposed for documentation/test purposes; the
|
// status sample. Exposed for documentation/test purposes; the
|
||||||
// controller validates incoming names against an allow-list.
|
// controller validates incoming names against an allow-list.
|
||||||
var SystemMetricKeys = []string{
|
var SystemMetricKeys = []string{
|
||||||
"cpu", "mem", "netUp", "netDown", "online", "load1", "load5", "load15",
|
"cpu", "mem", "netUp", "netDown", "pktUp", "pktDown", "diskRead", "diskWrite", "online", "load1", "load5", "load15",
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeMetricKeys lists the per-node metric names NodeHeartbeatJob writes.
|
// NodeMetricKeys lists the per-node metric names NodeHeartbeatJob writes.
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,14 @@ type Status struct {
|
||||||
Current uint64 `json:"current"`
|
Current uint64 `json:"current"`
|
||||||
Total uint64 `json:"total"`
|
Total uint64 `json:"total"`
|
||||||
} `json:"disk"`
|
} `json:"disk"`
|
||||||
|
DiskIO struct {
|
||||||
|
Read uint64 `json:"read"`
|
||||||
|
Write uint64 `json:"write"`
|
||||||
|
} `json:"diskIO"`
|
||||||
|
DiskTraffic struct {
|
||||||
|
Read uint64 `json:"read"`
|
||||||
|
Write uint64 `json:"write"`
|
||||||
|
} `json:"diskTraffic"`
|
||||||
Xray struct {
|
Xray struct {
|
||||||
State ProcessState `json:"state"`
|
State ProcessState `json:"state"`
|
||||||
ErrorMsg string `json:"errorMsg"`
|
ErrorMsg string `json:"errorMsg"`
|
||||||
|
|
@ -78,12 +86,16 @@ type Status struct {
|
||||||
TcpCount int `json:"tcpCount"`
|
TcpCount int `json:"tcpCount"`
|
||||||
UdpCount int `json:"udpCount"`
|
UdpCount int `json:"udpCount"`
|
||||||
NetIO struct {
|
NetIO struct {
|
||||||
Up uint64 `json:"up"`
|
Up uint64 `json:"up"`
|
||||||
Down uint64 `json:"down"`
|
Down uint64 `json:"down"`
|
||||||
|
PktUp uint64 `json:"pktUp"`
|
||||||
|
PktDown uint64 `json:"pktDown"`
|
||||||
} `json:"netIO"`
|
} `json:"netIO"`
|
||||||
NetTraffic struct {
|
NetTraffic struct {
|
||||||
Sent uint64 `json:"sent"`
|
Sent uint64 `json:"sent"`
|
||||||
Recv uint64 `json:"recv"`
|
Recv uint64 `json:"recv"`
|
||||||
|
PktSent uint64 `json:"pktSent"`
|
||||||
|
PktRecv uint64 `json:"pktRecv"`
|
||||||
} `json:"netTraffic"`
|
} `json:"netTraffic"`
|
||||||
PublicIP struct {
|
PublicIP struct {
|
||||||
IPv4 string `json:"ipv4"`
|
IPv4 string `json:"ipv4"`
|
||||||
|
|
@ -383,6 +395,30 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
||||||
status.Disk.Total = diskInfo.Total
|
status.Disk.Total = diskInfo.Total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diskIOStats, err := disk.IOCounters()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warning("get disk io counters failed:", err)
|
||||||
|
} else {
|
||||||
|
var totalRead, totalWrite uint64
|
||||||
|
for _, counter := range diskIOStats {
|
||||||
|
totalRead += counter.ReadBytes
|
||||||
|
totalWrite += counter.WriteBytes
|
||||||
|
}
|
||||||
|
status.DiskTraffic.Read = totalRead
|
||||||
|
status.DiskTraffic.Write = totalWrite
|
||||||
|
|
||||||
|
if lastStatus != nil {
|
||||||
|
duration := now.Sub(lastStatus.T)
|
||||||
|
seconds := float64(duration) / float64(time.Second)
|
||||||
|
if seconds > 0 && status.DiskTraffic.Read >= lastStatus.DiskTraffic.Read {
|
||||||
|
status.DiskIO.Read = uint64(float64(status.DiskTraffic.Read-lastStatus.DiskTraffic.Read) / seconds)
|
||||||
|
}
|
||||||
|
if seconds > 0 && status.DiskTraffic.Write >= lastStatus.DiskTraffic.Write {
|
||||||
|
status.DiskIO.Write = uint64(float64(status.DiskTraffic.Write-lastStatus.DiskTraffic.Write) / seconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load averages
|
// Load averages
|
||||||
avgState, err := load.Avg()
|
avgState, err := load.Avg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -396,7 +432,7 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("get io counters failed:", err)
|
logger.Warning("get io counters failed:", err)
|
||||||
} else {
|
} else {
|
||||||
var totalSent, totalRecv uint64
|
var totalSent, totalRecv, totalPktSent, totalPktRecv uint64
|
||||||
for _, iface := range ioStats {
|
for _, iface := range ioStats {
|
||||||
name := strings.ToLower(iface.Name)
|
name := strings.ToLower(iface.Name)
|
||||||
if isVirtualInterface(name) {
|
if isVirtualInterface(name) {
|
||||||
|
|
@ -404,9 +440,13 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
||||||
}
|
}
|
||||||
totalSent += iface.BytesSent
|
totalSent += iface.BytesSent
|
||||||
totalRecv += iface.BytesRecv
|
totalRecv += iface.BytesRecv
|
||||||
|
totalPktSent += iface.PacketsSent
|
||||||
|
totalPktRecv += iface.PacketsRecv
|
||||||
}
|
}
|
||||||
status.NetTraffic.Sent = totalSent
|
status.NetTraffic.Sent = totalSent
|
||||||
status.NetTraffic.Recv = totalRecv
|
status.NetTraffic.Recv = totalRecv
|
||||||
|
status.NetTraffic.PktSent = totalPktSent
|
||||||
|
status.NetTraffic.PktRecv = totalPktRecv
|
||||||
|
|
||||||
if lastStatus != nil {
|
if lastStatus != nil {
|
||||||
duration := now.Sub(lastStatus.T)
|
duration := now.Sub(lastStatus.T)
|
||||||
|
|
@ -415,6 +455,12 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
||||||
down := uint64(float64(status.NetTraffic.Recv-lastStatus.NetTraffic.Recv) / seconds)
|
down := uint64(float64(status.NetTraffic.Recv-lastStatus.NetTraffic.Recv) / seconds)
|
||||||
status.NetIO.Up = up
|
status.NetIO.Up = up
|
||||||
status.NetIO.Down = down
|
status.NetIO.Down = down
|
||||||
|
if seconds > 0 && status.NetTraffic.PktSent >= lastStatus.NetTraffic.PktSent {
|
||||||
|
status.NetIO.PktUp = uint64(float64(status.NetTraffic.PktSent-lastStatus.NetTraffic.PktSent) / seconds)
|
||||||
|
}
|
||||||
|
if seconds > 0 && status.NetTraffic.PktRecv >= lastStatus.NetTraffic.PktRecv {
|
||||||
|
status.NetIO.PktDown = uint64(float64(status.NetTraffic.PktRecv-lastStatus.NetTraffic.PktRecv) / seconds)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -521,6 +567,10 @@ func (s *ServerService) AppendStatusSample(t time.Time, status *Status) {
|
||||||
}
|
}
|
||||||
systemMetrics.append("netUp", t, float64(status.NetIO.Up))
|
systemMetrics.append("netUp", t, float64(status.NetIO.Up))
|
||||||
systemMetrics.append("netDown", t, float64(status.NetIO.Down))
|
systemMetrics.append("netDown", t, float64(status.NetIO.Down))
|
||||||
|
systemMetrics.append("diskRead", t, float64(status.DiskIO.Read))
|
||||||
|
systemMetrics.append("diskWrite", t, float64(status.DiskIO.Write))
|
||||||
|
systemMetrics.append("pktUp", t, float64(status.NetIO.PktUp))
|
||||||
|
systemMetrics.append("pktDown", t, float64(status.NetIO.PktDown))
|
||||||
online := 0
|
online := 0
|
||||||
if p != nil && p.IsRunning() {
|
if p != nil && p.IsRunning() {
|
||||||
online = len(p.GetOnlineClients())
|
online = len(p.GetOnlineClients())
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "حصل خطأ أثناء تشغيل Xray",
|
"xrayErrorPopoverTitle": "حصل خطأ أثناء تشغيل Xray",
|
||||||
"operationHours": "مدة التشغيل",
|
"operationHours": "مدة التشغيل",
|
||||||
"systemHistoryTitle": "تاريخ النظام",
|
"systemHistoryTitle": "تاريخ النظام",
|
||||||
|
"historyTitleCpu": "استخدام المعالج",
|
||||||
|
"historyTitleMem": "استخدام الذاكرة",
|
||||||
|
"historyTitleNetwork": "عرض النطاق الترددي للشبكة",
|
||||||
|
"historyTitlePackets": "حزم الشبكة",
|
||||||
|
"historyTitleDisk": "إدخال/إخراج القرص",
|
||||||
|
"historyTitleOnline": "العملاء المتصلون",
|
||||||
|
"historyTitleLoad": "متوسط حمل النظام (1 / 5 / 15 دقيقة)",
|
||||||
|
"historyTabBandwidth": "عرض النطاق",
|
||||||
|
"historyTabPackets": "الحزم",
|
||||||
|
"historyTabDisk": "Disk I/O",
|
||||||
|
"historyTabOnline": "متصل",
|
||||||
|
"historyTabLoad": "الحِمل",
|
||||||
"charts": "الرسوم البيانية",
|
"charts": "الرسوم البيانية",
|
||||||
"xrayMetricsTitle": "مقاييس Xray",
|
"xrayMetricsTitle": "مقاييس Xray",
|
||||||
|
"xrayTitleHeap": "ذاكرة الكومة المخصصة",
|
||||||
|
"xrayTitleSys": "الذاكرة المحجوزة من نظام التشغيل",
|
||||||
|
"xrayTitleObjects": "كائنات الكومة النشطة",
|
||||||
|
"xrayTitleGcCount": "دورات GC المكتملة",
|
||||||
|
"xrayTitleGcPause": "مدة توقف GC",
|
||||||
|
"xrayTitleObservatory": "صحة الاتصال الصادر",
|
||||||
|
"xrayTabHeap": "Heap",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "الكائنات",
|
||||||
|
"xrayTabGcCount": "عدد GC",
|
||||||
|
"xrayTabGcPause": "توقف GC",
|
||||||
|
"xrayTabObservatory": "المرصد",
|
||||||
"xrayMetricsDisabled": "نقطة نهاية مقاييس Xray غير مهيأة",
|
"xrayMetricsDisabled": "نقطة نهاية مقاييس Xray غير مهيأة",
|
||||||
"xrayMetricsHint": "أضف كتلة metrics على المستوى الأعلى في إعدادات xray مع tag باسم metrics_out و listen على 127.0.0.1:11111، ثم أعد تشغيل xray.",
|
"xrayMetricsHint": "أضف كتلة metrics على المستوى الأعلى في إعدادات xray مع tag باسم metrics_out و listen على 127.0.0.1:11111، ثم أعد تشغيل xray.",
|
||||||
"xrayObservatoryEmpty": "لا توجد بيانات Observatory بعد",
|
"xrayObservatoryEmpty": "لا توجد بيانات Observatory بعد",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "An error occurred while running Xray",
|
"xrayErrorPopoverTitle": "An error occurred while running Xray",
|
||||||
"operationHours": "Uptime",
|
"operationHours": "Uptime",
|
||||||
"systemHistoryTitle": "System History",
|
"systemHistoryTitle": "System History",
|
||||||
|
"historyTitleCpu": "CPU Usage",
|
||||||
|
"historyTitleMem": "Memory Usage",
|
||||||
|
"historyTitleNetwork": "Network Bandwidth",
|
||||||
|
"historyTitlePackets": "Network Packets",
|
||||||
|
"historyTitleDisk": "Disk I/O",
|
||||||
|
"historyTitleOnline": "Online Clients",
|
||||||
|
"historyTitleLoad": "System Load Average (1m / 5m / 15m)",
|
||||||
|
"historyTabBandwidth": "Bandwidth",
|
||||||
|
"historyTabPackets": "Packets",
|
||||||
|
"historyTabDisk": "Disk I/O",
|
||||||
|
"historyTabOnline": "Online",
|
||||||
|
"historyTabLoad": "Load",
|
||||||
"charts": "Charts",
|
"charts": "Charts",
|
||||||
"xrayMetricsTitle": "Xray Metrics",
|
"xrayMetricsTitle": "Xray Metrics",
|
||||||
|
"xrayTitleHeap": "Allocated Heap Memory",
|
||||||
|
"xrayTitleSys": "Memory Reserved from OS",
|
||||||
|
"xrayTitleObjects": "Live Heap Objects",
|
||||||
|
"xrayTitleGcCount": "Completed GC Cycles",
|
||||||
|
"xrayTitleGcPause": "GC Pause Duration",
|
||||||
|
"xrayTitleObservatory": "Outbound Connection Health",
|
||||||
|
"xrayTabHeap": "Heap",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "Objects",
|
||||||
|
"xrayTabGcCount": "GC Count",
|
||||||
|
"xrayTabGcPause": "GC Pause",
|
||||||
|
"xrayTabObservatory": "Observatory",
|
||||||
"xrayMetricsDisabled": "Xray metrics endpoint not configured",
|
"xrayMetricsDisabled": "Xray metrics endpoint not configured",
|
||||||
"xrayMetricsHint": "Add a top-level metrics block to the xray config with tag metrics_out and listen 127.0.0.1:11111, then restart xray.",
|
"xrayMetricsHint": "Add a top-level metrics block to the xray config with tag metrics_out and listen 127.0.0.1:11111, then restart xray.",
|
||||||
"xrayObservatoryEmpty": "No observatory data yet",
|
"xrayObservatoryEmpty": "No observatory data yet",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "Se produjo un error al ejecutar Xray",
|
"xrayErrorPopoverTitle": "Se produjo un error al ejecutar Xray",
|
||||||
"operationHours": "Tiempo de Funcionamiento",
|
"operationHours": "Tiempo de Funcionamiento",
|
||||||
"systemHistoryTitle": "Historial del Sistema",
|
"systemHistoryTitle": "Historial del Sistema",
|
||||||
|
"historyTitleCpu": "Uso de CPU",
|
||||||
|
"historyTitleMem": "Uso de Memoria",
|
||||||
|
"historyTitleNetwork": "Ancho de Banda de Red",
|
||||||
|
"historyTitlePackets": "Paquetes de Red",
|
||||||
|
"historyTitleDisk": "E/S de Disco",
|
||||||
|
"historyTitleOnline": "Clientes en Línea",
|
||||||
|
"historyTitleLoad": "Carga Media del Sistema (1 / 5 / 15 min)",
|
||||||
|
"historyTabBandwidth": "Ancho de Banda",
|
||||||
|
"historyTabPackets": "Paquetes",
|
||||||
|
"historyTabDisk": "Disco I/O",
|
||||||
|
"historyTabOnline": "En línea",
|
||||||
|
"historyTabLoad": "Carga",
|
||||||
"charts": "Gráficos",
|
"charts": "Gráficos",
|
||||||
"xrayMetricsTitle": "Métricas de Xray",
|
"xrayMetricsTitle": "Métricas de Xray",
|
||||||
|
"xrayTitleHeap": "Memoria Heap Asignada",
|
||||||
|
"xrayTitleSys": "Memoria Reservada del SO",
|
||||||
|
"xrayTitleObjects": "Objetos Heap Activos",
|
||||||
|
"xrayTitleGcCount": "Ciclos de GC Completados",
|
||||||
|
"xrayTitleGcPause": "Duración de Pausa de GC",
|
||||||
|
"xrayTitleObservatory": "Estado de Conexiones Salientes",
|
||||||
|
"xrayTabHeap": "Heap",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "Objetos",
|
||||||
|
"xrayTabGcCount": "Recuento GC",
|
||||||
|
"xrayTabGcPause": "Pausa GC",
|
||||||
|
"xrayTabObservatory": "Observatorio",
|
||||||
"xrayMetricsDisabled": "Endpoint de métricas de Xray no configurado",
|
"xrayMetricsDisabled": "Endpoint de métricas de Xray no configurado",
|
||||||
"xrayMetricsHint": "Añade un bloque metrics de nivel superior a la configuración de xray con tag metrics_out y listen 127.0.0.1:11111, luego reinicia xray.",
|
"xrayMetricsHint": "Añade un bloque metrics de nivel superior a la configuración de xray con tag metrics_out y listen 127.0.0.1:11111, luego reinicia xray.",
|
||||||
"xrayObservatoryEmpty": "Aún no hay datos de Observatory",
|
"xrayObservatoryEmpty": "Aún no hay datos de Observatory",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "خطا در هنگام اجرای Xray رخ داد",
|
"xrayErrorPopoverTitle": "خطا در هنگام اجرای Xray رخ داد",
|
||||||
"operationHours": "مدتکارکرد",
|
"operationHours": "مدتکارکرد",
|
||||||
"systemHistoryTitle": "تاریخچه سیستم",
|
"systemHistoryTitle": "تاریخچه سیستم",
|
||||||
|
"historyTitleCpu": "مصرف پردازنده",
|
||||||
|
"historyTitleMem": "مصرف حافظه",
|
||||||
|
"historyTitleNetwork": "پهنای باند شبکه",
|
||||||
|
"historyTitlePackets": "بستههای شبکه",
|
||||||
|
"historyTitleDisk": "ورودی/خروجی دیسک",
|
||||||
|
"historyTitleOnline": "کاربران آنلاین",
|
||||||
|
"historyTitleLoad": "میانگین بار سیستم (۱ / ۵ / ۱۵ دقیقه)",
|
||||||
|
"historyTabBandwidth": "پهنای باند",
|
||||||
|
"historyTabPackets": "بستهها",
|
||||||
|
"historyTabDisk": "Disk I/O",
|
||||||
|
"historyTabOnline": "آنلاین",
|
||||||
|
"historyTabLoad": "بار",
|
||||||
"charts": "نمودارها",
|
"charts": "نمودارها",
|
||||||
"xrayMetricsTitle": "متریکهای Xray",
|
"xrayMetricsTitle": "متریکهای Xray",
|
||||||
|
"xrayTitleHeap": "حافظهی Heap تخصیصیافته",
|
||||||
|
"xrayTitleSys": "حافظهی رزروشده از سیستمعامل",
|
||||||
|
"xrayTitleObjects": "اشیای زندهی Heap",
|
||||||
|
"xrayTitleGcCount": "چرخههای کاملشدهی GC",
|
||||||
|
"xrayTitleGcPause": "مدت مکث GC",
|
||||||
|
"xrayTitleObservatory": "سلامت اتصال خروجی",
|
||||||
|
"xrayTabHeap": "Heap",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "اشیا",
|
||||||
|
"xrayTabGcCount": "تعداد GC",
|
||||||
|
"xrayTabGcPause": "مکث GC",
|
||||||
|
"xrayTabObservatory": "رصدخانه",
|
||||||
"xrayMetricsDisabled": "نقطه پایانی متریکهای Xray پیکربندی نشده",
|
"xrayMetricsDisabled": "نقطه پایانی متریکهای Xray پیکربندی نشده",
|
||||||
"xrayMetricsHint": "یک بلاک metrics در سطح بالای پیکربندی xray با tag برابر metrics_out و listen برابر 127.0.0.1:11111 اضافه کنید، سپس xray را راهاندازی مجدد کنید.",
|
"xrayMetricsHint": "یک بلاک metrics در سطح بالای پیکربندی xray با tag برابر metrics_out و listen برابر 127.0.0.1:11111 اضافه کنید، سپس xray را راهاندازی مجدد کنید.",
|
||||||
"xrayObservatoryEmpty": "هنوز دادهای از Observatory دریافت نشده",
|
"xrayObservatoryEmpty": "هنوز دادهای از Observatory دریافت نشده",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "Terjadi kesalahan saat menjalankan Xray",
|
"xrayErrorPopoverTitle": "Terjadi kesalahan saat menjalankan Xray",
|
||||||
"operationHours": "Waktu Aktif",
|
"operationHours": "Waktu Aktif",
|
||||||
"systemHistoryTitle": "Riwayat Sistem",
|
"systemHistoryTitle": "Riwayat Sistem",
|
||||||
|
"historyTitleCpu": "Penggunaan CPU",
|
||||||
|
"historyTitleMem": "Penggunaan Memori",
|
||||||
|
"historyTitleNetwork": "Bandwidth Jaringan",
|
||||||
|
"historyTitlePackets": "Paket Jaringan",
|
||||||
|
"historyTitleDisk": "I/O Disk",
|
||||||
|
"historyTitleOnline": "Klien Online",
|
||||||
|
"historyTitleLoad": "Rata-rata Beban Sistem (1 / 5 / 15 mnt)",
|
||||||
|
"historyTabBandwidth": "Bandwidth",
|
||||||
|
"historyTabPackets": "Paket",
|
||||||
|
"historyTabDisk": "Disk I/O",
|
||||||
|
"historyTabOnline": "Online",
|
||||||
|
"historyTabLoad": "Beban",
|
||||||
"charts": "Grafik",
|
"charts": "Grafik",
|
||||||
"xrayMetricsTitle": "Metrik Xray",
|
"xrayMetricsTitle": "Metrik Xray",
|
||||||
|
"xrayTitleHeap": "Memori Heap Teralokasi",
|
||||||
|
"xrayTitleSys": "Memori Dicadangkan dari OS",
|
||||||
|
"xrayTitleObjects": "Objek Heap Aktif",
|
||||||
|
"xrayTitleGcCount": "Siklus GC Selesai",
|
||||||
|
"xrayTitleGcPause": "Durasi Jeda GC",
|
||||||
|
"xrayTitleObservatory": "Kesehatan Koneksi Keluar",
|
||||||
|
"xrayTabHeap": "Heap",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "Objek",
|
||||||
|
"xrayTabGcCount": "Jumlah GC",
|
||||||
|
"xrayTabGcPause": "Jeda GC",
|
||||||
|
"xrayTabObservatory": "Observatorium",
|
||||||
"xrayMetricsDisabled": "Endpoint metrik Xray belum dikonfigurasi",
|
"xrayMetricsDisabled": "Endpoint metrik Xray belum dikonfigurasi",
|
||||||
"xrayMetricsHint": "Tambahkan blok metrics tingkat atas ke konfigurasi xray dengan tag metrics_out dan listen 127.0.0.1:11111, lalu mulai ulang xray.",
|
"xrayMetricsHint": "Tambahkan blok metrics tingkat atas ke konfigurasi xray dengan tag metrics_out dan listen 127.0.0.1:11111, lalu mulai ulang xray.",
|
||||||
"xrayObservatoryEmpty": "Belum ada data Observatory",
|
"xrayObservatoryEmpty": "Belum ada data Observatory",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "Xrayの実行中にエラーが発生しました",
|
"xrayErrorPopoverTitle": "Xrayの実行中にエラーが発生しました",
|
||||||
"operationHours": "システム稼働時間",
|
"operationHours": "システム稼働時間",
|
||||||
"systemHistoryTitle": "システム履歴",
|
"systemHistoryTitle": "システム履歴",
|
||||||
|
"historyTitleCpu": "CPU 使用率",
|
||||||
|
"historyTitleMem": "メモリ使用率",
|
||||||
|
"historyTitleNetwork": "ネットワーク帯域幅",
|
||||||
|
"historyTitlePackets": "ネットワークパケット",
|
||||||
|
"historyTitleDisk": "ディスク I/O",
|
||||||
|
"historyTitleOnline": "オンラインクライアント",
|
||||||
|
"historyTitleLoad": "システム平均負荷(1分 / 5分 / 15分)",
|
||||||
|
"historyTabBandwidth": "帯域幅",
|
||||||
|
"historyTabPackets": "パケット",
|
||||||
|
"historyTabDisk": "ディスク I/O",
|
||||||
|
"historyTabOnline": "オンライン",
|
||||||
|
"historyTabLoad": "負荷",
|
||||||
"charts": "チャート",
|
"charts": "チャート",
|
||||||
"xrayMetricsTitle": "Xray メトリクス",
|
"xrayMetricsTitle": "Xray メトリクス",
|
||||||
|
"xrayTitleHeap": "割り当て済みヒープメモリ",
|
||||||
|
"xrayTitleSys": "OS から確保したメモリ",
|
||||||
|
"xrayTitleObjects": "ヒープオブジェクト数",
|
||||||
|
"xrayTitleGcCount": "完了した GC サイクル",
|
||||||
|
"xrayTitleGcPause": "GC 一時停止時間",
|
||||||
|
"xrayTitleObservatory": "アウトバウンド接続の状態",
|
||||||
|
"xrayTabHeap": "ヒープ",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "オブジェクト",
|
||||||
|
"xrayTabGcCount": "GC 回数",
|
||||||
|
"xrayTabGcPause": "GC 一時停止",
|
||||||
|
"xrayTabObservatory": "オブザーバトリ",
|
||||||
"xrayMetricsDisabled": "Xray メトリクスエンドポイントが設定されていません",
|
"xrayMetricsDisabled": "Xray メトリクスエンドポイントが設定されていません",
|
||||||
"xrayMetricsHint": "xray 設定にトップレベルの metrics ブロック(tag: metrics_out、listen: 127.0.0.1:11111)を追加し、xray を再起動してください。",
|
"xrayMetricsHint": "xray 設定にトップレベルの metrics ブロック(tag: metrics_out、listen: 127.0.0.1:11111)を追加し、xray を再起動してください。",
|
||||||
"xrayObservatoryEmpty": "Observatory データはまだありません",
|
"xrayObservatoryEmpty": "Observatory データはまだありません",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "Ocorreu um erro ao executar o Xray",
|
"xrayErrorPopoverTitle": "Ocorreu um erro ao executar o Xray",
|
||||||
"operationHours": "Tempo de Atividade",
|
"operationHours": "Tempo de Atividade",
|
||||||
"systemHistoryTitle": "Histórico do Sistema",
|
"systemHistoryTitle": "Histórico do Sistema",
|
||||||
|
"historyTitleCpu": "Uso da CPU",
|
||||||
|
"historyTitleMem": "Uso de Memória",
|
||||||
|
"historyTitleNetwork": "Largura de Banda da Rede",
|
||||||
|
"historyTitlePackets": "Pacotes de Rede",
|
||||||
|
"historyTitleDisk": "E/S de Disco",
|
||||||
|
"historyTitleOnline": "Clientes Online",
|
||||||
|
"historyTitleLoad": "Média de Carga do Sistema (1 / 5 / 15 min)",
|
||||||
|
"historyTabBandwidth": "Largura de Banda",
|
||||||
|
"historyTabPackets": "Pacotes",
|
||||||
|
"historyTabDisk": "Disco I/O",
|
||||||
|
"historyTabOnline": "Online",
|
||||||
|
"historyTabLoad": "Carga",
|
||||||
"charts": "Gráficos",
|
"charts": "Gráficos",
|
||||||
"xrayMetricsTitle": "Métricas do Xray",
|
"xrayMetricsTitle": "Métricas do Xray",
|
||||||
|
"xrayTitleHeap": "Memória Heap Alocada",
|
||||||
|
"xrayTitleSys": "Memória Reservada do SO",
|
||||||
|
"xrayTitleObjects": "Objetos Heap Ativos",
|
||||||
|
"xrayTitleGcCount": "Ciclos de GC Concluídos",
|
||||||
|
"xrayTitleGcPause": "Duração da Pausa do GC",
|
||||||
|
"xrayTitleObservatory": "Saúde das Conexões de Saída",
|
||||||
|
"xrayTabHeap": "Heap",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "Objetos",
|
||||||
|
"xrayTabGcCount": "Contagem GC",
|
||||||
|
"xrayTabGcPause": "Pausa GC",
|
||||||
|
"xrayTabObservatory": "Observatório",
|
||||||
"xrayMetricsDisabled": "Endpoint de métricas do Xray não configurado",
|
"xrayMetricsDisabled": "Endpoint de métricas do Xray não configurado",
|
||||||
"xrayMetricsHint": "Adicione um bloco metrics de nível superior à configuração do xray com tag metrics_out e listen 127.0.0.1:11111, depois reinicie o xray.",
|
"xrayMetricsHint": "Adicione um bloco metrics de nível superior à configuração do xray com tag metrics_out e listen 127.0.0.1:11111, depois reinicie o xray.",
|
||||||
"xrayObservatoryEmpty": "Ainda não há dados do Observatory",
|
"xrayObservatoryEmpty": "Ainda não há dados do Observatory",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "Ошибка при запуске Xray",
|
"xrayErrorPopoverTitle": "Ошибка при запуске Xray",
|
||||||
"operationHours": "Время работы системы",
|
"operationHours": "Время работы системы",
|
||||||
"systemHistoryTitle": "История системы",
|
"systemHistoryTitle": "История системы",
|
||||||
|
"historyTitleCpu": "Загрузка ЦП",
|
||||||
|
"historyTitleMem": "Использование памяти",
|
||||||
|
"historyTitleNetwork": "Пропускная способность сети",
|
||||||
|
"historyTitlePackets": "Сетевые пакеты",
|
||||||
|
"historyTitleDisk": "Дисковый ввод-вывод",
|
||||||
|
"historyTitleOnline": "Клиенты онлайн",
|
||||||
|
"historyTitleLoad": "Средняя нагрузка системы (1 / 5 / 15 мин)",
|
||||||
|
"historyTabBandwidth": "Пропускная способность",
|
||||||
|
"historyTabPackets": "Пакеты",
|
||||||
|
"historyTabDisk": "Диск I/O",
|
||||||
|
"historyTabOnline": "Онлайн",
|
||||||
|
"historyTabLoad": "Нагрузка",
|
||||||
"charts": "Графики",
|
"charts": "Графики",
|
||||||
"xrayMetricsTitle": "Метрики Xray",
|
"xrayMetricsTitle": "Метрики Xray",
|
||||||
|
"xrayTitleHeap": "Выделенная память кучи",
|
||||||
|
"xrayTitleSys": "Память, зарезервированная у ОС",
|
||||||
|
"xrayTitleObjects": "Активные объекты кучи",
|
||||||
|
"xrayTitleGcCount": "Завершённые циклы GC",
|
||||||
|
"xrayTitleGcPause": "Длительность паузы GC",
|
||||||
|
"xrayTitleObservatory": "Состояние исходящих соединений",
|
||||||
|
"xrayTabHeap": "Куча",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "Объекты",
|
||||||
|
"xrayTabGcCount": "Счётчик GC",
|
||||||
|
"xrayTabGcPause": "Пауза GC",
|
||||||
|
"xrayTabObservatory": "Обсерватория",
|
||||||
"xrayMetricsDisabled": "Конечная точка метрик Xray не настроена",
|
"xrayMetricsDisabled": "Конечная точка метрик Xray не настроена",
|
||||||
"xrayMetricsHint": "Добавьте блок metrics верхнего уровня в конфигурацию xray с tag metrics_out и listen 127.0.0.1:11111, затем перезапустите xray.",
|
"xrayMetricsHint": "Добавьте блок metrics верхнего уровня в конфигурацию xray с tag metrics_out и listen 127.0.0.1:11111, затем перезапустите xray.",
|
||||||
"xrayObservatoryEmpty": "Данных Observatory пока нет",
|
"xrayObservatoryEmpty": "Данных Observatory пока нет",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "Xray çalıştırılırken bir hata oluştu",
|
"xrayErrorPopoverTitle": "Xray çalıştırılırken bir hata oluştu",
|
||||||
"operationHours": "Çalışma Süresi",
|
"operationHours": "Çalışma Süresi",
|
||||||
"systemHistoryTitle": "Sistem Geçmişi",
|
"systemHistoryTitle": "Sistem Geçmişi",
|
||||||
|
"historyTitleCpu": "CPU Kullanımı",
|
||||||
|
"historyTitleMem": "Bellek Kullanımı",
|
||||||
|
"historyTitleNetwork": "Ağ Bant Genişliği",
|
||||||
|
"historyTitlePackets": "Ağ Paketleri",
|
||||||
|
"historyTitleDisk": "Disk G/Ç",
|
||||||
|
"historyTitleOnline": "Çevrimiçi İstemciler",
|
||||||
|
"historyTitleLoad": "Sistem Yük Ortalaması (1d / 5d / 15d)",
|
||||||
|
"historyTabBandwidth": "Bant Genişliği",
|
||||||
|
"historyTabPackets": "Paketler",
|
||||||
|
"historyTabDisk": "Disk G/Ç",
|
||||||
|
"historyTabOnline": "Çevrimiçi",
|
||||||
|
"historyTabLoad": "Yük",
|
||||||
"charts": "Grafikler",
|
"charts": "Grafikler",
|
||||||
"xrayMetricsTitle": "Xray Metrikleri",
|
"xrayMetricsTitle": "Xray Metrikleri",
|
||||||
|
"xrayTitleHeap": "Ayrılan Yığın Belleği",
|
||||||
|
"xrayTitleSys": "İşletim Sisteminden Ayrılan Bellek",
|
||||||
|
"xrayTitleObjects": "Aktif Yığın Nesneleri",
|
||||||
|
"xrayTitleGcCount": "Tamamlanan GC Döngüleri",
|
||||||
|
"xrayTitleGcPause": "GC Duraklama Süresi",
|
||||||
|
"xrayTitleObservatory": "Giden Bağlantı Durumu",
|
||||||
|
"xrayTabHeap": "Heap",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "Nesneler",
|
||||||
|
"xrayTabGcCount": "GC Sayısı",
|
||||||
|
"xrayTabGcPause": "GC Duraklaması",
|
||||||
|
"xrayTabObservatory": "Gözlemevi",
|
||||||
"xrayMetricsDisabled": "Xray metrik uç noktası yapılandırılmadı",
|
"xrayMetricsDisabled": "Xray metrik uç noktası yapılandırılmadı",
|
||||||
"xrayMetricsHint": "xray yapılandırmasına tag metrics_out ve listen 127.0.0.1:11111 olan üst düzey bir metrics bloğu ekleyin, sonra xray'i yeniden başlatın.",
|
"xrayMetricsHint": "xray yapılandırmasına tag metrics_out ve listen 127.0.0.1:11111 olan üst düzey bir metrics bloğu ekleyin, sonra xray'i yeniden başlatın.",
|
||||||
"xrayObservatoryEmpty": "Henüz Observatory verisi yok",
|
"xrayObservatoryEmpty": "Henüz Observatory verisi yok",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "Під час роботи Xray сталася помилка",
|
"xrayErrorPopoverTitle": "Під час роботи Xray сталася помилка",
|
||||||
"operationHours": "Час роботи",
|
"operationHours": "Час роботи",
|
||||||
"systemHistoryTitle": "Історія системи",
|
"systemHistoryTitle": "Історія системи",
|
||||||
|
"historyTitleCpu": "Завантаження ЦП",
|
||||||
|
"historyTitleMem": "Використання пам’яті",
|
||||||
|
"historyTitleNetwork": "Пропускна здатність мережі",
|
||||||
|
"historyTitlePackets": "Мережеві пакети",
|
||||||
|
"historyTitleDisk": "Дисковий ввід-вивід",
|
||||||
|
"historyTitleOnline": "Клієнти онлайн",
|
||||||
|
"historyTitleLoad": "Середнє навантаження системи (1 / 5 / 15 хв)",
|
||||||
|
"historyTabBandwidth": "Пропускна здатність",
|
||||||
|
"historyTabPackets": "Пакети",
|
||||||
|
"historyTabDisk": "Диск I/O",
|
||||||
|
"historyTabOnline": "Онлайн",
|
||||||
|
"historyTabLoad": "Навантаження",
|
||||||
"charts": "Графіки",
|
"charts": "Графіки",
|
||||||
"xrayMetricsTitle": "Метрики Xray",
|
"xrayMetricsTitle": "Метрики Xray",
|
||||||
|
"xrayTitleHeap": "Виділена пам’ять купи",
|
||||||
|
"xrayTitleSys": "Пам’ять, зарезервована в ОС",
|
||||||
|
"xrayTitleObjects": "Активні об’єкти купи",
|
||||||
|
"xrayTitleGcCount": "Завершені цикли GC",
|
||||||
|
"xrayTitleGcPause": "Тривалість паузи GC",
|
||||||
|
"xrayTitleObservatory": "Стан вихідних з’єднань",
|
||||||
|
"xrayTabHeap": "Купа",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "Об’єкти",
|
||||||
|
"xrayTabGcCount": "Лічильник GC",
|
||||||
|
"xrayTabGcPause": "Пауза GC",
|
||||||
|
"xrayTabObservatory": "Обсерваторія",
|
||||||
"xrayMetricsDisabled": "Кінцева точка метрик Xray не налаштована",
|
"xrayMetricsDisabled": "Кінцева точка метрик Xray не налаштована",
|
||||||
"xrayMetricsHint": "Додайте блок metrics верхнього рівня до конфігурації xray з tag metrics_out і listen 127.0.0.1:11111, потім перезапустіть xray.",
|
"xrayMetricsHint": "Додайте блок metrics верхнього рівня до конфігурації xray з tag metrics_out і listen 127.0.0.1:11111, потім перезапустіть xray.",
|
||||||
"xrayObservatoryEmpty": "Даних Observatory ще немає",
|
"xrayObservatoryEmpty": "Даних Observatory ще немає",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "Đã xảy ra lỗi khi chạy Xray",
|
"xrayErrorPopoverTitle": "Đã xảy ra lỗi khi chạy Xray",
|
||||||
"operationHours": "Thời gian hoạt động",
|
"operationHours": "Thời gian hoạt động",
|
||||||
"systemHistoryTitle": "Lịch sử hệ thống",
|
"systemHistoryTitle": "Lịch sử hệ thống",
|
||||||
|
"historyTitleCpu": "Mức sử dụng CPU",
|
||||||
|
"historyTitleMem": "Mức sử dụng bộ nhớ",
|
||||||
|
"historyTitleNetwork": "Băng thông mạng",
|
||||||
|
"historyTitlePackets": "Gói tin mạng",
|
||||||
|
"historyTitleDisk": "I/O đĩa",
|
||||||
|
"historyTitleOnline": "Máy khách trực tuyến",
|
||||||
|
"historyTitleLoad": "Tải trung bình hệ thống (1 / 5 / 15 phút)",
|
||||||
|
"historyTabBandwidth": "Băng thông",
|
||||||
|
"historyTabPackets": "Gói tin",
|
||||||
|
"historyTabDisk": "Đĩa I/O",
|
||||||
|
"historyTabOnline": "Trực tuyến",
|
||||||
|
"historyTabLoad": "Tải",
|
||||||
"charts": "Biểu đồ",
|
"charts": "Biểu đồ",
|
||||||
"xrayMetricsTitle": "Chỉ số Xray",
|
"xrayMetricsTitle": "Chỉ số Xray",
|
||||||
|
"xrayTitleHeap": "Bộ nhớ Heap đã cấp phát",
|
||||||
|
"xrayTitleSys": "Bộ nhớ dành riêng từ HĐH",
|
||||||
|
"xrayTitleObjects": "Đối tượng Heap đang hoạt động",
|
||||||
|
"xrayTitleGcCount": "Chu kỳ GC đã hoàn thành",
|
||||||
|
"xrayTitleGcPause": "Thời lượng tạm dừng GC",
|
||||||
|
"xrayTitleObservatory": "Tình trạng kết nối đi",
|
||||||
|
"xrayTabHeap": "Heap",
|
||||||
|
"xrayTabSys": "Sys",
|
||||||
|
"xrayTabObjects": "Đối tượng",
|
||||||
|
"xrayTabGcCount": "Số lần GC",
|
||||||
|
"xrayTabGcPause": "Tạm dừng GC",
|
||||||
|
"xrayTabObservatory": "Đài quan sát",
|
||||||
"xrayMetricsDisabled": "Điểm cuối chỉ số Xray chưa được cấu hình",
|
"xrayMetricsDisabled": "Điểm cuối chỉ số Xray chưa được cấu hình",
|
||||||
"xrayMetricsHint": "Thêm khối metrics cấp cao nhất vào cấu hình xray với tag là metrics_out và listen là 127.0.0.1:11111, sau đó khởi động lại xray.",
|
"xrayMetricsHint": "Thêm khối metrics cấp cao nhất vào cấu hình xray với tag là metrics_out và listen là 127.0.0.1:11111, sau đó khởi động lại xray.",
|
||||||
"xrayObservatoryEmpty": "Chưa có dữ liệu Observatory",
|
"xrayObservatoryEmpty": "Chưa có dữ liệu Observatory",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "运行Xray时发生错误",
|
"xrayErrorPopoverTitle": "运行Xray时发生错误",
|
||||||
"operationHours": "系统正常运行时间",
|
"operationHours": "系统正常运行时间",
|
||||||
"systemHistoryTitle": "系统历史",
|
"systemHistoryTitle": "系统历史",
|
||||||
|
"historyTitleCpu": "CPU 使用率",
|
||||||
|
"historyTitleMem": "内存使用率",
|
||||||
|
"historyTitleNetwork": "网络带宽",
|
||||||
|
"historyTitlePackets": "网络数据包",
|
||||||
|
"historyTitleDisk": "磁盘 I/O",
|
||||||
|
"historyTitleOnline": "在线客户端",
|
||||||
|
"historyTitleLoad": "系统平均负载(1 分钟 / 5 分钟 / 15 分钟)",
|
||||||
|
"historyTabBandwidth": "带宽",
|
||||||
|
"historyTabPackets": "数据包",
|
||||||
|
"historyTabDisk": "磁盘 I/O",
|
||||||
|
"historyTabOnline": "在线",
|
||||||
|
"historyTabLoad": "负载",
|
||||||
"charts": "图表",
|
"charts": "图表",
|
||||||
"xrayMetricsTitle": "Xray 指标",
|
"xrayMetricsTitle": "Xray 指标",
|
||||||
|
"xrayTitleHeap": "已分配的堆内存",
|
||||||
|
"xrayTitleSys": "向操作系统保留的内存",
|
||||||
|
"xrayTitleObjects": "存活的堆对象",
|
||||||
|
"xrayTitleGcCount": "已完成的 GC 周期",
|
||||||
|
"xrayTitleGcPause": "GC 暂停时间",
|
||||||
|
"xrayTitleObservatory": "出站连接健康状态",
|
||||||
|
"xrayTabHeap": "堆",
|
||||||
|
"xrayTabSys": "系统",
|
||||||
|
"xrayTabObjects": "对象",
|
||||||
|
"xrayTabGcCount": "GC 次数",
|
||||||
|
"xrayTabGcPause": "GC 暂停",
|
||||||
|
"xrayTabObservatory": "观测站",
|
||||||
"xrayMetricsDisabled": "未配置 Xray 指标端点",
|
"xrayMetricsDisabled": "未配置 Xray 指标端点",
|
||||||
"xrayMetricsHint": "在 xray 配置中添加顶级 metrics 块,tag 为 metrics_out,listen 为 127.0.0.1:11111,然后重启 xray。",
|
"xrayMetricsHint": "在 xray 配置中添加顶级 metrics 块,tag 为 metrics_out,listen 为 127.0.0.1:11111,然后重启 xray。",
|
||||||
"xrayObservatoryEmpty": "暂无 Observatory 数据",
|
"xrayObservatoryEmpty": "暂无 Observatory 数据",
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,32 @@
|
||||||
"xrayErrorPopoverTitle": "執行Xray時發生錯誤",
|
"xrayErrorPopoverTitle": "執行Xray時發生錯誤",
|
||||||
"operationHours": "系統正常執行時間",
|
"operationHours": "系統正常執行時間",
|
||||||
"systemHistoryTitle": "系統歷史",
|
"systemHistoryTitle": "系統歷史",
|
||||||
|
"historyTitleCpu": "CPU 使用率",
|
||||||
|
"historyTitleMem": "記憶體使用率",
|
||||||
|
"historyTitleNetwork": "網路頻寬",
|
||||||
|
"historyTitlePackets": "網路封包",
|
||||||
|
"historyTitleDisk": "磁碟 I/O",
|
||||||
|
"historyTitleOnline": "線上用戶端",
|
||||||
|
"historyTitleLoad": "系統平均負載(1 分鐘 / 5 分鐘 / 15 分鐘)",
|
||||||
|
"historyTabBandwidth": "頻寬",
|
||||||
|
"historyTabPackets": "封包",
|
||||||
|
"historyTabDisk": "磁碟 I/O",
|
||||||
|
"historyTabOnline": "線上",
|
||||||
|
"historyTabLoad": "負載",
|
||||||
"charts": "圖表",
|
"charts": "圖表",
|
||||||
"xrayMetricsTitle": "Xray 指標",
|
"xrayMetricsTitle": "Xray 指標",
|
||||||
|
"xrayTitleHeap": "已配置的堆積記憶體",
|
||||||
|
"xrayTitleSys": "向作業系統保留的記憶體",
|
||||||
|
"xrayTitleObjects": "存活的堆積物件",
|
||||||
|
"xrayTitleGcCount": "已完成的 GC 週期",
|
||||||
|
"xrayTitleGcPause": "GC 暫停時間",
|
||||||
|
"xrayTitleObservatory": "出站連線健康狀態",
|
||||||
|
"xrayTabHeap": "堆積",
|
||||||
|
"xrayTabSys": "系統",
|
||||||
|
"xrayTabObjects": "物件",
|
||||||
|
"xrayTabGcCount": "GC 次數",
|
||||||
|
"xrayTabGcPause": "GC 暫停",
|
||||||
|
"xrayTabObservatory": "觀測站",
|
||||||
"xrayMetricsDisabled": "未設定 Xray 指標端點",
|
"xrayMetricsDisabled": "未設定 Xray 指標端點",
|
||||||
"xrayMetricsHint": "在 xray 設定中加入頂層 metrics 區塊,tag 為 metrics_out,listen 為 127.0.0.1:11111,然後重啟 xray。",
|
"xrayMetricsHint": "在 xray 設定中加入頂層 metrics 區塊,tag 為 metrics_out,listen 為 127.0.0.1:11111,然後重啟 xray。",
|
||||||
"xrayObservatoryEmpty": "尚無 Observatory 資料",
|
"xrayObservatoryEmpty": "尚無 Observatory 資料",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue