live monitoring
This commit is contained in:
@@ -4,7 +4,54 @@ import Server, { ServerAPI } from "@/types/server";
|
|||||||
import { HttpStatusCode } from "axios";
|
import { HttpStatusCode } from "axios";
|
||||||
import { validate } from "../auth/route";
|
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 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) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
@@ -17,17 +64,6 @@ export async function GET(request: Request) {
|
|||||||
{ status: HttpStatusCode.Unauthorized }
|
{ status: HttpStatusCode.Unauthorized }
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentServers = servers.data.map(
|
|
||||||
(s) =>
|
|
||||||
({
|
|
||||||
...s,
|
|
||||||
status: "online",
|
|
||||||
cpu: 20,
|
|
||||||
memory: 70,
|
|
||||||
storage: 40,
|
|
||||||
network: 30,
|
|
||||||
} as Server)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
let tokens = (query || "").toLocaleLowerCase().split(" ");
|
let tokens = (query || "").toLocaleLowerCase().split(" ");
|
||||||
@@ -76,6 +112,25 @@ export async function POST(request: Request) {
|
|||||||
location: body.location,
|
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();
|
servers.write();
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
@@ -105,6 +160,9 @@ export async function DELETE(request: Request) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
servers.data.splice(parseInt(index), 1);
|
servers.data.splice(parseInt(index), 1);
|
||||||
|
currentServers.splice(parseInt(index), 1);
|
||||||
|
|
||||||
|
deletes.add(parseInt(index));
|
||||||
|
|
||||||
servers.write();
|
servers.write();
|
||||||
|
|
||||||
|
|||||||
+43
-11
@@ -3,11 +3,11 @@
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { cn } from "@/lib/utils";
|
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 { ChartAreaGradient } from "./chart";
|
||||||
import { Progress } from "@/components/ui/progress";
|
import { Progress } from "@/components/ui/progress";
|
||||||
import getSession from "@/hooks/getSession";
|
import getSession from "@/hooks/getSession";
|
||||||
import ServerComponent from "./server";
|
import ServerComponent, { ByteUnit, UsageBar } from "./server";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -90,7 +90,7 @@ export default function Dashboard() {
|
|||||||
network: 0,
|
network: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
const getServers = () => {
|
||||||
if (!session) return;
|
if (!session) return;
|
||||||
|
|
||||||
session.getServers().then((servers) => {
|
session.getServers().then((servers) => {
|
||||||
@@ -107,9 +107,21 @@ export default function Dashboard() {
|
|||||||
cpu: average(servers, (s) => s.cpu),
|
cpu: average(servers, (s) => s.cpu),
|
||||||
memory: average(servers, (s) => s.memory),
|
memory: average(servers, (s) => s.memory),
|
||||||
storage: average(servers, (s) => s.storage),
|
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]);
|
}, [session]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -171,11 +183,31 @@ export default function Dashboard() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>System Overview</CardTitle>
|
<CardTitle>System Overview</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-col gap-6 w-full md:w-96">
|
<CardContent className="flex flex-col gap-4 w-full md:w-96">
|
||||||
<Summary value={serverData.cpu}>Average CPU</Summary>
|
<UsageBar
|
||||||
<Summary value={serverData.memory}>Average Memory</Summary>
|
value={serverData.cpu}
|
||||||
<Summary value={serverData.storage}>Storage</Summary>
|
Icon={Cpu}
|
||||||
<Summary value={serverData.network}>Average Network</Summary>
|
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>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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({
|
export default function ServerComponent({
|
||||||
server,
|
server,
|
||||||
index,
|
index,
|
||||||
@@ -127,12 +164,14 @@ export default function ServerComponent({
|
|||||||
text="Storage"
|
text="Storage"
|
||||||
color="var(--chart-5)"
|
color="var(--chart-5)"
|
||||||
/>
|
/>
|
||||||
<UsageBar
|
<div className="flex flex-col gap-2 w-full">
|
||||||
value={server.network}
|
<div className="justify-between flex">
|
||||||
Icon={Network}
|
<Label className="text-md flex justify-center">
|
||||||
text="Network"
|
<Network size={16} color="var(--chart-3)" /> Network
|
||||||
color="var(--chart-3)"
|
</Label>
|
||||||
/>
|
<Label className="text-md">{formatBytes(server.network)}</Label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</ContextMenuTrigger>
|
</ContextMenuTrigger>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
SheetTitle,
|
SheetTitle,
|
||||||
SheetTrigger,
|
SheetTrigger,
|
||||||
} from "@/components/ui/sheet";
|
} from "@/components/ui/sheet";
|
||||||
import ServerComponent, { UsageBar } from "../dashboard/server";
|
import ServerComponent, { ByteUnit, UsageBar } from "../dashboard/server";
|
||||||
import {
|
import {
|
||||||
Cpu,
|
Cpu,
|
||||||
HardDrive,
|
HardDrive,
|
||||||
@@ -117,7 +117,7 @@ export default function Map({ servers }: { servers: Server[] }) {
|
|||||||
text="Storage"
|
text="Storage"
|
||||||
color="var(--chart-5)"
|
color="var(--chart-5)"
|
||||||
/>
|
/>
|
||||||
<UsageBar
|
<ByteUnit
|
||||||
value={sheet.network}
|
value={sheet.network}
|
||||||
Icon={Network}
|
Icon={Network}
|
||||||
text="Network"
|
text="Network"
|
||||||
|
|||||||
Reference in New Issue
Block a user