Skip to content

Commit 4a6f625

Browse files
committed
Releasing 0.4.0
1 parent 0ca6b93 commit 4a6f625

14 files changed

Lines changed: 339 additions & 277 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
66

77
## [Unreleased]
88

9+
## [0.4.0] - 2018-02-03
10+
911
### Added
1012
- API basic HTTP auth
1113

dist/api/index.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
'use strict';
2+
3+
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
4+
5+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6+
7+
var express = require('express');
8+
var helmet = require('helmet');
9+
var bodyParser = require('body-parser');
10+
var basicAuth = require('express-basic-auth');
11+
12+
var _require = require('./node'),
13+
getNodeStats = _require.getNodeStats,
14+
getSummary = _require.getSummary;
15+
16+
var _require2 = require('./peer'),
17+
getPeerStats = _require2.getPeerStats;
18+
19+
var _require3 = require('./webhooks'),
20+
startWebhooks = _require3.startWebhooks;
21+
22+
var DEFAULT_OPTIONS = {
23+
node: null,
24+
webhooks: [],
25+
webhookInterval: 30,
26+
username: null,
27+
password: null,
28+
apiPort: 18600,
29+
apiHostname: '127.0.0.1'
30+
};
31+
32+
/**
33+
* Creates an Express APP instance, also starts regular webhooks callbacks.
34+
* @param options
35+
* @returns {*|Function}
36+
*/
37+
function createAPI(options) {
38+
var opts = _extends({}, DEFAULT_OPTIONS, options);
39+
40+
// Start webhook callbacks
41+
if (opts.webhooks && opts.webhooks.length) {
42+
startWebhooks(opts.node, opts.webhooks, opts.webhookInterval);
43+
}
44+
45+
// Start API server
46+
var app = express();
47+
app.set('node', opts.node);
48+
49+
// Basic app protection
50+
app.use(helmet());
51+
52+
// Enable basic HTTP Auth
53+
if (opts.username && opts.password) {
54+
app.use(basicAuth({
55+
users: _defineProperty({}, opts.username, opts.password)
56+
}));
57+
}
58+
59+
// parse application/x-www-form-urlencoded
60+
app.use(bodyParser.urlencoded({ extended: false }));
61+
62+
// parse application/json
63+
app.use(bodyParser.json());
64+
65+
//////////////////////// ENDPOINTS ////////////////////////
66+
67+
app.get('/', function (req, res) {
68+
res.json(getNodeStats(opts.node));
69+
});
70+
71+
app.get('/peer-stats', function (req, res) {
72+
res.json(getSummary(opts.node));
73+
});
74+
75+
app.get('/peers', function (req, res) {
76+
res.json(opts.node.list.all().map(getPeerStats));
77+
});
78+
79+
return app.listen(opts.apiPort, opts.apiHostname);
80+
}
81+
82+
module.exports = {
83+
createAPI: createAPI,
84+
DEFAULT_OPTIONS: DEFAULT_OPTIONS
85+
};

