improve config loading; remove debug

This commit is contained in:
Josh Harvey 2017-04-25 02:25:32 +03:00
parent 5f0b70ca42
commit 614c64646a
15 changed files with 198 additions and 193 deletions

View file

@ -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 => {

View file

@ -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}

View file

@ -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)

View file

@ -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)
} }

View file

@ -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
View 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}

View file

@ -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]

View file

@ -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 => {

View file

@ -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) {

View file

@ -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 = {

View file

@ -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
} }

View file

@ -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 = {

View file

@ -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}

View file

@ -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]

View file

@ -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,