commit
67255f3e57
36
app.js
36
app.js
@ -3,6 +3,7 @@ var app = express();
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
var favicon = require('serve-favicon');
|
var favicon = require('serve-favicon');
|
||||||
var bodyParser = require('body-parser');
|
var bodyParser = require('body-parser');
|
||||||
|
var askedForHistory = false;
|
||||||
|
|
||||||
var Primus = require('primus'),
|
var Primus = require('primus'),
|
||||||
api,
|
api,
|
||||||
@ -62,13 +63,14 @@ api.on('connection', function(spark) {
|
|||||||
var info = Nodes.add(data);
|
var info = Nodes.add(data);
|
||||||
spark.emit('ready');
|
spark.emit('ready');
|
||||||
|
|
||||||
|
if(Nodes.getHistory().requiresUpdate() && !askedForHistory && Nodes.getNode({id: data.id}).canUpdate())
|
||||||
|
{
|
||||||
|
spark.emit('history', Nodes.getHistory().getHistoryRequestInterval());
|
||||||
|
askedForHistory = true;
|
||||||
|
}
|
||||||
|
|
||||||
client.write({action: 'add', data: info});
|
client.write({action: 'add', data: info});
|
||||||
|
client.write({action: 'charts', data: Nodes.getCharts()});
|
||||||
var blockPropagationChart = Nodes.blockPropagationChart();
|
|
||||||
client.write({action: 'blockPropagationChart', data: blockPropagationChart});
|
|
||||||
|
|
||||||
var uncleCount = Nodes.getUncleCount();
|
|
||||||
client.write({action: 'uncleCount', data: uncleCount});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -82,16 +84,22 @@ api.on('connection', function(spark) {
|
|||||||
if(stats !== false)
|
if(stats !== false)
|
||||||
{
|
{
|
||||||
client.write({action: 'update', data: stats});
|
client.write({action: 'update', data: stats});
|
||||||
|
client.write({action: 'charts', data: Nodes.getCharts()});
|
||||||
|
}
|
||||||
|
|
||||||
var blockPropagationChart = Nodes.blockPropagationChart();
|
if(Nodes.getHistory().requiresUpdate() && !askedForHistory && Nodes.getNode({id: data.id}).canUpdate())
|
||||||
client.write({action: 'blockPropagationChart', data: blockPropagationChart});
|
{
|
||||||
|
spark.emit('history', Nodes.getHistory().getHistoryRequestInterval());
|
||||||
var uncleCount = Nodes.getUncleCount();
|
askedForHistory = true;
|
||||||
client.write({action: 'uncleCount', data: uncleCount});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
spark.on('history', function(data){
|
||||||
|
client.write({action: 'charts', data: Nodes.addHistory(data.id, data.history)});
|
||||||
|
askedForHistory = false;
|
||||||
|
});
|
||||||
|
|
||||||
spark.on('node-ping', function(data){
|
spark.on('node-ping', function(data){
|
||||||
spark.emit('node-pong');
|
spark.emit('node-pong');
|
||||||
});
|
});
|
||||||
@ -118,11 +126,7 @@ client.on('connection', function(spark) {
|
|||||||
spark.on('ready', function(data){
|
spark.on('ready', function(data){
|
||||||
spark.emit('init', {nodes: Nodes.all()});
|
spark.emit('init', {nodes: Nodes.all()});
|
||||||
|
|
||||||
var blockPropagationChart = Nodes.blockPropagationChart();
|
spark.write({action: 'charts', data: Nodes.getCharts()});
|
||||||
spark.write({action: 'blockPropagationChart', data: blockPropagationChart});
|
|
||||||
|
|
||||||
var uncleCount = Nodes.getUncleCount();
|
|
||||||
spark.write({action: 'uncleCount', data: uncleCount});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
spark.on('client-pong', function(data) {
|
spark.on('client-pong', function(data) {
|
||||||
|
@ -40,6 +40,20 @@ Collection.prototype.update = function(id, stats)
|
|||||||
return node.setStats(stats, propagationHistory);
|
return node.setStats(stats, propagationHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Collection.prototype.addHistory = function(id, blocks)
|
||||||
|
{
|
||||||
|
var node = this.getNode({ id: id });
|
||||||
|
|
||||||
|
if(!node)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (var i = blocks.length - 1; i >= 0; i--) {
|
||||||
|
this._history.add(blocks[i], id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.getCharts();
|
||||||
|
}
|
||||||
|
|
||||||
Collection.prototype.updateLatency = function(id, latency)
|
Collection.prototype.updateLatency = function(id, latency)
|
||||||
{
|
{
|
||||||
var node = this.getNode({ id: id });
|
var node = this.getNode({ id: id });
|
||||||
@ -112,4 +126,14 @@ Collection.prototype.getUncleCount = function()
|
|||||||
return this._history.getUncleCount();
|
return this._history.getUncleCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Collection.prototype.getCharts = function()
|
||||||
|
{
|
||||||
|
return this._history.getCharts();
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection.prototype.getHistory = function()
|
||||||
|
{
|
||||||
|
return this._history;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = Collection;
|
module.exports = Collection;
|
@ -33,7 +33,7 @@ var History = function History(data)
|
|||||||
|
|
||||||
History.prototype.add = function(block, id)
|
History.prototype.add = function(block, id)
|
||||||
{
|
{
|
||||||
if(typeof block !== 'undefined' && typeof block.number !== 'undefined' && typeof block.uncles !== 'undefined' && typeof block.transactions !== 'undefined')
|
if(typeof block !== 'undefined' && typeof block.number !== 'undefined' && typeof block.uncles !== 'undefined' && typeof block.transactions !== 'undefined' && typeof block.difficulty !== 'undefined' && (this._items.length === 0 || block.number >= (this.bestBlock().height - MAX_HISTORY + 1)))
|
||||||
{
|
{
|
||||||
var historyBlock = this.search(block.number);
|
var historyBlock = this.search(block.number);
|
||||||
|
|
||||||
@ -63,6 +63,19 @@ History.prototype.add = function(block, id)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var prevBlock = this.prevMaxBlock(block.number);
|
||||||
|
|
||||||
|
if(prevBlock)
|
||||||
|
{
|
||||||
|
block.time = block.arrived - prevBlock.block.arrived;
|
||||||
|
|
||||||
|
if(block.number < this.bestBlock().height)
|
||||||
|
block.time = (block.timestamp - prevBlock.block.timestamp)*1000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block.time = 0;
|
||||||
|
}
|
||||||
var item = {
|
var item = {
|
||||||
height: block.number,
|
height: block.number,
|
||||||
block: block,
|
block: block,
|
||||||
@ -72,7 +85,6 @@ History.prototype.add = function(block, id)
|
|||||||
item.propagTimes.push({node: id, received: now, propagation: block.propagation});
|
item.propagTimes.push({node: id, received: now, propagation: block.propagation});
|
||||||
this._save(item);
|
this._save(item);
|
||||||
}
|
}
|
||||||
this.getNodePropagation(id);
|
|
||||||
|
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
@ -82,11 +94,13 @@ History.prototype.add = function(block, id)
|
|||||||
|
|
||||||
History.prototype._save = function(block)
|
History.prototype._save = function(block)
|
||||||
{
|
{
|
||||||
this._items.push(block);
|
this._items.unshift(block);
|
||||||
|
|
||||||
if(this._items.length > MAX_HISTORY){
|
if(this._items.length > MAX_HISTORY){
|
||||||
this._items.shift();
|
this._items.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._items = _.sortByOrder(this._items, 'height', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
History.prototype.search = function(number)
|
History.prototype.search = function(number)
|
||||||
@ -99,6 +113,18 @@ History.prototype.search = function(number)
|
|||||||
return this._items[index];
|
return this._items[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
History.prototype.prevMaxBlock = function(number)
|
||||||
|
{
|
||||||
|
var index = _.findIndex(this._items, function(item) {
|
||||||
|
return item.height < number;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(index < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return this._items[index];
|
||||||
|
}
|
||||||
|
|
||||||
History.prototype.bestBlock = function(obj)
|
History.prototype.bestBlock = function(obj)
|
||||||
{
|
{
|
||||||
return _.max(this._items, 'height');
|
return _.max(this._items, 'height');
|
||||||
@ -164,7 +190,7 @@ History.prototype.getBlockPropagation = function()
|
|||||||
return histogram;
|
return histogram;
|
||||||
}
|
}
|
||||||
|
|
||||||
History.prototype.getUncleCount = function(id)
|
History.prototype.getUncleCount = function()
|
||||||
{
|
{
|
||||||
var uncles = _(this._items)
|
var uncles = _(this._items)
|
||||||
.sortByOrder('height', false)
|
.sortByOrder('height', false)
|
||||||
@ -186,11 +212,41 @@ History.prototype.getUncleCount = function(id)
|
|||||||
return uncleBins;
|
return uncleBins;
|
||||||
}
|
}
|
||||||
|
|
||||||
History.prototype.getTransactionsCount = function(id)
|
History.prototype.getBlockTimes = function()
|
||||||
|
{
|
||||||
|
var blockTimes = _(this._items)
|
||||||
|
.sortByOrder('height', false)
|
||||||
|
.slice(0, MAX_BINS)
|
||||||
|
.reverse()
|
||||||
|
.map(function(item)
|
||||||
|
{
|
||||||
|
return item.block.time;
|
||||||
|
})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return blockTimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.getDifficulty = function()
|
||||||
|
{
|
||||||
|
var difficultyHistory = _(this._items)
|
||||||
|
.sortByOrder('height', false)
|
||||||
|
.slice(0, MAX_BINS)
|
||||||
|
.reverse()
|
||||||
|
.map(function(item)
|
||||||
|
{
|
||||||
|
return item.block.difficulty;
|
||||||
|
})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return difficultyHistory;
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.getTransactionsCount = function()
|
||||||
{
|
{
|
||||||
var txCount = _(this._items)
|
var txCount = _(this._items)
|
||||||
.sortByOrder('height', false)
|
.sortByOrder('height', false)
|
||||||
.slice(0, MAX_BINS - 1)
|
.slice(0, MAX_BINS)
|
||||||
.reverse()
|
.reverse()
|
||||||
.map(function(item)
|
.map(function(item)
|
||||||
{
|
{
|
||||||
@ -201,9 +257,74 @@ History.prototype.getTransactionsCount = function(id)
|
|||||||
return txCount;
|
return txCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
History.prototype.getGasSpending = function()
|
||||||
|
{
|
||||||
|
var gasSpending = _(this._items)
|
||||||
|
.sortByOrder('height', false)
|
||||||
|
.slice(0, MAX_BINS)
|
||||||
|
.reverse()
|
||||||
|
.map(function(item)
|
||||||
|
{
|
||||||
|
return item.block.gasUsed;
|
||||||
|
})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return gasSpending;
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.getCharts = function()
|
||||||
|
{
|
||||||
|
var chartHistory = _(this._items)
|
||||||
|
.sortByOrder('height', false)
|
||||||
|
.slice(0, MAX_BINS)
|
||||||
|
.reverse()
|
||||||
|
.map(function(item)
|
||||||
|
{
|
||||||
|
var chart = {
|
||||||
|
height: item.height,
|
||||||
|
blocktime: item.block.time/1000,
|
||||||
|
difficulty: item.block.difficulty,
|
||||||
|
uncles: item.block.uncles.length,
|
||||||
|
transactions: item.block.transactions.length,
|
||||||
|
gasSpending: item.block.gasUsed
|
||||||
|
}
|
||||||
|
return chart;
|
||||||
|
})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
var chart = {
|
||||||
|
height: _.pluck(chartHistory, 'height'),
|
||||||
|
blocktime: _.pluck(chartHistory, 'blocktime'),
|
||||||
|
difficulty: _.pluck(chartHistory, 'difficulty'),
|
||||||
|
uncles: _.pluck(chartHistory, 'uncles'),
|
||||||
|
transactions: _.pluck(chartHistory, 'transactions'),
|
||||||
|
gasSpending: _.pluck(chartHistory, 'gasSpending'),
|
||||||
|
propagation: this.getBlockPropagation(),
|
||||||
|
uncleCount: this.getUncleCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
History.prototype.history = function()
|
History.prototype.history = function()
|
||||||
{
|
{
|
||||||
return _.chain(this._items).sortBy('number').reverse().value();
|
return this._items;
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.requiresUpdate = function()
|
||||||
|
{
|
||||||
|
return ! (this._items.length === MAX_HISTORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.getHistoryRequestInterval = function()
|
||||||
|
{
|
||||||
|
if(this._items.length === 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var max = _.min(this._items, 'height').height - 1;
|
||||||
|
var min = max - Math.min(100, (MAX_HISTORY - this._items.length + 1)) + 1;
|
||||||
|
|
||||||
|
return {max: max, min: min};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = History;
|
module.exports = History;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
var geoip = require('geoip-lite');
|
var geoip = require('geoip-lite');
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
var MAX_HISTORY = 36;
|
var MAX_HISTORY = 40;
|
||||||
|
|
||||||
var Node = function Node(data)
|
var Node = function Node(data)
|
||||||
{
|
{
|
||||||
@ -49,9 +49,13 @@ var Node = function Node(data)
|
|||||||
if(typeof data.id !== 'undefined')
|
if(typeof data.id !== 'undefined')
|
||||||
this.id = data.id;
|
this.id = data.id;
|
||||||
|
|
||||||
if(typeof data.info !== 'undefined')
|
if(typeof data.info !== 'undefined') {
|
||||||
this.info = data.info;
|
this.info = data.info;
|
||||||
|
|
||||||
|
if(typeof data.info.canUpdateHistory === 'undefined')
|
||||||
|
data.info.canUpdateHistory = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(typeof data.ip !== 'undefined'){
|
if(typeof data.ip !== 'undefined'){
|
||||||
if(data.ip === '::ffff:127.0.0.1')
|
if(data.ip === '::ffff:127.0.0.1')
|
||||||
data.ip = '84.117.82.122';
|
data.ip = '84.117.82.122';
|
||||||
@ -75,9 +79,13 @@ Node.prototype.setGeo = function(ip)
|
|||||||
|
|
||||||
Node.prototype.setInfo = function(data)
|
Node.prototype.setInfo = function(data)
|
||||||
{
|
{
|
||||||
if(typeof data.info !== 'undefined')
|
if(typeof data.info !== 'undefined') {
|
||||||
this.info = data.info;
|
this.info = data.info;
|
||||||
|
|
||||||
|
if(typeof data.info.canUpdateHistory === 'undefined')
|
||||||
|
data.info.canUpdateHistory = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(typeof data.ip !== 'undefined'){
|
if(typeof data.ip !== 'undefined'){
|
||||||
this.info.ip = data.ip;
|
this.info.ip = data.ip;
|
||||||
this.setGeo(data.ip);
|
this.setGeo(data.ip);
|
||||||
@ -131,4 +139,9 @@ Node.prototype.setState = function(active)
|
|||||||
this.uptime.history.push({status: (active ? 'up' : 'down'), time: (new Date()).getTime()});
|
this.uptime.history.push({status: (active ? 'up' : 'down'), time: (new Date()).getTime()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node.prototype.canUpdate = function()
|
||||||
|
{
|
||||||
|
return this.info.canUpdateHistory || false;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = Node;
|
module.exports = Node;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "eth-netstats",
|
"name": "eth-netstats",
|
||||||
"description": "Ethereum Network Intelligence dashboard",
|
"description": "Ethereum Network Intelligence dashboard",
|
||||||
"version": "0.0.4",
|
"version": "0.0.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "0.12.0",
|
"node": "0.12.0",
|
||||||
|
@ -181,7 +181,7 @@ span.small-title span.small {
|
|||||||
.blocks-holder .block {
|
.blocks-holder .block {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
margin: 2px 1px 6px 1px;
|
margin: 2px 1px 6px 0px;
|
||||||
float: left;
|
float: left;
|
||||||
-webkit-border-radius: 1px;
|
-webkit-border-radius: 1px;
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
|
@ -32,7 +32,7 @@ function StatsCtrl($scope, $filter, socket, _, toastr) {
|
|||||||
|
|
||||||
$scope.latency = 0;
|
$scope.latency = 0;
|
||||||
|
|
||||||
$scope.currentApiVersion = "0.0.5";
|
$scope.currentApiVersion = "0.0.6";
|
||||||
|
|
||||||
$scope.predicate = ['-stats.active', '-stats.block.number', 'stats.block.propagation'];
|
$scope.predicate = ['-stats.active', '-stats.block.number', 'stats.block.propagation'];
|
||||||
$scope.reverse = false;
|
$scope.reverse = false;
|
||||||
@ -148,6 +148,23 @@ function StatsCtrl($scope, $filter, socket, _, toastr) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "charts":
|
||||||
|
$scope.lastBlocksTime = data.blocktime;
|
||||||
|
$scope.difficultyChart = data.difficulty;
|
||||||
|
$scope.transactionDensity = data.transactions;
|
||||||
|
$scope.gasSpending = data.gasSpending;
|
||||||
|
$scope.blockPropagationChart = data.propagation;
|
||||||
|
$scope.uncleCountChart = data.uncleCount;
|
||||||
|
$scope.uncleCount = data.uncleCount[0] + data.uncleCount[1];
|
||||||
|
|
||||||
|
jQuery('.spark-blocktimes').sparkline($scope.lastBlocksTime, {type: 'bar', tooltipSuffix: ' s'});
|
||||||
|
jQuery('.spark-difficulty').sparkline($scope.difficultyChart, {type: 'bar'});
|
||||||
|
jQuery('.spark-transactions').sparkline($scope.transactionDensity, {type: 'bar'});
|
||||||
|
jQuery('.spark-gasspending').sparkline($scope.gasSpending, {type: 'bar'});
|
||||||
|
jQuery('.spark-uncles').sparkline($scope.uncleCountChart.reverse(), {type: 'bar', barSpacing: 1});
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case "inactive":
|
case "inactive":
|
||||||
if(typeof data.stats !== 'undefined')
|
if(typeof data.stats !== 'undefined')
|
||||||
$scope.nodes[findIndex({id: data.id})].stats = data.stats;
|
$scope.nodes[findIndex({id: data.id})].stats = data.stats;
|
||||||
@ -237,11 +254,7 @@ function StatsCtrl($scope, $filter, socket, _, toastr) {
|
|||||||
}).stats;
|
}).stats;
|
||||||
|
|
||||||
$scope.lastBlock = $scope.bestStats.block.received;
|
$scope.lastBlock = $scope.bestStats.block.received;
|
||||||
$scope.lastBlocksTime = $scope.bestStats.blockTimes;
|
|
||||||
$scope.lastDifficulty = $scope.bestStats.block.difficulty;
|
$scope.lastDifficulty = $scope.bestStats.block.difficulty;
|
||||||
$scope.difficultyChart = $scope.bestStats.difficulty;
|
|
||||||
$scope.transactionDensity = $scope.bestStats.txDensity;
|
|
||||||
$scope.gasSpending = $scope.bestStats.gasSpending;
|
|
||||||
|
|
||||||
if(typeof $scope.bestStats.miners !== 'undefined') {
|
if(typeof $scope.bestStats.miners !== 'undefined') {
|
||||||
$scope.miners = $scope.bestStats.miners;
|
$scope.miners = $scope.bestStats.miners;
|
||||||
@ -258,11 +271,6 @@ function StatsCtrl($scope, $filter, socket, _, toastr) {
|
|||||||
$scope.miners[key].name = name;
|
$scope.miners[key].name = name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
jQuery('.spark-blocktimes').sparkline($scope.lastBlocksTime.reverse(), {type: 'bar', tooltipSuffix: ' s'});
|
|
||||||
jQuery('.spark-difficulty').sparkline($scope.difficultyChart.reverse(), {type: 'bar'});
|
|
||||||
jQuery('.spark-transactions').sparkline($scope.transactionDensity.reverse(), {type: 'bar'});
|
|
||||||
jQuery('.spark-gasspending').sparkline($scope.gasSpending.reverse(), {type: 'bar'});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.avgBlockTime = _.max($scope.nodes, function(node) {
|
$scope.avgBlockTime = _.max($scope.nodes, function(node) {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
$.fn.sparkline.defaults.bar.height = 63;
|
$.fn.sparkline.defaults.bar.height = 63;
|
||||||
$.fn.sparkline.defaults.bar.barWidth = 6;
|
$.fn.sparkline.defaults.bar.barWidth = 6;
|
||||||
$.fn.sparkline.defaults.bar.barSpacing = 2;
|
$.fn.sparkline.defaults.bar.barSpacing = 1;
|
||||||
$.fn.sparkline.defaults.bar.tooltipClassname = 'jqstooltip';
|
$.fn.sparkline.defaults.bar.tooltipClassname = 'jqstooltip';
|
||||||
$.fn.sparkline.defaults.bar.tooltipOffsetX = 0;
|
$.fn.sparkline.defaults.bar.tooltipOffsetX = 0;
|
||||||
$.fn.sparkline.defaults.bar.tooltipFormat = $.spformat('<div class="tooltip-arrow"></div><div class="tooltip-inner">{{prefix}}{{value}}{{suffix}}</div>');
|
$.fn.sparkline.defaults.bar.tooltipFormat = $.spformat('<div class="tooltip-arrow"></div><div class="tooltip-inner">{{prefix}}{{value}}{{suffix}}</div>');
|
||||||
|
@ -178,7 +178,7 @@ block content
|
|||||||
td.nodeInfo(rel="{{node.id}}")
|
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(data-toggle="tooltip", data-placement="top", data-html="true", data-original-title="{{node | geoTooltip}}") {{node.info.name}}
|
||||||
span.small #[ ]({{node.info.ip}})
|
span.small #[ ]({{node.info.ip}})
|
||||||
span.small(data-toggle="tooltip", data-placement="top", data-html="true", data-original-title="Netstats client needs update", class="{{ node.info | nodeClientClass : currentApiVersion }}")
|
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
|
i.icon-warning-o
|
||||||
td
|
td
|
||||||
div.small(ng-bind-html="node.info.node | nodeVersion")
|
div.small(ng-bind-html="node.info.node | nodeVersion")
|
||||||
|
Loading…
Reference in New Issue
Block a user