Merge pull request #59 from cmgustavo/feature/status_api

wow!
This commit is contained in:
Mario Colque 2014-01-16 06:35:21 -08:00
commit ffce4526a6
12 changed files with 422 additions and 5 deletions

51
app/controllers/status.js Normal file
View File

@ -0,0 +1,51 @@
'use strict';
/**
* Module dependencies.
*/
var Status = require('../models/Status');
var async = require('async');
/**
* Status
*/
exports.show = function(req, res) {
if (! req.query.q) {
res.status(400).send('Bad Request');
}
else {
var s = req.query.q;
var d = Status.new();
if (s == 'getInfo') {
d.getInfo(function(err) {
if (err) next(err);
res.jsonp(d);
});
}
else if (s == 'getDifficulty') {
d.getDifficulty(function(err) {
if (err) next(err);
res.jsonp(d);
});
}
else if (s == 'getTxOutSetInfo') {
d.getTxOutSetInfo(function(err) {
if (err) next(err);
res.jsonp(d);
});
}
else if (s == 'getBestBlockHash') {
d.getBestBlockHash(function(err) {
if (err) next(err);
res.jsonp(d);
});
}
else {
res.status(400).send('Bad Request');
}
}
};

86
app/models/Status.js Normal file
View File

@ -0,0 +1,86 @@
'use strict';
require('classtool');
function spec() {
var async = require('async');
var RpcClient = require('bitcore/RpcClient').class();
var config = require('../../config/config');
var rpc = new RpcClient(config.bitcoind);
function Status() {
this.info = {};
this.difficulty = {};
this.txoutsetinfo = {};
this.bestblockhash = {};
}
Status.prototype.getInfo = function(next) {
var that = this;
async.series([
function (cb) {
rpc.getInfo(function(err, info){
if (err) return cb(err);
that.info = info.result;
return cb();
});
}
], function (err) {
return next(err);
});
};
Status.prototype.getDifficulty = function(next) {
var that = this;
async.series([
function (cb) {
rpc.getDifficulty(function(err, df){
if (err) return cb(err);
that.difficulty = df.result;
return cb();
});
}
], function (err) {
return next(err);
});
};
Status.prototype.getTxOutSetInfo = function(next) {
var that = this;
async.series([
function (cb) {
rpc.getTxOutSetInfo(function(err, txout){
if (err) return cb(err);
that.txoutsetinfo = txout.result;
return cb();
});
}
], function (err) {
return next(err);
});
};
Status.prototype.getBestBlockHash = function(next) {
var that = this;
async.series([
function (cb) {
rpc.getBestBlockHash(function(err, bbh){
if (err) return cb(err);
that.bestblockhash = bbh.result;
return cb();
});
}
], function (err) {
return next(err);
});
};
return Status;
}
module.defineClass(spec);

View File

@ -27,6 +27,7 @@ script(type='text/javascript', src='/js/directives.js')
script(type='text/javascript', src='/js/filters.js')
//Application Services
script(type='text/javascript', src='/js/services/status.js')
script(type='text/javascript', src='/js/services/address.js')
script(type='text/javascript', src='/js/services/transactions.js')
script(type='text/javascript', src='/js/services/blocks.js')
@ -40,4 +41,5 @@ script(type='text/javascript', src='/js/controllers/blocks.js')
script(type='text/javascript', src='/js/controllers/transactions.js')
script(type='text/javascript', src='/js/controllers/address.js')
script(type='text/javascript', src='/js/controllers/search.js')
script(type='text/javascript', src='/js/controllers/status.js')
script(type='text/javascript', src='/js/init.js')

View File

@ -25,4 +25,8 @@ module.exports = function(app) {
app.get('/api/addr/:addr', addresses.show);
app.param('addr', addresses.address);
// Status route
var st = require('../app/controllers/status');
app.get('/api/status', st.show);
};

View File

@ -13,7 +13,8 @@ var app = angular.module('mystery',
'mystery.transactions',
'monospaced.qrcode',
'mystery.address',
'mystery.search'
'mystery.search',
'mystery.status'
]);
angular.module('mystery.system', []);
@ -22,3 +23,4 @@ angular.module('mystery.blocks', []);
angular.module('mystery.transactions', []);
angular.module('mystery.address', []);
angular.module('mystery.search', []);
angular.module('mystery.status', []);

View File

