lamassu-server/lib/postgresql_interface.js

181 lines
4 KiB
JavaScript

'use strict';
var pg = require('pg');
var logger = require('./logger');
var PG_ERRORS = {
23505: 'uniqueViolation'
};
var client = null;
function getInsertQuery(tableName, fields) {
// outputs string like: '$1, $2, $3...' with proper No of items
var placeholders = fields.map(function(_, i) {
return '$' + (i + 1);
}).join(', ');
return 'INSERT INTO ' + tableName +
' (' + fields.join(', ') + ')' +
' VALUES' +
' (' + placeholders + ')';
}
exports.init = function init(conString) {
if (client !== null) return;
if (!conString) {
throw new Error('Postgres connection string is required');
}
client = new pg.Client(conString);
client.on('error', function (err) { logger.error(err); });
client.connect();
};
// logs inputted bill and overall tx status (if available)
exports.recordBill = function recordBill(deviceFingerprint, rec, cb) {
var fields = [
'device_fingerprint',
'currency_code',
'to_address',
'transaction_id',
'device_time',
'satoshis',
'denomination'
];
var values = [
deviceFingerprint,
rec.currency,
rec.toAddress,
res.txId,
rec.deviceTime,
rec.satoshis,
rec.fiat
];
if (rec.partialTx) {
values.push(rec.partialTx.satoshis, rec.partialTx.fiat);
fields.push('total_satoshis', 'total_fiat');
}
client.query(getInsertQuery('bills', fields), values, cb);
};
exports.recordDeviceEvent = function recordDeviceEvent(deviceFingerprint, event, cb) {
client.query('INSERT INTO device_events (device_fingerprint, event_type, note, device_time)' +
'VALUES ($1, $2, $3, $4)',
[deviceFingerprint, event.eventType, event.note, event.deviceTime],
cb);
};
function _getTransactions(txId, onlyPending, cb) {
var query = 'SELECT * FROM transactions WHERE id=$1';
var values = [txId];
if (onlyPending) {
query += ' AND status=$2 AND tx_hash IS NULL';
values.push('pending');
}
client.query(query, values, function(err, results) {
if (err) return cb(err);
if (results.rows.length === 0)
return cb(new Error('Couldn\'t find transaction'));
cb(null, results.rows);
});
}
// returns complete [txs]
exports.getTransactions = function getTransactions(txId, cb) {
_getTransactions(txId, false, cb);
};
// TODO: this should probably return bills, associated with failed/inexistent tx
exports.getPendingTransactions = function getPendingTransactions(txId, cb) {
// should return
// is_completed === false
// amount from bils is less than sum of all parts with the same txId
// latest bill with txId not present in transactions
//
_getTransactions(txId, true, cb);
};
exports.summonTransaction = function summonTransaction(deviceFingerprint, tx, cb) {
var fields = [
'id',
'status',
'device_fingerprint',
'to_address',
'satoshis',
'currency_code',
'fiat'
];
var values = [
tx.txId,
tx.status || 'pending',
deviceFingerprint,
tx.toAddress,
tx.satoshis,
tx.currencyCode,
tx.fiat
];
if (tx.part && tx.part > 1) {
fields.push('part');
values.push(tx.part);
}
// First attampt an INSERT
// If it worked, go ahead with transaction
client.query(getInsertQuery('transactions', fields),
values,
function(err) {
if (err) {
if (PG_ERRORS[err.code] === 'uniqueViolation')
return exports.getTransactions(tx.txId, cb);
return cb(err);
}
cb();
});
};
// `@more` can contain `part`, `hash`, or `error`
exports.changeTxStatus = function changeTxStatus(txId, newStatus, more, cb) {
cb = typeof cb === 'function' ? cb : function() {};
var query = 'UPDATE transactions SET status=$1';
var values = [
newStatus
];
var n = 2;
if (newStatus === 'error') {
query += ', error=$' + n++;
values.push(more.error);
}
if (newStatus === 'completed') {
query += ', tx_hash=$' + n++;
values.push(more.hash);
}
query += ' WHERE id=$' + n;
values.push(txId);
client.query(query, values, cb);
};