mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-16 17:03:46 +00:00
dashboard: Inferno client
This commit is contained in:
parent
163aa99033
commit
6b499a610a
16 changed files with 999 additions and 333 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -30,3 +30,6 @@ build/_vendor/pkg
|
|||
# travis
|
||||
profile.tmp
|
||||
profile.cov
|
||||
|
||||
# dashboard
|
||||
/go-ethereum/dashboard/bundler/node_modules/
|
||||
32
dashboard/README.md
Normal file
32
dashboard/README.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
## Go Ethereum Dashboard
|
||||
### Description
|
||||
|
||||
The GED is a data visualizer integrated into geth, intended to collect and visualize useful information of an Ethereum node.
|
||||
The GED consists of two parts:
|
||||
* The server listens to connections, collects data with a given refresh rate, and updates the dashboards through the opened connections.
|
||||
* The client waits for update messages, updates the content and tries to reconnect on connection loss.
|
||||
|
||||
The client's UI is maintained by [Inferno][Inferno], a sympathetic React-like JavaScript library.
|
||||
In order to create the Inferno's virtual DOM using JSX syntax, there is need to use the provided babel plugin.
|
||||
[Webpack][Webpack] is used for bundling the resources in order to gain cost efficiency and maintainability.
|
||||
[Node.js][Node.js] is used for installing the necessary dependencies.
|
||||
|
||||
### Installation steps
|
||||
|
||||
_Server_
|
||||
|
||||
1. `cd .../go-ethereum/`
|
||||
1. `go generate ./dashboard && go install -v ./cmd/geth`
|
||||
1. run the server with `geth --rinkeby --dashboard --vmodule=dashboard=5 --metrics console`
|
||||
1. optionally use `--dashboard.assets=<path>` to set the assets' path => useful for debugging
|
||||
1. enter `localhost:8080` (or change the configuration)
|
||||
|
||||
_Module bundler_
|
||||
|
||||
1. `cd .../go-ethereum/dashboard/bundler/`
|
||||
1. `npm install`
|
||||
1. `./node_modules/.bin/webpack` // check out `webpack.config.js`
|
||||
|
||||
[Inferno]: https://infernojs.org/
|
||||
[Webpack]: https://webpack.github.io/
|
||||
[Node.js]: https://nodejs.org/en/
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,282 +0,0 @@
|
|||
Chart.defaults.global.legend = { enabled: false };
|
||||
|
||||
const Component = React.Component;
|
||||
// const Component = Inferno.Component;
|
||||
|
||||
// isNullOrUndefined returns true if a variable is null or undefined.
|
||||
let isNullOrUndefined = variable => variable === null || typeof variable === 'undefined';
|
||||
|
||||
// MenuItem renders an item for a Menu component and the belonging submenu items, if there is any.
|
||||
class MenuItem extends Component {
|
||||
render = () => <li>
|
||||
<a>
|
||||
<i className={`fa ${this.props.className}`}/>
|
||||
{this.props.text}
|
||||
<div className="fa fa-chevron-down"/>
|
||||
</a>
|
||||
{
|
||||
// Render dropdown menu only if there are children.
|
||||
isNullOrUndefined(this.props.children) || <ul className="nav child_menu">
|
||||
{React.Children.map(this.props.children, child => <li>{child}</li>)}
|
||||
</ul>
|
||||
}
|
||||
</li>;
|
||||
}
|
||||
|
||||
// Menu renders a menu component.
|
||||
let Menu = () => <ul className="nav side-menu">
|
||||
<MenuItem className="fa-home" text="Home">
|
||||
<a href="dashboard1.html">Dashboard1</a>
|
||||
<a href="dashboard2.html">Dashboard2</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-edit" text="Networking">
|
||||
<a href="networking.html">Networking</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-desktop" text="Txpool">
|
||||
<a href="txpool.html">Txpool</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-table" text="Logs">
|
||||
<a href="logs.html">Logs</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-clone" text="Blockchain">
|
||||
<a href="blockchain1.html">Blockchain1</a>
|
||||
<a href="blockchain2.html">Blockchain2</a>
|
||||
<a href="blockchain3.html">Blockchain3</a>
|
||||
<a href="blockchain4.html">Blockchain4</a>
|
||||
<a href="blockchain5.html">Blockchain5</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-bar-chart-o" text="System"/>
|
||||
</ul>;
|
||||
|
||||
let Clearfix = () => <div className="clearfix"/>;
|
||||
|
||||
// SideBar renders a sidebar component.
|
||||
let SideBar = () => <div className="col-md-3 left_col">
|
||||
<div className="left_col scroll-view">
|
||||
<div className="navbar nav_title" style={{border: 0}}>
|
||||
<a href="dashboard.html" className="site_title"><i className="fa fa-paw"/> <span>Go Ethereum Dashboard</span></a>
|
||||
</div>
|
||||
<Clearfix/>
|
||||
<div id="sidebar-menu" className="main_menu_side hidden-print main_menu">
|
||||
<div className="menu_section">
|
||||
<Menu/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
// TopNavigation renders a top navigation component.
|
||||
let TopNavigation = () => <div className="top_nav">
|
||||
<div className="nav_menu">
|
||||
<nav className="" role="navigation">
|
||||
<div className="nav toggle">
|
||||
<a id="_______menu_toggle"> {/* TODO (kurkomisi): Resize the main container on toggle */}
|
||||
<i className="fa fa-bars"/>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
// Chart name is already in use.
|
||||
// ChartComponent renders a chart component and updates it, when the related data changes.
|
||||
class ChartComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount = () => this.state.chart = new Chart(this.data, {
|
||||
type: this.props.type,
|
||||
data: this.props.data,
|
||||
});
|
||||
|
||||
render = () => {
|
||||
if (!isNullOrUndefined(this.state.chart)) {
|
||||
this.state.chart.update();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={this.props.className}>
|
||||
<div className="x_panel">
|
||||
<div className="x_title">
|
||||
<h2>{this.props.text}</h2>
|
||||
<ul className="nav navbar-right panel_toolbox">
|
||||
<li><a className="collapse-link"><i className="fa fa-chevron-up"/></a></li>
|
||||
{
|
||||
// Render dropdown menu only if there are children.
|
||||
isNullOrUndefined(this.props.children) || <li className="dropdown">
|
||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
aria-expanded="false"><i className="fa fa-wrench"/></a>
|
||||
<ul className="dropdown-menu" role="menu">
|
||||
{React.Children.map(this.props.children, child => <li>{child}</li>)}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
<li><a className="close-link"><i className="fa fa-close"/></a>
|
||||
</li>
|
||||
</ul>
|
||||
<Clearfix/>
|
||||
</div>
|
||||
<div className="x_content">
|
||||
{/* The chart will be generated here after the component is mounted (this.componentDidMount). */}
|
||||
<canvas ref={data => {
|
||||
this.data = data
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Row renders a row component of charts only if there is any chart.
|
||||
class Row extends Component {
|
||||
render = () => isNullOrUndefined(this.props.children) || <div className="row"> {this.props.children} </div>;
|
||||
}
|
||||
|
||||
// PageContent renders a component for the page content.
|
||||
class PageContent extends Component {
|
||||
render = () => <div className="right_col" role="main">
|
||||
<div className="">
|
||||
<div className="page-title">
|
||||
<div className="title_left">
|
||||
<h3>Go Ethereum Dashboard
|
||||
<small>Statistics</small>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<Clearfix/>
|
||||
<Row>
|
||||
<ChartComponent className="col-md-6 col-sm-6 col-xs-12" text="Memory usage system/memory/inuse" type="line" data={this.props.charts.memory}>
|
||||
<a href="#">Settings 1</a>
|
||||
<a href="#">Settings 2</a>
|
||||
</ChartComponent>
|
||||
<ChartComponent className="col-md-6 col-sm-6 col-xs-12" text="Inbound traffic p2p/InboundTraffic" type="line" data={this.props.charts.traffic}/>
|
||||
</Row>
|
||||
<Clearfix/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
// Footer renders a footer.
|
||||
let Footer = () => <footer>
|
||||
<div className="pull-right">
|
||||
Gentelella - Bootstrap Admin Template by <a href="https://colorlib.com">Colorlib</a>
|
||||
</div>
|
||||
<Clearfix/>
|
||||
</footer>;
|
||||
|
||||
const MEMORY_SAMPLE_LIMIT = 200; // Maximum number of memory data samples.
|
||||
const TRAFFIC_SAMPLE_LIMIT = 200; // Maximum number of traffic data samples.
|
||||
|
||||
// Dashboard renders a full dashboard component, which makes connection with the server and waits for messages.
|
||||
// When there is a message, correspondingly updates the page's content.
|
||||
class Dashboard extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
charts: {
|
||||
memory: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: "system/memory/inuse",
|
||||
backgroundColor: "rgba(38, 185, 154, 0.31)",
|
||||
borderColor: "rgba(38, 185, 154, 0.7)",
|
||||
pointBorderColor: "rgba(38, 185, 154, 0.7)",
|
||||
pointBackgroundColor: "rgba(38, 185, 154, 0.7)",
|
||||
pointHoverBackgroundColor: "#fff",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
}],
|
||||
},
|
||||
traffic: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: "p2p/InboundTraffic",
|
||||
backgroundColor: "rgba(3, 88, 106, 0.3)",
|
||||
borderColor: "rgba(3, 88, 106, 0.70)",
|
||||
pointBorderColor: "rgba(3, 88, 106, 0.70)",
|
||||
pointBackgroundColor: "rgba(3, 88, 106, 0.70)",
|
||||
pointHoverBackgroundColor: "#fff",
|
||||
pointHoverBorderColor: "rgba(151,187,205,1)",
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
}],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
updateCharts = msg => {
|
||||
let memory = this.state.charts.memory;
|
||||
let traffic = this.state.charts.traffic;
|
||||
|
||||
// Fill the dashboard with the past data. metrics is set only in the first msg, after the connection is established.
|
||||
if (msg.metrics !== undefined) {
|
||||
// Clear the arrays to prevent data confusion with the previous connection.
|
||||
memory.labels = [];
|
||||
traffic.labels = [];
|
||||
memory.datasets[0].data = [];
|
||||
traffic.datasets[0].data = [];
|
||||
|
||||
let mem = msg.metrics.memory;
|
||||
let traff = msg.metrics.processor; // TODO (kurkomisi): !!!
|
||||
|
||||
// Put the past data to the beginning of the arrays. This prevents confusion with the next msg data, which goes to the end.
|
||||
for (let i = mem.length - 1; i >= 0 && MEMORY_SAMPLE_LIMIT > memory.labels.length; --i) {
|
||||
memory.labels.unshift(mem[i].time.substring(mem[i].time.length - 5));
|
||||
traffic.labels.unshift(mem[i].time.substring(mem[i].time.length - 5));
|
||||
memory.datasets[0].data.unshift(mem[i].value);
|
||||
traffic.datasets[0].data.unshift(traff[i].value);
|
||||
}
|
||||
|
||||
this.setState({charts: {memory: memory, traffic: traffic,}}); // Update the components.
|
||||
return;
|
||||
}
|
||||
|
||||
// Put the new data to the end of the arrays.
|
||||
if (msg.memory !== undefined) {
|
||||
// Remove the first elements in case the samples' amount exceeds the limit.
|
||||
if (memory.labels.length === MEMORY_SAMPLE_LIMIT) {
|
||||
memory.labels.shift();
|
||||
traffic.labels.shift();
|
||||
memory.datasets[0].data.shift();
|
||||
traffic.datasets[0].data.shift();
|
||||
}
|
||||
memory.labels.push(msg.memory.time.substring(msg.memory.time.length - 5));
|
||||
traffic.labels.push(msg.memory.time.substring(msg.memory.time.length - 5));
|
||||
memory.datasets[0].data.push(msg.memory.value);
|
||||
traffic.datasets[0].data.push(msg.processor.value);
|
||||
|
||||
this.setState({charts: {memory: memory, traffic: traffic,}}); // Update the components.
|
||||
}
|
||||
};
|
||||
|
||||
reconnect = () => {
|
||||
let server = new WebSocket("ws://" + location.host + "/api");
|
||||
let that = this;
|
||||
|
||||
server.onmessage = event => {
|
||||
let msg = JSON.parse(event.data);
|
||||
if (isNullOrUndefined(msg)) {
|
||||
return;
|
||||
}
|
||||
that.updateCharts(msg);
|
||||
};
|
||||
|
||||
server.onclose = () => setTimeout(that.reconnect, 3000);
|
||||
};
|
||||
|
||||
componentDidMount = () => this.reconnect();
|
||||
|
||||
render = () => <div className="container body">
|
||||
<div className="main_container">
|
||||
<SideBar/>
|
||||
<TopNavigation/>
|
||||
<PageContent charts={this.state.charts}/>
|
||||
<Footer/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -12,37 +12,22 @@
|
|||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/css/custom.min.css" rel="stylesheet">
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router/4.1.2/react-router.min.js"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-noty/2.4.1/packaged/jquery.noty.packaged.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.0.0/Chart.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.0.0/Chart.min.js"></script>
|
||||
<script>
|
||||
Chart.defaults.global.legend = { enabled: false };
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.js"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!--<script src="Something replacing babel plugin"></script>-->
|
||||
<!--<script src="https://unpkg.com/inferno@3.7.0/dist/inferno.js"></script>-->
|
||||
<!--<script src="https://unpkg.com/inferno-component@3.7.0/dist/inferno-component.js"></script>-->
|
||||
<!--<script src="https://unpkg.com/inferno-router@3.7.0/dist/inferno-router.min.js"></script>-->
|
||||
<script src="https://unpkg.com/inferno@3.9.0/dist/inferno.min.js"></script>
|
||||
<script src="https://unpkg.com/inferno-component@3.9.0/dist/inferno-component.min.js"></script>
|
||||
|
||||
<div id="dashboard" class="nav-md"></div>
|
||||
<script src="js/bundle.js"></script>
|
||||
|
||||
<script type="text/babel" src="components/dashboard.js"></script>
|
||||
<script type="text/babel">
|
||||
ReactDOM.render(<Dashboard/>, document.getElementById('dashboard'));
|
||||
</script>
|
||||
|
||||
<!--<script type="text/babel">-->
|
||||
<!--Inferno.render(<Dashboard/>, document.getElementById('dashboard'));-->
|
||||
<!--</script>-->
|
||||
|
||||
<script type="text/babel" src="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/js/custom.min.js"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/js/custom.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
566
dashboard/assets/js/bundle.js
Normal file
566
dashboard/assets/js/bundle.js
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 3);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = Inferno;
|
||||
|
||||
/***/ }),
|
||||
/* 1 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return isNullOrUndefined; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "d", function() { return mapChildren; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Clearfix; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return MEMORY_SAMPLE_LIMIT; });
|
||||
/* unused harmony export TRAFFIC_SAMPLE_LIMIT */
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno__ = __webpack_require__(0);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_inferno__);
|
||||
// isNullOrUndefined returns true if a variable is null or undefined.
|
||||
var isNullOrUndefined = function isNullOrUndefined(variable) {
|
||||
return variable === null || typeof variable === 'undefined';
|
||||
};
|
||||
|
||||
var mapChildren = function mapChildren(children, mapFunc) {
|
||||
return !Array.isArray(children) || children.length < 1 || children.length === 1 ? mapFunc(children) : children.map(mapFunc);
|
||||
};
|
||||
|
||||
|
||||
var Clearfix = function Clearfix() {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "div", "clearfix");
|
||||
};
|
||||
|
||||
var MEMORY_SAMPLE_LIMIT = 200; // Maximum number of memory data samples.
|
||||
var TRAFFIC_SAMPLE_LIMIT = 200; // Maximum number of traffic data samples.
|
||||
|
||||
/***/ }),
|
||||
/* 2 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = Inferno.Component;
|
||||
|
||||
/***/ }),
|
||||
/* 3 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno__ = __webpack_require__(0);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_inferno__);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_Dashboard__ = __webpack_require__(4);
|
||||
|
||||
|
||||
|
||||
// Renders the whole dashboard.
|
||||
|
||||
__WEBPACK_IMPORTED_MODULE_0_inferno___default.a.render(Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__components_Dashboard__["a" /* default */]), document.getElementById('dashboard'));
|
||||
|
||||
/***/ }),
|
||||
/* 4 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component__ = __webpack_require__(2);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_component__);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Common__ = __webpack_require__(1);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__SideBar__ = __webpack_require__(5);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__TopNavigation__ = __webpack_require__(6);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__PageContent__ = __webpack_require__(7);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__Footer__ = __webpack_require__(8);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_inferno__ = __webpack_require__(0);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6_inferno__);
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Dashboard is the main component, which renders the whole page,
|
||||
// makes connection with the server and listens for messages.
|
||||
// When there is an incoming message, updates the page's content correspondingly.
|
||||
|
||||
|
||||
var Dashboard = function (_Component) {
|
||||
_inherits(Dashboard, _Component);
|
||||
|
||||
function Dashboard(props) {
|
||||
_classCallCheck(this, Dashboard);
|
||||
|
||||
var _this = _possibleConstructorReturn(this, _Component.call(this, props));
|
||||
|
||||
_this.updateCharts = function (msg) {
|
||||
var memory = _this.state.charts.memory;
|
||||
var traffic = _this.state.charts.traffic;
|
||||
|
||||
// Fill the dashboard with the past data. metrics is set only in the first msg,
|
||||
// after the connection is established.
|
||||
if (msg.metrics !== undefined) {
|
||||
// Clear the arrays to prevent data confusion with the previous connection.
|
||||
memory.labels = [];
|
||||
traffic.labels = [];
|
||||
memory.datasets[0].data = [];
|
||||
traffic.datasets[0].data = [];
|
||||
|
||||
var mem = msg.metrics.memory;
|
||||
var traff = msg.metrics.processor; // TODO (kurkomisi): !!!
|
||||
|
||||
// Put the past data to the beginning of the arrays. This prevents confusion with the next msg data,
|
||||
// which goes to the end.
|
||||
for (var i = mem.length - 1; i >= 0 && __WEBPACK_IMPORTED_MODULE_1__Common__["b" /* MEMORY_SAMPLE_LIMIT */] > memory.labels.length; --i) {
|
||||
memory.labels.unshift(mem[i].time.substring(mem[i].time.length - 5));
|
||||
traffic.labels.unshift(mem[i].time.substring(mem[i].time.length - 5));
|
||||
memory.datasets[0].data.unshift(mem[i].value);
|
||||
traffic.datasets[0].data.unshift(traff[i].value);
|
||||
}
|
||||
|
||||
_this.setState({ charts: { memory: memory, traffic: traffic } }); // Update the components.
|
||||
return;
|
||||
}
|
||||
|
||||
// Put the new data to the end of the arrays.
|
||||
if (msg.memory !== undefined) {
|
||||
// Remove the first elements in case the samples' amount exceeds the limit.
|
||||
if (memory.labels.length === __WEBPACK_IMPORTED_MODULE_1__Common__["b" /* MEMORY_SAMPLE_LIMIT */]) {
|
||||
memory.labels.shift();
|
||||
traffic.labels.shift();
|
||||
memory.datasets[0].data.shift();
|
||||
traffic.datasets[0].data.shift();
|
||||
}
|
||||
memory.labels.push(msg.memory.time.substring(msg.memory.time.length - 5));
|
||||
traffic.labels.push(msg.memory.time.substring(msg.memory.time.length - 5));
|
||||
memory.datasets[0].data.push(msg.memory.value);
|
||||
traffic.datasets[0].data.push(msg.processor.value);
|
||||
|
||||
_this.setState({ charts: { memory: memory, traffic: traffic } }); // Update the components.
|
||||
}
|
||||
};
|
||||
|
||||
_this.reconnect = function () {
|
||||
var server = new WebSocket("ws://" + location.host + "/api");
|
||||
|
||||
server.onmessage = function (event) {
|
||||
var msg = JSON.parse(event.data);
|
||||
if (Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(msg)) {
|
||||
return;
|
||||
}
|
||||
_this.updateCharts(msg);
|
||||
};
|
||||
|
||||
server.onclose = function () {
|
||||
return setTimeout(_this.reconnect, 3000);
|
||||
};
|
||||
};
|
||||
|
||||
_this.componentDidMount = function () {
|
||||
return _this.reconnect();
|
||||
};
|
||||
|
||||
_this.render = function () {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(2, "div", "container body", Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(2, "div", "main_container", [Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_2__SideBar__["a" /* SideBar */]), Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_3__TopNavigation__["a" /* TopNavigation */]), Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_4__PageContent__["a" /* default */], null, null, {
|
||||
"charts": _this.state.charts
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_5__Footer__["a" /* Footer */])]));
|
||||
};
|
||||
|
||||
_this.state = {
|
||||
charts: { // Stores the state of the charts.
|
||||
memory: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: "system/memory/inuse",
|
||||
backgroundColor: "rgba(38, 185, 154, 0.31)",
|
||||
borderColor: "rgba(38, 185, 154, 0.7)",
|
||||
pointBorderColor: "rgba(38, 185, 154, 0.7)",
|
||||
pointBackgroundColor: "rgba(38, 185, 154, 0.7)",
|
||||
pointHoverBackgroundColor: "#fff",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointBorderWidth: 1,
|
||||
data: []
|
||||
}]
|
||||
},
|
||||
traffic: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: "p2p/InboundTraffic",
|
||||
backgroundColor: "rgba(3, 88, 106, 0.3)",
|
||||
borderColor: "rgba(3, 88, 106, 0.70)",
|
||||
pointBorderColor: "rgba(3, 88, 106, 0.70)",
|
||||
pointBackgroundColor: "rgba(3, 88, 106, 0.70)",
|
||||
pointHoverBackgroundColor: "#fff",
|
||||
pointHoverBorderColor: "rgba(151,187,205,1)",
|
||||
pointBorderWidth: 1,
|
||||
data: []
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
return _this;
|
||||
}
|
||||
|
||||
// updateCharts Analyzes the incoming message, and updates the charts' content correspondingly.
|
||||
|
||||
|
||||
// reconnect establishes a websocket connection with the server, listens for incoming messages
|
||||
// and tries to reconnect on connection loss.
|
||||
|
||||
|
||||
// componentDidMount initiates the establishment of the first websocket connection after the component is rendered.
|
||||
|
||||
|
||||
// render renders the components of the dashboard.
|
||||
|
||||
|
||||
return Dashboard;
|
||||
}(__WEBPACK_IMPORTED_MODULE_0_component___default.a);
|
||||
|
||||
/* harmony default export */ __webpack_exports__["a"] = (Dashboard);
|
||||
|
||||
/***/ }),
|
||||
/* 5 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return SideBar; });
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component__ = __webpack_require__(2);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_component__);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Common__ = __webpack_require__(1);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_inferno__ = __webpack_require__(0);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_inferno__);
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
||||
|
||||
|
||||
|
||||
|
||||
// MenuItem renders an item for a Menu component and the belonging submenu items, if there is any.
|
||||
|
||||
|
||||
var MenuItem = function (_Component) {
|
||||
_inherits(MenuItem, _Component);
|
||||
|
||||
function MenuItem() {
|
||||
var _temp, _this, _ret;
|
||||
|
||||
_classCallCheck(this, MenuItem);
|
||||
|
||||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, _Component.call.apply(_Component, [this].concat(args))), _this), _this.render = function () {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa " + _this.props.className), _this.props.text, Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "fa fa-chevron-down")]),
|
||||
// Render dropdown menu only if there are children.
|
||||
Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(_this.props.children) || Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "ul", "nav child_menu", Object(__WEBPACK_IMPORTED_MODULE_1__Common__["d" /* mapChildren */])(_this.props.children, function (child) {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, child);
|
||||
}))]);
|
||||
}, _temp), _possibleConstructorReturn(_this, _ret);
|
||||
}
|
||||
|
||||
return MenuItem;
|
||||
}(__WEBPACK_IMPORTED_MODULE_0_component___default.a);
|
||||
|
||||
// Menu renders a menu component.
|
||||
|
||||
|
||||
var Menu = function Menu() {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "ul", "nav side-menu", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, {
|
||||
"className": "fa-home",
|
||||
"text": "Home",
|
||||
children: [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Dashboard1", {
|
||||
"href": "dashboard1.html"
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Dashboard2", {
|
||||
"href": "dashboard2.html"
|
||||
})]
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, {
|
||||
"className": "fa-edit",
|
||||
"text": "Networking",
|
||||
children: Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Networking", {
|
||||
"href": "networking.html"
|
||||
})
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, {
|
||||
"className": "fa-desktop",
|
||||
"text": "Txpool",
|
||||
children: Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Txpool", {
|
||||
"href": "txpool.html"
|
||||
})
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, {
|
||||
"className": "fa-table",
|
||||
"text": "Logs",
|
||||
children: Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Logs", {
|
||||
"href": "logs.html"
|
||||
})
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, {
|
||||
"className": "fa-clone",
|
||||
"text": "Blockchain",
|
||||
children: [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain1", {
|
||||
"href": "blockchain1.html"
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain2", {
|
||||
"href": "blockchain2.html"
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain3", {
|
||||
"href": "blockchain3.html"
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain4", {
|
||||
"href": "blockchain4.html"
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain5", {
|
||||
"href": "blockchain5.html"
|
||||
})]
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, {
|
||||
"className": "fa-bar-chart-o",
|
||||
"text": "System"
|
||||
})]);
|
||||
};
|
||||
|
||||
// SideBar renders a sidebar component.
|
||||
var SideBar = function SideBar() {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "col-md-3 left_col", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "left_col scroll-view", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "navbar nav_title", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", "site_title", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa fa-paw"), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "span", null, "Go Ethereum Dashboard")], {
|
||||
"href": "dashboard.html"
|
||||
}), {
|
||||
"style": { border: 0 }
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__Common__["a" /* Clearfix */]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "main_menu_side hidden-print main_menu", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "menu_section", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, Menu)), {
|
||||
"id": "sidebar-menu"
|
||||
})]));
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 6 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TopNavigation; });
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno__ = __webpack_require__(0);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_inferno__);
|
||||
|
||||
// TopNavigation renders a top navigation component.
|
||||
var TopNavigation = function TopNavigation() {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "div", "top_nav", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "div", "nav_menu", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "nav", "", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "div", "nav toggle", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "a", null, [" ", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "i", "fa fa-bars")], {
|
||||
"id": "_______menu_toggle"
|
||||
})), {
|
||||
"role": "navigation"
|
||||
})));
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 7 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component__ = __webpack_require__(2);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_component__);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Common__ = __webpack_require__(1);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_inferno__ = __webpack_require__(0);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_inferno__);
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
||||
|
||||
|
||||
|
||||
|
||||
// Chart name is already in use.
|
||||
// ChartComponent renders a chart component and updates it, when the related data changes.
|
||||
|
||||
|
||||
var ChartComponent = function (_Component) {
|
||||
_inherits(ChartComponent, _Component);
|
||||
|
||||
function ChartComponent(props) {
|
||||
_classCallCheck(this, ChartComponent);
|
||||
|
||||
var _this = _possibleConstructorReturn(this, _Component.call(this, props));
|
||||
|
||||
_this.componentDidMount = function () {
|
||||
return _this.state.chart = new Chart(_this.data, {
|
||||
type: _this.props.type,
|
||||
data: _this.props.data
|
||||
});
|
||||
};
|
||||
|
||||
_this.render = function () {
|
||||
if (!Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(_this.state.chart)) {
|
||||
_this.state.chart.update();
|
||||
}
|
||||
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", _this.props.className, Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "x_panel", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "x_title", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "h2", null, _this.props.text), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "ul", "nav navbar-right panel_toolbox", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", "collapse-link", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa fa-chevron-up"))),
|
||||
// Render dropdown menu only if there are children.
|
||||
Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(_this.props.children) || Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", "dropdown", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", "dropdown-toggle", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa fa-wrench"), {
|
||||
"href": "#",
|
||||
"data-toggle": "dropdown",
|
||||
"role": "button",
|
||||
"aria-expanded": "false"
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "ul", "dropdown-menu", Object(__WEBPACK_IMPORTED_MODULE_1__Common__["d" /* mapChildren */])(_this.props.children, function (child) {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, child);
|
||||
}), {
|
||||
"role": "menu"
|
||||
})]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", "close-link", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa fa-close")))]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__Common__["a" /* Clearfix */])]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "x_content", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "canvas", null, null, null, null, function (data) {
|
||||
_this.data = data;
|
||||
}))]));
|
||||
};
|
||||
|
||||
_this.state = {};
|
||||
return _this;
|
||||
}
|
||||
|
||||
return ChartComponent;
|
||||
}(__WEBPACK_IMPORTED_MODULE_0_component___default.a);
|
||||
|
||||
// Row renders a row component of charts only if there is any chart.
|
||||
|
||||
|
||||
var Row = function (_Component2) {
|
||||
_inherits(Row, _Component2);
|
||||
|
||||
function Row() {
|
||||
var _temp, _this2, _ret;
|
||||
|
||||
_classCallCheck(this, Row);
|
||||
|
||||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
return _ret = (_temp = (_this2 = _possibleConstructorReturn(this, _Component2.call.apply(_Component2, [this].concat(args))), _this2), _this2.render = function () {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(_this2.props.children) || Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "row", [" ", _this2.props.children, " "]);
|
||||
}, _temp), _possibleConstructorReturn(_this2, _ret);
|
||||
}
|
||||
|
||||
return Row;
|
||||
}(__WEBPACK_IMPORTED_MODULE_0_component___default.a);
|
||||
|
||||
// PageContent renders a component for the page content.
|
||||
|
||||
|
||||
var PageContent = function (_Component3) {
|
||||
_inherits(PageContent, _Component3);
|
||||
|
||||
function PageContent() {
|
||||
var _temp2, _this3, _ret2;
|
||||
|
||||
_classCallCheck(this, PageContent);
|
||||
|
||||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||
args[_key2] = arguments[_key2];
|
||||
}
|
||||
|
||||
return _ret2 = (_temp2 = (_this3 = _possibleConstructorReturn(this, _Component3.call.apply(_Component3, [this].concat(args))), _this3), _this3.render = function () {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "right_col", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "page-title", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "title_left", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "h3", null, ["Go Ethereum Dashboard", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "small", null, "Statistics")]))), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__Common__["a" /* Clearfix */]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, Row, null, null, {
|
||||
children: [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, ChartComponent, null, null, {
|
||||
"className": "col-md-6 col-sm-6 col-xs-12",
|
||||
"text": "Memory usage system/memory/inuse",
|
||||
"type": "line",
|
||||
"data": _this3.props.charts.memory,
|
||||
children: [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Settings 1", {
|
||||
"href": "#"
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Settings 2", {
|
||||
"href": "#"
|
||||
})]
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, ChartComponent, null, null, {
|
||||
"className": "col-md-6 col-sm-6 col-xs-12",
|
||||
"text": "Inbound traffic p2p/InboundTraffic",
|
||||
"type": "line",
|
||||
"data": _this3.props.charts.traffic
|
||||
})]
|
||||
}), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__Common__["a" /* Clearfix */])]), {
|
||||
"role": "main"
|
||||
});
|
||||
}, _temp2), _possibleConstructorReturn(_this3, _ret2);
|
||||
}
|
||||
|
||||
return PageContent;
|
||||
}(__WEBPACK_IMPORTED_MODULE_0_component___default.a);
|
||||
|
||||
/* harmony default export */ __webpack_exports__["a"] = (PageContent);
|
||||
|
||||
/***/ }),
|
||||
/* 8 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Footer; });
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__Common__ = __webpack_require__(1);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_inferno__ = __webpack_require__(0);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_inferno__);
|
||||
|
||||
|
||||
// Footer renders a footer component.
|
||||
|
||||
var Footer = function Footer() {
|
||||
return Object(__WEBPACK_IMPORTED_MODULE_1_inferno__["createVNode"])(2, "footer", null, [Object(__WEBPACK_IMPORTED_MODULE_1_inferno__["createVNode"])(2, "div", "pull-right", "Copyright 2017 The go-ethereum Authors"), Object(__WEBPACK_IMPORTED_MODULE_1_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_0__Common__["a" /* Clearfix */])]);
|
||||
};
|
||||
|
||||
/***/ })
|
||||
/******/ ]);
|
||||
38
dashboard/bundler/package.json
Normal file
38
dashboard/bundler/package.json
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"jshintConfig": {
|
||||
"esversion": 6
|
||||
},
|
||||
"eslintConfig": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
["es2015", {"loose": true, "modules": false}],
|
||||
"stage-0"
|
||||
],
|
||||
"plugins": [
|
||||
"babel-plugin-syntax-jsx",
|
||||
["babel-plugin-inferno", {"imports": true}]
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-plugin-inferno": "^3.2.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"eslint": "^4.5.0",
|
||||
"eslint-config-inferno-app": "^4.2.0",
|
||||
"eslint-loader": "^1.9.0",
|
||||
"glob": "^7.1.2",
|
||||
"inferno-devtools": "^3.8.2",
|
||||
"path": "^0.12.7",
|
||||
"url": "^0.11.0",
|
||||
"webpack": "^3.5.5",
|
||||
"webpack-dev-server": "^2.7.1"
|
||||
}
|
||||
}
|
||||
10
dashboard/bundler/src/components/Common.js
Normal file
10
dashboard/bundler/src/components/Common.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// isNullOrUndefined returns true if a variable is null or undefined.
|
||||
export const isNullOrUndefined = variable => variable === null || typeof variable === 'undefined';
|
||||
|
||||
export const mapChildren = (children, mapFunc) => !Array.isArray(children) || children.length < 1 ||
|
||||
children.length === 1 ? mapFunc(children) : children.map(mapFunc);
|
||||
|
||||
export const Clearfix = () => <div className="clearfix"/>;
|
||||
|
||||
export const MEMORY_SAMPLE_LIMIT = 200; // Maximum number of memory data samples.
|
||||
export const TRAFFIC_SAMPLE_LIMIT = 200; // Maximum number of traffic data samples.
|
||||
124
dashboard/bundler/src/components/Dashboard.js
Normal file
124
dashboard/bundler/src/components/Dashboard.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
import Component from 'component';
|
||||
import {isNullOrUndefined, MEMORY_SAMPLE_LIMIT} from "./Common";
|
||||
import {SideBar} from "./SideBar";
|
||||
import {TopNavigation} from "./TopNavigation";
|
||||
import PageContent from './PageContent';
|
||||
import {Footer} from './Footer';
|
||||
|
||||
// Dashboard is the main component, which renders the whole page,
|
||||
// makes connection with the server and listens for messages.
|
||||
// When there is an incoming message, updates the page's content correspondingly.
|
||||
export default class Dashboard extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
charts: { // Stores the state of the charts.
|
||||
memory: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: "system/memory/inuse",
|
||||
backgroundColor: "rgba(38, 185, 154, 0.31)",
|
||||
borderColor: "rgba(38, 185, 154, 0.7)",
|
||||
pointBorderColor: "rgba(38, 185, 154, 0.7)",
|
||||
pointBackgroundColor: "rgba(38, 185, 154, 0.7)",
|
||||
pointHoverBackgroundColor: "#fff",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
}],
|
||||
},
|
||||
traffic: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: "p2p/InboundTraffic",
|
||||
backgroundColor: "rgba(3, 88, 106, 0.3)",
|
||||
borderColor: "rgba(3, 88, 106, 0.70)",
|
||||
pointBorderColor: "rgba(3, 88, 106, 0.70)",
|
||||
pointBackgroundColor: "rgba(3, 88, 106, 0.70)",
|
||||
pointHoverBackgroundColor: "#fff",
|
||||
pointHoverBorderColor: "rgba(151,187,205,1)",
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
}],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// updateCharts Analyzes the incoming message, and updates the charts' content correspondingly.
|
||||
updateCharts = msg => {
|
||||
const memory = this.state.charts.memory;
|
||||
const traffic = this.state.charts.traffic;
|
||||
|
||||
// Fill the dashboard with the past data. metrics is set only in the first msg,
|
||||
// after the connection is established.
|
||||
if (msg.metrics !== undefined) {
|
||||
// Clear the arrays to prevent data confusion with the previous connection.
|
||||
memory.labels = [];
|
||||
traffic.labels = [];
|
||||
memory.datasets[0].data = [];
|
||||
traffic.datasets[0].data = [];
|
||||
|
||||
const mem = msg.metrics.memory;
|
||||
const traff = msg.metrics.processor; // TODO (kurkomisi): !!!
|
||||
|
||||
// Put the past data to the beginning of the arrays. This prevents confusion with the next msg data,
|
||||
// which goes to the end.
|
||||
for (let i = mem.length - 1; i >= 0 && MEMORY_SAMPLE_LIMIT > memory.labels.length; --i) {
|
||||
memory.labels.unshift(mem[i].time.substring(mem[i].time.length - 5));
|
||||
traffic.labels.unshift(mem[i].time.substring(mem[i].time.length - 5));
|
||||
memory.datasets[0].data.unshift(mem[i].value);
|
||||
traffic.datasets[0].data.unshift(traff[i].value);
|
||||
}
|
||||
|
||||
this.setState({charts: {memory, traffic,}}); // Update the components.
|
||||
return;
|
||||
}
|
||||
|
||||
// Put the new data to the end of the arrays.
|
||||
if (msg.memory !== undefined) {
|
||||
// Remove the first elements in case the samples' amount exceeds the limit.
|
||||
if (memory.labels.length === MEMORY_SAMPLE_LIMIT) {
|
||||
memory.labels.shift();
|
||||
traffic.labels.shift();
|
||||
memory.datasets[0].data.shift();
|
||||
traffic.datasets[0].data.shift();
|
||||
}
|
||||
memory.labels.push(msg.memory.time.substring(msg.memory.time.length - 5));
|
||||
traffic.labels.push(msg.memory.time.substring(msg.memory.time.length - 5));
|
||||
memory.datasets[0].data.push(msg.memory.value);
|
||||
traffic.datasets[0].data.push(msg.processor.value);
|
||||
|
||||
this.setState({charts: {memory, traffic,}}); // Update the components.
|
||||
}
|
||||
};
|
||||
|
||||
// reconnect establishes a websocket connection with the server, listens for incoming messages
|
||||
// and tries to reconnect on connection loss.
|
||||
reconnect = () => {
|
||||
const server = new WebSocket("ws://" + location.host + "/api");
|
||||
|
||||
server.onmessage = event => {
|
||||
const msg = JSON.parse(event.data);
|
||||
if (isNullOrUndefined(msg)) {
|
||||
return;
|
||||
}
|
||||
this.updateCharts(msg);
|
||||
};
|
||||
|
||||
server.onclose = () => setTimeout(this.reconnect, 3000);
|
||||
};
|
||||
|
||||
// componentDidMount initiates the establishment of the first websocket connection after the component is rendered.
|
||||
componentDidMount = () => this.reconnect();
|
||||
|
||||
// render renders the components of the dashboard.
|
||||
render = () => <div className="container body">
|
||||
<div className="main_container">
|
||||
<SideBar/>
|
||||
<TopNavigation/>
|
||||
<PageContent charts={this.state.charts}/>
|
||||
<Footer/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
9
dashboard/bundler/src/components/Footer.js
Normal file
9
dashboard/bundler/src/components/Footer.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import {Clearfix} from "./Common";
|
||||
|
||||
// Footer renders a footer component.
|
||||
export const Footer = () => <footer>
|
||||
<div className="pull-right">
|
||||
Copyright 2017 The go-ethereum Authors
|
||||
</div>
|
||||
<Clearfix/>
|
||||
</footer>;
|
||||
83
dashboard/bundler/src/components/PageContent.js
Normal file
83
dashboard/bundler/src/components/PageContent.js
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import Component from 'component';
|
||||
import {isNullOrUndefined, mapChildren, Clearfix} from "./Common";
|
||||
|
||||
// Chart name is already in use.
|
||||
// ChartComponent renders a chart component and updates it, when the related data changes.
|
||||
class ChartComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount = () => this.state.chart = new Chart(this.data, {
|
||||
type: this.props.type,
|
||||
data: this.props.data,
|
||||
});
|
||||
|
||||
render = () => {
|
||||
if (!isNullOrUndefined(this.state.chart)) {
|
||||
this.state.chart.update();
|
||||
}
|
||||
|
||||
return <div className={this.props.className}>
|
||||
<div className="x_panel">
|
||||
<div className="x_title">
|
||||
<h2>{this.props.text}</h2>
|
||||
<ul className="nav navbar-right panel_toolbox">
|
||||
<li><a className="collapse-link"><i className="fa fa-chevron-up"/></a></li>
|
||||
{
|
||||
// Render dropdown menu only if there are children.
|
||||
isNullOrUndefined(this.props.children) || <li className="dropdown">
|
||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
aria-expanded="false"><i className="fa fa-wrench"/></a>
|
||||
<ul className="dropdown-menu" role="menu">
|
||||
{mapChildren(this.props.children, child => <li>{child}</li>)}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
<li><a className="close-link"><i className="fa fa-close"/></a>
|
||||
</li>
|
||||
</ul>
|
||||
<Clearfix/>
|
||||
</div>
|
||||
<div className="x_content">
|
||||
{/* The chart will be generated here after the component is mounted (this.componentDidMount). */}
|
||||
<canvas ref={data => {
|
||||
this.data = data
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
// Row renders a row component of charts only if there is any chart.
|
||||
class Row extends Component {
|
||||
render = () => isNullOrUndefined(this.props.children) || <div className="row"> {this.props.children} </div>;
|
||||
}
|
||||
|
||||
// PageContent renders a component for the page content.
|
||||
export default class PageContent extends Component {
|
||||
render = () => <div className="right_col" role="main">
|
||||
<div className="">
|
||||
<div className="page-title">
|
||||
<div className="title_left">
|
||||
<h3>Go Ethereum Dashboard
|
||||
<small>Statistics</small>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<Clearfix/>
|
||||
<Row>
|
||||
<ChartComponent className="col-md-6 col-sm-6 col-xs-12" text="Memory usage system/memory/inuse"
|
||||
type="line" data={this.props.charts.memory}>
|
||||
<a href="#">Settings 1</a>
|
||||
<a href="#">Settings 2</a>
|
||||
</ChartComponent>
|
||||
<ChartComponent className="col-md-6 col-sm-6 col-xs-12" text="Inbound traffic p2p/InboundTraffic"
|
||||
type="line" data={this.props.charts.traffic}/>
|
||||
</Row>
|
||||
<Clearfix/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
60
dashboard/bundler/src/components/SideBar.js
Normal file
60
dashboard/bundler/src/components/SideBar.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import Component from 'component';
|
||||
import {isNullOrUndefined, mapChildren, Clearfix} from "./Common";
|
||||
|
||||
// MenuItem renders an item for a Menu component and the belonging submenu items, if there is any.
|
||||
class MenuItem extends Component {
|
||||
render = () => <li>
|
||||
<a>
|
||||
<i className={`fa ${this.props.className}`}/>
|
||||
{this.props.text}
|
||||
<div className="fa fa-chevron-down"/>
|
||||
</a>
|
||||
{
|
||||
// Render dropdown menu only if there are children.
|
||||
isNullOrUndefined(this.props.children) || <ul className="nav child_menu">
|
||||
{mapChildren(this.props.children, child => <li>{child}</li>)}
|
||||
</ul>
|
||||
}
|
||||
</li>;
|
||||
}
|
||||
|
||||
// Menu renders a menu component.
|
||||
const Menu = () => <ul className="nav side-menu">
|
||||
<MenuItem className="fa-home" text="Home">
|
||||
<a href="dashboard1.html">Dashboard1</a>
|
||||
<a href="dashboard2.html">Dashboard2</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-edit" text="Networking">
|
||||
<a href="networking.html">Networking</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-desktop" text="Txpool">
|
||||
<a href="txpool.html">Txpool</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-table" text="Logs">
|
||||
<a href="logs.html">Logs</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-clone" text="Blockchain">
|
||||
<a href="blockchain1.html">Blockchain1</a>
|
||||
<a href="blockchain2.html">Blockchain2</a>
|
||||
<a href="blockchain3.html">Blockchain3</a>
|
||||
<a href="blockchain4.html">Blockchain4</a>
|
||||
<a href="blockchain5.html">Blockchain5</a>
|
||||
</MenuItem>
|
||||
<MenuItem className="fa-bar-chart-o" text="System"/>
|
||||
</ul>;
|
||||
|
||||
// SideBar renders a sidebar component.
|
||||
export const SideBar = () => <div className="col-md-3 left_col">
|
||||
<div className="left_col scroll-view">
|
||||
<div className="navbar nav_title" style={{border: 0}}>
|
||||
<a href="dashboard.html" className="site_title"><i className="fa fa-paw"/>
|
||||
<span>Go Ethereum Dashboard</span></a>
|
||||
</div>
|
||||
<Clearfix/>
|
||||
<div id="sidebar-menu" className="main_menu_side hidden-print main_menu">
|
||||
<div className="menu_section">
|
||||
<Menu/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
12
dashboard/bundler/src/components/TopNavigation.js
Normal file
12
dashboard/bundler/src/components/TopNavigation.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// TopNavigation renders a top navigation component.
|
||||
export const TopNavigation = () => <div className="top_nav">
|
||||
<div className="nav_menu">
|
||||
<nav className="" role="navigation">
|
||||
<div className="nav toggle">
|
||||
<a id="_______menu_toggle"> {/* TODO (kurkomisi): Resize the main container on toggle */}
|
||||
<i className="fa fa-bars"/>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>;
|
||||
5
dashboard/bundler/src/index.js
Normal file
5
dashboard/bundler/src/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import Inferno from 'inferno';
|
||||
import Dashboard from './components/Dashboard';
|
||||
|
||||
// Renders the whole dashboard.
|
||||
Inferno.render(<Dashboard/>, document.getElementById('dashboard'));
|
||||
21
dashboard/bundler/webpack.config.js
Normal file
21
dashboard/bundler/webpack.config.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../assets/js'),
|
||||
filename: 'bundle.js',
|
||||
},
|
||||
externals: { // External libraries, which will not be included in the bundled file(s).
|
||||
inferno: 'Inferno',
|
||||
component: 'Inferno.Component',
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/, // regexp for JS files
|
||||
loader: 'babel-loader', // The babel configuration is in the package.json.
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
@ -81,7 +81,7 @@ type status struct {
|
|||
// New creates a new dashboard instance with the given configuration.
|
||||
func New(config *Config) (*dashboard, error) {
|
||||
return &dashboard{
|
||||
conns: make(map[uint32]*client),
|
||||
conns: make(map[uint32]*client),
|
||||
config: config,
|
||||
Metrics: &metricSamples{},
|
||||
closing: make(chan chan error),
|
||||
|
|
|
|||
Loading…
Reference in a new issue