Feat: Map Node Stats (Version, Operating System etc) to each Node in Feed (#591)

* added functionality for specifying node details per each node

* Backend done
Added new item in Ranking
node_map, mapping node id to node detail

Co-authored-by: Cyndie Kamau <cyndiekamaa@gmail.com>

* feat: last frontend working version

* chore: Clean up unused code

* fix(frontend): update node details to carry 10 fields

* chore: remove unnecessary code

* chore: run cargo fmt for formatting

* chore: run prettier to format frontend

* fixed e2e tests added missing struct params

* remoted .idea file

* Hide new columns by default, default to - if no data, and remove .idea folder

---------

Co-authored-by: MrishoLukamba <abdulrazzaqlukamba@gmail.com>
Co-authored-by: Cyndie Kamau <cyndiekamaa@gmail.com>
This commit is contained in:
James Wilson
2024-09-25 14:01:42 +01:00
committed by GitHub
parent bb4c7272d8
commit 0cd8726ce5
34 changed files with 611 additions and 53 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
htdocs
.idea
backend/target
# dependencies
+1
View File
@@ -1,3 +1,4 @@
target
*.Dockerfile
.git
.idea
@@ -69,7 +69,7 @@ pub enum FromShardWebsocket {
Disconnected,
}
/// The aggregator can these messages back to a shard connection.
/// The aggregator can send these messages back to a shard connection.
#[derive(Debug)]
pub enum ToShardWebsocket {
/// Mute messages to the core by passing the shard-local ID of them.
@@ -84,7 +84,7 @@ pub enum ToShardWebsocket {
pub enum FromFeedWebsocket {
/// When the socket is opened, it'll send this first
/// so that we have a way to communicate back to it.
/// Unbounded so that slow feeds don't block aggregato
/// Unbounded so that slow feeds don't block aggregator
/// progress.
Initialize {
channel: flume::Sender<ToFeedWebsocket>,
+13 -4
View File
@@ -183,12 +183,18 @@ impl FeedMessageWrite for AddedNode<'_> {
let AddedNode(nid, node, expose_node_details) = self;
let details = node.details();
// Hide the ip, sysinfo and hwbench if the `expose_node_details` flag was not specified.
// Always include sysinfo, conditionally include ip and hwbench based on expose_node_details.
let node_hwbench = node.hwbench();
let (ip, sys_info, hwbench) = if *expose_node_details {
(&details.ip, &details.sysinfo, &node_hwbench)
let ip = if *expose_node_details {
&details.ip
} else {
(&None, &None, &None)
&None
};
let sys_info = &details.sysinfo;
let hwbench = if *expose_node_details {
&node_hwbench
} else {
&None
};
let details = (
@@ -197,6 +203,9 @@ impl FeedMessageWrite for AddedNode<'_> {
&details.version,
&details.validator,
&details.network_id,
&details.target_os,
&details.target_arch,
&details.target_env,
&ip,
&sys_info,
&hwbench,
@@ -16,7 +16,6 @@
use super::counter::{Counter, CounterValue};
use crate::feed_message::ChainStats;
// These are the benchmark scores generated on our reference hardware.
const REFERENCE_CPU_SCORE: u64 = 1028;
const REFERENCE_MEMORY_SCORE: u64 = 14899;
+1 -1
View File
@@ -39,7 +39,7 @@ impl<K> Counter<K>
where
K: Sized + std::hash::Hash + Eq,
{
/// Either adds or removes a single occurrence of a given `key`.
/// Either adds or removes a single occurence of a given `key`.
pub fn modify<'a, Q>(&mut self, key: Option<&'a Q>, op: CounterValue)
where
Q: ?Sized + std::hash::Hash + Eq,
@@ -19,5 +19,4 @@
mod hash;
mod node_message;
pub use hash::Hash;
pub use node_message::*;
+21 -2
View File
@@ -136,6 +136,9 @@ pub struct NodeDetails {
pub version: String,
pub validator: Option<String>,
pub network_id: Option<String>,
pub os: String,
pub arch: String,
pub target_env: String,
pub ip: Option<String>,
pub sysinfo: Option<NodeSysInfo>,
}
@@ -144,7 +147,6 @@ impl FeedMessage {
/// Decode a slice of bytes into a vector of feed messages
pub fn from_bytes(bytes: &[u8]) -> Result<Vec<FeedMessage>, anyhow::Error> {
let v: Vec<&RawValue> = serde_json::from_slice(bytes)?;
let mut feed_messages = vec![];
for raw_keyval in v.chunks(2) {
let raw_key = raw_keyval[0];
@@ -161,6 +163,8 @@ impl FeedMessage {
// Deserialize the feed message to a value based on the "action" key
fn decode(action: u8, raw_val: &RawValue) -> Result<FeedMessage, anyhow::Error> {
println!("\n\n");
println!("{raw_val:#?}");
let feed_message = match action {
// Version:
0 => {
@@ -189,7 +193,19 @@ impl FeedMessage {
3 => {
let (
node_id,
(name, implementation, version, validator, network_id, ip, sysinfo, hwbench),
(
name,
implementation,
version,
validator,
network_id,
os,
arch,
target_env,
ip,
sysinfo,
hwbench,
),
stats,
io,
hardware,
@@ -209,6 +225,9 @@ impl FeedMessage {
version,
validator,
network_id,
os,
arch,
target_env,
ip,
sysinfo,
},
+1 -1
View File
@@ -30,7 +30,7 @@
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
+1 -1
View File
@@ -1,3 +1,3 @@
# substrate-telemetry-frontend
### [Documentation](https://github.com/paritytech/substrate-telemetry/blob/master/README.md)
### [Documentation](https://github.com/paritytech/substrate-telemetry/blob/master/README.md)
+10 -8
View File
@@ -1,23 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<title>Polkadot Telemetry</title>
<script type="text/javascript" src="/tmp/env-config.js"></script>
<style>
body, html {
body,
html {
background: #fff;
color: #111;
}
</style>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
</body>
</html>
+1 -1
View File
@@ -1,3 +1,3 @@
// loading images gives back a path. This is what we replace
// that with for tests:
module.exports = 'test-image-stub';
module.exports = 'test-image-stub';
+1 -1
View File
@@ -1,2 +1,2 @@
// For loading styles, give back an empty object in tests:
module.exports = {};
module.exports = {};
+3 -3
View File
@@ -14,6 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
+4 -3
View File
@@ -20,7 +20,8 @@ module.exports = {
testEnvironment: 'jsdom',
setupFiles: ['<rootDir>/setupJest.js'],
moduleNameMapper: {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/assets/mock.image.js",
"\\.(css|less|scss|sass)$": "<rootDir>/assets/mock.style.js"
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/assets/mock.image.js',
'\\.(css|less|scss|sass)$': '<rootDir>/assets/mock.style.js',
},
};
};
+1 -1
View File
@@ -22,4 +22,4 @@ global.TextEncoder = TextEncoder;
global.TextDecoder = TextDecoder;
// polyfill fetch since it's not in jsdom:
require('whatwg-fetch');
require('whatwg-fetch');
+17 -3
View File
@@ -46,6 +46,7 @@ export default class App extends React.Component {
constructor(props: Record<string, unknown>) {
super(props);
//todo! Check what's important to set to true, false
this.settings = new PersistentObject(
'settings',
{
@@ -55,8 +56,6 @@ export default class App extends React.Component {
networkId: false,
peers: true,
txs: true,
cpu: true,
mem: true,
upload: false,
download: false,
stateCacheSize: false,
@@ -65,12 +64,27 @@ export default class App extends React.Component {
diskWrite: false,
blocknumber: true,
blockhash: true,
blocktime: true,
finalized: false,
finalizedhash: false,
blocktime: true,
blockpropagation: true,
blocklasttime: false,
uptime: false,
version: false,
target_os: false,
target_arch: false,
cpu: false,
cpu_hashrate_score: true,
cpu_vendor: true,
core_count: false,
mem: true,
memory: false,
linux_distro: false,
linux_kernel: false,
memory_memcpy_score: true,
disk_sequential_write_score: true,
disk_random_write_score: true,
is_virtual_machine: false,
},
(settings) => {
const selectedColumns = this.selectedColumns(settings);
-1
View File
@@ -269,7 +269,6 @@ export class Connection {
case ACTIONS.NodeStats: {
const [id, nodeStats] = message.payload;
nodes.mutAndMaybeSort(
id,
(node) => node.updateStats(nodeStats),
+24 -1
View File
@@ -25,6 +25,15 @@ export type NodeId = Id<'Node'>;
export type NodeName = Opaque<string, 'NodeName'>;
export type NodeImplementation = Opaque<string, 'NodeImplementation'>;
export type NodeVersion = Opaque<string, 'NodeVersion'>;
export type OperatingSystem = Opaque<string, 'OperatingSystem'>;
export type CpuArchitecture = Opaque<string, 'CpuArchitecture'>;
export type Cpu = string;
export type CpuCores = number;
export type TargetEnv = string;
export type Memory = number;
export type VirtualMachine = boolean;
export type LinuxKernel = string;
export type LinuxDistro = string;
export type BlockNumber = Opaque<number, 'BlockNumber'>;
export type BlockHash = Opaque<string, 'BlockHash'>;
export type Address = Opaque<string, 'Address'>;
@@ -43,6 +52,15 @@ export type Bytes = Opaque<number, 'Bytes'>;
export type BytesPerSecond = Opaque<number, 'BytesPerSecond'>;
export type NetworkId = Opaque<string, 'NetworkId'>;
export type NodeSysInfo = {
cpu: string;
memory: number;
core_count: number;
linux_kernel: string;
linux_distro: string;
is_virtual_machine: boolean;
};
export type BlockDetails = [
BlockNumber,
BlockHash,
@@ -56,8 +74,13 @@ export type NodeDetails = [
NodeVersion,
Maybe<Address>,
Maybe<NetworkId>,
Maybe<string>
OperatingSystem,
CpuArchitecture,
TargetEnv,
undefined,
NodeSysInfo
];
export type NodeStats = [PeerCount, TransactionCount];
export type NodeIO = [Array<Bytes>];
export type NodeHardware = [
+19 -1
View File
@@ -38,6 +38,15 @@ import {
BlockPropagationColumn,
LastBlockColumn,
UptimeColumn,
CpuArchitectureColumn, //extra columns added
CpuColumn,
CpuCoresColumn,
LinuxKernelColumn,
IsVirtualMachineColumn,
MemoryColumn,
OperatingSystemColumn,
VersionColumn,
LinuxDistroColumn,
} from './';
export type Column =
@@ -58,7 +67,16 @@ export type Column =
| typeof BlockTimeColumn
| typeof BlockPropagationColumn
| typeof LastBlockColumn
| typeof UptimeColumn;
| typeof UptimeColumn
| typeof CpuArchitectureColumn
| typeof CpuColumn
| typeof CpuCoresColumn
| typeof LinuxDistroColumn
| typeof LinuxKernelColumn
| typeof IsVirtualMachineColumn
| typeof MemoryColumn
| typeof OperatingSystemColumn
| typeof VersionColumn;
export interface ColumnProps {
node: Node;
@@ -0,0 +1,43 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which CPU Architecture the node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class CpuArchitectureColumn extends React.Component<ColumnProps> {
public static readonly label = 'CPU Architecture';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'target_arch';
public static readonly sortBy = ({ target_arch }: Node) => target_arch || '';
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.hash;
}
render() {
const { target_arch } = this.props.node;
this.data = target_arch;
return <td className="Column">{target_arch || '-'}</td>;
}
}
@@ -0,0 +1,44 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which CPU type the node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class CpuColumn extends React.Component<ColumnProps> {
public static readonly label = 'CPU Column';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'cpu';
public static readonly sortBy = ({ cpu }: Node) => cpu || 0;
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.cpu;
}
render() {
const { cpu } = this.props.node;
this.data = cpu;
return <td className="Column">{cpu || '-'}</td>;
}
}
@@ -0,0 +1,41 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class CpuCoresColumn extends React.Component<ColumnProps> {
public static readonly label = 'CPU Cores';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'core_count';
public static readonly sortBy = ({ core_count }: Node) => core_count || 0;
private data: number;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.core_count;
}
render() {
const { core_count } = this.props.node;
this.data = core_count;
return <td className="Column">{core_count || '-'}</td>;
}
}
@@ -0,0 +1,44 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying whether each node runs a VM or not
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class IsVirtualMachineColumn extends React.Component<ColumnProps> {
public static readonly label = 'Virtual Machine';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'is_virtual_machine';
public static readonly sortBy = ({ is_virtual_machine }: Node) =>
is_virtual_machine || false;
private data: boolean;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.is_virtual_machine;
}
render() {
const { is_virtual_machine } = this.props.node;
this.data = is_virtual_machine;
return <td className="Column">{is_virtual_machine ? 'Yes' : 'No'}</td>;
}
}
@@ -0,0 +1,45 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying type of distro each node is running (Linux Distribution)
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class LinuxDistroColumn extends React.Component<ColumnProps> {
public static readonly label = 'Linux Distro';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'linux_distro';
public static readonly sortBy = ({ linux_distro }: Node) =>
linux_distro || '';
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.linux_distro;
}
render() {
const { linux_distro } = this.props.node;
this.data = linux_distro;
return <td className="Column">{linux_distro || '-'}</td>;
}
}
@@ -0,0 +1,43 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which kernel each node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class LinuxKernelColumn extends React.Component<ColumnProps> {
public static readonly label = 'Linux Kernel';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'linux_kernel';
public static readonly sortBy = ({ linux_kernel }: Node) => linux_kernel || 0;
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.linux_kernel;
}
render() {
const { linux_kernel } = this.props.node;
this.data = linux_kernel;
return <td className="Column">{linux_kernel || '-'}</td>;
}
}
@@ -0,0 +1,43 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column specifying type of memory each node has
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class MemoryColumn extends React.Component<ColumnProps> {
public static readonly label = 'memory';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'memory';
public static readonly sortBy = ({ memory }: Node) => memory || '';
private data: number;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.memory;
}
render() {
const { memory } = this.props.node;
this.data = memory;
return <td className="Column">{memory || '-'}</td>;
}
}
@@ -0,0 +1,44 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which OS the node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class OperatingSystemColumn extends React.Component<ColumnProps> {
public static readonly label = 'OS';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'target_os';
public static readonly sortBy = ({ target_os }: Node) => target_os || '';
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.hash;
}
render() {
const { target_os } = this.props.node;
this.data = target_os;
return <td className="Column">{target_os || '-'}</td>;
}
}
@@ -0,0 +1,44 @@
// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//Column for specifying which polkadot version node is running on
import * as React from 'react';
import { ColumnProps } from './';
import { Node } from '../../../state';
import icon from '../../../icons/file-binary.svg';
export class VersionColumn extends React.Component<ColumnProps> {
public static readonly label = 'version';
public static readonly icon = icon;
public static readonly width = 154;
public static readonly setting = 'version';
public static readonly sortBy = ({ version }: Node) => version || '';
private data: string;
public shouldComponentUpdate(nextProps: ColumnProps) {
return this.data !== nextProps.node.version;
}
render() {
const { version } = this.props.node;
this.data = version;
return <td className="Column">{version || '-'}</td>;
}
}
@@ -33,3 +33,12 @@ export * from './BlockTimeColumn';
export * from './BlockPropagationColumn';
export * from './LastBlockColumn';
export * from './UptimeColumn';
export * from './CpuArchitectureColumn'; //extra columns added
export * from './CpuCoresColumn';
export * from './LinuxDistroColumn';
export * from './IsVirtualMachineColumn';
export * from './MemoryColumn';
export * from './CpuColumn';
export * from './OperatingSystemColumn';
export * from './VersionColumn';
export * from './LinuxKernelColumn';
+19 -1
View File
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// Each Row in the List Page
import * as React from 'react';
import { Types } from '../../common';
import { Node } from '../../state';
@@ -38,6 +39,15 @@ import {
BlockPropagationColumn,
LastBlockColumn,
UptimeColumn,
CpuArchitectureColumn,
CpuColumn,
CpuCoresColumn,
MemoryColumn,
OperatingSystemColumn,
VersionColumn,
IsVirtualMachineColumn,
LinuxDistroColumn,
LinuxKernelColumn,
} from './';
import './Row.css';
@@ -72,8 +82,16 @@ export class Row extends React.Component<RowProps, RowState> {
BlockPropagationColumn,
LastBlockColumn,
UptimeColumn,
VersionColumn,
OperatingSystemColumn,
CpuArchitectureColumn,
CpuColumn,
CpuCoresColumn,
MemoryColumn,
LinuxDistroColumn,
LinuxKernelColumn,
IsVirtualMachineColumn,
];
private renderedChangeRef = 0;
public shouldComponentUpdate(nextProps: RowProps): boolean {
+63 -3
View File
@@ -26,8 +26,8 @@ export const PINNED_CHAINS = {
};
export function comparePinnedChains(a: string, b: string) {
const aWeight = PINNED_CHAINS[a] || 1024;
const bWeight = PINNED_CHAINS[b] || 1024;
const aWeight: number = (PINNED_CHAINS as any)[a] || 1024;
const bWeight: number = (PINNED_CHAINS as any)[b] || 1024;
return aWeight - bWeight;
}
@@ -62,6 +62,17 @@ export class Node {
public readonly validator: Maybe<Types.Address>;
public readonly networkId: Maybe<Types.NetworkId>;
public readonly startupTime: Maybe<Types.Timestamp>;
public readonly target_os: Types.OperatingSystem;
public readonly target_arch: Types.CpuArchitecture;
public readonly target_env: Types.TargetEnv;
public readonly sysInfo: Types.NodeSysInfo;
public readonly cpu: Types.Cpu;
public readonly memory: Types.Memory;
public readonly core_count: Types.CpuCores;
public readonly linux_kernel: Types.LinuxKernel;
public readonly linux_distro: Types.LinuxDistro;
public readonly is_virtual_machine: Types.VirtualMachine;
public readonly sortableName: string;
public readonly sortableVersion: number;
@@ -102,7 +113,18 @@ export class Node {
location: Maybe<Types.NodeLocation>,
startupTime: Maybe<Types.Timestamp>
) {
const [name, implementation, version, validator, networkId] = nodeDetails;
const [
name,
implementation,
version,
validator,
networkId,
target_os,
target_arch,
target_env,
customNullField,
sysInfo,
] = nodeDetails; //step 4
this.pinned = pinned;
@@ -113,6 +135,30 @@ export class Node {
this.validator = validator;
this.networkId = networkId;
this.startupTime = startupTime;
this.target_os = target_os;
this.target_arch = target_arch;
this.target_env = target_env;
this.sysInfo = {
cpu: sysInfo?.cpu ?? '', // Default value or some global constant
memory: sysInfo?.memory ?? 0,
core_count: sysInfo?.core_count ?? 0,
linux_kernel: sysInfo?.linux_kernel ?? '',
linux_distro: sysInfo?.linux_distro ?? '',
is_virtual_machine: sysInfo?.is_virtual_machine ?? false,
};
this.cpu = sysInfo && sysInfo.cpu ? sysInfo.cpu : '';
this.memory = sysInfo && sysInfo.memory ? Number(sysInfo.memory) : 0;
this.core_count =
sysInfo && sysInfo.core_count ? Number(sysInfo.core_count) : 0;
this.linux_kernel =
sysInfo && sysInfo.linux_kernel ? sysInfo.linux_kernel : '';
this.linux_distro =
sysInfo && sysInfo.linux_distro ? sysInfo.linux_distro : '';
this.is_virtual_machine =
sysInfo && sysInfo.is_virtual_machine
? Boolean(sysInfo.is_virtual_machine)
: false;
const [major = 0, minor = 0, patch = 0] = (version || '0.0.0')
.split('.')
@@ -255,6 +301,20 @@ export interface StateSettings {
blockpropagation: boolean;
blocklasttime: boolean;
uptime: boolean;
version: boolean;
target_os: boolean;
target_arch: boolean;
cpu: boolean;
core_count: boolean;
memory: boolean;
is_virtual_machine: boolean;
linux_distro: boolean;
linux_kernel: boolean;
cpu_hashrate_score: boolean;
memory_memcpy_score: boolean;
disk_sequential_write_score: boolean;
disk_random_write_score: boolean;
cpu_vendor: boolean;
}
export interface State {
+1 -5
View File
@@ -4,11 +4,7 @@
"outDir": "./build/",
"module": "es2020",
"target": "es2015",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"sourceMap": true,
"allowJs": false,
"jsx": "react",
+7 -7
View File
@@ -16,19 +16,19 @@ module.exports = {
// allow 'import "foo.css"' and '@import "foo.css" in css files
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
generator: { filename: 'styles/[name].[contenthash][ext]' }
generator: { filename: 'styles/[name].[contenthash][ext]' },
},
{
// allow 'import Icon from "./icon.png"'
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
generator: { filename: 'images/[name].[contenthash][ext]' }
generator: { filename: 'images/[name].[contenthash][ext]' },
},
{
// allow CSS @url('./my-font.woff2')" style font loading
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: { filename: 'fonts/[name].[contenthash][ext]' }
generator: { filename: 'fonts/[name].[contenthash][ext]' },
},
],
},
@@ -36,8 +36,8 @@ module.exports = {
// Use our index.html as a starting point (to add script links etc to)
// and make sure to use/copy over the favicon too.
new HtmlWebpackPlugin({
favicon: "./assets/favicon.svg",
template: "./assets/index.html"
favicon: './assets/favicon.svg',
template: './assets/index.html',
}),
],
resolve: {
@@ -46,6 +46,6 @@ module.exports = {
output: {
filename: 'main.[contenthash].js',
path: path.resolve(__dirname, 'build'),
clean: true
clean: true,
},
};
};