diff --git a/app/config.js b/app/config.js
index 548b9bb..597dc63 100644
--- a/app/config.js
+++ b/app/config.js
@@ -56,7 +56,5 @@ module.exports = {
{name: "Litecoin Explorer", url:"https://ltc.chaintools.io", imgUrl:"/img/logo/ltc.svg"},
{name: "Lightning Explorer", url:"https://lightning.chaintools.io", imgUrl:"/img/logo/lightning.svg"},
]
- },
-
- ipStackComApiAccessKey:""
+ }
};
diff --git a/app/credentials.js b/app/credentials.js
index edf9ef5..15b553a 100644
--- a/app/credentials.js
+++ b/app/credentials.js
@@ -8,5 +8,10 @@ module.exports = {
port:8332,
username:"username",
password:"password"
- }
+ },
+
+ // optional: enter your api access key from ipstack.com below
+ // to include a map of the estimated locations of your node's
+ // peers
+ ipStackComApiAccessKey:""
};
diff --git a/app/utils.js b/app/utils.js
index 349f2d1..c0ba002 100644
--- a/app/utils.js
+++ b/app/utils.js
@@ -5,6 +5,8 @@ var config = require("./config.js");
var coins = require("./coins.js");
var coinConfig = coins[config.coin];
+var ipCache = {};
+
var exponentScales = [
{val:1000000000000000000000000000000000, name:"?", abbreviation:"V", exponent:"33"},
{val:1000000000000000000000000000000, name:"?", abbreviation:"W", exponent:"30"},
@@ -270,28 +272,45 @@ function geoLocateIpAddresses(ipAddresses) {
ipStr = ipStr + chunks[i][j];
}
- var apiUrl = "http://api.ipstack.com/" + ipStr + "?access_key=" + config.ipStackComApiAccessKey;
- promises.push(new Promise(function(resolve2, reject2) {
- request(apiUrl, function(error, response, body) {
- if (error) {
- reject2(error);
-
- } else {
- resolve2(response);
- }
- });
- }));
+ if (ipCache[ipStr] != null) {
+ promises.push(new Promise(function(resolve2, reject2) {
+ resolve2(ipCache[ipStr]);
+ }));
+
+ } else if (config.credentials.ipStackComApiAccessKey && config.credentials.ipStackComApiAccessKey.trim().length > 0) {
+ var apiUrl = "http://api.ipstack.com/" + ipStr + "?access_key=" + config.credentials.ipStackComApiAccessKey;
+ promises.push(new Promise(function(resolve2, reject2) {
+ request(apiUrl, function(error, response, body) {
+ if (error) {
+ reject2(error);
+
+ } else {
+ resolve2(response);
+ }
+ });
+ }));
+ } else {
+ promises.push(new Promise(function(resolve2, reject2) {
+ resolve2(null);
+ }));
+ }
}
Promise.all(promises).then(function(results) {
- var ipDetails = {};
+ var ipDetails = {ips:[], detailsByIp:{}};
for (var i = 0; i < results.length; i++) {
var res = results[i];
- if (res["statusCode"] == 200) {
+ if (res != null && res["statusCode"] == 200) {
var resBody = JSON.parse(res["body"]);
+ var ip = resBody["ip"];
+
+ ipDetails.ips.push(ip);
+ ipDetails.detailsByIp[ip] = resBody;
- ipDetails[resBody["ip"]] = resBody;
+ if (ipCache[ip] == null) {
+ ipCache[ip] = res;
+ }
}
}
diff --git a/routes/baseActionsRouter.js b/routes/baseActionsRouter.js
index 1044ecb..d090b18 100644
--- a/routes/baseActionsRouter.js
+++ b/routes/baseActionsRouter.js
@@ -135,8 +135,26 @@ router.get("/peers", function(req, res) {
coreApi.getPeerSummary().then(function(peerSummary) {
res.locals.peerSummary = peerSummary;
- res.render("peers");
+ var peerIps = [];
+ for (var i = 0; i < peerSummary.getpeerinfo.length; i++) {
+ var ipWithPort = peerSummary.getpeerinfo[i].addr;
+ if (ipWithPort.lastIndexOf(":") >= 0) {
+ var ip = ipWithPort.substring(0, ipWithPort.lastIndexOf(":"));
+ if (ip.trim().length > 0) {
+ peerIps.push(ip.trim());
+ }
+ }
+ }
+ if (peerIps.length > 0) {
+ utils.geoLocateIpAddresses(peerIps).then(function(results) {
+ res.locals.peerIpSummary = results;
+
+ res.render("peers");
+ });
+ } else {
+ res.render("peers");
+ }
}).catch(function(err) {
res.locals.userMessage = "Error: " + err;
diff --git a/views/peers.pug b/views/peers.pug
index de2b5d2..6f56384 100644
--- a/views/peers.pug
+++ b/views/peers.pug
@@ -3,6 +3,17 @@ extends layout
block headContent
title Peers
+ link(rel="stylesheet", href="https://unpkg.com/leaflet@1.3.3/dist/leaflet.css", integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==", crossorigin="")
+
+ script(src="https://unpkg.com/leaflet@1.3.3/dist/leaflet.js", integrity="sha512-tAGcCfR4Sc5ZP5ZoVz0quoZDYX5aCtEm/eu1KhSLj2c9eFrylXZknQYmxUssFaVJKvvc0dJQixhGjG2yXWiV9Q==", crossorigin="")
+
+ style.
+ .versions-hidden-rows, .services-hidden-rows {
+ display: none;
+ }
+
+ #map { height: 700px; }
+
block content
h1(class="h3") Peers
hr
@@ -17,44 +28,54 @@ block content
div(class="tab-content")
div(id="tab-summary", class="tab-pane active", role="tabpanel")
- h2(class="h5") Versions
- hr
- table(class="table table-striped table-responsive-sm mt-4")
- thead
- tr
- th
- th(class="data-header") Version
- th(class="data-header") Count
- tbody
- each item, index in peerSummary.versionSummary
- tr
- th(class="data-cell") #{index + 1}
-
- td(class="data-cell") #{item[0]}
- td(class="data-cell") #{item[1].toLocaleString()}
-
-
- h2(class="h5") Services
- hr
- table(class="table table-striped table-responsive-sm mt-4")
- thead
- tr
- th
- th(class="data-header") Services
- th(class="data-header") Count
- tbody
- each item, index in peerSummary.servicesSummary
- tr
- th(class="data-cell") #{index + 1}
-
- td(class="data-cell") #{item[0]}
- td(class="data-cell") #{item[1].toLocaleString()}
+ h2(class="h5 mb-3") Connected to #{peerSummary.getpeerinfo.length}
+ if (peerSummary.getpeerinfo.length == 1)
+ span Peer
+ else
+ span Peers
+
+ if (config.credentials.ipStackComApiAccessKey && config.credentials.ipStackComApiAccessKey.trim().length > 0)
+ div(id="map", class="mb-4")
+
+ div(class="card mb-4")
+ div(class="card-header")
+ h2(class="h6 mb-0") Top Versions
+ div(class="card-body")
+ table(class="table table-striped table-responsive-sm")
+ thead
+ tr
+ th
+ th(class="data-header") Version
+ th(class="data-header") Count
+ tbody
+ each item, index in peerSummary.versionSummary
+ tr(class=(index >= 5 ? "versions-hidden-rows" : false))
+ th(class="data-cell") #{index + 1}
+
+ td(class="data-cell") #{item[0]}
+ td(class="data-cell") #{item[1].toLocaleString()}
+
+ div(class="card mb-4")
+ div(class="card-header")
+ h2(class="h6 mb-0") Top Service Flags
+ div(class="card-body")
+ table(class="table table-striped table-responsive-sm")
+ thead
+ tr
+ th
+ th(class="data-header") Services
+ th(class="data-header") Count
+ tbody
+ each item, index in peerSummary.servicesSummary
+ tr(class=(index >= 5 ? "services-hidden-rows" : false))
+ th(class="data-cell") #{index + 1}
+
+ td(class="data-cell") #{item[0]}
+ td(class="data-cell") #{item[1].toLocaleString()}
div(id="tab-details", class="tab-pane", role="tabpanel")
- h2(class="h5") Peers List
- hr
table(class="table table-striped table-responsive-sm mt-4")
thead
tr
@@ -62,6 +83,7 @@ block content
th(class="data-header") Version
th(class="data-header") Address
th(class="data-header") Services
+ th(class="data-header") Location
th(class="data-header") Last Send / Receive
tbody
@@ -75,12 +97,59 @@ block content
td(class="data-cell") #{item.subver}
td(class="data-cell") #{item.addr}
td(class="data-cell") #{item.services}
+ td(class="data-cell")
+ - var ipAddr = item.addr.substring(0, item.addr.lastIndexOf(":"));
+ if (peerIpSummary.ips.includes(ipAddr))
+ - var ipDetails = peerIpSummary.detailsByIp[ipAddr];
+ if (ipDetails.city)
+ span #{ipDetails.city},
+ if (ipDetails.country_name)
+ span #{ipDetails.country_name}
+ if (ipDetails.location && ipDetails.location.country_flag_emoji)
+ span #{ipDetails.location.country_flag_emoji}
+ else
+ span ?
+
+ - var ipAddr = null;
+
td(class="data-cell") #{lastSendAgo} / #{lastRecvAgo}
div(id="tab-raw", class="tab-pane", role="tabpanel")
+ h5 GetPeerInfo
pre
code #{JSON.stringify(peerSummary.getpeerinfo, null, 4)}
+ h5 IP Summary
+ pre
+ code #{JSON.stringify(peerIpSummary, null, 4)}
+
-
\ No newline at end of file
+block endOfBody
+ if (config.credentials.ipStackComApiAccessKey && config.credentials.ipStackComApiAccessKey.trim().length > 0)
+ script.
+ var mymap = L.map('map').setView([21.505, -0.09], 3);
+
+ L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
+ maxZoom: 18,
+ attribution: 'Map data © OpenStreetMap contributors, ' +
+ 'CC-BY-SA, ' +
+ 'Imagery © Mapbox',
+ id: 'mapbox.streets'
+ }).addTo(mymap);
+
+ /*L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
+ attribution: '© OpenStreetMap contributors'
+ }).addTo(mymap);*/
+
+ $(document).ready(function() {
+ window.dispatchEvent(new Event('resize'));
+ });
+
+ each ipAddress, index in peerIpSummary.ips
+ - var ipDetails = peerIpSummary.detailsByIp[ipAddress];
+ if (ipDetails && ipDetails.latitude && ipDetails.longitude)
+ script L.marker([#{ipDetails.latitude}, #{ipDetails.longitude}]).addTo(mymap);
+
+
+