Node status

This commit is contained in:
2025-12-27 14:03:51 +01:00
parent eed4a0e438
commit 1ab87edacd
5 changed files with 121 additions and 15 deletions
+27
View File
@@ -26,6 +26,7 @@
"react": "19.2.3", "react": "19.2.3",
"react-dom": "19.2.3", "react-dom": "19.2.3",
"recharts": "^2.15.4", "recharts": "^2.15.4",
"systeminformation": "^5.28.2",
"tailwind-merge": "^3.4.0" "tailwind-merge": "^3.4.0"
}, },
"devDependencies": { "devDependencies": {
@@ -3982,6 +3983,32 @@
} }
} }
}, },
"node_modules/systeminformation": {
"version": "5.28.2",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.28.2.tgz",
"integrity": "sha512-AWrPnmbIcfjLX7wiQMd7r6bl3PDaGoqvrfn8pCLJbk9CiB+vQXTk8RVxNs2vQRTezYlhb9skxEIyAPTm1If5bQ==",
"license": "MIT",
"os": [
"darwin",
"linux",
"win32",
"freebsd",
"openbsd",
"netbsd",
"sunos",
"android"
],
"bin": {
"systeminformation": "lib/cli.js"
},
"engines": {
"node": ">=8.0.0"
},
"funding": {
"type": "Buy me a coffee",
"url": "https://www.buymeacoffee.com/systeminfo"
}
},
"node_modules/tailwind-merge": { "node_modules/tailwind-merge": {
"version": "3.4.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
+1
View File
@@ -26,6 +26,7 @@
"react": "19.2.3", "react": "19.2.3",
"react-dom": "19.2.3", "react-dom": "19.2.3",
"recharts": "^2.15.4", "recharts": "^2.15.4",
"systeminformation": "^5.28.2",
"tailwind-merge": "^3.4.0" "tailwind-merge": "^3.4.0"
}, },
"devDependencies": { "devDependencies": {
+52
View File
@@ -0,0 +1,52 @@
import { NextRequest, NextResponse } from "next/server";
import testAuth from "../auth";
import { StatusCodes } from "http-status-codes";
import si from "systeminformation";
export async function GET(req: NextRequest): Promise<NextResponse> {
const auth = await testAuth(req);
if (auth) {
return NextResponse.json(
{ message: auth },
{ status: StatusCodes.UNAUTHORIZED }
);
}
const [cpu, mem, load, disk, network] = await Promise.all([
si.cpu(),
si.mem(),
si.currentLoad(),
si.fsSize(),
si.networkStats(),
]);
return NextResponse.json({
message: "ok",
liveData: {
cpu: {
manufacturer: cpu.manufacturer,
brand: cpu.brand,
cores: cpu.cores,
usage: load.currentLoad,
},
memory: {
total: mem.total,
used: mem.used,
free: mem.free,
usage: ((mem.total - mem.free) / mem.total) * 100,
},
disk: disk.map((d) => ({
fs: d.fs,
size: d.size,
used: d.used,
usage: d.use,
})),
network: network.map((n) => ({
iface: n.iface,
rx: n.rx_bytes,
tx: n.tx_bytes,
})),
},
});
}
+18 -10
View File
@@ -87,13 +87,19 @@ const applications = [
memory: 48, memory: 48,
status: "pending" as const, status: "pending" as const,
}, },
]; ].slice(-5);
export default function OverviewPage() { export default function OverviewPage() {
const [nodes, setNodes] = useState<NodeLiveData[]>([]); const [nodes, setNodes] = useState<NodeLiveData[]>([]);
useEffect(() => { useEffect(() => {
getLiveNodes().then(setNodes); const fetchNodes = async () => {
const nodes = await getLiveNodes();
setNodes(nodes);
};
fetchNodes();
const interval = setInterval(fetchNodes, 3000);
return () => clearInterval(interval);
}, []); }, []);
return ( return (
@@ -228,7 +234,7 @@ export default function OverviewPage() {
> >
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-3 gap-2"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-3 gap-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<StatusIndicator status={node.liveData.status} /> <StatusIndicator status={"running"} />
<span className="font-mono text-sm">{node.name}</span> <span className="font-mono text-sm">{node.name}</span>
</div> </div>
<span className="text-xs text-muted-foreground font-mono"> <span className="text-xs text-muted-foreground font-mono">
@@ -238,25 +244,27 @@ export default function OverviewPage() {
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between text-xs"> <div className="flex items-center justify-between text-xs">
<span className="text-muted-foreground">CPU</span> <span className="text-muted-foreground">CPU</span>
<span className="font-mono">{node.liveData.cpu}%</span> <span className="font-mono">
{node.liveData.cpu.usage.toFixed(0)}%
</span>
</div> </div>
<Progress <Progress
value={node.liveData.cpu} value={node.liveData.cpu.usage}
className="[&>div]:bg-chart-1" className="[&>div]:bg-chart-1"
/> />
<div className="flex items-center justify-between text-xs"> <div className="flex items-center justify-between text-xs">
<span className="text-muted-foreground">Memory</span> <span className="text-muted-foreground">Memory</span>
<span className="font-mono">{node.liveData.memory}%</span> <span className="font-mono">
{node.liveData.memory.usage.toFixed(0)}%
</span>
</div> </div>
<Progress <Progress
value={node.liveData.memory} value={node.liveData.memory.usage}
className="w-full [&>div]:bg-chart-3" className="w-full [&>div]:bg-chart-3"
/> />
<div className="flex items-center justify-between text-xs pt-2 border-t border-border mt-2"> <div className="flex items-center justify-between text-xs pt-2 border-t border-border mt-2">
<span className="text-muted-foreground">Containers</span> <span className="text-muted-foreground">Containers</span>
<span className="font-mono"> <span className="font-mono">{6}</span>
{node.liveData.containers}
</span>
</div> </div>
</div> </div>
</div> </div>
+23 -5
View File
@@ -11,10 +11,28 @@ export interface NodeLiveData {
ip: string; ip: string;
key: string; key: string;
liveData: { liveData: {
status: "running" | "pending" | "error"; cpu: {
cpu: number; manufacturer: string;
memory: number; brand: string;
disk: number; cores: number;
containers: number; usage: number;
};
memory: {
total: number;
used: number;
free: number;
usage: number;
};
disk: {
fs: string;
size: number;
used: number;
usage: number;
}[];
network: {
iface: string;
rx: number;
tx: number;
}[];
}; };
} }