diff --git a/frontend/src/pages/index/SystemHistoryModal.tsx b/frontend/src/pages/index/SystemHistoryModal.tsx
index 9d433278..59be4d05 100644
--- a/frontend/src/pages/index/SystemHistoryModal.tsx
+++ b/frontend/src/pages/index/SystemHistoryModal.tsx
@@ -3,12 +3,14 @@ import type { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { Modal, Select, Tabs } from 'antd';
import {
+ ApiOutlined,
DashboardOutlined,
DatabaseOutlined,
DeploymentUnitOutlined,
GlobalOutlined,
HddOutlined,
LineChartOutlined,
+ PieChartOutlined,
TeamOutlined,
} from '@ant-design/icons';
@@ -43,11 +45,13 @@ interface MetricDef {
}
const METRICS: MetricDef[] = [
- { 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: 'cpu', tab: 'CPU', tabKey: 'pages.index.cpu', title: 'pages.index.historyTitleCpu', icon: , valueMax: 100, unit: '%', stroke: '' },
+ { key: 'mem', tab: 'RAM', tabKey: 'pages.index.memory', title: 'pages.index.historyTitleMem', icon: , valueMax: 100, unit: '%', stroke: '#7c4dff', key2: 'swap', stroke2: '#ffa940', name1: 'pages.index.memory', name2: 'pages.index.swap' },
{ 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: 'tcpCount', tab: 'Connections', tabKey: 'pages.index.historyTabConnections', title: 'pages.index.historyTitleConnections', icon: , valueMax: null, unit: '', stroke: '#597ef7', key2: 'udpCount', stroke2: '#73d13d', name1: 'TCP', name2: 'UDP' },
{ 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: 'diskUsage', tab: 'Disk Usage', tabKey: 'pages.index.historyTabDiskUsage', title: 'pages.index.historyTitleDiskUsage', icon: , valueMax: 100, unit: '%', stroke: '#13c2c2' },
{ 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' },
];
@@ -64,7 +68,9 @@ function unitFormatter(unit: string, activeKey: string): (v: number) => string {
}
return (v) => {
const n = Number(v) || 0;
- if (activeKey === 'online') return String(Math.round(n));
+ if (activeKey === 'online' || activeKey === 'tcpCount' || activeKey === 'udpCount') {
+ return Math.round(n).toLocaleString();
+ }
return n.toFixed(2);
};
}
@@ -97,6 +103,7 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
const [timestamps, setTimestamps] = useState([]);
const activeMetric = useMemo(() => METRICS.find((m) => m.key === activeKey), [activeKey]);
+ const trName = (n?: string) => (n && n.startsWith('pages.') ? t(n) : n);
const strokeColor = activeMetric?.stroke || status?.cpu?.color || '#008771';
const yFormatter = useMemo(
() => unitFormatter(activeMetric?.unit ?? '', activeKey),
@@ -178,6 +185,13 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
if (open) fetchBucket();
}, [open, activeKey, bucket, fetchBucket]);
+ useEffect(() => {
+ if (!open) return undefined;
+ const ms = bucket <= 30 ? 2000 : 10000;
+ const id = window.setInterval(() => fetchBucket(), ms);
+ return () => window.clearInterval(id);
+ }, [open, bucket, fetchBucket]);
+
return (
metricCapacityDefault {
+ v = v[len(v)-metricCapacityDefault:]
+ }
+ h.metrics[k] = v
+ }
+}
+
// aggregate returns up to maxPoints buckets of size bucketSeconds,
// each bucket carrying the arithmetic mean of the underlying samples.
// Bucket alignment is to absolute Unix-second boundaries so two
@@ -137,7 +171,7 @@ var (
// status sample. Exposed for documentation/test purposes; the
// controller validates incoming names against an allow-list.
var SystemMetricKeys = []string{
- "cpu", "mem", "netUp", "netDown", "pktUp", "pktDown", "diskRead", "diskWrite", "online", "load1", "load5", "load15",
+ "cpu", "mem", "swap", "netUp", "netDown", "pktUp", "pktDown", "diskRead", "diskWrite", "diskUsage", "tcpCount", "udpCount", "online", "load1", "load5", "load15",
}
// NodeMetricKeys lists the per-node metric names NodeHeartbeatJob writes.
@@ -150,3 +184,54 @@ var NodeMetricKeys = []string{"cpu", "mem"}
var XrayMetricKeys = []string{
"xrAlloc", "xrSys", "xrHeapObjects", "xrNumGC", "xrPauseNs",
}
+
+// systemMetricsStorePath is where the host time-series is persisted between
+// restarts. It lives next to the database so a single volume mount carries
+// both. Only systemMetrics is persisted — node and xray series are cheap to
+// rebuild and tied to live connections.
+func systemMetricsStorePath() string {
+ return filepath.Join(config.GetDBFolderPath(), "system_metrics.gob")
+}
+
+// PersistSystemMetrics writes the host time-series to disk via a temp file +
+// rename so a crash mid-write can't corrupt the previous snapshot. Called on a
+// timer and at shutdown.
+func PersistSystemMetrics() error {
+ path := systemMetricsStorePath()
+ tmp := path + ".tmp"
+ f, err := os.Create(tmp)
+ if err != nil {
+ return err
+ }
+ if err := gob.NewEncoder(f).Encode(systemMetrics.snapshot()); err != nil {
+ f.Close()
+ os.Remove(tmp)
+ return err
+ }
+ if err := f.Close(); err != nil {
+ os.Remove(tmp)
+ return err
+ }
+ return os.Rename(tmp, path)
+}
+
+// RestoreSystemMetrics loads a previously persisted host time-series on startup.
+// A missing file is not an error (first boot). Aggregation already windows by
+// time, so any gap from downtime is handled by the readers.
+func RestoreSystemMetrics() {
+ path := systemMetricsStorePath()
+ f, err := os.Open(path)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ logger.Warning("restore system metrics failed:", err)
+ }
+ return
+ }
+ defer f.Close()
+ var data map[string][]MetricSample
+ if err := gob.NewDecoder(f).Decode(&data); err != nil {
+ logger.Warning("decode system metrics failed:", err)
+ return
+ }
+ systemMetrics.restore(data)
+}
diff --git a/web/service/server.go b/web/service/server.go
index 08e3a7d4..dcbe5e51 100644
--- a/web/service/server.go
+++ b/web/service/server.go
@@ -565,12 +565,22 @@ func (s *ServerService) AppendStatusSample(t time.Time, status *Status) {
if status.Mem.Total > 0 {
systemMetrics.append("mem", t, float64(status.Mem.Current)*100.0/float64(status.Mem.Total))
}
+ if status.Swap.Total > 0 {
+ systemMetrics.append("swap", t, float64(status.Swap.Current)*100.0/float64(status.Swap.Total))
+ } else {
+ systemMetrics.append("swap", t, 0)
+ }
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))
+ if status.Disk.Total > 0 {
+ systemMetrics.append("diskUsage", t, float64(status.Disk.Current)*100.0/float64(status.Disk.Total))
+ }
systemMetrics.append("pktUp", t, float64(status.NetIO.PktUp))
systemMetrics.append("pktDown", t, float64(status.NetIO.PktDown))
+ systemMetrics.append("tcpCount", t, float64(status.TcpCount))
+ systemMetrics.append("udpCount", t, float64(status.UdpCount))
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 55857ee5..84865320 100644
--- a/web/translation/ar-EG.json
+++ b/web/translation/ar-EG.json
@@ -128,12 +128,12 @@
},
"index": {
"title": "نظرة عامة",
- "cpu": "CPU",
+ "cpu": "المعالج",
"logicalProcessors": "المعالجات المنطقية",
"frequency": "التردد",
- "swap": "Swap",
+ "swap": "التبديل",
"storage": "تخزين",
- "memory": "RAM",
+ "memory": "الذاكرة",
"threads": "خيوط",
"xrayStatus": "Xray",
"stopXray": "إيقاف",
@@ -162,11 +162,15 @@
"historyTitleDisk": "إدخال/إخراج القرص",
"historyTitleOnline": "العملاء المتصلون",
"historyTitleLoad": "متوسط حمل النظام (1 / 5 / 15 دقيقة)",
+ "historyTitleConnections": "الاتصالات النشطة (TCP / UDP)",
+ "historyTitleDiskUsage": "استخدام مساحة القرص",
"historyTabBandwidth": "عرض النطاق",
"historyTabPackets": "الحزم",
- "historyTabDisk": "Disk I/O",
+ "historyTabDisk": "قرص I/O",
"historyTabOnline": "متصل",
"historyTabLoad": "الحِمل",
+ "historyTabConnections": "الاتصالات",
+ "historyTabDiskUsage": "استخدام القرص",
"charts": "الرسوم البيانية",
"xrayMetricsTitle": "مقاييس Xray",
"xrayTitleHeap": "ذاكرة الكومة المخصصة",
diff --git a/web/translation/en-US.json b/web/translation/en-US.json
index b94532ec..34b67d3b 100644
--- a/web/translation/en-US.json
+++ b/web/translation/en-US.json
@@ -162,11 +162,15 @@
"historyTitleDisk": "Disk I/O",
"historyTitleOnline": "Online Clients",
"historyTitleLoad": "System Load Average (1m / 5m / 15m)",
+ "historyTitleConnections": "Active Connections (TCP / UDP)",
+ "historyTitleDiskUsage": "Disk Space Usage",
"historyTabBandwidth": "Bandwidth",
"historyTabPackets": "Packets",
"historyTabDisk": "Disk I/O",
"historyTabOnline": "Online",
"historyTabLoad": "Load",
+ "historyTabConnections": "Connections",
+ "historyTabDiskUsage": "Disk Usage",
"charts": "Charts",
"xrayMetricsTitle": "Xray Metrics",
"xrayTitleHeap": "Allocated Heap Memory",
diff --git a/web/translation/es-ES.json b/web/translation/es-ES.json
index 2436a6c5..4d757f01 100644
--- a/web/translation/es-ES.json
+++ b/web/translation/es-ES.json
@@ -133,7 +133,7 @@
"frequency": "Frecuencia",
"swap": "Swap",
"storage": "Almacenamiento",
- "memory": "RAM",
+ "memory": "Memoria",
"threads": "Hilos",
"xrayStatus": "Xray",
"stopXray": "Detener",
@@ -162,11 +162,15 @@
"historyTitleDisk": "E/S de Disco",
"historyTitleOnline": "Clientes en Línea",
"historyTitleLoad": "Carga Media del Sistema (1 / 5 / 15 min)",
+ "historyTitleConnections": "Conexiones Activas (TCP / UDP)",
+ "historyTitleDiskUsage": "Uso del Espacio en Disco",
"historyTabBandwidth": "Ancho de Banda",
"historyTabPackets": "Paquetes",
"historyTabDisk": "Disco I/O",
"historyTabOnline": "En línea",
"historyTabLoad": "Carga",
+ "historyTabConnections": "Conexiones",
+ "historyTabDiskUsage": "Uso de Disco",
"charts": "Gráficos",
"xrayMetricsTitle": "Métricas de Xray",
"xrayTitleHeap": "Memoria Heap Asignada",
diff --git a/web/translation/fa-IR.json b/web/translation/fa-IR.json
index 131abe31..e316ec33 100644
--- a/web/translation/fa-IR.json
+++ b/web/translation/fa-IR.json
@@ -128,12 +128,12 @@
},
"index": {
"title": "نمای کلی",
- "cpu": "CPU",
+ "cpu": "پردازنده",
"logicalProcessors": "پردازندههای منطقی",
"frequency": "فرکانس",
- "swap": "Swap",
+ "swap": "سواپ",
"storage": "ذخیرهسازی",
- "memory": "RAM",
+ "memory": "حافظه",
"threads": "نخها",
"xrayStatus": "Xray",
"stopXray": "توقف",
@@ -162,11 +162,15 @@
"historyTitleDisk": "ورودی/خروجی دیسک",
"historyTitleOnline": "کاربران آنلاین",
"historyTitleLoad": "میانگین بار سیستم (۱ / ۵ / ۱۵ دقیقه)",
+ "historyTitleConnections": "اتصالات فعال (TCP / UDP)",
+ "historyTitleDiskUsage": "مصرف فضای دیسک",
"historyTabBandwidth": "پهنای باند",
"historyTabPackets": "بستهها",
- "historyTabDisk": "Disk I/O",
+ "historyTabDisk": "دیسک I/O",
"historyTabOnline": "آنلاین",
"historyTabLoad": "بار",
+ "historyTabConnections": "اتصالات",
+ "historyTabDiskUsage": "مصرف دیسک",
"charts": "نمودارها",
"xrayMetricsTitle": "متریکهای Xray",
"xrayTitleHeap": "حافظهی Heap تخصیصیافته",
diff --git a/web/translation/id-ID.json b/web/translation/id-ID.json
index 36c6b700..eb3dcc38 100644
--- a/web/translation/id-ID.json
+++ b/web/translation/id-ID.json
@@ -133,7 +133,7 @@
"frequency": "Frekuensi",
"swap": "Swap",
"storage": "Penyimpanan",
- "memory": "RAM",
+ "memory": "Memori",
"threads": "Thread",
"xrayStatus": "Xray",
"stopXray": "Hentikan",
@@ -162,11 +162,15 @@
"historyTitleDisk": "I/O Disk",
"historyTitleOnline": "Klien Online",
"historyTitleLoad": "Rata-rata Beban Sistem (1 / 5 / 15 mnt)",
+ "historyTitleConnections": "Koneksi Aktif (TCP / UDP)",
+ "historyTitleDiskUsage": "Penggunaan Ruang Disk",
"historyTabBandwidth": "Bandwidth",
"historyTabPackets": "Paket",
"historyTabDisk": "Disk I/O",
"historyTabOnline": "Online",
"historyTabLoad": "Beban",
+ "historyTabConnections": "Koneksi",
+ "historyTabDiskUsage": "Penggunaan Disk",
"charts": "Grafik",
"xrayMetricsTitle": "Metrik Xray",
"xrayTitleHeap": "Memori Heap Teralokasi",
diff --git a/web/translation/ja-JP.json b/web/translation/ja-JP.json
index 89eafa42..3d2e6217 100644
--- a/web/translation/ja-JP.json
+++ b/web/translation/ja-JP.json
@@ -131,9 +131,9 @@
"cpu": "CPU",
"logicalProcessors": "論理プロセッサ",
"frequency": "周波数",
- "swap": "Swap",
+ "swap": "スワップ",
"storage": "ストレージ",
- "memory": "RAM",
+ "memory": "メモリ",
"threads": "スレッド",
"xrayStatus": "Xray",
"stopXray": "停止",
@@ -162,11 +162,15 @@
"historyTitleDisk": "ディスク I/O",
"historyTitleOnline": "オンラインクライアント",
"historyTitleLoad": "システム平均負荷(1分 / 5分 / 15分)",
+ "historyTitleConnections": "アクティブな接続 (TCP / UDP)",
+ "historyTitleDiskUsage": "ディスク使用率",
"historyTabBandwidth": "帯域幅",
"historyTabPackets": "パケット",
"historyTabDisk": "ディスク I/O",
"historyTabOnline": "オンライン",
"historyTabLoad": "負荷",
+ "historyTabConnections": "接続数",
+ "historyTabDiskUsage": "ディスク使用量",
"charts": "チャート",
"xrayMetricsTitle": "Xray メトリクス",
"xrayTitleHeap": "割り当て済みヒープメモリ",
diff --git a/web/translation/pt-BR.json b/web/translation/pt-BR.json
index 0a358ee7..2f0540e7 100644
--- a/web/translation/pt-BR.json
+++ b/web/translation/pt-BR.json
@@ -133,7 +133,7 @@
"frequency": "Frequência",
"swap": "Swap",
"storage": "Armazenamento",
- "memory": "RAM",
+ "memory": "Memória",
"threads": "Threads",
"xrayStatus": "Xray",
"stopXray": "Parar",
@@ -162,11 +162,15 @@
"historyTitleDisk": "E/S de Disco",
"historyTitleOnline": "Clientes Online",
"historyTitleLoad": "Média de Carga do Sistema (1 / 5 / 15 min)",
+ "historyTitleConnections": "Conexões Ativas (TCP / UDP)",
+ "historyTitleDiskUsage": "Uso do Espaço em Disco",
"historyTabBandwidth": "Largura de Banda",
"historyTabPackets": "Pacotes",
"historyTabDisk": "Disco I/O",
"historyTabOnline": "Online",
"historyTabLoad": "Carga",
+ "historyTabConnections": "Conexões",
+ "historyTabDiskUsage": "Uso de Disco",
"charts": "Gráficos",
"xrayMetricsTitle": "Métricas do Xray",
"xrayTitleHeap": "Memória Heap Alocada",
diff --git a/web/translation/ru-RU.json b/web/translation/ru-RU.json
index 6cc088bc..43d341b1 100644
--- a/web/translation/ru-RU.json
+++ b/web/translation/ru-RU.json
@@ -128,12 +128,12 @@
},
"index": {
"title": "Дашборд",
- "cpu": "CPU",
+ "cpu": "ЦП",
"logicalProcessors": "Логические процессоры",
"frequency": "Частота",
- "swap": "Swap",
+ "swap": "Подкачка",
"storage": "Диск",
- "memory": "RAM",
+ "memory": "Память",
"threads": "Потоки",
"xrayStatus": "Xray",
"stopXray": "Стоп",
@@ -162,11 +162,15 @@
"historyTitleDisk": "Дисковый ввод-вывод",
"historyTitleOnline": "Клиенты онлайн",
"historyTitleLoad": "Средняя нагрузка системы (1 / 5 / 15 мин)",
+ "historyTitleConnections": "Активные соединения (TCP / UDP)",
+ "historyTitleDiskUsage": "Использование дискового пространства",
"historyTabBandwidth": "Пропускная способность",
"historyTabPackets": "Пакеты",
"historyTabDisk": "Диск I/O",
"historyTabOnline": "Онлайн",
"historyTabLoad": "Нагрузка",
+ "historyTabConnections": "Соединения",
+ "historyTabDiskUsage": "Использование диска",
"charts": "Графики",
"xrayMetricsTitle": "Метрики Xray",
"xrayTitleHeap": "Выделенная память кучи",
diff --git a/web/translation/tr-TR.json b/web/translation/tr-TR.json
index 7099e7c9..d3c1a084 100644
--- a/web/translation/tr-TR.json
+++ b/web/translation/tr-TR.json
@@ -131,9 +131,9 @@
"cpu": "CPU",
"logicalProcessors": "Mantıksal işlemciler",
"frequency": "Frekans",
- "swap": "Swap",
+ "swap": "Takas",
"storage": "Depolama",
- "memory": "RAM",
+ "memory": "Bellek",
"threads": "İş parçacığı",
"xrayStatus": "Xray",
"stopXray": "Durdur",
@@ -162,11 +162,15 @@
"historyTitleDisk": "Disk G/Ç",
"historyTitleOnline": "Çevrimiçi İstemciler",
"historyTitleLoad": "Sistem Yük Ortalaması (1d / 5d / 15d)",
+ "historyTitleConnections": "Etkin Bağlantılar (TCP / UDP)",
+ "historyTitleDiskUsage": "Disk Alanı Kullanımı",
"historyTabBandwidth": "Bant Genişliği",
"historyTabPackets": "Paketler",
"historyTabDisk": "Disk G/Ç",
"historyTabOnline": "Çevrimiçi",
"historyTabLoad": "Yük",
+ "historyTabConnections": "Bağlantılar",
+ "historyTabDiskUsage": "Disk Kullanımı",
"charts": "Grafikler",
"xrayMetricsTitle": "Xray Metrikleri",
"xrayTitleHeap": "Ayrılan Yığın Belleği",
diff --git a/web/translation/uk-UA.json b/web/translation/uk-UA.json
index dad37697..a86ffbe5 100644
--- a/web/translation/uk-UA.json
+++ b/web/translation/uk-UA.json
@@ -128,12 +128,12 @@
},
"index": {
"title": "Огляд",
- "cpu": "CPU",
+ "cpu": "ЦП",
"logicalProcessors": "Логічні процесори",
"frequency": "Частота",
- "swap": "Swap",
+ "swap": "Підкачка",
"storage": "Сховище",
- "memory": "RAM",
+ "memory": "Пам’ять",
"threads": "Потоки",
"xrayStatus": "Xray",
"stopXray": "Стоп",
@@ -162,11 +162,15 @@
"historyTitleDisk": "Дисковий ввід-вивід",
"historyTitleOnline": "Клієнти онлайн",
"historyTitleLoad": "Середнє навантаження системи (1 / 5 / 15 хв)",
+ "historyTitleConnections": "Активні з’єднання (TCP / UDP)",
+ "historyTitleDiskUsage": "Використання дискового простору",
"historyTabBandwidth": "Пропускна здатність",
"historyTabPackets": "Пакети",
"historyTabDisk": "Диск I/O",
"historyTabOnline": "Онлайн",
"historyTabLoad": "Навантаження",
+ "historyTabConnections": "З’єднання",
+ "historyTabDiskUsage": "Використання диска",
"charts": "Графіки",
"xrayMetricsTitle": "Метрики Xray",
"xrayTitleHeap": "Виділена пам’ять купи",
diff --git a/web/translation/vi-VN.json b/web/translation/vi-VN.json
index c30dfa14..1daf4967 100644
--- a/web/translation/vi-VN.json
+++ b/web/translation/vi-VN.json
@@ -133,7 +133,7 @@
"frequency": "Tần số",
"swap": "Swap",
"storage": "Lưu trữ",
- "memory": "RAM",
+ "memory": "Bộ nhớ",
"threads": "Luồng",
"xrayStatus": "Xray",
"stopXray": "Dừng",
@@ -162,11 +162,15 @@
"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)",
+ "historyTitleConnections": "Kết nối đang hoạt động (TCP / UDP)",
+ "historyTitleDiskUsage": "Sử dụng dung lượng đĩa",
"historyTabBandwidth": "Băng thông",
"historyTabPackets": "Gói tin",
"historyTabDisk": "Đĩa I/O",
"historyTabOnline": "Trực tuyến",
"historyTabLoad": "Tải",
+ "historyTabConnections": "Kết nối",
+ "historyTabDiskUsage": "Sử dụng đĩa",
"charts": "Biểu đồ",
"xrayMetricsTitle": "Chỉ số Xray",
"xrayTitleHeap": "Bộ nhớ Heap đã cấp phát",
diff --git a/web/translation/zh-CN.json b/web/translation/zh-CN.json
index 66523b22..0e0f6295 100644
--- a/web/translation/zh-CN.json
+++ b/web/translation/zh-CN.json
@@ -131,9 +131,9 @@
"cpu": "CPU",
"logicalProcessors": "逻辑处理器",
"frequency": "频率",
- "swap": "Swap",
+ "swap": "交换空间",
"storage": "存储",
- "memory": "RAM",
+ "memory": "内存",
"threads": "线程",
"xrayStatus": "Xray",
"stopXray": "停止",
@@ -162,11 +162,15 @@
"historyTitleDisk": "磁盘 I/O",
"historyTitleOnline": "在线客户端",
"historyTitleLoad": "系统平均负载(1 分钟 / 5 分钟 / 15 分钟)",
+ "historyTitleConnections": "活动连接 (TCP / UDP)",
+ "historyTitleDiskUsage": "磁盘空间使用率",
"historyTabBandwidth": "带宽",
"historyTabPackets": "数据包",
"historyTabDisk": "磁盘 I/O",
"historyTabOnline": "在线",
"historyTabLoad": "负载",
+ "historyTabConnections": "连接数",
+ "historyTabDiskUsage": "磁盘使用量",
"charts": "图表",
"xrayMetricsTitle": "Xray 指标",
"xrayTitleHeap": "已分配的堆内存",
diff --git a/web/translation/zh-TW.json b/web/translation/zh-TW.json
index abe72197..9aed7b5e 100644
--- a/web/translation/zh-TW.json
+++ b/web/translation/zh-TW.json
@@ -131,9 +131,9 @@
"cpu": "CPU",
"logicalProcessors": "邏輯處理器",
"frequency": "頻率",
- "swap": "Swap",
+ "swap": "交換空間",
"storage": "儲存",
- "memory": "RAM",
+ "memory": "記憶體",
"threads": "執行緒",
"xrayStatus": "Xray",
"stopXray": "停止",
@@ -162,11 +162,15 @@
"historyTitleDisk": "磁碟 I/O",
"historyTitleOnline": "線上用戶端",
"historyTitleLoad": "系統平均負載(1 分鐘 / 5 分鐘 / 15 分鐘)",
+ "historyTitleConnections": "使用中的連線 (TCP / UDP)",
+ "historyTitleDiskUsage": "磁碟空間使用率",
"historyTabBandwidth": "頻寬",
"historyTabPackets": "封包",
"historyTabDisk": "磁碟 I/O",
"historyTabOnline": "線上",
"historyTabLoad": "負載",
+ "historyTabConnections": "連線數",
+ "historyTabDiskUsage": "磁碟使用量",
"charts": "圖表",
"xrayMetricsTitle": "Xray 指標",
"xrayTitleHeap": "已配置的堆積記憶體",
diff --git a/web/web.go b/web/web.go
index ca990a3e..c4caa596 100644
--- a/web/web.go
+++ b/web/web.go
@@ -469,6 +469,9 @@ func (s *Server) stop(stopXray bool, stopTgBot bool) error {
if s.cron != nil {
s.cron.Stop()
}
+ if err := service.PersistSystemMetrics(); err != nil {
+ logger.Warning("persist system metrics on shutdown failed:", err)
+ }
if stopXray {
service.StopTrafficWriter()
}