live monitoring

This commit is contained in:
2025-07-17 10:40:36 -05:00
parent db178f2a23
commit 6b91411de5
4 changed files with 159 additions and 30 deletions
+69 -11
View File
@@ -4,7 +4,54 @@ import Server, { ServerAPI } from "@/types/server";
import { HttpStatusCode } from "axios";
import { validate } from "../auth/route";
const useServer = (server: ServerAPI, index: number) => {
const ws = new WebSocket(`ws://${server.ip}:5273`);
ws.onmessage = (msg) => {
try {
if (deletes.has(index) || !currentServers[index]) {
ws.close();
return;
}
if (msg.data === "Success") {return}
const data = JSON.parse(msg.data);
if (data.status) {
currentServers[index].status = data.status;
}
if (data.cpu) {
currentServers[index].cpu = data.cpu;
}
if (data.memory) {
currentServers[index].memory = data.memory;
}
if (data.disk) {
currentServers[index].storage = data.disk;
}
if (data.network) {
currentServers[index].network = data.network;
}
} catch (e) {
console.error("Error parsing WebSocket message:", e);
}
};
ws.onopen = () => ws.send("my-key");
};
const deletes = new Set<number>();
export const servers = new Data<ServerAPI[]>("sentryx/servers.json", []);
export var currentServers: Server[] = servers.data.map(
(s) =>
({
...s,
status: "offline",
cpu: 0,
memory: 0,
storage: 0,
network: 0,
} as Server)
);
servers.data.map(useServer);
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
@@ -17,17 +64,6 @@ export async function GET(request: Request) {
{ status: HttpStatusCode.Unauthorized }
);
const currentServers = servers.data.map(
(s) =>
({
...s,
status: "online",
cpu: 20,
memory: 70,
storage: 40,
network: 30,
} as Server)
);
if (query) {
let tokens = (query || "").toLocaleLowerCase().split(" ");
@@ -76,6 +112,25 @@ export async function POST(request: Request) {
location: body.location,
});
currentServers.push({
name: body.name,
ip: body.ip,
coordinates: body.coordinates,
location: body.location,
status: "offline",
cpu: 0,
memory: 0,
storage: 0,
network: 0,
});
useServer({
name: body.name,
ip: body.ip,
coordinates: body.coordinates,
location: body.location,
}, servers.data.length - 1);
servers.write();
return NextResponse.json({
@@ -105,6 +160,9 @@ export async function DELETE(request: Request) {
);
servers.data.splice(parseInt(index), 1);
currentServers.splice(parseInt(index), 1);
deletes.add(parseInt(index));
servers.write();
+43 -11
View File
@@ -3,11 +3,11 @@
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { cn } from "@/lib/utils";
import { CircleCheckBig, Cpu, MemoryStick, TriangleAlert } from "lucide-react";
import { CircleCheckBig, Cpu, HardDrive, MemoryStick, Network, TriangleAlert } from "lucide-react";
import { ChartAreaGradient } from "./chart";
import { Progress } from "@/components/ui/progress";
import getSession from "@/hooks/getSession";
import ServerComponent from "./server";
import ServerComponent, { ByteUnit, UsageBar } from "./server";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { useEffect, useState } from "react";
@@ -90,7 +90,7 @@ export default function Dashboard() {
network: 0,
});
useEffect(() => {
const getServers = () => {
if (!session) return;
session.getServers().then((servers) => {
@@ -107,9 +107,21 @@ export default function Dashboard() {
cpu: average(servers, (s) => s.cpu),
memory: average(servers, (s) => s.memory),
storage: average(servers, (s) => s.storage),
network: average(servers, (s) => s.network),
});
});
network: servers.reduce((sum, s) => sum + s.network, 0),
});``
}).catch((e) => {
console.error("Error fetching servers:", e);
})
};
useEffect(() => {
const interval = setInterval(() => {
console.log("Refreshing servers data...");
getServers();
}, 3000);
// Cleanup on unmount
return () => clearInterval(interval);
}, [session]);
useEffect(() => {
@@ -171,11 +183,31 @@ export default function Dashboard() {
<CardHeader>
<CardTitle>System Overview</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-6 w-full md:w-96">
<Summary value={serverData.cpu}>Average CPU</Summary>
<Summary value={serverData.memory}>Average Memory</Summary>
<Summary value={serverData.storage}>Storage</Summary>
<Summary value={serverData.network}>Average Network</Summary>
<CardContent className="flex flex-col gap-4 w-full md:w-96">
<UsageBar
value={serverData.cpu}
Icon={Cpu}
text="CPU"
color="var(--chart-2)"
/>
<UsageBar
value={serverData.memory}
Icon={MemoryStick}
text="Memory"
color="var(--chart-1)"
/>
<UsageBar
value={serverData.storage}
Icon={HardDrive}
text="Storage"
color="var(--chart-5)"
/>
<ByteUnit
value={serverData.network}
Icon={Network}
text="Network"
color="var(--chart-3)"
/>
</CardContent>
</Card>
</div>
+45 -6
View File
@@ -64,6 +64,43 @@ export function UsageBar({
);
}
function formatBytes(bytes: number, decimals = 2): string {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
const size = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
return `${size} ${sizes[i]}`;
}
export function ByteUnit({
value,
Icon,
text,
color,
}: {
value: number;
Icon: React.ElementType;
text: string;
color: string;
}) {
return (
<div className="flex flex-col gap-2 w-full">
<div className="justify-between flex">
<Label className="text-md flex justify-center">
<Icon size={16} color={color} /> {text}
</Label>
<Label className="text-md">{formatBytes(value)}</Label>
</div>
</div>
)
}
export default function ServerComponent({
server,
index,
@@ -127,12 +164,14 @@ export default function ServerComponent({
text="Storage"
color="var(--chart-5)"
/>
<UsageBar
value={server.network}
Icon={Network}
text="Network"
color="var(--chart-3)"
/>
<div className="flex flex-col gap-2 w-full">
<div className="justify-between flex">
<Label className="text-md flex justify-center">
<Network size={16} color="var(--chart-3)" /> Network
</Label>
<Label className="text-md">{formatBytes(server.network)}</Label>
</div>
</div>
</CardContent>
</Card>
</ContextMenuTrigger>
+2 -2
View File
@@ -10,7 +10,7 @@ import {
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import ServerComponent, { UsageBar } from "../dashboard/server";
import ServerComponent, { ByteUnit, UsageBar } from "../dashboard/server";
import {
Cpu,
HardDrive,
@@ -117,7 +117,7 @@ export default function Map({ servers }: { servers: Server[] }) {
text="Storage"
color="var(--chart-5)"
/>
<UsageBar
<ByteUnit
value={sheet.network}
Icon={Network}
text="Network"