improve config loading; remove debug
This commit is contained in:
parent
5f0b70ca42
commit
614c64646a
15 changed files with 198 additions and 193 deletions
|
|
@ -11,6 +11,7 @@ const settingsLoader = require('../settings-loader')
|
||||||
const db = require('../db')
|
const db = require('../db')
|
||||||
const options = require('../options')
|
const options = require('../options')
|
||||||
const configManager = require('../config-manager')
|
const configManager = require('../config-manager')
|
||||||
|
const configValidate = require('../config-validate')
|
||||||
|
|
||||||
const machines = require('./machines')
|
const machines = require('./machines')
|
||||||
|
|
||||||
|
|
@ -38,15 +39,6 @@ function allScopes (cryptoScopes, machineScopes) {
|
||||||
return scopes
|
return scopes
|
||||||
}
|
}
|
||||||
|
|
||||||
function allCryptoScopes (cryptos, cryptoScope) {
|
|
||||||
const cryptoScopes = []
|
|
||||||
|
|
||||||
if (cryptoScope === 'global' || cryptoScope === 'both') cryptoScopes.push('global')
|
|
||||||
if (cryptoScope === 'specific' || cryptoScope === 'both') cryptos.forEach(r => cryptoScopes.push(r))
|
|
||||||
|
|
||||||
return cryptoScopes
|
|
||||||
}
|
|
||||||
|
|
||||||
function allMachineScopes (machineList, machineScope) {
|
function allMachineScopes (machineList, machineScope) {
|
||||||
const machineScopes = []
|
const machineScopes = []
|
||||||
|
|
||||||
|
|
@ -56,43 +48,6 @@ function allMachineScopes (machineList, machineScope) {
|
||||||
return machineScopes
|
return machineScopes
|
||||||
}
|
}
|
||||||
|
|
||||||
function satisfiesRequire (config, cryptos, machineList, field, refFields) {
|
|
||||||
const fieldCode = field.code
|
|
||||||
|
|
||||||
const scopes = allScopes(
|
|
||||||
allCryptoScopes(cryptos, field.cryptoScope),
|
|
||||||
allMachineScopes(machineList, field.machineScope)
|
|
||||||
)
|
|
||||||
|
|
||||||
return scopes.every(scope => {
|
|
||||||
const isEnabled = () => refFields.some(refField => {
|
|
||||||
return isScopeEnabled(config, cryptos, machineList, refField, scope)
|
|
||||||
})
|
|
||||||
|
|
||||||
const isBlank = () => R.isNil(configManager.scopedValue(scope[0], scope[1], fieldCode, config))
|
|
||||||
const isRequired = refFields.length === 0 || isEnabled()
|
|
||||||
|
|
||||||
return isRequired ? !isBlank() : true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function isScopeEnabled (config, cryptos, machineList, refField, scope) {
|
|
||||||
const [cryptoScope, machineScope] = scope
|
|
||||||
const candidateCryptoScopes = cryptoScope === 'global'
|
|
||||||
? allCryptoScopes(cryptos, refField.cryptoScope)
|
|
||||||
: [cryptoScope]
|
|
||||||
|
|
||||||
const candidateMachineScopes = machineScope === 'global'
|
|
||||||
? allMachineScopes(machineList, refField.machineScope)
|
|
||||||
: [ machineScope ]
|
|
||||||
|
|
||||||
const allRefCandidateScopes = allScopes(candidateCryptoScopes, candidateMachineScopes)
|
|
||||||
const getFallbackValue = scope => configManager.scopedValue(scope[0], scope[1], refField.code, config)
|
|
||||||
const values = allRefCandidateScopes.map(getFallbackValue)
|
|
||||||
|
|
||||||
return values.some(r => r)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCryptos (config, machineList) {
|
function getCryptos (config, machineList) {
|
||||||
const scopes = allScopes(['global'], allMachineScopes(machineList, 'both'))
|
const scopes = allScopes(['global'], allMachineScopes(machineList, 'both'))
|
||||||
const scoped = scope => configManager.scopedValue(scope[0], scope[1], 'cryptoCurrencies', config)
|
const scoped = scope => configManager.scopedValue(scope[0], scope[1], 'cryptoCurrencies', config)
|
||||||
|
|
@ -112,60 +67,9 @@ function getField (schema, group, fieldCode) {
|
||||||
const fetchMachines = () => machines.getMachines()
|
const fetchMachines = () => machines.getMachines()
|
||||||
.then(machineList => machineList.map(r => r.deviceId))
|
.then(machineList => machineList.map(r => r.deviceId))
|
||||||
|
|
||||||
function validateFieldParameter (value, validator) {
|
|
||||||
switch (validator.code) {
|
|
||||||
case 'required':
|
|
||||||
return true // We don't validate this here
|
|
||||||
case 'min':
|
|
||||||
return value >= validator.min
|
|
||||||
case 'max':
|
|
||||||
return value <= validator.max
|
|
||||||
default:
|
|
||||||
throw new Error('Unknown validation type: ' + validator.code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validates specific field properties other than required property
|
|
||||||
function enforceValidConfigParameters (fieldInstances) {
|
|
||||||
return fetchSchema()
|
|
||||||
.then(schema => {
|
|
||||||
const pickField = fieldCode => schema.fields.find(r => r.code === fieldCode)
|
|
||||||
|
|
||||||
return fieldInstances.every(fieldInstance => {
|
|
||||||
const fieldCode = fieldInstance.fieldLocator.code
|
|
||||||
const field = pickField(fieldCode)
|
|
||||||
const fieldValue = fieldInstance.fieldValue
|
|
||||||
|
|
||||||
const isValid = field.fieldValidation
|
|
||||||
.every(validator => validateFieldParameter(fieldValue.value, validator))
|
|
||||||
|
|
||||||
if (isValid) return true
|
|
||||||
|
|
||||||
throw new Error('Invalid config value')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateConfig (config) {
|
|
||||||
return Promise.all([fetchSchema(), fetchMachines()])
|
|
||||||
.then(([schema, machineList]) => {
|
|
||||||
const cryptos = getCryptos(config, machineList)
|
|
||||||
return schema.groups.filter(group => {
|
|
||||||
return group.fields.some(fieldCode => {
|
|
||||||
const field = getField(schema, group, fieldCode)
|
|
||||||
if (!field.fieldValidation.find(r => r.code === 'required')) return false
|
|
||||||
|
|
||||||
const refFields = (field.enabledIf || []).map(R.curry(getField)(schema, null))
|
|
||||||
return !satisfiesRequire(config, cryptos, machineList, field, refFields)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(arr => arr.map(r => r.code))
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateCurrentConfig () {
|
function validateCurrentConfig () {
|
||||||
return fetchConfig()
|
return fetchConfig()
|
||||||
.then(validateConfig)
|
.then(configValidate.validateRequires)
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchConfigGroup (code) {
|
function fetchConfigGroup (code) {
|
||||||
|
|
@ -245,6 +149,7 @@ function fetchData () {
|
||||||
{code: 'bitgo', display: 'BitGo', class: 'wallet', cryptos: ['BTC']},
|
{code: 'bitgo', display: 'BitGo', class: 'wallet', cryptos: ['BTC']},
|
||||||
{code: 'geth', display: 'geth', class: 'wallet', cryptos: ['ETH']},
|
{code: 'geth', display: 'geth', class: 'wallet', cryptos: ['ETH']},
|
||||||
{code: 'mock-wallet', display: 'Mock wallet', class: 'wallet', cryptos: ['BTC', 'ETH']},
|
{code: 'mock-wallet', display: 'Mock wallet', class: 'wallet', cryptos: ['BTC', 'ETH']},
|
||||||
|
{code: 'no-exchange', display: 'No exchange', class: 'exchange', cryptos: ['BTC', 'ETH']},
|
||||||
{code: 'mock-exchange', display: 'Mock exchange', class: 'exchange', cryptos: ['BTC', 'ETH']},
|
{code: 'mock-exchange', display: 'Mock exchange', class: 'exchange', cryptos: ['BTC', 'ETH']},
|
||||||
{code: 'mock-sms', display: 'Mock SMS', class: 'sms'},
|
{code: 'mock-sms', display: 'Mock SMS', class: 'sms'},
|
||||||
{code: 'mock-id-verify', display: 'Mock ID verifier', class: 'idVerifier'},
|
{code: 'mock-id-verify', display: 'Mock ID verifier', class: 'idVerifier'},
|
||||||
|
|
@ -258,7 +163,7 @@ function fetchData () {
|
||||||
function saveConfigGroup (results) {
|
function saveConfigGroup (results) {
|
||||||
if (results.values.length === 0) return fetchConfigGroup(results.groupCode)
|
if (results.values.length === 0) return fetchConfigGroup(results.groupCode)
|
||||||
|
|
||||||
return enforceValidConfigParameters(results.values)
|
return configValidate.ensureConstraints(results.values)
|
||||||
.then(fetchConfig)
|
.then(fetchConfig)
|
||||||
.then(oldValues => {
|
.then(oldValues => {
|
||||||
results.values.forEach(newValue => {
|
results.values.forEach(newValue => {
|
||||||
|
|
|
||||||
|
|
@ -45,19 +45,26 @@ function status () {
|
||||||
const lastPing = statusRow && age.humanize()
|
const lastPing = statusRow && age.humanize()
|
||||||
|
|
||||||
return settingsLoader.loadLatest()
|
return settingsLoader.loadLatest()
|
||||||
|
.catch(() => null)
|
||||||
.then(settings => {
|
.then(settings => {
|
||||||
return ticker.getRates(settings, 'USD', 'BTC')
|
return getRates(settings)
|
||||||
.then(ratesRec => {
|
.then(rates => ({up, lastPing, rates, machineStatus}))
|
||||||
const rates = [{
|
|
||||||
crypto: 'BTC',
|
|
||||||
bid: parseFloat(ratesRec.rates.bid),
|
|
||||||
ask: parseFloat(ratesRec.rates.ask)
|
|
||||||
}]
|
|
||||||
return {up, lastPing, rates, machineStatus}
|
|
||||||
})
|
|
||||||
.catch(() => ({up, lastPing, rates: [], machineStatus}))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRates (settings) {
|
||||||
|
if (!settings) return Promise.resolve([])
|
||||||
|
|
||||||
|
return ticker.getRates(settings, 'USD', 'BTC')
|
||||||
|
.then(ratesRec => {
|
||||||
|
return [{
|
||||||
|
crypto: 'BTC',
|
||||||
|
bid: parseFloat(ratesRec.rates.bid),
|
||||||
|
ask: parseFloat(ratesRec.rates.ask)
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
.catch(() => [])
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {status}
|
module.exports = {status}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ const argv = require('minimist')(process.argv.slice(2))
|
||||||
const routes = require('./routes')
|
const routes = require('./routes')
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
const poller = require('./poller')
|
const poller = require('./poller')
|
||||||
const verifySchema = require('./verify-schema')
|
|
||||||
const settingsLoader = require('./settings-loader')
|
const settingsLoader = require('./settings-loader')
|
||||||
const options = require('./options')
|
const options = require('./options')
|
||||||
|
|
||||||
|
|
@ -33,8 +32,7 @@ function run () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function runOnce () {
|
function runOnce () {
|
||||||
return verifySchema.valid()
|
return settingsLoader.loadLatest()
|
||||||
.then(() => settingsLoader.loadLatest())
|
|
||||||
.then(settings => {
|
.then(settings => {
|
||||||
poller.start(settings)
|
poller.start(settings)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ function post (tx, pi) {
|
||||||
const isolationLevel = pgp.txMode.isolationLevel
|
const isolationLevel = pgp.txMode.isolationLevel
|
||||||
const tmSRD = new TransactionMode({tiLevel: isolationLevel.serializable})
|
const tmSRD = new TransactionMode({tiLevel: isolationLevel.serializable})
|
||||||
|
|
||||||
console.log('DEBUG502: %j', tx)
|
|
||||||
function transaction (t) {
|
function transaction (t) {
|
||||||
const sql = 'select * from cash_in_txs where id=$1'
|
const sql = 'select * from cash_in_txs where id=$1'
|
||||||
const sql2 = 'select * from bills where cash_in_txs_id=$1'
|
const sql2 = 'select * from bills where cash_in_txs_id=$1'
|
||||||
|
|
@ -131,7 +130,6 @@ function insertNewBills (billRows, tx) {
|
||||||
|
|
||||||
function upsert (oldTx, tx) {
|
function upsert (oldTx, tx) {
|
||||||
if (!oldTx) {
|
if (!oldTx) {
|
||||||
console.log('DEBUG500: %j', tx)
|
|
||||||
return insert(tx)
|
return insert(tx)
|
||||||
.then(newTx => [oldTx, newTx])
|
.then(newTx => [oldTx, newTx])
|
||||||
}
|
}
|
||||||
|
|
@ -160,9 +158,7 @@ function update (tx, changes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerTrades (pi, txVector) {
|
function registerTrades (pi, txVector) {
|
||||||
console.log('DEBUG400')
|
|
||||||
const newBills = _.last(txVector)
|
const newBills = _.last(txVector)
|
||||||
console.log('DEBUG401: %j', newBills)
|
|
||||||
_.forEach(bill => pi.buy(bill), newBills)
|
_.forEach(bill => pi.buy(bill), newBills)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ function httpError (msg, code) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function post (tx, pi) {
|
function post (tx, pi) {
|
||||||
console.log('DEBUG101: %j', tx)
|
|
||||||
const TransactionMode = pgp.txMode.TransactionMode
|
const TransactionMode = pgp.txMode.TransactionMode
|
||||||
const isolationLevel = pgp.txMode.isolationLevel
|
const isolationLevel = pgp.txMode.isolationLevel
|
||||||
const tmSRD = new TransactionMode({tiLevel: isolationLevel.serializable})
|
const tmSRD = new TransactionMode({tiLevel: isolationLevel.serializable})
|
||||||
|
|
@ -104,7 +103,6 @@ function logAction (action, _rec, tx) {
|
||||||
const rec = _.assign(_rec, {action, tx_id: tx.id, redeem: !!tx.redeem})
|
const rec = _.assign(_rec, {action, tx_id: tx.id, redeem: !!tx.redeem})
|
||||||
const sql = pgp.helpers.insert(rec, null, 'cash_out_actions')
|
const sql = pgp.helpers.insert(rec, null, 'cash_out_actions')
|
||||||
|
|
||||||
console.log('DEBUG110: %j', sql)
|
|
||||||
return db.none(sql)
|
return db.none(sql)
|
||||||
.then(_.constant(tx))
|
.then(_.constant(tx))
|
||||||
}
|
}
|
||||||
|
|
@ -204,9 +202,7 @@ function update (tx, changes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextHd (isHd, tx) {
|
function nextHd (isHd, tx) {
|
||||||
console.log('DEBUG160: %s', isHd)
|
|
||||||
if (!isHd) return Promise.resolve(tx)
|
if (!isHd) return Promise.resolve(tx)
|
||||||
console.log('DEBUG161: %s', isHd)
|
|
||||||
|
|
||||||
return db.one("select nextval('hd_indices_seq') as hd_index")
|
return db.one("select nextval('hd_indices_seq') as hd_index")
|
||||||
.then(row => _.set('hdIndex', row.hd_index, tx))
|
.then(row => _.set('hdIndex', row.hd_index, tx))
|
||||||
|
|
@ -257,9 +253,7 @@ function preProcess (oldTx, newTx, pi) {
|
||||||
return logAction(updatedTx.status, rec, updatedTx)
|
return logAction(updatedTx.status, rec, updatedTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('DEBUG120: %j', [oldTx, updatedTx])
|
|
||||||
if (!oldTx.dispenseConfirmed && updatedTx.dispenseConfirmed) {
|
if (!oldTx.dispenseConfirmed && updatedTx.dispenseConfirmed) {
|
||||||
console.log('DEBUG121')
|
|
||||||
return logDispense(updatedTx)
|
return logDispense(updatedTx)
|
||||||
.then(updateCassettes(updatedTx))
|
.then(updateCassettes(updatedTx))
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +274,6 @@ function postProcess (txVector, pi) {
|
||||||
const [oldTx, newTx] = txVector
|
const [oldTx, newTx] = txVector
|
||||||
|
|
||||||
if (newTx.dispense && !oldTx.dispense) {
|
if (newTx.dispense && !oldTx.dispense) {
|
||||||
console.log('DEBUG130')
|
|
||||||
return pi.buildCassettes()
|
return pi.buildCassettes()
|
||||||
.then(cassettes => {
|
.then(cassettes => {
|
||||||
pi.sell(newTx)
|
pi.sell(newTx)
|
||||||
|
|
|
||||||
150
lib/config-validate.js
Normal file
150
lib/config-validate.js
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
|
const configManager = require('./config-manager')
|
||||||
|
const machines = require('./admin/machines')
|
||||||
|
const schema = require('../lamassu-schema.json')
|
||||||
|
|
||||||
|
function allScopes (cryptoScopes, machineScopes) {
|
||||||
|
const scopes = []
|
||||||
|
cryptoScopes.forEach(c => {
|
||||||
|
machineScopes.forEach(m => scopes.push([c, m]))
|
||||||
|
})
|
||||||
|
|
||||||
|
return scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
function allCryptoScopes (cryptos, cryptoScope) {
|
||||||
|
const cryptoScopes = []
|
||||||
|
|
||||||
|
if (cryptoScope === 'global' || cryptoScope === 'both') cryptoScopes.push('global')
|
||||||
|
if (cryptoScope === 'specific' || cryptoScope === 'both') cryptos.forEach(r => cryptoScopes.push(r))
|
||||||
|
|
||||||
|
return cryptoScopes
|
||||||
|
}
|
||||||
|
|
||||||
|
function allMachineScopes (machineList, machineScope) {
|
||||||
|
const machineScopes = []
|
||||||
|
|
||||||
|
if (machineScope === 'global' || machineScope === 'both') machineScopes.push('global')
|
||||||
|
if (machineScope === 'specific' || machineScope === 'both') machineList.forEach(r => machineScopes.push(r))
|
||||||
|
|
||||||
|
return machineScopes
|
||||||
|
}
|
||||||
|
|
||||||
|
function satisfiesRequire (config, cryptos, machineList, field, refFields) {
|
||||||
|
const fieldCode = field.code
|
||||||
|
|
||||||
|
const scopes = allScopes(
|
||||||
|
allCryptoScopes(cryptos, field.cryptoScope),
|
||||||
|
allMachineScopes(machineList, field.machineScope)
|
||||||
|
)
|
||||||
|
|
||||||
|
return scopes.every(scope => {
|
||||||
|
const isEnabled = () => refFields.some(refField => {
|
||||||
|
return isScopeEnabled(config, cryptos, machineList, refField, scope)
|
||||||
|
})
|
||||||
|
|
||||||
|
const isBlank = () => _.isNil(configManager.scopedValue(scope[0], scope[1], fieldCode, config))
|
||||||
|
const isRequired = refFields.length === 0 || isEnabled()
|
||||||
|
|
||||||
|
return isRequired ? !isBlank() : true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function isScopeEnabled (config, cryptos, machineList, refField, scope) {
|
||||||
|
const [cryptoScope, machineScope] = scope
|
||||||
|
const candidateCryptoScopes = cryptoScope === 'global'
|
||||||
|
? allCryptoScopes(cryptos, refField.cryptoScope)
|
||||||
|
: [cryptoScope]
|
||||||
|
|
||||||
|
const candidateMachineScopes = machineScope === 'global'
|
||||||
|
? allMachineScopes(machineList, refField.machineScope)
|
||||||
|
: [ machineScope ]
|
||||||
|
|
||||||
|
const allRefCandidateScopes = allScopes(candidateCryptoScopes, candidateMachineScopes)
|
||||||
|
const getFallbackValue = scope => configManager.scopedValue(scope[0], scope[1], refField.code, config)
|
||||||
|
const values = allRefCandidateScopes.map(getFallbackValue)
|
||||||
|
|
||||||
|
return values.some(r => r)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCryptos (config, machineList) {
|
||||||
|
const scopes = allScopes(['global'], allMachineScopes(machineList, 'both'))
|
||||||
|
const scoped = scope => configManager.scopedValue(scope[0], scope[1], 'cryptoCurrencies', config)
|
||||||
|
return scopes.reduce((acc, scope) => _.union(acc, scoped(scope)), [])
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGroup (fieldCode) {
|
||||||
|
return _.find(group => _.includes(fieldCode, group.fields), schema.groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getField (group, fieldCode) {
|
||||||
|
if (!group) group = getGroup(fieldCode)
|
||||||
|
const field = _.find(_.matchesProperty('code', fieldCode), schema.fields)
|
||||||
|
return _.merge(_.pick(['cryptoScope', 'machineScope'], group), field)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchMachines = () => machines.getMachines()
|
||||||
|
.then(machineList => machineList.map(r => r.deviceId))
|
||||||
|
|
||||||
|
function validateFieldParameter (value, validator) {
|
||||||
|
switch (validator.code) {
|
||||||
|
case 'required':
|
||||||
|
return true // We don't validate this here
|
||||||
|
case 'min':
|
||||||
|
return value >= validator.min
|
||||||
|
case 'max':
|
||||||
|
return value <= validator.max
|
||||||
|
default:
|
||||||
|
throw new Error('Unknown validation type: ' + validator.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureConstraints (config) {
|
||||||
|
const pickField = fieldCode => schema.fields.find(r => r.code === fieldCode)
|
||||||
|
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
config.every(fieldInstance => {
|
||||||
|
const fieldCode = fieldInstance.fieldLocator.code
|
||||||
|
const field = pickField(fieldCode)
|
||||||
|
const fieldValue = fieldInstance.fieldValue
|
||||||
|
|
||||||
|
const isValid = field.fieldValidation
|
||||||
|
.every(validator => validateFieldParameter(fieldValue.value, validator))
|
||||||
|
|
||||||
|
if (isValid) return true
|
||||||
|
throw new Error('Invalid config value')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateRequires (config) {
|
||||||
|
return fetchMachines()
|
||||||
|
.then(machineList => {
|
||||||
|
const cryptos = getCryptos(config, machineList)
|
||||||
|
|
||||||
|
return schema.groups.filter(group => {
|
||||||
|
return group.fields.some(fieldCode => {
|
||||||
|
const field = getField(group, fieldCode)
|
||||||
|
if (!field.fieldValidation.find(r => r.code === 'required')) return false
|
||||||
|
|
||||||
|
const refFields = _.map(_.partial(getField, null), field.enabledIf)
|
||||||
|
return !satisfiesRequire(config, cryptos, machineList, field, refFields)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(arr => arr.map(r => r.code))
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate (config) {
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => ensureConstraints(config))
|
||||||
|
.then(() => validateRequires(config))
|
||||||
|
.then(arr => {
|
||||||
|
if (arr.length === 0) return config
|
||||||
|
throw new Error('Invalid configuration')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {validate, ensureConstraints, validateRequires}
|
||||||
|
|
@ -2,13 +2,16 @@ const configManager = require('./config-manager')
|
||||||
const ph = require('./plugin-helper')
|
const ph = require('./plugin-helper')
|
||||||
|
|
||||||
function lookupExchange (settings, cryptoCode) {
|
function lookupExchange (settings, cryptoCode) {
|
||||||
return configManager.cryptoScoped(cryptoCode, settings.config).exchange
|
const exchange = configManager.cryptoScoped(cryptoCode, settings.config).exchange
|
||||||
|
if (exchange === 'no-exchange') return null
|
||||||
|
return exchange
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchExchange (settings, cryptoCode) {
|
function fetchExchange (settings, cryptoCode) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const plugin = lookupExchange(settings, cryptoCode)
|
const plugin = lookupExchange(settings, cryptoCode)
|
||||||
|
if (!plugin) throw new Error('No exchange set')
|
||||||
const exchange = ph.load(ph.EXCHANGE, plugin)
|
const exchange = ph.load(ph.EXCHANGE, plugin)
|
||||||
const account = settings.accounts[plugin]
|
const account = settings.accounts[plugin]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ function sendNoAlerts (plugins) {
|
||||||
function checkNotification (plugins) {
|
function checkNotification (plugins) {
|
||||||
return checkStatus(plugins)
|
return checkStatus(plugins)
|
||||||
.then(alertRec => {
|
.then(alertRec => {
|
||||||
console.log('DEBUG445: %j', alertRec)
|
|
||||||
const currentAlertFingerprint = buildAlertFingerprint(alertRec)
|
const currentAlertFingerprint = buildAlertFingerprint(alertRec)
|
||||||
if (!currentAlertFingerprint) {
|
if (!currentAlertFingerprint) {
|
||||||
const inAlert = !!alertFingerprint
|
const inAlert = !!alertFingerprint
|
||||||
|
|
@ -80,7 +79,6 @@ function checkNotification (plugins) {
|
||||||
alertFingerprint = currentAlertFingerprint
|
alertFingerprint = currentAlertFingerprint
|
||||||
lastAlertTime = Date.now()
|
lastAlertTime = Date.now()
|
||||||
|
|
||||||
console.log('DEBUG446: %j', rec)
|
|
||||||
return plugins.sendMessage(rec)
|
return plugins.sendMessage(rec)
|
||||||
})
|
})
|
||||||
.then(results => {
|
.then(results => {
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ function plugins (settings, deviceId) {
|
||||||
function fetchCurrentConfigVersion () {
|
function fetchCurrentConfigVersion () {
|
||||||
const sql = `select id from user_config
|
const sql = `select id from user_config
|
||||||
where type=$1
|
where type=$1
|
||||||
|
and valid
|
||||||
order by id desc
|
order by id desc
|
||||||
limit 1`
|
limit 1`
|
||||||
|
|
||||||
|
|
@ -278,9 +279,7 @@ function plugins (settings, deviceId) {
|
||||||
|
|
||||||
const market = [fiatCode, cryptoCode].join('')
|
const market = [fiatCode, cryptoCode].join('')
|
||||||
|
|
||||||
console.log('DEBUG333')
|
|
||||||
if (!exchange.active(settings, cryptoCode)) return
|
if (!exchange.active(settings, cryptoCode)) return
|
||||||
console.log('DEBUG334')
|
|
||||||
|
|
||||||
logger.debug('[%s] Pushing trade: %d', market, cryptoAtoms)
|
logger.debug('[%s] Pushing trade: %d', market, cryptoAtoms)
|
||||||
if (!tradesQueues[market]) tradesQueues[market] = []
|
if (!tradesQueues[market]) tradesQueues[market] = []
|
||||||
|
|
@ -485,7 +484,6 @@ function plugins (settings, deviceId) {
|
||||||
function sweepHdRow (row) {
|
function sweepHdRow (row) {
|
||||||
const cryptoCode = row.crypto_code
|
const cryptoCode = row.crypto_code
|
||||||
|
|
||||||
console.log('DEBUG200')
|
|
||||||
return wallet.sweep(settings, cryptoCode, row.hd_index)
|
return wallet.sweep(settings, cryptoCode, row.hd_index)
|
||||||
.then(txHash => {
|
.then(txHash => {
|
||||||
if (txHash) {
|
if (txHash) {
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,9 @@ function sendMessage (account, rec) {
|
||||||
from: account.fromNumber
|
from: account.fromNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('DEBUG111: %j', opts)
|
|
||||||
|
|
||||||
return client.sendMessage(opts)
|
return client.sendMessage(opts)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log('DEBUG113: %s', err)
|
|
||||||
if (_.includes(err.code, BAD_NUMBER_CODES)) {
|
if (_.includes(err.code, BAD_NUMBER_CODES)) {
|
||||||
const badNumberError = new Error(err.message)
|
const badNumberError = new Error(err.message)
|
||||||
badNumberError.name = 'BadNumberError'
|
badNumberError.name = 'BadNumberError'
|
||||||
|
|
@ -30,7 +28,6 @@ function sendMessage (account, rec) {
|
||||||
|
|
||||||
throw new Error(err.message)
|
throw new Error(err.message)
|
||||||
})
|
})
|
||||||
.then(_.tap(() => console.log('DEBUG112')))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,6 @@ function getTx (req, res, next) {
|
||||||
|
|
||||||
function getPhoneTx (req, res, next) {
|
function getPhoneTx (req, res, next) {
|
||||||
if (req.query.phone) {
|
if (req.query.phone) {
|
||||||
console.log('DEBUG120: %s', req.query.phone)
|
|
||||||
return helpers.fetchPhoneTx(req.query.phone)
|
return helpers.fetchPhoneTx(req.query.phone)
|
||||||
.then(r => res.json(r))
|
.then(r => res.json(r))
|
||||||
.catch(next)
|
.catch(next)
|
||||||
|
|
@ -322,7 +321,6 @@ let oldVersionId = 'initial'
|
||||||
function populateSettings (req, res, next) {
|
function populateSettings (req, res, next) {
|
||||||
const versionId = req.headers['config-version']
|
const versionId = req.headers['config-version']
|
||||||
if (versionId !== oldVersionId) {
|
if (versionId !== oldVersionId) {
|
||||||
console.log('DEBUG611: %s', versionId)
|
|
||||||
oldVersionId = versionId
|
oldVersionId = versionId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,7 @@ const argv = require('minimist')(process.argv.slice(2))
|
||||||
const pify = require('pify')
|
const pify = require('pify')
|
||||||
|
|
||||||
const db = require('./db')
|
const db = require('./db')
|
||||||
const options = require('./options')
|
const configValidate = require('./config-validate')
|
||||||
const logger = require('./logger')
|
|
||||||
|
|
||||||
const schemaPath = path.resolve(options.lamassuServerPath, 'lamassu-schema.json')
|
|
||||||
const schema = require(schemaPath)
|
|
||||||
|
|
||||||
let settingsCache
|
let settingsCache
|
||||||
|
|
||||||
|
|
@ -69,17 +65,25 @@ function loadConfig (versionId) {
|
||||||
|
|
||||||
const sql = `select data
|
const sql = `select data
|
||||||
from user_config
|
from user_config
|
||||||
where id=$1 and type=$2`
|
where id=$1 and type=$2
|
||||||
|
and valid`
|
||||||
|
|
||||||
return db.oneOrNone(sql, [versionId, 'config'])
|
return db.one(sql, [versionId, 'config'])
|
||||||
.then(row => row ? row.data.config : [])
|
.then(row => row.data.config)
|
||||||
.then(validate)
|
.then(configValidate.validate)
|
||||||
|
.catch(err => {
|
||||||
|
if (err.name === 'QueryResultError') {
|
||||||
|
throw new Error('No such config version: ' + versionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw err
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLatestConfig () {
|
function loadLatestConfig () {
|
||||||
if (argv.fixture) return loadFixture()
|
if (argv.fixture) return loadFixture()
|
||||||
|
|
||||||
const sql = `select data
|
const sql = `select id, valid, data
|
||||||
from user_config
|
from user_config
|
||||||
where type=$1
|
where type=$1
|
||||||
and valid
|
and valid
|
||||||
|
|
@ -88,7 +92,7 @@ function loadLatestConfig () {
|
||||||
|
|
||||||
return db.one(sql, ['config'])
|
return db.one(sql, ['config'])
|
||||||
.then(row => row.data.config)
|
.then(row => row.data.config)
|
||||||
.then(validate)
|
.then(configValidate.validate)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (err.name === 'QueryResultError') {
|
if (err.name === 'QueryResultError') {
|
||||||
throw new Error('lamassu-server is not configured')
|
throw new Error('lamassu-server is not configured')
|
||||||
|
|
@ -98,38 +102,6 @@ function loadLatestConfig () {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkConstraint (entry, constraint) {
|
|
||||||
switch (constraint.code) {
|
|
||||||
case 'min':
|
|
||||||
return entry.fieldValue.value >= constraint.min
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateConstraint (entry, constraint) {
|
|
||||||
const isValid = checkConstraint(entry, constraint)
|
|
||||||
if (!isValid) logger.error(`Validation error: ${entry.fieldLocator.code} [${constraint.code}]`)
|
|
||||||
return isValid
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateEntry (entry) {
|
|
||||||
const fieldCode = entry.fieldLocator.code
|
|
||||||
const schemaEntry = _.find(_.matchesProperty('code', fieldCode), schema.fields)
|
|
||||||
if (!schemaEntry) throw new Error(`Unsupported field: ${fieldCode}`)
|
|
||||||
|
|
||||||
const validations = schemaEntry.fieldValidation
|
|
||||||
return _.every(constraint => validateConstraint(entry, constraint), validations)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValid (config) {
|
|
||||||
return _.every(validateEntry, config)
|
|
||||||
}
|
|
||||||
function validate (config) {
|
|
||||||
if (!isValid(config)) throw new Error('Invalid config')
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadAccounts () {
|
function loadAccounts () {
|
||||||
const toFields = fieldArr => _.fromPairs(_.map(r => [r.code, r.value], fieldArr))
|
const toFields = fieldArr => _.fromPairs(_.map(r => [r.code, r.value], fieldArr))
|
||||||
const toPairs = r => [r.code, toFields(r.fields)]
|
const toPairs = r => [r.code, toFields(r.fields)]
|
||||||
|
|
@ -147,7 +119,10 @@ function settings () {
|
||||||
|
|
||||||
function save (config) {
|
function save (config) {
|
||||||
const sql = 'insert into user_config (type, data, valid) values ($1, $2, $3)'
|
const sql = 'insert into user_config (type, data, valid) values ($1, $2, $3)'
|
||||||
return db.none(sql, ['config', {config}, isValid(config)])
|
|
||||||
|
return configValidate.validate(config)
|
||||||
|
.then(() => db.none(sql, ['config', {config}, true]))
|
||||||
|
.catch(() => db.none(sql, ['config', {config}, false]))
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
const util = require('util')
|
|
||||||
const config = require('./admin/config')
|
|
||||||
|
|
||||||
function valid () {
|
|
||||||
return config.validateCurrentConfig()
|
|
||||||
.then(errors => {
|
|
||||||
if (errors.length === 0) return
|
|
||||||
throw new Error('Schema validation error: ' + util.inspect(errors, {colors: true}))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {valid}
|
|
||||||
|
|
@ -30,7 +30,6 @@ function fetchWallet (settings, cryptoCode) {
|
||||||
.then(hex => {
|
.then(hex => {
|
||||||
const masterSeed = Buffer.from(hex.trim(), 'hex')
|
const masterSeed = Buffer.from(hex.trim(), 'hex')
|
||||||
const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
|
const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
|
||||||
console.log('DEBUG555: %s', plugin)
|
|
||||||
const wallet = ph.load(ph.WALLET, plugin)
|
const wallet = ph.load(ph.WALLET, plugin)
|
||||||
const account = settings.accounts[plugin]
|
const account = settings.accounts[plugin]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32713,7 +32713,7 @@ var _user$project$TransactionTypes$CashOutTxRec = function (a) {
|
||||||
return function (m) {
|
return function (m) {
|
||||||
return function (n) {
|
return function (n) {
|
||||||
return function (o) {
|
return function (o) {
|
||||||
return {id: a, machineName: b, toAddress: c, cryptoAtoms: d, cryptoCode: e, fiat: f, fiatCode: g, status: h, dispensed: i, notified: j, redeemed: k, phone: l, error: m, created: n, confirmed: o};
|
return {id: a, machineName: b, toAddress: c, cryptoAtoms: d, cryptoCode: e, fiat: f, fiatCode: g, status: h, dispense: i, notified: j, redeemed: k, phone: l, error: m, created: n, confirmed: o};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -32833,7 +32833,7 @@ var _user$project$TransactionDecoder$cashOutTxDecoder = A3(
|
||||||
_elm_lang$core$Json_Decode$bool,
|
_elm_lang$core$Json_Decode$bool,
|
||||||
A3(
|
A3(
|
||||||
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
||||||
'dispensed',
|
'dispense',
|
||||||
_elm_lang$core$Json_Decode$bool,
|
_elm_lang$core$Json_Decode$bool,
|
||||||
A3(
|
A3(
|
||||||
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue