Merge pull request #1 from goerli/a5-build-legacy

server: allow for poa and pow network stats #12
This commit is contained in:
Afri Schoedon 2018-12-03 20:53:40 +01:00 committed by GitHub
commit 2f25d94fc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1223 additions and 32 deletions

View File

@ -5,4 +5,5 @@ node_js: '8'
before_install: npm install -g grunt-cli
install:
- npm install
- npm run dist
- npm run dist -- pow
- npm run dist -- poa

View File

@ -1,6 +1,3 @@
var src = 'src/';
var dest = 'dist/';
var scripts = [
'src/js/app.js',
'src/js/controllers.js',
@ -38,8 +35,8 @@ module.exports = function(grunt) {
pkg: grunt.file.readJSON('package.json'),
clean: {
build: ['dist'],
cleanup_js: ['dist/js/*.*', '!dist/js/netstats.*'],
cleanup_css: ['dist/css/*.css', '!dist/css/netstats.*.css'],
js: ['dist/js/*.*', '!dist/js/netstats.*'],
css: ['dist/css/*.css', '!dist/css/netstats.*.css']
},
jade: {
build: {
@ -52,6 +49,17 @@ module.exports = function(grunt) {
files: {
'dist/index.html': 'src/views/index.jade'
}
},
build_pow: {
options: {
data: {
debug: false,
pretty: true
}
},
files: {
'dist/index.html': 'src/pow/views/index.jade'
}
}
},
copy: {
@ -67,7 +75,7 @@ module.exports = function(grunt) {
{
expand: true,
cwd: 'src/images/',
src: ['*.*'],
src: ['*.ico'],
dest: 'dist/',
filter: 'isFile'
},
@ -85,6 +93,37 @@ module.exports = function(grunt) {
dest: 'dist/js/lib'
}
]
},
build_pow: {
files: [
{
expand: true,
cwd: 'src/fonts/',
src: ['*.*'],
dest: 'dist/fonts/',
filter: 'isFile'
},
{
expand: true,
cwd: 'src/images/',
src: ['*.ico'],
dest: 'dist/',
filter: 'isFile'
},
{
expand: true,
cwd: 'src/pow/css/',
src: styles,
dest: 'dist/css/',
filter: 'isFile'
},
{
expand: true,
cwd: 'src/js/lib/',
src: ['*.*'],
dest: 'dist/js/lib'
}
]
}
},
cssmin: {
@ -149,7 +188,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['clean:build', 'clean:cleanup_js', 'clean:cleanup_css', 'jade:build', 'copy:build', 'cssmin:build', 'concat:vendor', 'concat:scripts', 'uglify:app', 'concat:netstats', 'concat:css', 'clean:cleanup_js', 'clean:cleanup_css']);
grunt.registerTask('build', 'default');
grunt.registerTask('all', 'default');
grunt.registerTask('default', ['clean:build', 'clean:js', 'clean:css', 'jade:build', 'copy:build', 'cssmin:build', 'concat:vendor', 'concat:scripts', 'uglify:app', 'concat:netstats', 'concat:css', 'clean:js', 'clean:css']);
grunt.registerTask('pow', ['clean:build', 'clean:js', 'clean:css', 'jade:build_pow', 'copy:build_pow', 'cssmin:build', 'concat:vendor', 'concat:scripts', 'uglify:app', 'concat:netstats', 'concat:css', 'clean:js', 'clean:css']);
grunt.registerTask('poa', 'default');
};

View File

@ -1,4 +1,4 @@
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>

View File

@ -1,42 +1,57 @@
Ethereum POA-Network Stats
============
Ethereum Network Stats with POA and POW support
===============================================
[![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url]
This is a visual interface for tracking proof-of-authority network status. It uses WebSockets to receive stats from running nodes and output them through an angular interface. It is the front-end implementation for [netstats-client](https://github.com/goerli/netstats-client).
This is a visual interface for tracking proof-of-work ("mainnet") and proof-of-authority ("testnet") network status. It uses WebSockets to receive stats from running nodes and output them through an angular interface. It is the front-end implementation for [netstats-client](https://github.com/goerli/netstats-client).
## Proof-of-Authority
![Screenshot](src/images/screenshot-v0.1.0.png "Screenshot")
## Prerequisite
#### Prerequisite
* node
* npm
## Installation
#### Installation
Make sure you have node.js and npm installed.
Clone the repository and install the dependencies
Clone the repository and install the dependencies:
```bash
git clone https://github.com/goerli/netstats-server
cd netstats-server
git clone https://github.com/goerli/ethstats-server
cd ethstats-server
npm install
sudo npm install -g grunt-cli
```
## Build the resources
#### Build
In order to build the static files you have to run grunt tasks which will generate dist directories containing the js and css files, fonts and images.
```bash
grunt
grunt poa
```
## Run
#### Run
Start a node process and pass the websocket secret to it.
```bash
WS_SECRET="asdf" npm start
```
see the interface at http://localhost:3000
Find the interface at http://localhost:3000
[travis-image]: https://travis-ci.org/goerli/netstats-server.svg
[travis-url]: https://travis-ci.org/goerli/netstats-server
[dep-image]: https://david-dm.org/goerli/netstats-server.svg
[dep-url]: https://david-dm.org/goerli/netstats-server
## Proof-of-Work (Legacy)
![Screenshot](src/images/screenshot-v0.0.6.png "Screenshot")
Same as above, just run the `pow` build task in Grunt.
```bash
grunt pow
WS_SECRET="asdf" npm start
```
:-)
[travis-image]: https://travis-ci.org/goerli/ethstats-server.svg
[travis-url]: https://travis-ci.org/goerli/ethstats-server
[dep-image]: https://david-dm.org/goerli/ethstats-server.svg
[dep-url]: https://david-dm.org/goerli/ethstats-server

View File

@ -1,3 +1,3 @@
#!/usr/bin/env node
var debug = require('debug')('eth-netstats');
var debug = require('debug')('ethstats-server');
var app = require('../app');

View File

@ -1,7 +1,7 @@
{
"name": "netstats-server",
"description": "Görli Network Dashboard",
"version": "0.1.0",
"name": "ethstats-server",
"description": "Ethstats Network Dashboard",
"version": "0.2.0",
"private": true,
"engines": {
"node": "8.11.1",

85
src/pow/css/animation.css Normal file
View File

@ -0,0 +1,85 @@
/*
Animation example, for spinners
*/
.animate-spin {
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
-webkit-animation: spin 2s infinite linear;
animation: spin 2s infinite linear;
display: inline-block;
}
@-moz-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-webkit-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-o-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-ms-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}

10
src/pow/css/bootstrap.min.css vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,23 @@
.icon-truck:before { content: '\e800'; } /* '' */
.icon-database:before { content: '\e801'; } /* '' */
.icon-mining:before { content: '\e802'; } /* '' */
.icon-check:before { content: '\e803'; } /* '' */
.icon-cancel:before { content: '\e804'; } /* '' */
.icon-loader:before { content: '\e805'; } /* '' */
.icon-check-o:before { content: '\e806'; } /* '' */
.icon-cancel-o:before { content: '\e807'; } /* '' */
.icon-warning-o:before { content: '\e808'; } /* '' */
.icon-network:before { content: '\e809'; } /* '' */
.icon-block:before { content: '\e80a'; } /* '' */
.icon-bulb:before { content: '\e80b'; } /* '' */
.icon-node:before { content: '\e80c'; } /* '' */
.icon-laptop:before { content: '\e80d'; } /* '' */
.icon-time:before { content: '\e80e'; } /* '' */
.icon-clock:before { content: '\e80f'; } /* '' */
.icon-group:before { content: '\e810'; } /* '' */
.icon-gas:before { content: '\e811'; } /* '' */
.icon-difficulty:before { content: '\e812'; } /* '' */
.icon-uncle:before { content: '\e813'; } /* '' */
.icon-hashrate:before { content: '\e814'; } /* '' */
.icon-gasprice:before { content: '\e815'; } /* '' */

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,23 @@
.icon-truck { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.icon-database { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
.icon-mining { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
.icon-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
.icon-loader { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
.icon-check-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
.icon-cancel-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
.icon-warning-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe808;&nbsp;'); }
.icon-network { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe809;&nbsp;'); }
.icon-block { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80a;&nbsp;'); }
.icon-bulb { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80b;&nbsp;'); }
.icon-node { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80c;&nbsp;'); }
.icon-laptop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80d;&nbsp;'); }
.icon-time { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80e;&nbsp;'); }
.icon-clock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80f;&nbsp;'); }
.icon-group { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe810;&nbsp;'); }
.icon-gas { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe811;&nbsp;'); }
.icon-difficulty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe812;&nbsp;'); }
.icon-uncle { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe813;&nbsp;'); }
.icon-hashrate { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe814;&nbsp;'); }
.icon-gasprice { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe815;&nbsp;'); }

View File

@ -0,0 +1,34 @@
[class^="icon-"], [class*=" icon-"] {
font-family: 'minimal-icons';
font-style: normal;
font-weight: normal;
/* fix buttons height */
line-height: 1em;
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
}
.icon-truck { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.icon-database { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
.icon-mining { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
.icon-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
.icon-loader { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
.icon-check-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
.icon-cancel-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
.icon-warning-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe808;&nbsp;'); }
.icon-network { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe809;&nbsp;'); }
.icon-block { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80a;&nbsp;'); }
.icon-bulb { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80b;&nbsp;'); }
.icon-node { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80c;&nbsp;'); }
.icon-laptop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80d;&nbsp;'); }
.icon-time { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80e;&nbsp;'); }
.icon-clock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80f;&nbsp;'); }
.icon-group { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe810;&nbsp;'); }
.icon-gas { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe811;&nbsp;'); }
.icon-difficulty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe812;&nbsp;'); }
.icon-uncle { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe813;&nbsp;'); }
.icon-hashrate { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe814;&nbsp;'); }
.icon-gasprice { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe815;&nbsp;'); }

View File

@ -0,0 +1,78 @@
@font-face {
font-family: 'minimal-icons';
src: url('../fonts/minimal-icons.eot?7541141');
src: url('../fonts/minimal-icons.eot?7541141#iefix') format('embedded-opentype'),
url('../fonts/minimal-icons.woff?7541141') format('woff'),
url('../fonts/minimal-icons.ttf?7541141') format('truetype'),
url('../fonts/minimal-icons.svg?7541141#minimal-icons') format('svg');
font-weight: normal;
font-style: normal;
}
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
/*
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'minimal-icons';
src: url('../fonts/minimal-icons.svg?7541141#minimal-icons') format('svg');
}
}
*/
[class^="icon-"]:before, [class*=" icon-"]:before {
font-family: "minimal-icons";
font-style: normal;
font-weight: normal;
speak: none;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: .2em;
text-align: center;
/* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
/* Animation center compensation - margins should be symmetric */
/* remove if not needed */
margin-left: .2em;
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
/*-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;*/
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
.icon-truck:before { content: '\e800'; } /* '' */
.icon-database:before { content: '\e801'; } /* '' */
.icon-mining:before { content: '\e802'; } /* '' */
.icon-check:before { content: '\e803'; } /* '' */
.icon-cancel:before { content: '\e804'; } /* '' */
.icon-loader:before { content: '\e805'; } /* '' */
.icon-check-o:before { content: '\e806'; } /* '' */
.icon-cancel-o:before { content: '\e807'; } /* '' */
.icon-warning-o:before { content: '\e808'; } /* '' */
.icon-network:before { content: '\e809'; } /* '' */
.icon-block:before { content: '\e80a'; } /* '' */
.icon-bulb:before { content: '\e80b'; } /* '' */
.icon-node:before { content: '\e80c'; } /* '' */
.icon-laptop:before { content: '\e80d'; } /* '' */
.icon-time:before { content: '\e80e'; } /* '' */
.icon-clock:before { content: '\e80f'; } /* '' */
.icon-group:before { content: '\e810'; } /* '' */
.icon-gas:before { content: '\e811'; } /* '' */
.icon-difficulty:before { content: '\e812'; } /* '' */
.icon-uncle:before { content: '\e813'; } /* '' */
.icon-hashrate:before { content: '\e814'; } /* '' */
.icon-gasprice:before { content: '\e815'; } /* '' */

558
src/pow/css/style.css Normal file
View File

@ -0,0 +1,558 @@
html {
width: 100%;
}
body {
width: 100%;
min-width: 1900px;
font-smooth: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 200;
src: local('Source Sans Pro ExtraLight'), local('SourceSansPro-ExtraLight'), url(../fonts/SourceSansPro-ExtraLight.woff2) format('woff2');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url(../fonts/SourceSansPro-Light.woff2) format('woff2');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(../fonts/SourceSansPro-Regular.woff2) format('woff2');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
src: local('Source Sans Pro SemiBold'), local('SourceSansPro-SemiBold'), url(../fonts/SourceSansPro-SemiBold.woff2) format('woff2');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(../fonts/SourceSansPro-Bold.woff2) format('woff2');
}
table td {
font-size: 14px;
white-space: nowrap !important;
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
}
.propagationBox {
position: relative;
width: 8px;
height: 8px;
float: left;
top: 5px;
margin-right: 5px;
-webkit-border-radius: 2px;
border-radius: 2px;
}
.bg-success,
.text-success .propagationBox {
background: #7bcc3a;
}
.bg-info,
.text-info .propagationBox {
background: #10a0de;
}
.bg-highlight,
.text-highlight .propagationBox {
background: #bd93f9;
}
.bg-warning,
.text-warning .propagationBox {
background: #FFD162;
}
.bg-orange,
.text-orange .propagationBox {
background: #ff8a00;
}
.bg-danger,
.text-danger .propagationBox {
background: #F74B4B;
}
.text-gray .propagationBox {
background: none !important;
border: 1px solid #777;
}
.bg-success,
.bg-info,
.bg-warning,
.bg-orange,
.bg-danger {
color: #000;
}
.text-gray {
color: #777 !important;
}
.text-orange {
color: #ff8a00;
}
.container-fluid {
padding-left: 30px;
padding-right: 30px;
}
.stat-holder {
background: #090909;
border: 1px solid rgba(255,255,255,0.05);
}
.big-info {
padding-top: 15px;
padding-bottom: 15px;
}
.big-info .icon-full-width i {
display: block;
width: 85px;
height: 70px;
font-size: 70px;
line-height: 70px;
margin-right: 15px;
margin-left: -15px;
}
.big-info span.small-title,
.big-info div.small-title-miner {
display: block;
}
span.small-title,
div.small-title-miner {
font-weight: 700;
font-size: 14px;
line-height: 20px;
letter-spacing: 1px;
text-transform: uppercase;
color: #aaa;
}
span.small-title span.small {
font-size: 11px;
font-weight: 600;
line-height: 16px;
letter-spacing: 0px;
color: #666;
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
}
.big-info .big-details {
display: block;
font-weight: 200;
font-size: 50px;
line-height: 55px;
letter-spacing: -4px;
word-spacing: nowrap !important;
}
.big-info .big-details .small-hash {
font-size: 87%;
}
.big-info .big-details-holder {
position: absolute;
top: 15px;
left: 99px;
}
.big-info.chart {
padding-top: 12px;
}
.big-info.chart .big-details {
display: table;
top: 40px;
margin: 0 auto;
}
.big-info.chart {
height: 120px;
-webkit-box-sizing: border-box
box-sizing: border-box;
}
.big-info.chart.double-chart {
height: 242px;
}
.blocks-holder {
width: 288px;
padding-top: 6px;
margin-left: -2px;
display: table;
margin: 0 auto;
}
.blocks-holder {
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
}
.blocks-holder div.small-title-miner {
font-family: "Lucida Console", "Courier New", Courier, monospace;
font-size: 11px;
letter-spacing: -0.1px;
text-transform: none;
white-space: nowrap;
color: #777;
}
.blocks-holder .block-count {
font-family: 'Lucida Console', "Courier New", Courier, monospace;
font-weight: bold;
font-size: 10px;
padding-top: 3px;
float: right;
}
.blocks-holder .block {
width: 6px;
height: 6px;
margin: 2px 1px 6px 0px;
float: left;
-webkit-border-radius: 1px;
border-radius: 1px;
opacity: .8;
}
.blocks-holder .block:first-child {
margin-left: 0px;
}
.blocks-holder .block:last-child {
margin-right: 0px;
}
.second-row .box {
height: 40px;
line-height: 24px !important;
padding: 5px 15px;
}
.second-row .box i,
.big-info.chart i {
position: relative;
top: 2px;
left: -3px;
font-size: 24px;
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
margin-right: 7px;
float: left;
}
.big-info.chart i {
font-size: 24px;
top: -2px;
}
.small-value {
font-weight: 300;
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
float: right;
}
.second-row .box .small-value {
float: right;
}
.big-info .small-value {
position: absolute;
right: 14px;
top: 10px;
}
table i {
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
}
table th,
table td {
border-color: #222 !important;
}
table td {
line-height: 18px !important;
}
table th {
color: #888;
}
table th i {
line-height: 1em;
font-size: 20px;
}
table td i {
position: relative;
top: 2px;
left: 2px;
}
table td.peerPropagationChart {
padding: 4px 5px !important;
}
nodepropagchart {
display: inline-block;
width: 107px;
height: 20px;
vertical-align: top;
}
.table>tbody>tr>td,
.table>thead>tr>th {
padding: 5px;
}
.th-nodecheck,
.td-nodecheck {
width: 38px;
text-align: center;
}
.td-nodecheck i {
left: 0px;
}
.th-nodename {
width: 300px;
text-overflow: ellipsis;
}
.th-nodetype {
width: 220px;
}
.th-latency {
width: 100px;
}
.th-blockhash {
width: 150px;
}
.th-blocktime {
width: 110px;
}
.th-peerPropagationTime {
width: 120px;
}
.th-peerPropagationChart {
width: 140px;
}
.nodeInfo .tooltip .tooltip-inner {
max-width: 400px;
text-align: left;
font-size: 12px;
}
.map-holder {
padding: 0;
}
#mapHolder {
position: relative;
display: block;
width: 100%;
height: 242px;
overflow: hidden;
}
#mapHolder > svg {
right: 0;
bottom: 0;
width: 100%;
height: 100%;
display: inline-block;
position: absolute;
top: 0;
left: 0;
}
.jqsfield {
position: relative;
padding: 5px 0;
width: auto;
left: -50%;
word-wrap: wrap;
text-align: center;
}
.d3-tip {
padding: 5px 0;
}
.jqsfield .tooltip-arrow {
position: absolute;
bottom: 0;
left: 50%;
margin-left: -5px;
border-width: 5px 5px 0;
border-top-color: #fff;
}
.datamaps-hoverover .tooltip-arrow,
.d3-tip .tooltip-arrow {
position: absolute;
top: -5px;
left: 0px;
margin-left: -5px;
border-width: 0px 5px 5px 5px;
border-bottom-color: #fff;
}
.d3-tip .tooltip-arrow {
top: 0px;
left: 50%;
}
.hoverinfo {
position: relative;
width: auto;
left: -50%;
text-align: center;
color: #333;
border: none !important;
box-shadow: none !important;
border-radius: 3px !important;
padding: 5px !important;
line-height: 14px !important;
}
.hoverinfo .propagationBox {
top: 3px;
}
svg {
overflow: visible !important;
}
svg .bars .bar {
opacity: 1;
shape-rendering: auto;
}
svg .bars .handle {
opacity: 0;
}
svg .bars .highlight {
opacity: 0;
}
svg .bars g:hover .highlight {
opacity: 1;
}
svg .line {
fill: none;
stroke: #ff0000;
stroke-width: 1.3px;
stroke-linejoin: round;
stroke-linecap: round;
shape-rendering: geometric-precision;
/*-webkit-svg-shadow: 0 0 7px #fff;*/
}
svg .bar text {
text-anchor: end;
font-size: 12px;
}
svg .axis path,
svg .axis line {
fill: none;
stroke: rgba(255,255,255,0.15);
shape-rendering: crispEdges;
}
svg .axis text {
fill: #777;
font-size: 10px;
letter-spacing: 0px;
font-family: "Source Sans Pro";
font-weight: 700;
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
}
svg .y.axis .tick:first-child text {
opacity: 0;
}
@media (max-width: 768px) {
.container-fluid {
padding-left: 5px;
padding-right: 5px;
}
.big-info .icon-full-width i {
width: 75px;
height: 67px;
font-size: 67px;
margin-left: -25px;
}
.big-info .big-details-holder {
left: 75px;
}
.big-info .big-details {
font-size: 35px;
}
.blocks-holder div.small-title-miner {
font-family: inherit;
font-size: 11px;
letter-spacing: -.5px;
}
.blocks-holder {
width: 100%;
}
.big-info.chart i, .second-row .box i {
font-size: 18px;
margin-right: 0px;
}
.second-row .box {
height: 100%; /* BUG XXX */
}
}
@media (max-width: 600px) {
.blocks-holder div.small-title-miner {
font-size: 10px;
}
}

1
src/pow/css/toastr.min.css vendored Normal file

File diff suppressed because one or more lines are too long

6
src/pow/views/error.jade Normal file
View File

@ -0,0 +1,6 @@
extends layout
block content
h1= message
h2= error.status
pre #{error.stack}

228
src/pow/views/index.jade Normal file
View File

@ -0,0 +1,228 @@
//- index.jade
extends ./layout.jade
block content
div.container-fluid(ng-controller='StatsCtrl')
div.row(ng-cloak)
div.col-xs-2.stat-holder
div.big-info.bestblock.text-info
div.pull-left.icon-full-width
i.icon-block
div.big-details-holder
span.small-title best block
span.big-details {{'#'}}{{ bestBlock | number}}
div.clearfix
div.col-xs-2.stat-holder
div.big-info.uncleCount.text-info
div.pull-left.icon-full-width
i.icon-uncle
div.big-details-holder
span.small-title uncles&nbsp;
span.small (current / last 50)
span.big-details {{ bestStats.block.uncles.length }}/{{ uncleCount }}
div.clearfix
div.col-xs-2.stat-holder
div.big-info.blocktime(class="{{ lastBlock | timeClass : true }}")
div.pull-left.icon-full-width
i.icon-time
div.big-details-holder
span.small-title last block
span.big-details {{ lastBlock | blockTimeFilter }}
//- span.big-details(time-ago="lastBlock")
div.clearfix
div.col-xs-2.stat-holder
div.big-info.avgblocktime(class="{{ avgBlockTime | avgTimeClass }}")
div.pull-left.icon-full-width
i.icon-gas
div.big-details-holder
span.small-title avg block time
span.big-details {{ avgBlockTime | avgTimeFilter }}
div.clearfix
div.col-xs-2.stat-holder
div.big-info.difficulty.text-orange
div.pull-left.icon-full-width
i.icon-hashrate
div.big-details-holder
span.small-title avg network hashrate
span.big-details(ng-bind-html="avgHashrate | networkHashrateFilter")
div.clearfix
div.col-xs-2.stat-holder
div.big-info.difficulty.text-danger
div.pull-left.icon-full-width
i.icon-difficulty
div.big-details-holder
span.small-title difficulty
span.big-details
span.small-hash {{ lastDifficulty | totalDifficultyFilter }}
div.clearfix
div.clearfix
div.row(ng-cloak)
div.col-xs-12.stats-boxes(style="padding-top: 0px;")
div.row.second-row
div.col-xs-2.stat-holder.box
div.active-nodes(class="{{ nodesActive | nodesActiveClass : nodesTotal }}")
i.icon-node
span.small-title active nodes
span.small-value {{nodesActive}}/{{nodesTotal}}
div.col-xs-2.stat-holder.box
div.gasprice.text-info
i.icon-gasprice
span.small-title gas price
span.small-value {{ bestStats.gasPrice.toString() | gasPriceFilter }}
div.col-xs-2.stat-holder.box
div.gasprice.text-info
i.icon-gasprice
span.small-title gas limit
span.small-value {{ bestStats.block.gasLimit }} gas
div.col-xs-2.stat-holder.box
div.page-latency(class="{{ {active: true, latency: latency} | latencyClass }}")
i.icon-clock
span.small-title page latency
span.small-value {{latency}} ms
div.col-xs-2.stat-holder.box
div.uptime(class="{{ upTimeTotal | upTimeClass : true }}")
i.icon-bulb
span.small-title uptime
span.small-value {{ upTimeTotal | upTimeFilter }}
div.col-xs-2.stat-holder.box
div.row
div.col-xs-8
div.row
div.col-xs-3.stat-holder
div.big-info.chart(class="{{ avgBlockTime | avgTimeClass }}")
//- i.icon-time
span.small-title block time
//- span.small-value {{ avgBlockTime | avgTimeFilter }}
sparkchart.big-details.spark-blocktimes(data="{{lastBlocksTime.join(',')}}", tooltipsuffix="s")
div.col-xs-3.stat-holder
div.big-info.chart.text-info
//- i.icon-difficulty
span.small-title difficulty
//- span.small-value {{ lastDifficulty | number }}
sparkchart.big-details.spark-difficulty(data="{{difficultyChart.join(',')}}")
div.col-xs-3.stat-holder.xpull-right
div.big-info.chart.xdouble-chart(class="{{ blockPropagationAvg | propagationAvgTimeClass : true }}")
//- i.icon-gas
span.small-title block propagation
//- span.small-value {{ blockPropagationAvg | blockPropagationFilter : '' }}
histogram.big-details.d3-blockpropagation(data="blockPropagationChart")
div.col-xs-3.stat-holder.xpull-right
div.big-info.chart.xdouble-chart
span.small-title last blocks miners
div.blocks-holder(ng-repeat='miner in miners track by miner.miner', data-toggle="tooltip", data-placement="right", data-original-title="{{miner.blocks}}")
div.block-count(class="{{miner.blocks | minerBlocksClass : 'text-'}}") {{miner.blocks}}
//- div.small-title-miner {{miner.miner | minerNameFilter : miner.name}}
div.small-title-miner {{miner.miner}}
minerblock(blocks="{{miner.blocks}}")
div.clearfix
div.col-xs-3.stat-holder
div.big-info.chart.text-info
//- i.icon-uncle
span.small-title uncle count&nbsp;
span.small (25 blocks per bar)
//- span.small-value {{ bestStats.block.uncles.length }}/{{ uncleCount }}
sparkchart.big-details.spark-uncles(data="{{uncleCountChart.join(',')}}")
div.col-xs-3.stat-holder
div.big-info.chart.text-info
//- i.icon-uncle
span.small-title transactions
sparkchart.big-details.spark-transactions(data="{{transactionDensity.join(',')}}")
div.col-xs-3.stat-holder
div.big-info.chart.text-info
//- i.icon-gasprice
span.small-title gas spending
sparkchart.big-details.spark-gasspending(data="{{gasSpending.join(',')}}")
div.col-xs-3.stat-holder
div.big-info.chart.text-info
//- i.icon-difficulty
span.small-title gas limit
//- span.small-value {{ lastDifficulty | number }}
sparkchart.big-details.spark-difficulty(data="{{lastGasLimit.join(',')}}")
div.col-xs-4.stat-holder.map-holder
//- div.col-xs-12
nodemap#mapHolder(data="map")
div.row(ng-cloak, style="padding-top: 10px")
table.table.table-striped
thead
tr.text-info
th.th-nodecheck
i.icon-check-o(data-toggle="tooltip", data-placement="top", title="Pin nodes to display first", ng-click="orderTable(['-stats.block.number', 'stats.block.propagation'], false)")
th.th-nodename
i.icon-node(data-toggle="tooltip", data-placement="top", title="Node name", ng-click="orderTable(['info.name'], false)")
th.th-nodetype
i.icon-laptop(data-toggle="tooltip", data-placement="top", title="Node type", ng-click="orderTable(['info.node'], false)")
th.th-latency
i.icon-clock(data-toggle="tooltip", data-placement="top", title="Node latency", ng-click="orderTable(['stats.latency'], false)")
th
i.icon-mining(data-toggle="tooltip", data-placement="top", title="Is mining", ng-click="orderTable(['-stats.hashrate'], false)")
th
i.icon-group(data-toggle="tooltip", data-placement="top", title="Peers", ng-click="orderTable(['-stats.peers'], false)")
th
i.icon-network(data-toggle="tooltip", data-placement="top", title="Pending transactions", ng-click="orderTable(['-stats.pending'], false)")
th
i.icon-block(data-toggle="tooltip", data-placement="top", title="Last block", ng-click="orderTable(['-stats.block.number', 'stats.block.propagation'], false)")
th.th-blockhash &nbsp;
th.th-blockhash
i.icon-difficulty(data-toggle="tooltip", data-placement="top", title="Total difficulty", ng-click="orderTable(['-stats.block.totalDifficulty'], false)")
th
i.icon-check-o(data-toggle="tooltip", data-placement="top", title="Block transactions", ng-click="orderTable(['-stats.block.transactions.length'], false)")
th
i.icon-uncle(data-toggle="tooltip", data-placement="top", title="Uncles", ng-click="orderTable(['-stats.block.uncles.length'], false)")
th.th-blocktime
i.icon-time(data-toggle="tooltip", data-placement="top", title="Last block time", ng-click="orderTable(['-stats.block.received'], false)")
th.th-peerPropagationTime
i.icon-gas(data-toggle="tooltip", data-placement="top", title="Propagation time", ng-click="orderTable(['-stats.block.number', 'stats.block.propagation'], false)")
th.th-peerPropagationChart
th.th-peerPropagationAvg
i.icon-gas(data-toggle="tooltip", data-placement="top", title="Average propagation time", ng-click="orderTable(['stats.propagationAvg'], false)")
th
i.icon-bulb(data-toggle="tooltip", data-placement="top", title="Up-time", ng-click="orderTable(['-stats.uptime'], false)")
tbody(ng-cloak)
tr(ng-repeat='node in nodes | orderBy:predicate track by node.id', class="{{ node.stats | mainClass : bestBlock }}", id="node_{{node.id}}")
td.td-nodecheck
i(ng-click="pinNode(node.id)", class="{{ node.pinned | nodePinClass }}", data-toggle="tooltip", data-placement="right", data-original-title="Click to {{ node.pinned ? 'un' : '' }}pin")
td.nodeInfo(rel="{{node.id}}")
span.small(data-toggle="tooltip", data-placement="top", data-html="true", data-original-title="{{node | geoTooltip}}") {{node.info.name}}
//- span.small &nbsp;({{node.info.ip}})
a.small(href="https://github.com/ethereum/wiki/wiki/Network-Status#updating", target="_blank", data-toggle="tooltip", data-placement="top", data-html="true", data-original-title="Netstats client needs update.<br>Click this icon for instructions.", class="{{ node.info | nodeClientClass : currentApiVersion }}")
i.icon-warning-o
td
div.small(ng-bind-html="node.info.node | nodeVersion")
td(class="{{ node.readable.latencyClass }}")
span.small {{ node.readable.latency }}
td(class="{{ node.stats.mining | hashrateClass : node.stats.active }}", ng-bind-html="node.stats.hashrate | hashrateFilter : node.stats.mining")
td(class="{{ node.stats.peers | peerClass : node.stats.active }}", style="padding-left: 11px;") {{node.stats.peers}}
td(style="padding-left: 15px;") {{node.stats.pending}}
td(class="{{ node.stats | blockClass : bestBlock }}")
span(class="{{ node.readable.forkMessage ? node.readable.forkClass : '' }}") {{'#'}}{{ node.stats.block.number | number }}
//- a.small(data-toggle="tooltip", data-placement="top", data-html="true", data-original-title="{{ node.readable.forkMessage }}", class="{{ node.readable.forkClass }}")
i.icon-warning-o
td(class="{{ node.stats | blockClass : bestBlock }}")
span.small {{node.stats.block.hash | hashFilter}}
td(class="{{ node.stats | blockClass : bestBlock }}")
span.small {{node.stats.block.totalDifficulty | number}}
td(style="padding-left: 14px;") {{node.stats.block.transactions.length || 0}}
td(style="padding-left: 14px;") {{node.stats.block.uncles.length || 0}}
td(class="{{ node.stats.block.received | timeClass : node.stats.active }}") {{node.stats.block.received | blockTimeFilter }}
td(class="{{ node.stats | propagationTimeClass : bestBlock }}")
div.propagationBox
span {{node.stats.block.propagation | blockPropagationFilter}}
td.peerPropagationChart(class="{{node.id}}")
nodepropagchart(data="{{node.history.join(',')}}")
td(class="{{ node.stats | propagationNodeAvgTimeClass : bestBlock }}") {{ node.stats | blockPropagationAvgFilter : bestBlock }}
td(class="{{ node.stats.uptime | upTimeClass : node.stats.active }}") {{ node.stats.uptime | upTimeFilter }}

14
src/pow/views/layout.jade Normal file
View File

@ -0,0 +1,14 @@
//- layout.jade
doctype html
html(ng-app="netStatsApp")
head
meta(name="viewport", content="width=device-width, initial-scale=1.0, maximum-scale=1.0")
title Ethereum Network Status
style(type="text/css") [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
link(rel='stylesheet', href='/css/netstats.min.css')
meta(name='robots', content='index,follow')
meta(name='googlebot', content='index,follow')
link(rel='shortcut icon', type="image/x-icon", href="/favicon.ico")
body
block content
script(src="/js/netstats.min.js")

View File

@ -3,7 +3,7 @@ doctype html
html(ng-app="netStatsApp")
head
meta(name="viewport", content="width=device-width, initial-scale=1.0, maximum-scale=1.0")
title Görli Network Status
title Testnet Network Status
style(type="text/css") [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
link(rel='stylesheet', href='/css/netstats.min.css')
meta(name='robots', content='index,follow')