diff --git a/services/gunDB/contact-api/jobs/onOrders.js b/services/gunDB/contact-api/jobs/onOrders.js index 5bacd961..b21d6a67 100644 --- a/services/gunDB/contact-api/jobs/onOrders.js +++ b/services/gunDB/contact-api/jobs/onOrders.js @@ -12,8 +12,9 @@ const { Constants: { ErrorCode }, Schema } = Common - +const SchemaManager = require('../../../schema') const LightningServices = require('../../../../utils/lightningServices') +const LNDHealthManager = require('../../../../utils/lightningServices/errors') const Key = require('../key') const Utils = require('../utils') @@ -248,7 +249,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { }) /** - * @param {Common.Invoice} invoice + * @param {Common.Invoice & {r_hash:Buffer}} invoice */ const invoiceSubCb = invoice => { if (!invoice.settled) { @@ -260,14 +261,22 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { .get(maybePostId) .set(null) // each item in the set is a tip } - const coordinate = 'lnPub + invoiceIndex + payment hash(?)' //.... - const orderData = { - someInfo: 'info ' + const myLndPub = LNDHealthManager.lndPub + const myGunPub = getUser()._.sea.pub + if (!myLndPub) { + return //should never happen but just to be safe } - getUser() - .get('orders') - .get(coordinate) - .set(orderData) + SchemaManager.AddOrder({ + amount: parseInt(invoice.amt_paid, 10), + coordinateHash: invoice.r_hash.toString('hex'), + coordinateIndex: parseInt(invoice.add_index, 10), + toLndPub: myLndPub, + inbound: true, + type: 'other', //TODO better this + fromGunPub: order.from, + toGunPub: myGunPub, + invoiceMemo: invoice.memo + }) } stream.on('data', invoiceSubCb) diff --git a/services/schema/index.js b/services/schema/index.js index 8753a5db..3268705a 100644 --- a/services/schema/index.js +++ b/services/schema/index.js @@ -9,8 +9,8 @@ const Key = require('../gunDB/contact-api/key') * * This represents a settled order only, unsettled orders have no coordinate * @typedef {object} CoordinateOrder - * @prop {string} fromLndPub - * @prop {string} toLndPub + * @prop {string=} fromLndPub can be unknown when inbound + * @prop {string} toLndPub always known * @prop {string=} fromGunPub can be optional, if the payment/invoice is not related to an order * @prop {string=} toGunPub can be optional, if the payment/invoice is not related to an order * @prop {boolean} inbound @@ -26,6 +26,7 @@ const Key = require('../gunDB/contact-api/key') * @prop {OrderType} type * @prop {number} amount * @prop {string=} description + * @prop {string=} invoiceMemo * @prop {string=} metadata JSON encoded string to store extra data for special use cases * @prop {number=} timestamp timestamp will be added at processing time if empty * @@ -34,7 +35,7 @@ const Key = require('../gunDB/contact-api/key') /** * @param {CoordinateOrder} order */ -const checkOrderInfo = (order = {}) => { +const checkOrderInfo = order => { const { fromLndPub, toLndPub, @@ -47,23 +48,25 @@ const checkOrderInfo = (order = {}) => { coordinateIndex, coordinateHash, metadata, + invoiceMemo } = order - if (typeof fromLndPub !== 'string' || fromLndPub === '') { - return 'invalid or no "fromLndPub" field provided to order coordinate' + if (fromLndPub && (typeof fromLndPub !== 'string' || fromLndPub === '')) { + return 'invalid "fromLndPub" field provided to order coordinate' } if (typeof toLndPub !== 'string' || toLndPub === '') { return 'invalid or no "toLndPub" field provided to order coordinate' } if (fromGunPub && (typeof fromGunPub !== 'string' || fromGunPub === '')) { - return 'invalid or no "fromGunPub" field provided to order coordinate' + return 'invalid "fromGunPub" field provided to order coordinate' } if (toGunPub && (typeof toGunPub !== 'string' || toGunPub === '')) { - return 'invalid or no "toGunPub" field provided to order coordinate' + return 'invalid "toGunPub" field provided to order coordinate' } if (typeof inbound !== 'boolean') { return 'invalid or no "inbound" field provided to order coordinate' } + //@ts-expect-error if (typeof type !== 'string' || type === '') { return 'invalid or no "type" field provided to order coordinate' } @@ -81,6 +84,9 @@ const checkOrderInfo = (order = {}) => { if (description && (typeof description !== 'string' || description === '')) { return 'invalid "description" field provided to order coordinate' } + if (invoiceMemo && (typeof invoiceMemo !== 'string' || invoiceMemo === '')) { + return 'invalid "invoiceMemo" field provided to order coordinate' + } if (metadata && (typeof metadata !== 'string' || metadata === '')) { return 'invalid "metadata" field provided to order coordinate' } @@ -90,7 +96,7 @@ const checkOrderInfo = (order = {}) => { class SchemaManager { constructor({ memIndex = false }) {//config flag? this.memIndex = memIndex - this.orderCreateIndexCallbacks.push(this.dateIndexCreateCb) //create more Cbs and p + this.orderCreateIndexCallbacks.push(this.dateIndexCreateCb) //create more Cbs and put them here for more indexes callbacks } dateIndexName = 'dateIndex' @@ -161,7 +167,7 @@ class SchemaManager { ) ) } else { - res() + res(null) } }) }) @@ -179,7 +185,7 @@ class SchemaManager { if (this.memIndex) { //update date memIndex } - const date = new Date(orderInfo.timestamp) + const date = new Date(orderInfo.timestamp || 0) //use UTC for consistency? const year = date.getUTCFullYear().toString() const month = date.getUTCMonth().toString() @@ -194,21 +200,23 @@ class SchemaManager { /** * if not provided, assume current month and year - * @param {number} year - * @param {number} month + * @param {number|null} year + * @param {number|null} month */ async getMonthCoordinates(year = null, month = null) { const now = Date.now() + //@ts-expect-error const stringYear = year !== null ? year.toString() : now.getUTCFullYear().toString() + //@ts-expect-error const stringMonth = month !== null ? month.toString() : now.getUTCMonth().toString() - const data = await new Promise((res, rej) => { + const data = await new Promise(res => { getGunUser() .get(Key.COORDINATE_INDEX) .get(this.dateIndexName) .get(stringYear) .get(stringMonth) - .load() + .load(res) }) const coordinatesArray = Object .values(data) @@ -219,13 +227,15 @@ class SchemaManager { /** * if not provided, assume current month and year - * @param {number} year - * @param {number} month + * @param {number|null} year + * @param {number|null} month * @returns {Promise} from newer to older */ async getMonthOrders(year = null, month = null) { const now = Date.now() + //@ts-expect-error const intYear = year !== null ? year : now.getUTCFullYear() + //@ts-expect-error const intMonth = month !== null ? month : now.getUTCMonth() let coordinates = null @@ -234,11 +244,21 @@ class SchemaManager { } else { coordinates = await this.getMonthCoordinates(intYear, intMonth) } - const orders = await CommonUtils.asyncMap(coordinates, async coordinateSHA256 => { + /** + * @type {CoordinateOrder[]} + */ + const orders = [] + if (!coordinates) { + return orders + } + await CommonUtils.asyncForEach(coordinates, async coordinateSHA256 => { const encryptedOrderString = await getGunUser() .get(Key.COORDINATES) .get(coordinateSHA256) .then() + if (typeof encryptedOrderString !== 'string') { + return + } const mySecret = require('../gunDB/Mediator').getMySecret() const decryptedString = await SEA.decrypt(encryptedOrderString, mySecret) @@ -246,10 +266,14 @@ class SchemaManager { * @type {CoordinateOrder} */ const orderJSON = JSON.parse(decryptedString) - return orderJSON + orders.push(orderJSON) }) - + //@ts-expect-error const orderedOrders = orders.sort((a, b) => b.timestamp - a.timestamp) return orderedOrders } -} \ No newline at end of file +} + +const Manager = new SchemaManager() + +module.exports = Manager \ No newline at end of file diff --git a/utils/lightningServices/errors.js b/utils/lightningServices/errors.js index 9fdbd737..434b0f9b 100644 --- a/utils/lightningServices/errors.js +++ b/utils/lightningServices/errors.js @@ -20,17 +20,26 @@ class LNDErrorManager { */ _isCheckingHealth = false + /** + * @type {string|null} + */ + _lndPub = null + + get lndPub() { + return this._lndPub + } + /** * @type {HealthListener[]} */ _healthListeners = [] //rejects if(err && err.code !== 12) - getAvailableService(){ - + getAvailableService() { + //require('shock-common').Utils.makePromise((res, rej) => ...) - return new Promise((res,rej)=>{ - if(!this._isCheckingHealth){ + return new Promise((res, rej) => { + if (!this._isCheckingHealth) { this._isCheckingHealth = true this.getInfo() } @@ -39,7 +48,7 @@ class LNDErrorManager { * @param {LNDError} err * @param {object} response */ - const listener = (err,response)=>{ + const listener = (err, response) => { if (err) { if (err.code === 12) { res({ @@ -58,7 +67,7 @@ class LNDErrorManager { walletStatus: 'unknown', success: false }) - } else if(err.code === 4){ + } else if (err.code === 4) { rej({ service: 'unknown', message: @@ -77,7 +86,7 @@ class LNDErrorManager { }) } } - + res({ service: 'lightning', message: response, @@ -85,7 +94,7 @@ class LNDErrorManager { walletStatus: 'unlocked', success: true }) - + } this._healthListeners.push(listener) }) @@ -93,28 +102,31 @@ class LNDErrorManager { } //private - getInfo(){ + getInfo() { const { lightning } = LightningServices.services /** * * @param {LNDError} err - * @param {object} response + * @param {{identity_pubkey:string}} response */ const callback = (err, response) => { - this._healthListeners.forEach(l =>{ - l(err,response) + if (response && response.identity_pubkey) { + this._lndPub = response.identity_pubkey + } + this._healthListeners.forEach(l => { + l(err, response) }) this._healthListeners.length = 0 this._isCheckingHealth = false } const deadline = Date.now() + 10000 - lightning.getInfo({},{deadline}, callback) + lightning.getInfo({}, { deadline }, callback) } /** * @param {LNDError} e */ - handleError(e){ + handleError(e) { return this.sanitizeLNDError(e) } @@ -122,13 +134,13 @@ class LNDErrorManager { * @param {LNDError} e */ // eslint-disable-next-line - sanitizeLNDError(e){ + sanitizeLNDError(e) { let eMessage = '' - if(typeof e === 'string'){ + if (typeof e === 'string') { eMessage = e - }else if(e.details){ + } else if (e.details) { eMessage = e.details - } else if(e.message){ + } else if (e.message) { eMessage = e.message } if (eMessage.toLowerCase().includes('unknown')) { @@ -137,13 +149,13 @@ class LNDErrorManager { ? splittedMessage.slice(1).join('') : splittedMessage.join('') } - if(eMessage === ''){ + if (eMessage === '') { return 'unknown LND error' } return eMessage } - + }