@ -22,6 +22,9 @@ angular.module('mystery').config(['$routeProvider',
when('/address/:addrStr', {
templateUrl: 'views/address.html'
}).
when('/status', {
templateUrl: 'views/status.html'
}).
otherwise({
redirectTo: '/'
});

View File

@ -3,10 +3,16 @@
angular.module('mystery.system').controller('HeaderController', ['$scope', 'Global', function ($scope, Global) {
$scope.global = Global;
$scope.menu = [{
'title': 'Blocks',
'link': 'blocks'
}];
$scope.menu = [
{
'title': 'Blocks',
'link': 'blocks'
},
{
'title': 'Status',
'link': 'status'
}
];
$scope.isCollapsed = false;
}]);

View File

@ -0,0 +1,26 @@
'use strict';
angular.module('mystery.status').controller('StatusController', ['$scope', '$routeParams', '$location', 'Global', 'Status', function ($scope, $routeParams, $location, Global, Status) {
$scope.global = Global;
$scope.getData = function(q) {
Status.get({
q: q
}, function(d) {
if (q == 'getInfo') {
$scope.info = d.info;
}
if (q == 'getDifficulty') {
$scope.difficulty = d.difficulty;
}
if (q == 'getTxOutSetInfo') {
$scope.txoutsetinfo = d.txoutsetinfo;
}
if (q == 'getBestBlockHash') {
$scope.bestblockhash = d.bestblockhash;
}
});
};
}]);

View File

@ -0,0 +1,8 @@
'use strict';
angular.module('mystery.status').factory('Status', ['$resource', function($resource) {
return $resource('/api/status', {
q: '@q'
});
}]);

144
public/views/status.html Normal file
View File

@ -0,0 +1,144 @@
<section data-ng-controller="StatusController">
<div class="page-header">
<h1>
Status
</h1>
</div>
<div class="row">
<div class="col-lg-6">
<h3>getInfo</h3>
<table class="table table-striped" data-ng-init="getData('getInfo')">
<tbody>
<tr data-ng-show="!info">
<td colspan="2" class="text-center">Loading...</td>
</tr>
<tr>
<td>Version</td>
<td>{{info.version}}</td>
</tr>
<tr>
<td>protocolversion</td>
<td>{{info.protocolversion}}</td>
</tr>
<tr>
<td>walletversion</td>
<td>{{info.walletversion}}</td>
</tr>
<tr>
<td>balance</td>
<td>{{info.balance}}</td>
</tr>
<tr>
<td>blocks</td>
<td>{{info.blocks}}</td>
</tr>
<tr>
<td>timeoffset</td>
<td>{{info.timeoffset}}</td>
</tr>
<tr>
<td>connections</td>
<td>{{info.connections}}</td>
</tr>
<tr>
<td>proxy</td>
<td>{{info.proxy}}</td>
</tr>
<tr>
<td>difficulty</td>
<td>{{info.difficulty}}</td>
</tr>
<tr>
<td>testnet</td>
<td>{{info.testnet}}</td>
</tr>
<tr>
<td>keypoololdest</td>
<td>{{info.keypoololdest}}</td>
</tr>
<tr>
<td>keypoolsize</td>
<td>{{info.keypoolsize}}</td>
</tr>
<tr>
<td>paytxfee</td>
<td>{{info.paytxfee}}</td>
</tr>
<tr>
<td>errors</td>
<td>{{info.errors}}</td>
</tr>
</tbody>
</table>
</div>
<div class="col-lg-6">
<h3>getTxOutSetInfo</h3>
<table class="table table-striped" data-ng-init="getData('getTxOutSetInfo')">
<tbody>
<tr data-ng-show="!txoutsetinfo">
<td colspan="2" class="text-center">Loading...</td>
</tr>
<tr>
<td>Height</td>
<td>{{txoutsetinfo.height}}</td>
</tr>
<tr>
<td>bestblock</td>
<td>{{txoutsetinfo.bestblock}}</td>
</tr>
<tr>
<td>transactions</td>
<td>{{txoutsetinfo.transactions}}</td>
</tr>
<tr>
<td>txouts</td>
<td>{{txoutsetinfo.txouts}}</td>
</tr>
<tr>
<td>bytes_serialized</td>
<td>{{txoutsetinfo.bytes_serialized}}</td>
</tr>
<tr>
<td>hash_serialized</td>
<td>{{txoutsetinfo.hash_serialized}}</td>
</tr>
<tr>
<td>total_amount</td>
<td>{{txoutsetinfo.total_amount}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<h3>getDifficulty</h3>
<table class="table table-striped" data-ng-init="getData('getDifficulty')">
<tbody>
<tr data-ng-show="!difficulty">
<td colspan="2" class="text-center">Loading...</td>
</tr>
<tr>
<td>Difficulty</td>
<td>{{difficulty}}</td>
</tr>
</tbody>
</table>
</div>
<div class="col-lg-6">
<h3>getBestBlockHash</h3>
<table class="table table-striped" data-ng-init="getData('getBestBlockHash')">
<tbody>
<tr data-ng-show="!bestblockhash">
<td colspan="1" class="text-center">Loading...</td>
</tr>
<tr>
<td>{{bestblockhash}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>

65
test/model/status.js Normal file
View File

@ -0,0 +1,65 @@
#!/usr/bin/env node
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
var
assert = require('assert'),
config = require('../../config/config'),
Status = require('../../app/models/Status').class(),
mongoose= require('mongoose');
describe('Status', function(){
before(function(done) {
mongoose.connect(config.db);
done();
});
after(function(done) {
mongoose.connection.close();
done();
});
it('getInfo', function(done) {
var d = new Status();
d.getInfo(function(err) {
if (err) done(err);
assert.equal('number', typeof d.info.difficulty);
done();
});
});
it('getDifficulty', function(done) {
var d = new Status();
d.getDifficulty(function(err) {
if (err) done(err);
assert.equal('number', typeof d.difficulty);
done();
});
});
it('getTxOutSetInfo', function(done) {
var d = new Status();
d.getTxOutSetInfo(function(err) {
if (err) done(err);
assert.equal('number', typeof d.txoutsetinfo.txouts);
done();
});
});
it('getBestBlockHash', function(done) {
var d = new Status();
d.getBestBlockHash(function(err) {
if (err) done(err);
assert.equal('string', typeof d.bestblockhash);
done();
});
});
});

20
util/status_info.js Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env node
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
var RpcClient = require('../node_modules/bitcore/RpcClient').class();
var config = require('../config/config');
var rpc = new RpcClient(config.bitcoind);
var block = rpc.getInfo(function(err, block) {
if (err) {
console.log("Err:");
console.log(err);
}
console.log("Block info:");
console.log(block);
});