mirror of
https://github.com/CyberL1/dlinux-dashboard.git
synced 2025-06-29 00:29:43 -04:00
feat(frontend): start doing container page
This commit is contained in:
@ -6,6 +6,9 @@ import { createBrowserRouter, RouterProvider } from "react-router";
|
||||
import Containers, {
|
||||
Loader as ContainersLoader,
|
||||
} from "./pages/containers/index.tsx";
|
||||
import ContainerPage, {
|
||||
Loader as ContainerPageLoader,
|
||||
} from "./pages/containers/[name].tsx";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@ -18,6 +21,11 @@ const router = createBrowserRouter([
|
||||
element: <Containers />,
|
||||
loader: ContainersLoader,
|
||||
},
|
||||
{
|
||||
path: "/containers/:name",
|
||||
element: <ContainerPage />,
|
||||
loader: ContainerPageLoader,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
88
frontend/src/pages/containers/[name].tsx
Normal file
88
frontend/src/pages/containers/[name].tsx
Normal file
@ -0,0 +1,88 @@
|
||||
import { useLoaderData, useRevalidator } from "react-router";
|
||||
import { Container } from "../../types";
|
||||
import { Button, ButtonGroup, Paper, Typography } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ContainerStats } from "dockerode";
|
||||
|
||||
interface Params {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export async function Loader({ params }: { params: Params }) {
|
||||
const container = await fetch(`/api/containers/${params.name}`);
|
||||
|
||||
const data = await container.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
export default function ContainerPage() {
|
||||
const container = useLoaderData() as Container & { statusCode: number };
|
||||
|
||||
if (container.statusCode === 404) {
|
||||
return "Container not found";
|
||||
}
|
||||
|
||||
const [stats, setStats] = useState<ContainerStats>();
|
||||
|
||||
useEffect(() => {
|
||||
const statsSource = new EventSource(
|
||||
`/api/containers/${container.name}/stats`,
|
||||
);
|
||||
|
||||
statsSource.onmessage = ({ data }) => {
|
||||
const parsed = JSON.parse(data);
|
||||
setStats(parsed);
|
||||
};
|
||||
|
||||
return () => {
|
||||
statsSource.close();
|
||||
};
|
||||
}, []);
|
||||
|
||||
console.log("stats", stats);
|
||||
const revalidator = useRevalidator();
|
||||
const [isPowerStateLocked, setPowerStateLocked] = useState<boolean>();
|
||||
|
||||
return (
|
||||
<Paper square sx={{ padding: 1 }}>
|
||||
<Paper sx={{ display: "flex" }} variant="outlined">
|
||||
<Typography variant="h6" sx={{ flexGrow: 1 }}>
|
||||
Managing: {container.name}
|
||||
</Typography>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
loading={isPowerStateLocked}
|
||||
onClick={async () => {
|
||||
await switchPowerState(
|
||||
container.status === "running" ? "stop" : "start",
|
||||
);
|
||||
}}
|
||||
>
|
||||
Power {container.status === "running" ? "off" : "on"}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={async () => await switchPowerState("restart")}
|
||||
loading={isPowerStateLocked}
|
||||
disabled={container.status === "exited"}
|
||||
>
|
||||
Restart
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Paper>
|
||||
CPU Usage: {stats?.cpu_stats.cpu_usage.total_usage}
|
||||
</Paper>
|
||||
);
|
||||
|
||||
async function switchPowerState(state: string) {
|
||||
setPowerStateLocked(true);
|
||||
|
||||
const res = await fetch(`/api/containers/${container.name}/${state}`, {
|
||||
method: "PUT",
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
setPowerStateLocked(false);
|
||||
revalidator.revalidate();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { useLoaderData, useNavigate } from "react-router";
|
||||
import { useLoaderData } from "react-router";
|
||||
import { Container } from "../../types";
|
||||
import {
|
||||
Button,
|
||||
@ -18,7 +18,6 @@ export async function Loader() {
|
||||
|
||||
export default function Containers() {
|
||||
const containers = useLoaderData() as Container[];
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -39,16 +38,7 @@ export default function Containers() {
|
||||
Status: {container.status}
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button
|
||||
onClick={async () =>
|
||||
await switchPower(
|
||||
container.id,
|
||||
container.status === "running" ? "stop" : "start",
|
||||
)
|
||||
}
|
||||
>
|
||||
Power {container.status === "running" ? "off" : "on"}
|
||||
</Button>
|
||||
<Button href={`/containers/${container.name}`}>Manage</Button>
|
||||
<Button
|
||||
href={`//${container.name}.${document.location.host}`}
|
||||
target="_blank"
|
||||
@ -62,9 +52,4 @@ export default function Containers() {
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
async function switchPower(id: string, state: string) {
|
||||
await fetch(`/api/containers/${id}/${state}`, { method: "PUT" });
|
||||
navigate(".", { replace: true });
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user