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 before_install: npm install -g grunt-cli
install: install:
- npm 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 = [ var scripts = [
'src/js/app.js', 'src/js/app.js',
'src/js/controllers.js', 'src/js/controllers.js',
@ -38,8 +35,8 @@ module.exports = function(grunt) {
pkg: grunt.file.readJSON('package.json'), pkg: grunt.file.readJSON('package.json'),
clean: { clean: {
build: ['dist'], build: ['dist'],
cleanup_js: ['dist/js/*.*', '!dist/js/netstats.*'], js: ['dist/js/*.*', '!dist/js/netstats.*'],
cleanup_css: ['dist/css/*.css', '!dist/css/netstats.*.css'], css: ['dist/css/*.css', '!dist/css/netstats.*.css']
}, },
jade: { jade: {
build: { build: {
@ -52,6 +49,17 @@ module.exports = function(grunt) {
files: { files: {
'dist/index.html': 'src/views/index.jade' '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: { copy: {
@ -67,7 +75,7 @@ module.exports = function(grunt) {
{ {
expand: true, expand: true,
cwd: 'src/images/', cwd: 'src/images/',
src: ['*.*'], src: ['*.ico'],
dest: 'dist/', dest: 'dist/',
filter: 'isFile' filter: 'isFile'
}, },
@ -85,6 +93,37 @@ module.exports = function(grunt) {
dest: 'dist/js/lib' 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: { cssmin: {
@ -149,7 +188,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-uglify'); 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('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('build', 'default'); 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('all', 'default'); grunt.registerTask('poa', 'default');
}; };

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] [![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") ![Screenshot](src/images/screenshot-v0.1.0.png "Screenshot")
## Prerequisite #### Prerequisite
* node * node
* npm * npm
## Installation #### Installation
Make sure you have node.js and npm installed. Make sure you have node.js and npm installed.
Clone the repository and install the dependencies Clone the repository and install the dependencies:
```bash ```bash
git clone https://github.com/goerli/netstats-server git clone https://github.com/goerli/ethstats-server
cd netstats-server cd ethstats-server
npm install npm install
sudo npm install -g grunt-cli 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. 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 ```bash
grunt grunt poa
``` ```
## Run #### Run
Start a node process and pass the websocket secret to it.
```bash ```bash
WS_SECRET="asdf" npm start 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 ## Proof-of-Work (Legacy)
[travis-url]: https://travis-ci.org/goerli/netstats-server
[dep-image]: https://david-dm.org/goerli/netstats-server.svg ![Screenshot](src/images/screenshot-v0.0.6.png "Screenshot")
[dep-url]: https://david-dm.org/goerli/netstats-server
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 #!/usr/bin/env node
var debug = require('debug')('eth-netstats'); var debug = require('debug')('ethstats-server');
var app = require('../app'); var app = require('../app');

View File

@ -1,7 +1,7 @@
{ {
"name": "netstats-server", "name": "ethstats-server",
"description": "Görli Network Dashboard", "description": "Ethstats Network Dashboard",
"version": "0.1.0", "version": "0.2.0",
"private": true, "private": true,
"engines": { "engines": {
"node": "8.11.1", "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 = ' '); }
.icon-database { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-mining { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-loader { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-check-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-cancel-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-warning-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-network { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-block { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-bulb { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-node { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-laptop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-time { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-clock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-group { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-gas { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-difficulty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-uncle { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-hashrate { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-gasprice { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }

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 = ' '); }
.icon-database { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-mining { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-loader { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-check-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-cancel-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-warning-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-network { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-block { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-bulb { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-node { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-laptop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-time { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-clock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-group { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-gas { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-difficulty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-uncle { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-hashrate { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
.icon-gasprice { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }

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 
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 
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  
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  ({{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") html(ng-app="netStatsApp")
head head
meta(name="viewport", content="width=device-width, initial-scale=1.0, maximum-scale=1.0") 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; } 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') link(rel='stylesheet', href='/css/netstats.min.css')
meta(name='robots', content='index,follow') meta(name='robots', content='index,follow')