format for latest standard
This commit is contained in:
parent
4108efd9c7
commit
c2af183911
77 changed files with 1697 additions and 1693 deletions
|
|
@ -8,22 +8,21 @@ const schemas = ph.loadSchemas()
|
|||
|
||||
function fetchAccounts () {
|
||||
return db.oneOrNone('select data from user_config where type=$1', ['accounts'])
|
||||
.then(row => {
|
||||
|
||||
.then(row => {
|
||||
// Hard code this for now
|
||||
const accounts = [{
|
||||
code: 'blockcypher',
|
||||
display: 'Blockcypher',
|
||||
fields: [
|
||||
{ code: 'confidenceFactor', display: 'Confidence Factor', fieldType: 'integer', required: true, value: 90 }
|
||||
]
|
||||
}]
|
||||
const accounts = [{
|
||||
code: 'blockcypher',
|
||||
display: 'Blockcypher',
|
||||
fields: [
|
||||
{ code: 'confidenceFactor', display: 'Confidence Factor', fieldType: 'integer', required: true, value: 90 }
|
||||
]
|
||||
}]
|
||||
|
||||
return row
|
||||
? Promise.resolve(row.data.accounts)
|
||||
: db.none('insert into user_config (type, data, valid) values ($1, $2, $3)', ['accounts', {accounts}, true])
|
||||
.then(fetchAccounts)
|
||||
})
|
||||
return row
|
||||
? Promise.resolve(row.data.accounts)
|
||||
: db.none('insert into user_config (type, data, valid) values ($1, $2, $3)', ['accounts', {accounts}, true])
|
||||
.then(fetchAccounts)
|
||||
})
|
||||
}
|
||||
|
||||
function selectedAccounts () {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ module.exports = {run}
|
|||
|
||||
function dbNotify () {
|
||||
return got.post('http://localhost:3030/dbChange')
|
||||
.catch(e => console.error('Error: lamassu-server not responding'))
|
||||
.catch(e => console.error('Error: lamassu-server not responding'))
|
||||
}
|
||||
|
||||
const skip = (req, res) => req.path === '/api/status/' && res.statusCode === 200
|
||||
|
|
@ -81,23 +81,23 @@ app.get('/api/totem', (req, res) => {
|
|||
if (!name) return res.status(400).send('Name is required')
|
||||
|
||||
return pairing.totem(hostname, name)
|
||||
.then(totem => res.send(totem))
|
||||
.then(totem => res.send(totem))
|
||||
})
|
||||
|
||||
app.get('/api/accounts', (req, res) => {
|
||||
accounts.selectedAccounts()
|
||||
.then(accounts => res.json({accounts: accounts}))
|
||||
.then(accounts => res.json({accounts: accounts}))
|
||||
})
|
||||
|
||||
app.get('/api/account/:account', (req, res) => {
|
||||
accounts.getAccount(req.params.account)
|
||||
.then(account => res.json(account))
|
||||
.then(account => res.json(account))
|
||||
})
|
||||
|
||||
app.post('/api/account', (req, res) => {
|
||||
return accounts.updateAccount(req.body)
|
||||
.then(account => res.json(account))
|
||||
.then(() => dbNotify())
|
||||
.then(account => res.json(account))
|
||||
.then(() => dbNotify())
|
||||
})
|
||||
|
||||
app.get('/api/config/:config', (req, res) =>
|
||||
|
|
@ -105,133 +105,133 @@ app.get('/api/config/:config', (req, res) =>
|
|||
|
||||
app.post('/api/config', (req, res, next) => {
|
||||
config.saveConfigGroup(req.body)
|
||||
.then(c => res.json(c))
|
||||
.then(() => dbNotify())
|
||||
.catch(next)
|
||||
.then(c => res.json(c))
|
||||
.then(() => dbNotify())
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.get('/api/accounts/account/:account', (req, res) => {
|
||||
accounts.getAccount(req.params.account)
|
||||
.then(r => res.send(r))
|
||||
.then(r => res.send(r))
|
||||
})
|
||||
|
||||
app.get('/api/machines', (req, res) => {
|
||||
machineLoader.getMachineNames()
|
||||
.then(r => res.send({machines: r}))
|
||||
.then(r => res.send({machines: r}))
|
||||
})
|
||||
|
||||
app.post('/api/machines', (req, res) => {
|
||||
machineLoader.setMachine(req.body)
|
||||
.then(() => machineLoader.getMachineNames())
|
||||
.then(r => res.send({machines: r}))
|
||||
.then(() => dbNotify())
|
||||
.then(() => machineLoader.getMachineNames())
|
||||
.then(r => res.send({machines: r}))
|
||||
.then(() => dbNotify())
|
||||
})
|
||||
|
||||
app.get('/api/funding', (req, res) => {
|
||||
return funding.getFunding()
|
||||
.then(r => res.json(r))
|
||||
.then(r => res.json(r))
|
||||
})
|
||||
|
||||
app.get('/api/funding/:cryptoCode', (req, res) => {
|
||||
const cryptoCode = req.params.cryptoCode
|
||||
|
||||
return funding.getFunding(cryptoCode)
|
||||
.then(r => res.json(r))
|
||||
.then(r => res.json(r))
|
||||
})
|
||||
|
||||
app.get('/api/status', (req, res, next) => {
|
||||
return Promise.all([server.status(), config.validateCurrentConfig()])
|
||||
.then(([serverStatus, invalidConfigGroups]) => res.send({
|
||||
server: serverStatus,
|
||||
invalidConfigGroups
|
||||
}))
|
||||
.catch(next)
|
||||
.then(([serverStatus, invalidConfigGroups]) => res.send({
|
||||
server: serverStatus,
|
||||
invalidConfigGroups
|
||||
}))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.get('/api/transactions', (req, res, next) => {
|
||||
return transactions.batch()
|
||||
.then(r => res.send({transactions: r}))
|
||||
.catch(next)
|
||||
.then(r => res.send({transactions: r}))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.get('/api/transaction/:id', (req, res, next) => {
|
||||
return transactions.single(req.params.id)
|
||||
.then(r => {
|
||||
if (!r) return res.status(404).send({Error: 'Not found'})
|
||||
return res.send(r)
|
||||
})
|
||||
.then(r => {
|
||||
if (!r) return res.status(404).send({Error: 'Not found'})
|
||||
return res.send(r)
|
||||
})
|
||||
})
|
||||
|
||||
app.patch('/api/transaction/:id', (req, res, next) => {
|
||||
if (!req.query.cancel) return res.status(400).send({Error: 'Requires cancel'})
|
||||
|
||||
return transactions.cancel(req.params.id)
|
||||
.then(r => {
|
||||
return res.send(r)
|
||||
})
|
||||
.catch(() => res.status(404).send({Error: 'Not found'}))
|
||||
.then(r => {
|
||||
return res.send(r)
|
||||
})
|
||||
.catch(() => res.status(404).send({Error: 'Not found'}))
|
||||
})
|
||||
|
||||
app.get('/api/customers', (req, res, next) => {
|
||||
return customers.batch()
|
||||
.then(r => res.send({customers: r}))
|
||||
.catch(next)
|
||||
.then(r => res.send({customers: r}))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.get('/api/customer/:id', (req, res, next) => {
|
||||
return customers.getById(req.params.id)
|
||||
.then(r => {
|
||||
if (!r) return res.status(404).send({Error: 'Not found'})
|
||||
return res.send(r)
|
||||
})
|
||||
.then(r => {
|
||||
if (!r) return res.status(404).send({Error: 'Not found'})
|
||||
return res.send(r)
|
||||
})
|
||||
})
|
||||
|
||||
app.get('/api/logs/:deviceId', (req, res, next) => {
|
||||
return logs.getMachineLogs(req.params.deviceId)
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.get('/api/logs', (req, res, next) => {
|
||||
return machineLoader.getMachines()
|
||||
.then(machines => {
|
||||
const firstMachine = _.first(machines)
|
||||
if (!firstMachine) return res.status(404).send({Error: 'No machines'})
|
||||
return logs.getMachineLogs(firstMachine.deviceId)
|
||||
.then(r => res.send(r))
|
||||
})
|
||||
.catch(next)
|
||||
.then(machines => {
|
||||
const firstMachine = _.first(machines)
|
||||
if (!firstMachine) return res.status(404).send({Error: 'No machines'})
|
||||
return logs.getMachineLogs(firstMachine.deviceId)
|
||||
.then(r => res.send(r))
|
||||
})
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.get('/api/support_logs', (req, res, next) => {
|
||||
return supportLogs.batch()
|
||||
.then(supportLogs => res.send({ supportLogs }))
|
||||
.catch(next)
|
||||
.then(supportLogs => res.send({ supportLogs }))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.get('/api/support_logs/logs', (req, res, next) => {
|
||||
return supportLogs.get(req.query.supportLogId)
|
||||
.then(log => (!_.isNil(log) && !_.isEmpty(log)) ? log : supportLogs.batch().then(_.first))
|
||||
.then(result => {
|
||||
const log = result || {}
|
||||
return logs.getMachineLogs(log.deviceId, log.timestamp)
|
||||
})
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
.then(log => (!_.isNil(log) && !_.isEmpty(log)) ? log : supportLogs.batch().then(_.first))
|
||||
.then(result => {
|
||||
const log = result || {}
|
||||
return logs.getMachineLogs(log.deviceId, log.timestamp)
|
||||
})
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.post('/api/support_logs', (req, res, next) => {
|
||||
return supportLogs.insert(req.query.deviceId)
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.patch('/api/customer/:id', (req, res, next) => {
|
||||
if (!req.params.id) return res.status(400).send({Error: 'Requires id'})
|
||||
const token = req.token || req.cookies.token
|
||||
return customers.update(req.params.id, req.query, token)
|
||||
.then(r => res.send(r))
|
||||
.catch(() => res.status(404).send({Error: 'Not found'}))
|
||||
.then(r => res.send(r))
|
||||
.catch(() => res.status(404).send({Error: 'Not found'}))
|
||||
})
|
||||
|
||||
app.use((err, req, res, next) => {
|
||||
|
|
@ -253,35 +253,35 @@ function register (req, res, next) {
|
|||
if (!otp) return next()
|
||||
|
||||
return login.register(otp)
|
||||
.then(r => {
|
||||
if (r.expired) return res.status(401).send('OTP expired, generate new registration link')
|
||||
.then(r => {
|
||||
if (r.expired) return res.status(401).send('OTP expired, generate new registration link')
|
||||
|
||||
// Maybe user is using old registration key, attempt to authenticate
|
||||
if (!r.success) return next()
|
||||
// Maybe user is using old registration key, attempt to authenticate
|
||||
if (!r.success) return next()
|
||||
|
||||
const cookieOpts = {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
domain: hostname,
|
||||
sameSite: true,
|
||||
expires: NEVER
|
||||
}
|
||||
const cookieOpts = {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
domain: hostname,
|
||||
sameSite: true,
|
||||
expires: NEVER
|
||||
}
|
||||
|
||||
const token = r.token
|
||||
req.token = token
|
||||
res.cookie('token', token, cookieOpts)
|
||||
next()
|
||||
})
|
||||
const token = r.token
|
||||
req.token = token
|
||||
res.cookie('token', token, cookieOpts)
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
function authenticate (req, res, next) {
|
||||
const token = req.token || req.cookies.token
|
||||
|
||||
return login.authenticate(token)
|
||||
.then(success => {
|
||||
if (!success) return res.status(401).send('Authentication failed')
|
||||
next()
|
||||
})
|
||||
.then(success => {
|
||||
if (!success) return res.status(401).send('Authentication failed')
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', err => {
|
||||
|
|
@ -303,27 +303,27 @@ const wss = new WebSocket.Server({server: webServer})
|
|||
|
||||
function establishSocket (ws, token) {
|
||||
return login.authenticate(token)
|
||||
.then(success => {
|
||||
if (!success) return ws.close(1008, 'Authentication error')
|
||||
.then(success => {
|
||||
if (!success) return ws.close(1008, 'Authentication error')
|
||||
|
||||
const listener = data => {
|
||||
ws.send(JSON.stringify(data))
|
||||
}
|
||||
const listener = data => {
|
||||
ws.send(JSON.stringify(data))
|
||||
}
|
||||
|
||||
// Reauthenticate every once in a while, in case token expired
|
||||
setInterval(() => {
|
||||
return login.authenticate(token)
|
||||
.then(success => {
|
||||
if (!success) {
|
||||
socketEmitter.removeListener('message', listener)
|
||||
ws.close()
|
||||
}
|
||||
})
|
||||
}, REAUTHENTICATE_INTERVAL)
|
||||
// Reauthenticate every once in a while, in case token expired
|
||||
setInterval(() => {
|
||||
return login.authenticate(token)
|
||||
.then(success => {
|
||||
if (!success) {
|
||||
socketEmitter.removeListener('message', listener)
|
||||
ws.close()
|
||||
}
|
||||
})
|
||||
}, REAUTHENTICATE_INTERVAL)
|
||||
|
||||
socketEmitter.on('message', listener)
|
||||
ws.send('Testing123')
|
||||
})
|
||||
socketEmitter.on('message', listener)
|
||||
ws.send('Testing123')
|
||||
})
|
||||
}
|
||||
|
||||
wss.on('connection', ws => {
|
||||
|
|
|
|||
|
|
@ -32,25 +32,25 @@ const certOptions = {
|
|||
|
||||
app.get('/api/support_logs', (req, res, next) => {
|
||||
return supportLogs.batch()
|
||||
.then(supportLogs => res.send({ supportLogs }))
|
||||
.catch(next)
|
||||
.then(supportLogs => res.send({ supportLogs }))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.get('/api/support_logs/logs', (req, res, next) => {
|
||||
return supportLogs.get(req.query.supportLogId)
|
||||
.then(log => (!_.isNil(log) && !_.isEmpty(log)) ? log : supportLogs.batch().then(_.first))
|
||||
.then(result => {
|
||||
const log = result || {}
|
||||
return logs.getUnlimitedMachineLogs(log.deviceId, log.timestamp)
|
||||
})
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
.then(log => (!_.isNil(log) && !_.isEmpty(log)) ? log : supportLogs.batch().then(_.first))
|
||||
.then(result => {
|
||||
const log = result || {}
|
||||
return logs.getUnlimitedMachineLogs(log.deviceId, log.timestamp)
|
||||
})
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
app.post('/api/support_logs', (req, res, next) => {
|
||||
return supportLogs.insert(req.query.deviceId)
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
.then(r => res.send(r))
|
||||
.catch(next)
|
||||
})
|
||||
|
||||
function run (port) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ function fetchSchema () {
|
|||
const schemaPath = path.resolve(options.lamassuServerPath, 'lamassu-schema.json')
|
||||
|
||||
return fs.readFile(schemaPath)
|
||||
.then(JSON.parse)
|
||||
.then(JSON.parse)
|
||||
}
|
||||
|
||||
function fetchConfig () {
|
||||
|
|
@ -27,7 +27,7 @@ function fetchConfig () {
|
|||
order by id desc limit 1`
|
||||
|
||||
return db.oneOrNone(sql, ['config'])
|
||||
.then(row => row ? row.data.config : [])
|
||||
.then(row => row ? row.data.config : [])
|
||||
}
|
||||
|
||||
function allScopes (cryptoScopes, machineScopes) {
|
||||
|
|
@ -65,11 +65,11 @@ function getField (schema, group, fieldCode) {
|
|||
}
|
||||
|
||||
const fetchMachines = () => machineLoader.getMachines()
|
||||
.then(machineList => machineList.map(r => r.deviceId))
|
||||
.then(machineList => machineList.map(r => r.deviceId))
|
||||
|
||||
function validateCurrentConfig () {
|
||||
return fetchConfig()
|
||||
.then(configValidate.validateRequires)
|
||||
.then(configValidate.validateRequires)
|
||||
}
|
||||
|
||||
function decorateEnabledIf (schemaFields, schemaField) {
|
||||
|
|
@ -85,43 +85,43 @@ function decorateEnabledIf (schemaFields, schemaField) {
|
|||
function fetchConfigGroup (code) {
|
||||
const fieldLocatorCodeEq = R.pathEq(['fieldLocator', 'code'])
|
||||
return Promise.all([fetchSchema(), fetchData(), fetchConfig(), fetchMachines()])
|
||||
.then(([schema, data, config, machineList]) => {
|
||||
const groupSchema = schema.groups.find(r => r.code === code)
|
||||
.then(([schema, data, config, machineList]) => {
|
||||
const groupSchema = schema.groups.find(r => r.code === code)
|
||||
|
||||
if (!groupSchema) throw new Error('No such group schema: ' + code)
|
||||
if (!groupSchema) throw new Error('No such group schema: ' + code)
|
||||
|
||||
const schemaFields = groupSchema.fields
|
||||
.map(R.curry(getField)(schema, groupSchema))
|
||||
.map(f => _.assign(f, {
|
||||
fieldEnabledIfAny: f.enabledIfAny || [],
|
||||
fieldEnabledIfAll: f.enabledIfAll || []
|
||||
}))
|
||||
const schemaFields = groupSchema.fields
|
||||
.map(R.curry(getField)(schema, groupSchema))
|
||||
.map(f => _.assign(f, {
|
||||
fieldEnabledIfAny: f.enabledIfAny || [],
|
||||
fieldEnabledIfAll: f.enabledIfAll || []
|
||||
}))
|
||||
|
||||
const candidateFields = [
|
||||
schemaFields.map(R.prop('requiredIf')),
|
||||
schemaFields.map(R.prop('enabledIfAny')),
|
||||
schemaFields.map(R.prop('enabledIfAll')),
|
||||
groupSchema.fields,
|
||||
'fiatCurrency'
|
||||
]
|
||||
const configFields = R.uniq(R.flatten(candidateFields)).filter(R.identity)
|
||||
const candidateFields = [
|
||||
schemaFields.map(R.prop('requiredIf')),
|
||||
schemaFields.map(R.prop('enabledIfAny')),
|
||||
schemaFields.map(R.prop('enabledIfAll')),
|
||||
groupSchema.fields,
|
||||
'fiatCurrency'
|
||||
]
|
||||
const configFields = R.uniq(R.flatten(candidateFields)).filter(R.identity)
|
||||
|
||||
const reducer = (acc, configField) => {
|
||||
return acc.concat(config.filter(fieldLocatorCodeEq(configField)))
|
||||
}
|
||||
const reducer = (acc, configField) => {
|
||||
return acc.concat(config.filter(fieldLocatorCodeEq(configField)))
|
||||
}
|
||||
|
||||
const values = _.map(f => decorateEnabledIf(schema.fields, f), configFields.reduce(reducer, []))
|
||||
const values = _.map(f => decorateEnabledIf(schema.fields, f), configFields.reduce(reducer, []))
|
||||
|
||||
groupSchema.fields = undefined
|
||||
groupSchema.entries = schemaFields
|
||||
groupSchema.fields = undefined
|
||||
groupSchema.entries = schemaFields
|
||||
|
||||
return {
|
||||
schema: groupSchema,
|
||||
values,
|
||||
selectedCryptos: getCryptos(config, machineList),
|
||||
data
|
||||
}
|
||||
})
|
||||
return {
|
||||
schema: groupSchema,
|
||||
values,
|
||||
selectedCryptos: getCryptos(config, machineList),
|
||||
data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function massageCurrencies (currencies) {
|
||||
|
|
@ -154,55 +154,55 @@ const ALL_CRYPTOS = ['BTC', 'ETH', 'LTC', 'DASH', 'ZEC', 'BCH']
|
|||
|
||||
function fetchData () {
|
||||
return machineLoader.getMachineNames()
|
||||
.then(machineList => ({
|
||||
currencies: massageCurrencies(currencies),
|
||||
cryptoCurrencies: [
|
||||
{crypto: 'BTC', display: 'Bitcoin'},
|
||||
{crypto: 'ETH', display: 'Ethereum'},
|
||||
{crypto: 'LTC', display: 'Litecoin'},
|
||||
{crypto: 'DASH', display: 'Dash'},
|
||||
{crypto: 'ZEC', display: 'Zcash'},
|
||||
{crypto: 'BCH', display: 'BCH'}
|
||||
],
|
||||
languages: languages,
|
||||
countries,
|
||||
accounts: [
|
||||
{code: 'bitpay', display: 'Bitpay', class: 'ticker', cryptos: ['BTC', 'BCH']},
|
||||
{code: 'kraken', display: 'Kraken', class: 'ticker', cryptos: ['BTC', 'ETH', 'LTC', 'DASH', 'ZEC', 'BCH']},
|
||||
{code: 'bitstamp', display: 'Bitstamp', class: 'ticker', cryptos: ['BTC', 'ETH', 'LTC', 'BCH']},
|
||||
{code: 'coinbase', display: 'Coinbase', class: 'ticker', cryptos: ['BTC', 'ETH', 'LTC', 'BCH']},
|
||||
{code: 'mock-ticker', display: 'Mock ticker', class: 'ticker', cryptos: ALL_CRYPTOS},
|
||||
{code: 'bitcoind', display: 'bitcoind', class: 'wallet', cryptos: ['BTC']},
|
||||
{code: 'lnd', display: 'Lightning Network', class: 'wallet', cryptos: ['BTC']},
|
||||
{code: 'geth', display: 'geth', class: 'wallet', cryptos: ['ETH']},
|
||||
{code: 'zcashd', display: 'zcashd', class: 'wallet', cryptos: ['ZEC']},
|
||||
{code: 'litecoind', display: 'litecoind', class: 'wallet', cryptos: ['LTC']},
|
||||
{code: 'dashd', display: 'dashd', class: 'wallet', cryptos: ['DASH']},
|
||||
{code: 'bitcoincashd', display: 'bitcoincashd', class: 'wallet', cryptos: ['BCH']},
|
||||
{code: 'bitgo', display: 'BitGo', class: 'wallet', cryptos: ['BTC']},
|
||||
{code: 'bitstamp', display: 'Bitstamp', class: 'exchange', cryptos: ['BTC', 'ETH', 'LTC', 'BCH']},
|
||||
{code: 'kraken', display: 'Kraken', class: 'exchange', cryptos: ['BTC', 'ETH', 'LTC', 'DASH', 'ZEC', 'BCH']},
|
||||
{code: 'mock-wallet', display: 'Mock wallet', class: 'wallet', cryptos: ALL_CRYPTOS},
|
||||
{code: 'no-exchange', display: 'No exchange', class: 'exchange', cryptos: ALL_CRYPTOS},
|
||||
{code: 'mock-exchange', display: 'Mock exchange', class: 'exchange', cryptos: ALL_CRYPTOS},
|
||||
{code: 'mock-sms', display: 'Mock SMS', class: 'sms'},
|
||||
{code: 'mock-id-verify', display: 'Mock ID verifier', class: 'idVerifier'},
|
||||
{code: 'twilio', display: 'Twilio', class: 'sms'},
|
||||
{code: 'mailjet', display: 'Mailjet', class: 'email'},
|
||||
{code: 'all-zero-conf', display: 'Always 0-conf', class: 'zeroConf', cryptos: ['BTC', 'ZEC', 'LTC', 'DASH', 'BCH']},
|
||||
{code: 'no-zero-conf', display: 'Always 1-conf', class: 'zeroConf', cryptos: ALL_CRYPTOS},
|
||||
{code: 'blockcypher', display: 'Blockcypher', class: 'zeroConf', cryptos: ['BTC']},
|
||||
{code: 'mock-zero-conf', display: 'Mock 0-conf', class: 'zeroConf', cryptos: ['BTC', 'ZEC', 'LTC', 'DASH', 'BCH']}
|
||||
],
|
||||
machines: machineList.map(machine => ({machine: machine.deviceId, display: machine.name}))
|
||||
}))
|
||||
.then(machineList => ({
|
||||
currencies: massageCurrencies(currencies),
|
||||
cryptoCurrencies: [
|
||||
{crypto: 'BTC', display: 'Bitcoin'},
|
||||
{crypto: 'ETH', display: 'Ethereum'},
|
||||
{crypto: 'LTC', display: 'Litecoin'},
|
||||
{crypto: 'DASH', display: 'Dash'},
|
||||
{crypto: 'ZEC', display: 'Zcash'},
|
||||
{crypto: 'BCH', display: 'BCH'}
|
||||
],
|
||||
languages: languages,
|
||||
countries,
|
||||
accounts: [
|
||||
{code: 'bitpay', display: 'Bitpay', class: 'ticker', cryptos: ['BTC', 'BCH']},
|
||||
{code: 'kraken', display: 'Kraken', class: 'ticker', cryptos: ['BTC', 'ETH', 'LTC', 'DASH', 'ZEC', 'BCH']},
|
||||
{code: 'bitstamp', display: 'Bitstamp', class: 'ticker', cryptos: ['BTC', 'ETH', 'LTC', 'BCH']},
|
||||
{code: 'coinbase', display: 'Coinbase', class: 'ticker', cryptos: ['BTC', 'ETH', 'LTC', 'BCH']},
|
||||
{code: 'mock-ticker', display: 'Mock ticker', class: 'ticker', cryptos: ALL_CRYPTOS},
|
||||
{code: 'bitcoind', display: 'bitcoind', class: 'wallet', cryptos: ['BTC']},
|
||||
{code: 'lnd', display: 'Lightning Network', class: 'wallet', cryptos: ['BTC']},
|
||||
{code: 'geth', display: 'geth', class: 'wallet', cryptos: ['ETH']},
|
||||
{code: 'zcashd', display: 'zcashd', class: 'wallet', cryptos: ['ZEC']},
|
||||
{code: 'litecoind', display: 'litecoind', class: 'wallet', cryptos: ['LTC']},
|
||||
{code: 'dashd', display: 'dashd', class: 'wallet', cryptos: ['DASH']},
|
||||
{code: 'bitcoincashd', display: 'bitcoincashd', class: 'wallet', cryptos: ['BCH']},
|
||||
{code: 'bitgo', display: 'BitGo', class: 'wallet', cryptos: ['BTC']},
|
||||
{code: 'bitstamp', display: 'Bitstamp', class: 'exchange', cryptos: ['BTC', 'ETH', 'LTC', 'BCH']},
|
||||
{code: 'kraken', display: 'Kraken', class: 'exchange', cryptos: ['BTC', 'ETH', 'LTC', 'DASH', 'ZEC', 'BCH']},
|
||||
{code: 'mock-wallet', display: 'Mock wallet', class: 'wallet', cryptos: ALL_CRYPTOS},
|
||||
{code: 'no-exchange', display: 'No exchange', class: 'exchange', cryptos: ALL_CRYPTOS},
|
||||
{code: 'mock-exchange', display: 'Mock exchange', class: 'exchange', cryptos: ALL_CRYPTOS},
|
||||
{code: 'mock-sms', display: 'Mock SMS', class: 'sms'},
|
||||
{code: 'mock-id-verify', display: 'Mock ID verifier', class: 'idVerifier'},
|
||||
{code: 'twilio', display: 'Twilio', class: 'sms'},
|
||||
{code: 'mailjet', display: 'Mailjet', class: 'email'},
|
||||
{code: 'all-zero-conf', display: 'Always 0-conf', class: 'zeroConf', cryptos: ['BTC', 'ZEC', 'LTC', 'DASH', 'BCH']},
|
||||
{code: 'no-zero-conf', display: 'Always 1-conf', class: 'zeroConf', cryptos: ALL_CRYPTOS},
|
||||
{code: 'blockcypher', display: 'Blockcypher', class: 'zeroConf', cryptos: ['BTC']},
|
||||
{code: 'mock-zero-conf', display: 'Mock 0-conf', class: 'zeroConf', cryptos: ['BTC', 'ZEC', 'LTC', 'DASH', 'BCH']}
|
||||
],
|
||||
machines: machineList.map(machine => ({machine: machine.deviceId, display: machine.name}))
|
||||
}))
|
||||
}
|
||||
|
||||
function saveConfigGroup (results) {
|
||||
if (results.values.length === 0) return fetchConfigGroup(results.groupCode)
|
||||
|
||||
return settingsLoader.modifyConfig(results.values)
|
||||
.then(() => fetchConfigGroup(results.groupCode))
|
||||
.then(() => fetchConfigGroup(results.groupCode))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ function getCryptos (config, machineList) {
|
|||
|
||||
function fetchMachines () {
|
||||
return machineLoader.getMachines()
|
||||
.then(machineList => machineList.map(r => r.deviceId))
|
||||
.then(machineList => machineList.map(r => r.deviceId))
|
||||
}
|
||||
|
||||
function computeCrypto (cryptoCode, _balance) {
|
||||
|
|
@ -55,45 +55,45 @@ function computeFiat (rate, cryptoCode, _balance) {
|
|||
|
||||
function getFunding (_cryptoCode) {
|
||||
return Promise.all([settingsLoader.loadLatest(), fetchMachines()])
|
||||
.then(([settings, machineList]) => {
|
||||
const config = configManager.unscoped(settings.config)
|
||||
const cryptoCodes = getCryptos(settings.config, machineList)
|
||||
const cryptoCode = _cryptoCode || cryptoCodes[0]
|
||||
const fiatCode = config.fiatCurrency
|
||||
const pareCoins = c => _.includes(c.cryptoCode, cryptoCodes)
|
||||
const cryptoCurrencies = coinUtils.cryptoCurrencies()
|
||||
const cryptoDisplays = _.filter(pareCoins, cryptoCurrencies)
|
||||
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
|
||||
.then(([settings, machineList]) => {
|
||||
const config = configManager.unscoped(settings.config)
|
||||
const cryptoCodes = getCryptos(settings.config, machineList)
|
||||
const cryptoCode = _cryptoCode || cryptoCodes[0]
|
||||
const fiatCode = config.fiatCurrency
|
||||
const pareCoins = c => _.includes(c.cryptoCode, cryptoCodes)
|
||||
const cryptoCurrencies = coinUtils.cryptoCurrencies()
|
||||
const cryptoDisplays = _.filter(pareCoins, cryptoCurrencies)
|
||||
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
|
||||
|
||||
if (!cryptoRec) throw new Error(`Unsupported coin: ${cryptoCode}`)
|
||||
if (!cryptoRec) throw new Error(`Unsupported coin: ${cryptoCode}`)
|
||||
|
||||
const promises = [
|
||||
wallet.newFunding(settings, cryptoCode),
|
||||
ticker.getRates(settings, fiatCode, cryptoCode)
|
||||
]
|
||||
const promises = [
|
||||
wallet.newFunding(settings, cryptoCode),
|
||||
ticker.getRates(settings, fiatCode, cryptoCode)
|
||||
]
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(([fundingRec, ratesRec]) => {
|
||||
const rates = ratesRec.rates
|
||||
const rate = (rates.ask.add(rates.bid)).div(2)
|
||||
const fundingConfirmedBalance = fundingRec.fundingConfirmedBalance
|
||||
const fiatConfirmedBalance = computeFiat(rate, cryptoCode, fundingConfirmedBalance)
|
||||
const pending = fundingRec.fundingPendingBalance.sub(fundingConfirmedBalance)
|
||||
const fiatPending = computeFiat(rate, cryptoCode, pending)
|
||||
const fundingAddress = fundingRec.fundingAddress
|
||||
const fundingAddressUrl = coinUtils.buildUrl(cryptoCode, fundingAddress)
|
||||
return Promise.all(promises)
|
||||
.then(([fundingRec, ratesRec]) => {
|
||||
const rates = ratesRec.rates
|
||||
const rate = (rates.ask.add(rates.bid)).div(2)
|
||||
const fundingConfirmedBalance = fundingRec.fundingConfirmedBalance
|
||||
const fiatConfirmedBalance = computeFiat(rate, cryptoCode, fundingConfirmedBalance)
|
||||
const pending = fundingRec.fundingPendingBalance.sub(fundingConfirmedBalance)
|
||||
const fiatPending = computeFiat(rate, cryptoCode, pending)
|
||||
const fundingAddress = fundingRec.fundingAddress
|
||||
const fundingAddressUrl = coinUtils.buildUrl(cryptoCode, fundingAddress)
|
||||
|
||||
return {
|
||||
cryptoCode,
|
||||
cryptoDisplays,
|
||||
fundingAddress,
|
||||
fundingAddressUrl,
|
||||
confirmedBalance: computeCrypto(cryptoCode, fundingConfirmedBalance).toFormat(5),
|
||||
pending: computeCrypto(cryptoCode, pending).toFormat(5),
|
||||
fiatConfirmedBalance: fiatConfirmedBalance.toFormat(2),
|
||||
fiatPending: fiatPending.toFormat(2),
|
||||
fiatCode
|
||||
}
|
||||
return {
|
||||
cryptoCode,
|
||||
cryptoDisplays,
|
||||
fundingAddress,
|
||||
fundingAddressUrl,
|
||||
confirmedBalance: computeCrypto(cryptoCode, fundingConfirmedBalance).toFormat(5),
|
||||
pending: computeCrypto(cryptoCode, pending).toFormat(5),
|
||||
fiatConfirmedBalance: fiatConfirmedBalance.toFormat(2),
|
||||
fiatPending: fiatPending.toFormat(2),
|
||||
fiatCode
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ function generateOTP (name) {
|
|||
const sql = 'insert into one_time_passes (token, name) values ($1, $2)'
|
||||
|
||||
return db.none(sql, [otp, name])
|
||||
.then(() => otp)
|
||||
.then(() => otp)
|
||||
}
|
||||
|
||||
function validateOTP (otp) {
|
||||
|
|
@ -17,22 +17,22 @@ function validateOTP (otp) {
|
|||
returning name, created < now() - interval '1 hour' as expired`
|
||||
|
||||
return db.one(sql, [otp])
|
||||
.then(r => ({success: !r.expired, expired: r.expired, name: r.name}))
|
||||
.catch(() => ({success: false, expired: false}))
|
||||
.then(r => ({success: !r.expired, expired: r.expired, name: r.name}))
|
||||
.catch(() => ({success: false, expired: false}))
|
||||
}
|
||||
|
||||
function register (otp) {
|
||||
return validateOTP(otp)
|
||||
.then(r => {
|
||||
if (!r.success) return r
|
||||
.then(r => {
|
||||
if (!r.success) return r
|
||||
|
||||
const token = crypto.randomBytes(32).toString('hex')
|
||||
const sql = 'insert into user_tokens (token, name) values ($1, $2)'
|
||||
const token = crypto.randomBytes(32).toString('hex')
|
||||
const sql = 'insert into user_tokens (token, name) values ($1, $2)'
|
||||
|
||||
return db.none(sql, [token, r.name])
|
||||
.then(() => ({success: true, token: token}))
|
||||
})
|
||||
.catch(() => ({success: false, expired: false}))
|
||||
return db.none(sql, [token, r.name])
|
||||
.then(() => ({success: true, token: token}))
|
||||
})
|
||||
.catch(() => ({success: false, expired: false}))
|
||||
}
|
||||
|
||||
function authenticate (token) {
|
||||
|
|
|
|||
|
|
@ -17,17 +17,17 @@ function totem (hostname, name) {
|
|||
const caPath = options.caPath
|
||||
|
||||
return readFile(caPath)
|
||||
.then(data => {
|
||||
const caHash = crypto.createHash('sha256').update(data).digest()
|
||||
const token = crypto.randomBytes(32)
|
||||
const hexToken = token.toString('hex')
|
||||
const caHexToken = crypto.createHash('sha256').update(hexToken).digest('hex')
|
||||
const buf = Buffer.concat([caHash, token, Buffer.from(hostname)])
|
||||
const sql = 'insert into pairing_tokens (token, name) values ($1, $3), ($2, $3)'
|
||||
.then(data => {
|
||||
const caHash = crypto.createHash('sha256').update(data).digest()
|
||||
const token = crypto.randomBytes(32)
|
||||
const hexToken = token.toString('hex')
|
||||
const caHexToken = crypto.createHash('sha256').update(hexToken).digest('hex')
|
||||
const buf = Buffer.concat([caHash, token, Buffer.from(hostname)])
|
||||
const sql = 'insert into pairing_tokens (token, name) values ($1, $3), ($2, $3)'
|
||||
|
||||
return db.none(sql, [hexToken, caHexToken, name])
|
||||
.then(() => bsAlpha.encode(buf))
|
||||
})
|
||||
return db.none(sql, [hexToken, caHexToken, name])
|
||||
.then(() => bsAlpha.encode(buf))
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {totem, unpair}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ const CONSIDERED_UP_SECS = 30
|
|||
|
||||
function checkWasConfigured () {
|
||||
return settingsLoader.loadLatest()
|
||||
.then(() => true)
|
||||
.catch(() => false)
|
||||
.then(() => true)
|
||||
.catch(() => false)
|
||||
}
|
||||
|
||||
function machinesLastPing () {
|
||||
|
|
@ -21,28 +21,28 @@ function machinesLastPing () {
|
|||
group by device_id`
|
||||
|
||||
return Promise.all([machineLoader.getMachineNames(), db.any(sql)])
|
||||
.then(([machines, events]) => {
|
||||
if (machines.length === 0) return 'No paired machines'
|
||||
.then(([machines, events]) => {
|
||||
if (machines.length === 0) return 'No paired machines'
|
||||
|
||||
const addName = event => {
|
||||
const machine = _.find(['deviceId', event.deviceId], machines)
|
||||
if (!machine) return null
|
||||
return _.set('name', machine.name, event)
|
||||
}
|
||||
const addName = event => {
|
||||
const machine = _.find(['deviceId', event.deviceId], machines)
|
||||
if (!machine) return null
|
||||
return _.set('name', machine.name, event)
|
||||
}
|
||||
|
||||
const mapper = _.flow(_.filter(row => row.age > CONSIDERED_UP_SECS), _.map(addName), _.compact)
|
||||
const downRows = mapper(events)
|
||||
const mapper = _.flow(_.filter(row => row.age > CONSIDERED_UP_SECS), _.map(addName), _.compact)
|
||||
const downRows = mapper(events)
|
||||
|
||||
if (downRows.length === 0) return 'All machines are up'
|
||||
if (downRows.length === 0) return 'All machines are up'
|
||||
|
||||
if (downRows.length === 1) {
|
||||
const row = downRows[0]
|
||||
const age = moment.duration(row.age, 'seconds')
|
||||
return `${row.name} down for ${age.humanize()}`
|
||||
}
|
||||
if (downRows.length === 1) {
|
||||
const row = downRows[0]
|
||||
const age = moment.duration(row.age, 'seconds')
|
||||
return `${row.name} down for ${age.humanize()}`
|
||||
}
|
||||
|
||||
return 'Multiple machines down'
|
||||
})
|
||||
return 'Multiple machines down'
|
||||
})
|
||||
}
|
||||
|
||||
function status () {
|
||||
|
|
@ -53,32 +53,32 @@ function status () {
|
|||
limit 1`
|
||||
|
||||
return Promise.all([checkWasConfigured(), db.oneOrNone(sql, ['ping']), machinesLastPing()])
|
||||
.then(([wasConfigured, statusRow, machineStatus]) => {
|
||||
const age = statusRow && moment.duration(statusRow.age, 'seconds')
|
||||
const up = statusRow ? statusRow.age < CONSIDERED_UP_SECS : false
|
||||
const lastPing = statusRow && age.humanize()
|
||||
.then(([wasConfigured, statusRow, machineStatus]) => {
|
||||
const age = statusRow && moment.duration(statusRow.age, 'seconds')
|
||||
const up = statusRow ? statusRow.age < CONSIDERED_UP_SECS : false
|
||||
const lastPing = statusRow && age.humanize()
|
||||
|
||||
return settingsLoader.loadLatest()
|
||||
.catch(() => null)
|
||||
.then(settings => {
|
||||
return getRates(settings)
|
||||
.then(rates => ({wasConfigured, up, lastPing, rates, machineStatus}))
|
||||
return settingsLoader.loadLatest()
|
||||
.catch(() => null)
|
||||
.then(settings => {
|
||||
return getRates(settings)
|
||||
.then(rates => ({wasConfigured, 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(() => [])
|
||||
.then(ratesRec => {
|
||||
return [{
|
||||
crypto: 'BTC',
|
||||
bid: parseFloat(ratesRec.rates.bid),
|
||||
ask: parseFloat(ratesRec.rates.ask)
|
||||
}]
|
||||
})
|
||||
.catch(() => [])
|
||||
}
|
||||
|
||||
module.exports = {status}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ const NUM_RESULTS = 20
|
|||
|
||||
function addNames (txs) {
|
||||
return machineLoader.getMachineNames()
|
||||
.then(machines => {
|
||||
const addName = tx => {
|
||||
const machine = _.find(['deviceId', tx.deviceId], machines)
|
||||
const name = machine ? machine.name : 'Unpaired'
|
||||
return _.set('machineName', name, tx)
|
||||
}
|
||||
.then(machines => {
|
||||
const addName = tx => {
|
||||
const machine = _.find(['deviceId', tx.deviceId], machines)
|
||||
const name = machine ? machine.name : 'Unpaired'
|
||||
return _.set('machineName', name, tx)
|
||||
}
|
||||
|
||||
return _.map(addName, txs)
|
||||
})
|
||||
return _.map(addName, txs)
|
||||
})
|
||||
}
|
||||
|
||||
const camelize = _.mapKeys(_.camelCase)
|
||||
|
|
@ -36,7 +36,7 @@ function batch () {
|
|||
order by created desc limit $1`
|
||||
|
||||
return Promise.all([db.any(cashInSql, [cashInTx.PENDING_INTERVAL, NUM_RESULTS]), db.any(cashOutSql, [NUM_RESULTS])])
|
||||
.then(packager)
|
||||
.then(packager)
|
||||
}
|
||||
|
||||
function single (txId) {
|
||||
|
|
@ -56,13 +56,13 @@ function single (txId) {
|
|||
db.oneOrNone(cashInSql, [cashInTx.PENDING_INTERVAL, txId]),
|
||||
db.oneOrNone(cashOutSql, [txId])
|
||||
])
|
||||
.then(packager)
|
||||
.then(_.head)
|
||||
.then(packager)
|
||||
.then(_.head)
|
||||
}
|
||||
|
||||
function cancel (txId) {
|
||||
return tx.cancel(txId)
|
||||
.then(() => single(txId))
|
||||
.then(() => single(txId))
|
||||
}
|
||||
|
||||
module.exports = {batch, single, cancel}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue