This commit is contained in:
Josh Harvey 2016-10-26 04:10:23 +03:00
parent 22e58844c9
commit 9c57b1de87
4 changed files with 88 additions and 30 deletions

View file

@ -3,6 +3,7 @@
const BigNumber = require('bignumber.js') const BigNumber = require('bignumber.js')
const logger = require('./logger') const logger = require('./logger')
const configManager = require('./config-manager') const configManager = require('./config-manager')
const db = require('./db')
let mock = false let mock = false
@ -121,16 +122,15 @@ function poll (req, res) {
.catch(logger.error) .catch(logger.error)
} }
function trade (req, res) { function trade (req, res, next) {
const tx = req.body const tx = req.body
tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms) tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms)
plugins.trade(getDeviceId(req), tx) plugins.trade(getDeviceId(req), tx)
.then(() => res.status(201).json({})) .then(() => {
.catch(err => { updateCachedAction(req, {}, 201)
logger.error(err) res.status(201).json({})
res.status(500).json({err}) })
})
} }
function stateChange (req, res) { function stateChange (req, res) {
@ -147,20 +147,11 @@ function send (req, res) {
tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms) tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms)
return plugins.sendCoins(getDeviceId(req), tx) return plugins.sendCoins(getDeviceId(req), tx)
.then(status => { .then(status => {
res.json({ const body = {txId: status && status.txId}
txHash: status && status.txHash, updateCachedAction(req, body, 200)
txId: status && status.txId .then(() => res.json(body))
}) })
})
.catch(err => {
console.log('DEBUG15: %s', err)
logger.error(err)
res.json({
err: err.message,
errType: err.name
})
})
} }
function cashOut (req, res) { function cashOut (req, res) {
@ -232,8 +223,14 @@ function pair (req, res) {
return pair.pair(token, deviceId) return pair.pair(token, deviceId)
.then(valid => { .then(valid => {
if (valid) return res.status(200).end() updateCachedAction(req, {})
return res.status(408).end() if (valid) {
updateCachedAction(req, {}, 200)
return res.json({})
}
updateCachedAction(req, {}, 408)
return res.status(408).json({})
}) })
} }
@ -333,6 +330,44 @@ function dispense (req, res) {
.catch(err => logger.error(err)) .catch(err => logger.error(err))
} }
function isUniqueViolation (err) {
return err.code === '23505'
}
function cacheAction (req, res, next) {
const sql = `insert into idempotents (request_id, device_id, body, status, pending)
values ($1, $2, $3, $4, $5)`
const requestId = req.headers['request-id']
const deviceId = getDeviceId(req)
db.none(sql, [requestId, deviceId, {}, 204, true])
.then(() => next())
.catch(err => {
if (!isUniqueViolation(err)) throw err
const sql2 = 'select body, status, pending from idempotents where request_id=$1'
return db.one(sql2, [requestId])
.then(row => {
if (row.pending) return res.status(204).end()
return res.status(row.status).json(row.body)
})
})
}
function cachedActionError (req, err, status) {
return updateCachedAction(req, {error: err}, status || 500)
}
function updateCachedAction (req, body, status) {
const sql = 'update idempotents set body=$1, status=$2, pending=$3 where request_id=$4 and device_id=$5'
const requestId = req.headers['request-id']
const deviceId = getDeviceId(req)
return db.none(sql, [body, status, false, requestId, deviceId])
}
function init (opts) { function init (opts) {
plugins = opts.plugins plugins = opts.plugins
mock = opts.mock mock = opts.mock
@ -341,6 +376,8 @@ function init (opts) {
const app = opts.app const app = opts.app
const localApp = opts.localApp const localApp = opts.localApp
app.post('*', cacheAction)
app.post('/pair', pair) app.post('/pair', pair)
app.get('/ca', ca) app.get('/ca', ca)
@ -363,6 +400,8 @@ function init (opts) {
app.get('/await_dispense/:txId', authMiddleware, waitForDispense) app.get('/await_dispense/:txId', authMiddleware, waitForDispense)
app.post('/dispense', authMiddleware, dispense) app.post('/dispense', authMiddleware, dispense)
app.post('*', updateCachedAction)
localApp.get('/pid', (req, res) => { localApp.get('/pid', (req, res) => {
const deviceId = req.query.device_id const deviceId = req.query.device_id
const pidRec = pids[deviceId] const pidRec = pids[deviceId]
@ -403,11 +442,3 @@ function getDeviceId (req) {
req.connection.getPeerCertificate().fingerprint) || 'unknown' req.connection.getPeerCertificate().fingerprint) || 'unknown'
} }
function cachedResponse (deviceId, txId, req) {
return plugins.cachedResponse(deviceId, txId, req.path, req.method)
.then(r => r.body)
}
function cacheResponse (deviceId, txId, req, body) {
return plugins.cacheResponse(deviceId, txId, req.path, req.method, body)
}

View file

@ -0,0 +1,20 @@
var db = require('./db')
exports.up = function (next) {
var sql = [
'drop table if exists cached_responses',
`create table idempotents (
request_id text PRIMARY KEY,
device_id text NOT NULL,
body json NOT NULL,
status integer NOT NULL,
pending boolean NOT NULL,
created timestamptz NOT NULL default now()
)`
]
db.multi(sql, next)
}
exports.down = function (next) {
next()
}

View file

@ -15,6 +15,7 @@
"bunyan": "^1.8.1", "bunyan": "^1.8.1",
"chalk": "^1.1.3", "chalk": "^1.1.3",
"express": "^4.13.4", "express": "^4.13.4",
"express-limiter": "^1.6.0",
"helmet": "^2.3.0", "helmet": "^2.3.0",
"inquirer": "^1.0.0", "inquirer": "^1.0.0",
"lamassu-bitcoinaverage": "~1.0.0", "lamassu-bitcoinaverage": "~1.0.0",
@ -36,6 +37,7 @@
"node-hkdf-sync": "^1.0.0", "node-hkdf-sync": "^1.0.0",
"node-uuid": "^1.4.2", "node-uuid": "^1.4.2",
"numeral": "^1.5.3", "numeral": "^1.5.3",
"on-finished": "^2.3.0",
"pg": "^4.5.5", "pg": "^4.5.5",
"pg-promise": "^4.3.3", "pg-promise": "^4.3.3",
"pify": "^2.3.0", "pify": "^2.3.0",

View file

@ -109,3 +109,8 @@ options: configure per machine; configure per crypto/fiat
v need to create CA: http://stackoverflow.com/questions/19665863/how-do-i-use-a-self-signed-certificate-for-a-https-node-js-server v need to create CA: http://stackoverflow.com/questions/19665863/how-do-i-use-a-self-signed-certificate-for-a-https-node-js-server
--------------------------------
- consistent error handling
- usage of http status codes (good for got)
- finish idempotency for all calls