From 78fc0d4360c68c53153c0b7c4162657006cd03b0 Mon Sep 17 00:00:00 2001 From: Damian Mee Date: Mon, 1 Sep 2014 02:09:20 +0200 Subject: [PATCH 1/8] fix(reload): plugins are now reloaded instantly after config change --- lib/plugins.js | 106 +++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 44 deletions(-) diff --git a/lib/plugins.js b/lib/plugins.js index ff053336..671c7a80 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -11,13 +11,22 @@ var POLLING_RATE = 60 * 1000; // poll each minute var db = null; + var tickerPlugin = null; var traderPlugin = null; var walletPlugin = null; var idVerifierPlugin = null; +var currentlyUsedPlugins = { + ticker: null, + transfer: null, + trader: null, + idVerifier: null +}; + + var cachedConfig = null; -var deviceCurrency = 'USD'; // Can 'USD' be set as default? +var deviceCurrency = 'USD'; var lastBalances = null; var lastRates = {}; @@ -93,56 +102,65 @@ function loadPlugin(name, config) { return plugin; } +function loadOrConfigPlugin(pluginHandle, pluginType, currency, onChangeCb) { + var currentName = cachedConfig.exchanges.plugins.current[pluginType]; + var pluginChanged = currentlyUsedPlugins[pluginType] !== currentName; + + if (currentName) { // some plugins may be disabled + var pluginConfig = cachedConfig.exchanges.plugins.settings[currentName] || {}; + + if (currency) pluginConfig.currency = currency; + + if (pluginHandle && !pluginChanged) pluginHandle.config(pluginConfig); + else pluginHandle = loadPlugin(currentName, pluginConfig); + } + + if (typeof onChangeCb === 'function') onChangeCb(pluginHandle, currency); + + return pluginHandle; +} + exports.configure = function configure(config) { if (config.exchanges.settings.lowBalanceMargin < 1) { throw new Error('`settings.lowBalanceMargin` has to be >= 1'); } - deviceCurrency = config.exchanges.settings.currency; - var plugins = config.exchanges.plugins; - - // [required] configure (or load) ticker - var tickerName = plugins.current.ticker; - var tickerConfig = plugins.settings[tickerName] || {}; - tickerConfig.currency = deviceCurrency; - - if (tickerPlugin) tickerPlugin.config(tickerConfig); - else tickerPlugin = loadPlugin(tickerName, tickerConfig); - - - // [required] configure (or load) wallet - var walletName = plugins.current.transfer; - var walletConfig = plugins.settings[walletName] || {}; - - if (walletPlugin) walletPlugin.config(walletConfig); - else walletPlugin = loadPlugin(walletName, walletConfig); - - - // [optional] configure (or load) trader - var traderName = plugins.current.trade; - if (traderName) { // traderPlugin may be disabled - var traderConfig = plugins.settings[traderName] || {}; - - if (traderPlugin) traderPlugin.config(traderConfig); - else traderPlugin = loadPlugin(traderName, traderConfig); - } - - - // [optional] ID Verifier - var verifierName = plugins.current.idVerifier; - if (verifierName) { // idVerifierPlugin may be disabled - var verifierConfig = plugins.settings[verifierName] || {}; - - if (idVerifierPlugin) idVerifierPlugin.config(verifierConfig); - else idVerifierPlugin = loadPlugin(verifierName, verifierConfig); - } - - cachedConfig = config; - pollBalance(); - pollRate(); + // TICKER [required] configure (or load) + loadOrConfigPlugin( + tickerPlugin, + 'ticker', + config.exchanges.settings.currency, // device currency + function onTickerChange(newTicker, currency) { + tickerPlugin = newTicker; + pollRate(); + } + ); + + // WALLET [required] configure (or load) + loadOrConfigPlugin( + walletPlugin, + 'transfer', + null, + function onWalletChange(newWallet) { + walletPlugin = newWallet; + pollBalance(); + } + ); + + // TRADER [optional] configure (or load) + traderPlugin = loadOrConfigPlugin( + traderPlugin, + 'trade' + ); + + // ID VERIFIER [optional] configure (or load) + idVerifierPlugin = loadOrConfigPlugin( + idVerifierPlugin, + 'idVerifier' + ); }; exports.getCachedConfig = function getCachedConfig() { return cachedConfig; @@ -298,7 +316,7 @@ function pollBalance(callback) { } function pollRate(callback) { - logger.debug('polling for rates'); + logger.debug('polling for rates (' + tickerPlugin.NAME + ')'); tickerPlugin.ticker(deviceCurrency, function(err, resRates) { if (err) { From 5e77655dddf3ba722c25769255732e86771b87ac Mon Sep 17 00:00:00 2001 From: Damian Mee Date: Mon, 1 Sep 2014 06:01:49 +0200 Subject: [PATCH 2/8] fix(ticker): Missing assignment added --- lib/plugins.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/plugins.js b/lib/plugins.js index 671c7a80..55eab372 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -127,12 +127,13 @@ exports.configure = function configure(config) { } cachedConfig = config; + deviceCurrency = config.exchanges.settings.currency; // TICKER [required] configure (or load) loadOrConfigPlugin( tickerPlugin, 'ticker', - config.exchanges.settings.currency, // device currency + deviceCurrency, // device currency function onTickerChange(newTicker, currency) { tickerPlugin = newTicker; pollRate(); From ab4970f84a7c1c1acc143b5e2648fb4cd5b522d0 Mon Sep 17 00:00:00 2001 From: Damian Mee Date: Mon, 1 Sep 2014 06:28:51 +0200 Subject: [PATCH 3/8] style(quotes): funny quotes changed to single quotes --- lib/plugins.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins.js b/lib/plugins.js index 55eab372..4cd001a0 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -42,7 +42,7 @@ var sessions = {}; // that's basically a constructor exports.init = function init(databaseHandle) { if (!databaseHandle) { - throw new Error('`db` is required'); + throw new Error('\'db\' is required'); } db = databaseHandle; @@ -123,7 +123,7 @@ function loadOrConfigPlugin(pluginHandle, pluginType, currency, onChangeCb) { exports.configure = function configure(config) { if (config.exchanges.settings.lowBalanceMargin < 1) { - throw new Error('`settings.lowBalanceMargin` has to be >= 1'); + throw new Error('\'settings.lowBalanceMargin\' has to be >= 1'); } cachedConfig = config; From fba1f972c70f5f2a01aa2cfcf0435f4f61416a1b Mon Sep 17 00:00:00 2001 From: Damian Mee Date: Mon, 1 Sep 2014 06:29:54 +0200 Subject: [PATCH 4/8] style(naming): callback name extended --- lib/plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins.js b/lib/plugins.js index 4cd001a0..9e8b2601 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -102,7 +102,7 @@ function loadPlugin(name, config) { return plugin; } -function loadOrConfigPlugin(pluginHandle, pluginType, currency, onChangeCb) { +function loadOrConfigPlugin(pluginHandle, pluginType, currency, onChangeCallback) { var currentName = cachedConfig.exchanges.plugins.current[pluginType]; var pluginChanged = currentlyUsedPlugins[pluginType] !== currentName; From ed3779ede47fd75074225c25d2a16350f8962707 Mon Sep 17 00:00:00 2001 From: Damian Mee Date: Tue, 2 Sep 2014 21:21:51 +0200 Subject: [PATCH 5/8] fix(trader): trader now gets disabled just after config change (bug #40) --- lib/plugins.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/plugins.js b/lib/plugins.js index 9e8b2601..1ebbfdfc 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -106,7 +106,8 @@ function loadOrConfigPlugin(pluginHandle, pluginType, currency, onChangeCallback var currentName = cachedConfig.exchanges.plugins.current[pluginType]; var pluginChanged = currentlyUsedPlugins[pluginType] !== currentName; - if (currentName) { // some plugins may be disabled + if (!currentName) pluginHandle = null; + else { // some plugins may be disabled var pluginConfig = cachedConfig.exchanges.plugins.settings[currentName] || {}; if (currency) pluginConfig.currency = currency; @@ -115,7 +116,7 @@ function loadOrConfigPlugin(pluginHandle, pluginType, currency, onChangeCallback else pluginHandle = loadPlugin(currentName, pluginConfig); } - if (typeof onChangeCb === 'function') onChangeCb(pluginHandle, currency); + if (typeof onChangeCallback === 'function') onChangeCallback(pluginHandle, currency); return pluginHandle; } @@ -154,7 +155,13 @@ exports.configure = function configure(config) { // TRADER [optional] configure (or load) traderPlugin = loadOrConfigPlugin( traderPlugin, - 'trade' + 'trade', + null, + function onTraderChange(newTrader) { + traderPlugin = newTrader; + if (newTrader === null) stopTrader(); + else startTrader(); + } ); // ID VERIFIER [optional] configure (or load) @@ -276,17 +283,27 @@ exports.startPolling = function startPolling() { rateInterval = setInterval(pollRate, POLLING_RATE); } + startTrader(); +}; + +function startTrader() { // Always start trading, even if we don't have a trade exchange configured, // since configuration can always change in `Trader#configure`. // `Trader#executeTrades` returns early if we don't have a trade exchange // configured at the moment. - if (!tradeInterval) { + if (traderPlugin && !tradeInterval) { tradeInterval = setInterval( executeTrades, cachedConfig.exchanges.settings.tradeInterval ); } -}; +} +function stopTrader() { + if (tradeInterval) { + clearInterval(tradeInterval); + tradeInterval = null; + } +} function pollBalance(callback) { From f06ace8c83d103bc966b72e293335280f3955a42 Mon Sep 17 00:00:00 2001 From: Damian Mee Date: Wed, 3 Sep 2014 01:40:04 +0200 Subject: [PATCH 6/8] feat(log): log module version, abbr git hash and branch on start --- lib/logger.js | 33 +++++++++++++++++++++++++++++++-- package.json | 1 + 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/logger.js b/lib/logger.js index 21b8df7d..20bff785 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -1,7 +1,36 @@ -var bunyan = require('bunyan'); +var bunyan = require('bunyan'); +var async = require('async'); var logLevel = process.env.LAMASSU_ENV === 'debug' ? 'debug' : 'info'; -module.exports = bunyan.createLogger({name: 'lamassu-server', level: logLevel}); \ No newline at end of file +var bunyan = bunyan.createLogger({name: 'lamassu-server', level: logLevel}); + + +// log version +var version = require('../package.json').version; +bunyan.info('Version:', version); + + +// log twitter stuff (optional) +function wrapper(fn) { + return function(cb) { + fn(function(value) { + cb(null, value); + }); + } +} +try { + var git = require('git-rev'); + async.parallel([ + wrapper(git.branch), + wrapper(git.short) + ], + function(err, values) { + bunyan.info('Git:', '#' + values[0], '@' + values[1]); + }); +} catch(_) {} + + +module.exports = bunyan; diff --git a/package.json b/package.json index cc13359e..31e19b33 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ }, "devDependencies": { "chai": "^1.9.1", + "git-rev": "^0.2.1", "lodash": "^2.4.1", "mocha": "^1.21.4", "mockery": "^1.4.0" From 56315ffd34a51fff4cf4505c99b7ccc0c0b84197 Mon Sep 17 00:00:00 2001 From: Damian Mee Date: Wed, 3 Sep 2014 20:30:22 +0200 Subject: [PATCH 7/8] fix(trader): trades queue now cleared on trader disable --- lib/plugins.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/plugins.js b/lib/plugins.js index 1ebbfdfc..185708ff 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -302,6 +302,7 @@ function stopTrader() { if (tradeInterval) { clearInterval(tradeInterval); tradeInterval = null; + tradesQueue = []; } } From 6b004fdf8e0d2e6de54fdb190f9d1ce57f964f59 Mon Sep 17 00:00:00 2001 From: Damian Mee Date: Thu, 4 Sep 2014 01:08:41 +0200 Subject: [PATCH 8/8] style(jshint): some style fixes and jshint compatibility improved --- bin/lamassu-server | 4 +++- lib/logger.js | 25 ++++++++++++++----------- lib/plugins.js | 11 +++-------- lib/routes.js | 3 +-- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/bin/lamassu-server b/bin/lamassu-server index 84ea417a..8b119d92 100755 --- a/bin/lamassu-server +++ b/bin/lamassu-server @@ -1,8 +1,10 @@ #!/usr/bin/env node + +'use strict'; + var fs = require('fs'); var createServer = require('../lib/app.js'); var argv = require('minimist')(process.argv.slice(2)); -var logger = require('../lib/logger'); var options = { postgres: process.env.DATABASE_URL diff --git a/lib/logger.js b/lib/logger.js index 20bff785..829b106d 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -1,3 +1,5 @@ +'use strict'; + var bunyan = require('bunyan'); var async = require('async'); @@ -10,25 +12,26 @@ var bunyan = bunyan.createLogger({name: 'lamassu-server', level: logLevel}); // log version var version = require('../package.json').version; -bunyan.info('Version:', version); +bunyan.info('Version: %s', version); -// log twitter stuff (optional) -function wrapper(fn) { - return function(cb) { - fn(function(value) { - cb(null, value); - }); - } +// log git stuff (optional) +// `git-rev` omits `err` param in callback, without this wrapper +// `async` interprets returned values as errors. +function wrapper(fn, cb) { + fn(function(value) { + cb(null, value); + }); } try { var git = require('git-rev'); + async.parallel([ - wrapper(git.branch), - wrapper(git.short) + async.apply(wrapper, git.branch), + async.apply(wrapper, git.short) ], function(err, values) { - bunyan.info('Git:', '#' + values[0], '@' + values[1]); + bunyan.info('Git: #%s @%s', values[0], values[1]); }); } catch(_) {} diff --git a/lib/plugins.js b/lib/plugins.js index 185708ff..bab22d6c 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -17,12 +17,7 @@ var traderPlugin = null; var walletPlugin = null; var idVerifierPlugin = null; -var currentlyUsedPlugins = { - ticker: null, - transfer: null, - trader: null, - idVerifier: null -}; +var currentlyUsedPlugins = {}; var cachedConfig = null; @@ -135,7 +130,7 @@ exports.configure = function configure(config) { tickerPlugin, 'ticker', deviceCurrency, // device currency - function onTickerChange(newTicker, currency) { + function onTickerChange(newTicker) { tickerPlugin = newTicker; pollRate(); } @@ -335,7 +330,7 @@ function pollBalance(callback) { } function pollRate(callback) { - logger.debug('polling for rates (' + tickerPlugin.NAME + ')'); + logger.debug('polling for rates (%s)', tickerPlugin.NAME); tickerPlugin.ticker(deviceCurrency, function(err, resRates) { if (err) { diff --git a/lib/routes.js b/lib/routes.js index 770ba512..92eb7fad 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -6,7 +6,6 @@ var mock = false; var plugins; var lamassuConfig; -var config; module.exports = { init: init, @@ -58,7 +57,7 @@ function poll(req, res) { }; if (response.idVerificationEnabled) - response.idVerificationLimit = complianceSettings.idVerificationLimit + response.idVerificationLimit = complianceSettings.idVerificationLimit; res.json(response); }