From 62b178ed0297549be9eb009c968d5d47d4fe8ffb Mon Sep 17 00:00:00 2001
From: DaneEveritt
Date: Fri, 13 May 2022 23:00:59 -0400
Subject: [PATCH] Show network usage on the server console view
---
.babel-plugin-macrosrc.js | 4 +-
CHANGELOG.md | 1 +
.../components/server/ServerDetailsBlock.tsx | 29 +++-
.../scripts/components/server/StatGraphs.tsx | 159 +++++++++---------
resources/scripts/helpers.ts | 2 -
5 files changed, 106 insertions(+), 89 deletions(-)
diff --git a/.babel-plugin-macrosrc.js b/.babel-plugin-macrosrc.js
index 4bcdbb88b..feea84525 100644
--- a/.babel-plugin-macrosrc.js
+++ b/.babel-plugin-macrosrc.js
@@ -5,7 +5,7 @@ module.exports = {
},
styledComponents: {
pure: true,
- displayName: false,
- fileName: false,
+ displayName: true,
+ fileName: true,
},
};
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6eeff7c3e..78a426356 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@ using these eggs should be updated to account for the new format.
* Adds support for naming docker image values in an Egg to improve front-end display capabilities.
* Adds command to return the configuration for a specific node in both YAML and JSON format (`php artisan p:node:configuration`).
* Adds command to return a list of all nodes available on the Panel in both table and JSON format (`php artisan p:node:list`).
+* Adds server network (inbound/outbound) usage graphs to the console screen.
### Removed
* Removes Google Analytics from the front end code.
diff --git a/resources/scripts/components/server/ServerDetailsBlock.tsx b/resources/scripts/components/server/ServerDetailsBlock.tsx
index 34d975b81..3eb5f01e9 100644
--- a/resources/scripts/components/server/ServerDetailsBlock.tsx
+++ b/resources/scripts/components/server/ServerDetailsBlock.tsx
@@ -1,20 +1,24 @@
import React, { useEffect, useState } from 'react';
import tw, { TwStyle } from 'twin.macro';
-import { faCircle, faEthernet, faHdd, faMemory, faMicrochip, faServer } from '@fortawesome/free-solid-svg-icons';
+import {
+ faArrowCircleDown,
+ faArrowCircleUp,
+ faCircle,
+ faEthernet,
+ faHdd,
+ faMemory,
+ faMicrochip,
+ faServer,
+} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { bytesToHuman, megabytesToHuman, formatIp } from '@/helpers';
+import { bytesToHuman, formatIp, megabytesToHuman } from '@/helpers';
import TitledGreyBox from '@/components/elements/TitledGreyBox';
import { ServerContext } from '@/state/server';
import CopyOnClick from '@/components/elements/CopyOnClick';
import { SocketEvent, SocketRequest } from '@/components/server/events';
import UptimeDuration from '@/components/server/UptimeDuration';
-interface Stats {
- memory: number;
- cpu: number;
- disk: number;
- uptime: number;
-}
+type Stats = Record<'memory' | 'cpu' | 'disk' | 'uptime' | 'rx' | 'tx', number>;
function statusToColor (status: string | null, installing: boolean): TwStyle {
if (installing) {
@@ -32,7 +36,7 @@ function statusToColor (status: string | null, installing: boolean): TwStyle {
}
const ServerDetailsBlock = () => {
- const [ stats, setStats ] = useState({ memory: 0, cpu: 0, disk: 0, uptime: 0 });
+ const [ stats, setStats ] = useState({ memory: 0, cpu: 0, disk: 0, uptime: 0, tx: 0, rx: 0 });
const status = ServerContext.useStoreState(state => state.status.value);
const connected = ServerContext.useStoreState(state => state.socket.connected);
@@ -50,6 +54,8 @@ const ServerDetailsBlock = () => {
memory: stats.memory_bytes,
cpu: stats.cpu_absolute,
disk: stats.disk_bytes,
+ tx: stats.network.tx_bytes,
+ rx: stats.network.rx_bytes,
uptime: stats.uptime || 0,
});
};
@@ -115,6 +121,11 @@ const ServerDetailsBlock = () => {
{bytesToHuman(stats.disk)}
/ {diskLimit}
+
+
+ {bytesToHuman(stats.tx)}
+ {bytesToHuman(stats.rx)}
+
);
};
diff --git a/resources/scripts/components/server/StatGraphs.tsx b/resources/scripts/components/server/StatGraphs.tsx
index 8e66f393a..00e1c511d 100644
--- a/resources/scripts/components/server/StatGraphs.tsx
+++ b/resources/scripts/components/server/StatGraphs.tsx
@@ -1,15 +1,14 @@
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useRef, useState } from 'react';
import Chart, { ChartConfiguration } from 'chart.js';
import { ServerContext } from '@/state/server';
-import { bytesToMegabytes } from '@/helpers';
import merge from 'deepmerge';
import TitledGreyBox from '@/components/elements/TitledGreyBox';
-import { faMemory, faMicrochip } from '@fortawesome/free-solid-svg-icons';
+import { faEthernet, faMemory, faMicrochip } from '@fortawesome/free-solid-svg-icons';
import tw from 'twin.macro';
import { SocketEvent } from '@/components/server/events';
import useWebsocketEvent from '@/plugins/useWebsocketEvent';
-const chartDefaults = (ticks?: Chart.TickOptions | undefined): ChartConfiguration => ({
+const chartDefaults = (ticks?: Chart.TickOptions): ChartConfiguration => ({
type: 'line',
options: {
legend: {
@@ -69,38 +68,43 @@ const chartDefaults = (ticks?: Chart.TickOptions | undefined): ChartConfiguratio
},
});
+type ChartState = [ (node: HTMLCanvasElement | null) => void, Chart | undefined ];
+
+/**
+ * Creates an element ref and a chart instance.
+ */
+const useChart = (options?: Chart.TickOptions): ChartState => {
+ const [ chart, setChart ] = useState();
+
+ const ref = useCallback<(node: HTMLCanvasElement | null) => void>(node => {
+ if (!node) return;
+
+ const chart = new Chart(node.getContext('2d')!, chartDefaults(options));
+
+ setChart(chart);
+ }, []);
+
+ return [ ref, chart ];
+};
+
+const updateChartDataset = (chart: Chart | null | undefined, value: Chart.ChartPoint & number): void => {
+ if (!chart || !chart.data?.datasets) return;
+
+ const data = chart.data.datasets[0].data!;
+ data.push(value);
+ data.shift();
+ chart.update({ lazy: true });
+};
+
export default () => {
const status = ServerContext.useStoreState(state => state.status.value);
const limits = ServerContext.useStoreState(state => state.server.data!.limits);
- const [ memory, setMemory ] = useState();
- const [ cpu, setCpu ] = useState();
-
- const memoryRef = useCallback<(node: HTMLCanvasElement | null) => void>(node => {
- if (!node) {
- return;
- }
-
- setMemory(
- new Chart(node.getContext('2d')!, chartDefaults({
- callback: (value) => `${value}Mb `,
- suggestedMax: limits.memory,
- })),
- );
- }, []);
-
- const cpuRef = useCallback<(node: HTMLCanvasElement | null) => void>(node => {
- if (!node) {
- return;
- }
-
- setCpu(
- new Chart(node.getContext('2d')!, chartDefaults({
- callback: (value) => `${value}% `,
- suggestedMax: limits.cpu,
- })),
- );
- }, []);
+ const previous = useRef>({ tx: -1, rx: -1 });
+ const [ cpuRef, cpu ] = useChart({ callback: (value) => `${value}% `, suggestedMax: limits.cpu });
+ const [ memoryRef, memory ] = useChart({ callback: (value) => `${value}Mb `, suggestedMax: limits.memory });
+ const [ txRef, tx ] = useChart({ callback: (value) => `${value}Kb/s ` });
+ const [ rxRef, rx ] = useChart({ callback: (value) => `${value}Kb/s ` });
useWebsocketEvent(SocketEvent.STATS, (data: string) => {
let stats: any = {};
@@ -110,54 +114,57 @@ export default () => {
return;
}
- if (memory && memory.data.datasets) {
- const data = memory.data.datasets[0].data!;
+ updateChartDataset(cpu, stats.cpu_absolute);
+ updateChartDataset(memory, Math.floor(stats.memory_bytes / 1024 / 1024));
+ updateChartDataset(tx, previous.current.tx < 0 ? 0 : Math.max(0, stats.network.tx_bytes - previous.current.tx) / 1024);
+ updateChartDataset(rx, previous.current.rx < 0 ? 0 : Math.max(0, stats.network.rx_bytes - previous.current.rx) / 1024);
- data.push(bytesToMegabytes(stats.memory_bytes));
- data.shift();
-
- memory.update({ lazy: true });
- }
-
- if (cpu && cpu.data.datasets) {
- const data = cpu.data.datasets[0].data!;
-
- data.push(stats.cpu_absolute);
- data.shift();
-
- cpu.update({ lazy: true });
- }
+ previous.current = { tx: stats.network.tx_bytes, rx: stats.network.rx_bytes };
});
return (
-
-
-
- {status !== 'offline' ?
-
- :
-
- Server is offline.
-
- }
-
-
-
-
- {status !== 'offline' ?
-
-
+
+
+ {status !== 'offline' ?
+
+ :
+
+ Server is offline.
+
+ }
+
+
+ {status !== 'offline' ?
+
+
+ {status !== 'offline' ?
+
+
+ {status !== 'offline' ?
+
);
};
diff --git a/resources/scripts/helpers.ts b/resources/scripts/helpers.ts
index cd57c55b6..67d079427 100644
--- a/resources/scripts/helpers.ts
+++ b/resources/scripts/helpers.ts
@@ -1,5 +1,3 @@
-export const bytesToMegabytes = (bytes: number) => Math.floor(bytes / 1024 / 1024);
-
export const megabytesToBytes = (mb: number) => Math.floor(mb * 1024 * 1024);
export function bytesToHuman (bytes: number): string {