diff --git a/config/log.js b/config/log.js index 7b63b7a8..aaf62fe6 100644 --- a/config/log.js +++ b/config/log.js @@ -1,10 +1,21 @@ // config/log.js const winston = require("winston"); +const util = require("util") require("winston-daily-rotate-file"); const winstonAttached = new Map(); +const transform = (info) => { + const args = info[Symbol.for('splat')]; + if (args) { + return {...info, message: util.format(info.message, ...args)}; + } + return info; +} + +const logFormatter = () => ({ transform }) + /** * @param {string} logFileName * @param {string} logLevel @@ -24,15 +35,18 @@ module.exports = (logFileName, logLevel) => { winston.add(new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), + logFormatter(), + winston.format.prettyPrint(), winston.format.timestamp(), + winston.format.simple(), winston.format.align(), winston.format.printf((info) => { const { - timestamp, level, message, ...args + timestamp, level, message } = info; const ts = timestamp.slice(0, 19).replace('T', ' '); - return `${ts} [${level}]: ${message} ${Object.keys(args).length ? JSON.stringify(args, null, 2) : ''}`; + return `${ts} [${level}]: ${typeof message === "object" ? JSON.stringify(message, null, 2) : message}`; }), ) })) diff --git a/package.json b/package.json index d33e13a1..2e374612 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "shockapi", - "version": "2021.04.10", + "version": "2021.04.19", "description": "", "main": "src/server.js", "scripts": { @@ -44,7 +44,7 @@ "jsonfile": "^4.0.0", "jsonwebtoken": "^8.3.0", "localtunnel": "git://github.com/shocknet/localtunnel#40cc2c2a46b05da2217bf2e20da11a5343a5cce7", - "lodash": "^4.17.20", + "lodash": "^4.17.21", "method-override": "^2.3.7", "node-fetch": "^2.6.1", "node-persist": "^3.1.0", diff --git a/src/cors.js b/src/cors.js index f3617730..dcbf1599 100644 --- a/src/cors.js +++ b/src/cors.js @@ -3,7 +3,7 @@ const setAccessControlHeaders = (req, res) => { res.header("Access-Control-Allow-Methods", "OPTIONS,POST,GET,PUT,DELETE") res.header( "Access-Control-Allow-Headers", - "Origin, X-Requested-With, Content-Type, Accept, Authorization, public-key-for-decryption, encryption-device-id" + "Origin, X-Requested-With, Content-Type, Accept, Authorization, public-key-for-decryption, encryption-device-id, public-key-for-decryption" ); }; diff --git a/src/routes.js b/src/routes.js index e4ef8897..0b50fd65 100644 --- a/src/routes.js +++ b/src/routes.js @@ -695,8 +695,8 @@ module.exports = async ( let intervalID = null intervalID = setInterval(() => { - if (tries === 3) { - rej(new Error(`Wallet did not warm up in under 3 seconds.`)) + if (tries === 7) { + rej(new Error(`Wallet did not warm up in under 7 seconds.`)) clearInterval(intervalID) return diff --git a/src/server.js b/src/server.js index 6ff743ac..85d11947 100644 --- a/src/server.js +++ b/src/server.js @@ -6,7 +6,6 @@ * Module dependencies. */ const server = program => { - const localtunnel = require('localtunnel') const Http = require('http') const Express = require('express') const Crypto = require('crypto') @@ -15,6 +14,9 @@ const server = program => { const Path = require('path') const { Logger: CommonLogger } = require('shock-common') const binaryParser = require('socket.io-msgpack-parser') + const { fork } = require('child_process') + const EventEmitter = require('events') + const ECC = require('../utils/ECC') const LightningServices = require('../utils/lightningServices') const Encryption = require('../utils/encryptionStore') @@ -54,6 +56,31 @@ const server = program => { require('../utils/server-utils')(module) logger.info('Mainnet Mode:', !!program.mainnet) + const tunnelTimeout = 5000 + let latestAliveTunnel = 0 + let tunnelHealthInterval = null + const tunnelHealthManager = new EventEmitter() + tunnelHealthManager.on('fork', ({ params, cb }) => { + if (latestAliveTunnel !== 0 && latestAliveTunnel < tunnelTimeout) { + return + } + clearInterval(tunnelHealthInterval) + tunnelHealthInterval = setInterval(() => { + if (Date.now() - latestAliveTunnel > tunnelTimeout) { + console.log('oh no! tunnel is dead, will restart it now') + tunnelHealthManager.emit('fork', { params, cb }) + } + }, 2000) + const forked = fork('src/tunnel.js') + forked.on('message', msg => { + //console.log('Message from child', msg); + if (msg && msg.type === 'info') { + cb(msg.tunnel) + } + latestAliveTunnel = Date.now() + }) + forked.send(params) + }) if (process.env.DISABLE_SHOCK_ENCRYPTION === 'true') { logger.error('Encryption Mode: false') @@ -203,10 +230,6 @@ 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()) { @@ -284,33 +307,36 @@ const server = program => { } 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 + tunnelHealthManager.emit('fork', { + params: tunnelOpts, + cb: async 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) + ]) + } + } }) - 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 storePersistentRandomField = async ({ fieldName, length = 16 }) => { @@ -443,9 +469,6 @@ 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/tunnel.js b/src/tunnel.js new file mode 100644 index 00000000..719b2f70 --- /dev/null +++ b/src/tunnel.js @@ -0,0 +1,14 @@ +const localtunnel = require('localtunnel') +process.on('message', async (tunnelOpts) => { + console.log('Message from parent:', tunnelOpts); + const tunnel = await localtunnel(tunnelOpts) + process.send({ type: 'info', tunnel:{ + url:tunnel.url, + token:tunnel.token, + clientId:tunnel.clientId, + } }); +}); + +setInterval(() => { + process.send({ type: "ping" }); +}, 1000); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index bdead2f5..2ca63533 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4687,10 +4687,10 @@ 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.20, lodash@^4.17.4, lodash@^4.17.5: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^3.0.0: version "3.0.0"