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/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..5408b469 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-expect-error + 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 22e163e7..4bee66cc 100644 --- a/src/routes.js +++ b/src/routes.js @@ -13,6 +13,7 @@ const responseTime = require("response-time"); const uuid = require("uuid/v4"); const Common = require('shock-common') const isARealUsableNumber = require('lodash/isFinite') +const size = require('lodash/size') const getListPage = require("../utils/paginate"); const auth = require("../services/auth/auth"); @@ -23,6 +24,7 @@ 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 GunKey = require('../services/gunDB/contact-api/key') const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10; const SESSION_ID = uuid(); @@ -272,8 +274,6 @@ module.exports = async ( app.use(async (req, res, next) => { try { - logger.info("Route:", req.path) - if (unprotectedRoutes[req.method][req.path]) { next(); return; @@ -437,76 +437,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 ...") @@ -1715,49 +1645,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:') @@ -1772,7 +1665,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 }) @@ -2025,8 +1918,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()) @@ -2040,6 +1938,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 }) @@ -2054,6 +1956,432 @@ 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 + }) + } + }) /** * Return app so that it can be used by express. */ diff --git a/src/server.js b/src/server.js index f576309f..5d17326e 100644 --- a/src/server.js +++ b/src/server.js @@ -198,28 +198,30 @@ 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() })