mirror of
https://github.com/moghtech/komodo.git
synced 2026-07-19 18:19:56 -05:00
current stats page
This commit is contained in:
Vendored
+2
-2
@@ -38,7 +38,7 @@
|
||||
"scope": "typescriptreact,javascriptreact",
|
||||
"prefix": "provider",
|
||||
"body": [
|
||||
"import { Component, createContext, useContext } from \"solid-js\";",
|
||||
"import { ParentComponent, createContext, useContext } from \"solid-js\";",
|
||||
"",
|
||||
"const value = () => {",
|
||||
"\treturn {};",
|
||||
@@ -48,7 +48,7 @@
|
||||
"",
|
||||
"const context = createContext<Value>();",
|
||||
"",
|
||||
"export const Provider: Component<{}> = (p) => {",
|
||||
"export const Provider: ParentComponent<{}> = (p) => {",
|
||||
"\treturn (",
|
||||
"\t\t<context.Provider value={value()}>",
|
||||
"\t\t\t{p.children}",
|
||||
|
||||
@@ -18,6 +18,7 @@ const HeatBar: Component<{
|
||||
containerClass?: string;
|
||||
containerStyle?: JSX.CSSProperties;
|
||||
barHeight?: string;
|
||||
onClick?: () => void;
|
||||
}> = (p) => {
|
||||
let el: HTMLDivElement;
|
||||
const [width, setWidth] = createSignal<number>();
|
||||
@@ -32,18 +33,26 @@ const HeatBar: Component<{
|
||||
removeEventListener("resize", handleResize);
|
||||
});
|
||||
return (
|
||||
<Flex ref={el!} gap={`${BAR_GAP}px`} class={p.containerClass} style={p.containerStyle}>
|
||||
<Flex
|
||||
ref={el!}
|
||||
gap={`${BAR_GAP}px`}
|
||||
class={p.containerClass}
|
||||
style={{
|
||||
cursor: p.onClick && "pointer",
|
||||
...p.containerStyle,
|
||||
}}
|
||||
onClick={p.onClick}
|
||||
>
|
||||
<For each={[...Array(p.total).keys()]}>
|
||||
{(index) => (
|
||||
<div
|
||||
style={{
|
||||
height: p.barHeight || "2rem",
|
||||
width: `${width()!}px`,
|
||||
"background-color": index <= p.filled ? blendColors(
|
||||
BLUE,
|
||||
RED,
|
||||
index / p.total
|
||||
) : "transparent",
|
||||
"background-color":
|
||||
index <= p.filled
|
||||
? blendColors(BLUE, RED, index / p.total)
|
||||
: "transparent",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -26,9 +26,12 @@ type LineDataPoint = {
|
||||
};
|
||||
|
||||
const LightweightChart: Component<{
|
||||
style?: JSX.CSSProperties;
|
||||
class?: string;
|
||||
lines?: () => LinesData[];
|
||||
class?: string;
|
||||
style?: JSX.CSSProperties;
|
||||
width?: string;
|
||||
height?: string;
|
||||
disableScroll?: boolean
|
||||
}> = (p) => {
|
||||
let el: HTMLDivElement;
|
||||
const [chart, setChart] = createSignal<IChartApi>();
|
||||
@@ -49,8 +52,8 @@ const LightweightChart: Component<{
|
||||
vertLines: { color: "#3f454d" },
|
||||
},
|
||||
timeScale: { timeVisible: true },
|
||||
// handleScroll: false,
|
||||
// handleScale: false,
|
||||
handleScroll: p.disableScroll ? false : true,
|
||||
handleScale: p.disableScroll ? false : true,
|
||||
});
|
||||
chart.timeScale().fitContent();
|
||||
setChart(chart);
|
||||
@@ -64,11 +67,12 @@ const LightweightChart: Component<{
|
||||
const series = chart()!.addLineSeries({
|
||||
color: line.color,
|
||||
title: line.title,
|
||||
priceLineVisible: line.priceLineVisible || false
|
||||
priceLineVisible: line.priceLineVisible || false,
|
||||
});
|
||||
series.setData(line.line as any);
|
||||
return series;
|
||||
});
|
||||
chart()!.timeScale().fitContent();
|
||||
lineSeries = series;
|
||||
}
|
||||
});
|
||||
@@ -86,7 +90,11 @@ const LightweightChart: Component<{
|
||||
<div
|
||||
ref={el!}
|
||||
class={p.class}
|
||||
style={{ width: "100%", height: "100%", ...p.style }}
|
||||
style={{
|
||||
width: p.width || "100%",
|
||||
height: p.height || "100%",
|
||||
...p.style,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
import { Accessor, Component, createSignal, For, Show } from "solid-js";
|
||||
import { SystemStats, SystemStatsRecord } from "../../types";
|
||||
import {
|
||||
convertTsMsToLocalUnixTsInSecs,
|
||||
get_to_one_sec_divisor,
|
||||
} from "../../util/helpers";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import LightweightChart from "../shared/LightweightChart";
|
||||
import s from "./stats.module.scss";
|
||||
|
||||
export const COLORS = {
|
||||
blue: "#184e9f",
|
||||
orange: "#ac5c36",
|
||||
purple: "#5A0B4D",
|
||||
green: "#41764c",
|
||||
red: "#952E23",
|
||||
};
|
||||
|
||||
const CHART_HEIGHT = "200px";
|
||||
const SMALL_CHART_HEIGHT = "150px";
|
||||
|
||||
export const CpuChart: Component<{
|
||||
stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>;
|
||||
small?: boolean;
|
||||
disableScroll?: boolean;
|
||||
}> = (p) => {
|
||||
const line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: s.cpu_perc,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={line()}>
|
||||
<Grid
|
||||
gap="0"
|
||||
class="card shadow"
|
||||
style={{
|
||||
height: "fit-content",
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "0.2rem",
|
||||
}}
|
||||
>
|
||||
<Show when={!p.small}>
|
||||
<h2>cpu</h2>
|
||||
</Show>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
height={p.small ? SMALL_CHART_HEIGHT : CHART_HEIGHT}
|
||||
lines={() => [{ title: "%", color: COLORS.blue, line: line()! }]}
|
||||
disableScroll={p.disableScroll}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export const MemChart: Component<{
|
||||
stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>;
|
||||
small?: boolean;
|
||||
disableScroll?: boolean;
|
||||
}> = (p) => {
|
||||
const line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: (100 * s.mem_used_gb) / s.mem_total_gb,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={line()}>
|
||||
<Grid
|
||||
gap="0"
|
||||
class="card shadow"
|
||||
style={{
|
||||
height: "fit-content",
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "0.2rem",
|
||||
}}
|
||||
>
|
||||
<Show when={!p.small}>
|
||||
<h2>memory</h2>
|
||||
</Show>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
height={p.small ? SMALL_CHART_HEIGHT : CHART_HEIGHT}
|
||||
lines={() => [{ title: "mem %", color: COLORS.blue, line: line()! }]}
|
||||
disableScroll={p.disableScroll}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export const DiskChart: Component<{
|
||||
stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>;
|
||||
small?: boolean;
|
||||
disableScroll?: boolean;
|
||||
}> = (p) => {
|
||||
const line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: (100 * s.disk.used_gb) / s.disk.total_gb,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={line()}>
|
||||
<Grid
|
||||
gap="0"
|
||||
class="card shadow"
|
||||
style={{
|
||||
height: "fit-content",
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "0.2rem",
|
||||
}}
|
||||
>
|
||||
<Show when={!p.small}>
|
||||
<h2>disk</h2>
|
||||
</Show>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
height={p.small ? SMALL_CHART_HEIGHT : CHART_HEIGHT}
|
||||
lines={() => [{ title: "disk %", color: "#184e9f", line: line()! }]}
|
||||
disableScroll={p.disableScroll}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export const NetworkIoCharts: Component<{
|
||||
stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>;
|
||||
small?: boolean;
|
||||
disableScroll?: boolean;
|
||||
}> = (p) => {
|
||||
const recv_line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value:
|
||||
s.networks?.length || 0 > 0
|
||||
? s.networks!.map((n) => n.recieved_kb).reduce((p, c) => p + c) /
|
||||
get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
const trans_line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value:
|
||||
s.networks?.length || 0 > 0
|
||||
? s.networks!.map((n) => n.transmitted_kb).reduce((p, c) => p + c) /
|
||||
get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={recv_line()}>
|
||||
<Grid
|
||||
gap="0"
|
||||
class="card shadow"
|
||||
style={{
|
||||
height: "fit-content",
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "0.2rem",
|
||||
}}
|
||||
>
|
||||
<Show when={!p.small}>
|
||||
<h2>network sent kb/s</h2>
|
||||
</Show>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
height={p.small ? SMALL_CHART_HEIGHT : CHART_HEIGHT}
|
||||
lines={() => [
|
||||
{ title: "sent kb/s", color: "#184e9f", line: trans_line()! },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid
|
||||
gap="0"
|
||||
class="card shadow"
|
||||
style={{
|
||||
height: "fit-content",
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "0.2rem",
|
||||
}}
|
||||
>
|
||||
<Show when={!p.small}>
|
||||
<h2>network received kb/s</h2>
|
||||
</Show>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
height={p.small ? SMALL_CHART_HEIGHT : CHART_HEIGHT}
|
||||
lines={() => [
|
||||
{ title: "recv kb/s", color: "#184e9f", line: recv_line()! },
|
||||
]}
|
||||
disableScroll={p.disableScroll}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export const DiskIoCharts: Component<{
|
||||
stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>;
|
||||
small?: boolean;
|
||||
disableScroll?: boolean;
|
||||
}> = (p) => {
|
||||
const read_line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value:
|
||||
s.disk.disks?.length || 0 > 0
|
||||
? s.disk.read_kb / get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
const write_line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value:
|
||||
s.disk.disks?.length || 0 > 0
|
||||
? s.disk.write_kb / get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={read_line()}>
|
||||
<Grid
|
||||
gap="0"
|
||||
class="card shadow"
|
||||
style={{
|
||||
height: "fit-content",
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "0.2rem",
|
||||
}}
|
||||
>
|
||||
<Show when={!p.small}>
|
||||
<h2>disk read kb/s</h2>
|
||||
</Show>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
height={p.small ? SMALL_CHART_HEIGHT : CHART_HEIGHT}
|
||||
lines={() => [
|
||||
{ title: "kb/s", color: "#184e9f", line: read_line()! },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid
|
||||
gap="0"
|
||||
class="card shadow"
|
||||
style={{
|
||||
height: "fit-content",
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "0.2rem",
|
||||
}}
|
||||
>
|
||||
<h2>disk write kb/s</h2>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
height={p.small ? SMALL_CHART_HEIGHT : CHART_HEIGHT}
|
||||
lines={() => [
|
||||
{ title: "kb/s", color: "#184e9f", line: write_line()! },
|
||||
]}
|
||||
disableScroll={p.disableScroll}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export const TempuratureChart: Component<{
|
||||
stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>;
|
||||
small?: boolean;
|
||||
disableScroll?: boolean;
|
||||
}> = (p) => {
|
||||
// const [selected, setSelected] = createSignal(p.stats()![p.stats()!.length - 1].components![0].label);
|
||||
const labels = () => {
|
||||
return p.stats()![p.stats()!.length - 1].components!.map((c) => c.label);
|
||||
};
|
||||
const line = (component: string) => {
|
||||
return p.stats()?.map((s) => {
|
||||
const temp = s.components!.find((c) => c.label === component)?.temp;
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: temp || 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<For each={labels()}>
|
||||
{(label) => (
|
||||
<Grid
|
||||
gap="0"
|
||||
class="card shadow"
|
||||
style={{
|
||||
height: "fit-content",
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "0.2rem",
|
||||
}}
|
||||
>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h2>{label}</h2>
|
||||
{/* <Selector
|
||||
selected={selected()}
|
||||
items={labels()}
|
||||
onSelect={setSelected}
|
||||
/> */}
|
||||
</Flex>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
height={p.small ? SMALL_CHART_HEIGHT : CHART_HEIGHT}
|
||||
lines={() => [
|
||||
{ title: "temp", color: "#184e9f", line: line(label)! },
|
||||
]}
|
||||
disableScroll={p.disableScroll}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</For>
|
||||
);
|
||||
};
|
||||
@@ -8,18 +8,32 @@ import {
|
||||
Setter,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { client, URL } from "../..";
|
||||
import { SystemStats } from "../../types";
|
||||
import { combineClasses, generateQuery } from "../../util/helpers";
|
||||
import { generateQuery } from "../../util/helpers";
|
||||
import { useLocalStorage } from "../../util/hooks";
|
||||
import HeatBar from "../shared/HeatBar";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import Loading from "../shared/loading/Loading";
|
||||
import { CpuChart, DiskChart, MemChart } from "./Charts";
|
||||
import s from "./stats.module.scss";
|
||||
|
||||
const CurrentStats: Component<{}> = (p) => {
|
||||
const params = useParams();
|
||||
const [stats, setStats] = createSignal<SystemStats>();
|
||||
const [showCpuGraph, setShowCpuGraph] = useLocalStorage(
|
||||
false,
|
||||
"current-stats-cpu-graph-v1"
|
||||
);
|
||||
const [showMemGraph, setShowMemGraph] = useLocalStorage(
|
||||
false,
|
||||
"current-stats-mem-graph-v1"
|
||||
);
|
||||
const [showDiskGraph, setShowDiskGraph] = useLocalStorage(
|
||||
false,
|
||||
"current-stats-disk-graph-v1"
|
||||
);
|
||||
const [stats, setStats] = createSignal<SystemStats[]>([]);
|
||||
const open = useStatsWs(params, setStats);
|
||||
createEffect(() => {
|
||||
client
|
||||
@@ -28,54 +42,82 @@ const CurrentStats: Component<{}> = (p) => {
|
||||
components: true,
|
||||
processes: true,
|
||||
})
|
||||
.then(setStats);
|
||||
.then((stats) => setStats([stats]));
|
||||
});
|
||||
const mem_perc = () => {
|
||||
return (100 * stats()!.mem_used_gb) / stats()!.mem_total_gb;
|
||||
return (
|
||||
(100 * stats()[stats()!.length - 1].mem_used_gb) /
|
||||
stats()![stats().length - 1].mem_total_gb
|
||||
);
|
||||
};
|
||||
const disk_perc = () => {
|
||||
return (100 * stats()!.disk.used_gb) / stats()!.disk.total_gb;
|
||||
return (
|
||||
(100 * stats()[stats().length - 1].disk.used_gb) /
|
||||
stats()![stats().length - 1].disk.total_gb
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Grid class={s.Content} placeItems="start center">
|
||||
<Show when={stats()} fallback={<Loading type="three-dot" />}>
|
||||
<Show when={stats().length > 0} fallback={<Loading type="three-dot" />}>
|
||||
<Grid class={s.HeatBars} placeItems="center start">
|
||||
<div />
|
||||
<h1>basic</h1>
|
||||
<div />
|
||||
|
||||
<h1>cpu:</h1>
|
||||
<HeatBar
|
||||
containerClass="card shadow"
|
||||
containerStyle={{ width: "60vw", "min-width": "300px" }}
|
||||
filled={Math.floor(stats()!.cpu_perc)}
|
||||
filled={Math.floor(stats()[stats().length - 1].cpu_perc)}
|
||||
total={100}
|
||||
onClick={() => setShowCpuGraph((curr) => !curr)}
|
||||
/>
|
||||
<h1>{stats()!.cpu_perc.toFixed(1)}%</h1>
|
||||
<h1>{stats()[stats().length - 1].cpu_perc.toFixed(1)}%</h1>
|
||||
<Show when={showCpuGraph()}>
|
||||
<div />
|
||||
<CpuChart stats={stats} small disableScroll />
|
||||
<div />
|
||||
</Show>
|
||||
<h1>mem:</h1>
|
||||
<HeatBar
|
||||
containerClass="card shadow"
|
||||
containerStyle={{ width: "60vw", "min-width": "300px" }}
|
||||
filled={Math.floor(mem_perc())}
|
||||
total={100}
|
||||
onClick={() => setShowMemGraph((curr) => !curr)}
|
||||
/>
|
||||
<Grid gap="0">
|
||||
<h1>{mem_perc().toFixed(1)}%</h1>
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
{stats()!.mem_used_gb.toFixed()}GB of{" "}
|
||||
{stats()!.mem_total_gb.toFixed()}GB
|
||||
{stats()[stats().length - 1].mem_used_gb.toFixed()}GB of{" "}
|
||||
{stats()[stats().length - 1].mem_total_gb.toFixed()}GB
|
||||
</div>
|
||||
</Grid>
|
||||
<Show when={showMemGraph()}>
|
||||
<div />
|
||||
<MemChart stats={stats} small disableScroll />
|
||||
<div />
|
||||
</Show>
|
||||
<h1>disk:</h1>
|
||||
<HeatBar
|
||||
containerClass="card shadow"
|
||||
containerStyle={{ width: "60vw", "min-width": "300px" }}
|
||||
filled={Math.floor(disk_perc())}
|
||||
total={100}
|
||||
onClick={() => setShowDiskGraph(curr => !curr)}
|
||||
/>
|
||||
<Grid gap="0">
|
||||
<h1>{disk_perc().toFixed(1)}%</h1>
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
{stats()!.disk.used_gb.toFixed()}GB of{" "}
|
||||
{stats()!.disk.total_gb.toFixed()}GB
|
||||
{stats()[stats().length - 1].disk.used_gb.toFixed()}GB of{" "}
|
||||
{stats()[stats().length - 1].disk.total_gb.toFixed()}GB
|
||||
</div>
|
||||
</Grid>
|
||||
<Show when={showDiskGraph()}>
|
||||
<div />
|
||||
<DiskChart stats={stats} small disableScroll />
|
||||
<div />
|
||||
</Show>
|
||||
</Grid>
|
||||
</Show>
|
||||
</Grid>
|
||||
@@ -84,7 +126,7 @@ const CurrentStats: Component<{}> = (p) => {
|
||||
|
||||
export default CurrentStats;
|
||||
|
||||
function useStatsWs(params: Params, setStats: Setter<SystemStats>) {
|
||||
function useStatsWs(params: Params, setStats: Setter<SystemStats[]>) {
|
||||
const ws = new ReconnectingWebSocket(
|
||||
`${URL.replace("http", "ws")}/ws/stats/${params.id}${generateQuery({
|
||||
networks: "true",
|
||||
@@ -105,7 +147,10 @@ function useStatsWs(params: Params, setStats: Setter<SystemStats>) {
|
||||
}
|
||||
const stats = JSON.parse(data) as SystemStats;
|
||||
// console.log(stats);
|
||||
setStats(stats);
|
||||
setStats((stats_arr) => [
|
||||
...(stats_arr.length > 200 ? stats_arr.slice(1) : stats_arr),
|
||||
stats,
|
||||
]);
|
||||
});
|
||||
ws.addEventListener("close", () => {
|
||||
console.log("stats connection closed");
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { Accessor, Component, createEffect, createSignal, For, Match, Show, Switch } from "solid-js";
|
||||
import { Component, createEffect, createSignal, Match, Show, Switch } from "solid-js";
|
||||
import { client } from "../..";
|
||||
import { SystemStatsRecord, Timelength } from "../../types";
|
||||
import { convertTsMsToLocalUnixTsInSecs, get_to_one_sec_divisor } from "../../util/helpers";
|
||||
import { useLocalStorage } from "../../util/hooks";
|
||||
import Icon from "../shared/Icon";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import LightweightChart from "../shared/LightweightChart";
|
||||
import Loading from "../shared/loading/Loading";
|
||||
import Selector from "../shared/menu/Selector";
|
||||
import { CpuChart, DiskChart, DiskIoCharts, MemChart, NetworkIoCharts, TempuratureChart } from "./Charts";
|
||||
import s from "./stats.module.scss";
|
||||
|
||||
const TIMELENGTHS = [
|
||||
@@ -22,13 +21,7 @@ const TIMELENGTHS = [
|
||||
Timelength.OneDay,
|
||||
];
|
||||
|
||||
const COLORS = {
|
||||
blue: "#184e9f",
|
||||
orange: "#ac5c36",
|
||||
purple: "#5A0B4D",
|
||||
green: "#41764c",
|
||||
red: "#952E23",
|
||||
};
|
||||
|
||||
|
||||
const VIEWS = [
|
||||
"basic",
|
||||
@@ -58,7 +51,7 @@ const HistoricalStats: Component<{}> = (p) => {
|
||||
});
|
||||
return (
|
||||
<Grid class={s.Content}>
|
||||
<Flex alignItems="center">
|
||||
<Flex alignItems="center" justifyContent="center">
|
||||
<Flex class="card light shadow" alignItems="center">
|
||||
<button
|
||||
class="darkgrey"
|
||||
@@ -115,8 +108,8 @@ const HistoricalStats: Component<{}> = (p) => {
|
||||
</Match>
|
||||
<Match when={view() === "i / o"}>
|
||||
<Grid class={s.Charts}>
|
||||
<DiskIoCharts stats={stats} />
|
||||
<NetworkIoCharts stats={stats} />
|
||||
<DiskIoCharts stats={stats} />
|
||||
</Grid>
|
||||
</Match>
|
||||
<Match when={view() === "temp"}>
|
||||
@@ -130,259 +123,4 @@ const HistoricalStats: Component<{}> = (p) => {
|
||||
);
|
||||
};
|
||||
|
||||
const CpuChart: Component<{
|
||||
stats: Accessor<SystemStatsRecord[] | undefined>;
|
||||
}> = (p) => {
|
||||
const line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value: s.cpu_perc,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={line()}>
|
||||
<Grid gap="0" class="card shadow" style={{ height: "fit-content" }}>
|
||||
<h2>cpu</h2>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [{ title: "%", color: COLORS.blue, line: line()! }]}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
const MemChart: Component<{
|
||||
stats: Accessor<SystemStatsRecord[] | undefined>;
|
||||
}> = (p) => {
|
||||
const [selected, setSelected] = createSignal("%");
|
||||
const line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value:
|
||||
selected() === "%"
|
||||
? (100 * s.mem_used_gb) / s.mem_total_gb
|
||||
: s.mem_used_gb,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={line()}>
|
||||
<Grid gap="0" class="card shadow" style={{ height: "fit-content" }}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h2>memory</h2>
|
||||
{/* <Selector
|
||||
selected={selected()}
|
||||
items={["%", "GB"]}
|
||||
onSelect={setSelected}
|
||||
/> */}
|
||||
</Flex>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [{ title: selected(), color: COLORS.blue, line: line()! }]}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
const DiskChart: Component<{
|
||||
stats: Accessor<SystemStatsRecord[] | undefined>;
|
||||
}> = (p) => {
|
||||
const [selected, setSelected] = createSignal("%");
|
||||
const line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value:
|
||||
selected() === "%"
|
||||
? (100 * s.disk.used_gb) / s.disk.total_gb
|
||||
: s.disk.used_gb,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={line()}>
|
||||
<Grid gap="0" class="card shadow" style={{ height: "fit-content" }}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h2>disk</h2>
|
||||
{/* <Selector
|
||||
selected={selected()}
|
||||
items={["%", "GB"]}
|
||||
onSelect={setSelected}
|
||||
/> */}
|
||||
</Flex>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [{ title: selected(), color: "#184e9f", line: line()! }]}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
const NetworkIoCharts: Component<{
|
||||
stats: Accessor<SystemStatsRecord[] | undefined>;
|
||||
}> = (p) => {
|
||||
const recv_line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value:
|
||||
s.networks?.length || 0 > 0
|
||||
? s.networks!.map((n) => n.recieved_kb).reduce((p, c) => p + c) /
|
||||
get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
const trans_line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value:
|
||||
s.networks?.length || 0 > 0
|
||||
? s.networks!.map((n) => n.transmitted_kb).reduce((p, c) => p + c) /
|
||||
get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={recv_line()}>
|
||||
<Grid gap="0" class="card shadow" style={{ height: "fit-content" }}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h2>network sent kb/s</h2>
|
||||
</Flex>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [
|
||||
{ title: "kb/s", color: "#184e9f", line: trans_line()! },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid gap="0" class="card shadow" style={{ height: "fit-content" }}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h2>network received kb/s</h2>
|
||||
</Flex>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [
|
||||
{ title: "kb/s", color: "#184e9f", line: recv_line()! },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
const DiskIoCharts: Component<{
|
||||
stats: Accessor<SystemStatsRecord[] | undefined>;
|
||||
}> = (p) => {
|
||||
const read_line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value:
|
||||
s.disk.disks?.length || 0 > 0
|
||||
? s.disk.read_kb /
|
||||
get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
const write_line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value:
|
||||
s.disk.disks?.length || 0 > 0
|
||||
? s.disk.write_kb / get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Show when={read_line()}>
|
||||
<Grid gap="0" class="card shadow" style={{ height: "fit-content" }}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h2>disk read kb/s</h2>
|
||||
</Flex>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [
|
||||
{ title: "kb/s", color: "#184e9f", line: read_line()! },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid gap="0" class="card shadow" style={{ height: "fit-content" }}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h2>disk write kb/s</h2>
|
||||
</Flex>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [
|
||||
{ title: "kb/s", color: "#184e9f", line: write_line()! },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
const TempuratureChart: Component<{
|
||||
stats: Accessor<SystemStatsRecord[] | undefined>;
|
||||
}> = (p) => {
|
||||
// const [selected, setSelected] = createSignal(p.stats()![p.stats()!.length - 1].components![0].label);
|
||||
const labels = () => {
|
||||
return p.stats()![p.stats()!.length - 1].components!.map((c) => c.label);
|
||||
};
|
||||
const line = (component: string) => {
|
||||
return p.stats()?.map((s) => {
|
||||
const temp = s.components!.find((c) => c.label === component)?.temp;
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value: temp || 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<For each={labels()}>
|
||||
{(label) => (
|
||||
<Grid
|
||||
gap="0"
|
||||
class="card shadow"
|
||||
style={{ height: "fit-content" }}
|
||||
>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h2>{label}</h2>
|
||||
{/* <Selector
|
||||
selected={selected()}
|
||||
items={labels()}
|
||||
onSelect={setSelected}
|
||||
/> */}
|
||||
</Flex>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [
|
||||
{ title: "temp", color: "#184e9f", line: line(label)! },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</For>
|
||||
);
|
||||
};
|
||||
|
||||
export default HistoricalStats;
|
||||
|
||||
@@ -18,7 +18,7 @@ const Stats: Component<{}> = (p) => {
|
||||
const [view, setView] = useLocalStorage("current", "stats-view-v1");
|
||||
return (
|
||||
<Grid class={s.Content}>
|
||||
<Flex alignItems="center">
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<Header />
|
||||
<Selector
|
||||
targetClass="grey"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@use "../../style/colors.scss" as c;
|
||||
|
||||
.Charts {
|
||||
grid-template-columns: repeat(auto-fit, minmax(520px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
|
||||
}
|
||||
|
||||
.LightweightChart {
|
||||
|
||||
Reference in New Issue
Block a user