dist/api/node.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
'use strict';
2+
3+
var _require = require('./peer'),
4+
getPeerStats = _require.getPeerStats;
5+
6+
var version = require('../../package.json').version;
7+
8+
/**
9+
* Returns summary of the node stats
10+
* @param {Node} node
11+
* @returns {{newNodes: {hourAgo, fourAgo, twelveAgo, dayAgo, weekAgo}, activeNodes: {hourAgo, fourAgo, twelveAgo, dayAgo, weekAgo}}}
12+
*/
13+
function getSummary(node) {
14+
var now = new Date();
15+
var hour = 3600000;
16+
var hourAgo = new Date(now - hour);
17+
var fourAgo = new Date(now - hour * 4);
18+
var twelveAgo = new Date(now - hour * 12);
19+
var dayAgo = new Date(now - hour * 24);
20+
var weekAgo = new Date(now - hour * 24 * 7);
21+
return {
22+
newNodes: {
23+
hourAgo: node.list.all().filter(function (p) {
24+
return p.data.dateCreated >= hourAgo;
25+
}).length,
26+
fourAgo: node.list.all().filter(function (p) {
27+
return p.data.dateCreated >= fourAgo;
28+
}).length,
29+
twelveAgo: node.list.all().filter(function (p) {
30+
return p.data.dateCreated >= twelveAgo;
31+
}).length,
32+
dayAgo: node.list.all().filter(function (p) {
33+
return p.data.dateCreated >= dayAgo;
34+
}).length,
35+
weekAgo: node.list.all().filter(function (p) {
36+
return p.data.dateCreated >= weekAgo;
37+
}).length
38+
},
39+
activeNodes: {
40+
hourAgo: node.list.all().filter(function (p) {
41+
return p.data.dateLastConnected >= hourAgo;
42+
}).length,
43+
fourAgo: node.list.all().filter(function (p) {
44+
return p.data.dateLastConnected >= fourAgo;
45+
}).length,
46+
twelveAgo: node.list.all().filter(function (p) {
47+
return p.data.dateLastConnected >= twelveAgo;
48+
}).length,
49+
dayAgo: node.list.all().filter(function (p) {
50+
return p.data.dateLastConnected >= dayAgo;
51+
}).length,
52+
weekAgo: node.list.all().filter(function (p) {
53+
return p.data.dateLastConnected >= weekAgo;
54+
}).length
55+
}
56+
};
57+
}
58+
59+
/**
60+
* Returns clean node stats to be used in the API
61+
* @param {Node} node
62+
* @returns {{name, version, ready: (boolean|*|null), isIRIHealthy: (*|boolean), iriStats: *, peerStats: {newNodes: {hourAgo, fourAgo, twelveAgo, dayAgo, weekAgo}, activeNodes: {hourAgo, fourAgo, twelveAgo, dayAgo, weekAgo}}, totalPeers, connectedPeers: Array, config: {cycleInterval: (Command.opts.cycleInterval|*), epochInterval: (Command.opts.epochInterval|*), beatInterval: (Command.opts.beatInterval|*), dataPath: (Command.opts.dataPath|*), port: (Command.opts.port|*), apiPort: (Command.opts.apiPort|*), IRIPort: (Command.opts.IRIPort|*), TCPPort: (Command.opts.TCPPort|*), UDPPort: (Command.opts.UDPPort|*), IRIProtocol: (Command.opts.IRIProtocol|*), isMaster: (Command.opts.isMaster|*), temporary: (Command.opts.temporary|*)}, heart: {lastCycle: (heart.lastCycle|Heart.lastCycle|_require2.Heart.lastCycle), lastEpoch: (heart.lastEpoch|Heart.lastEpoch|_require2.Heart.lastEpoch), personality: (heart.personality|Heart.personality|_require2.Heart.personality), currentCycle: (heart.currentCycle|Heart.currentCycle|_require2.Heart.currentCycle), currentEpoch: (heart.currentEpoch|Heart.currentEpoch|_require2.Heart.currentEpoch), startDate: (heart.startDate|Heart.startDate|_require2.Heart.startDate)}}}
63+
*/
64+
function getNodeStats(node) {
65+
var _node$opts = node.opts,
66+
cycleInterval = _node$opts.cycleInterval,
67+
epochInterval = _node$opts.epochInterval,
68+
beatInterval = _node$opts.beatInterval,
69+
dataPath = _node$opts.dataPath,
70+
port = _node$opts.port,
71+
apiPort = _node$opts.apiPort,
72+
IRIPort = _node$opts.IRIPort,
73+
TCPPort = _node$opts.TCPPort,
74+
UDPPort = _node$opts.UDPPort,
75+
isMaster = _node$opts.isMaster,
76+
IRIProtocol = _node$opts.IRIProtocol,
77+
temporary = _node$opts.temporary;
78+
var _node$heart = node.heart,
79+
lastCycle = _node$heart.lastCycle,
80+
lastEpoch = _node$heart.lastEpoch,
81+
personality = _node$heart.personality,
82+
currentCycle = _node$heart.currentCycle,
83+
currentEpoch = _node$heart.currentEpoch,
84+
startDate = _node$heart.startDate;
85+
86+
var totalPeers = node.list.all().length;
87+
var isIRIHealthy = node.iri && node.iri.isHealthy;
88+
var iriStats = node.iri && node.iri.iriStats;
89+
var connectedPeers = Array.from(node.sockets.keys()).filter(function (p) {
90+
return node.sockets.get(p).readyState === 1;
91+
}).map(getPeerStats);
92+
93+
return {
94+
name: node.opts.name,
95+
version: version,
96+
ready: node._ready,
97+
isIRIHealthy: isIRIHealthy,
98+
iriStats: iriStats,
99+
peerStats: getSummary(node),
100+
totalPeers: totalPeers,
101+
connectedPeers: connectedPeers,
102+
config: {
103+
cycleInterval: cycleInterval,
104+
epochInterval: epochInterval,
105+
beatInterval: beatInterval,
106+
dataPath: dataPath,
107+
port: port,
108+
apiPort: apiPort,
109+
IRIPort: IRIPort,
110+
TCPPort: TCPPort,
111+
UDPPort: UDPPort,
112+
IRIProtocol: IRIProtocol,
113+
isMaster: isMaster,
114+
temporary: temporary
115+
},
116+
heart: {
117+
lastCycle: lastCycle,
118+
lastEpoch: lastEpoch,
119+
personality: personality,
120+
currentCycle: currentCycle,
121+
currentEpoch: currentEpoch,
122+
startDate: startDate
123+
}
124+
};
125+
}
126+
127+
module.exports = {
128+
getSummary: getSummary,
129+
getNodeStats: getNodeStats
130+
};

