diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..163b5e8a --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,21 @@ +name: Update Wizard + +on: + push: + branches: [ 'feature/synchronized-wizard-builds' ] + pull_request: + branches: [ 'feature/synchronized-wizard-builds' ] + +jobs: + dispatch: + strategy: + matrix: + repo: ['shocknet/Wizard'] + runs-on: ubuntu-latest + steps: + - name: Repository Dispatch + uses: peter-evans/repository-dispatch@v1 + with: + token: ${{ secrets.REPO_ACCESS_TOKEN }} + repository: ${{ matrix.repo }} + event-type: api-update diff --git a/services/auth/secrets.json b/services/auth/secrets.json deleted file mode 100644 index 9e26dfee..00000000 --- a/services/auth/secrets.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index bfd8764b..83a34643 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -324,6 +324,18 @@ const authenticate = async (alias, pass, __user) => { API.Jobs.onAcceptedRequests(_user, mySEA) API.Jobs.onOrders(_user, gun, mySEA) API.Jobs.lastSeenNode(_user) + + API.Events.onAvatar(() => {}, user)() + API.Events.onBio(() => {}, user) + API.Events.onBlacklist(() => {}, user) + API.Events.onChats(() => {})() + API.Events.onCurrentHandshakeAddress(() => {}, user)() + API.Events.onDisplayName(() => {}, user)() + API.Events.onOutgoing(() => {})() + API.Events.onSeedBackup(() => {}, user, mySEA) + API.Events.onSimplerReceivedRequests(() => {})() + API.Events.onSimplerSentRequests(() => {})() + return _user._.sea.pub } @@ -372,6 +384,17 @@ const authenticate = async (alias, pass, __user) => { API.Jobs.onOrders(_user, gun, mySEA) API.Jobs.lastSeenNode(_user) + API.Events.onAvatar(() => {}, user)() + API.Events.onBio(() => {}, user) + API.Events.onBlacklist(() => {}, user) + API.Events.onChats(() => {})() + API.Events.onCurrentHandshakeAddress(() => {}, user)() + API.Events.onDisplayName(() => {}, user)() + API.Events.onOutgoing(() => {})() + API.Events.onSeedBackup(() => {}, user, mySEA) + API.Events.onSimplerReceivedRequests(() => {})() + API.Events.onSimplerSentRequests(() => {})() + return ack.sea.pub } else { logger.error( diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index a0ae6171..8bd27958 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -372,50 +372,13 @@ const cleanup = async pub => { const outGoingID = await Utils.recipientToOutgoingID(pub) - await new Promise((res, rej) => { - user - .get(Key.USER_TO_INCOMING) - .get(pub) - .put(null, ack => { - if (ack.err) { - rej(new Error(ack.err)) - } else { - res() - } - }) - }) + const promises = [] - await new Promise((res, rej) => { - user - .get(Key.RECIPIENT_TO_OUTGOING) - .get(pub) - .put(null, ack => { - if (ack.err) { - rej(new Error(ack.err)) - } else { - res() - } - }) - }) - - await new Promise((res, rej) => { - user - .get(Key.USER_TO_LAST_REQUEST_SENT) - .get(pub) - .put(null, ack => { - if (ack.err) { - rej(new Error(ack.err)) - } else { - res() - } - }) - }) - - if (outGoingID) { - await new Promise((res, rej) => { + promises.push( + new Promise((res, rej) => { user - .get(Key.OUTGOINGS) - .get(outGoingID) + .get(Key.USER_TO_INCOMING) + .get(pub) .put(null, ack => { if (ack.err) { rej(new Error(ack.err)) @@ -424,7 +387,56 @@ const cleanup = async pub => { } }) }) + ) + + promises.push( + new Promise((res, rej) => { + user + .get(Key.RECIPIENT_TO_OUTGOING) + .get(pub) + .put(null, ack => { + if (ack.err) { + rej(new Error(ack.err)) + } else { + res() + } + }) + }) + ) + + promises.push( + new Promise((res, rej) => { + user + .get(Key.USER_TO_LAST_REQUEST_SENT) + .get(pub) + .put(null, ack => { + if (ack.err) { + rej(new Error(ack.err)) + } else { + res() + } + }) + }) + ) + + if (outGoingID) { + promises.push( + new Promise((res, rej) => { + user + .get(Key.OUTGOINGS) + .get(outGoingID) + .put(null, ack => { + if (ack.err) { + rej(new Error(ack.err)) + } else { + res() + } + }) + }) + ) } + + await Promise.all(promises) } /** @@ -631,9 +643,9 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => { * @param {string} body * @param {UserGUNNode} user * @param {ISEA} SEA - * @returns {Promise} The message id. + * @returns {Promise} The message id. */ -const sendMessage = async (recipientPublicKey, body, user, SEA) => { +const sendMessageNew = async (recipientPublicKey, body, user, SEA) => { if (!user.is) { throw new Error(ErrorCode.NOT_AUTH) } @@ -691,12 +703,28 @@ const sendMessage = async (recipientPublicKey, body, user, SEA) => { if (ack.err) { rej(new Error(ack.err)) } else { - res(msgNode._.get) + res({ + body, + id: msgNode._.get, + outgoing: true, + timestamp: newMessage.timestamp + }) } }) }) } +/** + * Returns the message id. + * @param {string} recipientPublicKey + * @param {string} body + * @param {UserGUNNode} user + * @param {ISEA} SEA + * @returns {Promise} The message id. + */ +const sendMessage = async (recipientPublicKey, body, user, SEA) => + (await sendMessageNew(recipientPublicKey, body, user, SEA)).id + /** * @param {string} recipientPub * @param {string} msgID @@ -1211,9 +1239,7 @@ const disconnect = async pub => { throw new Error('No handshake exists for this pub') } - await cleanup(pub) - - await generateHandshakeAddress() + await Promise.all([cleanup(pub), generateHandshakeAddress()]) } /** @@ -1589,5 +1615,6 @@ module.exports = { deletePost, follow, unfollow, - initWall + initWall, + sendMessageNew } diff --git a/services/gunDB/contact-api/events/index.js b/services/gunDB/contact-api/events/index.js index 9d2fa01e..24f66a0b 100644 --- a/services/gunDB/contact-api/events/index.js +++ b/services/gunDB/contact-api/events/index.js @@ -329,7 +329,11 @@ const getCurrentOutgoings = () => currentOutgoings const outgoingsListeners = new Set() outgoingsListeners.add(o => { - logger.info(`new outgoings: ${JSON.stringify(o, null, 4)}`) + const values = Object.values(o) + const nulls = values.filter(x => x === null).length + const nonNulls = values.length - nulls + + logger.info(`new outgoings, ${nulls} nulls and ${nonNulls} nonNulls`) }) const notifyOutgoingsListeners = () => { @@ -452,7 +456,7 @@ const getChats = () => currentChats const chatsListeners = new Set() chatsListeners.add(c => { - logger.info(`new Chats: ${JSON.stringify(c, null, 4)}`) + logger.info(`Chats: ${c.length}`) }) const notifyChatsListeners = () => { diff --git a/services/gunDB/contact-api/events/onReceivedReqs.js b/services/gunDB/contact-api/events/onReceivedReqs.js index 0398d688..a0b74e30 100644 --- a/services/gunDB/contact-api/events/onReceivedReqs.js +++ b/services/gunDB/contact-api/events/onReceivedReqs.js @@ -2,6 +2,7 @@ const debounce = require('lodash/debounce') const logger = require('winston') const { Schema } = require('shock-common') +const size = require('lodash/size') const Key = require('../key') const Streams = require('../streams') @@ -50,6 +51,7 @@ const react = debounce(() => { for (const [id, req] of Object.entries(currAddressData)) { const inContact = Array.isArray(pubToFeed[req.from]) + const isDisconnected = pubToFeed[req.from] === 'disconnected' if (typeof pubToAvatar[req.from] === 'undefined') { // eslint-disable-next-line no-empty-function @@ -60,7 +62,7 @@ const react = debounce(() => { Streams.onDisplayName(() => {}, req.from)() } - if (!inContact) { + if (!inContact && !isDisconnected) { newReceivedReqsMap[req.from] = { id, requestorAvatar: pubToAvatar[req.from] || null, @@ -95,8 +97,7 @@ const listenerForAddr = addr => data => { } } - logger.info('data for address: ' + addr) - logger.info(JSON.stringify(data, null, 4)) + logger.info('data for address length: ' + size(addr)) react() } diff --git a/services/gunDB/contact-api/events/onSentReqs.js b/services/gunDB/contact-api/events/onSentReqs.js index 03686a2d..93e0740d 100644 --- a/services/gunDB/contact-api/events/onSentReqs.js +++ b/services/gunDB/contact-api/events/onSentReqs.js @@ -1,6 +1,7 @@ /** @format */ const debounce = require('lodash/debounce') const logger = require('winston') +const size = require('lodash/size') const Streams = require('../streams') /** @@ -29,7 +30,7 @@ const listeners = new Set() let currentReqs = [] listeners.add(() => { - logger.info(`new sent reqs: ${JSON.stringify(currentReqs)}`) + logger.info(`new sent reqs length: ${size(currentReqs)}`) }) const getCurrentSentReqs = () => currentReqs @@ -55,9 +56,7 @@ const react = debounce(() => { // pk to display name const pubToDN = Streams.getPubToDn() - logger.info( - `pubToLastSentREqID: ${JSON.stringify(pubToLastSentReqID, null, 4)}` - ) + logger.info(`pubToLastSentREqID length: ${size(pubToLastSentReqID)}`) for (const storedReq of storedReqs) { const { handshakeAddress, recipientPub, sentReqID, timestamp } = storedReq diff --git a/services/gunDB/contact-api/getters/follows.js b/services/gunDB/contact-api/getters/follows.js index 22a49333..55ed47f8 100644 --- a/services/gunDB/contact-api/getters/follows.js +++ b/services/gunDB/contact-api/getters/follows.js @@ -28,6 +28,7 @@ exports.currentFollows = async () => { return true } + // load sometimes returns an empty set on the first try if (size(v) === 0) { return true } diff --git a/services/gunDB/contact-api/getters/index.js b/services/gunDB/contact-api/getters/index.js index 3a82e410..4472ed41 100644 --- a/services/gunDB/contact-api/getters/index.js +++ b/services/gunDB/contact-api/getters/index.js @@ -9,6 +9,7 @@ const Utils = require('../utils') const Wall = require('./wall') const Feed = require('./feed') const User = require('./user') +const { size } = require('lodash') /** * @param {string} pub @@ -51,7 +52,18 @@ exports.userToIncomingID = async pub => { const getMyUser = async () => { const oldProfile = await Utils.tryAndWait( (_, user) => new Promise(res => user.get(Key.PROFILE).load(res)), - v => typeof v !== 'object' + v => { + if (typeof v !== 'object') { + return true + } + + if (v === null) { + return true + } + + // load sometimes returns an empty set on the first try + return size(v) === 0 + } ) const bio = await Utils.tryAndWait( diff --git a/services/gunDB/contact-api/getters/user.js b/services/gunDB/contact-api/getters/user.js index fb8054e1..52d04e72 100644 --- a/services/gunDB/contact-api/getters/user.js +++ b/services/gunDB/contact-api/getters/user.js @@ -2,6 +2,7 @@ * @format */ const Common = require('shock-common') +const size = require('lodash/size') const Key = require('../key') const Utils = require('../utils') @@ -72,7 +73,18 @@ module.exports.getAnUser = getAnUser const getMyUser = async () => { const oldProfile = await Utils.tryAndWait( (_, user) => new Promise(res => user.get(Key.PROFILE).load(res)), - v => typeof v !== 'object' + v => { + if (typeof v !== 'object') { + return true + } + + if (v === null) { + return true + } + + // load sometimes returns an empty set on the first try + return size(v) === 0 + } ) const bio = await Utils.tryAndWait( diff --git a/services/gunDB/contact-api/jobs/onAcceptedRequests.js b/services/gunDB/contact-api/jobs/onAcceptedRequests.js index 55568679..cff5c14d 100644 --- a/services/gunDB/contact-api/jobs/onAcceptedRequests.js +++ b/services/gunDB/contact-api/jobs/onAcceptedRequests.js @@ -6,6 +6,7 @@ const { Constants: { ErrorCode }, Schema } = require('shock-common') +const size = require('lodash/size') const Key = require('../key') const Utils = require('../utils') @@ -39,24 +40,31 @@ const onAcceptedRequests = (user, SEA) => { logger.info( `------------------------------------\nPROCID:${procid} (used for debugging memory leaks in jobs)\n---------------------------------------` ) + const mySecret = require('../../Mediator').getMySecret() + try { if (!Schema.isStoredRequest(storedReq)) { - logger.warn( + throw new Error( 'Stored request not an StoredRequest, instead got: ' + JSON.stringify(storedReq) + ' this can be due to nulling out an old request (if null) or something else happened (please look at the output)' ) - return } + // get the recipient pub from the stored request to avoid an attacker + // overwriting the handshake request in the root graph const recipientPub = await SEA.decrypt(storedReq.recipientPub, mySecret) if (typeof recipientPub !== 'string') { - throw new TypeError() + throw new TypeError( + `Expected storedReq.recipientPub to be an string, instead got: ${recipientPub}` + ) } + if (await Utils.successfulHandshakeAlreadyExists(recipientPub)) { return } + const requestAddress = await SEA.decrypt( storedReq.handshakeAddress, mySecret @@ -99,9 +107,9 @@ const onAcceptedRequests = (user, SEA) => { return } - // The response can be decrypted with the same secret regardless of who - // wrote to it last (see HandshakeRequest definition). - // This could be our feed ID for the recipient, or the recipient's feed + // The response can be decrypted with the same secret regardless + // of who wrote to it last (see HandshakeRequest definition). This + // could be our feed ID for the recipient, or the recipient's feed // id if he accepted the request. const feedID = await SEA.decrypt(sentReq.response, ourSecret) @@ -126,8 +134,8 @@ const onAcceptedRequests = (user, SEA) => { res(feed) }) }), - // retry on undefined, might be a false negative - v => typeof v === 'undefined' + // @ts-ignore + v => size(v) === 0 ) const feedIDExistsOnRecipientsOutgoings = diff --git a/services/gunDB/contact-api/streams/addresses.js b/services/gunDB/contact-api/streams/addresses.js index d35513dd..705e8b4c 100644 --- a/services/gunDB/contact-api/streams/addresses.js +++ b/services/gunDB/contact-api/streams/addresses.js @@ -1,5 +1,6 @@ /** @format */ const logger = require('winston') +const size = require('lodash/size') const Key = require('../key') /** @@ -13,7 +14,7 @@ const pubToAddress = {} const listeners = new Set() listeners.add(() => { - logger.info(`pubToAddress: ${JSON.stringify(pubToAddress, null, 4)}`) + logger.info(`pubToAddress length: ${size(pubToAddress)}`) }) const notify = () => listeners.forEach(l => l()) diff --git a/services/gunDB/contact-api/streams/pubToFeed.js b/services/gunDB/contact-api/streams/pubToFeed.js index 30e49394..f6f96cc4 100644 --- a/services/gunDB/contact-api/streams/pubToFeed.js +++ b/services/gunDB/contact-api/streams/pubToFeed.js @@ -3,6 +3,7 @@ const uuidv1 = require('uuid/v1') const logger = require('winston') const debounce = require('lodash/debounce') const { Schema, Utils: CommonUtils } = require('shock-common') +const size = require('lodash/size') const Key = require('../key') const Utils = require('../utils') @@ -29,7 +30,7 @@ let pubToFeed = {} const getPubToFeed = () => pubToFeed feedsListeners.add(() => { - logger.info(`new pubToFeed: ${JSON.stringify(getPubToFeed())}`) + logger.info(`new pubToFeed length: ${size(getPubToFeed())}`) }) /** @param {Feeds} ptf */ diff --git a/services/gunDB/contact-api/streams/pubToIncoming.js b/services/gunDB/contact-api/streams/pubToIncoming.js index 72b18b9d..2834ca79 100644 --- a/services/gunDB/contact-api/streams/pubToIncoming.js +++ b/services/gunDB/contact-api/streams/pubToIncoming.js @@ -3,6 +3,7 @@ const uuidv1 = require('uuid/v1') const debounce = require('lodash/debounce') const logger = require('winston') const { Utils: CommonUtils } = require('shock-common') +const size = require('lodash/size') const { USER_TO_INCOMING } = require('../key') /** @typedef {import('../SimpleGUN').OpenListenerData} OpenListenerData */ @@ -30,9 +31,7 @@ const setPubToIncoming = pti => { let latestUpdate = uuidv1() listeners.add(() => { - logger.info( - `new pubToIncoming: ${JSON.stringify(getPubToIncoming(), null, 4)}` - ) + logger.info(`new pubToIncoming length: ${size(getPubToIncoming())}`) }) const onOpen = debounce(async uti => { diff --git a/src/routes.js b/src/routes.js index 8c1fc4ad..22473bc9 100644 --- a/src/routes.js +++ b/src/routes.js @@ -14,6 +14,7 @@ const uuid = require("uuid/v4"); const Common = require('shock-common') const isARealUsableNumber = require('lodash/isFinite') const Big = require('big.js') +const size = require('lodash/size') const getListPage = require("../utils/paginate"); const auth = require("../services/auth/auth"); @@ -23,7 +24,8 @@ const LightningServices = require("../utils/lightningServices"); const GunDB = require("../services/gunDB/Mediator"); const { unprotectedRoutes, nonEncryptedRoutes } = require("../utils/protectedRoutes"); const GunActions = require("../services/gunDB/contact-api/actions") -const GunGetters = require('../services/gunDB/contact-api/getters'); +const GunGetters = require('../services/gunDB/contact-api/getters') +const GunKey = require('../services/gunDB/contact-api/key') const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10; const SESSION_ID = uuid(); @@ -273,8 +275,6 @@ module.exports = async ( app.use(async (req, res, next) => { try { - logger.info("Route:", req.path) - if (unprotectedRoutes[req.method][req.path]) { next(); return; @@ -438,76 +438,6 @@ module.exports = async ( }); - /* - const feedObj = { - feed: [ - { - id:'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba', - paragraphs:[ - "SOme text and stuff 12" - "SOme text and stuff" - ], - profilePic:"", - username:"bobni", - media:[ - { - type:'VIDEO', - ratio_x: 1024, - ratio_y: 436, - magnetUri:'magnet:?xt=urn:btih:08ada5a7a6183aae1e09d831df6748d566095a10&dn=Sintel&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fsintel.torrent', - }, - ] - }, - { - id:'3ac68afc-c605-48d3-a4f8-fbd91aa97f63', - paragraphs:[ - "SOme text and stuff" - ], - profilePic:"", - username:"bobni", - media:[ - { - type:'VIDEO', - ratio_x: 1920, - ratio_y: 804, - magnetUri:'magnet:?xt=urn:btih:c9e15763f722f23e98a29decdfae341b98d53056&dn=Cosmos+Laundromat&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fcosmos-laundromat.torrent', - }, - ] - }, - { - id:'58694a0f-3da1-471f-bd96-145571e29d72', - paragraphs:[ - "SOme text and stuff" - ], - profilePic:"", - username:"bobni", - media:[ - { - type:'VIDEO', - ratio_x: 1920, - ratio_y: 1080, - magnetUri:'magnet:?xt=urn:btih:dd8255ecdc7ca55fb0bbf81323d87062db1f6d1c&dn=Big+Buck+Bunny&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fbig-buck-bunny.torrent', - }, - ] - } - ] - } - user.get("FEED_POC").put(JSON.stringify(feedObj), ack => { - if (ack.err) { - //rej(new Error(ack.err)) - }*/ - const feedObj = { - feed :{} - } - user.get("FEED_POC").put(feedObj, ack => { - if (ack.err) { - //rej(ack.err) - logger.log(ack.err) - } else { - logger.log(ack.err) - } - }) - //register to listen for channel backups const onNewChannelBackup = () => { logger.warn("Subscribing to channel backup ...") @@ -1748,49 +1678,12 @@ module.exports = async ( const Events = require('../services/gunDB/contact-api/events') - app.get(`/api/gun/${GunEvent.ON_RECEIVED_REQUESTS}`, (_, res) => { - try { - // spinup - Events.onSimplerReceivedRequests(() => {})() - const data = Events.getCurrentReceivedReqs() - res.json({ - data, - }) - } catch (err) { - logger.info('Error in Received Requests poll:') - logger.error(err) - res.status(err.message === 'NON_AUTH' ? 401 : 500).json({ - errorMessage: typeof err === 'string' ? err : err.message - }) - } - }) - - app.get(`/api/gun/${GunEvent.ON_SENT_REQUESTS}`, (_, res) => { - try { - // spinup - Events.onSimplerSentRequests(() => {})() - const data = Events.getCurrentSentReqs() - logger.info(`Sent requests poll: ${JSON.stringify(data, null, 4)}`) - res.json({ - data, - }) - } catch (err) { - logger.info('Error in sentRequests poll:') - logger.error(err) - res.status(err.message === 'NON_AUTH' ? 401 : 500).json({ - errorMessage: typeof err === 'string' ? err : err.message - }) - } - }) - app.get(`/api/gun/${GunEvent.ON_CHATS}`, (_, res) => { try { - // spinup - Events.onChats(() => {})() const data = Events.getChats() - logger.info(`Chats polled: ${JSON.stringify(data, null, 4)}`) + logger.info(`Chats polled: ${data.length}`) res.json({ - data + data, }) } catch (err) { logger.info('Error in Chats poll:') @@ -1805,7 +1698,7 @@ module.exports = async ( try { const user = require('../services/gunDB/Mediator').getUser() const data = await timeout5(user.get(Key.PROFILE).get(Key.AVATAR).then()) - logger.info(`avatar poll:${data}`) + logger.info(`avatar poll:${(data || '').length} chars`) res.json({ data }) @@ -2058,8 +1951,13 @@ module.exports = async ( * @type {RequestHandler<{}>} */ const apiGunMePut = async (req, res) => { + /** + * @typedef {Omit} UserWithoutPK + * @typedef {{ handshakeAddress: boolean }} HasHandshakeAddress + * @typedef {UserWithoutPK & HasHandshakeAddress} MePutBody + */ try { - const { avatar, bio , displayName} = /** @type {Partial>} */ (req.body) + const { avatar, bio , displayName, handshakeAddress } = /** @type {Partial} */ (req.body) if (avatar) { await GunActions.setAvatar(avatar, require('../services/gunDB/Mediator').getUser()) @@ -2073,6 +1971,10 @@ module.exports = async ( await GunActions.setDisplayName(displayName, require('../services/gunDB/Mediator').getUser()) } + if (handshakeAddress) { + await GunActions.generateHandshakeAddress(); + } + return res.status(200).json({ ok: true }) @@ -2087,6 +1989,440 @@ module.exports = async ( ap.get(`/api/gun/me`, apiGunMeGet) ap.put(`/api/gun/me`, apiGunMePut) + /** + * @typedef {object} ChatsRouteParams + * @prop {(string|undefined)=} publicKey + */ + + /** + * @type {RequestHandler} + */ + const apiGunChatsPost = async (req, res) => { + const { publicKey } = req.params + const { body } = req.body + + if (!publicKey) { + return res.status(400).json({ + errorMessage: `Must specify a publicKey route param for POSTing a message` + }) + } + + try { + const user = GunDB.getUser() + const SEA = GunDB.mySEA + + return res.status(200).json( + await GunActions.sendMessageNew(publicKey,body, user, SEA) + ) + } catch (err) { + logger.error(err) + return res.status(500).json({ + errorMessage: err.message + }) + } + } + + /** + * @type {RequestHandler} + */ + const apiGunChatsDelete = async (req, res) => { + const { publicKey } = req.params + + if (!publicKey) { + return res.status(400).json({ + errorMessage: `Must specify a publicKey route param for DELETING a chat` + }) + } + + try { + await GunActions.disconnect(publicKey) + + return res.status(200).json({ + ok: true + }) + } catch (err) { + logger.error(err) + return res.status(500).json({ + errorMessage: err.message + }) + } + } + + ap.post(`/api/gun/chats/:publicKey?`, apiGunChatsPost) + ap.delete(`/api/gun/chats/:publicKey?`, apiGunChatsDelete) + + /** + * @typedef {object} RequestsRouteParams + * @prop {(string|undefined)=} requestID + */ + + /** + * @type {RequestHandler<{}>} + */ + const apiGunRequestsReceivedGet = (_, res) => { + try { + const data = Events.getCurrentReceivedReqs() + res.json({ + data, + }) + } catch (err) { + logger.error(err) + return res.status(500).json({ + errorMessage: err.message + }) + } + } + + /** + * @type {RequestHandler<{}>} + */ + const apiGunRequestsSentGet = (_, res) => { + try { + const data = Events.getCurrentSentReqs() + res.json({ + data, + }) + } catch (err) { + logger.error(err) + return res.status(500).json({ + errorMessage: err.message + }) + } + } + + /** + * @typedef {object} RequestsRoutePOSTBody + * @prop {string=} initialMsg + * @prop {string} publicKey + */ + + /** + * @type {RequestHandler<{}>} + */ + const apiGunRequestsPost = async (req, res) => { + const { initialMsg, publicKey } = /** @type {RequestsRoutePOSTBody} */(req.body) + + if (!publicKey) { + return res.status(400).json({ + errorMessage: `Must specify a publicKey route param for POSTing a message` + }) + } + + try { + const gun = require('../services/gunDB/Mediator').getGun() + const user = require('../services/gunDB/Mediator').getUser() + const SEA = require('../services/gunDB/Mediator').mySEA + + if (initialMsg) { + await GunActions.sendHRWithInitialMsg(initialMsg, publicKey, gun, user, SEA) + } else { + await GunActions.sendHandshakeRequest(publicKey, gun, user, SEA) + } + + return res.status(200).json({ + ok: true + }) + } catch (err) { + logger.error(err); + return res.status(500).json({ + errorMessage: err.message + }) + } + } + + /** + * @typedef {object} RequestsRoutePUTBody + * @prop {boolean=} accept + */ + + /** + * @type {RequestHandler} + */ + const apiGunRequestsPut = async (req, res) => { + const { requestID } = req.params + const { accept } = /** @type {RequestsRoutePUTBody} */(req.body) + + if (!requestID) { + return res.status(400).json({ + errorMessage: `Must specify a requestID route param for accepting a request` + }) + } + + if (!accept) { + return res.status(200).json({ + ok: true + }) + } + + try { + const gun = require('../services/gunDB/Mediator').getGun() + const user = require('../services/gunDB/Mediator').getUser() + const SEA = require('../services/gunDB/Mediator').mySEA + + await GunActions.acceptRequest(requestID, gun, user, SEA) + + return res.status(200).json({ + ok: true + }) + } catch (err) { + logger.error(err); + return res.status(500).json({ + errorMessage: err.message + }) + } + } + + ap.get(`/api/gun/${GunEvent.ON_RECEIVED_REQUESTS}`, apiGunRequestsReceivedGet) + ap.get(`/api/gun/${GunEvent.ON_SENT_REQUESTS}`, apiGunRequestsSentGet) + ap.get(`/api/gun/requests/received`, apiGunRequestsReceivedGet) + ap.get(`/api/gun/requests/sent`, apiGunRequestsSentGet) + ap.post('/api/gun/requests/', apiGunRequestsPost) + ap.put(`/api/gun/requests/:requestID?`, apiGunRequestsPut) + + + + ap.get(`/api/gun/dev/userToIncoming`, async (_, res) => { + try { + const {tryAndWait} = require('../services/gunDB/contact-api/utils') + + const data = await tryAndWait((_, u) => new Promise(res => { + u.get(GunKey.USER_TO_INCOMING).load(data => { + res(data) + }) + }), v => { + if (typeof v !== 'object') { + return true + } + + if (v === null) { + return true + } + + // load sometimes returns an empty set on the first try + return size(v) === 0 + }) + + return res.status(200).json({ + data + }) + } catch (err) { + return res.status(500).json({ + errorMessage: err.message + }) + } + }) + + ap.get(`/api/gun/dev/recipientToOutgoing`, async (_, res) => { + try { + const {tryAndWait} = require('../services/gunDB/contact-api/utils') + + const data = await tryAndWait((_, u) => new Promise(res => { + u.get(GunKey.RECIPIENT_TO_OUTGOING).load(data => { + res(data) + }) + }), v => { + if (typeof v !== 'object') { + return true + } + + if (v === null) { + return true + } + + // load sometimes returns an empty set on the first try + return size(v) === 0 + }) + + return res.status(200).json({ + data + }) + } catch (err) { + return res.status(500).json({ + errorMessage: err.message + }) + } + }) + + ap.get(`/api/gun/dev/outgoings`, async (_, res) => { + try { + const {tryAndWait} = require('../services/gunDB/contact-api/utils') + + const data = await tryAndWait((_, u) => new Promise(res => { + u.get(GunKey.OUTGOINGS).load(data => { + res(data) + }) + }), v => { + if (typeof v !== 'object') { + return true + } + + if (v === null) { + return true + } + + // load sometimes returns an empty set on the first try + return size(v) === 0 + }) + + return res.status(200).json({ + data + }) + } catch (err) { + return res.status(500).json({ + errorMessage: err.message + }) + } + }) + + + ap.get(`/api/gun/dev/currentHandshakeAddress`, async (_, res) => { + try { + const {tryAndWait} = require('../services/gunDB/contact-api/utils') + + const data = await tryAndWait((_, u) => u.get(GunKey.CURRENT_HANDSHAKE_ADDRESS).then()) + + return res.status(200).json({ + data + }) + } catch (err) { + return res.status(500).json({ + errorMessage: err.message + }) + } + }) + + ap.get(`/api/gun/dev/handshakeNodes/:handshakeAddress`, async (req, res) => { + try { + const {tryAndWait} = require('../services/gunDB/contact-api/utils') + + const data = await tryAndWait((g) => + new Promise((res) => { + g.get(GunKey.HANDSHAKE_NODES).get(req.params.handshakeAddress).load(data => { + res(data) + }) + }) + , v => { + if (typeof v !== 'object') { + return true + } + + if (v === null) { + return true + } + + // load sometimes returns an empty set on the first try + return size(v) === 0 + }) + + return res.status(200).json({ + data + }) + } catch (err) { + return res.status(500).json({ + errorMessage: err.message + }) + } + }) + + ap.get(`/api/gun/dev/user/:publicKey`, async (req, res) => { + try { + const {tryAndWait} = require('../services/gunDB/contact-api/utils') + + const data = await tryAndWait((g) => + new Promise((res) => { + g.user(req.params.publicKey).load(data => { + res(data) + }) + }) + , v => { + if (typeof v !== 'object') { + return true + } + + if (v === null) { + return true + } + + // load sometimes returns an empty set on the first try + return size(v) === 0 + }) + + return res.status(200).json({ + data + }) + } catch (err) { + return res.status(500).json({ + errorMessage: err.message + }) + } + }) + + + ap.get(`/api/gun/dev/storedReqs`, async (req, res) => { + try { + const {tryAndWait} = require('../services/gunDB/contact-api/utils') + + const data = await tryAndWait((_, u) => + new Promise((res) => u.get(Key.STORED_REQS).load(res)) + , v => { + if (typeof v !== 'object') { + return true + } + + if (v === null) { + return true + } + + // load sometimes returns an empty set on the first try + return size(v) === 0 + }) + + return res.status(200).json({ + data + }) + } catch (err) { + return res.status(500).json({ + errorMessage: err.message + }) + } + }) + + ap.get(`/api/gun/dev/userToLastReqSent`, async (req, res) => { + try { + const {tryAndWait} = require('../services/gunDB/contact-api/utils') + + const data = await tryAndWait((_, u) => + new Promise((res) => u.get(Key.USER_TO_LAST_REQUEST_SENT).load(res)) + , v => { + if (typeof v !== 'object') { + return true + } + + if (v === null) { + return true + } + + // load sometimes returns an empty set on the first try + return size(v) === 0 + }) + + return res.status(200).json({ + data + }) + } catch (err) { + return res.status(500).json({ + errorMessage: err.message + }) + } + }) + + ap.get(`/api/gun/auth`, (_, res) => { + const { isAuthenticated } = require('../services/gunDB/Mediator') + + return res.status(200).json({ + data: isAuthenticated() + }) + }) /** * Return app so that it can be used by express. */ diff --git a/src/server.js b/src/server.js index 20b4b3c7..978aa2e6 100644 --- a/src/server.js +++ b/src/server.js @@ -195,31 +195,34 @@ const server = program => { }) app.use((req, res, next) => { - if (sensitiveRoutes[req.method][req.path]) { - logger.info( - JSON.stringify({ - time: new Date(), - ip: req.ip, - method: req.method, - path: req.path, - sessionId: req.sessionId - }) - ) - } else { - logger.info( - JSON.stringify({ - time: new Date(), - ip: req.ip, - method: req.method, - path: req.path, - body: req.body, - query: req.query, - sessionId: req.sessionId - }) - ) + if (process.env.ROUTE_LOGGING === 'true') { + if (sensitiveRoutes[req.method][req.path]) { + logger.info( + JSON.stringify({ + time: new Date(), + ip: req.ip, + method: req.method, + path: req.path, + sessionId: req.sessionId + }) + ) + } else { + logger.info( + JSON.stringify({ + time: new Date(), + ip: req.ip, + method: req.method, + path: req.path, + body: req.body, + query: req.query, + sessionId: req.sessionId + }) + ) + } } next() }) + app.use( session({ secret: defaults.sessionSecret, diff --git a/utils/protectedRoutes.js b/utils/protectedRoutes.js index 8ece4f12..e3d53d44 100644 --- a/utils/protectedRoutes.js +++ b/utils/protectedRoutes.js @@ -7,7 +7,9 @@ module.exports = { "/favicon.ico": true, "/api/lnd/connect": true, "/api/lnd/wallet/status": true, - "/api/lnd/auth": true + "/api/lnd/auth": true, + // + "/api/gun/auth": true }, POST: { "/api/lnd/connect": true, @@ -28,5 +30,5 @@ module.exports = { PUT: {}, DELETE: {} }, - nonEncryptedRoutes: ['/api/security/exchangeKeys', '/healthz', '/ping', '/api/lnd/wallet/status'] + nonEncryptedRoutes: ['/api/security/exchangeKeys', '/healthz', '/ping', '/api/lnd/wallet/status', '/api/gun/auth'] } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 998c73d5..cbdd0632 100644 --- a/yarn.lock +++ b/yarn.lock @@ -377,10 +377,10 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@peculiar/asn1-schema@^2.0.1", "@peculiar/asn1-schema@^2.0.3": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.0.5.tgz#ba6c5a107eec16a23804d0176a3595837b53c0e9" - integrity sha512-VIKJjsgMkv+yyWx3C+D4xo6/NeCg0XFBgNlavtkxELijV+aKAq53du5KkOJbeZtm1nn9CinQKny2PqL8zCfpeA== +"@peculiar/asn1-schema@^2.0.1", "@peculiar/asn1-schema@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.0.8.tgz#bafb74388590f6ec3d53d1b2a4fdbe66d44224a4" + integrity sha512-D8ZqT61DdzuXfrILNvtdf7MUcTY2o9WHwmF0WgTKPEGNY5SDxNAjBY3enuwV9SXcSuCAwWac9c9v0vsswB1NIw== dependencies: "@types/asn1js" "^0.0.1" asn1js "^2.0.26" @@ -388,22 +388,22 @@ tslib "^1.11.1" "@peculiar/json-schema@^1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.10.tgz#d772b4323c9a4b5352b5ad52dc821a07b0db4877" - integrity sha512-kbpnG9CkF1y6wwGkW7YtSA+yYK4X5uk4rAwsd1hxiaYE3Hkw2EsGlbGh/COkMLyFf+Fe830BoFiMSB3QnC/ItA== + version "1.1.12" + resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" + integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== dependencies: - tslib "^1.11.1" + tslib "^2.0.0" "@peculiar/webcrypto@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.1.1.tgz#4c7498e4861878e299ef058bce1208a4d063d0ff" - integrity sha512-Bu2XgOvzirnLcojZYs4KQ8hOLf2ETpa0NL6btQt5NgsAwctI6yVkzgYP+EcG7Mm579RBP+V0LM5rXyMlTVx23A== + version "1.1.2" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.1.2.tgz#3114da877ddd9d2d0be10188371e15855aa71368" + integrity sha512-BkgD5iH2n3+Fdd/+xfhac8VbISo4MPvECPhK1kRpuYC7PnhxaJe2rpU7B4udvMeEL8lhJlvCWybo8Y7A29u/xQ== dependencies: - "@peculiar/asn1-schema" "^2.0.3" + "@peculiar/asn1-schema" "^2.0.8" "@peculiar/json-schema" "^1.1.10" pvtsutils "^1.0.10" - tslib "^1.11.2" - webcrypto-core "^1.1.0" + tslib "^2.0.0" + webcrypto-core "^1.1.2" "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -2921,9 +2921,15 @@ grpc@^1.21.1: node-pre-gyp "^0.13.0" protobufjs "^5.0.3" +<<<<<<< HEAD "gun@git://github.com/amark/gun#3e678b8568a4a8a825b84b26759d4dd4f3b0988e": version "0.2020.520" resolved "git://github.com/amark/gun#3e678b8568a4a8a825b84b26759d4dd4f3b0988e" +======= +"gun@git://github.com/amark/gun#97aa976c97e6219a9f93095d32c220dcd371ca62": + version "0.2020.520" + resolved "git://github.com/amark/gun#97aa976c97e6219a9f93095d32c220dcd371ca62" +>>>>>>> master dependencies: ws "^7.2.1" optionalDependencies: @@ -4298,7 +4304,7 @@ lodash@=4.17.4: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= -lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.5.0: +lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@^4.17.5: version "4.17.19" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== @@ -6463,6 +6469,11 @@ tslib@^1.11.1, tslib@^1.11.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== +tslib@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" + integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -6703,7 +6714,7 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -webcrypto-core@^1.1.0: +webcrypto-core@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.1.2.tgz#c522a9e5596688f2b6bb19e2d336f68efa8bdd57" integrity sha512-LxM/dTcXr/ZnwwKLox0tGEOIqvP7KIJ4Hk/fFPX20tr1EgqTmpEFZinmu4FzoGVbs6e4jI1priQKCDrOBD3L6w==