chore: formatting
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2025-03-05 16:26:47 +00:00
parent 7ba2659fbf
commit c6e4a9e3c9
4 changed files with 246 additions and 181 deletions

View File

@ -116,7 +116,7 @@ export class LNVpsApi {
constructor( constructor(
readonly url: string, readonly url: string,
readonly publisher: EventPublisher | undefined, readonly publisher: EventPublisher | undefined,
) { } ) {}
async getAccount() { async getAccount() {
const { data } = await this.#handleResponse<ApiResponse<AccountDetail>>( const { data } = await this.#handleResponse<ApiResponse<AccountDetail>>(
@ -147,9 +147,9 @@ export class LNVpsApi {
} }
async getVmTimeSeries(id: number) { async getVmTimeSeries(id: number) {
const { data } = await this.#handleResponse<ApiResponse<Array<TimeSeriesData>>>( const { data } = await this.#handleResponse<
await this.#req(`/api/v1/vm/${id}/time-series`, "GET"), ApiResponse<Array<TimeSeriesData>>
); >(await this.#req(`/api/v1/vm/${id}/time-series`, "GET"));
return data; return data;
} }

View File

@ -61,7 +61,7 @@ const router = createBrowserRouter([
}, },
{ {
path: "/vm/graphs", path: "/vm/graphs",
element: <VmGraphsPage /> element: <VmGraphsPage />,
}, },
{ {
path: "/tos", path: "/tos",

View File

@ -32,7 +32,7 @@ export function VmBillingPage() {
useEffect(() => { useEffect(() => {
if (params["action"] === "renew" && login && state) { if (params["action"] === "renew" && login && state) {
renew() renew();
} }
}, [login, state, params, renew]); }, [login, state, params, renew]);
@ -54,8 +54,9 @@ export function VmBillingPage() {
Expires: {expireDate.toDateString()} ({Math.floor(days)} days) Expires: {expireDate.toDateString()} ({Math.floor(days)} days)
</div> </div>
)} )}
{days < 0 && params["action"] !== "renew" {days < 0 && params["action"] !== "renew" && (
&& <div className="text-red-500 text-xl">Expired</div>} <div className="text-red-500 text-xl">Expired</div>
)}
{!payment && ( {!payment && (
<div> <div>
<AsyncButton onClick={renew}>Extend Now</AsyncButton> <AsyncButton onClick={renew}>Extend Now</AsyncButton>

View File

@ -2,7 +2,15 @@ import { Link, useLocation } from "react-router-dom";
import { TimeSeriesData, VmInstance } from "../api"; import { TimeSeriesData, VmInstance } from "../api";
import useLogin from "../hooks/login"; import useLogin from "../hooks/login";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { ResponsiveContainer, XAxis, YAxis, Tooltip, LineChart, Line, Legend } from "recharts"; import {
ResponsiveContainer,
XAxis,
YAxis,
Tooltip,
LineChart,
Line,
Legend,
} from "recharts";
export function VmGraphsPage() { export function VmGraphsPage() {
const { state } = useLocation() as { state?: VmInstance }; const { state } = useLocation() as { state?: VmInstance };
@ -14,7 +22,8 @@ export function VmGraphsPage() {
login?.api.getVmTimeSeries(state.id).then(setData); login?.api.getVmTimeSeries(state.id).then(setData);
}, [login]); }, [login]);
const maxRam = data?.reduce((acc, v) => { const maxRam =
data?.reduce((acc, v) => {
const mb = v.memory_size / 1024 / 1024; const mb = v.memory_size / 1024 / 1024;
return acc < mb ? mb : acc; return acc < mb ? mb : acc;
}, 0) ?? 0; }, 0) ?? 0;
@ -23,12 +32,15 @@ export function VmGraphsPage() {
const MB = 1024 * 1024; const MB = 1024 * 1024;
function scaleLabel(v: number) { function scaleLabel(v: number) {
switch (net_scale) { switch (net_scale) {
case MB: return "MiB"; case MB:
case KB: return "KiB"; return "MiB";
case KB:
return "KiB";
} }
return "B"; return "B";
} }
const net_scale = data?.reduce((acc, v) => { const net_scale =
data?.reduce((acc, v) => {
const b = Math.max(v.net_in, v.net_out); const b = Math.max(v.net_in, v.net_out);
if (b > MB && b > acc) { if (b > MB && b > acc) {
return MB; return MB;
@ -39,7 +51,8 @@ export function VmGraphsPage() {
} }
}, 0) ?? 0; }, 0) ?? 0;
const net_scale_label = scaleLabel(net_scale); const net_scale_label = scaleLabel(net_scale);
const disk_scale = data?.reduce((acc, v) => { const disk_scale =
data?.reduce((acc, v) => {
const b = Math.max(v.disk_read, v.disk_write); const b = Math.max(v.disk_read, v.disk_write);
if (b > MB && b > acc) { if (b > MB && b > acc) {
return MB; return MB;
@ -61,24 +74,39 @@ export function VmGraphsPage() {
DISK_READ: v.disk_read / disk_scale, DISK_READ: v.disk_read / disk_scale,
DISK_WRITE: v.disk_write / disk_scale, DISK_WRITE: v.disk_write / disk_scale,
})); }));
const toolTip = <Tooltip cursor={{ fill: "rgba(200,200,200,0.5)" }} const toolTip = (
<Tooltip
cursor={{ fill: "rgba(200,200,200,0.5)" }}
content={({ active, payload }) => { content={({ active, payload }) => {
if (active && payload && payload.length) { if (active && payload && payload.length) {
const data = payload[0].payload as TimeSeriesData; const data = payload[0].payload as TimeSeriesData;
return <div className="flex flex-col gap-2 bg-neutral-700 rounded-xl px-2 py-3"> return (
<div className="flex flex-col gap-2 bg-neutral-700 rounded-xl px-2 py-3">
<div>{data.timestamp}</div> <div>{data.timestamp}</div>
{payload.map((p) => <div>{p.name}: {Number(p.value).toFixed(2)}{p.unit}</div>)} {payload.map((p) => (
<div>
</div>; {p.name}: {Number(p.value).toFixed(2)}
{p.unit}
</div>
))}
</div>
);
} }
}} />; }}
return <div className="flex flex-col gap-4"> />
);
return (
<div className="flex flex-col gap-4">
<Link to={"/vm"} state={state}> <Link to={"/vm"} state={state}>
&lt; Back &lt; Back
</Link> </Link>
<h2>CPU</h2> <h2>CPU</h2>
<ResponsiveContainer height={200}> <ResponsiveContainer height={200}>
<LineChart data={sortedData} margin={{ left: 0, right: 0 }} style={{ userSelect: "none" }}> <LineChart
data={sortedData}
margin={{ left: 0, right: 0 }}
style={{ userSelect: "none" }}
>
<XAxis dataKey="timestamp" /> <XAxis dataKey="timestamp" />
<YAxis unit="%" domain={[0, 100]} /> <YAxis unit="%" domain={[0, 100]} />
<Line type="monotone" dataKey="CPU" unit="%" dot={false} /> <Line type="monotone" dataKey="CPU" unit="%" dot={false} />
@ -87,7 +115,11 @@ export function VmGraphsPage() {
</ResponsiveContainer> </ResponsiveContainer>
<h2>Memory</h2> <h2>Memory</h2>
<ResponsiveContainer height={200}> <ResponsiveContainer height={200}>
<LineChart data={sortedData} margin={{ left: 0, right: 0 }} style={{ userSelect: "none" }}> <LineChart
data={sortedData}
margin={{ left: 0, right: 0 }}
style={{ userSelect: "none" }}
>
<XAxis dataKey="timestamp" /> <XAxis dataKey="timestamp" />
<YAxis unit="MB" domain={[0, maxRam]} /> <YAxis unit="MB" domain={[0, maxRam]} />
<Line type="monotone" dataKey="RAM" unit="MB" dot={false} /> <Line type="monotone" dataKey="RAM" unit="MB" dot={false} />
@ -96,26 +128,58 @@ export function VmGraphsPage() {
</ResponsiveContainer> </ResponsiveContainer>
<h2>Network</h2> <h2>Network</h2>
<ResponsiveContainer height={200}> <ResponsiveContainer height={200}>
<LineChart data={sortedData} margin={{ left: 20, right: 0 }} style={{ userSelect: "none" }}> <LineChart
data={sortedData}
margin={{ left: 20, right: 0 }}
style={{ userSelect: "none" }}
>
<XAxis dataKey="timestamp" /> <XAxis dataKey="timestamp" />
<YAxis unit={`${net_scale_label}/s`} domain={[0, "auto"]} /> <YAxis unit={`${net_scale_label}/s`} domain={[0, "auto"]} />
<Line type="monotone" dataKey="NET_IN" unit={`${net_scale_label}/s`} stroke="red" dot={false} /> <Line
<Line type="monotone" dataKey="NET_OUT" unit={`${net_scale_label}/s`} stroke="green" dot={false} /> type="monotone"
dataKey="NET_IN"
unit={`${net_scale_label}/s`}
stroke="red"
dot={false}
/>
<Line
type="monotone"
dataKey="NET_OUT"
unit={`${net_scale_label}/s`}
stroke="green"
dot={false}
/>
{toolTip} {toolTip}
<Legend /> <Legend />
</LineChart> </LineChart>
</ResponsiveContainer> </ResponsiveContainer>
<h2>Disk</h2> <h2>Disk</h2>
<ResponsiveContainer height={200}> <ResponsiveContainer height={200}>
<LineChart data={sortedData} margin={{ left: 20, right: 0 }} style={{ userSelect: "none" }}> <LineChart
data={sortedData}
margin={{ left: 20, right: 0 }}
style={{ userSelect: "none" }}
>
<XAxis dataKey="timestamp" /> <XAxis dataKey="timestamp" />
<YAxis unit={`${disk_scale_label}/s`} domain={[0, "auto"]} /> <YAxis unit={`${disk_scale_label}/s`} domain={[0, "auto"]} />
<Line type="monotone" dataKey="DISK_READ" unit={`${disk_scale_label}/s`} stroke="red" dot={false} /> <Line
<Line type="monotone" dataKey="DISK_WRITE" unit={`${disk_scale_label}/s`} stroke="green" dot={false} /> type="monotone"
dataKey="DISK_READ"
unit={`${disk_scale_label}/s`}
stroke="red"
dot={false}
/>
<Line
type="monotone"
dataKey="DISK_WRITE"
unit={`${disk_scale_label}/s`}
stroke="green"
dot={false}
/>
{toolTip} {toolTip}
<Legend /> <Legend />
</LineChart> </LineChart>
</ResponsiveContainer> </ResponsiveContainer>
</div> </div>
);
} }