dist/api/peer.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"use strict";
2+
3+
/**
4+
* Returns a clean Peer object that can be used in the API
5+
* @param {Peer} peer
6+
* @returns {{name, hostname, ip, port, TCPPort, UDPPort, protocol, IRIProtocol, seen, connected, tried, weight, dateTried, dateLastConnected, dateCreated, isTrusted, lastConnections}}
7+
*/
8+
function getPeerStats(peer) {
9+
var _peer$data = peer.data,
10+
name = _peer$data.name,
11+
hostname = _peer$data.hostname,
12+
ip = _peer$data.ip,
13+
port = _peer$data.port,
14+
TCPPort = _peer$data.TCPPort,
15+
UDPPort = _peer$data.UDPPort,
16+
protocol = _peer$data.protocol,
17+
seen = _peer$data.seen,
18+
connected = _peer$data.connected,
19+
tried = _peer$data.tried,
20+
weight = _peer$data.weight,
21+
dateTried = _peer$data.dateTried,
22+
dateLastConnected = _peer$data.dateLastConnected,
23+
dateCreated = _peer$data.dateCreated,
24+
IRIProtocol = _peer$data.IRIProtocol,
25+
isTrusted = _peer$data.isTrusted,
26+
lastConnections = _peer$data.lastConnections;
27+
28+
return {
29+
name: name,
30+
hostname: hostname,
31+
ip: ip,
32+
port: port,
33+
TCPPort: TCPPort,
34+
UDPPort: UDPPort,
35+
protocol: protocol,
36+
IRIProtocol: IRIProtocol,
37+
seen: seen,
38+
connected: connected,
39+
tried: tried,
40+
weight: weight,
41+
dateTried: dateTried,
42+
dateLastConnected: dateLastConnected,
43+
dateCreated: dateCreated,
44+
isTrusted: isTrusted,
45+
lastConnections: lastConnections
46+
};
47+
}
48+
49+
module.exports = {
50+
getPeerStats: getPeerStats
51+
};

dist/api/utils.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"use strict";

dist/api/webhooks.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
var request = require('request');
4+
5+
var _require = require('./node'),
6+
getNodeStats = _require.getNodeStats;
7+
8+
function startWebhooks(node, webhooks, webhookInterval) {
9+
var interval = setInterval(function () {
10+
webhooks.forEach(function (uri) {
11+
return request({
12+
uri: uri,
13+
method: 'POST',
14+
json: getNodeStats(node)
15+
}, function (err) {
16+
if (err) {
17+
node.log(('Webhook returned error: ' + uri).yellow);
18+
}
19+
});
20+
});
21+
}, webhookInterval * 1000);
22+
return {
23+
stop: function stop() {
24+
clearInterval(interval);
25+
}
26+
};
27+
}
28+
29+
module.exports = {
30+
startWebhooks: startWebhooks
31+
};

