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' ? - - : -

- Server is offline. -

- } -
-
+
+ + {status !== 'offline' ? + + : +

+ Server is offline. +

+ } +
+ + {status !== 'offline' ? + + : +

+ Server is offline. +

+ } +
+ + {status !== 'offline' ? + + : +

+ Server is offline. +

+ } +
+ + {status !== 'offline' ? + + : +

+ Server is 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 {