diff --git a/package-lock.json b/package-lock.json index a3f93d7..a1f74dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,11 +16,13 @@ "@radix-ui/react-tooltip": "^1.2.8", "@types/dockerode": "^3.3.47", "@types/js-cookie": "^3.0.6", + "@types/simple-oauth2": "^5.0.8", "axios": "^1.13.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dockerode": "^4.0.9", "http-status-codes": "^2.3.0", + "iron-session": "^8.0.4", "js-cookie": "^3.0.5", "lucide-react": "^0.562.0", "next": "16.1.1", @@ -28,6 +30,7 @@ "react": "19.2.3", "react-dom": "19.2.3", "recharts": "^2.15.4", + "simple-oauth2": "^5.1.0", "systeminformation": "^5.28.2", "tailwind-merge": "^3.4.0" }, @@ -166,6 +169,53 @@ "node": ">=6" } }, + "node_modules/@hapi/boom": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", + "integrity": "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/bourne": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", + "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/hoek": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", + "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@hapi/topo/node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/wreck": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.1.0.tgz", + "integrity": "sha512-0z6ZRCmFEfV/MQqkQomJ7sl/hyxvcZM7LtuVqN3vdAO4vM9eBbowl0kaqQj9EJJQab+3Uuh1GxbGIBFy4NfJ4w==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/hoek": "^11.0.2" + } + }, "node_modules/@img/colour": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", @@ -1818,6 +1868,33 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/address/node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -2217,6 +2294,12 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/simple-oauth2": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@types/simple-oauth2/-/simple-oauth2-5.0.8.tgz", + "integrity": "sha512-TehQqoOGdy3/rmFsCEGgnt1f4JhUCA0joWemGGCTbVYvoZvfBjkRsBFYmz8k0V/sn2XQZHe33L4lWxqMhIO3tQ==", + "license": "MIT" + }, "node_modules/@types/ssh2": { "version": "1.15.5", "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz", @@ -2495,6 +2578,15 @@ "node": ">= 0.8" } }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cpu-features": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", @@ -3102,6 +3194,30 @@ "node": ">=12" } }, + "node_modules/iron-session": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/iron-session/-/iron-session-8.0.4.tgz", + "integrity": "sha512-9ivNnaKOd08osD0lJ3i6If23GFS2LsxyMU8Gf/uBUEgm8/8CC1hrrCHFDpMo3IFbpBgwoo/eairRsaD3c5itxA==", + "funding": [ + "https://github.com/sponsors/vvo", + "https://github.com/sponsors/brc-dd" + ], + "license": "MIT", + "dependencies": { + "cookie": "^0.7.2", + "iron-webcrypto": "^1.2.1", + "uncrypto": "^0.1.3" + } + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3139,6 +3255,25 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/joi/node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -4124,6 +4259,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-oauth2": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/simple-oauth2/-/simple-oauth2-5.1.0.tgz", + "integrity": "sha512-gWDa38Ccm4MwlG5U7AlcJxPv3lvr80dU7ARJWrGdgvOKyzSj1gr3GBPN1rABTedAYvC/LsGYoFuFxwDBPtGEbw==", + "license": "Apache-2.0", + "dependencies": { + "@hapi/hoek": "^11.0.4", + "@hapi/wreck": "^18.0.0", + "debug": "^4.3.4", + "joi": "^17.6.4" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4353,6 +4500,12 @@ "node": ">=14.17" } }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", diff --git a/package.json b/package.json index 59f0325..05246f9 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,13 @@ "@radix-ui/react-tooltip": "^1.2.8", "@types/dockerode": "^3.3.47", "@types/js-cookie": "^3.0.6", + "@types/simple-oauth2": "^5.0.8", "axios": "^1.13.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dockerode": "^4.0.9", "http-status-codes": "^2.3.0", + "iron-session": "^8.0.4", "js-cookie": "^3.0.5", "lucide-react": "^0.562.0", "next": "16.1.1", @@ -28,6 +30,7 @@ "react": "19.2.3", "react-dom": "19.2.3", "recharts": "^2.15.4", + "simple-oauth2": "^5.1.0", "systeminformation": "^5.28.2", "tailwind-merge": "^3.4.0" }, diff --git a/src/app/api/auth/callback/route.ts b/src/app/api/auth/callback/route.ts new file mode 100644 index 0000000..24b3ec5 --- /dev/null +++ b/src/app/api/auth/callback/route.ts @@ -0,0 +1,49 @@ +import axios from "axios"; +import { StatusCodes } from "http-status-codes"; +import { cookies } from "next/headers"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const code = searchParams.get("code"); + + if (!code) { + return NextResponse.json( + { error: "No code provided" }, + { status: StatusCodes.BAD_REQUEST } + ); + } + + try { + const response = await axios.post( + "https://github.com/login/oauth/access_token", + { + client_id: process.env.GITHUB_CLIENT_ID, + client_secret: process.env.GITHUB_CLIENT_SECRET, + code, + }, + { + headers: { + Accept: "application/json", + }, + } + ); + + const accessToken = response.data.access_token; + + const c = await cookies(); + c.set("github_access", accessToken, { + httpOnly: true, + path: "/", + secure: process.env.NODE_ENV === "production", + }); + + return NextResponse.redirect(new URL("/overview", request.url)); + } catch (error) { + console.log(error); + return NextResponse.json( + { error: "Error exchanging code for access token" }, + { status: StatusCodes.INTERNAL_SERVER_ERROR } + ); + } +} diff --git a/src/app/api/get-docker/route.ts b/src/app/api/get-docker/route.ts index c70f62c..4e847f0 100644 --- a/src/app/api/get-docker/route.ts +++ b/src/app/api/get-docker/route.ts @@ -1,7 +1,7 @@ import { NextRequest, NextResponse } from "next/server"; import testAuth from "../auth"; import { StatusCodes } from "http-status-codes"; -import { getDocker } from "@/core/docker"; +import { getDocker } from "@/core/server/docker"; import { Docker } from "@/lib/types"; export async function GET(req: NextRequest): Promise { diff --git a/src/app/monitoring/page.tsx b/src/app/monitoring/page.tsx index cb0945e..2aa6d3e 100644 --- a/src/app/monitoring/page.tsx +++ b/src/app/monitoring/page.tsx @@ -5,12 +5,11 @@ import { Progress } from "@/components/ui/progress"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { type ChartConfig } from "@/components/ui/chart"; import GradientAreaChart from "@/components/GradientAreaChart"; -import { getMetrics } from "@/core/client/metrics"; +import { getMetrics } from "@/core/metrics"; import { useEffect, useState } from "react"; import { useIsMobile } from "@/hooks/use-mobile"; import RadialChart from "@/components/RadialChart"; -import { Node, NodeLiveData } from "@/lib/types"; -import { getLiveNodes } from "@/core/client/node"; +import { getLiveNodes } from "@/core/node"; import { average } from "@/lib/format"; const containerMetrics = [ @@ -102,19 +101,23 @@ export default function MonitoringPage() {
- Cluster CPU + CPU usage
-
68%
- +
+ {liveData?.cpuUsage?.toFixed(0) || 0}% +
+
- Cluster Memory + Memory usage
-
71%
- +
+ {liveData?.ramUsage?.toFixed(0) || 0}% +
+
diff --git a/src/app/nodes/page.tsx b/src/app/nodes/page.tsx index 25564c6..7516e5f 100644 --- a/src/app/nodes/page.tsx +++ b/src/app/nodes/page.tsx @@ -8,9 +8,9 @@ import { Server, Plus, Settings, Trash2 } from "lucide-react"; import { Progress } from "@/components/ui/progress"; import { Docker, Node, NodeLiveData } from "@/lib/types"; import { useEffect, useState } from "react"; -import { getDockerNode, getLiveNodes } from "@/core/client/node"; +import { getDockerNode, getLiveNodes } from "@/core/node"; import { formatBytes } from "@/lib/format"; -import { setNodes as updateNodes } from "@/core/client/node"; +import { setNodes as updateNodes } from "@/core/node"; import AddNode from "@/components/dialogs/AddNode"; function DiskUsage({ liveData }: NodeLiveData) { diff --git a/src/app/overview/page.tsx b/src/app/overview/page.tsx index 98f59bf..0bfa0d2 100644 --- a/src/app/overview/page.tsx +++ b/src/app/overview/page.tsx @@ -16,9 +16,12 @@ import { } from "lucide-react"; import { Progress } from "@/components/ui/progress"; import Link from "next/link"; -import { getLiveNodes } from "@/core/client/node"; +import { getLiveNodes } from "@/core/node"; import { useEffect, useState } from "react"; import { Node, NodeLiveData } from "@/lib/types"; +import axios from "axios"; +import Cookies from "js-cookie"; +import { getSession } from "@/core/auth/session"; const recentDeployments = [ { @@ -99,6 +102,7 @@ export default function OverviewPage() { }; fetchNodes(); const interval = setInterval(fetchNodes, 3000); + return () => clearInterval(interval); }, []); diff --git a/src/app/page.tsx b/src/app/page.tsx index 49cf960..6749ac3 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,35 @@ "use client"; -export default function Home() { - return "ok"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import redirectAuth from "@/core/auth/redirect"; +import { GithubIcon } from "lucide-react"; +import Image from "next/image"; + +export default function LogIn() { + const withGithub = () => { + redirectAuth(); + }; + + return ( +
+ + + + Dockship + + + + + + +
+ ); } diff --git a/src/components/dialogs/AddNode.tsx b/src/components/dialogs/AddNode.tsx index 29b80d5..ace0d4a 100644 --- a/src/components/dialogs/AddNode.tsx +++ b/src/components/dialogs/AddNode.tsx @@ -18,7 +18,7 @@ import { authNode, getNodes, setNodes as updateNodes, -} from "@/core/client/node"; +} from "@/core/node"; export default function AddNode({ children }: { children: ReactNode }) { const [step, setStep] = useState<1 | 2>(1); diff --git a/src/core/auth/oauth.ts b/src/core/auth/oauth.ts new file mode 100644 index 0000000..2157e50 --- /dev/null +++ b/src/core/auth/oauth.ts @@ -0,0 +1,13 @@ +import { AuthorizationCode } from "simple-oauth2"; + +export const client = new AuthorizationCode({ + client: { + id: process.env.GITHUB_CLIENT_ID!, + secret: process.env.GITHUB_CLIENT_SECRET!, + }, + auth: { + tokenHost: "https://github.com", + tokenPath: "/login/oauth/access_token", + authorizePath: "/login/oauth/authorize", + }, +}); diff --git a/src/core/auth/redirect.ts b/src/core/auth/redirect.ts new file mode 100644 index 0000000..b3ed3f7 --- /dev/null +++ b/src/core/auth/redirect.ts @@ -0,0 +1,14 @@ +"use server"; + +import { client } from "@/core/auth/oauth"; +import { redirect } from "next/navigation"; + +export default async function redirectAuth() { + const authorizationUri = client.authorizeURL({ + redirect_uri: "http://localhost:3000/api/auth/callback", + scope: "user:email", + state: "random_state_string", + }); + + redirect(authorizationUri); +} diff --git a/src/core/auth/session.ts b/src/core/auth/session.ts new file mode 100644 index 0000000..eb0eda5 --- /dev/null +++ b/src/core/auth/session.ts @@ -0,0 +1,51 @@ +"use server"; +import { read, write } from "@/app/api/file"; +import axios from "axios"; +import { cookies } from "next/headers"; +import { redirect } from "next/navigation"; + +type Users = Record; + +export async function getSession(): Promise< + undefined | { user: { id: number }; accessToken: string } +> { + const c = await cookies(); + const accessToken = c.get("github_access")?.value; + + if (!accessToken) { + redirect("/"); + } + + // Test the token + try { + const user = ( + await axios.get("https://api.github.com/user", { + headers: { Authorization: `Bearer ${accessToken}` }, + }) + ).data; + + const users = Object.keys( + await read(() => ({}), "data", "users.json") + ); + + if (!users.includes(String(user.id))) { + if (users.length === 0) { + await write( + { [String(user.id)]: { roles: ["admin"] } } as Users, // First user is always an admin + "data", + "users.json" + ); + } else { + throw new Error("Invalid user"); + } + } + + return { user, accessToken }; + } catch { + redirect("/"); + } +} + +export async function verifySession(): Promise { + return !(await getSession())?.accessToken; +} diff --git a/src/core/client/docker.ts b/src/core/client/docker.ts deleted file mode 100644 index 60103c8..0000000 --- a/src/core/client/docker.ts +++ /dev/null @@ -1,12 +0,0 @@ -"use server"; - -import axios from "axios"; -import { Node } from "@/lib/types"; - -export async function getDocker(node: Node): Promise<{ version: string }> { - return await ( - await axios.get(`http://${node.ip}:3000/api/get-docker`, { - headers: { Authorization: `ApiKey ${node.key}` }, - }) - ).data; -} diff --git a/src/core/docker.ts b/src/core/docker.ts index fdb79a2..60103c8 100644 --- a/src/core/docker.ts +++ b/src/core/docker.ts @@ -1,11 +1,12 @@ "use server"; -import Docker from "dockerode"; +import axios from "axios"; +import { Node } from "@/lib/types"; -const docker = new Docker(); - -export async function getDocker() { - const version = await docker.version(); - const containers = await docker.listContainers(); - return { version, containers }; +export async function getDocker(node: Node): Promise<{ version: string }> { + return await ( + await axios.get(`http://${node.ip}:3000/api/get-docker`, { + headers: { Authorization: `ApiKey ${node.key}` }, + }) + ).data; } diff --git a/src/core/client/metrics.ts b/src/core/metrics.ts similarity index 100% rename from src/core/client/metrics.ts rename to src/core/metrics.ts diff --git a/src/core/client/node.ts b/src/core/node.ts similarity index 50% rename from src/core/client/node.ts rename to src/core/node.ts index 842fa79..a9cda4b 100644 --- a/src/core/client/node.ts +++ b/src/core/node.ts @@ -3,35 +3,42 @@ import { read, write } from "@/app/api/file"; import { Docker, Node, NodeLiveData } from "@/lib/types"; import axios from "axios"; +import { verifySession } from "./auth/session"; + +export async function getNodes(): Promise { + if (await verifySession()) return; -export async function getNodes(): Promise { return await read(() => [], "data", "nodes.json"); } export async function setNodes(nodes: Node[]) { + if (await verifySession()) return; + await write(nodes, "data", "nodes.json"); } -export async function getNode(node_id: string): Promise { - const nodes = await read(() => [], "data", "nodes.json"); - return nodes.find((v) => v.node_id === node_id); +export async function getLiveNodes(): Promise< + (NodeLiveData & Node)[] | undefined +> { + if (await verifySession()) return; + + const nodes: Node[] = await read(() => [], "data", "nodes.json"); + + return await Promise.all( + nodes.map(async (node) => ({ + ...node, + liveData: await ( + await axios.get(`http://${node.ip}:3000/api/status`, { + headers: { Authorization: `ApiKey ${node.key}` }, + }) + ).data.liveData, + })) + ); } -export async function getLiveNode(node: Node): Promise { - const data = await ( - await axios.get(`http://${node.ip}:3000/api/status`, { - headers: { Authorization: `ApiKey ${node.key}` }, - }) - ).data; - return { ...node, liveData: data.liveData }; -} +export async function getDockerNode(node: Node): Promise { + if (await verifySession()) return; -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 { const data = await ( await axios.get(`http://${node.ip}:3000/api/get-docker`, { headers: { Authorization: `ApiKey ${node.key}` }, @@ -41,6 +48,8 @@ export async function getDockerNode(node: Node): Promise { } export async function authNode(ip: string, key: string) { + if (await verifySession()) return; + const data = await ( await axios.get(`http://${ip}:3000/api/auth`, { headers: { Authorization: `ApiKey ${key}` }, diff --git a/src/core/server/docker.ts b/src/core/server/docker.ts new file mode 100644 index 0000000..fdb79a2 --- /dev/null +++ b/src/core/server/docker.ts @@ -0,0 +1,11 @@ +"use server"; + +import Docker from "dockerode"; + +const docker = new Docker(); + +export async function getDocker() { + const version = await docker.version(); + const containers = await docker.listContainers(); + return { version, containers }; +}