Node data
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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[];
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user