From 58a69977db3745e8891def32a2b8538527df3703 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Sun, 17 May 2020 15:36:59 -0400 Subject: [PATCH] use common package --- services/gunDB/Mediator/index.js | 14 +- services/gunDB/action-constants.js | 16 - services/gunDB/contact-api/actions.js | 40 +- services/gunDB/contact-api/errorCode.js | 45 -- services/gunDB/contact-api/events/index.js | 118 ++-- .../contact-api/events/onReceivedReqs.js | 6 +- .../gunDB/contact-api/events/onSentReqs.js | 16 +- services/gunDB/contact-api/index.js | 3 +- .../gunDB/contact-api/jobs/lastSeenNode.js | 8 +- .../contact-api/jobs/onAcceptedRequests.js | 6 +- services/gunDB/contact-api/jobs/onOrders.js | 11 +- services/gunDB/contact-api/schema-types.ts | 15 - services/gunDB/contact-api/schema.js | 531 ------------------ services/gunDB/contact-api/streams/index.js | 7 +- .../gunDB/contact-api/streams/pubToFeed.js | 14 +- .../contact-api/streams/pubToIncoming.js | 21 +- services/gunDB/contact-api/utils/index.js | 70 +-- services/gunDB/event-constants.js | 13 - 18 files changed, 140 insertions(+), 814 deletions(-) delete mode 100644 services/gunDB/action-constants.js delete mode 100644 services/gunDB/contact-api/errorCode.js delete mode 100644 services/gunDB/contact-api/schema-types.ts delete mode 100644 services/gunDB/contact-api/schema.js delete mode 100644 services/gunDB/event-constants.js diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index 167c9460..2b91bae7 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -23,9 +23,6 @@ const mySEA = {} const $$__SHOCKWALLET__MSG__ = '$$__SHOCKWALLET__MSG__' const $$__SHOCKWALLET__ENCRYPTED__ = '$$_SHOCKWALLET__ENCRYPTED__' -// TO DO: Move this constant to common repo -const IS_GUN_AUTH = 'IS_GUN_AUTH' - mySEA.encrypt = (msg, secret) => { if (typeof msg !== 'string') { throw new TypeError( @@ -172,10 +169,11 @@ mySEA.secret = async (recipientOrSenderEpub, recipientOrSenderSEA) => { const auth = require('../../auth/auth') -const Action = require('../action-constants.js') +const { Constants } = require('shock-common') +const { Action, Event } = Constants + const API = require('../contact-api/index') const Config = require('../config') -const Event = require('../event-constants') // const { nonEncryptedRoutes } = require('../../../utils/protectedRoutes') /** @@ -441,7 +439,7 @@ class Mediator { this.socket.on(Event.ON_BIO, this.onBio) this.socket.on(Event.ON_SEED_BACKUP, this.onSeedBackup) - this.socket.on(IS_GUN_AUTH, this.isGunAuth) + this.socket.on(Constants.Misc.IS_GUN_AUTH, this.isGunAuth) this.socket.on(Action.SET_LAST_SEEN_APP, this.setLastSeenApp) @@ -557,7 +555,7 @@ class Mediator { try { const isGunAuth = isAuthenticated() - this.socket.emit(IS_GUN_AUTH, { + this.socket.emit(Constants.Misc.IS_GUN_AUTH, { ok: true, msg: { isGunAuth @@ -565,7 +563,7 @@ class Mediator { origBody: {} }) } catch (err) { - this.socket.emit(IS_GUN_AUTH, { + this.socket.emit(Constants.Misc.IS_GUN_AUTH, { ok: false, msg: err.message, origBody: {} diff --git a/services/gunDB/action-constants.js b/services/gunDB/action-constants.js deleted file mode 100644 index 4056ffec..00000000 --- a/services/gunDB/action-constants.js +++ /dev/null @@ -1,16 +0,0 @@ -const Actions = { - ACCEPT_REQUEST: "ACCEPT_REQUEST", - BLACKLIST: "BLACKLIST", - GENERATE_NEW_HANDSHAKE_NODE: "GENERATE_NEW_HANDSHAKE_NODE", - SEND_HANDSHAKE_REQUEST: "SEND_HANDSHAKE_REQUEST", - SEND_HANDSHAKE_REQUEST_WITH_INITIAL_MSG: "SEND_HANDSHAKE_REQUEST_WITH_INITIAL_MSG", - SEND_MESSAGE: "SEND_MESSAGE", - SEND_PAYMENT: "SEND_PAYMENT", - SET_AVATAR: "SET_AVATAR", - SET_DISPLAY_NAME: "SET_DISPLAY_NAME", - SET_BIO: "SET_BIO", - DISCONNECT: "DISCONNECT", - SET_LAST_SEEN_APP: "SET_LAST_SEEN_APP" -}; - -module.exports = Actions; diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index 183781c1..722c26e0 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -3,37 +3,29 @@ */ const uuidv1 = require('uuid/v1') const logger = require('winston') +const { Constants, Schema } = require('shock-common') + +const { ErrorCode } = Constants const LightningServices = require('../../../utils/lightningServices') -const ErrorCode = require('./errorCode') const Getters = require('./getters') const Key = require('./key') const Utils = require('./utils') -// const { promisifyGunNode: p } = Utils -const { - isHandshakeRequest, - isOrderResponse, - encodeSpontaneousPayment -} = require('./schema') + /** * @typedef {import('./SimpleGUN').GUNNode} GUNNode * @typedef {import('./SimpleGUN').ISEA} ISEA * @typedef {import('./SimpleGUN').UserGUNNode} UserGUNNode - * @typedef {import('./schema').HandshakeRequest} HandshakeRequest - * @typedef {import('./schema').StoredRequest} StoredReq - * @typedef {import('./schema').Message} Message - * @typedef {import('./schema').Outgoing} Outgoing - * @typedef {import('./schema').PartialOutgoing} PartialOutgoing - * @typedef {import('./schema').Order} Order + * @typedef {import('shock-common').Schema.HandshakeRequest} HandshakeRequest + * @typedef {import('shock-common').Schema.StoredRequest} StoredReq + * @typedef {import('shock-common').Schema.Message} Message + * @typedef {import('shock-common').Schema.Outgoing} Outgoing + * @typedef {import('shock-common').Schema.PartialOutgoing} PartialOutgoing + * @typedef {import('shock-common').Schema.Order} Order * @typedef {import('./SimpleGUN').Ack} Ack */ -/** - * An special message signaling the acceptance. - */ -const INITIAL_MSG = '$$__SHOCKWALLET__INITIAL__MESSAGE' - /** * Create a an outgoing feed. The feed will have an initial special acceptance * message. Returns a promise that resolves to the id of the newly-created @@ -92,7 +84,7 @@ const __createOutgoingFeed = async (withPublicKey, user, SEA) => { /** @type {Message} */ const initialMsg = { - body: await SEA.encrypt(INITIAL_MSG, ourSecret), + body: await SEA.encrypt(Constants.Misc.INITIAL_MSG, ourSecret), timestamp: Date.now() } @@ -203,7 +195,7 @@ const acceptRequest = async ( .get(requestID) .then() - if (!isHandshakeRequest(hr)) { + if (!Schema.isHandshakeRequest(hr)) { throw new Error(ErrorCode.TRIED_TO_ACCEPT_AN_INVALID_REQUEST) } @@ -981,7 +973,7 @@ const sendPayment = async (to, amount, memo) => { ) /** - * @type {import('./schema').OrderResponse} + * @type {import('shock-common').Schema.OrderResponse} */ const encryptedOrderRes = await Promise.race([ Promise.race([onMethod, freshGunMethod]).then(v => { @@ -996,14 +988,14 @@ const sendPayment = async (to, amount, memo) => { }) ]) - if (!isOrderResponse(encryptedOrderRes)) { + if (!Schema.isOrderResponse(encryptedOrderRes)) { throw new Error( 'received response not an OrderResponse, instead got: ' + JSON.stringify(encryptedOrderRes) ) } - /** @type {import('./schema').OrderResponse} */ + /** @type {import('shock-common').Schema.OrderResponse} */ const orderResponse = { response: await SEA.decrypt(encryptedOrderRes.response, ourSecret), type: encryptedOrderRes.type @@ -1075,7 +1067,7 @@ const sendPayment = async (to, amount, memo) => { if (Utils.successfulHandshakeAlreadyExists(to)) { await sendMessage( to, - encodeSpontaneousPayment(amount, memo || 'no memo', preimage), + Schema.encodeSpontaneousPayment(amount, memo || 'no memo', preimage), require('../Mediator').getUser(), require('../Mediator').mySEA ) diff --git a/services/gunDB/contact-api/errorCode.js b/services/gunDB/contact-api/errorCode.js deleted file mode 100644 index c3785d4a..00000000 --- a/services/gunDB/contact-api/errorCode.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @prettier - */ -exports.ALREADY_AUTH = 'ALREADY_AUTH' - -exports.NOT_AUTH = 'NOT_AUTH' - -exports.COULDNT_ACCEPT_REQUEST = 'COULDNT_ACCEPT_REQUEST' - -exports.COULDNT_SENT_REQUEST = 'COULDNT_SENT_REQUEST' - -exports.COULDNT_PUT_REQUEST_RESPONSE = 'COULDNT_PUT_REQUEST_RESPONSE' - -/** - * Error thrown when trying to accept a request, and on retrieval of that - * request invalid data (not resembling a request) is received. - */ -exports.TRIED_TO_ACCEPT_AN_INVALID_REQUEST = - 'TRIED_TO_ACCEPT_AN_INVALID_REQUEST' - -exports.UNSUCCESSFUL_LOGOUT = 'UNSUCCESSFUL_LOGOUT' - -exports.UNSUCCESSFUL_REQUEST_ACCEPT = 'UNSUCCESSFUL_REQUEST_ACCEPT' - -/** - * Error thrown when trying to send a handshake request to an user for which - * there's already an successful handshake. - */ -exports.ALREADY_HANDSHAKED = 'ALREADY_HANDSHAKED' - -/** - * Error thrown when trying to send a handshake request to an user for which - * there's already a handshake request on his current handshake node. - */ -exports.ALREADY_REQUESTED_HANDSHAKE = 'ALREADY_REQUESTED_HANDSHAKE' - -/** - * Error thrown when trying to send a handshake request to an user on an stale - * handshake address. - */ -exports.STALE_HANDSHAKE_ADDRESS = 'STALE_HANDSHAKE_ADDRESS' - -exports.TIMEOUT_ERR = 'TIMEOUT_ERR' - -exports.ORDER_NOT_ANSWERED_IN_TIME = 'ORDER_NOT_ANSWERED_IN_TIME' diff --git a/services/gunDB/contact-api/events/index.js b/services/gunDB/contact-api/events/index.js index ef50dfb9..9d2fa01e 100644 --- a/services/gunDB/contact-api/events/index.js +++ b/services/gunDB/contact-api/events/index.js @@ -3,24 +3,27 @@ */ const debounce = require('lodash/debounce') const logger = require('winston') +const { + Constants: { ErrorCode }, + Schema, + Utils: CommonUtils +} = require('shock-common') -const ErrorCode = require('../errorCode') const Key = require('../key') -const Schema = require('../schema') const Utils = require('../utils') /** * @typedef {import('../SimpleGUN').UserGUNNode} UserGUNNode * @typedef {import('../SimpleGUN').GUNNode} GUNNode * @typedef {import('../SimpleGUN').ISEA} ISEA * @typedef {import('../SimpleGUN').ListenerData} ListenerData - * @typedef {import('../schema').HandshakeRequest} HandshakeRequest - * @typedef {import('../schema').Message} Message - * @typedef {import('../schema').Outgoing} Outgoing - * @typedef {import('../schema').PartialOutgoing} PartialOutgoing - * @typedef {import('../schema').Chat} Chat - * @typedef {import('../schema').ChatMessage} ChatMessage - * @typedef {import('../schema').SimpleSentRequest} SimpleSentRequest - * @typedef {import('../schema').SimpleReceivedRequest} SimpleReceivedRequest + * @typedef {import('shock-common').Schema.HandshakeRequest} HandshakeRequest + * @typedef {import('shock-common').Schema.Message} Message + * @typedef {import('shock-common').Schema.Outgoing} Outgoing + * @typedef {import('shock-common').Schema.PartialOutgoing} PartialOutgoing + * @typedef {import('shock-common').Schema.Chat} Chat + * @typedef {import('shock-common').Schema.ChatMessage} ChatMessage + * @typedef {import('shock-common').Schema.SimpleSentRequest} SimpleSentRequest + * @typedef {import('shock-common').Schema.SimpleReceivedRequest} SimpleReceivedRequest */ const DEBOUNCE_WAIT_TIME = 500 @@ -360,59 +363,62 @@ const onOutgoing = cb => { const SEA = require('../../Mediator').mySEA const mySecret = await Utils.mySecret() - await Utils.asyncForEach(Object.entries(data), async ([id, out]) => { - if (typeof out !== 'object') { - return - } - - if (out === null) { - newOuts[id] = null - return - } - - const { with: encPub, messages } = out - - if (typeof encPub !== 'string') { - return - } - - const pub = await SEA.decrypt(encPub, mySecret) - - if (!newOuts[id]) { - newOuts[id] = { - with: pub, - messages: {} + await CommonUtils.asyncForEach( + Object.entries(data), + async ([id, out]) => { + if (typeof out !== 'object') { + return } - } - const ourSec = await SEA.secret( - await Utils.pubToEpub(pub), - user._.sea - ) + if (out === null) { + newOuts[id] = null + return + } - if (typeof messages === 'object' && messages !== null) { - await Utils.asyncForEach( - Object.entries(messages), - async ([mid, msg]) => { - if (typeof msg === 'object' && msg !== null) { - if ( - typeof msg.body === 'string' && - typeof msg.timestamp === 'number' - ) { - const newOut = newOuts[id] - if (!newOut) { - return - } - newOut.messages[mid] = { - body: await SEA.decrypt(msg.body, ourSec), - timestamp: msg.timestamp + const { with: encPub, messages } = out + + if (typeof encPub !== 'string') { + return + } + + const pub = await SEA.decrypt(encPub, mySecret) + + if (!newOuts[id]) { + newOuts[id] = { + with: pub, + messages: {} + } + } + + const ourSec = await SEA.secret( + await Utils.pubToEpub(pub), + user._.sea + ) + + if (typeof messages === 'object' && messages !== null) { + await CommonUtils.asyncForEach( + Object.entries(messages), + async ([mid, msg]) => { + if (typeof msg === 'object' && msg !== null) { + if ( + typeof msg.body === 'string' && + typeof msg.timestamp === 'number' + ) { + const newOut = newOuts[id] + if (!newOut) { + return + } + newOut.messages[mid] = { + body: await SEA.decrypt(msg.body, ourSec), + timestamp: msg.timestamp + } } } } - } - ) + ) + } } - }) + ) currentOutgoings = newOuts notifyOutgoingsListeners() diff --git a/services/gunDB/contact-api/events/onReceivedReqs.js b/services/gunDB/contact-api/events/onReceivedReqs.js index 4eb4ee61..0398d688 100644 --- a/services/gunDB/contact-api/events/onReceivedReqs.js +++ b/services/gunDB/contact-api/events/onReceivedReqs.js @@ -1,13 +1,13 @@ /** @format */ const debounce = require('lodash/debounce') const logger = require('winston') +const { Schema } = require('shock-common') const Key = require('../key') -const Schema = require('../schema') const Streams = require('../streams') /** - * @typedef {Readonly} SimpleReceivedRequest + * @typedef {Readonly} SimpleReceivedRequest * @typedef {(reqs: ReadonlyArray) => void} Listener */ @@ -22,7 +22,7 @@ let currReceivedReqsMap = {} /** * Unprocessed requests in current handshake node. - * @type {Record} + * @type {Record} */ let currAddressData = {} diff --git a/services/gunDB/contact-api/events/onSentReqs.js b/services/gunDB/contact-api/events/onSentReqs.js index a476160d..03686a2d 100644 --- a/services/gunDB/contact-api/events/onSentReqs.js +++ b/services/gunDB/contact-api/events/onSentReqs.js @@ -8,14 +8,14 @@ const Streams = require('../streams') * @typedef {import('../SimpleGUN').GUNNode} GUNNode * @typedef {import('../SimpleGUN').ISEA} ISEA * @typedef {import('../SimpleGUN').ListenerData} ListenerData - * @typedef {import('../schema').HandshakeRequest} HandshakeRequest - * @typedef {import('../schema').Message} Message - * @typedef {import('../schema').Outgoing} Outgoing - * @typedef {import('../schema').PartialOutgoing} PartialOutgoing - * @typedef {import('../schema').Chat} Chat - * @typedef {import('../schema').ChatMessage} ChatMessage - * @typedef {import('../schema').SimpleSentRequest} SimpleSentRequest - * @typedef {import('../schema').SimpleReceivedRequest} SimpleReceivedRequest + * @typedef {import('shock-common').Schema.HandshakeRequest} HandshakeRequest + * @typedef {import('shock-common').Schema.Message} Message + * @typedef {import('shock-common').Schema.Outgoing} Outgoing + * @typedef {import('shock-common').Schema.PartialOutgoing} PartialOutgoing + * @typedef {import('shock-common').Schema.Chat} Chat + * @typedef {import('shock-common').Schema.ChatMessage} ChatMessage + * @typedef {import('shock-common').Schema.SimpleSentRequest} SimpleSentRequest + * @typedef {import('shock-common').Schema.SimpleReceivedRequest} SimpleReceivedRequest */ /** diff --git a/services/gunDB/contact-api/index.js b/services/gunDB/contact-api/index.js index 1a90d22b..8ca1c443 100644 --- a/services/gunDB/contact-api/index.js +++ b/services/gunDB/contact-api/index.js @@ -5,6 +5,5 @@ const Actions = require('./actions') const Events = require('./events') const Jobs = require('./jobs') const Key = require('./key') -const Schema = require('./schema') -module.exports = { Actions, Events, Jobs, Key, Schema } +module.exports = { Actions, Events, Jobs, Key } diff --git a/services/gunDB/contact-api/jobs/lastSeenNode.js b/services/gunDB/contact-api/jobs/lastSeenNode.js index 881d41b2..002a167c 100644 --- a/services/gunDB/contact-api/jobs/lastSeenNode.js +++ b/services/gunDB/contact-api/jobs/lastSeenNode.js @@ -4,9 +4,13 @@ const logger = require('winston') -const ErrorCode = require('../errorCode') +const { + Constants: { + ErrorCode, + Misc: { LAST_SEEN_NODE_INTERVAL } + } +} = require('shock-common') const Key = require('../key') -const { LAST_SEEN_NODE_INTERVAL } = require('../utils') /** * @typedef {import('../SimpleGUN').GUNNode} GUNNode diff --git a/services/gunDB/contact-api/jobs/onAcceptedRequests.js b/services/gunDB/contact-api/jobs/onAcceptedRequests.js index 3d3966a4..55568679 100644 --- a/services/gunDB/contact-api/jobs/onAcceptedRequests.js +++ b/services/gunDB/contact-api/jobs/onAcceptedRequests.js @@ -2,10 +2,12 @@ * @format */ const logger = require('winston') +const { + Constants: { ErrorCode }, + Schema +} = require('shock-common') -const ErrorCode = require('../errorCode') const Key = require('../key') -const Schema = require('../schema') const Utils = require('../utils') /** diff --git a/services/gunDB/contact-api/jobs/onOrders.js b/services/gunDB/contact-api/jobs/onOrders.js index 39d6566f..0ff401c8 100644 --- a/services/gunDB/contact-api/jobs/onOrders.js +++ b/services/gunDB/contact-api/jobs/onOrders.js @@ -6,11 +6,14 @@ const logger = require('winston') const isFinite = require('lodash/isFinite') const isNumber = require('lodash/isNumber') const isNaN = require('lodash/isNaN') +const { + Constants: { ErrorCode }, + Schema +} = require('shock-common') const LightningServices = require('../../../../utils/lightningServices') -const ErrorCode = require('../errorCode') + const Key = require('../key') -const Schema = require('../schema') const Utils = require('../utils') const getUser = () => require('../../Mediator').getUser() @@ -127,7 +130,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { `onOrders() -> Will now place the encrypted invoice in order to response usergraph: ${addr}` ) - /** @type {Schema.OrderResponse} */ + /** @type {import('shock-common').Schema.OrderResponse} */ const orderResponse = { response: encInvoice, type: 'invoice' @@ -157,7 +160,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { ) logger.error(err) - /** @type {Schema.OrderResponse} */ + /** @type {import('shock-common').Schema.OrderResponse} */ const orderResponse = { response: err.message, type: 'err' diff --git a/services/gunDB/contact-api/schema-types.ts b/services/gunDB/contact-api/schema-types.ts deleted file mode 100644 index 2d96ab21..00000000 --- a/services/gunDB/contact-api/schema-types.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @format - * Contains types that are used throughout the application. Written in - * typescript for easier implementation. - * - * Nominal types are archieved through the enum method as outlined here: - * https://basarat.gitbook.io/typescript/main-1/nominaltyping - */ -enum _EncSpontPayment { - _ = '' -} -/** - * Spontaneous payment as found inside a chat message body. - */ -export type EncSpontPayment = _EncSpontPayment & string diff --git a/services/gunDB/contact-api/schema.js b/services/gunDB/contact-api/schema.js deleted file mode 100644 index d4f61f57..00000000 --- a/services/gunDB/contact-api/schema.js +++ /dev/null @@ -1,531 +0,0 @@ -/** - * @format - */ -const logger = require('winston') -const isFinite = require('lodash/isFinite') -const isNumber = require('lodash/isNumber') -const isNaN = require('lodash/isNaN') - -/** - * @typedef {object} HandshakeRequest - * @prop {string} from Public key of the requestor. - * @prop {string} response Encrypted string where, if the recipient accepts the - * request, his outgoing feed id will be put. Before that the sender's outgoing - * feed ID will be placed here, encrypted so only the recipient can access it. - * @prop {number} timestamp Unix time. - */ - -/** - * @typedef {object} Message - * @prop {string} body - * @prop {number} timestamp - */ - -/** - * @typedef {object} ChatMessage - * @prop {string} body - * @prop {string} id - * @prop {boolean} outgoing True if the message is an outgoing message, - * otherwise it is an incoming message. - * @prop {number} timestamp - */ - -/** - * - * @param {any} item - * @returns {item is ChatMessage} - */ -exports.isChatMessage = item => { - if (typeof item !== 'object') { - return false - } - - if (item === null) { - return false - } - - const obj = /** @type {ChatMessage} */ (item) - - if (typeof obj.body !== 'string') { - return false - } - - if (typeof obj.id !== 'string') { - return false - } - - if (typeof obj.outgoing !== 'boolean') { - return false - } - - if (typeof obj.timestamp !== 'number') { - return false - } - - return true -} - -/** - * A simpler representation of a conversation between two users than the - * outgoing/incoming feed paradigm. It combines both the outgoing and incoming - * messages into one data structure plus metada about the chat. - * @typedef {object} Chat - * @prop {string} id Chats now have IDs because of disconnect. - * RecipientPublicKey will no longer be unique. - * @prop {string|null} recipientAvatar Base64 encoded image. - * @prop {string} recipientPublicKey A way to uniquely identify each chat. - * @prop {ChatMessage[]} messages Sorted from most recent to least recent. - * @prop {string|null} recipientDisplayName - * @prop {boolean} didDisconnect True if the recipient performed a disconnect. - * @prop {number|undefined|null} lastSeenApp - */ - -/** - * @param {any} item - * @returns {item is Chat} - */ -exports.isChat = item => { - if (typeof item !== 'object') { - return false - } - - if (item === null) { - return false - } - - const obj = /** @type {Chat} */ (item) - - if (typeof obj.recipientAvatar !== 'string' && obj.recipientAvatar !== null) { - return false - } - - if (!Array.isArray(obj.messages)) { - return false - } - - if (typeof obj.recipientPublicKey !== 'string') { - return false - } - - if (obj.recipientPublicKey.length === 0) { - return false - } - - if (typeof obj.didDisconnect !== 'boolean') { - return false - } - - if (typeof obj.id !== 'string') { - return false - } - - return obj.messages.every(msg => exports.isChatMessage(msg)) -} - -/** - * @typedef {object} Outgoing - * @prop {Record} messages - * @prop {string} with Public key for whom the outgoing messages are intended. - */ - -/** - * @typedef {object} PartialOutgoing - * @prop {string} with (Encrypted) Public key for whom the outgoing messages are - * intended. - */ - -/** - * @typedef {object} StoredRequest - * @prop {string} sentReqID - * @prop {string} recipientPub - * @prop {string} handshakeAddress - * @prop {number} timestamp - */ - -/** - * @param {any} item - * @returns {item is StoredRequest} - */ -exports.isStoredRequest = item => { - if (typeof item !== 'object') return false - if (item === null) return false - const obj = /** @type {StoredRequest} */ (item) - if (typeof obj.recipientPub !== 'string') return false - if (typeof obj.handshakeAddress !== 'string') return false - if (typeof obj.handshakeAddress !== 'string') return false - if (typeof obj.timestamp !== 'number') return false - return true -} - -/** - * @typedef {object} SimpleSentRequest - * @prop {string} id - * @prop {string|null} recipientAvatar - * @prop {boolean} recipientChangedRequestAddress True if the recipient changed - * the request node address and therefore can't no longer accept the request. - * @prop {string|null} recipientDisplayName - * @prop {string} recipientPublicKey Fallback for when user has no display name. - * @prop {number} timestamp - */ - -/** - * @param {any} item - * @returns {item is SimpleSentRequest} - */ -exports.isSimpleSentRequest = item => { - if (typeof item !== 'object') { - return false - } - - if (item === null) { - return false - } - - const obj = /** @type {SimpleSentRequest} */ (item) - - if (typeof obj.id !== 'string') { - return false - } - - if (typeof obj.recipientAvatar !== 'string' && obj.recipientAvatar !== null) { - return false - } - - if (typeof obj.recipientChangedRequestAddress !== 'boolean') { - return false - } - - if ( - typeof obj.recipientDisplayName !== 'string' && - obj.recipientDisplayName !== null - ) { - return false - } - - if (typeof obj.recipientPublicKey !== 'string') { - return false - } - - if (typeof obj.timestamp !== 'number') { - return false - } - - return true -} - -/** - * @typedef {object} SimpleReceivedRequest - * @prop {string} id - * @prop {string|null} requestorAvatar - * @prop {string|null} requestorDisplayName - * @prop {string} requestorPK - * @prop {number} timestamp - */ - -/** - * @param {any} item - * @returns {item is SimpleReceivedRequest} - */ -exports.isSimpleReceivedRequest = item => { - if (typeof item !== 'object') { - return false - } - - if (item === null) { - return false - } - - const obj = /** @type {SimpleReceivedRequest} */ (item) - - if (typeof obj.id !== 'string') { - return false - } - - if (typeof obj.requestorAvatar !== 'string' && obj.requestorAvatar !== null) { - return false - } - - if ( - typeof obj.requestorDisplayName !== 'string' && - obj.requestorDisplayName !== null - ) { - return false - } - - if (typeof obj.requestorPK !== 'string') { - return false - } - - if (typeof obj.timestamp !== 'number') { - return false - } - - return true -} - -/** - * @param {any} item - * @returns {item is HandshakeRequest} - */ -exports.isHandshakeRequest = item => { - if (typeof item !== 'object') { - return false - } - - if (item === null) { - return false - } - - const obj = /** @type {HandshakeRequest} */ (item) - - if (typeof obj.from !== 'string') { - return false - } - - if (typeof obj.response !== 'string') { - return false - } - - if (typeof obj.timestamp !== 'number') { - return false - } - - return true -} - -/** - * @param {any} item - * @returns {item is Message} - */ -exports.isMessage = item => { - if (typeof item !== 'object') { - return false - } - - if (item === null) { - return false - } - - const obj = /** @type {Message} */ (item) - - return typeof obj.body === 'string' && typeof obj.timestamp === 'number' -} - -/** - * @param {any} item - * @returns {item is PartialOutgoing} - */ -exports.isPartialOutgoing = item => { - if (typeof item !== 'object') { - return false - } - - if (item === null) { - return false - } - - const obj = /** @type {PartialOutgoing} */ (item) - - return typeof obj.with === 'string' -} - -/** - * @param {any} item - * @returns {item is Outgoing} - */ -exports.isOutgoing = item => { - if (typeof item !== 'object') { - return false - } - - if (item === null) { - return false - } - - const obj = /** @type {Outgoing} */ (item) - - const messagesAreMessages = Object.values(obj.messages).every(msg => - exports.isMessage(msg) - ) - - return typeof obj.with === 'string' && messagesAreMessages -} - -/** - * @typedef {object} Order - * @prop {string} from Public key of sender. - * @prop {string} amount Encrypted - * @prop {string} memo Encrypted - * @prop {number} timestamp - */ - -/** - * @param {any} item - * @returns {item is Order} - */ -exports.isOrder = item => { - if (typeof item !== 'object') { - return false - } - - if (item === null) { - return false - } - - const obj = /** @type {Order} */ (item) - - if (typeof obj.amount !== 'string') { - return false - } - - if (typeof obj.from !== 'string') { - return false - } - - if (typeof obj.memo !== 'string') { - return false - } - - return typeof obj.timestamp === 'number' -} - -/** - * @typedef {object} OrderResponse - * @prop {'err'|'invoice'} type - * @prop {string} response - */ - -/** - * @param {*} o - * @returns {o is OrderResponse} - */ -exports.isOrderResponse = o => { - if (typeof o !== 'object') { - return false - } - - if (o === null) { - return false - } - - const obj = /** @type {OrderResponse} */ (o) - - if (typeof obj.response !== 'string') { - return false - } - - return obj.type === 'err' || obj.type === 'invoice' -} - -/** - * @typedef {import('./schema-types').EncSpontPayment} EncSpontPayment - */ - -const ENC_SPONT_PAYMENT_PREFIX = '$$__SHOCKWALLET__SPONT__PAYMENT' - -/** - * @param {string} s - * @returns {s is EncSpontPayment} - */ -const isEncodedSpontPayment = s => s.indexOf(ENC_SPONT_PAYMENT_PREFIX) === 0 - -exports.isEncodedSpontPayment = isEncodedSpontPayment - -/** - * @typedef {object} SpontaneousPayment - * @prop {number} amt - * @prop {string} memo - * @prop {string} preimage - */ - -/** - * - * @param {EncSpontPayment} sp - * @throws {Error} If decoding fails. - * @returns {SpontaneousPayment} - */ -exports.decodeSpontPayment = sp => { - try { - const [preimage, amtStr, memo] = sp - .slice((ENC_SPONT_PAYMENT_PREFIX + '__').length) - .split('__') - - if (typeof preimage !== 'string') { - throw new TypeError('Could not parse preimage') - } - - if (typeof amtStr !== 'string') { - throw new TypeError('Could not parse amt') - } - - if (typeof memo !== 'string') { - throw new TypeError('Could not parse memo') - } - - const amt = Number(amtStr) - - if (!isNumber(amt)) { - throw new TypeError(`Could parse amount as a number, not a number?`) - } - - if (isNaN(amt)) { - throw new TypeError(`Could not amount as a number, got NaN.`) - } - - if (!isFinite(amt)) { - throw new TypeError( - `Amount was correctly parsed, but got a non finite number.` - ) - } - - return { - amt, - memo, - preimage - } - } catch (err) { - logger.debug(`Encoded spontaneous payment: ${sp}`) - logger.error(err) - throw err - } -} - -/** - * @param {number} amt - * @param {string} memo - * @param {string} preimage - * @returns {EncSpontPayment} - */ -exports.encodeSpontaneousPayment = (amt, memo, preimage) => { - if (typeof amt !== 'number') { - throw new TypeError('amt must be a number') - } - - if (typeof memo !== 'string') { - throw new TypeError('memo must be an string') - } - - if (typeof preimage !== 'string') { - throw new TypeError('preimage must be an string') - } - - if (amt <= 0) { - throw new RangeError('Amt must be greater than zero') - } - - if (memo.length < 1) { - throw new TypeError('Memo must be populated') - } - - if (preimage.length < 1) { - throw new TypeError('preimage must be populated') - } - - const enc = `${ENC_SPONT_PAYMENT_PREFIX}__${amt}__${memo}__${preimage}` - - if (isEncodedSpontPayment(enc)) { - return enc - } - - throw new Error('isEncodedSpontPayment(enc) false') -} diff --git a/services/gunDB/contact-api/streams/index.js b/services/gunDB/contact-api/streams/index.js index 7d296090..a76fecbf 100644 --- a/services/gunDB/contact-api/streams/index.js +++ b/services/gunDB/contact-api/streams/index.js @@ -1,6 +1,7 @@ /** @format */ +const { Schema, Utils: CommonUtils } = require('shock-common') + const Key = require('../key') -const Schema = require('../schema') const Utils = require('../utils') /** * @typedef {Record} Avatars @@ -97,7 +98,7 @@ const onDisplayName = (cb, pub) => { } /** - * @typedef {import('../schema').StoredRequest} StoredRequest + * @typedef {import('shock-common').Schema.StoredRequest} StoredRequest * @typedef {(reqs: StoredRequest[]) => void} StoredRequestsListener */ @@ -121,7 +122,7 @@ const processStoredReqs = async () => { encryptedStoredReqs = [] const mySecret = await Utils.mySecret() const SEA = require('../../Mediator').mySEA - const finalReqs = await Utils.asyncMap(ereqs, async er => { + const finalReqs = await CommonUtils.asyncMap(ereqs, async er => { /** @type {StoredRequest} */ const r = { handshakeAddress: await SEA.decrypt(er.handshakeAddress, mySecret), diff --git a/services/gunDB/contact-api/streams/pubToFeed.js b/services/gunDB/contact-api/streams/pubToFeed.js index 35f03d9d..30e49394 100644 --- a/services/gunDB/contact-api/streams/pubToFeed.js +++ b/services/gunDB/contact-api/streams/pubToFeed.js @@ -2,12 +2,12 @@ const uuidv1 = require('uuid/v1') const logger = require('winston') const debounce = require('lodash/debounce') +const { Schema, Utils: CommonUtils } = require('shock-common') -const Schema = require('../schema') const Key = require('../key') const Utils = require('../utils') /** - * @typedef {import('../schema').ChatMessage} Message + * @typedef {import('shock-common').Schema.ChatMessage} Message * @typedef {import('../SimpleGUN').OpenListenerData} OpenListenerData */ @@ -98,7 +98,7 @@ const onOpenForPubFeedPair = ([pub, feed]) => return } - const incoming = /** @type {Schema.Outgoing} */ (data) + const incoming = /** @type {import('shock-common').Schema.Outgoing} */ (data) // incomplete data, let's not assume anything if ( @@ -108,12 +108,12 @@ const onOpenForPubFeedPair = ([pub, feed]) => return } - /** @type {Schema.ChatMessage[]} */ + /** @type {import('shock-common').Schema.ChatMessage[]} */ const newMsgs = Object.entries(incoming.messages) // filter out messages with incomplete data .filter(([_, msg]) => Schema.isMessage(msg)) .map(([id, msg]) => { - /** @type {Schema.ChatMessage} */ + /** @type {import('shock-common').Schema.ChatMessage} */ const m = { // we'll decrypt later body: msg.body, @@ -144,8 +144,8 @@ const onOpenForPubFeedPair = ([pub, feed]) => const ourSecret = await SEA.secret(await Utils.pubToEpub(pub), user._.sea) - const decryptedMsgs = await Utils.asyncMap(newMsgs, async m => { - /** @type {Schema.ChatMessage} */ + const decryptedMsgs = await CommonUtils.asyncMap(newMsgs, async m => { + /** @type {import('shock-common').Schema.ChatMessage} */ const decryptedMsg = { ...m, body: await SEA.decrypt(m.body, ourSecret) diff --git a/services/gunDB/contact-api/streams/pubToIncoming.js b/services/gunDB/contact-api/streams/pubToIncoming.js index e1ca1263..72b18b9d 100644 --- a/services/gunDB/contact-api/streams/pubToIncoming.js +++ b/services/gunDB/contact-api/streams/pubToIncoming.js @@ -2,9 +2,9 @@ const uuidv1 = require('uuid/v1') const debounce = require('lodash/debounce') const logger = require('winston') +const { Utils: CommonUtils } = require('shock-common') const { USER_TO_INCOMING } = require('../key') -const { asyncForEach } = require('../utils') /** @typedef {import('../SimpleGUN').OpenListenerData} OpenListenerData */ /** @@ -49,16 +49,19 @@ const onOpen = debounce(async uti => { /** @type {PubToIncoming} */ const newPubToIncoming = {} - await asyncForEach(Object.entries(uti), async ([pub, encFeedID]) => { - if (encFeedID === null) { - newPubToIncoming[pub] = null - return - } + await CommonUtils.asyncForEach( + Object.entries(uti), + async ([pub, encFeedID]) => { + if (encFeedID === null) { + newPubToIncoming[pub] = null + return + } - if (typeof encFeedID === 'string') { - newPubToIncoming[pub] = await SEA.decrypt(encFeedID, mySec) + if (typeof encFeedID === 'string') { + newPubToIncoming[pub] = await SEA.decrypt(encFeedID, mySec) + } } - }) + ) // avoid old data from overwriting new data if decrypting took longer to // process for the older open() call than for the newer open() call diff --git a/services/gunDB/contact-api/utils/index.js b/services/gunDB/contact-api/utils/index.js index 93b3dd4c..28dc0ebb 100644 --- a/services/gunDB/contact-api/utils/index.js +++ b/services/gunDB/contact-api/utils/index.js @@ -3,8 +3,8 @@ */ /* eslint-disable init-declarations */ const logger = require('winston') +const { Constants } = require('shock-common') -const ErrorCode = require('../errorCode') const Key = require('../key') /** @@ -41,7 +41,7 @@ const timeout10 = promise => { new Promise((_, rej) => { timeoutID = setTimeout(() => { - rej(new Error(ErrorCode.TIMEOUT_ERR)) + rej(new Error(Constants.ErrorCode.TIMEOUT_ERR)) }, 10000) }) ]) @@ -64,7 +64,7 @@ const timeout5 = promise => { new Promise((_, rej) => { timeoutID = setTimeout(() => { - rej(new Error(ErrorCode.TIMEOUT_ERR)) + rej(new Error(Constants.ErrorCode.TIMEOUT_ERR)) }, 5000) }) ]) @@ -252,55 +252,6 @@ const recipientToOutgoingID = async recipientPub => { return null } -/** - * @template T - * @param {T[]} arr - * @param {(item: T) => void} cb - * @returns {Promise} - */ -const asyncForEach = async (arr, cb) => { - const promises = arr.map(item => cb(item)) - - await Promise.all(promises) -} - -/** - * @template T - * @template U - * @param {T[]} arr - * @param {(item: T) => Promise} cb - * @returns {Promise} - */ -const asyncMap = (arr, cb) => { - if (arr.length === 0) { - return Promise.resolve([]) - } - - const promises = arr.map(item => cb(item)) - - return Promise.all(promises) -} - -/** - * @template T - * @param {T[]} arr - * @param {(item: T) => Promise} cb - * @returns {Promise} - */ -const asyncFilter = async (arr, cb) => { - if (arr.length === 0) { - return [] - } - - /** @type {Promise[]} */ - const promises = arr.map(item => cb(item)) - - /** @type {boolean[]} */ - const results = await Promise.all(promises) - - return arr.filter((_, idx) => results[idx]) -} - /** * @param {import('../SimpleGUN').ListenerData} listenerData * @returns {listenerData is import('../SimpleGUN').ListenerObj} @@ -308,14 +259,6 @@ const asyncFilter = async (arr, cb) => { const dataHasSoul = listenerData => typeof listenerData === 'object' && listenerData !== null -/** - * @param {string} pub - * @returns {string} - */ -const defaultName = pub => 'anon' + pub.slice(0, 8) - -const LAST_SEEN_NODE_INTERVAL = 10000 - /** * @param {string} pub * @returns {Promise} @@ -341,15 +284,12 @@ const isNodeOnline = async pub => { return ( isOnlineApp || (typeof lastSeenNode === 'number' && - Date.now() - lastSeenNode < LAST_SEEN_NODE_INTERVAL * 2) + Date.now() - lastSeenNode < Constants.Misc.LAST_SEEN_NODE_INTERVAL * 2) ) } module.exports = { - asyncMap, - asyncFilter, dataHasSoul, - defaultName, delay, pubToEpub, recipientPubToLastReqSentID, @@ -358,8 +298,6 @@ module.exports = { tryAndWait, mySecret, promisifyGunNode: require('./promisifygun'), - asyncForEach, timeout5, - LAST_SEEN_NODE_INTERVAL, isNodeOnline } diff --git a/services/gunDB/event-constants.js b/services/gunDB/event-constants.js deleted file mode 100644 index c4ffdae4..00000000 --- a/services/gunDB/event-constants.js +++ /dev/null @@ -1,13 +0,0 @@ -const Events = { - ON_AVATAR: "ON_AVATAR", - ON_BLACKLIST: "ON_BLACKLIST", - ON_CHATS: "ON_CHATS", - ON_DISPLAY_NAME: "ON_DISPLAY_NAME", - ON_HANDSHAKE_ADDRESS: "ON_HANDSHAKE_ADDRESS", - ON_RECEIVED_REQUESTS: "ON_RECEIVED_REQUESTS", - ON_SENT_REQUESTS: "ON_SENT_REQUESTS", - ON_BIO: "ON_BIO", - ON_SEED_BACKUP: "ON_SEED_BACKUP", -}; - -module.exports = Events;