diff --git a/lib/addresses.js b/lib/addresses.js index a855a5f..a7ea7e2 100644 --- a/lib/addresses.js +++ b/lib/addresses.js @@ -22,12 +22,19 @@ AddressController.prototype.show = function(req, res) { noTxList: parseInt(req.query.noTxList) }; + self.common.bindStopFlagOnClose(res, options); + + /*DEPRECATED if (req.query.from && req.query.to) { options.from = parseInt(req.query.from); options.to = parseInt(req.query.to); + }*/ + + if (req.query.after) { + options.after = req.query.after; } - this._address.getAddressSummary(req.addr, options, function(err, data) { + self._address.getAddressSummary(req.addr, options, function(err, data) { if(err) { return self.common.handleErrors(err, res); } @@ -40,25 +47,59 @@ AddressController.prototype.show = function(req, res) { }); }; +AddressController.prototype.show_ws = function(ws, req) { + var self = this; + var options = { noTxList: true }; + + if (req.query.after) { + options.after = req.query.after; + } + + self.common.bindStopFlagOnClose(ws, options); + + self._address.getAddressSummary(req.addr, options, function (err, data) { + if(err) { + return self.common.handleErrors_ws(err, ws); + } + + ws.send({data}); + + }, function(err, result) { + + if(err) { + return self.common.handleErrors_ws(err, ws); + } + + if(ws.readyState === ws.OPEN){ + ws.send({result}); + ws.close(); + } + + }); +}; + AddressController.prototype.balance = function(req, res) { - this.addressSummarySubQuery(req, res, 'balanceSat'); + this.addressSummarySubQuery(req, res, 'balance'); }; AddressController.prototype.totalReceived = function(req, res) { - this.addressSummarySubQuery(req, res, 'totalReceivedSat'); + this.addressSummarySubQuery(req, res, 'totalReceived'); }; AddressController.prototype.totalSent = function(req, res) { - this.addressSummarySubQuery(req, res, 'totalSentSat'); + this.addressSummarySubQuery(req, res, 'totalSent'); }; AddressController.prototype.unconfirmedBalance = function(req, res) { - this.addressSummarySubQuery(req, res, 'unconfirmedBalanceSat'); + this.addressSummarySubQuery(req, res, 'unconfirmedBalance'); }; AddressController.prototype.addressSummarySubQuery = function(req, res, param) { var self = this; - this.getAddressSummary(req.addr, {}, function(err, data) { + var options = { noTxList: true }; + self.common.bindStopFlagOnClose(res, options); + + self.getAddressSummary(req.addr, options, function(err, data) { if(err) { return self.common.handleErrors(err, res); } @@ -70,23 +111,23 @@ AddressController.prototype.addressSummarySubQuery = function(req, res, param) { AddressController.prototype.getAddressSummary = function(address, options, callback) { var self = this; - this._address.getAddressSummary(address, options, function(err, summary) { + self._address.getAddressSummary(address, options, function(err, summary) { if(err) { return callback(err); } var transformed = { address: self.common.translateOutputAddress(address), - balance: Unit.fromSatoshis(summary.balance).toBTC(), - balanceSat: summary.balance, - totalReceived: Unit.fromSatoshis(summary.totalReceived).toBTC(), + balance: summary.balance, + balanceSat: summary.balanceSat, + totalReceived: summary.totalReceived, totalReceivedSat: summary.totalReceivedSat, - totalSent: Unit.fromSatoshis(summary.totalSent).toBTC(), + totalSent: summary.totalSent, totalSentSat: summary.totalSentSat, - unconfirmedBalance: Unit.fromSatoshis(summary.unconfirmedBalance).toBTC(), - unconfirmedBalanceSat: summary.unconfirmedBalance, - unconfirmedTxApperances: summary.unconfirmedAppearances, // misspelling - ew - txApperances: summary.txApperances, // yuck + unconfirmedBalance: summary.unconfirmedBalance, + unconfirmedBalanceSat: summary.unconfirmedBalanceSat, + unconfirmedTxApperances: summary.unconfirmedTxApperances, + txApperances: summary.txApperances, transactions: summary.transactions }; @@ -114,7 +155,7 @@ AddressController.prototype.checkAddrs = function(req, res, next) { } if(!_.isArray(req.addrs) || _.compact(req.addrs).length < 1) { - return this.common.handleErrors({ + return self.common.handleErrors({ message: 'Must include address', code: 1 }, res); @@ -125,7 +166,7 @@ AddressController.prototype.checkAddrs = function(req, res, next) { req.addr = req.addrs[0]; } catch(e) { console.log('[addresses.js.130]', e); //TODO - return this.common.handleErrors({ + return self.common.handleErrors({ message: 'Invalid address: ' + e, code: 1 }, res); @@ -137,7 +178,7 @@ console.log('[addresses.js.130]', e); //TODO AddressController.prototype.utxo = function(req, res) { var self = this; - this._address.getAddressUnspentOutputs(req.addr, {}, function(err, utxos) { + self._address.getAddressUnspentOutputs(req.addr, {}, function(err, utxos) { var results; if(err) { return self.common.handleErrors(err, res); @@ -245,11 +286,21 @@ AddressController.prototype.multiutxo = function(req, res) { AddressController.prototype.multitxs = function(req, res) { var self = this; - var options = { - from: parseInt(req.query.from) || parseInt(req.body.from) || 0 - }; + var options = {}; - options.to = parseInt(req.query.to) || parseInt(req.body.to) || parseInt(options.from) + 10; + options.after = req.query.after || req.body.after || undefined; + + //Temporary support + if(req.query.from || req.body.from) { + options.from = parseInt(req.query.from) || parseInt(req.body.from) || undefined; + } + + //Temporary support + if(req.query.to || req.body.to) { + options.to = parseInt(req.query.to) || parseInt(req.body.to) || undefined; + } + + self.common.bindStopFlagOnClose(res, options); self._address.getAddressHistory(req.addrs, options, function(err, result) { @@ -265,10 +316,14 @@ AddressController.prototype.multitxs = function(req, res) { return self.common.handleErrors(err, res); } + var lastItem = items.find(a => a.confirmations !== 0), //assuming items is recent tx first order + lastItem = typeof lastItem === 'object' ? lastItem.txid: undefined + var ret = { totalItems: result.totalCount, - from: options.from, - to: Math.min(options.to, result.totalCount), + lastItem: lastItem, + //from: options.from, + //to: Math.min(options.to, result.totalCount), items: items }; @@ -278,6 +333,64 @@ AddressController.prototype.multitxs = function(req, res) { }); }; +AddressController.prototype.multitxs_ws = function(ws, req) { + var self = this; + + var options = {}; + + if (req.query.after) { + options.after = req.query.after; + } + + options.txNotNeeded = true; + + var transformOptions = self._getTransformOptions(req); + + self.common.bindStopFlagOnClose(ws, options); + + var lastItem = {id: '', height: 0}; + + self._address.getAddressHistory(req.addrs, options, function (err, data) { + if(err) { + return self.common.handleErrors_ws(err, ws, false); + } + + self.txController.transformTransaction(data, transformOptions, function(err, tx){ + + if(err) { + return self.common.handleErrors_ws(err, ws, false); + } + + //finding the last key (useful for `after` option on next request call) + if(tx.confirmations) + if(lastItem.height < tx.blockheight || (lastItem.height == tx.blockheight && lastItem.id < tx.txid)){ + lastItem.id = tx.txid; + lastItem.height = tx.blockheight; + } + + ws.send({data: tx}) + + }); + + }, function(err, result) { + + if(err) { + return self.common.handleErrors_ws(err, ws); + } + + var ret = { + totalItems: result.totalCount, + lastItem: lastItem.id + } + + if(ws.readyState === ws.OPEN){ + ws.send({result: ret}); + ws.close(); + } + + }); +}; + AddressController.prototype.transformAddressHistoryForMultiTxs = function(txs, options, callback) { var self = this; diff --git a/lib/common.js b/lib/common.js index d11ad79..cd6ff2d 100644 --- a/lib/common.js +++ b/lib/common.js @@ -13,6 +13,17 @@ Common.prototype.notReady = function (err, res, p) { res.status(503).send('Server not yet ready. Sync Percentage:' + p); }; +Common.prototype.notReady_ws = function (err, ws, p) { + if(ws.readyState !== ws.OPEN) + return; + ws.send({error: {message: 'Server not yet ready. Sync Percentage:' + p, code: 503}}); + ws.close(); +}; + +Common.prototype.bindStopFlagOnClose = function (res_ws, obj) { + res_ws.on("close", () => obj.flag_stop = true); +}; + Common.prototype.handleErrors = function (err, res) { if (err) { if (err.code) { @@ -29,6 +40,22 @@ Common.prototype.handleErrors = function (err, res) { } }; +Common.prototype.handleErrors_ws = function (err, ws, close = true) { + if(ws.readyState !== ws.OPEN) + return; + if (err) { + if (err.code) + ws.send({error: {message: err.message, code: err.code}}); + else { + this.log.error(err.stack); + ws.send({error: {message: err.message, code: 503}}); + } + } else { + ws.send({error: {message: 'Not found', code: 404}}); + } + if(close) + ws.close(); +} Common.prototype.translateInputAddresses= function(addresses) { var self = this; diff --git a/lib/index.js b/lib/index.js index b65f649..b887c2c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -143,7 +143,7 @@ FlosightAPI.prototype._getRateLimiter = function() { return limiter; }; -FlosightAPI.prototype.setupRoutes = function(app) { +FlosightAPI.prototype.setupRoutes = function(app, express, express_ws) { var self = this; @@ -184,6 +184,9 @@ FlosightAPI.prototype.setupRoutes = function(app) { } }); + //Add ws listener to app + var expressWS = express_ws(app); + //Block routes var blockOptions = { node: this.node, @@ -216,10 +219,12 @@ FlosightAPI.prototype.setupRoutes = function(app) { // Address routes var addresses = new AddressController(this.node, this.translateAddresses); app.get('/addr/:addr', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.show.bind(addresses)); + app.ws('/addr/:addr', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.show_ws.bind(addresses)); app.get('/addr/:addr/utxo', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.utxo.bind(addresses)); app.get('/addrs/:addrs/utxo', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses)); app.post('/addrs/utxo', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses)); app.get('/addrs/:addrs/txs', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses)); + app.ws('/addrs/:addrs/txs', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multitxs_ws.bind(addresses)); app.post('/addrs/txs', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses)); // Address property routes diff --git a/package.json b/package.json index bba7855..3fd5c14 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "flosight-api", "description": "A Florincoin blockchain REST and web socket API service for Flocore Node.", - "version": "5.1.0-beta.75", - "repository": "git://github.com/oipwg/flosight-api.git", + "version": "5.1.1-beta-rm", + "repository": "git://github.com/ranchimall/flosight-api.git", "bugs": { - "url": "https://github.com/oipwg/flosight-api/issues" + "url": "https://github.com/ranchimall/flosight-api/issues" }, - "homepage": "https://github.com/oipwg/flosight-api", + "homepage": "https://github.com/ranchimall/flosight-api", "license": "MIT", "keywords": [ "flosight",