diff --git a/.env.example b/.env.example index 83e2ce68..ce097770 100644 --- a/.env.example +++ b/.env.example @@ -5,5 +5,6 @@ DISABLE_SHOCK_ENCRYPTION=false CACHE_HEADERS_MANDATORY=true SHOCK_CACHE=true TRUSTED_KEYS=true +LOCAL_TUNNEL_SERVER=https://tunnel.rip TORRENT_SEED_URL=https://webtorrent.shock.network TORRENT_SEED_TOKEN=jibberish \ No newline at end of file diff --git a/README.md b/README.md index c7dcc9c1..c3cde0d8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

ShockAPI

-![GitHub last commit](https://img.shields.io/github/last-commit/shocknet/wallet?style=flat-square) +![GitHub last commit](https://img.shields.io/github/last-commit/shocknet/api?style=flat-square) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![Chat](https://img.shields.io/badge/chat-on%20Telegram-blue?style=flat-square)](https://t.me/Shockwallet) [![Twitter Follow](https://img.shields.io/twitter/follow/ShockBTC?style=flat-square)](https://twitter.com/shockbtc) diff --git a/config/defaults.js b/config/defaults.js index 356edbb1..a44376f4 100644 --- a/config/defaults.js +++ b/config/defaults.js @@ -49,6 +49,7 @@ module.exports = (mainnet = false) => { lndDirPath: lndDirectory, peers: ['https://gun.shock.network:8765/gun'], useTLS: false, - tokenExpirationMS: 259200000 + tokenExpirationMS: 259200000, + localtunnelHost:'https://tunnel.rip' }; }; diff --git a/main.js b/main.js index 29f478bb..7f1ae125 100644 --- a/main.js +++ b/main.js @@ -6,26 +6,18 @@ const {version} = (JSON.parse(require('fs').readFileSync("./package.json", "utf- program .version(version) .option("-s, --serverport [port]", "web server http listening port (defaults to 8280)") - .option("-x, --httpsport [port]", "web server https listening port (defaults to 8283)") .option("-h, --serverhost [host]", "web server listening host (defaults to localhost)") .option("-l, --lndhost [host:port]", "RPC lnd host (defaults to localhost:10009)") - .option("-t, --usetls [path]", "path to a directory containing key.pem and cert.pem files") .option("-u, --user [login]", "basic authentication login") .option("-p, --pwd [password]", "basic authentication password") - .option("-r, --limituser [login]", "basic authentication login for readonly account") - .option("-w, --limitpwd [password]", "basic authentication password for readonly account") .option("-m, --macaroon-path [file path]", "path to admin.macaroon file") .option("-d, --lnd-cert-path [file path]", "path to LND cert file") .option("-f, --logfile [file path]", "path to file where to store the application logs") .option("-e, --loglevel [level]", "level of logs to display (debug, info, warn, error)") - .option("-n, --lndlogfile ", "path to lnd log file to send to browser") .option("-k, --le-email [email]", "lets encrypt required contact email") .option("-c, --mainnet", "run server on mainnet mode") + .option("-t, --tunnel","create a localtunnel to listen behind a firewall") .parse(process.argv); // load server -if (program.serverhost && program.leEmail) { - require("./app/server-le")(program); // Let"s Encrypt server version -} else { - require("./src/server")(program); // Standard server version -} +require("./src/server")(program); // Standard server version diff --git a/package.json b/package.json index 6debf058..7955cca1 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "dependencies": { "@grpc/grpc-js": "^1.2.2", "@grpc/proto-loader": "^0.5.5", + "assert-never": "^1.2.1", "axios": "^0.21.1", "basic-auth": "^2.0.0", "big.js": "^5.2.2", @@ -41,18 +42,19 @@ "husky": "^4.2.5", "jsonfile": "^4.0.0", "jsonwebtoken": "^8.3.0", - "localtunnel": "^1.9.0", + "localtunnel": "git://github.com/shocknet/localtunnel#40cc2c2a46b05da2217bf2e20da11a5343a5cce7", "lodash": "^4.17.20", "method-override": "^2.3.7", "node-fetch": "^2.6.1", "node-persist": "^3.1.0", "promise": "^8.1.0", + "qrcode-terminal": "^0.12.0", "ramda": "^0.27.1", "request": "^2.88.2", "request-promise": "^4.2.6", "response-time": "^2.3.2", "shelljs": "^0.8.2", - "shock-common": "31.1.0", + "shock-common": "32.0.0", "socket.io": "2.1.1", "text-encoding": "^0.7.0", "tingodb": "^0.6.1", @@ -68,6 +70,7 @@ "@types/jest": "^24.0.18", "@types/jsonwebtoken": "^8.3.7", "@types/lodash": "^4.14.141", + "@types/node-fetch": "^2.5.8", "@types/ramda": "types/npm-ramda#dist", "@types/react": "16.x.x", "@types/socket.io": "^2.1.11", @@ -86,7 +89,7 @@ "rimraf": "^3.0.2", "ts-node": "^9.1.1", "ts-type": "^1.2.16", - "typescript": "^4.0.2" + "typescript": "latest" }, "lint-staged": { "*.js": [ diff --git a/services/coordinates.js b/services/coordinates.js new file mode 100644 index 00000000..d56981bb --- /dev/null +++ b/services/coordinates.js @@ -0,0 +1,63 @@ +/** + * @format + */ + +const Common = require('shock-common') +const mapValues = require('lodash/mapValues') +const pickBy = require('lodash/pickBy') +const Bluebird = require('bluebird') +const Logger = require('winston') +const Key = require('../services/gunDB/contact-api/key') + +const { getUser, getMySecret, mySEA } = require('./gunDB/Mediator') + +/** + * @param {string} coordID + * @param {Common.Coordinate} data + * @returns {Promise} + */ +module.exports.writeCoordinate = async (coordID, data) => { + if (coordID !== data.id) { + throw new Error('CoordID must be equal to data.id') + } + + try { + /** + * Because there are optional properties, typescript can also allow them + * to be specified but with a value of `undefined`. Filter out these. + * @type {Record} + */ + const sanitizedData = pickBy(data, v => typeof v !== 'undefined') + + const encData = await Bluebird.props( + mapValues(sanitizedData, v => { + return mySEA.encrypt(v, getMySecret()) + }) + ) + + getUser() + .get(Key.COORDINATES) + .get(coordID) + .put(encData, ack => { + if (ack.err && typeof ack.err !== 'number') { + Logger.info( + `Error writting corrdinate, coordinate id: ${coordID}, data: ${JSON.stringify( + data, + null, + 2 + )}` + ) + Logger.error(ack.err) + } + }) + } catch (e) { + Logger.info( + `Error writing coordinate, coordinate id: ${coordID}, data: ${JSON.stringify( + data, + null, + 2 + )}` + ) + Logger.error(e.message) + } +} diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index e7914de8..eddb2466 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -314,7 +314,7 @@ const authenticate = async (alias, pass, __user) => { // clock skew await new Promise(res => setTimeout(res, 2000)) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { _user.get(Key.FOLLOWS).put( { unused: null @@ -327,7 +327,7 @@ const authenticate = async (alias, pass, __user) => { } } ) - }) + })) return ack.sea.pub } else { @@ -345,7 +345,7 @@ const authenticate = async (alias, pass, __user) => { // clock skew await new Promise(res => setTimeout(res, 2000)) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { _user.get(Key.FOLLOWS).put( { unused: null @@ -358,7 +358,7 @@ const authenticate = async (alias, pass, __user) => { } } ) - }) + })) // move this to a subscription; implement off() ? todo API.Jobs.onAcceptedRequests(_user, mySEA) @@ -404,7 +404,7 @@ const authenticate = async (alias, pass, __user) => { await new Promise(res => setTimeout(res, 5000)) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { _user.get(Key.FOLLOWS).put( { unused: null @@ -417,7 +417,7 @@ const authenticate = async (alias, pass, __user) => { } } ) - }) + })) API.Jobs.onAcceptedRequests(_user, mySEA) API.Jobs.onOrders(_user, gun, mySEA) diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index c03e0307..05bf741d 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -11,7 +11,8 @@ const { ErrorCode } = Constants const { sendPaymentV2Invoice, - decodePayReq + decodePayReq, + myLNDPub } = require('../../../utils/lightningServices/v2') /** @@ -100,7 +101,7 @@ const __createOutgoingFeed = async (withPublicKey, user, SEA) => { timestamp: Date.now() } - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.OUTGOINGS) .get(newOutgoingFeedID) @@ -113,14 +114,14 @@ const __createOutgoingFeed = async (withPublicKey, user, SEA) => { res() } }) - }) + })) const encryptedForMeNewOutgoingFeedID = await SEA.encrypt( newOutgoingFeedID, mySecret ) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.RECIPIENT_TO_OUTGOING) .get(withPublicKey) @@ -131,7 +132,7 @@ const __createOutgoingFeed = async (withPublicKey, user, SEA) => { res() } }) - }) + })) outgoingFeedID = newOutgoingFeedID } @@ -237,7 +238,7 @@ const acceptRequest = async ( const mySecret = require('../Mediator').getMySecret() const encryptedForMeIncomingID = await SEA.encrypt(incomingID, mySecret) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.USER_TO_INCOMING) .get(senderPublicKey) @@ -248,7 +249,7 @@ const acceptRequest = async ( res() } }) - }) + })) //////////////////////////////////////////////////////////////////////////// // NOTE: perform non-reversable actions before destructive actions @@ -261,7 +262,7 @@ const acceptRequest = async ( ourSecret ) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { gun .get(Key.HANDSHAKE_NODES) .get(handshakeAddress) @@ -278,7 +279,7 @@ const acceptRequest = async ( } } ) - }) + })) } /** @@ -287,7 +288,7 @@ const acceptRequest = async ( * @param {UserGUNNode} userNode */ const authenticate = (user, pass, userNode) => - new Promise((resolve, reject) => { + /** @type {Promise} */ (new Promise((resolve, reject) => { if (typeof user !== 'string') { throw new TypeError('expected user to be of type string') } @@ -317,7 +318,7 @@ const authenticate = (user, pass, userNode) => resolve() } }) - }) + })) /** * @param {string} publicKey @@ -349,7 +350,7 @@ const generateHandshakeAddress = async () => { const address = uuidv1() - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { user.get(Key.CURRENT_HANDSHAKE_ADDRESS).put(address, ack => { if (ack.err && typeof ack.err !== 'number') { rej(new Error(ack.err)) @@ -357,9 +358,9 @@ const generateHandshakeAddress = async () => { res() } }) - }) + })) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { gun .get(Key.HANDSHAKE_NODES) .get(address) @@ -370,7 +371,7 @@ const generateHandshakeAddress = async () => { res() } }) - }) + })) } /** @@ -387,7 +388,7 @@ const cleanup = async pub => { const promises = [] promises.push( - new Promise((res, rej) => { + /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.USER_TO_INCOMING) .get(pub) @@ -398,11 +399,11 @@ const cleanup = async pub => { res() } }) - }) + })) ) promises.push( - new Promise((res, rej) => { + /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.RECIPIENT_TO_OUTGOING) .get(pub) @@ -413,11 +414,11 @@ const cleanup = async pub => { res() } }) - }) + })) ) promises.push( - new Promise((res, rej) => { + /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.USER_TO_LAST_REQUEST_SENT) .get(pub) @@ -428,12 +429,12 @@ const cleanup = async pub => { res() } }) - }) + })) ) if (outGoingID) { promises.push( - new Promise((res, rej) => { + /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.OUTGOINGS) .get(outGoingID) @@ -444,7 +445,7 @@ const cleanup = async pub => { res() } }) - }) + })) ) } @@ -618,7 +619,7 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => { }) }) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.USER_TO_LAST_REQUEST_SENT) .get(recipientPublicKey) @@ -629,7 +630,7 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => { res() } }) - }) + })) // This needs to come before the write to sent requests. Because that write // triggers Jobs.onAcceptedRequests and it in turn reads from request-to-user @@ -644,7 +645,7 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => { timestamp } - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { //@ts-ignore user.get(Key.STORED_REQS).set(storedReq, ack => { if (ack.err && typeof ack.err !== 'number') { @@ -657,7 +658,7 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => { res() } }) - }) + })) } /** @@ -1210,7 +1211,7 @@ const generateOrderAddress = user => * @returns {Promise} */ const setBio = (bio, user) => - new Promise((resolve, reject) => { + /** @type {Promise} */ (new Promise((resolve, reject) => { if (!user.is) { throw new Error(ErrorCode.NOT_AUTH) } @@ -1234,7 +1235,7 @@ const setBio = (bio, user) => resolve() } }) - }).then( + })).then( () => new Promise((resolve, reject) => { user @@ -1318,7 +1319,7 @@ const disconnect = async pub => { * @returns {Promise} */ const setLastSeenApp = () => - new Promise((res, rej) => { + /** @type {Promise} */ (new Promise((res, rej) => { require('../Mediator') .getUser() .get(Key.LAST_SEEN_APP) @@ -1329,7 +1330,7 @@ const setLastSeenApp = () => res() } }) - }).then( + })).then( () => new Promise((res, rej) => { require('../Mediator') @@ -1350,10 +1351,10 @@ const setLastSeenApp = () => * @param {string[]} tags * @param {string} title * @param {Common.Schema.ContentItem[]} content - * @param {ISEA} SEA * @returns {Promise<[string, Common.Schema.RawPost]>} */ -const createPostNew = async (tags, title, content, SEA) => { +const createPostNew = async (tags, title, content) => { + const SEA = require('../Mediator').mySEA /** @type {Common.Schema.RawPost} */ const newPost = { date: Date.now(), @@ -1362,10 +1363,13 @@ const createPostNew = async (tags, title, content, SEA) => { title, contentItems: {} } + const mySecret = require('../Mediator').getMySecret() + await Common.Utils.asyncForEach(content, async c => { // @ts-expect-error const uuid = Gun.text.random() + newPost.contentItems[uuid] = c if ( (c.type === 'image/embedded' || c.type === 'video/embedded') && c.isPrivate @@ -1459,7 +1463,7 @@ const createPost = async (tags, title, content, SEA) => { pageIdx = Number(pageIdx + 1).toString() } - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { require('../Mediator') .getUser() .get(Key.WALL) @@ -1480,7 +1484,7 @@ const createPost = async (tags, title, content, SEA) => { res() } ) - }) + })) const [postID, newPost] = await createPostNew(tags, title, content, SEA) @@ -1506,7 +1510,7 @@ const createPost = async (tags, title, content, SEA) => { }) if (shouldBeNewPage || numOfPages === 0) { - await new Promise(res => { + await /** @type {Promise} */ (new Promise(res => { require('../Mediator') .getUser() .get(Key.WALL) @@ -1518,7 +1522,7 @@ const createPost = async (tags, title, content, SEA) => { res() }) - }) + })) } const loadedPost = await new Promise(res => { @@ -1561,7 +1565,7 @@ const createPost = async (tags, title, content, SEA) => { * @returns {Promise} */ const deletePost = async (postId, page) => { - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { require('../Mediator') .getUser() .get(Key.WALL) @@ -1576,15 +1580,15 @@ const deletePost = async (postId, page) => { res() } }) - }) + })) } /** * @param {string} publicKey * @param {boolean} isPrivate Will overwrite previous private status. - * @returns {Promise} + * @returns {Promise} */ -const follow = (publicKey, isPrivate) => { +const follow = async (publicKey, isPrivate) => { /** @type {import('shock-common').Schema.Follow} */ const newFollow = { private: isPrivate, @@ -1592,7 +1596,7 @@ const follow = (publicKey, isPrivate) => { user: publicKey } - return new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { require('../Mediator') .getUser() .get(Key.FOLLOWS) @@ -1605,7 +1609,7 @@ const follow = (publicKey, isPrivate) => { res() } }) - }) + })) } /** @@ -1637,7 +1641,7 @@ const initWall = async () => { const promises = [] promises.push( - new Promise((res, rej) => { + /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.WALL) .get(Key.NUM_OF_PAGES) @@ -1648,11 +1652,11 @@ const initWall = async () => { res() } }) - }) + })) ) promises.push( - new Promise((res, rej) => { + /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.WALL) .get(Key.PAGES) @@ -1670,11 +1674,11 @@ const initWall = async () => { } } ) - }) + })) ) promises.push( - new Promise((res, rej) => { + /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.WALL) .get(Key.PAGES) @@ -1687,7 +1691,7 @@ const initWall = async () => { res() } }) - }) + })) ) await Promise.all(promises) diff --git a/services/gunDB/contact-api/jobs/onAcceptedRequests.js b/services/gunDB/contact-api/jobs/onAcceptedRequests.js index ba9cad5a..255cc107 100644 --- a/services/gunDB/contact-api/jobs/onAcceptedRequests.js +++ b/services/gunDB/contact-api/jobs/onAcceptedRequests.js @@ -92,7 +92,7 @@ const onAcceptedRequests = (user, SEA) => { const recipientEpub = await Utils.pubToEpub(recipientPub) const ourSecret = await SEA.secret(recipientEpub, user._.sea) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { gun .get(Key.HANDSHAKE_NODES) .get(requestAddress) @@ -151,7 +151,7 @@ const onAcceptedRequests = (user, SEA) => { mySecret ) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.USER_TO_INCOMING) .get(recipientPub) @@ -162,9 +162,9 @@ const onAcceptedRequests = (user, SEA) => { res() } }) - }) + })) - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { user .get(Key.STORED_REQS) .get(id) @@ -175,12 +175,12 @@ const onAcceptedRequests = (user, SEA) => { res() } }) - }) + })) // ensure this listeners gets called at least once res() }) - }) + })) } catch (err) { logger.warn(`Jobs.onAcceptedRequests() -> ${err.message}`) logger.error(err) diff --git a/services/gunDB/contact-api/jobs/onOrders.js b/services/gunDB/contact-api/jobs/onOrders.js index cede83c6..54c56d9d 100644 --- a/services/gunDB/contact-api/jobs/onOrders.js +++ b/services/gunDB/contact-api/jobs/onOrders.js @@ -18,9 +18,14 @@ const { } = Common const SchemaManager = require('../../../schema') const LightningServices = require('../../../../utils/lightningServices') - +const { + addInvoice, + myLNDPub +} = require('../../../../utils/lightningServices/v2') +const { writeCoordinate } = require('../../../coordinates') const Key = require('../key') const Utils = require('../utils') +const { gunUUID } = require('../../../../utils') const getUser = () => require('../../Mediator').getUser() @@ -60,28 +65,6 @@ const ordersProcessed = new Set() let currentOrderAddr = '' -/** - * @param {InvoiceRequest} invoiceReq - * @returns {Promise} - */ -const _addInvoice = invoiceReq => - new Promise((resolve, rej) => { - const { - services: { lightning } - } = LightningServices - - lightning.addInvoice(invoiceReq, ( - /** @type {any} */ error, - /** @type {InvoiceResponse} */ response - ) => { - if (error) { - rej(error) - } else { - resolve(response) - } - }) - }) - /** * @param {string} addr * @param {ISEA} SEA @@ -118,8 +101,6 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { )} -- addr: ${addr}` ) - const orderAnswerStartTime = performance.now() - const alreadyAnswered = await getUser() .get(Key.ORDER_TO_RESPONSE) .get(orderID) @@ -130,12 +111,6 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { return } - const orderAnswerEndTime = performance.now() - orderAnswerStartTime - - logger.info(`[PERF] Order Already Answered: ${orderAnswerEndTime}ms`) - - const decryptStartTime = performance.now() - const senderEpub = await Utils.pubToEpub(order.from) const secret = await SEA.secret(senderEpub, getUser()._.sea) @@ -144,10 +119,6 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { SEA.decrypt(order.memo, secret) ]) - const decryptEndTime = performance.now() - decryptStartTime - - logger.info(`[PERF] Decrypt invoice info: ${decryptEndTime}ms`) - const amount = Number(decryptedAmount) if (!isNumber(amount)) { @@ -179,26 +150,19 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { `onOrders() -> Will now create an invoice : ${JSON.stringify(invoiceReq)}` ) - const invoiceStartTime = performance.now() - - const invoice = await _addInvoice(invoiceReq) - - const invoiceEndTime = performance.now() - invoiceStartTime - - logger.info(`[PERF] LND Invoice created in ${invoiceEndTime}ms`) + const invoice = await addInvoice( + invoiceReq.value, + invoiceReq.memo, + true, + invoiceReq.expiry + ) logger.info( 'onOrders() -> Successfully created the invoice, will now encrypt it' ) - const invoiceEncryptStartTime = performance.now() - const encInvoice = await SEA.encrypt(invoice.payment_request, secret) - const invoiceEncryptEndTime = performance.now() - invoiceEncryptStartTime - - logger.info(`[PERF] Invoice encrypted in ${invoiceEncryptEndTime}ms`) - logger.info( `onOrders() -> Will now place the encrypted invoice in order to response usergraph: ${addr}` ) @@ -213,9 +177,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { ackNode } - const invoicePutStartTime = performance.now() - - await new Promise((res, rej) => { + await /** @type {Promise} */ (new Promise((res, rej) => { getUser() .get(Key.ORDER_TO_RESPONSE) .get(orderID) @@ -231,11 +193,9 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { res() } }) - }) + })) - const invoicePutEndTime = performance.now() - invoicePutStartTime - - logger.info(`[PERF] Added invoice to GunDB in ${invoicePutEndTime}ms`) + //logger.info(`[PERF] Added invoice to GunDB in ${invoicePutEndTime}ms`) /** * * @param {Common.Schema.InvoiceWhenListed & {r_hash:Buffer,payment_addr:string}} paidInvoice @@ -261,7 +221,8 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { } getUser() .get('postToTipCount') - .get(postID) + // CAST: Checked above. + .get(/** @type {string} */ (order.ackInfo)) .set(null) // each item in the set is a tip break } diff --git a/services/gunDB/contact-api/utils/index.js b/services/gunDB/contact-api/utils/index.js index bcab3105..3af89742 100644 --- a/services/gunDB/contact-api/utils/index.js +++ b/services/gunDB/contact-api/utils/index.js @@ -212,29 +212,19 @@ const tryAndWait = async (promGen, shouldRetry = () => false) => { */ const pubToEpub = async pub => { try { - const epub = await tryAndWait(async gun => { - const _epub = await CommonUtils.makePromise(res => { - gun + const epub = await timeout10( + CommonUtils.makePromise(res => { + require('../../Mediator/index') + .getGun() .user(pub) .get('epub') - .once( - data => { + .on(data => { + if (typeof data === 'string') { res(data) - }, - { - wait: 1000 } - ) + }) }) - - if (typeof _epub !== 'string') { - throw new TypeError( - `Expected gun.user(pub).get(epub) to be an string. Instead got: ${typeof _epub}` - ) - } - - return _epub - }) + ) return epub } catch (err) { diff --git a/src/server.js b/src/server.js index 5de6dbd7..02f7c033 100644 --- a/src/server.js +++ b/src/server.js @@ -6,6 +6,7 @@ * Module dependencies. */ const server = program => { + const localtunnel = require('localtunnel') const Http = require('http') const Express = require('express') const Crypto = require('crypto') @@ -21,6 +22,7 @@ const server = program => { const bodyParser = require('body-parser') const session = require('express-session') const methodOverride = require('method-override') + const qrcode = require('qrcode-terminal') const { unprotectedRoutes, sensitiveRoutes, @@ -36,6 +38,7 @@ const server = program => { const serverPort = program.serverport || defaults.serverPort const serverHost = program.serverhost || defaults.serverHost + const tunnelHost = process.env.LOCAL_TUNNEL_SERVER || defaults.localtunnelHost // setup winston logging ========== const logger = require('../config/log')( @@ -158,6 +161,10 @@ const server = program => { // eslint-disable-next-line consistent-return const startServer = async () => { + /** + * @type {localtunnel.Tunnel} + */ + let tunnelRef = null try { LightningServices.setDefaults(program) if (!LightningServices.isInitialized()) { @@ -220,6 +227,49 @@ const server = program => { await Storage.init({ dir: storageDirectory }) + if (program.tunnel) { + // setup localtunnel ========== + const [tunnelToken, tunnelSubdomain, tunnelUrl] = await Promise.all([ + Storage.getItem('tunnel/token'), + Storage.getItem('tunnel/subdomain'), + Storage.getItem('tunnel/url') + ]) + const tunnelOpts = { port: serverPort, host: tunnelHost } + if (tunnelToken && tunnelSubdomain) { + tunnelOpts.tunnelToken = tunnelToken + tunnelOpts.subdomain = tunnelSubdomain + logger.info('Recreating tunnel... with subdomain: ' + tunnelSubdomain) + } else { + logger.info('Creating new tunnel... ') + } + const tunnel = await localtunnel(tunnelOpts) + tunnelRef = tunnel + logger.info('Tunnel created! connect to: ' + tunnel.url) + const dataToQr = JSON.stringify({ + internalIP: tunnel.url, + walletPort: 443, + externalIP: tunnel.url + }) + qrcode.generate(dataToQr, { small: true }) + if (!tunnelToken) { + await Promise.all([ + Storage.setItem('tunnel/token', tunnel.token), + Storage.setItem('tunnel/subdomain', tunnel.clientId), + Storage.setItem('tunnel/url', tunnel.url) + ]) + } + if (tunnelUrl && tunnel.url !== tunnelUrl) { + logger.error('New tunnel URL different from OLD tunnel url') + logger.error('OLD: ' + tunnelUrl + ':80') + logger.error('NEW: ' + tunnel.url + ':80') + logger.error('New pair required') + await Promise.all([ + Storage.setItem('tunnel/token', tunnel.token), + Storage.setItem('tunnel/subdomain', tunnel.clientId), + Storage.setItem('tunnel/url', tunnel.url) + ]) + } + } const getSessionSecret = async () => { const sessionSecret = await Storage.getItem('config/sessionSecret') @@ -317,6 +367,9 @@ const server = program => { } catch (err) { logger.error({ exception: err, message: err.message, code: err.code }) logger.info('Restarting server in 30 seconds...') + if (tunnelRef) { + tunnelRef.close() + } await wait(30) startServer() return false diff --git a/src/sockets.js b/src/sockets.js index 353435f2..7404745e 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -67,66 +67,6 @@ module.exports = ( } } - const parseJSON = data => { - try { - if (typeof data === 'string') { - return JSON.parse(data) - } - - return data - } catch (err) { - return data - } - } - - const decryptEvent = ({ eventName, data, socket }) => { - try { - const deviceId = socket.handshake.query['x-shockwallet-device-id'] - if (Encryption.isNonEncrypted(eventName)) { - return data - } - - if (!data) { - return data - } - - const parsedData = parseJSON(data) - - if (!deviceId) { - throw { - field: 'deviceId', - message: 'Please specify a device ID' - } - } - - if (!Encryption.isAuthorizedDevice({ deviceId })) { - throw { - field: 'deviceId', - message: 'Please exchange keys with the API before using the socket' - } - } - - const decryptedKey = Encryption.decryptKey({ - deviceId, - message: parsedData.encryptedKey - }) - const decryptedMessage = Encryption.decryptMessage({ - message: parsedData.encryptedData, - key: decryptedKey, - iv: parsedData.iv - }) - const decryptedData = JSON.parse(decryptedMessage) - return decryptedData - } catch (err) { - logger.error( - `[SOCKET] An error has occurred while decrypting an event (${eventName}):`, - err - ) - - return socket.emit('encryption:error', err) - } - } - const onNewInvoice = (socket, subID) => { const { lightning } = LightningServices.services logger.warn('Subscribing to invoices socket...' + subID) @@ -701,7 +641,7 @@ module.exports = ( } /** - * @param {Common.Schema.SimpleReceivedRequest[]} receivedReqs + * @param {ReadonlyArray} receivedReqs */ const onReceivedReqs = receivedReqs => { const processed = receivedReqs.map(({ id, requestorPK, timestamp }) => { diff --git a/utils/index.js b/utils/index.js index 90e80eee..516d762a 100644 --- a/utils/index.js +++ b/utils/index.js @@ -1,9 +1,21 @@ /** * @format */ +const Gun = require('gun') const { asyncFilter } = require('./helpers') -module.exports = { - asyncFilter +/** + * @returns {string} + */ +const gunUUID = () => { + // @ts-expect-error Not typed + const uuid = Gun.Text.random() + + return uuid +} + +module.exports = { + asyncFilter, + gunUUID } diff --git a/utils/lightningServices/v2.js b/utils/lightningServices/v2.js index e54e3ee4..6f2cccbb 100644 --- a/utils/lightningServices/v2.js +++ b/utils/lightningServices/v2.js @@ -6,6 +6,8 @@ const logger = require('winston') const Common = require('shock-common') const Ramda = require('ramda') +const { writeCoordinate } = require('../../services/coordinates') + const lightningServices = require('./lightning-services') /** * @typedef {import('./types').PaymentV2} PaymentV2 @@ -213,12 +215,50 @@ const isValidSendPaymentInvoiceParams = sendPaymentInvoiceParams => { return true } +/** + * @param {string} payReq + * @returns {Promise} + */ +const decodePayReq = payReq => + Common.Utils.makePromise((res, rej) => { + lightningServices.lightning.decodePayReq( + { pay_req: payReq }, + /** + * @param {{ message: any; }} err + * @param {any} paymentRequest + */ + (err, paymentRequest) => { + if (err) { + rej(new Error(err.message)) + } else { + res(paymentRequest) + } + } + ) + }) + +/** + * @returns {Promise} + */ +const myLNDPub = () => + Common.makePromise((res, rej) => { + const { lightning } = lightningServices.getServices() + + lightning.getInfo({}, (err, data) => { + if (err) { + rej(new Error(err.message)) + } else { + res(data.identity_pubkey) + } + }) + }) + /** * aklssjdklasd * @param {SendPaymentV2Request} sendPaymentRequest * @returns {Promise} */ -const sendPaymentV2 = sendPaymentRequest => { +const sendPaymentV2 = async sendPaymentRequest => { const { services: { router } } = lightningServices @@ -229,7 +269,10 @@ const sendPaymentV2 = sendPaymentRequest => { ) } - return new Promise((res, rej) => { + /** + * @type {import("./types").PaymentV2} + */ + const paymentV2 = await Common.makePromise((res, rej) => { const stream = router.sendPaymentV2(sendPaymentRequest) stream.on( @@ -268,6 +311,33 @@ const sendPaymentV2 = sendPaymentRequest => { } ) }) + + /** @type {Common.Coordinate} */ + const coord = { + amount: Number(paymentV2.value_sat), + id: paymentV2.payment_hash, + inbound: false, + timestamp: Date.now(), + toLndPub: await myLNDPub(), + fromLndPub: undefined, + invoiceMemo: undefined, + type: 'payment' + } + + if (sendPaymentRequest.payment_request) { + const invoice = await decodePayReq(sendPaymentRequest.payment_request) + + coord.invoiceMemo = invoice.description + coord.toLndPub = invoice.destination + } + + if (sendPaymentRequest.dest) { + coord.toLndPub = sendPaymentRequest.dest.toString('base64') + } + + await writeCoordinate(paymentV2.payment_hash, coord) + + return paymentV2 } /** @@ -380,28 +450,6 @@ const listPayments = req => { }) } -/** - * @param {string} payReq - * @returns {Promise} - */ -const decodePayReq = payReq => - Common.Utils.makePromise((res, rej) => { - lightningServices.lightning.decodePayReq( - { pay_req: payReq }, - /** - * @param {{ message: any; }} err - * @param {any} paymentRequest - */ - (err, paymentRequest) => { - if (err) { - rej(new Error(err.message)) - } else { - res(paymentRequest) - } - } - ) - }) - /** * @param {0|1} type * @returns {Promise} diff --git a/yarn.lock b/yarn.lock index 6b9685a7..4fbe3b46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -183,9 +183,9 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/runtime@^7.6.3": - version "7.11.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" - integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== + version "7.12.18" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.18.tgz#af137bd7e7d9705a412b3caaf991fe6aaa97831b" + integrity sha512-BogPQ7ciE6SYAUPtlm9tWbgI9+2AgqSam6QivMgXgAT+fKbgppaj4ZX15MHeLC1PVF5sNk70huBu20XxWOs8Cg== dependencies: regenerator-runtime "^0.13.4" @@ -722,6 +722,14 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw== +"@types/node-fetch@^2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb" + integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node@*": version "12.7.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.4.tgz#64db61e0359eb5a8d99b55e05c729f130a678b04" @@ -1105,6 +1113,11 @@ asn1js@^2.0.26: dependencies: pvutils latest +assert-never@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.2.1.tgz#11f0e363bf146205fb08193b5c7b90f4d1cf44fe" + integrity sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw== + assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" @@ -1587,11 +1600,6 @@ camelcase@^2.0.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= - camelcase@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -1734,7 +1742,7 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= -cliui@^3.0.3, cliui@^3.2.0: +cliui@^3.0.3: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= @@ -1833,7 +1841,7 @@ colour@~0.7.1: resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" integrity sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g= -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -2375,7 +2383,7 @@ enquirer@^2.3.4: dependencies: ansi-colors "^3.2.1" -error-ex@^1.2.0, error-ex@^1.3.1: +error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== @@ -2870,14 +2878,6 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -2941,6 +2941,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -3030,11 +3039,6 @@ gcp-metadata@^4.2.0: gaxios "^4.0.0" json-bigint "^1.0.0" -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -3803,11 +3807,6 @@ is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4525,17 +4524,6 @@ listr2@1.3.8: through "^2.3.8" uuid "^7.0.2" -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -4546,15 +4534,14 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" -localtunnel@^1.9.0: - version "1.9.2" - resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-1.9.2.tgz#0012fcabc29cf964c130a01858768aa2bb65b5af" - integrity sha512-NEKF7bDJE9U3xzJu3kbayF0WTvng6Pww7tzqNb/XtEARYwqw7CKEX7BvOMg98FtE9es2CRizl61gkV3hS8dqYg== +"localtunnel@git://github.com/shocknet/localtunnel#40cc2c2a46b05da2217bf2e20da11a5343a5cce7": + version "2.0.0" + resolved "git://github.com/shocknet/localtunnel#40cc2c2a46b05da2217bf2e20da11a5343a5cce7" dependencies: axios "0.19.0" debug "4.1.1" openurl "1.1.1" - yargs "6.6.0" + yargs "13.3.0" locate-path@^3.0.0: version "3.0.0" @@ -5108,9 +5095,9 @@ normalize-path@^3.0.0: integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalizr@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/normalizr/-/normalizr-3.6.0.tgz#b8bbc4546ffe43c1c2200503041642915fcd3e1c" - integrity sha512-25cd8DiDu+pL46KIaxtVVvvEPjGacJgv0yUg950evr62dQ/ks2JO1kf7+Vi5/rMFjaSTSTls7aCnmRlUSljtiA== + version "3.6.1" + resolved "https://registry.yarnpkg.com/normalizr/-/normalizr-3.6.1.tgz#d367ab840e031ff382141b8d81ce279292ff69fe" + integrity sha512-8iEmqXmPtll8PwbEFrbPoDxVw7MKnNvt3PZzR2Xvq9nggEEOgBlNICPXYzyZ4w4AkHUzCU998mdatER3n2VaMA== npm-bundled@^1.0.1: version "1.0.6" @@ -5379,13 +5366,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -5438,13 +5418,6 @@ path-dirname@^1.0.0: resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -5485,15 +5458,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -5516,11 +5480,6 @@ picomatch@^2.0.5: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -5531,18 +5490,6 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - pirates@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" @@ -5715,6 +5662,11 @@ pvutils@latest: resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.0.17.tgz#ade3c74dfe7178944fe44806626bd2e249d996bf" integrity sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ== +qrcode-terminal@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819" + integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== + qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" @@ -5770,14 +5722,6 @@ react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.10.1.tgz#0612786bf19df406502d935494f0450b40b8294f" integrity sha512-BXUMf9sIOPXXZWqr7+c5SeOKJykyVr2u0UDzEf4LNGc6taGkQe1A9DFD07umCIXz45RLr9oAAwZbAJ0Pkknfaw== -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - read-pkg-up@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" @@ -5786,15 +5730,6 @@ read-pkg-up@^4.0.0: find-up "^3.0.0" read-pkg "^3.0.0" -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -5999,11 +5934,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -6282,10 +6212,10 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shock-common@31.1.0: - version "31.1.0" - resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-31.1.0.tgz#9c8f25d0d405a9a9c52849c2d96452c5ddd17267" - integrity sha512-1490v3gTY5ZNEB/Lelfix+6bI4mfFE8hVrtN4ijz0aj/Cl1ZP5ATKdYO+hffReI+4yDaPSAAWd/HYk9b497Kxw== +shock-common@32.0.0: + version "32.0.0" + resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-32.0.0.tgz#bb7f70d7a572783c46aeae7420179935eb5096d4" + integrity sha512-1GorUFRpkRGXdKT9PImwnj2orpoJaESU/iD+rvL8sqFMLKazkW9LfLAcRwEWCvytWdKFJ35UO2gN49N8dPiRmA== dependencies: immer "^6.0.6" lodash "^4.17.19" @@ -6561,7 +6491,7 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" -string-width@^1.0.1, string-width@^1.0.2: +string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= @@ -6672,13 +6602,6 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -7031,10 +6954,10 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" -typescript@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" - integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== +typescript@latest: + version "4.1.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" + integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== uglify-js@^3.1.4: version "3.10.2" @@ -7267,11 +7190,6 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= - which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -7445,7 +7363,7 @@ xmlhttprequest-ssl@~1.5.4: resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= -y18n@^3.2.0, y18n@^3.2.1: +y18n@^3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= @@ -7483,33 +7401,7 @@ yargs-parser@^13.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" - integrity sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw= - dependencies: - camelcase "^3.0.0" - -yargs@6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" - integrity sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg= - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "^4.2.0" - -yargs@^13.3.0: +yargs@13.3.0, yargs@^13.3.0: version "13.3.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==