fix: WireGuard client filter, feat: system monitor storage and total ram
This commit is contained in:
parent
e48fef3154
commit
497482aef3
3 changed files with 28 additions and 5 deletions
|
|
@ -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
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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} />
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue