WIP
This commit is contained in:
parent
a35e9d2d44
commit
340ad2b518
8 changed files with 129 additions and 38 deletions
|
|
@ -11,10 +11,13 @@ function machinesLastPing () {
|
||||||
const sql = `select name, min(extract(epoch from (now() - machine_events.created))) as age
|
const sql = `select name, min(extract(epoch from (now() - machine_events.created))) as age
|
||||||
from machine_events, devices
|
from machine_events, devices
|
||||||
where machine_events.device_id = devices.device_id
|
where machine_events.device_id = devices.device_id
|
||||||
|
and devices.paired
|
||||||
group by name`
|
group by name`
|
||||||
|
|
||||||
return db.any(sql)
|
return db.any(sql)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
|
if (r.length === 0) return 'No paired machines'
|
||||||
|
|
||||||
const downRows = r.filter(row => row.age > CONSIDERED_UP_SECS)
|
const downRows = r.filter(row => row.age > CONSIDERED_UP_SECS)
|
||||||
if (downRows.length === 0) return 'All machines are up'
|
if (downRows.length === 0) return 'All machines are up'
|
||||||
|
|
||||||
|
|
@ -54,6 +57,7 @@ function status () {
|
||||||
}]
|
}]
|
||||||
return {up, lastPing, rates, machineStatus}
|
return {up, lastPing, rates, machineStatus}
|
||||||
})
|
})
|
||||||
|
.catch(() => ({up, lastPing, rates: [], machineStatus}))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ function runOnce () {
|
||||||
? http.createServer(routes.app)
|
? http.createServer(routes.app)
|
||||||
: https.createServer(httpsServerOptions, routes.app)
|
: https.createServer(httpsServerOptions, routes.app)
|
||||||
|
|
||||||
const port = 3000
|
const port = argv.port || 3000
|
||||||
const localPort = 3030
|
const localPort = 3030
|
||||||
const localServer = http.createServer(routes.localApp)
|
const localServer = http.createServer(routes.localApp)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,9 @@ function plugins (settings, deviceId) {
|
||||||
|
|
||||||
function buildCartridges () {
|
function buildCartridges () {
|
||||||
const config = configManager.machineScoped(deviceId, settings.config)
|
const config = configManager.machineScoped(deviceId, settings.config)
|
||||||
|
|
||||||
|
if (!config.cashOutEnabled) return Promise.resolve()
|
||||||
|
|
||||||
const cartridges = [ config.topCashOutDenomination,
|
const cartridges = [ config.topCashOutDenomination,
|
||||||
config.bottomCashOutDenomination ]
|
config.bottomCashOutDenomination ]
|
||||||
const virtualCartridges = [config.virtualCashOutDenomination]
|
const virtualCartridges = [config.virtualCashOutDenomination]
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ const db = require('./db')
|
||||||
const dbm = require('./postgresql_interface')
|
const dbm = require('./postgresql_interface')
|
||||||
const T = require('./time')
|
const T = require('./time')
|
||||||
const BN = require('./bn')
|
const BN = require('./bn')
|
||||||
|
const settingsLoader = require('./settings-loader')
|
||||||
|
|
||||||
const TRANSACTION_EXPIRATION = 2 * T.days
|
const TRANSACTION_EXPIRATION = 2 * T.days
|
||||||
|
|
||||||
|
|
@ -89,4 +90,27 @@ function updateDeviceConfigVersion (versionId) {
|
||||||
return db.none('update devices set user_config_id=$1', [versionId])
|
return db.none('update devices set user_config_id=$1', [versionId])
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {stateChange, fetchPhoneTx, fetchStatusTx, updateDeviceConfigVersion}
|
function updateMachineDefaults (deviceId) {
|
||||||
|
const newFields = [{
|
||||||
|
fieldLocator: {
|
||||||
|
fieldScope: {
|
||||||
|
crypto: 'global',
|
||||||
|
machine: deviceId
|
||||||
|
},
|
||||||
|
code: 'cashOutEnabled',
|
||||||
|
fieldType: 'onOff',
|
||||||
|
fieldClass: null
|
||||||
|
},
|
||||||
|
fieldValue: {
|
||||||
|
fieldType: 'onOff',
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
return settingsLoader.loadLatest()
|
||||||
|
.then(settings => {
|
||||||
|
return settingsLoader.save({config: settingsLoader.mergeValues(settings.config, newFields)})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {stateChange, fetchPhoneTx, fetchStatusTx, updateDeviceConfigVersion, updateMachineDefaults}
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,11 @@ function pair (req, res, next) {
|
||||||
|
|
||||||
return pairing.pair(token, deviceId)
|
return pairing.pair(token, deviceId)
|
||||||
.then(valid => {
|
.then(valid => {
|
||||||
if (valid) return res.end()
|
if (valid) {
|
||||||
|
return helpers.updateMachineDefaults(deviceId)
|
||||||
|
.then(() => res.json({status: 'paired'}))
|
||||||
|
}
|
||||||
|
|
||||||
throw httpError('Pairing failed')
|
throw httpError('Pairing failed')
|
||||||
})
|
})
|
||||||
.catch(next)
|
.catch(next)
|
||||||
|
|
|
||||||
|
|
@ -6,27 +6,68 @@ 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 schema = require('../lamassu-schema.json')
|
||||||
|
|
||||||
|
const mapWithKey = _.map.convert({cap: false})
|
||||||
|
|
||||||
let settingsCache
|
let settingsCache
|
||||||
|
|
||||||
|
function expandFixture (fixture) {
|
||||||
|
const deviceId = argv.machine
|
||||||
|
|
||||||
|
function findField (code) {
|
||||||
|
const field = _.find(_.matchesProperty('code', code), schema.fields)
|
||||||
|
const group = _.find(r => _.includes(code, r.fields), schema.groups)
|
||||||
|
|
||||||
|
if (!field || !group) {
|
||||||
|
throw new Error('No such field from fixture: ' + code)
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.merge({cryptoScope: group.cryptoScope, machineScope: group.machineScope}, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
function expand (value, code) {
|
||||||
|
const field = findField(code)
|
||||||
|
|
||||||
|
const machine = field.machineScope === 'specific'
|
||||||
|
? deviceId
|
||||||
|
: 'global'
|
||||||
|
|
||||||
|
const crypto = field.cryptoScope === 'specific'
|
||||||
|
? 'BTC'
|
||||||
|
: 'global'
|
||||||
|
|
||||||
|
return {
|
||||||
|
fieldLocator: {
|
||||||
|
fieldScope: {crypto, machine},
|
||||||
|
code,
|
||||||
|
fieldType: field.fieldType,
|
||||||
|
fieldClass: field.fieldClass
|
||||||
|
},
|
||||||
|
fieldValue: {
|
||||||
|
fieldType: field.fieldType,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapWithKey(expand, fixture)
|
||||||
|
}
|
||||||
|
|
||||||
function loadFixture () {
|
function loadFixture () {
|
||||||
const fixture = argv.fixture
|
const fixture = argv.fixture
|
||||||
const machine = argv.machine
|
const deviceId = argv.machine
|
||||||
|
|
||||||
if (fixture && !machine) throw new Error('Missing --machine parameter for --fixture')
|
if (fixture && !deviceId) throw new Error('Missing --machine parameter for --fixture')
|
||||||
|
|
||||||
const fixturePath = fixture => path.resolve(__dirname, '..', 'test', 'fixtures', fixture + '.json')
|
const fixturePath = fixture => path.resolve(__dirname, '..', 'test', 'fixtures', fixture + '.json')
|
||||||
|
|
||||||
const promise = fixture
|
const promise = fixture
|
||||||
? pify(fs.readFile)(fixturePath(fixture)).then(JSON.parse)
|
? pify(fs.readFile)(fixturePath(fixture)).then(JSON.parse)
|
||||||
: Promise.resolve([])
|
: Promise.resolve({})
|
||||||
|
|
||||||
return promise
|
return promise
|
||||||
.then(values => _.map(v => {
|
.then(expandFixture)
|
||||||
return (v.fieldLocator.fieldScope.machine === 'machine')
|
|
||||||
? _.set('fieldLocator.fieldScope.machine', machine, v)
|
|
||||||
: v
|
|
||||||
}, values))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEquivalentField (a, b) {
|
function isEquivalentField (a, b) {
|
||||||
|
|
@ -60,15 +101,14 @@ function loadLatest () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadConfig (versionId) {
|
function loadConfig (versionId) {
|
||||||
|
if (argv.fixture) return loadFixture()
|
||||||
|
|
||||||
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`
|
||||||
|
|
||||||
return Promise.all([db.oneOrNone(sql, [versionId, 'config']), loadFixture()])
|
return db.oneOrNone(sql, [versionId, 'config'])
|
||||||
.then(([row, fixture]) => {
|
.then(row => row ? row.data.config : [])
|
||||||
const config = row ? row.data.config : []
|
|
||||||
return mergeValues(config, fixture)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLatestConfig () {
|
function loadLatestConfig () {
|
||||||
|
|
@ -78,11 +118,8 @@ function loadLatestConfig () {
|
||||||
order by id desc
|
order by id desc
|
||||||
limit 1`
|
limit 1`
|
||||||
|
|
||||||
return Promise.all([db.oneOrNone(sql, ['config']), loadFixture()])
|
return db.oneOrNone(sql, ['config'])
|
||||||
.then(([row, fixture]) => {
|
.then(row => row ? row.data.config : [])
|
||||||
const config = row ? row.data.config : []
|
|
||||||
return mergeValues(config, fixture)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAccounts () {
|
function loadAccounts () {
|
||||||
|
|
|
||||||
|
|
@ -24292,7 +24292,7 @@ var _user$project$TransactionTypes$CashInTxRec = function (a) {
|
||||||
return function (i) {
|
return function (i) {
|
||||||
return function (j) {
|
return function (j) {
|
||||||
return function (k) {
|
return function (k) {
|
||||||
return {id: a, machineName: b, toAddress: c, cryptoAtoms: d, cryptoCode: e, fiat: f, currencyCode: g, txHash: h, phone: i, error: j, created: k};
|
return {id: a, machineName: b, toAddress: c, cryptoAtoms: d, cryptoCode: e, fiat: f, fiatCode: g, txHash: h, phone: i, error: j, created: k};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -24320,7 +24320,7 @@ var _user$project$TransactionTypes$CashOutTxRec = function (a) {
|
||||||
return function (n) {
|
return function (n) {
|
||||||
return function (o) {
|
return function (o) {
|
||||||
return function (p) {
|
return function (p) {
|
||||||
return {id: a, machineName: b, toAddress: c, cryptoAtoms: d, cryptoCode: e, fiat: f, currencyCode: g, txHash: h, status: i, dispensed: j, notified: k, redeemed: l, phone: m, error: n, created: o, confirmed: p};
|
return {id: a, machineName: b, toAddress: c, cryptoAtoms: d, cryptoCode: e, fiat: f, fiatCode: g, txHash: h, status: i, dispensed: j, notified: k, redeemed: l, phone: m, error: n, created: o, confirmed: p};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -24388,7 +24388,7 @@ var _user$project$TransactionDecoder$cashInTxDecoder = A3(
|
||||||
_elm_lang$core$Json_Decode$nullable(_elm_lang$core$Json_Decode$string),
|
_elm_lang$core$Json_Decode$nullable(_elm_lang$core$Json_Decode$string),
|
||||||
A3(
|
A3(
|
||||||
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
||||||
'currencyCode',
|
'fiatCode',
|
||||||
_elm_lang$core$Json_Decode$string,
|
_elm_lang$core$Json_Decode$string,
|
||||||
A3(
|
A3(
|
||||||
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
||||||
|
|
@ -24453,7 +24453,7 @@ var _user$project$TransactionDecoder$cashOutTxDecoder = A3(
|
||||||
_elm_lang$core$Json_Decode$nullable(_elm_lang$core$Json_Decode$string),
|
_elm_lang$core$Json_Decode$nullable(_elm_lang$core$Json_Decode$string),
|
||||||
A3(
|
A3(
|
||||||
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
||||||
'currencyCode',
|
'fiatCode',
|
||||||
_elm_lang$core$Json_Decode$string,
|
_elm_lang$core$Json_Decode$string,
|
||||||
A3(
|
A3(
|
||||||
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
_NoRedInk$elm_decode_pipeline$Json_Decode_Pipeline$required,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
body {
|
body {
|
||||||
font-family: Brandon Text;
|
font-family: Nunito, sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,14 +45,15 @@ p {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-family: Fira Code;
|
font-family: Inconsolata, monospace;
|
||||||
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lamassuAdminButtonRow {
|
.lamassuAdminButtonRow {
|
||||||
text-align: left;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lamassuAdminButton {
|
.lamassuAdminButton {
|
||||||
|
|
@ -95,10 +96,9 @@ p {
|
||||||
}
|
}
|
||||||
|
|
||||||
.lamassuAdminContent {
|
.lamassuAdminContent {
|
||||||
padding: 20px;
|
margin: 20px;
|
||||||
background-color: #f6f6f4;
|
background-color: #ffffff;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lamassuAdminCryptoTabs {
|
.lamassuAdminCryptoTabs {
|
||||||
|
|
@ -140,6 +140,24 @@ p {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lamassuAdminConfigContainer {
|
||||||
|
padding: 20px 60px;
|
||||||
|
border-radius: 0px 7px 7px 7px;
|
||||||
|
background-color: #f6f6f4;
|
||||||
|
margin: 0 0 10px;
|
||||||
|
animation: fadein 0.8s;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 15em;
|
||||||
|
min-width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lamassuAdminNoInput {
|
||||||
|
font-family: Inconsolata, monospace;
|
||||||
|
color: #5f5f56;
|
||||||
|
font-weight: normal;
|
||||||
|
text-align: left !important;
|
||||||
|
}
|
||||||
|
|
||||||
.lamassuAdminTxTable {
|
.lamassuAdminTxTable {
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
|
|
@ -161,7 +179,7 @@ p {
|
||||||
}
|
}
|
||||||
|
|
||||||
.lamassuAdminTxTable tbody {
|
.lamassuAdminTxTable tbody {
|
||||||
font-family: Fira Code;
|
font-family: Inconsolata, monospace;
|
||||||
color: #5f5f56;
|
color: #5f5f56;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,7 +236,7 @@ p {
|
||||||
|
|
||||||
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminNoOptions {
|
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminNoOptions {
|
||||||
background-color: #fcfcfa;
|
background-color: #fcfcfa;
|
||||||
font-size: 11px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #5f5f56;
|
color: #5f5f56;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
|
@ -274,7 +292,7 @@ p {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
padding: 4px 4px 3px;
|
padding: 4px 4px 3px;
|
||||||
margin: 0 1px;
|
margin: 0 1px;
|
||||||
font-family: Fira Code;
|
font-family: Inconsolata, monospace;
|
||||||
font-size: 70%;
|
font-size: 70%;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
@ -285,8 +303,8 @@ p {
|
||||||
}
|
}
|
||||||
|
|
||||||
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminSingleItemContainer .lamassuAdminSelectedItem {
|
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminSingleItemContainer .lamassuAdminSelectedItem {
|
||||||
font-family: Fira Code;
|
font-family: Inconsolata, monospace;
|
||||||
font-size: 11px;
|
font-size: 14px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -324,8 +342,9 @@ p {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: Fira Code;
|
font-family: Inconsolata, monospace;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
outline: none;
|
outline: none;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
@ -334,7 +353,7 @@ p {
|
||||||
background-color: #fcfcfa;
|
background-color: #fcfcfa;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
font-size: 11px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #5f5f56;
|
color: #5f5f56;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue