fix: WireGuard client filter, feat: system monitor storage and total ram

This commit is contained in:
xtcnet 2026-03-08 20:49:00 +07:00
parent e48fef3154
commit 497482aef3
3 changed files with 28 additions and 5 deletions

View file

@ -41,18 +41,29 @@ router
.all(jwtdecode()) .all(jwtdecode())
.get(async (req, res, next) => { .get(async (req, res, next) => {
try { try {
const [cpuTotal, memData, networkStats] = await Promise.all([ const [cpuTotal, memData, networkStats, fsData] = await Promise.all([
si.currentLoad(), si.currentLoad(),
si.mem(), si.mem(),
si.networkStats("*"), si.networkStats("*"),
si.fsSize()
]); ]);
// Grab eth0 or the first active interface // Grab eth0 or the first active interface
const activeNet = networkStats.find(n => n.operstate === 'up' && n.iface !== 'lo') || networkStats[0] || {}; const activeNet = networkStats.find(n => n.operstate === 'up' && n.iface !== 'lo') || networkStats[0] || {};
// Summarize all detected physical drives to find total storage and used storage
const totalStorage = fsData.reduce((acc, disk) => acc + (disk.size || 0), 0);
const usedStorage = fsData.reduce((acc, disk) => acc + (disk.used || 0), 0);
const storagePercent = totalStorage > 0 ? Math.round((usedStorage / totalStorage) * 100) : 0;
res.status(200).json({ res.status(200).json({
cpu: Math.round(cpuTotal.currentLoad), cpu: Math.round(cpuTotal.currentLoad),
memory: Math.round((memData.active / memData.total) * 100), memory: Math.round((memData.active / memData.total) * 100),
memoryTotal: memData.total,
memoryActive: memData.active,
storage: storagePercent,
storageTotal: totalStorage,
storageUsed: usedStorage,
networkRx: (activeNet.rx_sec / 1024 / 1024 * 8).toFixed(2), // Mbps networkRx: (activeNet.rx_sec / 1024 / 1024 * 8).toFixed(2), // Mbps
networkTx: (activeNet.tx_sec / 1024 / 1024 * 8).toFixed(2), // Mbps networkTx: (activeNet.tx_sec / 1024 / 1024 * 8).toFixed(2), // Mbps
}); });

View file

@ -1,11 +1,16 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { IconCpu, IconServer, IconArrowsDownUp } from "@tabler/icons-react"; import { IconCpu, IconServer, IconArrowsDownUp, IconDatabase } from "@tabler/icons-react";
import * as api from "../api/backend/base"; import * as api from "../api/backend/base";
export function SiteFooter() { export function SiteFooter() {
const [sysStats, setSysStats] = useState({ const [sysStats, setSysStats] = useState({
cpu: 0, cpu: 0,
memory: 0, memory: 0,
memoryTotal: 0,
memoryActive: 0,
storage: 0,
storageTotal: 0,
storageUsed: 0,
networkRx: "0.00", networkRx: "0.00",
networkTx: "0.00" networkTx: "0.00"
}); });
@ -35,6 +40,9 @@ export function SiteFooter() {
}; };
}, []); }, []);
// Convert bytes to GB string
const formatGB = (bytes: number) => (bytes / 1024 / 1024 / 1024).toFixed(1);
return ( return (
<footer className="footer d-print-none py-3"> <footer className="footer d-print-none py-3">
<div className="container-xl"> <div className="container-xl">
@ -44,9 +52,13 @@ export function SiteFooter() {
<IconCpu size={16} /> <IconCpu size={16} />
<span>{sysStats.cpu}%</span> <span>{sysStats.cpu}%</span>
</div> </div>
<div title="Memory Usage" className="d-flex align-items-center gap-1"> <div title={`Memory Usage (${formatGB(sysStats.memoryActive)}GB / ${formatGB(sysStats.memoryTotal)}GB)`} className="d-flex align-items-center gap-1">
<IconServer size={16} /> <IconServer size={16} />
<span>{sysStats.memory}%</span> <span>{sysStats.memory}% of {Math.round(sysStats.memoryTotal / 1024 / 1024 / 1024)}GB</span>
</div>
<div title={`Storage Usage (${formatGB(sysStats.storageUsed)}GB / ${formatGB(sysStats.storageTotal)}GB)`} className="d-flex align-items-center gap-1">
<IconDatabase size={16} />
<span>Free {formatGB(sysStats.storageTotal - sysStats.storageUsed)}GB</span>
</div> </div>
<div title="Network Bandwidth" className="d-flex align-items-center gap-1"> <div title="Network Bandwidth" className="d-flex align-items-center gap-1">
<IconArrowsDownUp size={16} /> <IconArrowsDownUp size={16} />

View file

@ -80,7 +80,7 @@ function WireGuard() {
const filteredClients = clients?.filter((c: any) => { const filteredClients = clients?.filter((c: any) => {
// Filter by selected server // Filter by selected server
const cInterfaceId = c.interfaceId || c.interface_id; const cInterfaceId = c.interfaceId || c.interface_id;
if (selectedServerId !== "all" && cInterfaceId !== selectedServerId) { if (selectedServerId !== "all" && Number(cInterfaceId) !== selectedServerId) {
return false; return false;
} }
// Filter by search text // Filter by search text