dist/index.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require('colors');
88
var request = require('request');
99
var terminal = require('./node/tools/terminal');
1010
var node = require('./node').node;
11-
var api = require('./node').api;
11+
var api = require('./api/index');
1212
var utils = require('./node').utils;
1313

1414
// Some general TODOs:
@@ -30,7 +30,15 @@ module.exports = _extends({
3030
opts.gui && terminal.init(opts.name, utils.getVersion(), terminate);
3131

3232
_node.start().then(function (n) {
33-
api.createAPI(n, opts.webhooks, opts.webhookInterval);
33+
api.createAPI({
34+
node: n,
35+
webhooks: opts.webhooks,
36+
webhookInterval: opts.webhookInterval,
37+
apiPort: opts.apiPort,
38+
apiHostname: opts.apiHostname,
39+
username: opts.apiAuth && opts.apiAuth.username,
40+
password: opts.apiAuth && opts.apiAuth.password
41+
});
3442
terminal.ports(n.opts);
3543
n.log(('Nelson v.' + utils.getVersion() + ' initialized').green.bold);
3644
});

dist/nelson.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ var _require4 = require('./node/peer'),
2323
var _require5 = require('./node/peer-list'),
2424
DEFAULT_LIST_OPTIONS = _require5.DEFAULT_OPTIONS;
2525

26+
var _require6 = require('./api/index'),
27+
DEFAULT_API_OPTIONS = _require6.DEFAULT_OPTIONS;
28+
2629
var version = require('../package.json').version;
2730

2831
var parseNeighbors = function parseNeighbors(val) {
@@ -42,8 +45,21 @@ var parseProtocol = function parseProtocol(val) {
4245
var parseNumber = function parseNumber(v) {
4346
return parseInt(v);
4447
};
48+
var parseAuth = function parseAuth(v) {
49+
var tokens = v.split(':');
50+
if (!tokens.length === 2) {
51+
throw new Error('Wrong apiAuth format! Use: "username.password"');
52+
}
53+
if (!tokens[0].length) {
54+
throw new Error('apiAuth username not provided!');
55+
}
56+
if (!tokens[1].length) {
57+
throw new Error('apiAuth password not provided!');
58+
}
59+
return { username: tokens[0], password: tokens[1] };
60+
};
4561

46-
program.version(version).option('--name [value]', 'Name of your node instance', DEFAULT_OPTIONS.name).option('-n, --neighbors [value]', 'Trusted neighbors', parseNeighbors, []).option('--getNeighbors [url]', 'Download default set of neighbors', false).option('-c, --cycleInterval [value]', 'Cycle interval in seconds', parseNumber, DEFAULT_OPTIONS.cycleInterval).option('-e, --epochInterval [value]', 'Epoch interval in seconds', parseNumber, DEFAULT_OPTIONS.epochInterval).option('--incomingMax [value]', 'Maximal incoming connection slots', parseNumber, DEFAULT_OPTIONS.incomingMax).option('--outgoingMax [value]', 'Maximal outgoing connection slots', parseNumber, DEFAULT_OPTIONS.outgoingMax).option('--lazyLimit [value]', 'Seconds after which neighbor is dropped for not having provided any new TXs', parseNumber, DEFAULT_OPTIONS.lazyLimit).option('--lazyTimesLimit [value]', 'How many consecutive times a lazy neighbor can connect before getting penalized', parseNumber, DEFAULT_OPTIONS.lazyTimesLimit).option('-a, --apiPort [value]', 'Nelson API port', parseNumber, DEFAULT_OPTIONS.apiPort).option('-o, --apiHostname [value]', 'Nelson API hostname', DEFAULT_OPTIONS.apiHostname).option('-w, --webhooks [value]', 'Nelson API webhook URLs', parseURLs, []).option('--webhookInterval [value]', 'Webhooks callback interval in seconds', parseNumber, 30).option('-p, --port [value]', 'Nelson port', parseNumber, DEFAULT_OPTIONS.port).option('-r, --IRIHostname [value]', 'IRI API hostname', DEFAULT_OPTIONS.IRIHostname).option('-i, --IRIPort [value]', 'IRI API port', parseNumber, DEFAULT_OPTIONS.IRIPort).option('-t, --TCPPort [value]', 'IRI TCP port', parseNumber, DEFAULT_OPTIONS.TCPPort).option('-u, --UDPPort [value]', 'IRI UDP port', parseNumber, DEFAULT_OPTIONS.UDPPort).option('--IRIProtocol [value]', 'IRI protocol to use: udp, tcp, prefertcp, preferudp or any', parseProtocol, DEFAULT_OPTIONS.IRIProtocol).option('-d, --dataPath [value]', 'Peer database path', DEFAULT_LIST_OPTIONS.dataPath).option('-m, --isMaster [value]', 'Is a master node', false).option('-s, --silent [value]', 'Silent', false).option('-g, --gui [value]', 'GUI', false).option('--temporary [value]', 'Create a temporary node', false).option('--config [value]', 'Config file path', null).parse(process.argv);
62+
program.version(version).option('--name [value]', 'Name of your node instance', DEFAULT_OPTIONS.name).option('-n, --neighbors [value]', 'Trusted neighbors', parseNeighbors, []).option('--getNeighbors [url]', 'Download default set of neighbors', false).option('-c, --cycleInterval [value]', 'Cycle interval in seconds', parseNumber, DEFAULT_OPTIONS.cycleInterval).option('-e, --epochInterval [value]', 'Epoch interval in seconds', parseNumber, DEFAULT_OPTIONS.epochInterval).option('--incomingMax [value]', 'Maximal incoming connection slots', parseNumber, DEFAULT_OPTIONS.incomingMax).option('--outgoingMax [value]', 'Maximal outgoing connection slots', parseNumber, DEFAULT_OPTIONS.outgoingMax).option('--lazyLimit [value]', 'Seconds after which neighbor is dropped for not having provided any new TXs', parseNumber, DEFAULT_OPTIONS.lazyLimit).option('--lazyTimesLimit [value]', 'How many consecutive times a lazy neighbor can connect before getting penalized', parseNumber, DEFAULT_OPTIONS.lazyTimesLimit).option('--apiAuth [value]', 'Nelson API username:password', parseAuth, null).option('-a, --apiPort [value]', 'Nelson API port', parseNumber, DEFAULT_API_OPTIONS.apiPort).option('-o, --apiHostname [value]', 'Nelson API hostname', DEFAULT_API_OPTIONS.apiHostname).option('-w, --webhooks [value]', 'Nelson API webhook URLs', parseURLs, DEFAULT_API_OPTIONS.webhooks).option('--webhookInterval [value]', 'Webhooks callback interval in seconds', parseNumber, DEFAULT_API_OPTIONS.webhookInterval).option('-p, --port [value]', 'Nelson port', parseNumber, DEFAULT_OPTIONS.port).option('-r, --IRIHostname [value]', 'IRI API hostname', DEFAULT_OPTIONS.IRIHostname).option('-i, --IRIPort [value]', 'IRI API port', parseNumber, DEFAULT_OPTIONS.IRIPort).option('-t, --TCPPort [value]', 'IRI TCP port', parseNumber, DEFAULT_OPTIONS.TCPPort).option('-u, --UDPPort [value]', 'IRI UDP port', parseNumber, DEFAULT_OPTIONS.UDPPort).option('--IRIProtocol [value]', 'IRI protocol to use: udp, tcp, prefertcp, preferudp or any', parseProtocol, DEFAULT_OPTIONS.IRIProtocol).option('-d, --dataPath [value]', 'Peer database path', DEFAULT_LIST_OPTIONS.dataPath).option('-m, --isMaster [value]', 'Is a master node', false).option('-s, --silent [value]', 'Silent', false).option('-g, --gui [value]', 'GUI', false).option('--temporary [value]', 'Create a temporary node', false).option('--config [value]', 'Config file path', null).parse(process.argv);
4763

4864
var configPath = process.env.NELSON_CONFIG || program.config;
4965

0 commit comments

Comments
 (0)