systemInfoComponent.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import React, { Component } from "react";
  2. import * as ReactDom from "react-dom/client";
  3. import { DAL } from "../DAL/systemInfo";
  4. import {SystemInfo, CpuInfo, NetworkInfo, DriveInfo} from "../../src/models/systemInfo";
  5. import menu from "./menu";
  6. interface SystemInfoComponentState {
  7. loading: boolean;
  8. hidden: boolean;
  9. }
  10. interface SystemInfoProps {
  11. hostname: string;
  12. setHidden: any;//((val:boolean)=>void|null);
  13. }
  14. export class SystemInfoComponent extends Component<SystemInfoProps, SystemInfoComponentState> {
  15. private data: SystemInfo|undefined = undefined;
  16. public constructor(props: SystemInfoProps) {
  17. super(props);
  18. this.state = {
  19. loading: true,
  20. hidden: this.isFiltered()
  21. };
  22. DAL.SystemInfo.getInfo(this.props.hostname).then(data => {
  23. this.data = data; // FIXME error handling
  24. this.setState({...this.state, loading: false});
  25. });
  26. menu.addFilterEventListener(() => {
  27. this.refreshFilter();
  28. });
  29. }
  30. private isFiltered(): boolean {
  31. const filterShown = menu.getFilteredHostnames();
  32. return (filterShown.indexOf(this.props.hostname) < 0);
  33. }
  34. private refreshFilter() {
  35. const hidden = this.isFiltered();
  36. if (hidden === this.state.hidden)
  37. return;
  38. this.setState({...this.state, hidden: hidden});
  39. }
  40. private layout(content: React.JSX.Element) {
  41. return <section key={this.props.hostname} className={(this.state.hidden ? "hidden" : "")}><div><h1>{this.props.hostname}</h1>{content}</div></section>;
  42. }
  43. private statItem(key: string, value: string|React.JSX.Element, listKey?: number): React.JSX.Element {
  44. if (!value)
  45. return <></>;
  46. key = key.charAt(0).toUpperCase()+String(key).slice(1);
  47. if (listKey === undefined)
  48. return <li>{key}: {value}</li>;
  49. return <li key={listKey}>{key}: {value}</li>;
  50. }
  51. private statItemNumber(key: string, value: number): React.JSX.Element {
  52. if (!value || isNaN(value))
  53. return <></>;
  54. return this.statItem(key, `${value}`);
  55. }
  56. private statItemMhz(key: string, value: number): React.JSX.Element {
  57. if (!value || isNaN(value))
  58. return <></>;
  59. return this.statItem(key, `${value}`);
  60. }
  61. private stringifyByte(value: number): string {
  62. if (!value || isNaN(value))
  63. return "";
  64. let units = ["Byte", "kB", "MB", "GB", "TB", "PB"];
  65. let unitIndex = 0;
  66. while (value > 1024 && unitIndex < units.length) {
  67. value /= 1024;
  68. unitIndex++;
  69. }
  70. value = Math.round(value*100)/100;
  71. return `${value}${units[unitIndex]}`;
  72. }
  73. private statItemBytes(key: string, value: number): React.JSX.Element {
  74. if (!value || isNaN(value))
  75. return <></>;
  76. return this.statItem(key, this.stringifyByte(value));
  77. }
  78. private statItemReadOnly(key: string, value: boolean|undefined): React.JSX.Element {
  79. if (value === undefined)
  80. return <></>;
  81. return this.statItem(key, value ? "Read-Write" : "Read Only");
  82. }
  83. private statItemBoolean(key: string, value: boolean|undefined): React.JSX.Element {
  84. if (value === undefined)
  85. return <></>;
  86. return this.statItem(key, value ? "true" : "false");
  87. }
  88. private statItemArray(key: string, values: string[]): React.JSX.Element {
  89. return this.statItem(key, <ul>{values.map((x, idx) => <li key={idx}>{x}</li>)}</ul>);
  90. }
  91. private statItemFragArray(key: string, values: React.JSX.Element[]): React.JSX.Element {
  92. return this.statItem(key, <ul>{values}</ul>);
  93. }
  94. private uptime(data: SystemInfo): string {
  95. let totalTime = data.uptime;
  96. let timeInSec = Math.floor(totalTime % 60);
  97. totalTime /= 60;
  98. let timeInMin = Math.floor(totalTime % 60);
  99. totalTime /= 60;
  100. let timeInHours = Math.floor(totalTime % 24);
  101. totalTime /= 24;
  102. let timeInDays = Math.floor(totalTime);
  103. let result = "";
  104. if (timeInDays)
  105. result += `${timeInDays}d, `;
  106. if (result.length || timeInHours)
  107. result += (`${timeInHours}:`).padStart(3, "0");
  108. result += (`${timeInMin}`).padStart(2, "0")+':'+(`${timeInSec}`).padStart(2, "0");
  109. return result;
  110. }
  111. private cpuInfos(cpu: CpuInfo, cpuIndex: number): React.JSX.Element {
  112. return this.statItem("model", cpu.model, cpuIndex);
  113. }
  114. private networkInfo(network: NetworkInfo, netIndex: number): React.JSX.Element {
  115. return <li key={netIndex}><ul>
  116. {this.statItem("Interface", network.iface)}
  117. {this.statItem("Address", network.address)}
  118. {this.statItem("Netmask", network.netmask)}
  119. {this.statItem("Family", network.family)}
  120. {this.statItem("Mac Address", network.mac)}
  121. </ul></li>;
  122. }
  123. private driveInfo(drive: DriveInfo, driveIdx: number): React.JSX.Element {
  124. return <li key={driveIdx}><ul>
  125. {this.statItem("name", drive.name)}
  126. {this.statItem("type", drive.type)}
  127. {this.statItem("Mount point", drive.mount)}
  128. {this.statItem("label", drive.label)}
  129. {this.statItemBytes("Size", drive.size)}
  130. {this.statItemBytes("Used Size", drive.usedSize)}
  131. {this.statItemBytes("Remaining Size", drive.size - drive.usedSize)}
  132. {this.statItemNumber("Used (%)", Math.round(10000 * drive.usedSize / drive.size) / 100)}
  133. {this.statItem("Physical", drive.physical)}
  134. {this.statItem("UUID", drive.uuid)}
  135. {this.statItem("model", drive.model)}
  136. {this.statItem("serial", drive.serial)}
  137. {this.statItemBoolean("removable", drive.removable)}
  138. {this.statItem("protocol", drive.protocol)}
  139. {this.statItem("device", drive.device)}
  140. {this.statItemReadOnly("Access", drive.rw)}
  141. </ul></li>;
  142. }
  143. public render(): React.JSX.Element {
  144. if (this.state.loading)
  145. return this.layout(<>Loading..</>);
  146. else if (this.data === undefined)
  147. return this.layout(<>Error</>);
  148. const data = this.data!;
  149. return this.layout(<>
  150. <ul>
  151. {this.statItem("Platform", data.platform)}
  152. {this.statItem("Distribution", data.distribution)}
  153. {this.statItem("Architecture", data.arch)}
  154. {this.statItem("OS Version", data.osVersion)}
  155. {this.statItem("uptime", this.uptime(data))}
  156. {this.statItem("Architecture", data.arch)}
  157. {this.statItem("Node Version", data.nodeVersion)}
  158. </ul>
  159. <ul>
  160. {this.statItem("Manufacturer", data.manufacturer)}
  161. {this.statItem("Model", data.model)}
  162. {this.statItemMhz("CPU Max Speed", data.cpuMaxSpeed)}
  163. {this.statItemFragArray("Cpus", data.cpus.map((x, idx) => this.cpuInfos(x, idx)))}
  164. {this.statItemBytes("Memory", data.memory)}
  165. {this.statItem("Memory Layout", <ul>{data.memoryLayout.map((x, idx) => <li key={idx}>{this.stringifyByte(x)}</li>)}</ul>)}
  166. </ul>
  167. <ul>
  168. {this.statItemArray("DNS Servers", data.dnsServers)}
  169. {this.statItemFragArray("Network Interfaces", data.network.map((x, idx) => this.networkInfo(x, idx)))}
  170. </ul>
  171. <ul>
  172. {this.statItemFragArray("Drives", data.drives.map((x, idx) => this.driveInfo(x, idx)))}
  173. </ul>
  174. </>);
  175. }
  176. public static async renderMultiple(container: HTMLElement): Promise<React.JSX.Element[]> {
  177. let domNodes = (await DAL.SystemInfo.listHosts()).map(hostname => <SystemInfoComponent key={hostname} setHidden={null} hostname={hostname}/>);
  178. ReactDom.createRoot(container).render(<>{domNodes}</>);
  179. return domNodes;
  180. }
  181. }