Node data

This commit is contained in:
2025-12-27 14:58:50 +01:00
parent e8dc4a4590
commit bd37c8dc44
5 changed files with 56 additions and 27 deletions
+7 -2
View File
@@ -1,6 +1,8 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import testAuth from "../auth"; import testAuth from "../auth";
import { StatusCodes } from "http-status-codes"; import { StatusCodes } from "http-status-codes";
import { getDocker } from "@/core/docker";
import { Docker } from "@/lib/types";
export async function GET(req: NextRequest): Promise<NextResponse> { export async function GET(req: NextRequest): Promise<NextResponse> {
const auth = await testAuth(req); const auth = await testAuth(req);
@@ -12,8 +14,11 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
); );
} }
const docker = await getDocker();
return NextResponse.json({ return NextResponse.json({
message: "ok", message: "ok",
containers: [], version: docker.version.Version,
}); containers: docker.containers,
} as Docker);
} }
+23 -16
View File
@@ -6,9 +6,9 @@ import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Server, Plus, Settings, Trash2 } from "lucide-react"; import { Server, Plus, Settings, Trash2 } from "lucide-react";
import { Progress } from "@/components/ui/progress"; import { Progress } from "@/components/ui/progress";
import { Node, NodeLiveData } from "@/lib/types"; import { Docker, Node, NodeLiveData } from "@/lib/types";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { getLiveNodes } from "@/core/client/node"; import { getDockerNode, getLiveNodes } from "@/core/client/node";
import { formatBytes } from "@/lib/format"; import { formatBytes } from "@/lib/format";
function DiskUsage({ liveData }: NodeLiveData) { function DiskUsage({ liveData }: NodeLiveData) {
@@ -28,6 +28,7 @@ function DiskUsage({ liveData }: NodeLiveData) {
export default function NodesPage() { export default function NodesPage() {
const [nodes, setNodes] = useState<(NodeLiveData & Node)[]>([]); const [nodes, setNodes] = useState<(NodeLiveData & Node)[]>([]);
const [dockerNodes, setDockerNodes] = useState<Docker[]>([]);
useEffect(() => { useEffect(() => {
const fetchNodes = async () => { const fetchNodes = async () => {
@@ -39,6 +40,13 @@ export default function NodesPage() {
return () => clearInterval(interval); return () => clearInterval(interval);
}, []); }, []);
useEffect(() => {
const fetchDocker = async () => {
setDockerNodes(await Promise.all(nodes.map(getDockerNode)));
};
fetchDocker();
}, [nodes]);
return ( return (
<DashboardLayout title="Nodes" subtitle="Cluster node management"> <DashboardLayout title="Nodes" subtitle="Cluster node management">
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between mb-6 gap-2"> <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between mb-6 gap-2">
@@ -54,7 +62,7 @@ export default function NodesPage() {
</div> </div>
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
{nodes.map((node) => ( {nodes.map((node, i) => (
<Card key={node.node_id}> <Card key={node.node_id}>
<CardHeader className="flex flex-col sm:flex-row sm:items-start sm:justify-between pb-2 gap-2 sm:gap-0"> <CardHeader className="flex flex-col sm:flex-row sm:items-start sm:justify-between pb-2 gap-2 sm:gap-0">
<div className="flex flex-col sm:flex-row sm:items-center gap-3"> <div className="flex flex-col sm:flex-row sm:items-center gap-3">
@@ -72,16 +80,11 @@ export default function NodesPage() {
</div> </div>
</div> </div>
<div className="flex flex-wrap items-center gap-2 mt-2 sm:mt-0"> <div className="flex flex-wrap items-center gap-2 mt-2 sm:mt-0">
{node.labels && {node.labels.map((label) => (
node.labels.map((label) => ( <Badge key={label} variant="outline" className="text-[10px]">
<Badge {label}
key={label} </Badge>
variant="outline" ))}
className="text-[10px]"
>
{label}
</Badge>
))}
<Button variant="ghost" size="icon-sm"> <Button variant="ghost" size="icon-sm">
<Settings className="w-4 h-4" /> <Settings className="w-4 h-4" />
</Button> </Button>
@@ -100,15 +103,19 @@ export default function NodesPage() {
<div className="space-y-1"> <div className="space-y-1">
<div className="flex justify-between text-xs"> <div className="flex justify-between text-xs">
<span className="text-muted-foreground">OS</span> <span className="text-muted-foreground">OS</span>
{/* <span className="font-mono">{node.os}</span> */} <span className="font-mono">{node.os}</span>
</div> </div>
<div className="flex justify-between text-xs"> <div className="flex justify-between text-xs">
<span className="text-muted-foreground">Docker</span> <span className="text-muted-foreground">Docker</span>
{/* <span className="font-mono">{node.docker}</span> */} <span className="font-mono">
{dockerNodes[i]?.version}
</span>
</div> </div>
<div className="flex justify-between text-xs"> <div className="flex justify-between text-xs">
<span className="text-muted-foreground">Containers</span> <span className="text-muted-foreground">Containers</span>
{/* <span className="font-mono">{node.containers}</span> */} <span className="font-mono">
{dockerNodes[i]?.containers.length}
</span>
</div> </div>
</div> </div>
</div> </div>
+15 -6
View File
@@ -1,7 +1,7 @@
"use server"; "use server";
import { read, write } from "@/app/api/file"; import { read, write } from "@/app/api/file";
import { Node, NodeLiveData } from "@/lib/types"; import { Docker, Node, NodeLiveData } from "@/lib/types";
import axios from "axios"; import axios from "axios";
export async function getNodes(): Promise<Node[]> { export async function getNodes(): Promise<Node[]> {
@@ -17,11 +17,6 @@ export async function getNode(node_id: string): Promise<Node | undefined> {
return nodes.find((v) => v.node_id === node_id); return nodes.find((v) => v.node_id === node_id);
} }
export async function getLiveNodes(): Promise<(NodeLiveData & Node)[]> {
const nodes = await read(() => [], "data", "nodes.json");
return await Promise.all(nodes.map(getLiveNode));
}
export async function getLiveNode(node: Node): Promise<NodeLiveData & Node> { export async function getLiveNode(node: Node): Promise<NodeLiveData & Node> {
const data = await ( const data = await (
await axios.get(`http://${node.ip}:3000/api/status`, { await axios.get(`http://${node.ip}:3000/api/status`, {
@@ -30,3 +25,17 @@ export async function getLiveNode(node: Node): Promise<NodeLiveData & Node> {
).data; ).data;
return { ...node, liveData: data.liveData }; return { ...node, liveData: data.liveData };
} }
export async function getLiveNodes(): Promise<(NodeLiveData & Node)[]> {
const nodes = await read(() => [], "data", "nodes.json");
return await Promise.all(nodes.map(getLiveNode));
}
export async function getDockerNode(node: Node): Promise<Docker> {
const data = await (
await axios.get(`http://${node.ip}:3000/api/get-docker`, {
headers: { Authorization: `ApiKey ${node.key}` },
})
).data;
return { ...node, containers: data.containers, version: data.version };
}
+2 -2
View File
@@ -6,6 +6,6 @@ const docker = new Docker();
export async function getDocker() { export async function getDocker() {
const version = await docker.version(); const version = await docker.version();
console.log("Docker version:", version); const containers = await docker.listContainers();
return version; return { version, containers };
} }
+9 -1
View File
@@ -1,9 +1,12 @@
import { ContainerInfo } from "dockerode";
export interface Node { export interface Node {
node_id: string; node_id: string;
key: string; key: string;
ip: string; ip: string;
os: string;
name: string; name: string;
labels?: string[]; labels: string[];
} }
export interface NodeLiveData { export interface NodeLiveData {
@@ -38,3 +41,8 @@ export interface NodeLiveData {
}[]; }[];
}; };
} }
export interface Docker {
version: string;
containers: ContainerInfo[];
}