diff --git a/config.json b/config.json index 0c0d3cb..f602d64 100644 --- a/config.json +++ b/config.json @@ -20,7 +20,8 @@ "enabled": true, "siteTitle": "Cryppit", "port": 80, - "statUpdateInterval": 5 + "statUpdateInterval": 5, + "hashrateWindow": 600 }, "proxy": { "enabled": false, diff --git a/init.js b/init.js index 09c66b2..3732e58 100644 --- a/init.js +++ b/init.js @@ -174,7 +174,7 @@ var startPaymentProcessor = function(poolConfigs){ worker.on('exit', function(code, signal){ logError('paymentProcessor', 'system', 'Payment processor died, spawning replacement...'); setTimeout(function(){ - startPaymentProcessor.apply(null, arguments); + startPaymentProcessor(poolConfigs); }, 2000); }); }; @@ -192,7 +192,7 @@ var startWebsite = function(portalConfig, poolConfigs){ worker.on('exit', function(code, signal){ logError('website', 'system', 'Website process died, spawning replacement...'); setTimeout(function(){ - startWebsite.apply(null, arguments); + startWebsite(portalConfig, poolConfigs); }, 2000); }); }; diff --git a/libs/api.js b/libs/api.js deleted file mode 100644 index 0a7e335..0000000 --- a/libs/api.js +++ /dev/null @@ -1,116 +0,0 @@ -var redis = require('redis'); -var async = require('async'); - -var os = require('os'); - - -module.exports = function(logger, poolConfigs){ - - var _this = this; - - var redisClients = []; - - Object.keys(poolConfigs).forEach(function(coin){ - var poolConfig = poolConfigs[coin]; - var internalConfig = poolConfig.shareProcessing.internal; - var redisConfig = internalConfig.redis; - - for (var i = 0; i < redisClients.length; i++){ - var client = redisClients[i]; - if (client.client.port === redisConfig.port && client.client.host === redisConfig.host){ - client.coins.push(coin); - return; - } - } - redisClients.push({ - coins: [coin], - client: redis.createClient(redisConfig.port, redisConfig.host) - }); - }); - - //Every 10 minutes clear out old hashrate stats for each coin from redis - var clearExpiredHashrates = function(){ - redisClients.forEach(function(client){ - var tenMinutesAgo = (Date.now() / 1000 | 0) - (60 * 10); - var redisCommands = client.coins.map(function(coin){ - return ['zremrangebyscore', coin + '_hashrate', '-inf', tenMinutesAgo]; - }); - client.client.multi(redisCommands).exec(function(err, replies){ - if (err) - console.log('error with clearing old hashrates ' + JSON.stringify(err)); - }); - }); - }; - setInterval(clearExpiredHashrates, 10 * 60 * 1000); - clearExpiredHashrates(); - - - this.stats = {}; - - - this.getStats = function(callback){ - - async.map(redisClients, function(client, callback){ - var tenMinutesAgo = (Date.now() / 1000 | 0) - (60 * 10); - var redisCommands = client.coins.map(function(coin){ - return ['zrangebyscore', coin + '_hashrate', tenMinutesAgo, '+inf']; - }); - client.client.multi(redisCommands).exec(function(err, replies){ - if (err){ - console.log('error with getting hashrate stats ' + JSON.stringify(err)); - callback(err); - } - else{ - var replyObj = {}; - for(var i = 0; i < replies.length; i++){ - replyObj[client.coins[i]] = replies[i]; - } - callback(null, replyObj); - } - }); - }, function(err, results){ - - var portalStats = { - global:{ - workers: 0, - shares: 0 - }, - pools:{ - - } - }; - - results.forEach(function(r){ - var coin = Object.keys(r)[0]; - var coinStats = {workers: {}, shares: 0}; - r[coin].forEach(function(ins){ - var parts = ins.split(':'); - var workerShares = parseInt(parts[0]);; - coinStats.shares += workerShares - var worker = parts[1]; - if (worker in coinStats.workers) - coinStats.workers[worker] += workerShares - else - coinStats.workers[worker] = workerShares - }); - portalStats.pools[coin] = coinStats; - portalStats.global.shares += coinStats.shares; - portalStats.global.workers += Object.keys(coinStats.workers).length; - }); - - _this.stats = portalStats; - callback(); - }); - - /* - { global: { - - } - - */ - - //get stats like hashrate and in/valid shares/blocks and workers in current round - - }; -}; - diff --git a/libs/stats.js b/libs/stats.js new file mode 100644 index 0000000..f42f750 --- /dev/null +++ b/libs/stats.js @@ -0,0 +1,123 @@ +var redis = require('redis'); +var async = require('async'); + +var os = require('os'); + + +module.exports = function(logger, portalConfig, poolConfigs){ + + var _this = this; + + var redisClients = []; + + Object.keys(poolConfigs).forEach(function(coin){ + var poolConfig = poolConfigs[coin]; + var internalConfig = poolConfig.shareProcessing.internal; + var redisConfig = internalConfig.redis; + + for (var i = 0; i < redisClients.length; i++){ + var client = redisClients[i]; + if (client.client.port === redisConfig.port && client.client.host === redisConfig.host){ + client.coins.push(coin); + return; + } + } + redisClients.push({ + coins: [coin], + client: redis.createClient(redisConfig.port, redisConfig.host) + }); + }); + + + this.stats = {}; + + + this.getStats = function(callback){ + + var allCoinStats = []; + + async.each(redisClients, function(client, callback){ + var windowTime = (Date.now() / 1000 | 0) - portalConfig.website.hashrateWindow; + var redisCommands = []; + var commandsPerCoin = 4; + + //Clear out old hashrate stats for each coin from redis + client.coins.forEach(function(coin){ + redisCommands.push(['zremrangebyscore', coin + '_hashrate', '-inf', windowTime]); + redisCommands.push(['zrangebyscore', coin + '_hashrate', windowTime, '+inf']); + redisCommands.push(['hgetall', coin + '_stats']); + redisCommands.push(['scard', coin + '_blocks']); + }); + + + client.client.multi(redisCommands).exec(function(err, replies){ + if (err){ + console.log('error with getting hashrate stats ' + JSON.stringify(err)); + callback(err); + } + else{ + for(var i = 0; i < replies.length; i += commandsPerCoin){ + var coinStats = { + coinName: client.coins[i / commandsPerCoin | 0], + hashrates: replies[i + 1], + poolStats: replies[i + 2], + poolPendingBlocks: replies[i + 3] + }; + allCoinStats.push(coinStats) + + } + callback(); + } + }); + }, function(err){ + if (err){ + console.log('error getting all stats' + JSON.stringify(err)); + callback(); + return; + } + + var portalStats = { + global:{ + workers: 0, + hashrate: 0 + }, + pools: allCoinStats + }; + + allCoinStats.forEach(function(coinStats){ + coinStats.workers = {}; + coinStats.shares = 0; + coinStats.hashrates.forEach(function(ins){ + var parts = ins.split(':'); + var workerShares = parseInt(parts[0]); + coinStats.shares += workerShares; + var worker = parts[1]; + if (worker in coinStats.workers) + coinStats.workers[worker] += workerShares + else + coinStats.workers[worker] = workerShares + }); + coinStats.hashrate = (coinStats.shares * 4294967296 / portalConfig.website.hashrateWindow) / 100000000 | 0; + delete coinStats.hashrates; + portalStats.global.hashrate += coinStats.hashrate; + portalStats.global.workers += Object.keys(coinStats.workers).length; + }); + + console.log(JSON.stringify(portalStats, null, 4)); + + _this.stats = portalStats; + callback(); + }); + + /* + { global: { + + } + + */ + + //get stats like hashrate and in/valid shares/blocks and workers in current round + + }; +}; + diff --git a/libs/website.js b/libs/website.js index 605382f..6f02760 100644 --- a/libs/website.js +++ b/libs/website.js @@ -31,7 +31,7 @@ var async = require('async'); var dot = require('dot'); var express = require('express'); -var api = require('./api.js'); +var stats = require('./stats.js'); module.exports = function(logger){ @@ -41,7 +41,7 @@ module.exports = function(logger){ var websiteConfig = portalConfig.website; - var portalApi = new api(logger, poolConfigs); + var portalStats = new stats(logger, portalConfig, poolConfigs); var logIdentify = 'Website'; @@ -75,7 +75,7 @@ module.exports = function(logger){ for (var pageName in pageTemplates){ pageProcessed[pageName] = pageTemplates[pageName]({ poolsConfigs: poolConfigs, - stats: portalApi.stats, + stats: portalStats.stats, portalConfig: portalConfig }); } @@ -107,13 +107,20 @@ module.exports = function(logger){ }); - portalApi.getStats(function(){ + portalStats.getStats(function(){ readPageFiles(Object.keys(pageFiles)); }); var buildUpdatedWebsite = function(){ - portalApi.getStats(function(){ + portalStats.getStats(function(){ processTemplates(); + + var statData = 'data: ' + JSON.stringify(portalStats.stats) + '\n\n'; + for (var uid in liveStatConnections){ + var res = liveStatConnections[uid]; + res.write(statData); + } + }); }; @@ -136,17 +143,21 @@ module.exports = function(logger){ var data = pageTemplates.index({ page: requestedPage, selected: pageId, - stats: portalApi.stats, + stats: portalStats.stats, poolConfigs: poolConfigs, portalConfig: portalConfig }); - res.send(data); + res.end(data); } else next(); }; + + var liveStatConnections = {}; + + app.get('/:page', route); app.get('/', route); @@ -156,21 +167,35 @@ module.exports = function(logger){ case 'get_page': var requestedPage = getPage(req.query.id); if (requestedPage){ - res.send(requestedPage); + res.end(requestedPage); return; } + case 'live_stats': + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive' + }); + res.write('\n'); + var uid = Math.random().toString(); + liveStatConnections[uid] = res; + req.on("close", function() { + delete liveStatConnections[uid]; + }); + + return; default: next(); } - res.send('you did api method ' + req.params.method); + //res.send('you did api method ' + req.params.method); }); app.use('/static', express.static('website')); app.use(function(err, req, res, next){ console.error(err.stack); - res.send(500, 'Something broke!'); + res.end(500, 'Something broke!'); }); app.listen(portalConfig.website.port, function(){ diff --git a/statsExampleJson.js b/statsExampleJson.js deleted file mode 100644 index 5c7e28d..0000000 --- a/statsExampleJson.js +++ /dev/null @@ -1,33 +0,0 @@ -var stats = { - - global:{ - hashrate: 1000, //in KH/s - validShares: 1, - invalidShares: 1, - validBlocks: 1, - invalidBlocks: 1, - blocksPending: 1, - blocksConfirmed: 1, - blocksOrphaned: 1, - connectedMiners: 344 - }, - pools:[ - { - coin: "Dogecoin", - sybmol: 'doge', - stats:{ - hashrate: 1000, //in KH/s - validShares: 1, - invalidShares: 1, - validBlocks: 1, - invalidBlocks: 1, - blocksPending: 1, - blocksConfirmed: 1, - blocksOrphaned: 1, - connectedMiners: 34545, - totalPayedOut: 3343.789797 - } - } - ] - -} \ No newline at end of file diff --git a/website/index.html b/website/index.html index 1b70889..1371b95 100644 --- a/website/index.html +++ b/website/index.html @@ -23,11 +23,36 @@
+ +
+
 {{=it.stats.global.workers}} Miners
+
 {{=it.stats.global.hashrate}} KH/s
+
+
@@ -40,10 +65,13 @@