diff --git a/frontend/src/components/viz/Sparkline.css b/frontend/src/components/viz/Sparkline.css index 189567a2..ce2fdde6 100644 --- a/frontend/src/components/viz/Sparkline.css +++ b/frontend/src/components/viz/Sparkline.css @@ -32,3 +32,28 @@ gap: 4px; 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; +} diff --git a/frontend/src/components/viz/Sparkline.tsx b/frontend/src/components/viz/Sparkline.tsx index 11bf1ecf..1b648291 100644 --- a/frontend/src/components/viz/Sparkline.tsx +++ b/frontend/src/components/viz/Sparkline.tsx @@ -31,6 +31,13 @@ const DEFAULT_MAX_COLOR = '#fa541c'; interface SparklineProps { data: number[]; + data2?: number[]; + data3?: number[]; + stroke2?: string; + stroke3?: string; + name1?: string; + name2?: string; + name3?: string; labels?: (string | number)[]; height?: number; stroke?: string; @@ -56,11 +63,20 @@ interface SparklineProps { interface ChartPoint { index: number; value: number; + value2: number; + value3: number; label: string; } export default function Sparkline({ data, + data2 = [], + data3 = [], + stroke2 = '#722ed1', + stroke3 = '#a0d911', + name1, + name2, + name3, labels = [], height = 80, stroke = '#008771', @@ -85,28 +101,39 @@ export default function Sparkline({ const reactId = useId(); const safeId = reactId.replace(/[^a-zA-Z0-9]/g, ''); 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(() => { const n = Math.min(data.length, maxPoints); if (n === 0) return []; const sliceStart = data.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) => ({ index: i, 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), })); - }, [data, labels, maxPoints]); + }, [data, data2, data3, labels, maxPoints]); const yDomain = useMemo<[number, number]>(() => { if (valueMax != null) return [valueMin, valueMax]; let max = valueMin; for (const p of points) { 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; return [valueMin, max * 1.1]; - }, [points, valueMin, valueMax]); + }, [points, valueMin, valueMax, hasSeries2, hasSeries3]); const yTicks = useMemo(() => { if (!showAxes) return undefined; @@ -129,7 +156,7 @@ export default function Sparkline({ const fmtTooltip = tooltipFormatter ?? yFormatter; const extremaPoints = useMemo(() => { - if (!extrema?.show || points.length < 2) return null; + if (!extrema?.show || multiSeries || points.length < 2) return null; let minIdx = 0; let maxIdx = 0; for (let i = 1; i < points.length; i++) { @@ -138,7 +165,17 @@ export default function Sparkline({ } if (minIdx === maxIdx) return null; 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 minColor = extrema?.minColor ?? DEFAULT_MIN_COLOR; @@ -156,6 +193,13 @@ export default function Sparkline({ )} + {legendItems.length > 0 && ( + + )} + + + + + + + + {showGrid && ( @@ -209,9 +261,9 @@ export default function Sparkline({ }} labelStyle={{ color: 'var(--ant-color-text-tertiary)', marginBottom: 4, fontSize: 11 }} 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))} - separator="" + separator={multiSeries ? ': ' : ''} /> )} {referenceLines?.map((rl, idx) => ( @@ -256,6 +308,7 @@ export default function Sparkline({ + {hasSeries2 && ( + + )} + {hasSeries3 && ( + + )} diff --git a/frontend/src/layouts/AppSidebar.tsx b/frontend/src/layouts/AppSidebar.tsx index f8d48f25..8e662ed2 100644 --- a/frontend/src/layouts/AppSidebar.tsx +++ b/frontend/src/layouts/AppSidebar.tsx @@ -32,6 +32,7 @@ import { import { HttpUtil } from '@/utils'; import { pauseAnimationsUntilLeave, useTheme } from '@/hooks/useTheme'; +import { useAllSettings } from '@/api/queries/useAllSettings'; import './AppSidebar.css'; const SIDEBAR_COLLAPSED_KEY = 'isSidebarCollapsed'; @@ -121,6 +122,8 @@ export default function AppSidebar() { const { isDark, isUltra, toggleTheme, toggleUltra } = useTheme(); const navigate = useNavigate(); const { pathname, hash } = useLocation(); + const { allSetting } = useAllSettings(); + const showSubFormats = !!(allSetting.subJsonEnable || allSetting.subClashEnable); const [collapsed, setCollapsed] = useState(() => readCollapsed()); const [drawerOpen, setDrawerOpen] = useState(false); @@ -143,13 +146,18 @@ export default function AppSidebar() { const navItems = useMemo(() => tabs.filter((tab) => tab.icon !== 'logout'), [tabs]); const utilItems = useMemo(() => tabs.filter((tab) => tab.icon === 'logout'), [tabs]); - const settingsChildren = useMemo>(() => [ - { key: '/settings#general', icon: , label: t('pages.settings.panelSettings') }, - { key: '/settings#security', icon: , label: t('pages.settings.securitySettings') }, - { key: '/settings#telegram', icon: , label: t('pages.settings.TGBotSettings') }, - { key: '/settings#subscription', icon: , label: t('pages.settings.subSettings') }, - { key: '/settings#subscription-formats', icon: , label: 'Sub Formats' }, - ], [t]); + const settingsChildren = useMemo>(() => { + const children: NonNullable = [ + { key: '/settings#general', icon: , label: t('pages.settings.panelSettings') }, + { key: '/settings#security', icon: , label: t('pages.settings.securitySettings') }, + { key: '/settings#telegram', icon: , label: t('pages.settings.TGBotSettings') }, + { key: '/settings#subscription', icon: , label: t('pages.settings.subSettings') }, + ]; + if (showSubFormats) { + children.push({ key: '/settings#subscription-formats', icon: , label: 'Sub Formats' }); + } + return children; + }, [t, showSubFormats]); const xrayChildren = useMemo>(() => [ { key: '/xray#basic', icon: , label: t('pages.xray.basicTemplate') }, diff --git a/frontend/src/pages/index/SystemHistoryModal.css b/frontend/src/pages/index/SystemHistoryModal.css index 27249a2c..e66e6530 100644 --- a/frontend/src/pages/index/SystemHistoryModal.css +++ b/frontend/src/pages/index/SystemHistoryModal.css @@ -13,6 +13,13 @@ margin-bottom: 4px; } +.history-chart-title { + margin-bottom: 12px; + font-size: 14px; + font-weight: 600; + color: var(--ant-color-text); +} + .cpu-chart-wrap { margin: 8px 8px 16px; padding: 16px 18px 18px; diff --git a/frontend/src/pages/index/SystemHistoryModal.tsx b/frontend/src/pages/index/SystemHistoryModal.tsx index 9c03b741..9d433278 100644 --- a/frontend/src/pages/index/SystemHistoryModal.tsx +++ b/frontend/src/pages/index/SystemHistoryModal.tsx @@ -1,6 +1,16 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; +import type { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { Modal, Select, Tabs } from 'antd'; +import { + DashboardOutlined, + DatabaseOutlined, + DeploymentUnitOutlined, + GlobalOutlined, + HddOutlined, + LineChartOutlined, + TeamOutlined, +} from '@ant-design/icons'; import { HttpUtil, SizeFormatter } from '@/utils'; import { Sparkline } from '@/components/viz'; @@ -17,26 +27,38 @@ interface SystemHistoryModalProps { interface MetricDef { key: string; tab: string; + tabKey?: string; + title: string; + icon: ReactNode; valueMax: number | null; unit: string; stroke: string; + key2?: string; + stroke2?: string; + name1?: string; + name2?: string; + key3?: string; + stroke3?: string; + name3?: string; } const METRICS: MetricDef[] = [ - { key: 'cpu', tab: 'CPU', valueMax: 100, unit: '%', stroke: '' }, - { key: 'mem', tab: 'RAM', valueMax: 100, unit: '%', stroke: '#7c4dff' }, - { key: 'netUp', tab: 'Net Up', valueMax: null, unit: 'B/s', stroke: '#1890ff' }, - { key: 'netDown', tab: 'Net Down', valueMax: null, unit: 'B/s', stroke: '#13c2c2' }, - { key: 'online', tab: 'Online', valueMax: null, unit: '', stroke: '#52c41a' }, - { key: 'load1', tab: 'Load 1m', valueMax: null, unit: '', stroke: '#fa8c16' }, - { key: 'load5', tab: 'Load 5m', valueMax: null, unit: '', stroke: '#f5222d' }, - { key: 'load15', tab: 'Load 15m', valueMax: null, unit: '', stroke: '#a0d911' }, + { key: 'cpu', tab: 'CPU', title: 'pages.index.historyTitleCpu', icon: , valueMax: 100, unit: '%', stroke: '' }, + { key: 'mem', tab: 'RAM', title: 'pages.index.historyTitleMem', icon: , valueMax: 100, unit: '%', stroke: '#7c4dff' }, + { key: 'netUp', tab: 'Bandwidth', tabKey: 'pages.index.historyTabBandwidth', title: 'pages.index.historyTitleNetwork', icon: , valueMax: null, unit: 'B/s', stroke: '#1890ff', key2: 'netDown', stroke2: '#13c2c2', name1: 'Up', name2: 'Down' }, + { key: 'pktUp', tab: 'Packets', tabKey: 'pages.index.historyTabPackets', title: 'pages.index.historyTitlePackets', icon: , valueMax: null, unit: 'pkt/s', stroke: '#2f54eb', key2: 'pktDown', stroke2: '#36cfc9', name1: 'Up', name2: 'Down' }, + { key: 'diskRead', tab: 'Disk I/O', tabKey: 'pages.index.historyTabDisk', title: 'pages.index.historyTitleDisk', icon: , valueMax: null, unit: 'B/s', stroke: '#eb2f96', key2: 'diskWrite', stroke2: '#722ed1', name1: 'Read', name2: 'Write' }, + { key: 'online', tab: 'Online', tabKey: 'pages.index.historyTabOnline', title: 'pages.index.historyTitleOnline', icon: , valueMax: null, unit: '', stroke: '#52c41a' }, + { key: 'load1', tab: 'Load', tabKey: 'pages.index.historyTabLoad', title: 'pages.index.historyTitleLoad', icon: , valueMax: null, unit: '', stroke: '#fa8c16', key2: 'load5', stroke2: '#f5222d', name1: '1m', name2: '5m', key3: 'load15', stroke3: '#a0d911', name3: '15m' }, ]; function unitFormatter(unit: string, activeKey: string): (v: number) => string { if (unit === 'B/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 === '%') { return (v) => `${Number(v).toFixed(1)}%`; } @@ -69,6 +91,8 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist const [activeKey, setActiveKey] = useState('cpu'); const [bucket, setBucket] = useState(2); const [points, setPoints] = useState([]); + const [points2, setPoints2] = useState([]); + const [points3, setPoints3] = useState([]); const [labels, setLabels] = useState([]); const [timestamps, setTimestamps] = useState([]); @@ -116,15 +140,32 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist setLabels(labs); setPoints(vals); setTimestamps(tss); + + const fetchAligned = async (key?: string): Promise => { + 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(); + 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 { setLabels([]); setPoints([]); + setPoints2([]); + setPoints3([]); setTimestamps([]); } } catch (e) { console.error('Failed to fetch history bucket', e); setLabels([]); setPoints([]); + setPoints2([]); + setPoints3([]); setTimestamps([]); } }, [activeMetric, bucket]); @@ -168,12 +209,26 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist onChange={setActiveKey} size="small" 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 ? {m.icon} : tabLabel, + }; + })} />
+ {activeMetric?.title &&
{t(activeMetric.title)}
}
diff --git a/frontend/src/pages/index/XrayMetricsModal.tsx b/frontend/src/pages/index/XrayMetricsModal.tsx index 5f1902fd..9e134ed2 100644 --- a/frontend/src/pages/index/XrayMetricsModal.tsx +++ b/frontend/src/pages/index/XrayMetricsModal.tsx @@ -1,6 +1,15 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import type { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; 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 { Sparkline } from '@/components/viz'; @@ -17,6 +26,9 @@ interface XrayMetricsModalProps { interface MetricDef { key: string; tab: string; + tabKey: string; + title: string; + icon: ReactNode; unit: 'B' | 'ns' | 'ms' | ''; stroke: string; } @@ -36,12 +48,12 @@ interface ObservatoryTag { } const METRICS: MetricDef[] = [ - { key: 'xrAlloc', tab: 'Heap', unit: 'B', stroke: '#7c4dff' }, - { key: 'xrSys', tab: 'Sys', unit: 'B', stroke: '#1890ff' }, - { key: 'xrHeapObjects', tab: 'Objects', unit: '', stroke: '#13c2c2' }, - { key: 'xrNumGC', tab: 'GC Count', unit: '', stroke: '#fa8c16' }, - { key: 'xrPauseNs', tab: 'GC Pause', unit: 'ns', stroke: '#f5222d' }, - { key: OBS_KEY, tab: 'Observatory', unit: 'ms', stroke: '#52c41a' }, + { key: 'xrAlloc', tab: 'Heap', tabKey: 'pages.index.xrayTabHeap', title: 'pages.index.xrayTitleHeap', icon: , unit: 'B', stroke: '#7c4dff' }, + { key: 'xrSys', tab: 'Sys', tabKey: 'pages.index.xrayTabSys', title: 'pages.index.xrayTitleSys', icon: , unit: 'B', stroke: '#1890ff' }, + { key: 'xrHeapObjects', tab: 'Objects', tabKey: 'pages.index.xrayTabObjects', title: 'pages.index.xrayTitleObjects', icon: , unit: '', stroke: '#13c2c2' }, + { key: 'xrNumGC', tab: 'GC Count', tabKey: 'pages.index.xrayTabGcCount', title: 'pages.index.xrayTitleGcCount', icon: , unit: '', stroke: '#fa8c16' }, + { key: 'xrPauseNs', tab: 'GC Pause', tabKey: 'pages.index.xrayTabGcPause', title: 'pages.index.xrayTitleGcPause', icon: , unit: 'ns', stroke: '#f5222d' }, + { key: OBS_KEY, tab: 'Observatory', tabKey: 'pages.index.xrayTabObservatory', title: 'pages.index.xrayTitleObservatory', icon: , unit: 'ms', stroke: '#52c41a' }, ]; function unitFormatter(unit: string): (v: number) => string { @@ -299,7 +311,13 @@ export default function XrayMetricsModal({ open, onClose }: XrayMetricsModalProp onChange={setActiveKey} size="small" 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 ? {m.icon} : tabLabel, + }; + })} /> {isObservatory && ( @@ -353,6 +371,7 @@ export default function XrayMetricsModal({ open, onClose }: XrayMetricsModalProp )}
+ {activeMetric?.title &&
{t(activeMetric.title)}
} 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 avgState, err := load.Avg() if err != nil { @@ -396,7 +432,7 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status { if err != nil { logger.Warning("get io counters failed:", err) } else { - var totalSent, totalRecv uint64 + var totalSent, totalRecv, totalPktSent, totalPktRecv uint64 for _, iface := range ioStats { name := strings.ToLower(iface.Name) if isVirtualInterface(name) { @@ -404,9 +440,13 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status { } totalSent += iface.BytesSent totalRecv += iface.BytesRecv + totalPktSent += iface.PacketsSent + totalPktRecv += iface.PacketsRecv } status.NetTraffic.Sent = totalSent status.NetTraffic.Recv = totalRecv + status.NetTraffic.PktSent = totalPktSent + status.NetTraffic.PktRecv = totalPktRecv if lastStatus != nil { 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) status.NetIO.Up = up 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("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 if p != nil && p.IsRunning() { online = len(p.GetOnlineClients()) diff --git a/web/translation/ar-EG.json b/web/translation/ar-EG.json index 6c20acf2..55857ee5 100644 --- a/web/translation/ar-EG.json +++ b/web/translation/ar-EG.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "حصل خطأ أثناء تشغيل Xray", "operationHours": "مدة التشغيل", "systemHistoryTitle": "تاريخ النظام", + "historyTitleCpu": "استخدام المعالج", + "historyTitleMem": "استخدام الذاكرة", + "historyTitleNetwork": "عرض النطاق الترددي للشبكة", + "historyTitlePackets": "حزم الشبكة", + "historyTitleDisk": "إدخال/إخراج القرص", + "historyTitleOnline": "العملاء المتصلون", + "historyTitleLoad": "متوسط حمل النظام (1 / 5 / 15 دقيقة)", + "historyTabBandwidth": "عرض النطاق", + "historyTabPackets": "الحزم", + "historyTabDisk": "Disk I/O", + "historyTabOnline": "متصل", + "historyTabLoad": "الحِمل", "charts": "الرسوم البيانية", "xrayMetricsTitle": "مقاييس Xray", + "xrayTitleHeap": "ذاكرة الكومة المخصصة", + "xrayTitleSys": "الذاكرة المحجوزة من نظام التشغيل", + "xrayTitleObjects": "كائنات الكومة النشطة", + "xrayTitleGcCount": "دورات GC المكتملة", + "xrayTitleGcPause": "مدة توقف GC", + "xrayTitleObservatory": "صحة الاتصال الصادر", + "xrayTabHeap": "Heap", + "xrayTabSys": "Sys", + "xrayTabObjects": "الكائنات", + "xrayTabGcCount": "عدد GC", + "xrayTabGcPause": "توقف GC", + "xrayTabObservatory": "المرصد", "xrayMetricsDisabled": "نقطة نهاية مقاييس Xray غير مهيأة", "xrayMetricsHint": "أضف كتلة metrics على المستوى الأعلى في إعدادات xray مع tag باسم metrics_out و listen على 127.0.0.1:11111، ثم أعد تشغيل xray.", "xrayObservatoryEmpty": "لا توجد بيانات Observatory بعد", diff --git a/web/translation/en-US.json b/web/translation/en-US.json index 3fd37bda..b94532ec 100644 --- a/web/translation/en-US.json +++ b/web/translation/en-US.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "An error occurred while running Xray", "operationHours": "Uptime", "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", "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", "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", diff --git a/web/translation/es-ES.json b/web/translation/es-ES.json index 69d6d55d..2436a6c5 100644 --- a/web/translation/es-ES.json +++ b/web/translation/es-ES.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "Se produjo un error al ejecutar Xray", "operationHours": "Tiempo de Funcionamiento", "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", "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", "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", diff --git a/web/translation/fa-IR.json b/web/translation/fa-IR.json index 1dcb3115..131abe31 100644 --- a/web/translation/fa-IR.json +++ b/web/translation/fa-IR.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "خطا در هنگام اجرای Xray رخ داد", "operationHours": "مدت‌کارکرد", "systemHistoryTitle": "تاریخچه سیستم", + "historyTitleCpu": "مصرف پردازنده", + "historyTitleMem": "مصرف حافظه", + "historyTitleNetwork": "پهنای باند شبکه", + "historyTitlePackets": "بسته‌های شبکه", + "historyTitleDisk": "ورودی/خروجی دیسک", + "historyTitleOnline": "کاربران آنلاین", + "historyTitleLoad": "میانگین بار سیستم (۱ / ۵ / ۱۵ دقیقه)", + "historyTabBandwidth": "پهنای باند", + "historyTabPackets": "بسته‌ها", + "historyTabDisk": "Disk I/O", + "historyTabOnline": "آنلاین", + "historyTabLoad": "بار", "charts": "نمودارها", "xrayMetricsTitle": "متریک‌های Xray", + "xrayTitleHeap": "حافظه‌ی Heap تخصیص‌یافته", + "xrayTitleSys": "حافظه‌ی رزروشده از سیستم‌عامل", + "xrayTitleObjects": "اشیای زنده‌ی Heap", + "xrayTitleGcCount": "چرخه‌های کامل‌شده‌ی GC", + "xrayTitleGcPause": "مدت مکث GC", + "xrayTitleObservatory": "سلامت اتصال خروجی", + "xrayTabHeap": "Heap", + "xrayTabSys": "Sys", + "xrayTabObjects": "اشیا", + "xrayTabGcCount": "تعداد GC", + "xrayTabGcPause": "مکث GC", + "xrayTabObservatory": "رصدخانه", "xrayMetricsDisabled": "نقطه پایانی متریک‌های Xray پیکربندی نشده", "xrayMetricsHint": "یک بلاک metrics در سطح بالای پیکربندی xray با tag برابر metrics_out و listen برابر 127.0.0.1:11111 اضافه کنید، سپس xray را راه‌اندازی مجدد کنید.", "xrayObservatoryEmpty": "هنوز داده‌ای از Observatory دریافت نشده", diff --git a/web/translation/id-ID.json b/web/translation/id-ID.json index 4ab5bde0..36c6b700 100644 --- a/web/translation/id-ID.json +++ b/web/translation/id-ID.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "Terjadi kesalahan saat menjalankan Xray", "operationHours": "Waktu Aktif", "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", "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", "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", diff --git a/web/translation/ja-JP.json b/web/translation/ja-JP.json index 7e07dc82..89eafa42 100644 --- a/web/translation/ja-JP.json +++ b/web/translation/ja-JP.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "Xrayの実行中にエラーが発生しました", "operationHours": "システム稼働時間", "systemHistoryTitle": "システム履歴", + "historyTitleCpu": "CPU 使用率", + "historyTitleMem": "メモリ使用率", + "historyTitleNetwork": "ネットワーク帯域幅", + "historyTitlePackets": "ネットワークパケット", + "historyTitleDisk": "ディスク I/O", + "historyTitleOnline": "オンラインクライアント", + "historyTitleLoad": "システム平均負荷(1分 / 5分 / 15分)", + "historyTabBandwidth": "帯域幅", + "historyTabPackets": "パケット", + "historyTabDisk": "ディスク I/O", + "historyTabOnline": "オンライン", + "historyTabLoad": "負荷", "charts": "チャート", "xrayMetricsTitle": "Xray メトリクス", + "xrayTitleHeap": "割り当て済みヒープメモリ", + "xrayTitleSys": "OS から確保したメモリ", + "xrayTitleObjects": "ヒープオブジェクト数", + "xrayTitleGcCount": "完了した GC サイクル", + "xrayTitleGcPause": "GC 一時停止時間", + "xrayTitleObservatory": "アウトバウンド接続の状態", + "xrayTabHeap": "ヒープ", + "xrayTabSys": "Sys", + "xrayTabObjects": "オブジェクト", + "xrayTabGcCount": "GC 回数", + "xrayTabGcPause": "GC 一時停止", + "xrayTabObservatory": "オブザーバトリ", "xrayMetricsDisabled": "Xray メトリクスエンドポイントが設定されていません", "xrayMetricsHint": "xray 設定にトップレベルの metrics ブロック(tag: metrics_out、listen: 127.0.0.1:11111)を追加し、xray を再起動してください。", "xrayObservatoryEmpty": "Observatory データはまだありません", diff --git a/web/translation/pt-BR.json b/web/translation/pt-BR.json index ee86885d..0a358ee7 100644 --- a/web/translation/pt-BR.json +++ b/web/translation/pt-BR.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "Ocorreu um erro ao executar o Xray", "operationHours": "Tempo de Atividade", "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", "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", "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", diff --git a/web/translation/ru-RU.json b/web/translation/ru-RU.json index 4107284a..6cc088bc 100644 --- a/web/translation/ru-RU.json +++ b/web/translation/ru-RU.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "Ошибка при запуске Xray", "operationHours": "Время работы системы", "systemHistoryTitle": "История системы", + "historyTitleCpu": "Загрузка ЦП", + "historyTitleMem": "Использование памяти", + "historyTitleNetwork": "Пропускная способность сети", + "historyTitlePackets": "Сетевые пакеты", + "historyTitleDisk": "Дисковый ввод-вывод", + "historyTitleOnline": "Клиенты онлайн", + "historyTitleLoad": "Средняя нагрузка системы (1 / 5 / 15 мин)", + "historyTabBandwidth": "Пропускная способность", + "historyTabPackets": "Пакеты", + "historyTabDisk": "Диск I/O", + "historyTabOnline": "Онлайн", + "historyTabLoad": "Нагрузка", "charts": "Графики", "xrayMetricsTitle": "Метрики Xray", + "xrayTitleHeap": "Выделенная память кучи", + "xrayTitleSys": "Память, зарезервированная у ОС", + "xrayTitleObjects": "Активные объекты кучи", + "xrayTitleGcCount": "Завершённые циклы GC", + "xrayTitleGcPause": "Длительность паузы GC", + "xrayTitleObservatory": "Состояние исходящих соединений", + "xrayTabHeap": "Куча", + "xrayTabSys": "Sys", + "xrayTabObjects": "Объекты", + "xrayTabGcCount": "Счётчик GC", + "xrayTabGcPause": "Пауза GC", + "xrayTabObservatory": "Обсерватория", "xrayMetricsDisabled": "Конечная точка метрик Xray не настроена", "xrayMetricsHint": "Добавьте блок metrics верхнего уровня в конфигурацию xray с tag metrics_out и listen 127.0.0.1:11111, затем перезапустите xray.", "xrayObservatoryEmpty": "Данных Observatory пока нет", diff --git a/web/translation/tr-TR.json b/web/translation/tr-TR.json index 276cc773..7099e7c9 100644 --- a/web/translation/tr-TR.json +++ b/web/translation/tr-TR.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "Xray çalıştırılırken bir hata oluştu", "operationHours": "Çalışma Süresi", "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", "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ı", "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", diff --git a/web/translation/uk-UA.json b/web/translation/uk-UA.json index 207faf07..dad37697 100644 --- a/web/translation/uk-UA.json +++ b/web/translation/uk-UA.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "Під час роботи Xray сталася помилка", "operationHours": "Час роботи", "systemHistoryTitle": "Історія системи", + "historyTitleCpu": "Завантаження ЦП", + "historyTitleMem": "Використання пам’яті", + "historyTitleNetwork": "Пропускна здатність мережі", + "historyTitlePackets": "Мережеві пакети", + "historyTitleDisk": "Дисковий ввід-вивід", + "historyTitleOnline": "Клієнти онлайн", + "historyTitleLoad": "Середнє навантаження системи (1 / 5 / 15 хв)", + "historyTabBandwidth": "Пропускна здатність", + "historyTabPackets": "Пакети", + "historyTabDisk": "Диск I/O", + "historyTabOnline": "Онлайн", + "historyTabLoad": "Навантаження", "charts": "Графіки", "xrayMetricsTitle": "Метрики Xray", + "xrayTitleHeap": "Виділена пам’ять купи", + "xrayTitleSys": "Пам’ять, зарезервована в ОС", + "xrayTitleObjects": "Активні об’єкти купи", + "xrayTitleGcCount": "Завершені цикли GC", + "xrayTitleGcPause": "Тривалість паузи GC", + "xrayTitleObservatory": "Стан вихідних з’єднань", + "xrayTabHeap": "Купа", + "xrayTabSys": "Sys", + "xrayTabObjects": "Об’єкти", + "xrayTabGcCount": "Лічильник GC", + "xrayTabGcPause": "Пауза GC", + "xrayTabObservatory": "Обсерваторія", "xrayMetricsDisabled": "Кінцева точка метрик Xray не налаштована", "xrayMetricsHint": "Додайте блок metrics верхнього рівня до конфігурації xray з tag metrics_out і listen 127.0.0.1:11111, потім перезапустіть xray.", "xrayObservatoryEmpty": "Даних Observatory ще немає", diff --git a/web/translation/vi-VN.json b/web/translation/vi-VN.json index c3a77e56..c30dfa14 100644 --- a/web/translation/vi-VN.json +++ b/web/translation/vi-VN.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "Đã xảy ra lỗi khi chạy Xray", "operationHours": "Thời gian hoạt độ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 đồ", "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", "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", diff --git a/web/translation/zh-CN.json b/web/translation/zh-CN.json index b95951ce..66523b22 100644 --- a/web/translation/zh-CN.json +++ b/web/translation/zh-CN.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "运行Xray时发生错误", "operationHours": "系统正常运行时间", "systemHistoryTitle": "系统历史", + "historyTitleCpu": "CPU 使用率", + "historyTitleMem": "内存使用率", + "historyTitleNetwork": "网络带宽", + "historyTitlePackets": "网络数据包", + "historyTitleDisk": "磁盘 I/O", + "historyTitleOnline": "在线客户端", + "historyTitleLoad": "系统平均负载(1 分钟 / 5 分钟 / 15 分钟)", + "historyTabBandwidth": "带宽", + "historyTabPackets": "数据包", + "historyTabDisk": "磁盘 I/O", + "historyTabOnline": "在线", + "historyTabLoad": "负载", "charts": "图表", "xrayMetricsTitle": "Xray 指标", + "xrayTitleHeap": "已分配的堆内存", + "xrayTitleSys": "向操作系统保留的内存", + "xrayTitleObjects": "存活的堆对象", + "xrayTitleGcCount": "已完成的 GC 周期", + "xrayTitleGcPause": "GC 暂停时间", + "xrayTitleObservatory": "出站连接健康状态", + "xrayTabHeap": "堆", + "xrayTabSys": "系统", + "xrayTabObjects": "对象", + "xrayTabGcCount": "GC 次数", + "xrayTabGcPause": "GC 暂停", + "xrayTabObservatory": "观测站", "xrayMetricsDisabled": "未配置 Xray 指标端点", "xrayMetricsHint": "在 xray 配置中添加顶级 metrics 块,tag 为 metrics_out,listen 为 127.0.0.1:11111,然后重启 xray。", "xrayObservatoryEmpty": "暂无 Observatory 数据", diff --git a/web/translation/zh-TW.json b/web/translation/zh-TW.json index 424cda61..abe72197 100644 --- a/web/translation/zh-TW.json +++ b/web/translation/zh-TW.json @@ -155,8 +155,32 @@ "xrayErrorPopoverTitle": "執行Xray時發生錯誤", "operationHours": "系統正常執行時間", "systemHistoryTitle": "系統歷史", + "historyTitleCpu": "CPU 使用率", + "historyTitleMem": "記憶體使用率", + "historyTitleNetwork": "網路頻寬", + "historyTitlePackets": "網路封包", + "historyTitleDisk": "磁碟 I/O", + "historyTitleOnline": "線上用戶端", + "historyTitleLoad": "系統平均負載(1 分鐘 / 5 分鐘 / 15 分鐘)", + "historyTabBandwidth": "頻寬", + "historyTabPackets": "封包", + "historyTabDisk": "磁碟 I/O", + "historyTabOnline": "線上", + "historyTabLoad": "負載", "charts": "圖表", "xrayMetricsTitle": "Xray 指標", + "xrayTitleHeap": "已配置的堆積記憶體", + "xrayTitleSys": "向作業系統保留的記憶體", + "xrayTitleObjects": "存活的堆積物件", + "xrayTitleGcCount": "已完成的 GC 週期", + "xrayTitleGcPause": "GC 暫停時間", + "xrayTitleObservatory": "出站連線健康狀態", + "xrayTabHeap": "堆積", + "xrayTabSys": "系統", + "xrayTabObjects": "物件", + "xrayTabGcCount": "GC 次數", + "xrayTabGcPause": "GC 暫停", + "xrayTabObservatory": "觀測站", "xrayMetricsDisabled": "未設定 Xray 指標端點", "xrayMetricsHint": "在 xray 設定中加入頂層 metrics 區塊,tag 為 metrics_out,listen 為 127.0.0.1:11111,然後重啟 xray。", "xrayObservatoryEmpty": "尚無 Observatory 資料",