diff --git a/.github/README.md b/.github/README.md index bf5f07aa5..74b0fa608 100644 --- a/.github/README.md +++ b/.github/README.md @@ -79,6 +79,22 @@ Example RTL-Config.json: "multiPass": "password", "port": "3000", "defaultNodeIndex": 1, + "ssl": { + "key": null, + "cert": null, + "ca": null, + "altIp": "127.0.0.1", + "commonName": "localhost", + "countryName": "US", + "encryptionBits": 2048, + "stateName": "Florida", + "localityName": "Miami", + "organizationName": "RTL", + "organizationalUnit": "RTL", + "validForYears": 10, + "rejectUnauthorized": true, + "requestCert": false + }, "SSO": { "rtlSSO": 0, "rtlCookiePath": "", @@ -173,7 +189,7 @@ E.g. if the IP address of your node is 192.168.0.15 then open your browser at th 3. Config tweaks for running RTL server and LND on separate devices on the same network can be found [here](./docs/RTL_setups.md). 4. Any Other setup: **Please be advised, if you are accessing your node remotely via RTL, its critical to encrypt the communication via use of https. You can use solutions like nginx and letsencrypt or TOR to setup secure access for RTL.** -- Sample SSL setup guide can be found [here](./docs/RTL_SSL_setup.md) +- RTL now supports native SSL! See config options [here](./docs/Application_configurations.md). Alternatively, you can run nginx as an SSL proxy. An example nginx SSL setup guide can be found [here](./docs/RTL_SSL_setup.md) - (For advanced users) A sample SSL guide to serve remote access over an encrypted Tor connection can be found [here](./docs/RTL_TOR_setup.md) ### Troubleshooting diff --git a/.github/docs/Application_configurations.md b/.github/docs/Application_configurations.md index eb7e1f1cd..ab9e78a4e 100644 --- a/.github/docs/Application_configurations.md +++ b/.github/docs/Application_configurations.md @@ -1,6 +1,7 @@ RTL allows the user to configure and control specific application parameters for app customization and integration.
-The parameters can be configured via RTL-Config.json file or through environment variables defined at the OS level. Required
-parameters have `default` values for initial setup and can be updated after RTL server initial start.
+The parameters can be configured via RTL-Config.json file or through environment variables defined at the OS level.
+Required parameters have `default` values for initial setup and can be updated after RTL server initial start.
+SSL config parameter can be set as `undefined`, `true` (which uses self-signed certs), `false` (http only), or an `` as outlined below. `key`, `cert` and `ca` parameters are reserved for static file paths to valid SSL key files. If `key`, `cert`, or `ca` are used, no self-signed certificate options will be used. `rejectUnauthorized` and `requestCert` can be used in all cases to enforce x509 certificate validation.

### RTL-Config.json
``` @@ -9,6 +10,22 @@ parameters have `default` values for initial setup and can be updated after RTL "port": "", "host": "", "defaultNodeIndex": , + "ssl": { + "key": "", + "cert": "", + "ca": , + "altIp": "", + "commonName": "", + "countryName": "", + "stateName": "", + "localityName": "", + "organizationName": "", + "organizationalUnit": "", + "encryptionBits": , + "validForYears": , + "rejectUnauthorized": , + "requestCert": + }, "SSO": { "rtlSSO": , "rtlCookiePath": "", @@ -50,6 +67,7 @@ If the environment variables are set, it will take precedence over the parameter
PORT (port number for the rtl node server, default 3000, Required)
HOST (host for the rtl node server, default localhost, Optional)
+SSL (true, false, or a stringified JSON object formatted like the 'ssl' property above)
APP_PASSWORD (Plaintext password to be provided by the parent container, NOT suggested for standalone RTL applications, to be used by Umbrel) (Optional)
LN_IMPLEMENTATION (LND/CLN/ECL. Default 'LND', Required)
LN_SERVER_URL (LN server URL for LNP REST APIs, default https://localhost:8080) (Required)
diff --git a/.github/docs/RTL_SSL_setup.md b/.github/docs/RTL_SSL_setup.md index 3abf92d4c..b4d7a8bcc 100644 --- a/.github/docs/RTL_SSL_setup.md +++ b/.github/docs/RTL_SSL_setup.md @@ -1,4 +1,4 @@ -### Setup https access for RTL +### Setup https forwarding for RTL Forward the ports 80 and 3002 on the router to the device running RTL. Allow the ports through the firewall of the device. diff --git a/Sample-RTL-Config.json b/Sample-RTL-Config.json index a633d7b7e..235eadf70 100644 --- a/Sample-RTL-Config.json +++ b/Sample-RTL-Config.json @@ -2,6 +2,7 @@ "multiPass": "password", "port": "3000", "defaultNodeIndex": 1, + "ssl": false, "SSO": { "rtlSSO": 0, "rtlCookiePath": "", diff --git a/backend/controllers/cln/balance.js b/backend/controllers/cln/balance.js index c621d31c9..906691b89 100644 --- a/backend/controllers/cln/balance.js +++ b/backend/controllers/cln/balance.js @@ -11,7 +11,8 @@ export const getBalance = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/getBalance'; - request(options).then((body) => { + request(options) + .then((body) => { if (!body.totalBalance) { body.totalBalance = 0; } @@ -21,9 +22,16 @@ export const getBalance = (req, res, next) => { if (!body.unconfBalance) { body.unconfBalance = 0; } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Balance', + msg: 'Balance Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/channels.js b/backend/controllers/cln/channels.js index 7176b806a..3e88066a7 100644 --- a/backend/controllers/cln/channels.js +++ b/backend/controllers/cln/channels.js @@ -5,26 +5,39 @@ let options = null; const logger = Logger; const common = Common; export const listChannels = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Channels..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listChannels'; - request(options).then((body) => { + request(options) + .then((body) => { body.map((channel) => { if (!channel.alias || channel.alias === '') { channel.alias = channel.id.substring(0, 20); } - const local = (channel.msatoshi_to_us) ? channel.msatoshi_to_us : 0; - const remote = (channel.msatoshi_to_them) ? channel.msatoshi_to_them : 0; + const local = channel.msatoshi_to_us ? channel.msatoshi_to_us : 0; + const remote = channel.msatoshi_to_them ? channel.msatoshi_to_them : 0; const total = channel.msatoshi_total ? channel.msatoshi_total : 0; - channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); + channel.balancedness = total === 0 ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); return channel; }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channels List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channels List Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -37,28 +50,63 @@ export const openChannel = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/openChannel'; options.body = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Options', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Open Channel Options', + data: options.body + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Opened', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const setChannelFee = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Setting Channel Fee..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Setting Channel Fee..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/setChannelFee'; options.body = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updated Channel Policy', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Update Channel Policy Options', + data: options.body + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Updated Channel Policy', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -71,51 +119,97 @@ export const closeChannel = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } const unilateralTimeoutQuery = req.query.force ? '?unilateralTimeout=1' : ''; - options.url = req.session.selectedNode.ln_server_url + '/v1/channel/closeChannel/' + req.params.channelId + unilateralTimeoutQuery; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel', data: options.url }); - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed', data: body }); + options.url = + req.session.selectedNode.ln_server_url + + '/v1/channel/closeChannel/' + + req.params.channelId + + unilateralTimeoutQuery; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Closing Channel', + data: options.url + }); + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Closed', + data: body + }); res.status(204).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getLocalRemoteBalance = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Local & Remote Balances..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Local & Remote Balances..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/localremotebal'; - request(options).then((body) => { + request(options) + .then((body) => { if (!body.localBalance) { body.localBalance = 0; } if (!body.remoteBalance) { body.remoteBalance = 0; } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Local Remote Balance Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Local Remote Balance Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Local Remote Balance Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const listForwards = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channel List Forwards..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Channel List Forwards..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listForwards?status=' + req.query.status; - request.get(options).then((body) => { + request + .get(options) + .then((body) => { if (body && body.length > 0) { body = common.sortDescByKey(body, 'received_time'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Forwarding History Received For Status' + req.query.status, data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Forwarding History Received For Status' + req.query.status, + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Forwarding History Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/fees.js b/backend/controllers/cln/fees.js index 07f4ad82b..4a848b4a8 100644 --- a/backend/controllers/cln/fees.js +++ b/backend/controllers/cln/fees.js @@ -11,13 +11,21 @@ export const getFees = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/getFees'; - request(options).then((body) => { + request(options) + .then((body) => { if (!body.feeCollected) { body.feeCollected = 0; } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Fee Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/getInfo.js b/backend/controllers/cln/getInfo.js index 266f3f8fb..af1b69135 100644 --- a/backend/controllers/cln/getInfo.js +++ b/backend/controllers/cln/getInfo.js @@ -9,7 +9,12 @@ const common = Common; const clWsClient = CLWSClient; const databaseService = Database; export const getInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting Core Lightning Node Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Getting Core Lightning Node Information..' + }); common.logEnvVariables(req); common.setOptions(req); options = common.getOptions(req); @@ -17,17 +22,28 @@ export const getInfo = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo'; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from Core Lightning server url ' + options.url }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Selected Node ' + req.session.selectedNode.ln_node + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Calling Info from Core Lightning server url ' + options.url + }); if (!options.headers || !options.headers.macaroon) { const errMsg = 'Core lightning get info failed due to bad or missing macaroon!'; const err = common.handleError({ statusCode: 502, message: 'Bad Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { - return request(options).then((body) => { - const body_str = (!body) ? '' : JSON.stringify(body); - const search_idx = (!body) ? -1 : body_str.search('Not Found'); + return request(options) + .then((body) => { + const body_str = !body ? '' : JSON.stringify(body); + const search_idx = !body ? -1 : body_str.search('Not Found'); if (!body || search_idx > -1 || body.error) { if (body && !body.error) { body.error = 'Error From Server!'; @@ -67,13 +83,25 @@ export const getInfo = (req, res, next) => { } req.session.selectedNode.api_version = body.api_version || ''; req.session.selectedNode.ln_version = body.version || ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: "Connecting to the Core Lightning's Websocket Server." + }); clWsClient.updateSelectedNode(req.session.selectedNode); databaseService.loadDatabase(req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Node Information Received', + data: body + }); return res.status(200).json(body); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/invoices.js b/backend/controllers/cln/invoices.js index 0864a290c..8a45a5206 100644 --- a/backend/controllers/cln/invoices.js +++ b/backend/controllers/cln/invoices.js @@ -5,54 +5,107 @@ let options = null; const logger = Logger; const common = Common; export const deleteExpiredInvoice = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Deleting Expired Invoices..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Deleting Expired Invoices..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } const queryStr = req.query.maxExpiry ? '?maxexpiry=' + req.query.maxExpiry : ''; options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/delExpiredInvoice' + queryStr; - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoices Deleted', data: body }); + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoices Deleted', + data: body + }); res.status(204).json({ status: 'Invoice Deleted Successfully' }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoice', 'Delete Invoice Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const listInvoices = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Getting Invoices..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Getting Invoices..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } const labelQuery = req.query.label ? '?label=' + req.query.label : ''; options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/listInvoices' + labelQuery; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url }); - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Invoice', + msg: 'Invoices List URL', + data: options.url + }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Invoice', + msg: 'Invoices List Received', + data: body + }); if (body.invoices && body.invoices.length > 0) { body.invoices = common.sortDescByKey(body.invoices, 'expires_at'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Sorted Invoices List Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const addInvoice = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Creating Invoice..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/genInvoice'; options.body = req.body; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Created', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Created', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoice', 'Add Invoice Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/message.js b/backend/controllers/cln/message.js index c9331679c..9bb943b9e 100644 --- a/backend/controllers/cln/message.js +++ b/backend/controllers/cln/message.js @@ -12,25 +12,48 @@ export const signMessage = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/v1/utility/signMessage'; options.form = { message: req.body.message }; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Signed', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Message Signed', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Message', 'Sign Message Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const verifyMessage = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Verifying Message..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Verifying Message..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/utility/checkMessage/' + req.body.message + '/' + req.body.signature; - request.get(options, (error, response, body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified', data: body }); + options.url = + req.session.selectedNode.ln_server_url + '/v1/utility/checkMessage/' + req.body.message + '/' + req.body.signature; + request + .get(options, (error, response, body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Message Verified', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Message', 'Verify Message Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/network.js b/backend/controllers/cln/network.js index e79981ff7..c98ec01cc 100644 --- a/backend/controllers/cln/network.js +++ b/backend/controllers/cln/network.js @@ -5,16 +5,30 @@ let options = null; const logger = Logger; const common = Common; export const getRoute = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Getting Network Routes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Getting Network Routes..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/network/getRoute/' + req.params.destPubkey + '/' + req.params.amount; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Routes Received', data: body }); + options.url = + req.session.selectedNode.ln_server_url + '/v1/network/getRoute/' + req.params.destPubkey + '/' + req.params.amount; + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Network Routes Received', + data: body + }); res.status(200).json({ routes: body }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Network', 'Query Routes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -26,10 +40,18 @@ export const listNode = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/listNode/' + req.params.id; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Node Lookup Finished', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -41,25 +63,46 @@ export const listChannel = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/listChannel/' + req.params.channelShortId; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Channel Lookup Finished', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Channel Lookup Finished', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Network', 'Channel Lookup Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const feeRates = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Getting Network Fee Rates..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Getting Network Fee Rates..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/feeRates/' + req.params.feeRateStyle; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Fee Rates Received for ' + req.params.feeRateStyle, data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Network Fee Rates Received for ' + req.params.feeRateStyle, + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Network', 'Fee Rates Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/offers.js b/backend/controllers/cln/offers.js index 9a735b568..2e0cb0040 100644 --- a/backend/controllers/cln/offers.js +++ b/backend/controllers/cln/offers.js @@ -8,24 +8,52 @@ const logger = Logger; const common = Common; const databaseService = Database; export const listOfferBookmarks = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Bookmarks..' }); - databaseService.find(req.session.selectedNode, CollectionsEnum.OFFERS).then((offers) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmarks Received', data: offers }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Getting Offer Bookmarks..' + }); + databaseService + .find(req.session.selectedNode, CollectionsEnum.OFFERS) + .then((offers) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Bookmarks Received', + data: offers + }); if (offers && offers.length > 0) { offers = common.sortDescByKey(offers, 'lastUpdatedAt'); } res.status(200).json(offers); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Offers', 'Offer Bookmarks Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const deleteOfferBookmark = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' }); - databaseService.destroy(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmark Deleted', data: deleteRes }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Deleting Offer Bookmark..' + }); + databaseService + .destroy(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr) + .then((deleteRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Bookmark Deleted', + data: deleteRes + }); res.status(204).json(req.params.offerStr); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Offers', 'Offer Bookmark Delete Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -43,11 +71,25 @@ export const listOffers = (req, res, next) => { if (req.query.active_only) { options.url = options.url + '?active_only=' + req.query.active_only; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offers List URL', data: options.url }); - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offers List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Offers', + msg: 'Offers List URL', + data: options.url + }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offers List Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Offers', 'List Offers Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -60,27 +102,56 @@ export const createOffer = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/v1/offers/offer'; options.body = req.body; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Created', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Created', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Offer', 'Create Offer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const fetchOfferInvoice = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Invoice..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Getting Offer Invoice..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/offers/fetchInvoice'; options.body = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offer Invoice Body', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Invoice Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Offers', + msg: 'Offer Invoice Body', + data: options.body + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Invoice Received', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Offers', 'Get Offer Invoice Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -92,10 +163,19 @@ export const disableOffer = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/offers/disableOffer/' + req.params.offerID; - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Disabled', data: body }); + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Disabled', + data: body + }); res.status(202).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Offers', 'Disable Offer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/onchain.js b/backend/controllers/cln/onchain.js index cd97479db..1ce07c6bc 100644 --- a/backend/controllers/cln/onchain.js +++ b/backend/controllers/cln/onchain.js @@ -5,33 +5,66 @@ let options = null; const logger = Logger; const common = Common; export const getNewAddress = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Generating New Address..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Generating New Address..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/newaddr?addrType=' + req.query.type; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'New Address Generated', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'OnChain', 'New Address Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const onChainWithdraw = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Withdrawing from On Chain..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Withdrawing from On Chain..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/withdraw'; options.body = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'OnChain', msg: 'OnChain Withdraw Options', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Withdraw Finished', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'OnChain', + msg: 'OnChain Withdraw Options', + data: options.body + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Withdraw Finished', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'OnChain', 'Withdraw Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -43,13 +76,21 @@ export const getUTXOs = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/listFunds'; - request(options).then((body) => { + request(options) + .then((body) => { if (body.outputs) { body.outputs = common.sortDescByStrKey(body.outputs, 'status'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Funds List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Funds List Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'OnChain', 'List Funds Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/payments.js b/backend/controllers/cln/payments.js index 30852f6f2..918d33f95 100644 --- a/backend/controllers/cln/payments.js +++ b/backend/controllers/cln/payments.js @@ -36,7 +36,9 @@ function summaryReducer(accumulator, mpp) { } function groupBy(payments) { const paymentsInGroups = payments.reduce(paymentReducer, {}); - const paymentsGrpArray = Object.keys(paymentsInGroups).map((key) => ((paymentsInGroups[key].length && paymentsInGroups[key].length > 1) ? common.sortDescByKey(paymentsInGroups[key], 'partid') : paymentsInGroups[key])); + const paymentsGrpArray = Object.keys(paymentsInGroups).map((key) => paymentsInGroups[key].length && paymentsInGroups[key].length > 1 + ? common.sortDescByKey(paymentsInGroups[key], 'partid') + : paymentsInGroups[key]); return paymentsGrpArray.reduce((acc, curr) => { let temp = {}; if (curr.length && curr.length === 1) { @@ -47,10 +49,21 @@ function groupBy(payments) { delete temp.partid; } else { - const paySummary = curr.reduce(summaryReducer, { msatoshi: 0, msatoshi_sent: 0, status: (curr[0] && curr[0].status) ? curr[0].status : 'failed' }); + const paySummary = curr.reduce(summaryReducer, { + msatoshi: 0, + msatoshi_sent: 0, + status: curr[0] && curr[0].status ? curr[0].status : 'failed' + }); temp = { - is_group: true, is_expanded: false, total_parts: (curr.length ? curr.length : 0), status: paySummary.status, payment_hash: curr[0].payment_hash, - destination: curr[0].destination, msatoshi: paySummary.msatoshi, msatoshi_sent: paySummary.msatoshi_sent, created_at: curr[0].created_at, + is_group: true, + is_expanded: false, + total_parts: curr.length ? curr.length : 0, + status: paySummary.status, + payment_hash: curr[0].payment_hash, + destination: curr[0].destination, + msatoshi: paySummary.msatoshi, + msatoshi_sent: paySummary.msatoshi_sent, + created_at: curr[0].created_at, mpps: curr }; if (paySummary.bolt11) { @@ -70,29 +83,56 @@ export const listPayments = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Payment List Received', + data: body.payments + }); if (body && body.payments && body.payments.length > 0) { body.payments = common.sortDescByKey(body.payments, 'created_at'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sorted Payments List Received', data: body.payments }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Sorted Payments List Received', + data: body.payments + }); res.status(200).json(groupBy(body.payments)); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const decodePayment = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Decoding Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Decoding Payment..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/utility/decode/' + req.params.payReq; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Decoded', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -103,37 +143,74 @@ export const postPayment = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } if (req.body.paymentType === 'KEYSEND') { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Keysend Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Keysend Payment..' + }); options.url = req.session.selectedNode.ln_server_url + '/v1/pay/keysend'; options.body = req.body; } else { if (req.body.paymentType === 'OFFER') { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Offer Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Sending Offer Payment..' + }); options.body = { invoice: req.body.invoice }; } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Invoice Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Sending Invoice Payment..' + }); options.body = req.body; } options.url = req.session.selectedNode.ln_server_url + '/v1/pay'; } - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Sent', + data: body + }); if (req.body.paymentType === 'OFFER') { if (req.body.saveToDB && req.body.bolt12) { - const offerToUpdate = { bolt12: req.body.bolt12, amountmSat: (req.body.zeroAmtOffer ? 0 : req.body.amount), title: req.body.title, lastUpdatedAt: new Date(Date.now()).getTime() }; + const offerToUpdate = { + bolt12: req.body.bolt12, + amountmSat: req.body.zeroAmtOffer ? 0 : req.body.amount, + title: req.body.title, + lastUpdatedAt: new Date(Date.now()).getTime() + }; if (req.body.vendor) { offerToUpdate['vendor'] = req.body.vendor; } if (req.body.description) { offerToUpdate['description'] = req.body.description; } - return databaseService.update(req.session.selectedNode, CollectionsEnum.OFFERS, offerToUpdate, CollectionFieldsEnum.BOLT12, req.body.bolt12).then((updatedOffer) => { + return databaseService + .update(req.session.selectedNode, CollectionsEnum.OFFERS, offerToUpdate, CollectionFieldsEnum.BOLT12, req.body.bolt12) + .then((updatedOffer) => { logger.log({ level: 'DEBUG', fileName: 'Offer', msg: 'Offer Updated', data: updatedOffer }); return res.status(201).json({ paymentResponse: body, saveToDBResponse: updatedOffer }); - }).catch((errDB) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Payments', msg: 'Offer DB update error', error: errDB }); + }) + .catch((errDB) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Payments', + msg: 'Offer DB update error', + error: errDB + }); return res.status(201).json({ paymentResponse: body, saveToDBError: errDB }); }); } @@ -147,7 +224,8 @@ export const postPayment = (req, res, next) => { if (req.body.paymentType === 'KEYSEND') { return res.status(201).json(body); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/peers.js b/backend/controllers/cln/peers.js index fc9bee073..33acaa736 100644 --- a/backend/controllers/cln/peers.js +++ b/backend/controllers/cln/peers.js @@ -11,16 +11,24 @@ export const getPeers = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers'; - request(options).then((body) => { + request(options) + .then((body) => { body.forEach((peer) => { if (!peer.alias || peer.alias === '') { peer.alias = peer.id.substring(0, 20); } }); - const peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers with Alias Received', data: peers }); + const peers = body ? common.sortDescByStrKey(body, 'alias') : []; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peers with Alias Received', + data: peers + }); res.status(200).json(peers); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -33,19 +41,36 @@ export const postPeer = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/v1/peer/connect'; options.body = req.body; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peer Connected', + data: body + }); options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers'; - request(options).then((body) => { - let peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; + request(options) + .then((body) => { + let peers = body ? common.sortDescByStrKey(body, 'alias') : []; peers = common.newestOnTop(peers, 'id', req.body.id); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peers List after Connect Received', + data: peers + }); res.status(201).json(peers); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -56,11 +81,21 @@ export const deletePeer = (req, res, next) => { if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/peer/disconnect/' + req.params.peerId + '?force=' + req.query.force; - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected', data: body }); + options.url = + req.session.selectedNode.ln_server_url + '/v1/peer/disconnect/' + req.params.peerId + '?force=' + req.query.force; + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peer Disconnected', + data: body + }); res.status(204).json({}); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Detach Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/cln/webSocketClient.js b/backend/controllers/cln/webSocketClient.js index 3fdad6da1..d1f8c4ec6 100644 --- a/backend/controllers/cln/webSocketClient.js +++ b/backend/controllers/cln/webSocketClient.js @@ -16,10 +16,15 @@ export class CLWebSocketClient { if (this.reconnectTimeOut) { return; } - this.waitTime = (this.waitTime >= 64) ? 64 : (this.waitTime * 2); + this.waitTime = this.waitTime >= 64 ? 64 : this.waitTime * 2; this.reconnectTimeOut = setTimeout(() => { if (clWsClt.selectedNode) { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Reconnecting to the Core Lightning\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Reconnecting to the Core Lightning's Websocket Server.." + }); this.connect(clWsClt.selectedNode); } this.reconnectTimeOut = null; @@ -36,7 +41,8 @@ export class CLWebSocketClient { } } else { - if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) { + if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && + selectedNode.ln_server_url) { clientExists.reConnect = true; this.connectWithClient(clientExists); } @@ -47,17 +53,32 @@ export class CLWebSocketClient { } }; this.connectWithClient = (clWsClt) => { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connecting to the Core Lightning\'s Websocket Server..' }); - const WS_LINK = (clWsClt.selectedNode.ln_server_url).replace(/^http/, 'ws') + '/v1/ws'; + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Connecting to the Core Lightning's Websocket Server.." + }); + const WS_LINK = clWsClt.selectedNode.ln_server_url.replace(/^http/, 'ws') + '/v1/ws'; const mcrnHexEncoded = Buffer.from(fs.readFileSync(join(clWsClt.selectedNode.macaroon_path, 'access.macaroon'))).toString('hex'); clWsClt.webSocketClient = new WebSocket(WS_LINK, [mcrnHexEncoded, 'hex'], { rejectUnauthorized: false }); clWsClt.webSocketClient.onopen = () => { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connected to the Core Lightning\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Connected to the Core Lightning's Websocket Server.." + }); this.waitTime = 0.5; }; clWsClt.webSocketClient.onclose = (e) => { if (clWsClt && clWsClt.selectedNode && clWsClt.selectedNode.ln_implementation === 'CLN') { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Web socket disconnected, will reconnect again...' }); + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: 'Web socket disconnected, will reconnect again...' + }); clWsClt.webSocketClient.close(); if (clWsClt.reConnect) { this.reconnet(clWsClt); @@ -65,16 +86,34 @@ export class CLWebSocketClient { } }; clWsClt.webSocketClient.onmessage = (msg) => { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'DEBUG', fileName: 'CLWebSocket', msg: 'Received message from the server..', data: msg.data }); - msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data; + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'DEBUG', + fileName: 'CLWebSocket', + msg: 'Received message from the server..', + data: msg.data + }); + msg = typeof msg.data === 'string' ? JSON.parse(msg.data) : msg.data; msg['source'] = 'CLN'; const msgStr = JSON.stringify(msg); this.wsServer.sendEventsToAllLNClients(msgStr, clWsClt.selectedNode); }; clWsClt.webSocketClient.onerror = (err) => { - if (clWsClt.selectedNode.api_version === '' || !clWsClt.selectedNode.api_version || this.common.isVersionCompatible(clWsClt.selectedNode.api_version, '0.6.0')) { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'ERROR', fileName: 'CLWebSocket', msg: 'Web socket error', error: err }); - const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }')); + if (clWsClt.selectedNode.api_version === '' || + !clWsClt.selectedNode.api_version || + this.common.isVersionCompatible(clWsClt.selectedNode.api_version, '0.6.0')) { + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'ERROR', + fileName: 'CLWebSocket', + msg: 'Web socket error', + error: err + }); + const errStr = typeof err === 'object' && err.message + ? JSON.stringify({ error: err.message }) + : typeof err === 'object' + ? JSON.stringify({ error: err }) + : '{ "error": ' + err + ' }'; this.wsServer.sendErrorToAllLNClients(errStr, clWsClt.selectedNode); clWsClt.webSocketClient.close(); if (clWsClt.reConnect) { @@ -89,7 +128,12 @@ export class CLWebSocketClient { this.disconnect = (selectedNode) => { const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index); if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) { - this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the Core Lightning\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clientExists.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Disconnecting from the Core Lightning's Websocket Server.." + }); clientExists.reConnect = false; clientExists.webSocketClient.close(); const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index); diff --git a/backend/controllers/eclair/channels.js b/backend/controllers/eclair/channels.js index a1de58981..d4a22dcf9 100644 --- a/backend/controllers/eclair/channels.js +++ b/backend/controllers/eclair/channels.js @@ -13,24 +13,55 @@ export const simplifyAllChannels = (selNode, channels) => { nodeId: channel.nodeId ? channel.nodeId : '', channelId: channel.channelId ? channel.channelId : '', state: channel.state ? channel.state : '', - channelFlags: channel.data && channel.data.commitments && channel.data.commitments.channelFlags ? channel.data.commitments.channelFlags : 0, - toLocal: (channel.data.commitments.localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.localCommit.spec.toLocal / 1000) : 0, - toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) : 0, + channelFlags: channel.data && channel.data.commitments && channel.data.commitments.channelFlags + ? channel.data.commitments.channelFlags + : 0, + toLocal: channel.data.commitments.localCommit.spec.toLocal + ? Math.round(+channel.data.commitments.localCommit.spec.toLocal / 1000) + : 0, + toRemote: channel.data.commitments.localCommit.spec.toRemote + ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) + : 0, shortChannelId: channel.data && channel.data.shortChannelId ? channel.data.shortChannelId : '', - isFunder: channel.data && channel.data.commitments && channel.data.commitments.localParams && channel.data.commitments.localParams.isFunder ? channel.data.commitments.localParams.isFunder : false, + isFunder: channel.data && + channel.data.commitments && + channel.data.commitments.localParams && + channel.data.commitments.localParams.isFunder + ? channel.data.commitments.localParams.isFunder + : false, buried: channel.data && channel.data.buried ? channel.data.buried : false, - feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0, - feeRatePerKw: (channel.data.commitments.localCommit.spec.feeratePerKw) ? channel.data.commitments.localCommit.spec.feeratePerKw : 0, - feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0, + feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat + ? channel.data.channelUpdate.feeBaseMsat + : 0, + feeRatePerKw: channel.data.commitments.localCommit.spec.feeratePerKw + ? channel.data.commitments.localCommit.spec.feeratePerKw + : 0, + feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths + ? channel.data.channelUpdate.feeProportionalMillionths + : 0, alias: '' }); }); channelNodeIds = channelNodeIds.substring(1); options.url = selNode.ln_server_url + '/nodes'; options.form = { nodeIds: channelNodeIds }; - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Node Ids to find alias', data: channelNodeIds }); - return request.post(options).then((nodes) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Filtered Nodes Received', data: nodes }); + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Node Ids to find alias', + data: channelNodeIds + }); + return request + .post(options) + .then((nodes) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Filtered Nodes Received', + data: nodes + }); let foundPeer = null; simplifiedChannels.map((channel) => { foundPeer = nodes.find((channelWithAlias) => channel.nodeId === channelWithAlias.nodeId); @@ -38,7 +69,8 @@ export const simplifyAllChannels = (selNode, channels) => { return channel; }); return simplifiedChannels; - }).catch((err) => simplifiedChannels); + }) + .catch((err) => simplifiedChannels); }; export const getChannels = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'List Channels..' }); @@ -50,44 +82,101 @@ export const getChannels = (req, res, next) => { options.form = {}; if (req.query && req.query.nodeId) { options.form = req.query; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels Node Id', data: options.form }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Channels Node Id', + data: options.form + }); } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Options', data: options }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Options', + data: options + }); if (common.read_dummy_data) { - common.getDummyData('Channels', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(data); }); + common.getDummyData('Channels', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(data); + }); } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels List Received', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Channels List Received', + data: body + }); if (body && body.length) { return simplifyAllChannels(req.session.selectedNode, body).then((simplifiedChannels) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Simplified Channels with Alias Received', data: simplifiedChannels }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Simplified Channels with Alias Received', + data: simplifiedChannels + }); res.status(200).json(simplifiedChannels); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Empty Channels List Received' }); - res.status(200).json({ activeChannels: [], pendingChannels: [], inactiveChannels: [], lightningBalances: { localBalance: 0, remoteBalance: 0 }, channelStatus: { active: { channels: 0, capacity: 0 }, inactive: { channels: 0, capacity: 0 }, pending: { channels: 0, capacity: 0 } } }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Empty Channels List Received' + }); + res.status(200).json({ + activeChannels: [], + pendingChannels: [], + inactiveChannels: [], + lightningBalances: { localBalance: 0, remoteBalance: 0 }, + channelStatus: { + active: { channels: 0, capacity: 0 }, + inactive: { channels: 0, capacity: 0 }, + pending: { channels: 0, capacity: 0 } + } + }); } - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } }; export const getChannelStats = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channel States..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Channel States..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/channelstats'; options.form = {}; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel States Received', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel States Received', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Get Channel Stats Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -100,28 +189,63 @@ export const openChannel = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/open'; options.form = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Params', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Open Channel Params', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Opened', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const updateChannelRelayFee = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updating Channel Relay Fee..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Updating Channel Relay Fee..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/updaterelayfee'; options.form = req.query; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Relay Fee Params', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Relay Fee Updated', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Update Relay Fee Params', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Relay Fee Updated', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Update Relay Fee Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -132,20 +256,51 @@ export const closeChannel = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } if (req.query.force !== 'true') { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Closing Channel..' + }); options.url = req.session.selectedNode.ln_server_url + '/close'; } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Force Closing Channel..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Force Closing Channel..' + }); options.url = req.session.selectedNode.ln_server_url + '/forceclose'; } options.form = { channelId: req.query.channelId }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Close URL', data: options.url }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Close Params', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Close URL', + data: options.url + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Close Params', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Closed', + data: body + }); res.status(204).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/eclair/fees.js b/backend/controllers/eclair/fees.js index 05176e70d..bf3094290 100644 --- a/backend/controllers/eclair/fees.js +++ b/backend/controllers/eclair/fees.js @@ -13,11 +13,11 @@ export const arrangeFees = (selNode, body, current_time) => { fee = Math.round((relayedEle.amountIn - relayedEle.amountOut) / 1000); if (relayedEle.timestamp) { if (relayedEle.timestamp.unix) { - if ((relayedEle.timestamp.unix * 1000) >= day_start_time) { + if (relayedEle.timestamp.unix * 1000 >= day_start_time) { fees.daily_fee = fees.daily_fee + fee; fees.daily_txs = fees.daily_txs + 1; } - if ((relayedEle.timestamp.unix * 1000) >= week_start_time) { + if (relayedEle.timestamp.unix * 1000 >= week_start_time) { fees.weekly_fee = fees.weekly_fee + fee; fees.weekly_txs = fees.weekly_txs + 1; } @@ -91,7 +91,13 @@ export const arrangePayments = (selNode, body) => { payments.sent = common.sortDescByKey(payments.sent, 'firstPartTimestamp'); payments.received = common.sortDescByKey(payments.received, 'firstPartTimestamp'); payments.relayed = common.sortDescByKey(payments.relayed, 'timestamp'); - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Payments Received', data: payments }); + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Arranged Payments Received', + data: payments + }); return payments; }; export const getFees = (req, res, next) => { @@ -102,21 +108,38 @@ export const getFees = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/audit'; const today = new Date(Date.now()); - const tillToday = (Math.round(today.getTime() / 1000)).toString(); - const fromLastMonth = (Math.round(new Date(today.getFullYear(), today.getMonth() - 1, today.getDate() + 1, 0, 0, 0).getTime() / 1000)).toString(); + const tillToday = Math.round(today.getTime() / 1000).toString(); + const fromLastMonth = Math.round(new Date(today.getFullYear(), today.getMonth() - 1, today.getDate() + 1, 0, 0, 0).getTime() / 1000).toString(); options.form = { from: fromLastMonth, to: tillToday }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Fee Audit Options', data: options.form }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Fee Audit Options', + data: options.form + }); if (common.read_dummy_data) { - common.getDummyData('Fees', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangeFees(req.session.selectedNode, data, Math.round((new Date().getTime())))); }); + common.getDummyData('Fees', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(arrangeFees(req.session.selectedNode, data, Math.round(new Date().getTime()))); + }); } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body }); - res.status(200).json(arrangeFees(req.session.selectedNode, body, Math.round((new Date().getTime())))); - }).catch((errRes) => { + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Fee Received', + data: body + }); + res.status(200).json(arrangeFees(req.session.selectedNode, body, Math.round(new Date().getTime()))); + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -131,14 +154,24 @@ export const getPayments = (req, res, next) => { options.url = req.session.selectedNode.ln_server_url + '/audit'; options.form = null; if (common.read_dummy_data) { - common.getDummyData('Payments', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangePayments(req.session.selectedNode, data)); }); + common.getDummyData('Payments', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(arrangePayments(req.session.selectedNode, data)); + }); } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Payments Received', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Payments Received', + data: body + }); res.status(200).json(arrangePayments(req.session.selectedNode, body)); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Fees', 'Get Payments Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/eclair/getInfo.js b/backend/controllers/eclair/getInfo.js index 0f90db68f..213507150 100644 --- a/backend/controllers/eclair/getInfo.js +++ b/backend/controllers/eclair/getInfo.js @@ -9,7 +9,12 @@ const common = Common; const eclWsClient = ECLWSClient; const databaseService = Database; export const getInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting Eclair Node Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Getting Eclair Node Information..' + }); common.logEnvVariables(req); common.setOptions(req); options = common.getOptions(req); @@ -18,8 +23,18 @@ export const getInfo = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/getinfo'; options.form = {}; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from Eclair server url ' + options.url }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Selected Node ' + req.session.selectedNode.ln_node + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Calling Info from Eclair server url ' + options.url + }); if (common.read_dummy_data) { common.getDummyData('GetInfo', req.session.selectedNode.ln_implementation).then((data) => { data.lnImplementation = 'Eclair'; @@ -33,15 +48,29 @@ export const getInfo = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { - return request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Eclair\'s Websocket Server.' }); + return request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: "Connecting to the Eclair's Websocket Server." + }); body.lnImplementation = 'Eclair'; req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; eclWsClient.updateSelectedNode(req.session.selectedNode); databaseService.loadDatabase(req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Node Information Received', + data: body + }); return res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/eclair/invoices.js b/backend/controllers/eclair/invoices.js index 947e31ebd..eefe89b0d 100644 --- a/backend/controllers/eclair/invoices.js +++ b/backend/controllers/eclair/invoices.js @@ -7,7 +7,7 @@ const common = Common; let pendingInvoices = []; export const getReceivedPaymentInfo = (lnServerUrl, invoice) => { let idx = -1; - invoice.expiresAt = (!invoice.expiry) ? null : (+invoice.timestamp + +invoice.expiry); + invoice.expiresAt = !invoice.expiry ? null : +invoice.timestamp + +invoice.expiry; if (invoice.amount) { invoice.amount = Math.round(invoice.amount / 1000); } @@ -15,14 +15,16 @@ export const getReceivedPaymentInfo = (lnServerUrl, invoice) => { if (idx < 0) { options.url = lnServerUrl + '/getreceivedinfo'; options.form = { paymentHash: invoice.paymentHash }; - return request(options).then((response) => { + return request(options) + .then((response) => { invoice.status = response.status.type; if (response.status && response.status.type === 'received') { invoice.amountSettled = response.status.amount ? Math.round(response.status.amount / 1000) : 0; invoice.receivedAt = response.status.receivedAt ? Math.round(response.status.receivedAt / 1000) : 0; } return invoice; - }).catch((err) => { + }) + .catch((err) => { invoice.status = 'unknown'; return invoice; }); @@ -41,20 +43,34 @@ export const getInvoice = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/getinvoice'; options.form = { paymentHash: req.params.paymentHash }; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Found', data: body }); - const current_time = (Math.round(new Date(Date.now()).getTime() / 1000)); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Found', + data: body + }); + const current_time = Math.round(new Date(Date.now()).getTime() / 1000); body.amount = body.amount ? body.amount / 1000 : 0; - body.expiresAt = body.expiresAt ? body.expiresAt : (body.timestamp + body.expiry); - body.status = body.status ? body.status : (+body.expiresAt < current_time ? 'expired' : 'unknown'); + body.expiresAt = body.expiresAt ? body.expiresAt : body.timestamp + body.expiry; + body.status = body.status ? body.status : +body.expiresAt < current_time ? 'expired' : 'unknown'; res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoices', 'Get Invoice Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const listInvoices = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Getting List Invoices..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Getting List Invoices..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -68,59 +84,89 @@ export const listInvoices = (req, res, next) => { options2.form = {}; if (common.read_dummy_data) { return common.getDummyData('Invoices', req.session.selectedNode.ln_implementation).then((body) => { - const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0]; - pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1]; - return Promise.all(invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))). - then((values) => { + const invoices = !body[0] || body[0].length <= 0 ? [] : body[0]; + pendingInvoices = !body[1] || body[1].length <= 0 ? [] : body[1]; + return Promise.all(invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))).then((values) => { body = common.sortDescByKey(invoices, 'expiresAt'); return res.status(200).json(invoices); }); }); } else { - return Promise.all([request(options1), request(options2)]). - then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); - const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0]; - pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1]; + return Promise.all([request(options1), request(options2)]) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Invoice', + msg: 'Invoices List Received', + data: body + }); + const invoices = !body[0] || body[0].length <= 0 ? [] : body[0]; + pendingInvoices = !body[1] || body[1].length <= 0 ? [] : body[1]; if (invoices && invoices.length > 0) { - return Promise.all(invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))). - then((values) => { + return Promise.all(invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))) + .then((values) => { body = common.sortDescByKey(invoices, 'expiresAt'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Sorted Invoices List Received', data: invoices }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Sorted Invoices List Received', + data: invoices + }); return res.status(200).json(invoices); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Empty List Invoice Received' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Empty List Invoice Received' + }); return res.status(200).json([]); } - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } }; export const createInvoice = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Creating Invoice..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/createinvoice'; options.form = req.body; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Created', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Created', + data: body + }); if (body.amount) { body.amount = Math.round(body.amount / 1000); } res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoices', 'Create Invoice Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/eclair/network.js b/backend/controllers/eclair/network.js index bd5f7001c..a58e92c40 100644 --- a/backend/controllers/eclair/network.js +++ b/backend/controllers/eclair/network.js @@ -12,10 +12,19 @@ export const getNodes = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/nodes'; options.form = { nodeIds: req.params.id }; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Node Lookup Finished', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/eclair/onchain.js b/backend/controllers/eclair/onchain.js index 05e255fad..d2ea82104 100644 --- a/backend/controllers/eclair/onchain.js +++ b/backend/controllers/eclair/onchain.js @@ -16,23 +16,42 @@ export const arrangeBalances = (body) => { return body; }; export const getNewAddress = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Generating New Address..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Generating New Address..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/getnewaddress'; options.form = {}; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'New Address Generated', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'OnChain', 'Get New Address Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getBalance = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Balance..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Getting On Chain Balance..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -40,21 +59,36 @@ export const getBalance = (req, res, next) => { options.url = req.session.selectedNode.ln_server_url + '/onchainbalance'; options.form = {}; if (common.read_dummy_data) { - common.getDummyData('OnChainBalance', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangeBalances(data)); }); + common.getDummyData('OnChainBalance', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(arrangeBalances(data)); + }); } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Balance Received', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'On Chain Balance Received', + data: body + }); res.status(200).json(arrangeBalances(body)); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'OnChain', 'Get Balance Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } }; export const getTransactions = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Getting On Chain Transactions..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -64,20 +98,40 @@ export const getTransactions = (req, res, next) => { count: req.query.count, skip: req.query.skip }; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions Options', data: options.form }); - request.post(options).then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Getting On Chain Transactions Options', + data: options.form + }); + request + .post(options) + .then((body) => { if (body && body.length > 0) { body = common.sortDescByKey(body, 'timestamp'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Transactions Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'On Chain Transactions Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'OnChain', 'Get Transactions Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const sendFunds = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Sending On Chain Funds..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Sending On Chain Funds..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -88,11 +142,26 @@ export const sendFunds = (req, res, next) => { amountSatoshis: req.body.amount, confirmationTarget: req.body.blocks }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Onchain', msg: 'Send Funds Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Onchain', msg: 'On Chain Funds Sent', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Onchain', + msg: 'Send Funds Options', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Onchain', + msg: 'On Chain Funds Sent', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'OnChain', 'Send Funds Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/eclair/payments.js b/backend/controllers/eclair/payments.js index 17adb0067..911b49c66 100644 --- a/backend/controllers/eclair/payments.js +++ b/backend/controllers/eclair/payments.js @@ -7,8 +7,16 @@ const common = Common; export const getSentInfoFromPaymentRequest = (selNode, payment) => { options.url = selNode.ln_server_url + '/getsentinfo'; options.form = { paymentHash: payment }; - return request.post(options).then((body) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Sent Information Received', data: body }); + return request + .post(options) + .then((body) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Payment Sent Information Received', + data: body + }); body.forEach((sentPayment) => { if (sentPayment.amount) { sentPayment.amount = Math.round(sentPayment.amount / 1000); @@ -18,31 +26,55 @@ export const getSentInfoFromPaymentRequest = (selNode, payment) => { } }); return body; - }).catch((err) => err); + }) + .catch((err) => err); }; export const getQueryNodes = (selNode, nodeIds) => { options.url = selNode.ln_server_url + '/nodes'; options.form = { nodeIds: nodeIds.reduce((acc, curr) => acc + ',' + curr) }; - return request.post(options).then((nodes) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Nodes Received', data: nodes }); + return request + .post(options) + .then((nodes) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Query Nodes Received', + data: nodes + }); return nodes; - }).catch((err) => []); + }) + .catch((err) => []); }; export const decodePayment = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Decoding Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Decoding Payment..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/parseinvoice'; options.form = { invoice: req.params.invoice }; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Decoded', + data: body + }); if (body.amount) { body.amount = Math.round(body.amount / 1000); } res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -55,17 +87,37 @@ export const postPayment = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/payinvoice'; options.form = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Send Payment Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Invoice Paid', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Send Payment Options', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Invoice Paid', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const queryPaymentRoute = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Querying Payment Route..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Querying Payment Route..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -75,9 +127,23 @@ export const queryPaymentRoute = (req, res, next) => { nodeId: req.query.nodeId, amountMsat: req.query.amountMsat }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Payment Route Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Payment Route Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Query Payment Route Options', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Query Payment Route Received', + data: body + }); if (body && body.routes && body.routes.length) { let allRoutesNodeIds = []; allRoutesNodeIds = body.routes.reduce((accRoutes, currRoute) => [...new Set([...accRoutes, ...currRoute.nodeIds])], []); @@ -90,39 +156,67 @@ export const queryPaymentRoute = (req, res, next) => { return node; }); }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Query Routes with Alias Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Query Routes with Alias Received', + data: body + }); res.status(200).json(body); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Empty Payment Route Information Received' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Empty Payment Route Information Received' + }); res.status(200).json({ routes: [] }); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'Query Route Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getSentPaymentsInformation = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting Sent Payment Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Getting Sent Payment Information..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } if (req.body.payments) { const paymentsArr = req.body.payments.split(','); - return Promise.all(paymentsArr.map((payment) => getSentInfoFromPaymentRequest(req.session.selectedNode, payment))). - then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent Information Received', data: values }); + return Promise.all(paymentsArr.map((payment) => getSentInfoFromPaymentRequest(req.session.selectedNode, payment))) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Sent Information Received', + data: values + }); return res.status(200).json(values); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'Sent Payment Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Empty Sent Payment Information Received' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Empty Sent Payment Information Received' + }); return res.status(200).json([]); } }; diff --git a/backend/controllers/eclair/peers.js b/backend/controllers/eclair/peers.js index c28ccb5af..12dfd7176 100644 --- a/backend/controllers/eclair/peers.js +++ b/backend/controllers/eclair/peers.js @@ -7,10 +7,19 @@ const common = Common; export const getFilteredNodes = (selNode, peersNodeIds) => { options.url = selNode.ln_server_url + '/nodes'; options.form = { nodeIds: peersNodeIds }; - return request.post(options).then((nodes) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Peers', msg: 'Filtered Nodes Received', data: nodes }); + return request + .post(options) + .then((nodes) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Filtered Nodes Received', + data: nodes + }); return nodes; - }).catch((err) => []); + }) + .catch((err) => []); }; export const getPeers = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Getting Peers..' }); @@ -21,14 +30,26 @@ export const getPeers = (req, res, next) => { options.url = req.session.selectedNode.ln_server_url + '/peers'; options.form = {}; if (common.read_dummy_data) { - common.getDummyData('Peers', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(data); }); + common.getDummyData('Peers', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(data); + }); } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peers List Received', + data: body + }); if (body && body.length) { let peersNodeIds = ''; - body.forEach((peer) => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; }); + body.forEach((peer) => { + peersNodeIds = peersNodeIds + ',' + peer.nodeId; + }); peersNodeIds = peersNodeIds.substring(1); return getFilteredNodes(req.session.selectedNode, peersNodeIds).then((peersWithAlias) => { let foundPeer = null; @@ -38,16 +59,27 @@ export const getPeers = (req, res, next) => { return peer; }); body = common.sortDescByStrKey(body, 'alias'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Sorted Peers List Received', + data: body + }); res.status(200).json(body); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Empty Peers Received' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Empty Peers Received' + }); res.status(200).json([]); } - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -63,10 +95,24 @@ export const connectPeer = (req, res, next) => { options.form = {}; if (req.query) { options.form = req.query; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Connect Peer Params', data: options.form }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Connect Peer Params', + data: options.form + }); } - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peer Connected', + data: body + }); if (typeof body === 'string' && body.includes('already connected')) { const err = common.handleError({ statusCode: 500, message: 'Connect Peer Error', error: body }, 'Peers', body, req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); @@ -77,11 +123,21 @@ export const connectPeer = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/peers'; options.form = {}; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List after Connect', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peers List after Connect', + data: body + }); if (body && body.length) { let peersNodeIds = ''; - body.forEach((peer) => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; }); + body.forEach((peer) => { + peersNodeIds = peersNodeIds + ',' + peer.nodeId; + }); peersNodeIds = peersNodeIds.substring(1); return getFilteredNodes(req.session.selectedNode, peersNodeIds).then((peersWithAlias) => { let foundPeer = null; @@ -90,20 +146,32 @@ export const connectPeer = (req, res, next) => { peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20); return peer; }); - let peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; - peers = common.newestOnTop(peers, 'nodeId', req.query.nodeId ? req.query.nodeId : req.query.uri ? req.query.uri.substring(0, req.query.uri.indexOf('@')) : ''); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers }); + let peers = body ? common.sortDescByStrKey(body, 'alias') : []; + peers = common.newestOnTop(peers, 'nodeId', req.query.nodeId + ? req.query.nodeId + : req.query.uri + ? req.query.uri.substring(0, req.query.uri.indexOf('@')) + : ''); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peers List after Connect Received', + data: peers + }); res.status(201).json(peers); }); } else { res.status(201).json([]); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -118,12 +186,27 @@ export const deletePeer = (req, res, next) => { options.form = {}; if (req.params.nodeId) { options.form = { nodeId: req.params.nodeId }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Disconnect Peer Params', data: options.form }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Disconnect Peer Params', + data: options.form + }); } - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peer Disconnected', + data: body + }); res.status(204).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/eclair/webSocketClient.js b/backend/controllers/eclair/webSocketClient.js index fdcd7d3dc..3b28b8b69 100644 --- a/backend/controllers/eclair/webSocketClient.js +++ b/backend/controllers/eclair/webSocketClient.js @@ -14,10 +14,15 @@ export class ECLWebSocketClient { if (this.reconnectTimeOut) { return; } - this.waitTime = (this.waitTime >= 64) ? 64 : (this.waitTime * 2); + this.waitTime = this.waitTime >= 64 ? 64 : this.waitTime * 2; this.reconnectTimeOut = setTimeout(() => { if (eclWsClt.selectedNode) { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Reconnecting to the Eclair\'s Websocket Server..' }); + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: "Reconnecting to the Eclair's Websocket Server.." + }); this.connect(eclWsClt.selectedNode); } this.reconnectTimeOut = null; @@ -34,7 +39,8 @@ export class ECLWebSocketClient { } } else { - if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) { + if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && + selectedNode.ln_server_url) { clientExists.reConnect = true; this.connectWithClient(clientExists); } @@ -45,18 +51,38 @@ export class ECLWebSocketClient { } }; this.connectWithClient = (eclWsClt) => { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Connecting to the Eclair\'s Websocket Server..' }); - const UpdatedLNServerURL = (eclWsClt.selectedNode.ln_server_url).replace(/^http/, 'ws'); - const firstSubStrIndex = (UpdatedLNServerURL.indexOf('//') + 2); - const WS_LINK = UpdatedLNServerURL.slice(0, firstSubStrIndex) + ':' + eclWsClt.selectedNode.ln_api_password + '@' + UpdatedLNServerURL.slice(firstSubStrIndex) + '/ws'; + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: "Connecting to the Eclair's Websocket Server.." + }); + const UpdatedLNServerURL = eclWsClt.selectedNode.ln_server_url.replace(/^http/, 'ws'); + const firstSubStrIndex = UpdatedLNServerURL.indexOf('//') + 2; + const WS_LINK = UpdatedLNServerURL.slice(0, firstSubStrIndex) + + ':' + + eclWsClt.selectedNode.ln_api_password + + '@' + + UpdatedLNServerURL.slice(firstSubStrIndex) + + '/ws'; eclWsClt.webSocketClient = new WebSocket(WS_LINK); eclWsClt.webSocketClient.onopen = () => { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Connected to the Eclair\'s Websocket Server..' }); + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: "Connected to the Eclair's Websocket Server.." + }); this.waitTime = 0.5; }; eclWsClt.webSocketClient.onclose = (e) => { if (eclWsClt && eclWsClt.selectedNode && eclWsClt.selectedNode.ln_implementation === 'ECL') { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Web socket disconnected, will reconnect again...' }); + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: 'Web socket disconnected, will reconnect again...' + }); eclWsClt.webSocketClient.close(); if (eclWsClt.reConnect) { this.reconnet(eclWsClt); @@ -64,16 +90,34 @@ export class ECLWebSocketClient { } }; eclWsClt.webSocketClient.onmessage = (msg) => { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'DEBUG', fileName: 'ECLWebSocket', msg: 'Received message from the server..', data: msg.data }); - msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data; + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'DEBUG', + fileName: 'ECLWebSocket', + msg: 'Received message from the server..', + data: msg.data + }); + msg = typeof msg.data === 'string' ? JSON.parse(msg.data) : msg.data; msg['source'] = 'ECL'; const msgStr = JSON.stringify(msg); this.wsServer.sendEventsToAllLNClients(msgStr, eclWsClt.selectedNode); }; eclWsClt.webSocketClient.onerror = (err) => { - if (eclWsClt.selectedNode.ln_version === '' || !eclWsClt.selectedNode.ln_version || this.common.isVersionCompatible(eclWsClt.selectedNode.ln, '0.5.0')) { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'ERROR', fileName: 'ECLWebSocket', msg: 'Web socket error', error: err }); - const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }')); + if (eclWsClt.selectedNode.ln_version === '' || + !eclWsClt.selectedNode.ln_version || + this.common.isVersionCompatible(eclWsClt.selectedNode.ln, '0.5.0')) { + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'ERROR', + fileName: 'ECLWebSocket', + msg: 'Web socket error', + error: err + }); + const errStr = typeof err === 'object' && err.message + ? JSON.stringify({ error: err.message }) + : typeof err === 'object' + ? JSON.stringify({ error: err }) + : '{ "error": ' + err + ' }'; this.wsServer.sendErrorToAllLNClients(errStr, eclWsClt.selectedNode); eclWsClt.webSocketClient.close(); if (eclWsClt.reConnect) { @@ -88,7 +132,12 @@ export class ECLWebSocketClient { this.disconnect = (selectedNode) => { const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index); if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) { - this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Disconnecting from the Eclair\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clientExists.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: "Disconnecting from the Eclair's Websocket Server.." + }); clientExists.reConnect = false; clientExists.webSocketClient.close(); const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index); diff --git a/backend/controllers/lnd/balance.js b/backend/controllers/lnd/balance.js index a73898d5b..afa925001 100644 --- a/backend/controllers/lnd/balance.js +++ b/backend/controllers/lnd/balance.js @@ -12,9 +12,22 @@ export const getBlockchainBalance = (req, res, next) => { } options.url = req.session.selectedNode.ln_server_url + '/v1/balance/blockchain'; options.qs = req.query; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Balance', msg: 'Request params', data: req.params }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Balance', msg: 'Request Query', data: req.query }); - request(options).then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Balance', + msg: 'Request params', + data: req.params + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Balance', + msg: 'Request Query', + data: req.query + }); + request(options) + .then((body) => { if (body) { if (!body.total_balance) { body.total_balance = 0; @@ -25,10 +38,17 @@ export const getBlockchainBalance = (req, res, next) => { if (!body.unconfirmed_balance) { body.unconfirmed_balance = 0; } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Balance', + msg: 'Balance Received', + data: body + }); res.status(200).json(body); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/channels.js b/backend/controllers/lnd/channels.js index fd735b6f1..8a5eeabed 100644 --- a/backend/controllers/lnd/channels.js +++ b/backend/controllers/lnd/channels.js @@ -5,19 +5,32 @@ let options = null; const logger = Logger; const common = Common; export const getAliasForChannel = (selNode, channel) => { - const pubkey = (channel.remote_pubkey) ? channel.remote_pubkey : (channel.remote_node_pub) ? channel.remote_node_pub : ''; + const pubkey = channel.remote_pubkey ? channel.remote_pubkey : channel.remote_node_pub ? channel.remote_node_pub : ''; options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey; - return request(options).then((aliasBody) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Alias Received', data: aliasBody.node.alias }); + return request(options) + .then((aliasBody) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Alias Received', + data: aliasBody.node.alias + }); channel.remote_alias = aliasBody.node.alias; return aliasBody.node.alias; - }).catch((err) => { + }) + .catch((err) => { channel.remote_alias = pubkey.slice(0, 10) + '...' + pubkey.slice(-10); return pubkey; }); }; export const getAllChannels = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Channels..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -27,43 +40,70 @@ export const getAllChannels = (req, res, next) => { let local = 0; let remote = 0; let total = 0; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels List Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Channels List Received', + data: body + }); if (body.channels) { return Promise.all(body.channels.map((channel) => { - local = (channel.local_balance) ? +channel.local_balance : 0; - remote = (channel.remote_balance) ? +channel.remote_balance : 0; + local = channel.local_balance ? +channel.local_balance : 0; + remote = channel.remote_balance ? +channel.remote_balance : 0; total = local + remote; - channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); + channel.balancedness = total === 0 ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); return getAliasForChannel(req.session.selectedNode, channel); - })).then((values) => { + })) + .then((values) => { body.channels = common.sortDescByKey(body.channels, 'balancedness'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Sorted Channels List Received', + data: body + }); return res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Get All Channel Aliases Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } else { body.channels = []; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Empty Channels List Received' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Empty Channels List Received' + }); return res.status(200).json(body); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getPendingChannels = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Pending Channels..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Pending Channels..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channels/pending'; options.qs = req.query; - request(options).then((body) => { + request(options) + .then((body) => { if (!body.total_limbo_balance) { body.total_limbo_balance = 0; } @@ -80,37 +120,59 @@ export const getPendingChannels = (req, res, next) => { if (body.waiting_close_channels && body.waiting_close_channels.length > 0) { body.waiting_close_channels.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel))); } - return Promise.all(promises).then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Pending Channels List Received', data: body }); + return Promise.all(promises) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Pending Channels List Received', + data: body + }); return res.status(200).json(body); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Get Pending Channel Aliases Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'List Pending Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getClosedChannels = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Closed Channels..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Closed Channels..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channels/closed'; options.qs = req.query; - request(options).then((body) => { + request(options) + .then((body) => { if (body.channels && body.channels.length > 0) { return Promise.all(body.channels.map((channel) => { - channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type; + channel.close_type = !channel.close_type ? 'COOPERATIVE_CLOSE' : channel.close_type; return getAliasForChannel(req.session.selectedNode, channel); - })).then((values) => { + })) + .then((values) => { body.channels = common.sortDescByKey(body.channels, 'close_height'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Closed Channels List Received', + data: body + }); return res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Get Closed Channel Aliases Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -119,7 +181,8 @@ export const getClosedChannels = (req, res, next) => { body.channels = []; return res.status(200).json(body); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'List Closed Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -144,10 +207,19 @@ export const postChannel = (req, res, next) => { options.form.sat_per_byte = req.body.trans_type_value; } options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Opened', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -176,26 +248,46 @@ export const postTransactions = (req, res, next) => { options.form.last_hop_pubkey = Buffer.from(req.body.lastHopPubkey, 'hex').toString('base64'); } options.form = JSON.stringify(options.form); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Send Payment Options', data: options.form }); - request.post(options).then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Send Payment Options', + data: options.form + }); + request + .post(options) + .then((body) => { if (body.payment_error) { const err = common.handleError(body.payment_error, 'Channels', 'Send Payment Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Payment Sent', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Payment Sent', + data: body + }); res.status(201).json(body); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Send Payment Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const closeChannel = (req, res, next) => { try { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Closing Channel..' + }); if (!req.session.selectedNode) { - const err = common.handleError({ message: 'Session Expired after a day\'s inactivity.', statusCode: 401 }, 'Session Expired', 'Session Expiry Error', null); + const err = common.handleError({ message: "Session Expired after a day's inactivity.", statusCode: 401 }, 'Session Expired', 'Session Expiry Error', null); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options = common.getOptions(req); @@ -210,18 +302,40 @@ export const closeChannel = (req, res, next) => { if (req.query.sat_per_byte) { options.url = options.url + '&sat_per_byte=' + req.query.sat_per_byte; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel Options URL', data: options.url }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Closing Channel Options URL', + data: options.url + }); request.delete(options); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Close Requested' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Close Requested' + }); res.status(202).json({ message: 'Close channel request has been submitted.' }); } catch (error) { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Channels', msg: 'Close Channel Error', error: error.message }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Channels', + msg: 'Close Channel Error', + error: error.message + }); return res.status(500).json({ message: 'Close Channel Error', error: error.message }); } }; export const postChanPolicy = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updating Channel Policy..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Updating Channel Policy..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -246,11 +360,26 @@ export const postChanPolicy = (req, res, next) => { chan_point: { funding_txid_str: txid_str, output_index: parseInt(output_idx) } }); } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Policy Updated', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Update Channel Policy Options', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Policy Updated', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/channelsBackup.js b/backend/controllers/lnd/channelsBackup.js index 59b9cc7c4..ac98b164c 100644 --- a/backend/controllers/lnd/channelsBackup.js +++ b/backend/controllers/lnd/channelsBackup.js @@ -31,7 +31,12 @@ function getFilesList(channelBackupPath, callback) { }); } export const getBackup = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Getting Channel Backup..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Getting Channel Backup..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -44,7 +49,12 @@ export const getBackup = (req, res, next) => { options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup'; } else { - channel_backup_file = req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; + channel_backup_file = + req.session.selectedNode.channel_backup_path + + sep + + 'channel-' + + req.params.channelPoint.replace(':', '-') + + '.bak'; message = 'Channel Backup Successful.'; const channelpoint = req.params.channelPoint.replace(':', '/'); options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/' + channelpoint; @@ -63,25 +73,44 @@ export const getBackup = (req, res, next) => { } } } - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'ChannelsBackup', msg: 'Channel Backup Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'ChannelsBackup', + msg: 'Channel Backup Received', + data: body + }); fs.writeFile(channel_backup_file, JSON.stringify(body), (errRes) => { if (errRes) { const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Backed up and Saved', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Channel Backed up and Saved', + data: body + }); res.status(200).json({ message: message }); } }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const postBackupVerify = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Verifying Channel Backup..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Verifying Channel Backup..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -116,7 +145,12 @@ export const postBackupVerify = (req, res, next) => { } else { message = 'Channel Verify Successful.'; - channel_verify_file = req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; + channel_verify_file = + req.session.selectedNode.channel_backup_path + + sep + + 'channel-' + + req.params.channelPoint.replace(':', '-') + + '.bak'; const exists = fs.existsSync(channel_verify_file); if (exists) { verify_backup = fs.readFileSync(channel_verify_file, 'utf-8'); @@ -130,18 +164,31 @@ export const postBackupVerify = (req, res, next) => { } } if (verify_backup !== '') { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Backup Verified', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Channel Backup Verified', + data: body + }); res.status(201).json({ message: message }); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'ChannelsBackup', 'Verify Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } }; export const postRestore = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Restoring Channel Backup..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Restoring Channel Backup..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -188,7 +235,14 @@ export const postRestore = (req, res, next) => { } else { message = 'Channel Restore Successful.'; - channel_restore_file = req.session.selectedNode.channel_backup_path + sep + 'restore' + sep + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; + channel_restore_file = + req.session.selectedNode.channel_backup_path + + sep + + 'restore' + + sep + + 'channel-' + + req.params.channelPoint.replace(':', '-') + + '.bak'; const exists = fs.existsSync(channel_restore_file); if (exists) { restore_backup = fs.readFileSync(channel_restore_file, 'utf-8'); @@ -202,8 +256,16 @@ export const postRestore = (req, res, next) => { } } if (restore_backup !== '') { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'ChannelBackup', msg: 'Channel Restored', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'ChannelBackup', + msg: 'Channel Restored', + data: body + }); if (req.params.channelPoint === 'ALL') { channel_restore_file = channel_restore_file + 'channel-all.bak'; } @@ -215,13 +277,18 @@ export const postRestore = (req, res, next) => { return res.status(err.statusCode).json({ message: err.error, list: getFilesListRes }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Restored and Saved' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Channel Restored and Saved' + }); return res.status(201).json({ message: message, list: getFilesListRes }); } }); }); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'ChannelsBackup', 'Restore Channel Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/fees.js b/backend/controllers/lnd/fees.js index 37d500f22..8c13ca051 100644 --- a/backend/controllers/lnd/fees.js +++ b/backend/controllers/lnd/fees.js @@ -12,22 +12,53 @@ export const getFees = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/fees'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Fee Received', + data: body + }); const today = new Date(Date.now()); const start_date = new Date(today.getFullYear(), today.getMonth(), 1, 0, 0, 0); - const current_time = (Math.round(today.getTime() / 1000)); - const month_start_time = (Math.round(start_date.getTime() / 1000)); + const current_time = Math.round(today.getTime() / 1000); + const month_start_time = Math.round(start_date.getTime() / 1000); const week_start_time = current_time - 604800; const day_start_time = current_time - 86400; return getAllForwardingEvents(req, month_start_time, current_time, 0, 'fees', (history) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Forwarding History Received', data: history }); - const daily_sum = history.forwarding_events.reduce((acc, curr) => ((curr.timestamp >= day_start_time) ? [(acc[0] + 1), (acc[1] + +curr.fee_msat)] : acc), [0, 0]); - const weekly_sum = history.forwarding_events.reduce((acc, curr) => ((curr.timestamp >= week_start_time) ? [(acc[0] + 1), (acc[1] + +curr.fee_msat)] : acc), [0, 0]); - const monthly_sum = history.forwarding_events.reduce((acc, curr) => [(acc[0] + 1), (acc[1] + +curr.fee_msat)], [0, 0]); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Daily Sum (Transactions, Fee)', data: daily_sum }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Weekly Sum (Transactions, Fee)', data: weekly_sum }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Monthly Sum (Transactions, Fee)', data: monthly_sum }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Forwarding History Received', + data: history + }); + const daily_sum = history.forwarding_events.reduce((acc, curr) => (curr.timestamp >= day_start_time ? [acc[0] + 1, acc[1] + +curr.fee_msat] : acc), [0, 0]); + const weekly_sum = history.forwarding_events.reduce((acc, curr) => (curr.timestamp >= week_start_time ? [acc[0] + 1, acc[1] + +curr.fee_msat] : acc), [0, 0]); + const monthly_sum = history.forwarding_events.reduce((acc, curr) => [acc[0] + 1, acc[1] + +curr.fee_msat], [0, 0]); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Daily Sum (Transactions, Fee)', + data: daily_sum + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Weekly Sum (Transactions, Fee)', + data: weekly_sum + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Monthly Sum (Transactions, Fee)', + data: monthly_sum + }); body.daily_tx_count = daily_sum[0]; body.weekly_tx_count = weekly_sum[0]; body.monthly_tx_count = monthly_sum[0]; @@ -36,12 +67,25 @@ export const getFees = (req, res, next) => { body.month_fee_sum = (monthly_sum[1] / 1000).toFixed(2); body.forwarding_events_history = history; if (history.error) { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Fees', msg: 'Fetch Forwarding Events Error', error: history.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Fees', + msg: 'Fetch Forwarding Events Error', + error: history.error + }); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fees Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Fees Received', + data: body + }); res.status(200).json(body); }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Fees', 'Get Forwarding Events Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/getInfo.js b/backend/controllers/lnd/getInfo.js index cdd570e11..b0ae73254 100644 --- a/backend/controllers/lnd/getInfo.js +++ b/backend/controllers/lnd/getInfo.js @@ -9,7 +9,12 @@ const common = Common; const lndWsClient = LNDWSClient; const databaseService = Database; export const getInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting LND Node Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Getting LND Node Information..' + }); common.logEnvVariables(req); common.setOptions(req); options = common.getOptions(req); @@ -17,8 +22,18 @@ export const getInfo = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo'; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from LND server url ' + options.url }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Selected Node ' + req.session.selectedNode.ln_node + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Calling Info from LND server url ' + options.url + }); if (!options.headers || !options.headers['Grpc-Metadata-macaroon']) { const errMsg = 'LND Get info failed due to bad or missing macaroon! Please check RTL-Config.json to verify the setup!'; const err = common.handleError({ statusCode: 502, message: 'Bad or Missing Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode); @@ -31,9 +46,10 @@ export const getInfo = (req, res, next) => { } return node; }); - return request(options).then((body) => { - const body_str = (!body) ? '' : JSON.stringify(body); - const search_idx = (!body) ? -1 : body_str.search('Not Found'); + return request(options) + .then((body) => { + const body_str = !body ? '' : JSON.stringify(body); + const search_idx = !body ? -1 : body_str.search('Not Found'); if (!body || search_idx > -1 || body.error) { if (body && !body.error) { body.error = 'Error From Server!'; @@ -45,10 +61,17 @@ export const getInfo = (req, res, next) => { req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; lndWsClient.updateSelectedNode(req.session.selectedNode); databaseService.loadDatabase(req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Node Information Received', + data: body + }); return res.status(200).json(body); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/graph.js b/backend/controllers/lnd/graph.js index 1ac1ad47f..dec0f21f5 100644 --- a/backend/controllers/lnd/graph.js +++ b/backend/controllers/lnd/graph.js @@ -6,119 +6,229 @@ const logger = Logger; const common = Common; export const getAliasFromPubkey = (selNode, pubkey) => { options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey; - return request(options).then((res) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Graph', msg: 'Alias Received', data: res.node.alias }); + return request(options) + .then((res) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Graph', + msg: 'Alias Received', + data: res.node.alias + }); return res.node.alias; - }). - catch((err) => pubkey.substring(0, 17) + '...'); + }) + .catch((err) => pubkey.substring(0, 17) + '...'); }; export const getDescribeGraph = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Network Graph..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Network Graph..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/graph'; - request.get(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Network Graph Received', data: body }); + request + .get(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Network Graph Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Graph', 'Describe Graph Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getGraphInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Graph Information..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/graph/info'; - request.get(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Information Received', data: body }); + request + .get(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Information Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Graph', 'Graph Information Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getGraphNode = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Node Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Graph Node Information..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/graph/node/' + req.params.pubKey; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Node Information Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Node Information Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Graph', 'Get Node Info Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getGraphEdge = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Edge Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Graph Edge Information..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/graph/edge/' + req.params.chanid; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Edge Information Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Edge Information Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Graph', 'Get Edge Info Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getQueryRoutes = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Routes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Graph Routes..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/graph/routes/' + req.params.destPubkey + '/' + req.params.amount; + options.url = + req.session.selectedNode.ln_server_url + '/v1/graph/routes/' + req.params.destPubkey + '/' + req.params.amount; if (req.query.outgoing_chan_id) { options.url = options.url + '?outgoing_chan_id=' + req.query.outgoing_chan_id; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes URL', data: options.url }); - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes Received', data: body }); - if (body.routes && body.routes.length && body.routes.length > 0 && body.routes[0].hops && body.routes[0].hops.length && body.routes[0].hops.length > 0) { - return Promise.all(body.routes[0].hops.map((hop) => getAliasFromPubkey(req.session.selectedNode, hop.pub_key))). - then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Graph', + msg: 'Query Routes URL', + data: options.url + }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Graph', + msg: 'Query Routes Received', + data: body + }); + if (body.routes && + body.routes.length && + body.routes.length > 0 && + body.routes[0].hops && + body.routes[0].hops.length && + body.routes[0].hops.length > 0) { + return Promise.all(body.routes[0].hops.map((hop) => getAliasFromPubkey(req.session.selectedNode, hop.pub_key))) + .then((values) => { body.routes[0].hops.map((hop, i) => { hop.hop_sequence = i + 1; hop.pubkey_alias = values[i]; return hop; }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes with Alias Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Routes with Alias Received', + data: body + }); res.status(200).json(body); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Routes Received', + data: body + }); return res.status(200).json(body); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getRemoteFeePolicy = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Remote Fee Policy..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Remote Fee Policy..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/graph/edge/' + req.params.chanid; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Edge Info Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Graph', + msg: 'Edge Info Received', + data: body + }); let remoteNodeFee = {}; if (body.node1_pub === req.params.localPubkey) { remoteNodeFee = { @@ -134,9 +244,16 @@ export const getRemoteFeePolicy = (req, res, next) => { fee_rate_milli_msat: body.node1_policy.fee_rate_milli_msat }; } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Remote Fee Policy Received', data: remoteNodeFee }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Remote Fee Policy Received', + data: remoteNodeFee + }); res.status(200).json(remoteNodeFee); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Graph', 'Remote Fee Policy Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -148,12 +265,18 @@ export const getAliasesForPubkeys = (req, res, next) => { } if (req.query.pubkeys) { const pubkeyArr = req.query.pubkeys.split(','); - return Promise.all(pubkeyArr.map((pubkey) => getAliasFromPubkey(req.session.selectedNode, pubkey))). - then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Node Alias', data: values }); + return Promise.all(pubkeyArr.map((pubkey) => getAliasFromPubkey(req.session.selectedNode, pubkey))) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Node Alias', + data: values + }); res.status(200).json(values); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Graph', 'Get Aliases for Pubkeys Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/invoices.js b/backend/controllers/lnd/invoices.js index 2823d0535..cbf46f7ec 100644 --- a/backend/controllers/lnd/invoices.js +++ b/backend/controllers/lnd/invoices.js @@ -7,7 +7,12 @@ const logger = Logger; const common = Common; const lndWsClient = LNDWSClient; export const invoiceLookup = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Getting Invoice Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Getting Invoice Information..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -19,38 +24,75 @@ export const invoiceLookup = (req, res, next) => { else { options.url = options.url + '?payment_hash=' + req.query.payment_hash; } - request(options).then((body) => { + request(options) + .then((body) => { body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : ''; body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : ''; - body.description_hash = body.description_hash ? Buffer.from(body.description_hash, 'base64').toString('hex') : null; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Information Received', data: body }); + body.description_hash = body.description_hash + ? Buffer.from(body.description_hash, 'base64').toString('hex') + : null; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Information Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoices', 'Invoice Lookup Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const listInvoices = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Getting List Invoices..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Getting List Invoices..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=' + req.query.num_max_invoices + '&index_offset=' + req.query.index_offset + - '&reversed=' + req.query.reversed; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); + options.url = + req.session.selectedNode.ln_server_url + + '/v1/invoices?num_max_invoices=' + + req.query.num_max_invoices + + '&index_offset=' + + req.query.index_offset + + '&reversed=' + + req.query.reversed; + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Invoice', + msg: 'Invoices List Received', + data: body + }); if (body.invoices && body.invoices.length > 0) { body.invoices.forEach((invoice) => { invoice.r_preimage = invoice.r_preimage ? Buffer.from(invoice.r_preimage, 'base64').toString('hex') : ''; invoice.r_hash = invoice.r_hash ? Buffer.from(invoice.r_hash, 'base64').toString('hex') : ''; - invoice.description_hash = invoice.description_hash ? Buffer.from(invoice.description_hash, 'base64').toString('hex') : null; + invoice.description_hash = invoice.description_hash + ? Buffer.from(invoice.description_hash, 'base64').toString('hex') + : null; }); body.invoices = common.sortDescByKey(body.invoices, 'creation_date'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Sorted Invoices List Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -74,8 +116,16 @@ export const addInvoice = (req, res, next) => { options.form.value = req.body.amount; } options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Added', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Added', + data: body + }); try { if (body.r_hash) { lndWsClient.subscribeToInvoice(options, req.session.selectedNode, body.r_hash); @@ -83,13 +133,22 @@ export const addInvoice = (req, res, next) => { } catch (errRes) { const err = common.handleError(errRes, 'Invoices', 'Subscribe to Newly Added Invoice Error', req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Invoice', msg: 'Subscribe to Newly Added Invoice Error', error: err }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Invoice', + msg: 'Subscribe to Newly Added Invoice Error', + error: err + }); } body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : ''; body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : ''; - body.description_hash = body.description_hash ? Buffer.from(body.description_hash, 'base64').toString('hex') : null; + body.description_hash = body.description_hash + ? Buffer.from(body.description_hash, 'base64').toString('hex') + : null; res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoices', 'Add Invoice Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/message.js b/backend/controllers/lnd/message.js index 19a62aa31..547152df4 100644 --- a/backend/controllers/lnd/message.js +++ b/backend/controllers/lnd/message.js @@ -14,16 +14,30 @@ export const signMessage = (req, res, next) => { options.form = JSON.stringify({ msg: Buffer.from(req.body.message).toString('base64') }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Signed', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Message Signed', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Messages', 'Sign Message Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const verifyMessage = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Verifying Message..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Verifying Message..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -33,10 +47,19 @@ export const verifyMessage = (req, res, next) => { msg: Buffer.from(req.body.message).toString('base64'), signature: req.body.signature }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Message Verified', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Messages', 'Verify Message Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/newAddress.js b/backend/controllers/lnd/newAddress.js index ac2f9e158..9f9201d4a 100644 --- a/backend/controllers/lnd/newAddress.js +++ b/backend/controllers/lnd/newAddress.js @@ -5,16 +5,29 @@ let options = null; const logger = Logger; const common = Common; export const getNewAddress = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'NewAddress', msg: 'Getting New Address..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'NewAddress', + msg: 'Getting New Address..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/newaddress?type=' + req.query.type; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'NewAddress', msg: 'New Address Generated', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'NewAddress', + msg: 'New Address Generated', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'NewAddress', 'New Address Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/payments.js b/backend/controllers/lnd/payments.js index d51e51c1c..8190833c8 100644 --- a/backend/controllers/lnd/payments.js +++ b/backend/controllers/lnd/payments.js @@ -6,96 +6,198 @@ const logger = Logger; const common = Common; export const decodePaymentFromPaymentRequest = (selNode, payment) => { options.url = selNode.ln_server_url + '/v1/payreq/' + payment; - return request(options).then((res) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'PayReq', msg: 'Description Received', data: res.description }); + return request(options) + .then((res) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'PayReq', + msg: 'Description Received', + data: res.description + }); return res; - }).catch((err) => { }); + }) + .catch((err) => { }); }; export const decodePayment = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Decoding Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Decoding Payment..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/payreq/' + req.params.payRequest; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Payment Decoded', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Payment Decoded', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'PayRequest', 'Decode Payment Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const decodePayments = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Decoding Payments List..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Decoding Payments List..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } if (req.body.payments) { const paymentsArr = req.body.payments.split(','); - return Promise.all(paymentsArr.map((payment) => decodePaymentFromPaymentRequest(req.session.selectedNode, payment))). - then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Payment List Decoded', data: values }); + return Promise.all(paymentsArr.map((payment) => decodePaymentFromPaymentRequest(req.session.selectedNode, payment))) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Payment List Decoded', + data: values + }); res.status(200).json(values); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'PayRequest', 'Decode Payments Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Empty Payment List Decoded' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Empty Payment List Decoded' + }); return res.status(200).json([]); } }; export const getPayments = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting Payments List..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Getting Payments List..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/payments?max_payments=' + req.query.max_payments + '&index_offset=' + req.query.index_offset + '&reversed=' + req.query.reversed; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body }); + options.url = + req.session.selectedNode.ln_server_url + + '/v1/payments?max_payments=' + + req.query.max_payments + + '&index_offset=' + + req.query.index_offset + + '&reversed=' + + req.query.reversed; + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Payment List Received', + data: body + }); if (body.payments && body.payments.length > 0) { body.payments = common.sortDescByKey(body.payments, 'creation_date'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sorted Payments List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Sorted Payments List Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getAllLightningTransactions = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting All Lightning Transactions..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Getting All Lightning Transactions..' + }); const options1 = JSON.parse(JSON.stringify(common.getOptions(req))); const options2 = JSON.parse(JSON.stringify(common.getOptions(req))); // options1.url = req.session.selectedNode.ln_server_url + '/v1/payments?max_payments=100000&index_offset=0&reversed=true'; - options2.url = req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=100000&index_offset=0&reversed=true'; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'All Payments Options', data: options1 }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'All Invoices Options', data: options2 }); + options2.url = + req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=100000&index_offset=0&reversed=true'; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'All Payments Options', + data: options1 + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'All Invoices Options', + data: options2 + }); // return Promise.all([request(options1), request(options2)]).then((values) => { - return Promise.all([{ payments: [] }, request(options2)]).then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'All Lightning Transactions Received', data: ({ totalPayments: values[0].payments.length || 0, totalInvoices: values[1].invoices.length || 0 }) }); + return Promise.all([{ payments: [] }, request(options2)]) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'All Lightning Transactions Received', + data: { totalPayments: values[0].payments.length || 0, totalInvoices: values[1].invoices.length || 0 } + }); res.status(200).json({ listPaymentsAll: values[0], listInvoicesAll: values[1] }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'All Lightning Transactions Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const paymentLookup = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Looking up Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Looking up Payment..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v2/router/track/' + req.params.paymentHash; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Information Received for ' + req.params.paymentHash, data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Information Received for ' + req.params.paymentHash, + data: body + }); res.status(200).json(body.result || body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'Payment Lookup Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/peers.js b/backend/controllers/lnd/peers.js index 5a035ff43..687bead11 100644 --- a/backend/controllers/lnd/peers.js +++ b/backend/controllers/lnd/peers.js @@ -6,11 +6,19 @@ const logger = Logger; const common = Common; export const getAliasForPeers = (selNode, peer) => { options.url = selNode.ln_server_url + '/v1/graph/node/' + peer.pub_key; - return request(options).then((aliasBody) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Peers', msg: 'Alias Received', data: aliasBody.node.alias }); + return request(options) + .then((aliasBody) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Alias Received', + data: aliasBody.node.alias + }); peer.alias = aliasBody.node.alias; return aliasBody.node.alias; - }).catch((err) => { + }) + .catch((err) => { peer.alias = peer.pub_key.slice(0, 10) + '...' + peer.pub_key.slice(-10); return peer.pub_key; }); @@ -22,18 +30,38 @@ export const getPeers = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/peers'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peers List Received', + data: body + }); const peers = !body.peers ? [] : body.peers; return Promise.all(peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers with Alias before Sort', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peers with Alias before Sort', + data: body + }); if (body.peers) { body.peers = common.sortDescByStrKey(body.peers, 'alias'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body.peers }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Sorted Peers List Received', + data: body.peers + }); res.status(200).json(body.peers); }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -49,27 +77,46 @@ export const postPeer = (req, res, next) => { addr: { host: req.body.host, pubkey: req.body.pubkey }, perm: req.body.perm }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peer Connected', + data: body + }); options.url = req.session.selectedNode.ln_server_url + '/v1/peers'; - request(options).then((body) => { - const peers = (!body.peers) ? [] : body.peers; - return Promise.all(peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => { + request(options) + .then((body) => { + const peers = !body.peers ? [] : body.peers; + return Promise.all(peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))) + .then((values) => { if (body.peers) { body.peers = common.sortDescByStrKey(body.peers, 'alias'); body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peers List after Connect Received', + data: body + }); } res.status(201).json(body.peers); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -81,11 +128,26 @@ export const deletePeer = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/peers/' + req.params.peerPubKey; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnect Pubkey', data: req.params.peerPubKey }); - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconneted', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peer Disconnect Pubkey', + data: req.params.peerPubKey + }); + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peer Disconneted', + data: body + }); res.status(204).json({}); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/switch.js b/backend/controllers/lnd/switch.js index 51827f414..5c305763c 100644 --- a/backend/controllers/lnd/switch.js +++ b/backend/controllers/lnd/switch.js @@ -4,7 +4,10 @@ import { Common } from '../../utils/common.js'; let options = null; const logger = Logger; const common = Common; -const responseData = { switch: { forwarding_events: [], last_offset_index: 0 }, fees: { forwarding_events: [], last_offset_index: 0 } }; +const responseData = { + switch: { forwarding_events: [], last_offset_index: 0 }, + fees: { forwarding_events: [], last_offset_index: 0 } +}; const num_max_events = 100; export const forwardingHistory = (req, res, next) => { getAllForwardingEvents(req, req.body.start_time, req.body.end_time, 0, 'switch', (eventsResponse) => { @@ -17,12 +20,17 @@ export const forwardingHistory = (req, res, next) => { }); }; export const getAllForwardingEvents = (req, start, end, offset, caller, callback) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Switch', msg: 'Getting Forwarding Events..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Switch', + msg: 'Getting Forwarding Events..' + }); if (offset === 0) { responseData[caller] = { forwarding_events: [], last_offset_index: 0 }; } if (!req.session.selectedNode) { - const err = common.handleError({ message: 'Session Expired after a day\'s inactivity.', statusCode: 401 }, 'Balance', 'Get Balance Error', req.session.selectedNode); + const err = common.handleError({ message: "Session Expired after a day's inactivity.", statusCode: 401 }, 'Balance', 'Get Balance Error', req.session.selectedNode); return callback({ message: err.message, error: err.error, statusCode: err.statusCode }); } options = common.getOptions(req); @@ -37,9 +45,23 @@ export const getAllForwardingEvents = (req, start, end, offset, caller, callback options.form.num_max_events = num_max_events; options.form.index_offset = offset; options.form = JSON.stringify(options.form); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Switch', msg: 'Forwarding Events Params', data: options.form }); - return request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Switch', msg: 'Forwarding Events Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Switch', + msg: 'Forwarding Events Params', + data: options.form + }); + return request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Switch', + msg: 'Forwarding Events Received', + data: body + }); if (body.forwarding_events) { responseData[caller].forwarding_events.push(...body.forwarding_events); responseData[caller].last_offset_index = body.last_offset_index ? body.last_offset_index : 0; @@ -54,7 +76,8 @@ export const getAllForwardingEvents = (req, start, end, offset, caller, callback else { return getAllForwardingEvents(req, start, end, offset + num_max_events, caller, callback); } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Switch', 'Get All Forwarding Events Error', req.session.selectedNode); return callback({ message: err.message, error: err.error, statusCode: err.statusCode }); }); diff --git a/backend/controllers/lnd/transactions.js b/backend/controllers/lnd/transactions.js index 8c42775b2..2de3d8aa3 100644 --- a/backend/controllers/lnd/transactions.js +++ b/backend/controllers/lnd/transactions.js @@ -5,26 +5,50 @@ let options = null; const logger = Logger; const common = Common; export const getTransactions = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Getting Transactions..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Transactions', + msg: 'Getting Transactions..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/transactions'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Transactions', msg: 'Transactions List Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Transactions', + msg: 'Transactions List Received', + data: body + }); if (body.transactions && body.transactions.length > 0) { body.transactions = common.sortDescByKey(body.transactions, 'time_stamp'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Sorted Transactions List Received', data: body.transactions }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Transactions', + msg: 'Sorted Transactions List Received', + data: body.transactions + }); res.status(200).json(body.transactions); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Transactions', 'List Transactions Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const postTransactions = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Sending Transaction..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Transactions', + msg: 'Sending Transaction..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -40,10 +64,19 @@ export const postTransactions = (req, res, next) => { options.form.send_all = req.body.sendAll; } options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Transaction Sent', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Transactions', + msg: 'Transaction Sent', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Transactions', 'Send Transaction Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/wallet.js b/backend/controllers/lnd/wallet.js index 61b1c4e4f..8cf38ef3c 100644 --- a/backend/controllers/lnd/wallet.js +++ b/backend/controllers/lnd/wallet.js @@ -12,15 +12,26 @@ export const genSeed = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } if (req.params.passphrase) { - options.url = req.session.selectedNode.ln_server_url + '/v1/genseed?aezeed_passphrase=' + Buffer.from(atob(req.params.passphrase)).toString('base64'); + options.url = + req.session.selectedNode.ln_server_url + + '/v1/genseed?aezeed_passphrase=' + + Buffer.from(atob(req.params.passphrase)).toString('base64'); } else { options.url = req.session.selectedNode.ln_server_url + '/v1/genseed'; } - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Seed Generated', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Seed Generated', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Wallet', 'Gen Seed Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -33,7 +44,12 @@ export const operateWallet = (req, res, next) => { } options.method = 'POST'; if (!req.params.operation || req.params.operation === 'unlockwallet') { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Unlocking Wallet..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Unlocking Wallet..' + }); options.url = req.session.selectedNode.ln_server_url + '/v1/unlockwallet'; options.form = JSON.stringify({ wallet_password: Buffer.from(atob(req.body.wallet_password)).toString('base64') @@ -41,7 +57,12 @@ export const operateWallet = (req, res, next) => { err_message = 'Unlocking wallet failed! Verify that lnd is running and the wallet is locked!'; } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Initializing Wallet..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Initializing Wallet..' + }); options.url = req.session.selectedNode.ln_server_url + '/v1/initwallet'; if (req.body.aezeed_passphrase && req.body.aezeed_passphrase !== '') { options.form = JSON.stringify({ @@ -58,9 +79,10 @@ export const operateWallet = (req, res, next) => { } err_message = 'Initializing wallet failed!'; } - request(options).then((body) => { - const body_str = (!body) ? '' : JSON.stringify(body); - const search_idx = (!body) ? -1 : body_str.search('Not Found'); + request(options) + .then((body) => { + const body_str = !body ? '' : JSON.stringify(body); + const search_idx = !body ? -1 : body_str.search('Not Found'); if (!body) { const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: err_message }, 'Wallet', err_message, req.session.selectedNode); return res.status(err.statusCode).json({ message: err.error, error: err.error }); @@ -70,21 +92,34 @@ export const operateWallet = (req, res, next) => { return res.status(err.statusCode).json({ message: err.error, error: err.error }); } else if (body.error) { - if ((body.code === 1 && body.error === 'context canceled') || (body.code === 14 && body.error === 'transport is closing')) { + if ((body.code === 1 && body.error === 'context canceled') || + (body.code === 14 && body.error === 'transport is closing')) { res.status(201).json('Successful'); } else { - const errMsg = (body.error && typeof body.error === 'object') ? JSON.stringify(body.error) : (body.error && typeof body.error === 'string') ? body.error : err_message; + const errMsg = body.error && typeof body.error === 'object' + ? JSON.stringify(body.error) + : body.error && typeof body.error === 'string' + ? body.error + : err_message; const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: errMsg }, 'Wallet', errMsg, req.session.selectedNode); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Wallet Unlocked/Initialized', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Wallet Unlocked/Initialized', + data: body + }); res.status(201).json('Successful'); } - }).catch((errRes) => { - if ((errRes.error.code === 1 && errRes.error.error === 'context canceled') || (errRes.error.code === 14 && errRes.error.error === 'transport is closing')) { + }) + .catch((errRes) => { + if ((errRes.error.code === 1 && errRes.error.error === 'context canceled') || + (errRes.error.code === 14 && errRes.error.error === 'transport is closing')) { res.status(201).json('Successful'); } else { @@ -110,10 +145,19 @@ export const getUTXOs = (req, res, next) => { else { options.url = options.url + '?max_confs=' + req.query.max_confs; } - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXOs List Received', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'UTXOs List Received', + data: body + }); res.status(200).json(body.utxos ? body.utxos : []); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Wallet', 'List UTXOs Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -137,16 +181,30 @@ export const bumpFee = (req, res, next) => { options.form.sat_per_byte = req.body.satPerByte; } options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Fee Bumped', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Fee Bumped', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Wallet', 'Bump Fee Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const labelTransaction = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Labelling Transaction..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Labelling Transaction..' + }); options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); @@ -157,11 +215,26 @@ export const labelTransaction = (req, res, next) => { options.form.label = req.body.label; options.form.overwrite = req.body.overwrite; options.form = JSON.stringify(options.form); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'Label Transaction Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Transaction Labelled', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Wallet', + msg: 'Label Transaction Options', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Transaction Labelled', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Wallet', 'Label Transaction Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -180,11 +253,26 @@ export const leaseUTXO = (req, res, next) => { output_index: req.body.outputIndex }; options.form = JSON.stringify(options.form); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'UTXO Lease Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXO Leased', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Wallet', + msg: 'UTXO Lease Options', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'UTXO Leased', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Wallet', 'Lease UTXO Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -203,10 +291,19 @@ export const releaseUTXO = (req, res, next) => { output_index: req.body.outputIndex }; options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXO Released', data: body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'UTXO Released', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Wallet', 'Release UTXO Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/lnd/webSocketClient.js b/backend/controllers/lnd/webSocketClient.js index 6fb3b151b..fd28027b4 100644 --- a/backend/controllers/lnd/webSocketClient.js +++ b/backend/controllers/lnd/webSocketClient.js @@ -23,11 +23,23 @@ export class LNDWebSocketClient { } }; this.fetchUnpaidInvoices = (selectedNode) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Getting Unpaid Invoices..' }); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Getting Unpaid Invoices..' + }); const options = this.setOptionsForSelNode(selectedNode); options.url = selectedNode.ln_server_url + '/v1/invoices?pending_only=true'; - return request(options).then((body) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Unpaid Invoices Received', data: body }); + return request(options) + .then((body) => { + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Unpaid Invoices Received', + data: body + }); if (body.invoices && body.invoices.length > 0) { body.invoices.forEach((invoice) => { if (invoice.state === 'OPEN') { @@ -36,65 +48,127 @@ export class LNDWebSocketClient { }); } return null; - }).catch((errRes) => { + }) + .catch((errRes) => { const err = this.common.handleError(errRes, 'WebSocketClient', 'Pending Invoices Error', selectedNode); - return ({ message: err.message, error: err.error }); + return { message: err.message, error: err.error }; }); }; this.subscribeToInvoice = (options, selectedNode, rHash) => { rHash = rHash.replace(/\+/g, '-').replace(/[/]/g, '_'); - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Subscribing to Invoice ' + rHash + ' ..' }); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Subscribing to Invoice ' + rHash + ' ..' + }); options.url = selectedNode.ln_server_url + '/v2/invoices/subscribe/' + rHash; - request(options).then((msg) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Invoice Information Received for ' + rHash }); + request(options) + .then((msg) => { + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Invoice Information Received for ' + rHash + }); if (typeof msg === 'string') { const results = msg.split('\n'); - msg = (results.length && results.length > 1) ? JSON.parse(results[1]) : JSON.parse(msg); - msg.result.r_preimage = msg.result.r_preimage ? Buffer.from(msg.result.r_preimage, 'base64').toString('hex') : ''; + msg = results.length && results.length > 1 ? JSON.parse(results[1]) : JSON.parse(msg); + msg.result.r_preimage = msg.result.r_preimage + ? Buffer.from(msg.result.r_preimage, 'base64').toString('hex') + : ''; msg.result.r_hash = msg.result.r_hash ? Buffer.from(msg.result.r_hash, 'base64').toString('hex') : ''; - msg.result.description_hash = msg.result.description_hash ? Buffer.from(msg.result.description_hash, 'base64').toString('hex') : null; + msg.result.description_hash = msg.result.description_hash + ? Buffer.from(msg.result.description_hash, 'base64').toString('hex') + : null; } msg['type'] = 'invoice'; msg['source'] = 'LND'; const msgStr = JSON.stringify(msg); - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Invoice Info Received', data: msgStr }); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Invoice Info Received', + data: msgStr + }); this.wsServer.sendEventsToAllLNClients(msgStr, selectedNode); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = this.common.handleError(errRes, 'Invoices', 'Subscribe to Invoice Error for ' + rHash, selectedNode); - const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message + ' ' + rHash }) : (typeof err === 'object') ? JSON.stringify({ error: err + ' ' + rHash }) : ('{ "error": ' + err + ' ' + rHash + ' }')); + const errStr = typeof err === 'object' && err.message + ? JSON.stringify({ error: err.message + ' ' + rHash }) + : typeof err === 'object' + ? JSON.stringify({ error: err + ' ' + rHash }) + : '{ "error": ' + err + ' ' + rHash + ' }'; this.wsServer.sendErrorToAllLNClients(errStr, selectedNode); }); }; this.subscribeToPayment = (options, selectedNode, paymentHash) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Subscribing to Payment ' + paymentHash + ' ..' }); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Subscribing to Payment ' + paymentHash + ' ..' + }); options.url = selectedNode.ln_server_url + '/v2/router/track/' + paymentHash; - request(options).then((msg) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Payment Information Received for ' + paymentHash }); + request(options) + .then((msg) => { + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Payment Information Received for ' + paymentHash + }); msg['type'] = 'payment'; msg['source'] = 'LND'; const msgStr = JSON.stringify(msg); - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Payment Info Received', data: msgStr }); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Payment Info Received', + data: msgStr + }); this.wsServer.sendEventsToAllLNClients(msgStr, selectedNode); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = this.common.handleError(errRes, 'Payment', 'Subscribe to Payment Error for ' + paymentHash, selectedNode); - const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message + ' ' + paymentHash }) : (typeof err === 'object') ? JSON.stringify({ error: err + ' ' + paymentHash }) : ('{ "error": ' + err + ' ' + paymentHash + ' }')); + const errStr = typeof err === 'object' && err.message + ? JSON.stringify({ error: err.message + ' ' + paymentHash }) + : typeof err === 'object' + ? JSON.stringify({ error: err + ' ' + paymentHash }) + : '{ "error": ' + err + ' ' + paymentHash + ' }'; this.wsServer.sendErrorToAllLNClients(errStr, selectedNode); }); }; this.setOptionsForSelNode = (selectedNode) => { const options = { url: '', rejectUnauthorized: false, json: true, form: null }; try { - options['headers'] = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') }; + options['headers'] = { + 'Grpc-Metadata-macaroon': fs.readFileSync(join(selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') + }; } catch (err) { - this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'WebSocketClient', msg: 'Set Options Error', error: JSON.stringify(err) }); + this.logger.log({ + selectedNode: selectedNode, + level: 'ERROR', + fileName: 'WebSocketClient', + msg: 'Set Options Error', + error: JSON.stringify(err) + }); } return options; }; this.disconnect = (selectedNode) => { const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index); if (clientExists) { - this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the LND\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clientExists.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Disconnecting from the LND's Websocket Server.." + }); const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index); this.webSocketClients.splice(clientIdx, 1); } @@ -107,7 +181,9 @@ export class LNDWebSocketClient { } newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode)); this.webSocketClients[clientIdx] = newClient; - if (this.webSocketClients[clientIdx].selectedNode.ln_version === '' || !this.webSocketClients[clientIdx].selectedNode.ln_version || this.common.isVersionCompatible(this.webSocketClients[clientIdx].selectedNode.ln_version, '0.11.0')) { + if (this.webSocketClients[clientIdx].selectedNode.ln_version === '' || + !this.webSocketClients[clientIdx].selectedNode.ln_version || + this.common.isVersionCompatible(this.webSocketClients[clientIdx].selectedNode.ln_version, '0.11.0')) { this.fetchUnpaidInvoices(this.webSocketClients[clientIdx].selectedNode); } }; diff --git a/backend/controllers/shared/RTLConf.js b/backend/controllers/shared/RTLConf.js index 6923a9716..e0067d1b3 100644 --- a/backend/controllers/shared/RTLConf.js +++ b/backend/controllers/shared/RTLConf.js @@ -13,8 +13,17 @@ const common = Common; const wsServer = WSServer; const databaseService = Database; export const updateSelectedNode = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating Selected Node..' }); - const selNodeIndex = req.body.currNodeIndex ? req.body.currNodeIndex : common.initSelectedNode ? common.initSelectedNode.index : 1; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating Selected Node..' + }); + const selNodeIndex = req.body.currNodeIndex + ? req.body.currNodeIndex + : common.initSelectedNode + ? common.initSelectedNode.index + : 1; req.session.selectedNode = common.findNode(selNodeIndex); if (req.headers && req.headers.authorization && req.headers.authorization !== '') { wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.body.prevNodeIndex); @@ -23,16 +32,32 @@ export const updateSelectedNode = (req, res, next) => { } } const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Selected Node Updated To ' + responseVal }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Selected Node Updated To ' + responseVal + }); res.status(200).json({ status: 'Selected Node Updated To: ' + JSON.stringify(responseVal) + '!' }); }; export const getRTLConfigInitial = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting Initial RTL Configuration..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Getting Initial RTL Configuration..' + }); const confFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; fs.readFile(confFile, 'utf8', (errRes, data) => { if (errRes) { if (errRes.code === 'ENOENT') { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'RTLConf', msg: 'Node config does not exist!', error: { error: 'Node config does not exist.' } }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'RTLConf', + msg: 'Node config does not exist!', + error: { error: 'Node config does not exist.' } + }); res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] }); } else { @@ -51,9 +76,9 @@ export const getRTLConfigInitial = (req, res, next) => { common.nodes.forEach((node, i) => { const settings = {}; settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT'; - settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY'; - settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE'; - settings.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false; + settings.themeMode = node.theme_mode ? node.theme_mode : 'DAY'; + settings.themeColor = node.theme_color ? node.theme_color : 'PURPLE'; + settings.fiatConversion = node.fiat_conversion ? !!node.fiat_conversion : false; settings.currencyUnit = node.currency_unit; nodesArr.push({ index: node.index, @@ -64,19 +89,45 @@ export const getRTLConfigInitial = (req, res, next) => { }); }); } - const body = { defaultNodeIndex: nodeConfData.defaultNodeIndex, selectedNodeIndex: (req.session.selectedNode && req.session.selectedNode.index ? req.session.selectedNode.index : common.initSelectedNode.index), sso: sso, enable2FA: enable2FA, allowPasswordUpdate: allowPasswordUpdate, nodes: nodesArr }; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Initial RTL Configuration Received', data: body }); + const body = { + defaultNodeIndex: nodeConfData.defaultNodeIndex, + selectedNodeIndex: req.session.selectedNode && req.session.selectedNode.index + ? req.session.selectedNode.index + : common.initSelectedNode.index, + sso: sso, + enable2FA: enable2FA, + allowPasswordUpdate: allowPasswordUpdate, + nodes: nodesArr + }; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Initial RTL Configuration Received', + data: body + }); res.status(200).json(body); } }); }; export const getRTLConfig = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting RTL Configuration..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Getting RTL Configuration..' + }); const confFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; fs.readFile(confFile, 'utf8', (errRes, data) => { if (errRes) { if (errRes.code === 'ENOENT') { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'RTLConf', msg: 'Node config does not exist!', error: { error: 'Node config does not exist.' } }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'RTLConf', + msg: 'Node config does not exist!', + error: { error: 'Node config does not exist.' } + }); res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] }); } else { @@ -94,14 +145,14 @@ export const getRTLConfig = (req, res, next) => { if (common.nodes && common.nodes.length > 0) { common.nodes.forEach((node, i) => { const authentication = {}; - authentication.configPath = (node.config_path) ? node.config_path : ''; - authentication.swapMacaroonPath = (node.swap_macaroon_path) ? node.swap_macaroon_path : ''; - authentication.boltzMacaroonPath = (node.boltz_macaroon_path) ? node.boltz_macaroon_path : ''; + authentication.configPath = node.config_path ? node.config_path : ''; + authentication.swapMacaroonPath = node.swap_macaroon_path ? node.swap_macaroon_path : ''; + authentication.boltzMacaroonPath = node.boltz_macaroon_path ? node.boltz_macaroon_path : ''; const settings = {}; settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT'; - settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY'; - settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE'; - settings.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false; + settings.themeMode = node.theme_mode ? node.theme_mode : 'DAY'; + settings.themeColor = node.theme_color ? node.theme_color : 'PURPLE'; + settings.fiatConversion = node.fiat_conversion ? !!node.fiat_conversion : false; settings.bitcoindConfigPath = node.bitcoind_config_path; settings.logLevel = node.log_level ? node.log_level : 'ERROR'; settings.lnServerUrl = node.ln_server_url; @@ -119,24 +170,46 @@ export const getRTLConfig = (req, res, next) => { }); }); } - const body = { defaultNodeIndex: nodeConfData.defaultNodeIndex, selectedNodeIndex: (req.session.selectedNode && req.session.selectedNode.index ? req.session.selectedNode.index : common.initSelectedNode.index), sso: sso, enable2FA: enable2FA, allowPasswordUpdate: allowPasswordUpdate, nodes: nodesArr }; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'RTL Configuration Received', data: body }); + const body = { + defaultNodeIndex: nodeConfData.defaultNodeIndex, + selectedNodeIndex: req.session.selectedNode && req.session.selectedNode.index + ? req.session.selectedNode.index + : common.initSelectedNode.index, + sso: sso, + enable2FA: enable2FA, + allowPasswordUpdate: allowPasswordUpdate, + nodes: nodesArr + }; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'RTL Configuration Received', + data: body + }); res.status(200).json(body); } }); }; export const updateUISettings = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating UI Settings..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating UI Settings..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); - const node = config.nodes.find((node) => (node.index === req.session.selectedNode.index)); + const node = config.nodes.find((node) => node.index === req.session.selectedNode.index); if (node && node.Settings) { node.Settings.userPersona = req.body.updatedSettings.userPersona; node.Settings.themeMode = req.body.updatedSettings.themeMode; node.Settings.themeColor = req.body.updatedSettings.themeColor; node.Settings.fiatConversion = req.body.updatedSettings.fiatConversion; if (req.body.updatedSettings.fiatConversion) { - node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD'; + node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit + ? req.body.updatedSettings.currencyUnit + : 'USD'; } else { delete node.Settings.currencyUnit; @@ -147,7 +220,9 @@ export const updateUISettings = (req, res, next) => { selectedNode.theme_color = req.body.updatedSettings.themeColor; selectedNode.fiat_conversion = req.body.updatedSettings.fiatConversion; if (req.body.updatedSettings.fiatConversion) { - selectedNode.currency_unit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD'; + selectedNode.currency_unit = req.body.updatedSettings.currencyUnit + ? req.body.updatedSettings.currencyUnit + : 'USD'; } else { delete selectedNode.currency_unit; @@ -156,7 +231,13 @@ export const updateUISettings = (req, res, next) => { } try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'UI Settings Updated', data: maskPasswords(config) }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'UI Settings Updated', + data: maskPasswords(config) + }); res.status(201).json({ message: 'Node Settings Updated Successfully' }); } catch (errRes) { @@ -166,7 +247,12 @@ export const updateUISettings = (req, res, next) => { } }; export const update2FASettings = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating 2FA Settings..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating 2FA Settings..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); if (req.body.secret2fa && req.body.secret2fa.trim() !== '') { @@ -175,7 +261,9 @@ export const update2FASettings = (req, res, next) => { else { delete config.secret2fa; } - const message = req.body.secret2fa.trim() === '' ? 'Two factor authentication disabled successfully.' : 'Two factor authentication enabled successfully.'; + const message = req.body.secret2fa.trim() === '' + ? 'Two factor authentication disabled successfully.' + : 'Two factor authentication enabled successfully.'; try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); common.rtl_secret2fa = config.secret2fa; @@ -189,13 +277,24 @@ export const update2FASettings = (req, res, next) => { } }; export const updateDefaultNode = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating Default Node..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating Default Node..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); config.defaultNodeIndex = req.body.defaultNodeIndex; try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Default Node Updated', data: maskPasswords(config) }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Default Node Updated', + data: maskPasswords(config) + }); res.status(201).json({ message: 'Default Node Updated Successfully' }); } catch (errRes) { @@ -205,7 +304,12 @@ export const updateDefaultNode = (req, res, next) => { } }; export const getConfig = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Reading Configuration File..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Reading Configuration File..' + }); let confFile = ''; let fileFormat = 'INI'; switch (req.params.nodeType) { @@ -223,8 +327,20 @@ export const getConfig = (req, res, next) => { confFile = ''; break; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'Node Type', data: req.params.nodeType }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'File Path', data: confFile }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'RTLConf', + msg: 'Node Type', + data: req.params.nodeType + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'RTLConf', + msg: 'File Path', + data: confFile + }); fs.readFile(confFile, 'utf8', (errRes, data) => { if (errRes) { const errMsg = 'Reading Config Error'; @@ -249,17 +365,37 @@ export const getConfig = (req, res, next) => { } } jsonConfig = maskPasswords(jsonConfig); - const responseJSON = (fileFormat === 'JSON') ? jsonConfig : ini.stringify(jsonConfig).replace('color=\\#', 'color=#'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Configuration File Data Received', data: responseJSON }); + const responseJSON = fileFormat === 'JSON' ? jsonConfig : ini.stringify(jsonConfig).replace('color=\\#', 'color=#'); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Configuration File Data Received', + data: responseJSON + }); res.status(200).json({ format: fileFormat, data: responseJSON }); } }); }; export const getFile = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting File..' }); - const file = req.query.path ? req.query.path : (req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.query.channel.replace(':', '-') + '.bak'); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'Channel Point', data: req.query.channel }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'File Path', data: file }); + const file = req.query.path + ? req.query.path + : req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.query.channel.replace(':', '-') + '.bak'; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'RTLConf', + msg: 'Channel Point', + data: req.query.channel + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'RTLConf', + msg: 'File Path', + data: file + }); fs.readFile(file, 'utf8', (errRes, data) => { if (errRes) { if (errRes.code && errRes.code === 'ENOENT') { @@ -270,32 +406,62 @@ export const getFile = (req, res, next) => { return res.status(err.statusCode).json({ message: err.error, error: err.error }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'File Data Received', data: data }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'File Data Received', + data: data + }); res.status(200).json(data); } }); }; export const getCurrencyRates = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting Currency Rates..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Getting Currency Rates..' + }); options.url = 'https://blockchain.info/ticker'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Currency Rates Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Currency Rates Received', + data: body + }); res.status(200).json(JSON.parse(body)); - }).catch((errRes) => { + }) + .catch((errRes) => { const errMsg = 'Get Rates Error'; const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); return res.status(err.statusCode).json({ message: err.error, error: err.error }); }); }; export const updateSSO = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating SSO Settings..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating SSO Settings..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); delete config.SSO; config.SSO = req.body.SSO; try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'SSO Setting Updated', data: maskPasswords(config) }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'SSO Setting Updated', + data: maskPasswords(config) + }); res.status(201).json({ message: 'SSO Updated Successfully' }); } catch (errRes) { @@ -305,7 +471,12 @@ export const updateSSO = (req, res, next) => { } }; export const updateServiceSettings = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating Service Settings..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating Service Settings..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); const selectedNode = common.findNode(req.session.selectedNode.index); @@ -353,7 +524,13 @@ export const updateServiceSettings = (req, res, next) => { }); try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Service Settings Updated', data: maskPasswords(config) }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Service Settings Updated', + data: maskPasswords(config) + }); res.status(201).json({ message: 'Service Settings Updated Successfully' }); } catch (errRes) { @@ -371,8 +548,10 @@ export const maskPasswords = (obj) => { keys[keys[i]] = maskPasswords(obj[keys[i]]); } if (typeof keys[i] === 'string' && - (keys[i].toLowerCase().includes('password') || keys[i].toLowerCase().includes('multipass') || - keys[i].toLowerCase().includes('rpcpass') || keys[i].toLowerCase().includes('rpcpassword'))) { + (keys[i].toLowerCase().includes('password') || + keys[i].toLowerCase().includes('multipass') || + keys[i].toLowerCase().includes('rpcpass') || + keys[i].toLowerCase().includes('rpcpassword'))) { obj[keys[i]] = '********************'; } } diff --git a/backend/controllers/shared/authenticate.js b/backend/controllers/shared/authenticate.js index ab91f07d8..0c3b180f0 100644 --- a/backend/controllers/shared/authenticate.js +++ b/backend/controllers/shared/authenticate.js @@ -13,7 +13,7 @@ const failedLoginAttempts = {}; const databaseService = Database; const loginInterval = setInterval(() => { for (const ip in failedLoginAttempts) { - if (new Date().getTime() > (failedLoginAttempts[ip].lastTried + LOCKING_PERIOD)) { + if (new Date().getTime() > failedLoginAttempts[ip].lastTried + LOCKING_PERIOD) { delete failedLoginAttempts[ip]; clearInterval(loginInterval); } @@ -21,7 +21,7 @@ const loginInterval = setInterval(() => { }, LOCKING_PERIOD); export const getFailedInfo = (reqIP, currentTime) => { let failed = { count: 0, lastTried: currentTime }; - if ((!failedLoginAttempts[reqIP]) || (currentTime > (failed.lastTried + LOCKING_PERIOD))) { + if (!failedLoginAttempts[reqIP] || currentTime > failed.lastTried + LOCKING_PERIOD) { failed = { count: 0, lastTried: currentTime }; failedLoginAttempts[reqIP] = failed; } @@ -31,35 +31,62 @@ export const getFailedInfo = (reqIP, currentTime) => { return failed; }; const handleMultipleFailedAttemptsError = (failed, currentTime, errMsg) => { - if (failed.count >= ALLOWED_LOGIN_ATTEMPTS && (currentTime <= (failed.lastTried + LOCKING_PERIOD))) { + if (failed.count >= ALLOWED_LOGIN_ATTEMPTS && currentTime <= failed.lastTried + LOCKING_PERIOD) { return { message: 'Multiple Failed Login Attempts!', - error: 'Application locked for ' + (LOCKING_PERIOD / ONE_MINUTE) + ' minutes due to multiple failed attempts!\nTry again after ' + common.convertTimestampToTime((failed.lastTried + LOCKING_PERIOD) / 1000) + '!' + error: 'Application locked for ' + + LOCKING_PERIOD / ONE_MINUTE + + ' minutes due to multiple failed attempts!\nTry again after ' + + common.convertTimestampToTime((failed.lastTried + LOCKING_PERIOD) / 1000) + + '!' }; } else { return { message: 'Authentication Failed!', - error: errMsg + '\nApplication will be locked after ' + (ALLOWED_LOGIN_ATTEMPTS - failed.count) + ' more unsuccessful attempts!' + error: errMsg + + '\nApplication will be locked after ' + + (ALLOWED_LOGIN_ATTEMPTS - failed.count) + + ' more unsuccessful attempts!' }; } }; -export const verifyToken = (twoFAToken) => !!(common.rtl_secret2fa && common.rtl_secret2fa !== '' && otplib.authenticator.check(twoFAToken, common.rtl_secret2fa)); +export const verifyToken = (twoFAToken) => !!(common.rtl_secret2fa && + common.rtl_secret2fa !== '' && + otplib.authenticator.check(twoFAToken, common.rtl_secret2fa)); export const authenticateUser = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Authenticating User..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'Authenticating User..' + }); if (+common.rtl_sso) { if (req.body.authenticateWith === 'JWT' && jwt.verify(req.body.authenticationValue, common.secret_key)) { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'User Authenticated' }); - res.status(406).json({ message: 'SSO Authentication Error', error: 'Login with Password is not allowed with SSO.' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'User Authenticated' + }); + res + .status(406) + .json({ message: 'SSO Authentication Error', error: 'Login with Password is not allowed with SSO.' }); } else if (req.body.authenticateWith === 'PASSWORD') { - if (common.cookie_value.trim().length >= 32 && crypto.timingSafeEqual(Buffer.from(crypto.createHash('sha256').update(common.cookie_value).digest('hex'), 'utf-8'), Buffer.from(req.body.authenticationValue, 'utf-8'))) { + if (common.cookie_value.trim().length >= 32 && + crypto.timingSafeEqual(Buffer.from(crypto.createHash('sha256').update(common.cookie_value).digest('hex'), 'utf-8'), Buffer.from(req.body.authenticationValue, 'utf-8'))) { common.refreshCookie(); if (!req.session.selectedNode) { req.session.selectedNode = common.initSelectedNode; } const token = jwt.sign({ user: 'SSO_USER' }, common.secret_key); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'User Authenticated' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'User Authenticated' + }); res.status(200).json({ token: token }); } else { @@ -77,7 +104,13 @@ export const authenticateUser = (req, res, next) => { if (common.rtl_pass === password && failed.count < ALLOWED_LOGIN_ATTEMPTS) { if (req.body.twoFAToken && req.body.twoFAToken !== '') { if (!verifyToken(req.body.twoFAToken)) { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Authenticate', msg: 'Invalid Token! Failed IP ' + reqIP, error: { error: 'Invalid token.' } }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Authenticate', + msg: 'Invalid Token! Failed IP ' + reqIP, + error: { error: 'Invalid token.' } + }); failed.count = failed.count + 1; failed.lastTried = currentTime; return res.status(401).json(handleMultipleFailedAttemptsError(failed, currentTime, 'Invalid 2FA Token!')); @@ -88,19 +121,35 @@ export const authenticateUser = (req, res, next) => { } delete failedLoginAttempts[reqIP]; const token = jwt.sign({ user: 'NODE_USER' }, common.secret_key); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'User Authenticated' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'User Authenticated' + }); res.status(200).json({ token: token }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Authenticate', msg: 'Invalid Password! Failed IP ' + reqIP, error: { error: 'Invalid password.' } }); - failed.count = common.rtl_pass !== password ? (failed.count + 1) : failed.count; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Authenticate', + msg: 'Invalid Password! Failed IP ' + reqIP, + error: { error: 'Invalid password.' } + }); + failed.count = common.rtl_pass !== password ? failed.count + 1 : failed.count; failed.lastTried = common.rtl_pass !== password ? currentTime : failed.lastTried; return res.status(401).json(handleMultipleFailedAttemptsError(failed, currentTime, 'Invalid Password!')); } } }; export const resetPassword = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Resetting Password..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'Resetting Password..' + }); if (+common.rtl_sso) { const errMsg = 'Password cannot be reset for SSO authentication'; const err = common.handleError({ statusCode: 401, message: 'Password Reset Error', error: errMsg }, 'Authenticate', errMsg, req.session.selectedNode); @@ -111,7 +160,12 @@ export const resetPassword = (req, res, next) => { if (common.rtl_pass === currPassword) { common.rtl_pass = common.replacePasswordWithHash(req.body.newPassword); const token = jwt.sign({ user: 'NODE_USER' }, common.secret_key); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Password Reset Successful' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'Password Reset Successful' + }); res.status(200).json({ token: token }); } else { diff --git a/backend/controllers/shared/boltz.js b/backend/controllers/shared/boltz.js index 716b9fc7b..8b006e156 100644 --- a/backend/controllers/shared/boltz.js +++ b/backend/controllers/shared/boltz.js @@ -5,7 +5,12 @@ let options = null; const logger = Logger; const common = Common; export const getInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting Boltz Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Getting Boltz Information..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; @@ -13,16 +18,29 @@ export const getInfo = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/info'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Information Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Information Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Boltz', 'Get Info Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getServiceInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting Service Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Getting Service Information..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; @@ -30,10 +48,18 @@ export const getServiceInfo = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/serviceinfo'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Get Service Info Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Get Service Info Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Boltz', 'Get Service Information Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -47,8 +73,15 @@ export const listSwaps = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/listswaps'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz List Swaps Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz List Swaps Received', + data: body + }); if (body && body.swaps && body.swaps.length && body.swaps.length > 0) { body.swaps = body.swaps.reverse(); } @@ -56,13 +89,19 @@ export const listSwaps = (req, res, next) => { body.reverseSwaps = body.reverseSwaps.reverse(); } res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Boltz', 'List Swaps Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const getSwapInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting Swap Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Getting Swap Information..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; @@ -70,10 +109,18 @@ export const getSwapInfo = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/swap/' + req.params.swapId; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Swap Information Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Swap Information Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Boltz', 'Swap Info Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -91,17 +138,37 @@ export const createSwap = (req, res, next) => { if (req.body.address !== '') { options.body.address = req.body.address; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Swap Options Body', data: options.body }); - request.post(options).then((createSwapRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Swap Created', data: createSwapRes }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Boltz', + msg: 'Create Swap Options Body', + data: options.body + }); + request + .post(options) + .then((createSwapRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Swap Created', + data: createSwapRes + }); res.status(201).json(createSwapRes); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Boltz', 'Create Swap Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const createReverseSwap = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Creating Reverse Swap..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Creating Reverse Swap..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; @@ -113,17 +180,37 @@ export const createReverseSwap = (req, res, next) => { if (req.body.address !== '') { options.body.address = req.body.address; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Reverse Swap Body', data: options.body }); - request.post(options).then((createReverseSwapRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Reverse Swap Created', data: createReverseSwapRes }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Boltz', + msg: 'Create Reverse Swap Body', + data: options.body + }); + request + .post(options) + .then((createReverseSwapRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Reverse Swap Created', + data: createReverseSwapRes + }); res.status(201).json(createReverseSwapRes); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Boltz', 'Create Reverse Swap Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const createChannel = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Creating Boltz Channel..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Creating Boltz Channel..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; @@ -135,17 +222,37 @@ export const createChannel = (req, res, next) => { if (req.body.address !== '') { options.body.address = req.body.address; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Channel Options Body', data: options.body }); - request.post(options).then((createChannelRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Channel Created', data: createChannelRes }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Boltz', + msg: 'Create Channel Options Body', + data: options.body + }); + request + .post(options) + .then((createChannelRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Channel Created', + data: createChannelRes + }); res.status(201).json(createChannelRes); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Boltz', 'Create Channel Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const deposit = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Deposit Start..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Deposit Start..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; @@ -153,10 +260,19 @@ export const deposit = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/deposit'; - request.post(options).then((depositRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Deposit Finished', data: depositRes }); + request + .post(options) + .then((depositRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Deposit Finished', + data: depositRes + }); res.status(201).json(depositRes); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Boltz', 'Deposit Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/controllers/shared/loop.js b/backend/controllers/shared/loop.js index 2fd33b3d0..a67e3efa4 100644 --- a/backend/controllers/shared/loop.js +++ b/backend/controllers/shared/loop.js @@ -30,17 +30,37 @@ export const loopOut = (req, res, next) => { if (req.body.destAddress !== '') { options.body['dest'] = req.body.destAddress; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Body', data: options.body }); - request.post(options).then((loopOutRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looped Out', data: loopOutRes }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Body', + data: options.body + }); + request + .post(options) + .then((loopOutRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Looped Out', + data: loopOutRes + }); res.status(201).json(loopOutRes); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop Out Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const loopOutTerms = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop Out Terms..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; @@ -48,36 +68,77 @@ export const loopOutTerms = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/out/terms'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Terms Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Out Terms Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop Out Terms Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const loopOutQuote = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Quotes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop Out Quotes..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; const err = common.handleError({ statusCode: 500, message: 'Loop Out Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } - options.url = options.url + '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Quote URL', data: options.url }); - request(options).then((quoteRes) => { + options.url = + options.url + + '/v1/loop/out/quote/' + + req.params.amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Quote URL', + data: options.url + }); + request(options) + .then((quoteRes) => { quoteRes.amount = +req.params.amount; - quoteRes.swap_payment_dest = quoteRes.swap_payment_dest ? Buffer.from(quoteRes.swap_payment_dest, 'base64').toString('hex') : ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Quote Received', data: quoteRes }); + quoteRes.swap_payment_dest = quoteRes.swap_payment_dest + ? Buffer.from(quoteRes.swap_payment_dest, 'base64').toString('hex') + : ''; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Out Quote Received', + data: quoteRes + }); res.status(200).json(quoteRes); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop Out Quotes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const loopOutTermsAndQuotes = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms & Quotes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop Out Terms & Quotes..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; @@ -85,27 +146,79 @@ export const loopOutTermsAndQuotes = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/out/terms'; - request(options).then((terms) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Terms Received', data: terms }); + request(options) + .then((terms) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Terms Received', + data: terms + }); const options1 = common.getSwapServerOptions(req); const options2 = common.getSwapServerOptions(req); - options1.url = options1.url + '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - options2.url = options2.url + '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Min Quote Options', data: options1 }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Max Quote Options', data: options2 }); - return Promise.all([request(options1), request(options2)]).then((values) => { + options1.url = + options1.url + + '/v1/loop/out/quote/' + + terms.min_swap_amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + options2.url = + options2.url + + '/v1/loop/out/quote/' + + terms.max_swap_amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Min Quote Options', + data: options1 + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Max Quote Options', + data: options2 + }); + return Promise.all([request(options1), request(options2)]) + .then((values) => { values[0].amount = +terms.min_swap_amount; values[1].amount = +terms.max_swap_amount; - values[0].swap_payment_dest = values[0].swap_payment_dest ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') : ''; - values[1].swap_payment_dest = values[1].swap_payment_dest ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') : ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Quotes 1 Received', data: values[0] }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Quotes 2 Received', data: values[1] }); + values[0].swap_payment_dest = values[0].swap_payment_dest + ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') + : ''; + values[1].swap_payment_dest = values[1].swap_payment_dest + ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') + : ''; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Out Quotes 1 Received', + data: values[0] + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Out Quotes 2 Received', + data: values[1] + }); res.status(200).json(values); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop Out Terms & Quotes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop Out Terms & Quotes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -125,17 +238,37 @@ export const loopIn = (req, res, next) => { max_miner_fee: req.body.minerFee, initiator: 'RTL' }; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Body', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looped In', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Body', + data: options.body + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Looped In', + data: body + }); res.status(201).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop In Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const loopInTerms = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop In Terms..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; @@ -143,36 +276,77 @@ export const loopInTerms = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/in/terms'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Terms Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Terms Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop In Terms Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const loopInQuote = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Quotes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop In Quotes..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; const err = common.handleError({ statusCode: 500, message: 'Loop In Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } - options.url = options.url + '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Quote Options', data: options.url }); - request(options).then((body) => { + options.url = + options.url + + '/v1/loop/in/quote/' + + req.params.amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop In Quote Options', + data: options.url + }); + request(options) + .then((body) => { body.amount = +req.params.amount; - body.swap_payment_dest = body.swap_payment_dest ? Buffer.from(body.swap_payment_dest, 'base64').toString('hex') : ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Qoutes Received', data: body }); + body.swap_payment_dest = body.swap_payment_dest + ? Buffer.from(body.swap_payment_dest, 'base64').toString('hex') + : ''; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Qoutes Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop In Quote Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const loopInTermsAndQuotes = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms & Quotes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop In Terms & Quotes..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; @@ -180,27 +354,79 @@ export const loopInTermsAndQuotes = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/in/terms'; - request(options).then((terms) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Terms Received', data: terms }); + request(options) + .then((terms) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop In Terms Received', + data: terms + }); const options1 = common.getSwapServerOptions(req); const options2 = common.getSwapServerOptions(req); - options1.url = options1.url + '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - options2.url = options2.url + '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Min Quote Options', data: options1 }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Max Quote Options', data: options2 }); - return Promise.all([request(options1), request(options2)]).then((values) => { + options1.url = + options1.url + + '/v1/loop/in/quote/' + + terms.min_swap_amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + options2.url = + options2.url + + '/v1/loop/in/quote/' + + terms.max_swap_amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop In Min Quote Options', + data: options1 + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop In Max Quote Options', + data: options2 + }); + return Promise.all([request(options1), request(options2)]) + .then((values) => { values[0].amount = +terms.min_swap_amount; values[1].amount = +terms.max_swap_amount; - values[0].swap_payment_dest = values[0].swap_payment_dest ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') : ''; - values[1].swap_payment_dest = values[1].swap_payment_dest ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') : ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Quotes 1 Received', data: values[0] }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Quotes 2 Received', data: values[1] }); + values[0].swap_payment_dest = values[0].swap_payment_dest + ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') + : ''; + values[1].swap_payment_dest = values[1].swap_payment_dest + ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') + : ''; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Quotes 1 Received', + data: values[0] + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Quotes 2 Received', + data: values[1] + }); res.status(200).json(values); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop In Terms & Quotes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop In Terms & Quotes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -214,20 +440,39 @@ export const swaps = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/swaps'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Swaps Received', + data: body + }); if (body.swaps && body.swaps.length > 0) { body.swaps = common.sortDescByKey(body.swaps, 'initiation_time'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Sorted Loop Swaps List Received', data: body }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Sorted Loop Swaps List Received', + data: body + }); } res.status(200).json(body.swaps); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'List Swaps Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); }; export const swap = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Swap Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Swap Information..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; @@ -235,10 +480,18 @@ export const swap = (req, res, next) => { return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/swap/' + req.params.id; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Swap Information Received', + data: body + }); res.status(200).json(body); - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/backend/models/database.model.js b/backend/models/database.model.js index 0ae353ca6..dd453625e 100644 --- a/backend/models/database.model.js +++ b/backend/models/database.model.js @@ -23,16 +23,16 @@ export class Offer { } export const validateOffer = (documentToValidate) => { if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.BOLT12)) { - return ({ isValid: false, error: CollectionFieldsEnum.BOLT12 + 'is mandatory.' }); + return { isValid: false, error: CollectionFieldsEnum.BOLT12 + 'is mandatory.' }; } if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.AMOUNTMSAT)) { - return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'is mandatory.' }); + return { isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'is mandatory.' }; } if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TITLE)) { - return ({ isValid: false, error: CollectionFieldsEnum.TITLE + 'is mandatory.' }); + return { isValid: false, error: CollectionFieldsEnum.TITLE + 'is mandatory.' }; } - if ((typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number')) { - return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'should be a number.' }); + if (typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number') { + return { isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'should be a number.' }; } - return ({ isValid: true }); + return { isValid: true }; }; diff --git a/backend/utils/app.js b/backend/utils/app.js index a5973ff88..13d33fd03 100644 --- a/backend/utils/app.js +++ b/backend/utils/app.js @@ -6,6 +6,7 @@ import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import CORS from './cors.js'; import CSRF from './csrf.js'; +import CertificateIdentity from './certificateIdentity.js'; import sharedRoutes from '../routes/shared/index.js'; import lndRoutes from '../routes/lnd/index.js'; import clnRoutes from '../routes/cln/index.js'; @@ -31,53 +32,106 @@ export class ExpressApplication { this.loadConfiguration = () => { this.config.setServerConfiguration(); }; - this.setCORS = () => { CORS.mount(this.app); }; - this.setCSRF = () => { CSRF.mount(this.app); }; + this.setCORS = () => { + CORS.mount(this.app); + }; + this.setCSRF = () => { + CSRF.mount(this.app); + }; this.setApplicationRoutes = () => { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Setting up Application Routes..' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'App', + msg: 'Setting up Application Routes..' + }); this.app.use(this.common.baseHref + '/api', sharedRoutes); this.app.use(this.common.baseHref + '/api/lnd', lndRoutes); this.app.use(this.common.baseHref + '/api/cln', clnRoutes); this.app.use(this.common.baseHref + '/api/ecl', eclRoutes); this.app.use(this.common.baseHref, express.static(join(this.directoryName, '../..', 'frontend'))); this.app.use((req, res, next) => { - res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : ''); + res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : '', { secure: this.common.ssl ? true : false }); res.sendFile(join(this.directoryName, '../..', 'frontend', 'index.html')); }); this.app.use((err, req, res, next) => this.handleApplicationErrors(err, res)); - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Application Routes Set' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'App', + msg: 'Application Routes Set' + }); }; this.handleApplicationErrors = (err, res) => { switch (err.code) { case 'EACCES': - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Server requires elevated privileges' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'Server requires elevated privileges' + }); res.status(406).send('Server requires elevated privileges.'); break; case 'EADDRINUSE': - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Server is already in use' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'Server is already in use' + }); res.status(409).send('Server is already in use.'); break; case 'ECONNREFUSED': - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Server is down/locked' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'Server is down/locked' + }); res.status(401).send('Server is down/locked.'); break; case 'EBADCSRFTOKEN': - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Invalid CSRF token. Form tempered.' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'Invalid CSRF token. Form tempered.' + }); res.status(403).send('Invalid CSRF token, form tempered.'); break; default: - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'DEFUALT ERROR', error: err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'DEFUALT ERROR', + error: err + }); res.status(400).send(JSON.stringify(err)); break; } }; - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Starting Express Application..' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'App', + msg: 'Starting Express Application..' + }); + this.loadConfiguration(); this.app.set('trust proxy', true); - this.app.use(sessions({ secret: this.common.secret_key, saveUninitialized: true, cookie: { secure: false, maxAge: ONE_DAY }, resave: false })); + if (this.common.ssl && this.common.ssl.requestCert) { + this.app.use(CertificateIdentity(this.common.ssl.rejectUnauthorized)); + } + this.app.use(sessions({ + secret: this.common.secret_key, + saveUninitialized: true, + cookie: { secure: this.common.ssl ? true : false, maxAge: ONE_DAY }, + resave: false + })); this.app.use(cookieParser(this.common.secret_key)); this.app.use(bodyParser.json({ limit: '25mb' })); this.app.use(bodyParser.urlencoded({ extended: false, limit: '25mb' })); - this.loadConfiguration(); this.setCORS(); this.setCSRF(); this.setApplicationRoutes(); diff --git a/backend/utils/authCheck.js b/backend/utils/authCheck.js index f01cad295..55d4beadf 100644 --- a/backend/utils/authCheck.js +++ b/backend/utils/authCheck.js @@ -19,8 +19,10 @@ export const isAuthenticated = (req, res, next) => { }; export const verifyWSUser = (info, next) => { const headers = JSON.parse(JSON.stringify(info.req.headers)); - const protocols = !info.req.headers['sec-websocket-protocol'] ? [] : info.req.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); - const jwToken = (protocols && protocols.length > 0) ? protocols[0] : ''; + const protocols = !info.req.headers['sec-websocket-protocol'] + ? [] + : info.req.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); + const jwToken = protocols && protocols.length > 0 ? protocols[0] : ''; if (!jwToken || jwToken === '') { next(false, 401, 'Authentication Failed! Please Login First!'); } @@ -46,7 +48,13 @@ export const verifyWSUser = (info, next) => { catch (err) { cookies = {}; updatedReq['cookies'] = JSON.parse(cookies); - logger.log({ selectedNode: common.initSelectedNode, level: 'WARN', fileName: 'AuthCheck', msg: '403 Unable to read CSRF token cookie', data: err }); + logger.log({ + selectedNode: common.initSelectedNode, + level: 'WARN', + fileName: 'AuthCheck', + msg: '403 Unable to read CSRF token cookie', + data: err + }); } csurfProtection(updatedReq, null, (err) => { if (err) { @@ -58,7 +66,13 @@ export const verifyWSUser = (info, next) => { }); } catch (err) { - logger.log({ selectedNode: common.initSelectedNode, level: 'WARN', fileName: 'AuthCheck', msg: '403 Unable to verify CSRF token', data: err }); + logger.log({ + selectedNode: common.initSelectedNode, + level: 'WARN', + fileName: 'AuthCheck', + msg: '403 Unable to verify CSRF token', + data: err + }); next(true); } } diff --git a/backend/utils/certificateFactory.js b/backend/utils/certificateFactory.js new file mode 100644 index 000000000..2e305da61 --- /dev/null +++ b/backend/utils/certificateFactory.js @@ -0,0 +1,287 @@ +import forge from 'node-forge'; +forge.options.usePureJavaScript = true; +//--------------------------------------------------------------------------------------- +//Private Module Variables +//--------------------------------------------------------------------------------------- +const { assign, freeze, keys } = Object; +const max32BitInt = 2147483647; +//--------------------------------------------------------------------------------------- +class CertificateFactory { + //--------------------------------------------------------------------------------------- + //Constructor + // + //Input: obj (options object) + //Output: certificate factory instance + //--------------------------------------------------------------------------------------- + constructor(opts = {}) { + this.forge = forge; + this.certBundle = null; + this.encryptionBits = 2048; + this.certType = 'component'; + this.validForYears = 10; + this.commonName = 'localhost'; + this.organizationalUnit = 'RTL'; + this.organizationName = 'RTL'; + this.countryName = 'US'; + this.stateName = 'New York'; + this.localityName = 'New York'; + this.altName = 'https://localhost'; + this.altIp = '127.0.0.1'; + const { altIp = '127.0.0.1', certType = 'component', commonName = 'localhost', countryName = 'US', encryptionBits = '2048', stateName = 'New York', localityName = 'New York', organizationName = 'RTL', organizationalUnit = 'RTL', validForYears = '10' } = opts; + assign(this, { + certBundle: null, + altName: `https://${commonName}`, + altIp, + certType, + commonName, + countryName, + encryptionBits: parseInt(encryptionBits, 10), + stateName, + localityName, + organizationName, + organizationalUnit, + validForYears: parseInt(validForYears, 10) + }); + } + //--------------------------------------------------------------------------------------- + //Method: getStaticBundle + // + //Input: type + //Output: bundle + // + //This method will generate a constant static cert bundle that can be "shared" between + //many classes that may require a common certificate set. It has a single input argument + //that allows external classes to specify which format they would like their cert bundle + //to be in. (string or buffer) + //--------------------------------------------------------------------------------------- + getStaticBundle(type = 'string', password = null) { + if (!this.certBundle) { + this.certBundle = freeze(this.generateRandomCerts()); + } + return this._convertBundle(this.certBundle, type, password); + } + //--------------------------------------------------------------------------------------- + //Method: _convertBundle + // + //Input: bundle , type + //Output: bundle + // + //This method will converts all key value pairs of a certificate bundle to the desired + //type and returns a new bundle. This also performs a defacto copy of the bundle to + //prevent outside callers from tampering with each other's returned bundle instance, or + //the internal cached bundle referenced by getStaticBundle. + //--------------------------------------------------------------------------------------- + _convertBundle(bundle, type, password) { + const newBundle = {}; + const bundleKeys = keys(bundle); + const converter = type === 'string' ? this._valueToString : this._valueToBuffer; + if (type === 'p12') { + return this._bundleToP12(bundle, password); + } + bundleKeys.forEach((k) => { + if (bundle[k]) { + newBundle[k] = converter(bundle[k]); + } + else { + newBundle[k] = bundle[k]; + } + }); + return newBundle; + } + //--------------------------------------------------------------------------------------- + //Method: _valueToString + // + //Input: v + //Output: v + // + //This method converts its input value to a string and returns it. + //--------------------------------------------------------------------------------------- + _valueToString(v) { + return `${v}`; + } + //--------------------------------------------------------------------------------------- + //Method: _valueToBuffer + // + //Input: v + //Output: v + // + //This method converts its input value to a buffer and returns it. + //--------------------------------------------------------------------------------------- + _valueToBuffer(v) { + return Buffer.from(v); + } + //--------------------------------------------------------------------------------------- + //Method: _bundleToP12 + // + //Input: bundle + //Output: p12Base64 + // + //This method converts a certificate bundle to a .p12 file that can be used in a browser. + //--------------------------------------------------------------------------------------- + _bundleToP12(bundle, password) { + const { forge: { asn1, pki, pkcs12, util } } = this; + const pemCertificate = pki.certificateFromPem(bundle.cert); + const pemKey = pki.privateKeyFromPem(bundle.key); + const p12Asn1 = pkcs12.toPkcs12Asn1(pemKey, pemCertificate, password, { + algorithm: '3des' + }); + const p12Bytes = asn1.toDer(p12Asn1).getBytes(); + const p12Base64 = util.encode64(p12Bytes); + return p12Base64; + } + //--------------------------------------------------------------------------------------- + //Method: generateRandomCerts + // + //Input: none + //Output: bundle + // + //This method utilizes node-forge to create a key/cert/ca bundle entirely in javascript. + //It uses the factory properties set in the factory constructor to configure the new + //key/cert/ca bundle, and then returns the newly generated bundle. Directly calling this + //method will result in new bundles with different private keys, but the certificate + //x509 attributes will match across all bundles generated with the same factory instance. + //--------------------------------------------------------------------------------------- + generateRandomCerts() { + const { forge: { pki } } = this; + //generate a keypair and create an X.509v3 certificate + const keys = pki.rsa.generateKeyPair(this.encryptionBits); + const cert = pki.createCertificate(); + cert.publicKey = keys.publicKey; + cert.serialNumber = Math.round(Math.random() * max32BitInt).toString(16); + cert.validity.notBefore = new Date(); + cert.validity.notAfter = new Date(); + cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + this.validForYears); + const altNames = []; + let attrs; + if (this.certType === 'person') { + const OUArray = Array.isArray(this.organizationalUnit) + ? this.organizationalUnit.map((v) => { + return { + shortName: 'OU', + value: v + }; + }) + : [ + { + shortName: 'OU', + value: 'DoD' + }, + { + shortName: 'OU', + value: 'DoDIIS' + }, + { + shortName: 'OU', + value: 'People' + } + ]; + attrs = [].concat([ + { + name: 'countryName', + value: this.countryName + }, + { + name: 'organizationName', + value: this.organizationName + } + ], OUArray, [ + { + name: 'commonName', + value: this.commonName + } + ]); + } + else { + attrs = [ + { + name: 'commonName', + value: this.commonName + }, + { + name: 'countryName', + value: this.countryName + }, + { + shortName: 'ST', + value: this.stateName + }, + { + name: 'localityName', + value: this.localityName + }, + { + name: 'organizationName', + value: this.organizationName + }, + { + shortName: 'OU', + value: this.organizationalUnit + } + ]; + if (this.altName) { + altNames.push({ + type: 6, + value: this.altName + }); + } + if (this.altIp) { + altNames.push({ + type: 7, + ip: this.altIp + }); + } + } + cert.setSubject(attrs); + cert.setIssuer(attrs); + cert.setExtensions([ + { + name: 'basicConstraints', + cA: true + }, + { + name: 'keyUsage', + keyCertSign: true, + digitalSignature: true, + nonRepudiation: true, + keyEncipherment: true, + dataEncipherment: true + }, + { + name: 'extKeyUsage', + serverAuth: true, + clientAuth: true, + codeSigning: true, + emailProtection: true, + timeStamping: true + }, + { + name: 'nsCertType', + client: true, + server: true, + email: true, + objsign: true, + sslCA: true, + emailCA: true, + objCA: true + }, + { + name: 'subjectAltName', + altNames: altNames + }, + { + name: 'subjectKeyIdentifier' + } + ]); + cert.sign(keys.privateKey); + // convert a Forge certificate to PEM + // for self generated certs, ca will be undefined + const pem = pki.certificateToPem(cert); + const certBundle = { + key: pki.privateKeyToPem(keys.privateKey), + cert: pem, + ca: undefined + }; + return certBundle; + } +} +export default CertificateFactory; diff --git a/backend/utils/certificateIdentity.js b/backend/utils/certificateIdentity.js new file mode 100644 index 000000000..338425719 --- /dev/null +++ b/backend/utils/certificateIdentity.js @@ -0,0 +1,42 @@ +function subjectToString(subject) { + const aggr = []; + Object.keys(subject).forEach((k) => { + if (typeof subject[k] === 'string') { + aggr.push(`${k}=${subject[k]}`); + } + else if (Array.isArray(subject[k]) && subject[k].length > 0) { + const temp = []; + subject[k].forEach((v) => { + temp.push(`${k}=${v}`); + }); + aggr.push(temp.reverse().join(',')); + } + }); + return aggr.reverse().join(','); +} +export default (rejectUnauthorized = false) => { + return function (req, res, next) { + const { connection, protocol } = req; + if (protocol === 'https' && (connection.authorized || rejectUnauthorized === false)) { + try { + const { subject } = connection.getPeerCertificate(); + const CN = subject.CN; + const DN = subjectToString(subject); + req.identity = { + DN, + CN + }; + return next(); + } + catch (e) { + //if this server requsts certs, but there is an error getting them, DO NOT PASS GO! + return res.status(500).json({ + message: e.toString() + }); + } + } + return res.status(403).json({ + message: 'forbidden' + }); + }; +}; diff --git a/backend/utils/common.js b/backend/utils/common.js index 012ea6d62..39f8c2295 100644 --- a/backend/utils/common.js +++ b/backend/utils/common.js @@ -12,6 +12,7 @@ export class CommonService { this.rtl_conf_file_path = ''; this.port = 3000; this.host = null; + this.ssl = false; this.rtl_pass = ''; this.flg_allow_password_update = true; this.rtl_secret2fa = ''; @@ -24,7 +25,20 @@ export class CommonService { this.read_dummy_data = false; this.baseHref = '/rtl'; this.dummy_data_array_from_file = []; - this.MONTHS = [{ name: 'JAN', days: 31 }, { name: 'FEB', days: 28 }, { name: 'MAR', days: 31 }, { name: 'APR', days: 30 }, { name: 'MAY', days: 31 }, { name: 'JUN', days: 30 }, { name: 'JUL', days: 31 }, { name: 'AUG', days: 31 }, { name: 'SEP', days: 30 }, { name: 'OCT', days: 31 }, { name: 'NOV', days: 30 }, { name: 'DEC', days: 31 }]; + this.MONTHS = [ + { name: 'JAN', days: 31 }, + { name: 'FEB', days: 28 }, + { name: 'MAR', days: 31 }, + { name: 'APR', days: 30 }, + { name: 'MAY', days: 31 }, + { name: 'JUN', days: 30 }, + { name: 'JUL', days: 31 }, + { name: 'AUG', days: 31 }, + { name: 'SEP', days: 30 }, + { name: 'OCT', days: 31 }, + { name: 'NOV', days: 30 }, + { name: 'DEC', days: 31 } + ]; this.getSwapServerOptions = (req) => { const swapOptions = { url: req.session.selectedNode.swap_server_url, @@ -34,13 +48,29 @@ export class CommonService { }; if (req.session.selectedNode.swap_macaroon_path) { try { - swapOptions.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.swap_macaroon_path, 'loop.macaroon')).toString('hex') }; + swapOptions.headers = { + 'Grpc-Metadata-macaroon': fs + .readFileSync(join(req.session.selectedNode.swap_macaroon_path, 'loop.macaroon')) + .toString('hex') + }; } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Loop macaroon Error', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Loop macaroon Error', + error: err + }); } } - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Swap Options', data: swapOptions }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Swap Options', + data: swapOptions + }); return swapOptions; }; this.getBoltzServerOptions = (req) => { @@ -52,23 +82,42 @@ export class CommonService { }; if (req.session.selectedNode.boltz_macaroon_path) { try { - boltzOptions.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.boltz_macaroon_path, 'admin.macaroon')).toString('hex') }; + boltzOptions.headers = { + 'Grpc-Metadata-macaroon': fs + .readFileSync(join(req.session.selectedNode.boltz_macaroon_path, 'admin.macaroon')) + .toString('hex') + }; } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Boltz macaroon Error', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Boltz macaroon Error', + error: err + }); } } - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Boltz Options', data: boltzOptions }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Boltz Options', + data: boltzOptions + }); return boltzOptions; }; this.getOptions = (req) => { if (req.session.selectedNode && req.session.selectedNode.options) { - req.session.selectedNode.options.method = (req.session.selectedNode.ln_implementation && req.session.selectedNode.ln_implementation.toUpperCase() !== 'ECL') ? 'GET' : 'POST'; + req.session.selectedNode.options.method = + req.session.selectedNode.ln_implementation && req.session.selectedNode.ln_implementation.toUpperCase() !== 'ECL' + ? 'GET' + : 'POST'; delete req.session.selectedNode.options.form; req.session.selectedNode.options.qs = {}; return req.session.selectedNode.options; } - return this.handleError({ statusCode: 401, message: 'Session expired after a day\'s inactivity' }, 'Session Expired', 'Session Expiry Error', this.initSelectedNode); + return this.handleError({ statusCode: 401, message: "Session expired after a day's inactivity" }, 'Session Expired', 'Session Expiry Error', this.initSelectedNode); }; this.updateSelectedNodeOptions = (req) => { if (!req.session.selectedNode) { @@ -84,18 +133,32 @@ export class CommonService { if (req.session.selectedNode && req.session.selectedNode.ln_implementation) { switch (req.session.selectedNode.ln_implementation.toUpperCase()) { case 'CLN': - req.session.selectedNode.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'access.macaroon'))).toString('base64') }; + req.session.selectedNode.options.headers = { + macaroon: Buffer.from(fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'access.macaroon'))).toString('base64') + }; break; case 'ECL': - req.session.selectedNode.options.headers = { authorization: 'Basic ' + Buffer.from(':' + req.session.selectedNode.ln_api_password).toString('base64') }; + req.session.selectedNode.options.headers = { + authorization: 'Basic ' + Buffer.from(':' + req.session.selectedNode.ln_api_password).toString('base64') + }; break; default: - req.session.selectedNode.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') }; + req.session.selectedNode.options.headers = { + 'Grpc-Metadata-macaroon': fs + .readFileSync(join(req.session.selectedNode.macaroon_path, 'admin.macaroon')) + .toString('hex') + }; break; } } if (req.session.selectedNode) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Updated Node Options for ' + req.session.selectedNode.ln_node, data: req.session.selectedNode.options }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Updated Node Options for ' + req.session.selectedNode.ln_node, + data: req.session.selectedNode.options + }); } return { status: 200, message: 'Updated Successfully' }; } @@ -106,7 +169,13 @@ export class CommonService { json: true, form: null }; - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Update Selected Node Options Error', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Update Selected Node Options Error', + error: err + }); return { status: 502, message: err }; } }; @@ -126,19 +195,31 @@ export class CommonService { if (node.ln_implementation) { switch (node.ln_implementation.toUpperCase()) { case 'CLN': - node.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(node.macaroon_path, 'access.macaroon'))).toString('base64') }; + node.options.headers = { + macaroon: Buffer.from(fs.readFileSync(join(node.macaroon_path, 'access.macaroon'))).toString('base64') + }; break; case 'ECL': - node.options.headers = { authorization: 'Basic ' + Buffer.from(':' + node.ln_api_password).toString('base64') }; + node.options.headers = { + authorization: 'Basic ' + Buffer.from(':' + node.ln_api_password).toString('base64') + }; break; default: - node.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(node.macaroon_path, 'admin.macaroon')).toString('hex') }; + node.options.headers = { + 'Grpc-Metadata-macaroon': fs.readFileSync(join(node.macaroon_path, 'admin.macaroon')).toString('hex') + }; break; } } } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Common Set Options Error', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Common Set Options Error', + error: err + }); node.options = { url: '', rejectUnauthorized: false, @@ -146,7 +227,13 @@ export class CommonService { form: '' }; } - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Set Node Options for ' + node.ln_node, data: node.options }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Set Node Options for ' + node.ln_node, + data: node.options + }); }); this.updateSelectedNodeOptions(req); } @@ -168,23 +255,33 @@ export class CommonService { minutes = +minutes < 10 ? '0' + minutes : minutes; let seconds = myDate.getSeconds().toString(); seconds = +seconds < 10 ? '0' + seconds : seconds; - return days + '/' + this.MONTHS[myDate.getMonth()].name + '/' + myDate.getFullYear() + ' ' + hours + ':' + minutes + ':' + seconds; + return (days + + '/' + + this.MONTHS[myDate.getMonth()].name + + '/' + + myDate.getFullYear() + + ' ' + + hours + + ':' + + minutes + + ':' + + seconds); }; this.sortAscByKey = (array, key) => array.sort((a, b) => { const x = +a[key]; const y = +b[key]; - return ((x < y) ? -1 : ((x > y) ? 1 : 0)); + return x < y ? -1 : x > y ? 1 : 0; }); this.sortAscByStrKey = (array, key) => array.sort((a, b) => { const x = a[key] ? a[key].toUpperCase() : ''; const y = b[key] ? b[key].toUpperCase() : ''; - return ((x < y) ? -1 : ((x > y) ? 1 : 0)); + return x < y ? -1 : x > y ? 1 : 0; }); this.sortDescByKey = (array, key) => { const temp = array.sort((a, b) => { const x = +a[key] ? +a[key] : 0; const y = +b[key] ? +b[key] : 0; - return (x > y) ? -1 : ((x < y) ? 1 : 0); + return x > y ? -1 : x < y ? 1 : 0; }); return temp; }; @@ -192,7 +289,7 @@ export class CommonService { const temp = array.sort((a, b) => { const x = a[key] ? a[key].toUpperCase() : ''; const y = b[key] ? b[key].toUpperCase() : ''; - return (x > y) ? -1 : ((x < y) ? 1 : 0); + return x > y ? -1 : x < y ? 1 : 0; }); return temp; }; @@ -211,7 +308,10 @@ export class CommonService { if (err.options && err.options.headers && err.options.headers['Grpc-Metadata-macaroon']) { delete err.options.headers['Grpc-Metadata-macaroon']; } - if (err.response && err.response.request && err.response.request.headers && err.response.request.headers['Grpc-Metadata-macaroon']) { + if (err.response && + err.response.request && + err.response.request.headers && + err.response.request.headers['Grpc-Metadata-macaroon']) { delete err.response.request.headers['Grpc-Metadata-macaroon']; } break; @@ -219,7 +319,10 @@ export class CommonService { if (err.options && err.options.headers && err.options.headers.macaroon) { delete err.options.headers.macaroon; } - if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.macaroon) { + if (err.response && + err.response.request && + err.response.request.headers && + err.response.request.headers.macaroon) { delete err.response.request.headers.macaroon; } break; @@ -227,7 +330,10 @@ export class CommonService { if (err.options && err.options.headers && err.options.headers.authorization) { delete err.options.headers.authorization; } - if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) { + if (err.response && + err.response.request && + err.response.request.headers && + err.response.request.headers.authorization) { delete err.response.request.headers.authorization; } break; @@ -237,24 +343,45 @@ export class CommonService { } break; } - this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: fileName, msg: errMsg, error: (typeof err === 'object' ? JSON.stringify(err) : (typeof err === 'string') ? err : 'Unknown Error') }); + this.logger.log({ + selectedNode: selectedNode, + level: 'ERROR', + fileName: fileName, + msg: errMsg, + error: typeof err === 'object' ? JSON.stringify(err) : typeof err === 'string' ? err : 'Unknown Error' + }); const newErrorObj = { - statusCode: err.statusCode ? err.statusCode : err.status ? err.status : (err.error && err.error.code && err.error.code === 'ECONNREFUSED') ? 503 : 500, - message: (err.error && err.error.message) ? err.error.message : err.message ? err.message : errMsg, - error: ((err.error && err.error.error && err.error.error.error && typeof err.error.error.error === 'string') ? err.error.error.error : - (err.error && err.error.error && typeof err.error.error === 'string') ? err.error.error : - (err.error && err.error.error && err.error.error.message && typeof err.error.error.message === 'string') ? err.error.error.message : - (err.error && err.error.message && typeof err.error.message === 'string') ? err.error.message : - (err.error && typeof err.error === 'string') ? err.error : - (err.message && typeof err.message === 'string') ? err.message : (typeof err === 'string') ? err : 'Unknown Error') + statusCode: err.statusCode + ? err.statusCode + : err.status + ? err.status + : err.error && err.error.code && err.error.code === 'ECONNREFUSED' + ? 503 + : 500, + message: err.error && err.error.message ? err.error.message : err.message ? err.message : errMsg, + error: err.error && err.error.error && err.error.error.error && typeof err.error.error.error === 'string' + ? err.error.error.error + : err.error && err.error.error && typeof err.error.error === 'string' + ? err.error.error + : err.error && err.error.error && err.error.error.message && typeof err.error.error.message === 'string' + ? err.error.error.message + : err.error && err.error.message && typeof err.error.message === 'string' + ? err.error.message + : err.error && typeof err.error === 'string' + ? err.error + : err.message && typeof err.message === 'string' + ? err.message + : typeof err === 'string' + ? err + : 'Unknown Error' }; return newErrorObj; }; - this.getRequestIP = (req) => ((typeof req.headers['x-forwarded-for'] === 'string' && req.headers['x-forwarded-for'].split(',').shift()) || + this.getRequestIP = (req) => (typeof req.headers['x-forwarded-for'] === 'string' && req.headers['x-forwarded-for'].split(',').shift()) || req.ip || req.connection.remoteAddress || req.socket.remoteAddress || - (req.connection.socket ? req.connection.socket.remoteAddress : null)); + (req.connection.socket ? req.connection.socket.remoteAddress : null); this.getDummyData = (dataKey, lnImplementation) => { const dummyDataFile = this.rtl_conf_file_path + sep + 'ECLDummyData.log'; return new Promise((resolve, reject) => { @@ -262,10 +389,20 @@ export class CommonService { fs.readFile(dummyDataFile, 'utf8', (err, data) => { if (err) { if (err.code === 'ENOENT') { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Dummy data file does not exist' }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Dummy data file does not exist' + }); } else { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Getting dummy data failed' }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Getting dummy data failed' + }); } } else { @@ -286,7 +423,12 @@ export class CommonService { this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8'); } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while reading cookie: \n' + err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while reading cookie: \n' + err + }); throw new Error(err); } } @@ -298,7 +440,12 @@ export class CommonService { this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8'); } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while reading the cookie: \n' + err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while reading the cookie: \n' + err + }); throw new Error(err); } } @@ -309,7 +456,13 @@ export class CommonService { this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8'); } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Something went wrong while refreshing cookie', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Something went wrong while refreshing cookie', + error: err + }); throw new Error(err); } }; @@ -336,18 +489,31 @@ export class CommonService { }, initDir); }; this.replacePasswordWithHash = (multiPassHashed) => { - this.rtl_conf_file_path = process.env.RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : join(dirname(fileURLToPath(import.meta.url)), '../..'); + this.rtl_conf_file_path = process.env.RTL_CONFIG_PATH + ? process.env.RTL_CONFIG_PATH + : join(dirname(fileURLToPath(import.meta.url)), '../..'); try { const RTLConfFile = this.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); config.multiPassHashed = multiPassHashed; delete config.multiPass; fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Please note that, RTL has encrypted the plaintext password into its corresponding hash' }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Please note that, RTL has encrypted the plaintext password into its corresponding hash' + }); return config.multiPassHashed; } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Password hashing failed', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Password hashing failed', + error: err + }); } }; this.getAllNodeAllChannelBackup = (node) => { @@ -358,28 +524,63 @@ export class CommonService { json: true, headers: { 'Grpc-Metadata-macaroon': fs.readFileSync(node.macaroon_path + '/admin.macaroon').toString('hex') } }; - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Getting Channel Backup for Node ' + node.ln_node + '..' }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Getting Channel Backup for Node ' + node.ln_node + '..' + }); request(options).then((body) => { fs.writeFile(channel_backup_file, JSON.stringify(body), (err) => { if (err) { if (node.ln_node) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for Node ' + node.ln_node, error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Error in Channel Backup for Node ' + node.ln_node, + error: err + }); } else { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for File ' + channel_backup_file, error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Error in Channel Backup for File ' + channel_backup_file, + error: err + }); } } else { if (node.ln_node) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Successful in Channel Backup for Node ' + node.ln_node, data: body }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Successful in Channel Backup for Node ' + node.ln_node, + data: body + }); } else { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Successful in Channel Backup for File ' + channel_backup_file, data: body }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Successful in Channel Backup for File ' + channel_backup_file, + data: body + }); } } }); }, (err) => { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for Node ' + node.ln_node, error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Error in Channel Backup for Node ' + node.ln_node, + error: err + }); fs.writeFile(channel_backup_file, '', () => { }); }); }; @@ -387,27 +588,84 @@ export class CommonService { if (currentVersion) { const versionsArr = currentVersion.trim().replace('v', '').split('-')[0].split('.') || []; const checkVersionsArr = checkVersion.split('.'); - return (+versionsArr[0] > +checkVersionsArr[0]) || + return (+versionsArr[0] > +checkVersionsArr[0] || (+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] > +checkVersionsArr[1]) || - (+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] === +checkVersionsArr[1] && +versionsArr[2] >= +checkVersionsArr[2]); + (+versionsArr[0] === +checkVersionsArr[0] && + +versionsArr[1] === +checkVersionsArr[1] && + +versionsArr[2] >= +checkVersionsArr[2])); } return false; }; - this.getMonthDays = (selMonth, selYear) => ((selMonth === 1 && selYear % 4 === 0) ? (this.MONTHS[selMonth].days + 1) : this.MONTHS[selMonth].days); + this.getMonthDays = (selMonth, selYear) => selMonth === 1 && selYear % 4 === 0 ? this.MONTHS[selMonth].days + 1 : this.MONTHS[selMonth].days; this.logEnvVariables = (req) => { const selNode = req.session.selectedNode; if (selNode && selNode.index) { - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'PORT: ' + this.port }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'HOST: ' + this.host }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'SSO: ' + this.rtl_sso }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'DEFAULT NODE INDEX: ' + selNode.index }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'INDEX: ' + selNode.index }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LN NODE: ' + selNode.ln_node }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LN IMPLEMENTATION: ' + selNode.ln_implementation }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'FIAT CONVERSION: ' + selNode.fiat_conversion }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'CURRENCY UNIT: ' + selNode.currency_unit }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LN SERVER URL: ' + selNode.ln_server_url }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LOGOUT REDIRECT LINK: ' + this.logout_redirect_link + '\r\n' }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'PORT: ' + this.port + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'HOST: ' + this.host + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'SSO: ' + this.rtl_sso + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'DEFAULT NODE INDEX: ' + selNode.index + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'INDEX: ' + selNode.index + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'LN NODE: ' + selNode.ln_node + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'LN IMPLEMENTATION: ' + selNode.ln_implementation + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'FIAT CONVERSION: ' + selNode.fiat_conversion + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'CURRENCY UNIT: ' + selNode.currency_unit + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'LN SERVER URL: ' + selNode.ln_server_url + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'LOGOUT REDIRECT LINK: ' + this.logout_redirect_link + '\r\n' + }); } }; this.filterData = (dataKey, lnImplementation) => { @@ -484,7 +742,9 @@ export class CommonService { } } const foundDataLine = this.dummy_data_array_from_file.find((dataItem) => dataItem.includes(search_string)); - const dataStr = foundDataLine ? foundDataLine.substring((foundDataLine.indexOf(search_string)) + search_string.length) : '{}'; + const dataStr = foundDataLine + ? foundDataLine.substring(foundDataLine.indexOf(search_string) + search_string.length) + : '{}'; return JSON.parse(dataStr); }; } diff --git a/backend/utils/config.js b/backend/utils/config.js index d06d40f9d..a623c5c9c 100644 --- a/backend/utils/config.js +++ b/backend/utils/config.js @@ -86,9 +86,44 @@ export class ConfigService { } return false; }; + this.normalizeSSL = (val) => { + let ssl; + if (typeof val === 'string') { + ssl = val === 'true' ? true : val === 'false' ? false : JSON.parse(val); + } + else if (typeof val === 'undefined') { + return false; + } + else if (typeof val === 'boolean') { + return val; + } + else { + ssl = val; + } + const { key = undefined, cert = undefined, ca = undefined, altIp = '127.0.0.1', certType = 'component', commonName = 'localhost', countryName = 'US', encryptionBits = 2048, stateName = 'New York', localityName = 'New York', organizationName = 'RTL', organizationalUnit = 'RTL', validForYears = 10, requestCert = false, rejectUnauthorized = false } = ssl; + return { + key, + cert, + ca, + altIp, + certType, + commonName, + countryName, + encryptionBits, + stateName, + localityName, + organizationName, + organizationalUnit, + validForYears, + requestCert, + rejectUnauthorized + }; + }; this.updateLogByLevel = () => { let updateLogFlag = false; - this.common.rtl_conf_file_path = process.env.RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : join(this.directoryName, '../..'); + this.common.rtl_conf_file_path = process.env.RTL_CONFIG_PATH + ? process.env.RTL_CONFIG_PATH + : join(this.directoryName, '../..'); try { const RTLConfFile = this.common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); @@ -108,7 +143,7 @@ export class ConfigService { } }; this.validateNodeConfig = (config) => { - if ((+process.env.RTL_SSO === 0) || (typeof process.env.RTL_SSO === 'undefined' && +config.SSO.rtlSSO === 0)) { + if (+process.env.RTL_SSO === 0 || (typeof process.env.RTL_SSO === 'undefined' && +config.SSO.rtlSSO === 0)) { if (process.env.APP_PASSWORD && process.env.APP_PASSWORD.trim() !== '') { this.common.rtl_pass = this.hash.update(process.env.APP_PASSWORD).digest('hex'); this.common.flg_allow_password_update = false; @@ -120,7 +155,8 @@ export class ConfigService { this.common.rtl_pass = this.common.replacePasswordWithHash(this.hash.update(config.multiPass).digest('hex')); } else { - this.errMsg = this.errMsg + '\nNode Authentication can be set with multiPass only. Please set multiPass in RTL-Config.json'; + this.errMsg = + this.errMsg + '\nNode Authentication can be set with multiPass only. Please set multiPass in RTL-Config.json'; } this.common.rtl_secret2fa = config.secret2fa; } @@ -129,21 +165,39 @@ export class ConfigService { this.errMsg = this.errMsg + '\nRTL Password cannot be set with SSO. Please set SSO as 0 or remove password.'; } } - this.common.port = (process.env.PORT) ? this.normalizePort(process.env.PORT) : (config.port) ? this.normalizePort(config.port) : 3000; - this.common.host = (process.env.HOST) ? process.env.HOST : (config.host) ? config.host : null; + this.common.port = process.env.PORT + ? this.normalizePort(process.env.PORT) + : config.port + ? this.normalizePort(config.port) + : 3000; + this.common.ssl = process.env.SSL + ? this.normalizeSSL(process.env.SSL) + : config.ssl + ? this.normalizeSSL(config.ssl) + : false; + this.common.host = process.env.HOST ? process.env.HOST : config.host ? config.host : null; if (config.nodes && config.nodes.length > 0) { config.nodes.forEach((node, idx) => { this.common.nodes[idx] = {}; this.common.nodes[idx].index = node.index; this.common.nodes[idx].ln_node = node.lnNode; - this.common.nodes[idx].ln_implementation = (process.env.LN_IMPLEMENTATION) ? process.env.LN_IMPLEMENTATION : node.lnImplementation ? node.lnImplementation : 'LND'; + this.common.nodes[idx].ln_implementation = process.env.LN_IMPLEMENTATION + ? process.env.LN_IMPLEMENTATION + : node.lnImplementation + ? node.lnImplementation + : 'LND'; if (this.common.nodes[idx].ln_implementation === 'CLT') { this.common.nodes[idx].ln_implementation = 'CLN'; } - if (this.common.nodes[idx].ln_implementation !== 'ECL' && process.env.MACAROON_PATH && process.env.MACAROON_PATH.trim() !== '') { + if (this.common.nodes[idx].ln_implementation !== 'ECL' && + process.env.MACAROON_PATH && + process.env.MACAROON_PATH.trim() !== '') { this.common.nodes[idx].macaroon_path = process.env.MACAROON_PATH; } - else if (this.common.nodes[idx].ln_implementation !== 'ECL' && node.Authentication && node.Authentication.macaroonPath && node.Authentication.macaroonPath.trim() !== '') { + else if (this.common.nodes[idx].ln_implementation !== 'ECL' && + node.Authentication && + node.Authentication.macaroonPath && + node.Authentication.macaroonPath.trim() !== '') { this.common.nodes[idx].macaroon_path = node.Authentication.macaroonPath; } else if (this.common.nodes[idx].ln_implementation !== 'ECL') { @@ -169,14 +223,18 @@ export class ConfigService { else { this.common.nodes[idx].config_path = ''; } - if (this.common.nodes[idx].ln_implementation === 'ECL' && this.common.nodes[idx].ln_api_password === '' && this.common.nodes[idx].config_path !== '') { + if (this.common.nodes[idx].ln_implementation === 'ECL' && + this.common.nodes[idx].ln_api_password === '' && + this.common.nodes[idx].config_path !== '') { try { const exists = fs.existsSync(this.common.nodes[idx].config_path); if (exists) { try { const configFile = fs.readFileSync(this.common.nodes[idx].config_path, 'utf-8'); const iniParsed = ini.parse(configFile); - this.common.nodes[idx].ln_api_password = iniParsed['eclair.api.password'] ? iniParsed['eclair.api.password'] : parseHocon(configFile).eclair.api.password; + this.common.nodes[idx].ln_api_password = iniParsed['eclair.api.password'] + ? iniParsed['eclair.api.password'] + : parseHocon(configFile).eclair.api.password; } catch (err) { this.errMsg = this.errMsg + '\nSomething went wrong while reading config file: \n' + err; @@ -191,22 +249,35 @@ export class ConfigService { } } if (this.common.nodes[idx].ln_implementation === 'ECL' && this.common.nodes[idx].ln_api_password === '') { - this.errMsg = this.errMsg + '\nPlease set config path Or api password for node index ' + node.index + ' in RTL-Config.json! It is mandatory for Eclair authentication!'; + this.errMsg = + this.errMsg + + '\nPlease set config path Or api password for node index ' + + node.index + + ' in RTL-Config.json! It is mandatory for Eclair authentication!'; } if (process.env.LN_SERVER_URL && process.env.LN_SERVER_URL.trim() !== '') { - this.common.nodes[idx].ln_server_url = process.env.LN_SERVER_URL.endsWith('/v1') ? process.env.LN_SERVER_URL.slice(0, -3) : process.env.LN_SERVER_URL; + this.common.nodes[idx].ln_server_url = process.env.LN_SERVER_URL.endsWith('/v1') + ? process.env.LN_SERVER_URL.slice(0, -3) + : process.env.LN_SERVER_URL; } else if (process.env.LND_SERVER_URL && process.env.LND_SERVER_URL.trim() !== '') { - this.common.nodes[idx].ln_server_url = process.env.LND_SERVER_URL.endsWith('/v1') ? process.env.LND_SERVER_URL.slice(0, -3) : process.env.LND_SERVER_URL; + this.common.nodes[idx].ln_server_url = process.env.LND_SERVER_URL.endsWith('/v1') + ? process.env.LND_SERVER_URL.slice(0, -3) + : process.env.LND_SERVER_URL; } else if (node.Settings.lnServerUrl && node.Settings.lnServerUrl.trim() !== '') { - this.common.nodes[idx].ln_server_url = node.Settings.lnServerUrl.endsWith('/v1') ? node.Settings.lnServerUrl.slice(0, -3) : node.Settings.lnServerUrl; + this.common.nodes[idx].ln_server_url = node.Settings.lnServerUrl.endsWith('/v1') + ? node.Settings.lnServerUrl.slice(0, -3) + : node.Settings.lnServerUrl; } else if (node.Settings.lndServerUrl && node.Settings.lndServerUrl.trim() !== '') { - this.common.nodes[idx].ln_server_url = node.Settings.lndServerUrl.endsWith('/v1') ? node.Settings.lndServerUrl.slice(0, -3) : node.Settings.lndServerUrl; + this.common.nodes[idx].ln_server_url = node.Settings.lndServerUrl.endsWith('/v1') + ? node.Settings.lndServerUrl.slice(0, -3) + : node.Settings.lndServerUrl; } else { - this.errMsg = this.errMsg + '\nPlease set LN Server URL for node index ' + node.index + ' in RTL-Config.json!'; + this.errMsg = + this.errMsg + '\nPlease set LN Server URL for node index ' + node.index + ' in RTL-Config.json!'; } this.common.nodes[idx].user_persona = node.Settings.userPersona ? node.Settings.userPersona : 'MERCHANT'; this.common.nodes[idx].theme_mode = node.Settings.themeMode ? node.Settings.themeMode : 'DAY'; @@ -217,32 +288,56 @@ export class ConfigService { this.common.nodes[idx].currency_unit = node.Settings.currencyUnit ? node.Settings.currencyUnit : 'USD'; } if (process.env.SWAP_SERVER_URL && process.env.SWAP_SERVER_URL.trim() !== '') { - this.common.nodes[idx].swap_server_url = process.env.SWAP_SERVER_URL.endsWith('/v1') ? process.env.SWAP_SERVER_URL.slice(0, -3) : process.env.SWAP_SERVER_URL; + this.common.nodes[idx].swap_server_url = process.env.SWAP_SERVER_URL.endsWith('/v1') + ? process.env.SWAP_SERVER_URL.slice(0, -3) + : process.env.SWAP_SERVER_URL; this.common.nodes[idx].swap_macaroon_path = process.env.SWAP_MACAROON_PATH; } else if (node.Settings.swapServerUrl && node.Settings.swapServerUrl.trim() !== '') { - this.common.nodes[idx].swap_server_url = node.Settings.swapServerUrl.endsWith('/v1') ? node.Settings.swapServerUrl.slice(0, -3) : node.Settings.swapServerUrl; - this.common.nodes[idx].swap_macaroon_path = node.Authentication.swapMacaroonPath ? node.Authentication.swapMacaroonPath : ''; + this.common.nodes[idx].swap_server_url = node.Settings.swapServerUrl.endsWith('/v1') + ? node.Settings.swapServerUrl.slice(0, -3) + : node.Settings.swapServerUrl; + this.common.nodes[idx].swap_macaroon_path = node.Authentication.swapMacaroonPath + ? node.Authentication.swapMacaroonPath + : ''; } else { this.common.nodes[idx].swap_server_url = ''; this.common.nodes[idx].swap_macaroon_path = ''; } if (process.env.BOLTZ_SERVER_URL && process.env.BOLTZ_SERVER_URL.trim() !== '') { - this.common.nodes[idx].boltz_server_url = process.env.BOLTZ_SERVER_URL.endsWith('/v1') ? process.env.BOLTZ_SERVER_URL.slice(0, -3) : process.env.BOLTZ_SERVER_URL; + this.common.nodes[idx].boltz_server_url = process.env.BOLTZ_SERVER_URL.endsWith('/v1') + ? process.env.BOLTZ_SERVER_URL.slice(0, -3) + : process.env.BOLTZ_SERVER_URL; this.common.nodes[idx].boltz_macaroon_path = process.env.BOLTZ_MACAROON_PATH; } else if (node.Settings.boltzServerUrl && node.Settings.boltzServerUrl.trim() !== '') { - this.common.nodes[idx].boltz_server_url = node.Settings.boltzServerUrl.endsWith('/v1') ? node.Settings.boltzServerUrl.slice(0, -3) : node.Settings.boltzServerUrl; - this.common.nodes[idx].boltz_macaroon_path = node.Authentication.boltzMacaroonPath ? node.Authentication.boltzMacaroonPath : ''; + this.common.nodes[idx].boltz_server_url = node.Settings.boltzServerUrl.endsWith('/v1') + ? node.Settings.boltzServerUrl.slice(0, -3) + : node.Settings.boltzServerUrl; + this.common.nodes[idx].boltz_macaroon_path = node.Authentication.boltzMacaroonPath + ? node.Authentication.boltzMacaroonPath + : ''; } else { this.common.nodes[idx].boltz_server_url = ''; this.common.nodes[idx].boltz_macaroon_path = ''; } - this.common.nodes[idx].enable_offers = process.env.ENABLE_OFFERS ? process.env.ENABLE_OFFERS : (node.Settings.enableOffers) ? node.Settings.enableOffers : false; - this.common.nodes[idx].bitcoind_config_path = process.env.BITCOIND_CONFIG_PATH ? process.env.BITCOIND_CONFIG_PATH : (node.Settings.bitcoindConfigPath) ? node.Settings.bitcoindConfigPath : ''; - this.common.nodes[idx].channel_backup_path = process.env.CHANNEL_BACKUP_PATH ? process.env.CHANNEL_BACKUP_PATH : (node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : this.common.rtl_conf_file_path + sep + 'channels-backup' + sep + 'node-' + node.index; + this.common.nodes[idx].enable_offers = process.env.ENABLE_OFFERS + ? process.env.ENABLE_OFFERS + : node.Settings.enableOffers + ? node.Settings.enableOffers + : false; + this.common.nodes[idx].bitcoind_config_path = process.env.BITCOIND_CONFIG_PATH + ? process.env.BITCOIND_CONFIG_PATH + : node.Settings.bitcoindConfigPath + ? node.Settings.bitcoindConfigPath + : ''; + this.common.nodes[idx].channel_backup_path = process.env.CHANNEL_BACKUP_PATH + ? process.env.CHANNEL_BACKUP_PATH + : node.Settings.channelBackupPath + ? node.Settings.channelBackupPath + : this.common.rtl_conf_file_path + sep + 'channels-backup' + sep + 'node-' + node.index; try { this.common.createDirectory(this.common.nodes[idx].channel_backup_path); const exists = fs.existsSync(this.common.nodes[idx].channel_backup_path + sep + 'channel-all.bak'); @@ -257,15 +352,30 @@ export class ConfigService { } } catch (err) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while creating backup file: \n' + err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while creating backup file: \n' + err + }); } } } catch (err) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while creating the backup directory: \n' + err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while creating the backup directory: \n' + err + }); } this.common.nodes[idx].log_file = this.common.rtl_conf_file_path + '/logs/RTL-Node-' + node.index + '.log'; - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'Config', msg: 'Node Config: ' + JSON.stringify(this.common.nodes[idx]) }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'Config', + msg: 'Node Config: ' + JSON.stringify(this.common.nodes[idx]) + }); const log_file = this.common.nodes[idx].log_file; if (fs.existsSync(log_file)) { fs.writeFile(log_file, '', () => { }); @@ -278,7 +388,12 @@ export class ConfigService { createStream.end(); } catch (err) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while creating log file ' + log_file + ': \n' + err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while creating log file ' + log_file + ': \n' + err + }); } } }); @@ -329,7 +444,9 @@ export class ConfigService { }; this.setServerConfiguration = () => { try { - this.common.rtl_conf_file_path = (process.env.RTL_CONFIG_PATH) ? process.env.RTL_CONFIG_PATH : join(this.directoryName, '../..'); + this.common.rtl_conf_file_path = process.env.RTL_CONFIG_PATH + ? process.env.RTL_CONFIG_PATH + : join(this.directoryName, '../..'); const confFileFullPath = this.common.rtl_conf_file_path + sep + 'RTL-Config.json'; if (!fs.existsSync(confFileFullPath)) { fs.writeFileSync(confFileFullPath, JSON.stringify(this.setDefaultConfig())); @@ -340,7 +457,12 @@ export class ConfigService { this.setSelectedNode(config); } catch (err) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while configuring the node server: \n' + err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while configuring the node server: \n' + err + }); throw new Error(err); } }; diff --git a/backend/utils/cors.js b/backend/utils/cors.js index 4137b13d4..316881d1d 100644 --- a/backend/utils/cors.js +++ b/backend/utils/cors.js @@ -6,7 +6,12 @@ class CORS { this.common = Common; } mount(app) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CORS', msg: 'Setting up CORS..' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'CORS', + msg: 'Setting up CORS..' + }); app.use((req, res, next) => { res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, filePath'); @@ -20,6 +25,5 @@ class CORS { this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CORS', msg: 'CORS Set' }); return app; } - ; } -export default new CORS; +export default new CORS(); diff --git a/backend/utils/csrf.js b/backend/utils/csrf.js index bca5ab20d..a9e4c790c 100644 --- a/backend/utils/csrf.js +++ b/backend/utils/csrf.js @@ -3,18 +3,22 @@ import { Logger } from './logger.js'; import { Common } from './common.js'; class CSRF { constructor() { - this.csrfProtection = csurf({ cookie: true }); + this.csrfProtection = csurf({ cookie: true, secure: Common.ssl ? true : false }); this.logger = Logger; this.common = Common; } mount(app) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CSRF', msg: 'Setting up CSRF..' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'CSRF', + msg: 'Setting up CSRF..' + }); if (process.env.NODE_ENV !== 'development') { app.use((req, res, next) => this.csrfProtection(req, res, next)); } this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CSRF', msg: 'CSRF Set' }); return app; } - ; } -export default new CSRF; +export default new CSRF(); diff --git a/backend/utils/database.js b/backend/utils/database.js index 79bfc622f..b773b9de3 100644 --- a/backend/utils/database.js +++ b/backend/utils/database.js @@ -20,7 +20,13 @@ export class DatabaseService { this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData(); } catch (err) { - this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err }); + this.logger.log({ + selectedNode: selectedNode, + level: 'ERROR', + fileName: 'Database', + msg: 'Database Load Error', + error: err + }); } } create(selectedNode, collectionName, newDocument) { @@ -54,7 +60,10 @@ export class DatabaseService { let foundDoc = null; if (this.nodeDatabase[selectedNode.index].data[collectionName]) { foundDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex((document) => document[documentFieldName] === documentFieldValue); - foundDoc = foundDocIdx > -1 ? JSON.parse(JSON.stringify(this.nodeDatabase[selectedNode.index].data[collectionName][foundDocIdx])) : null; + foundDoc = + foundDocIdx > -1 + ? JSON.parse(JSON.stringify(this.nodeDatabase[selectedNode.index].data[collectionName][foundDocIdx])) + : null; } if (foundDocIdx > -1 && foundDoc) { for (const docKey in updatedDocument) { @@ -131,23 +140,47 @@ export class DatabaseService { case CollectionsEnum.OFFERS: return validateOffer(documentToValidate); default: - return ({ isValid: false, error: 'Collection does not exist' }); + return { isValid: false, error: 'Collection does not exist' }; } } saveDatabase(nodeIndex) { try { - const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null; + const selNode = this.nodeDatabase[nodeIndex] && + this.nodeDatabase[nodeIndex].adapter && + this.nodeDatabase[nodeIndex].adapter.selNode + ? this.nodeDatabase[nodeIndex].adapter.selNode + : null; if (!this.nodeDatabase[nodeIndex]) { - this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error: Selected Node Setup Not Found.' }); + this.logger.log({ + selectedNode: selNode, + level: 'ERROR', + fileName: 'Database', + msg: 'Database Save Error: Selected Node Setup Not Found.' + }); throw new Error('Database Save Error: Selected Node Setup Not Found.'); } this.nodeDatabase[nodeIndex].adapter.saveData(this.nodeDatabase[nodeIndex].data); - this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'INFO', fileName: 'Database', msg: 'Database Saved' }); + this.logger.log({ + selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, + level: 'INFO', + fileName: 'Database', + msg: 'Database Saved' + }); return true; } catch (err) { - const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null; - this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error', error: err }); + const selNode = this.nodeDatabase[nodeIndex] && + this.nodeDatabase[nodeIndex].adapter && + this.nodeDatabase[nodeIndex].adapter.selNode + ? this.nodeDatabase[nodeIndex].adapter.selNode + : null; + this.logger.log({ + selectedNode: selNode, + level: 'ERROR', + fileName: 'Database', + msg: 'Database Save Error', + error: err + }); return new Error(err); } } diff --git a/backend/utils/logger.js b/backend/utils/logger.js index 6ab543c5a..76ae9ccd6 100644 --- a/backend/utils/logger.js +++ b/backend/utils/logger.js @@ -7,8 +7,21 @@ export class LoggerService { switch (msgJSON.level) { case 'ERROR': if (msgJSON.error) { - msgStr = msgStr + ': ' + ((msgJSON.error.error && msgJSON.error.error.message && typeof msgJSON.error.error.message === 'string') ? msgJSON.error.error.message : (typeof msgJSON.error === 'object' && msgJSON.error.message && typeof msgJSON.error.message === 'string') ? msgJSON.error.message : (typeof msgJSON.error === 'object' && msgJSON.error.stack && typeof msgJSON.error.stack === 'string') ? - msgJSON.error.stack : (typeof msgJSON.error === 'object') ? JSON.stringify(msgJSON.error) : (typeof msgJSON.error === 'string') ? msgJSON.error : '') + '\r\n'; + msgStr = + msgStr + + ': ' + + (msgJSON.error.error && msgJSON.error.error.message && typeof msgJSON.error.error.message === 'string' + ? msgJSON.error.error.message + : typeof msgJSON.error === 'object' && msgJSON.error.message && typeof msgJSON.error.message === 'string' + ? msgJSON.error.message + : typeof msgJSON.error === 'object' && msgJSON.error.stack && typeof msgJSON.error.stack === 'string' + ? msgJSON.error.stack + : typeof msgJSON.error === 'object' + ? JSON.stringify(msgJSON.error) + : typeof msgJSON.error === 'string' + ? msgJSON.error + : '') + + '\r\n'; } else { msgStr = msgStr + '.\r\n'; @@ -20,7 +33,10 @@ export class LoggerService { break; case 'WARN': msgStr = prepMsgData(msgJSON, msgStr); - if (!msgJSON.selectedNode || msgJSON.selectedNode.log_level === 'WARN' || msgJSON.selectedNode.log_level === 'INFO' || msgJSON.selectedNode.log_level === 'DEBUG') { + if (!msgJSON.selectedNode || + msgJSON.selectedNode.log_level === 'WARN' || + msgJSON.selectedNode.log_level === 'INFO' || + msgJSON.selectedNode.log_level === 'DEBUG') { console.warn(msgStr); if (msgJSON.selectedNode && msgJSON.selectedNode.log_file) { fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { }); @@ -65,10 +81,21 @@ export class LoggerService { }; } } -; const prepMsgData = (msgJSON, msgStr) => { if (msgJSON.data) { - msgStr = msgStr + ': ' + (typeof msgJSON.data === 'object' ? (msgJSON.data.message && typeof msgJSON.data.message === 'string') ? msgJSON.data.message : (msgJSON.data.stack && typeof msgJSON.data.stack === 'string') ? msgJSON.data.stack : JSON.stringify(msgJSON.data) : (typeof msgJSON.data === 'string') ? msgJSON.data : '') + '\r\n'; + msgStr = + msgStr + + ': ' + + (typeof msgJSON.data === 'object' + ? msgJSON.data.message && typeof msgJSON.data.message === 'string' + ? msgJSON.data.message + : msgJSON.data.stack && typeof msgJSON.data.stack === 'string' + ? msgJSON.data.stack + : JSON.stringify(msgJSON.data) + : typeof msgJSON.data === 'string' + ? msgJSON.data + : '') + + '\r\n'; } else { msgStr = msgStr + '.\r\n'; diff --git a/backend/utils/ssl.js b/backend/utils/ssl.js new file mode 100644 index 000000000..8fd50370d --- /dev/null +++ b/backend/utils/ssl.js @@ -0,0 +1,91 @@ +import * as fs from 'fs'; +import CertificateFactory from './certificateFactory.js'; +import { Logger } from './logger.js'; +const { assign } = Object; +class SSL { + constructor(options = true) { + this.logger = Logger; + this.key = undefined; + this.cert = undefined; + this.ca = undefined; + this.requestCert = false; + this.rejectUnauthorized = false; + const factoryOptions = {}; + const filePaths = {}; + if (typeof options === 'object') { + const { altIp, commonName, countryName, encryptionBits, stateName, localityName, organizationName, organizationalUnit, validForYears, requestCert, rejectUnauthorized } = options; + assign(factoryOptions, { + altIp, + commonName, + countryName, + encryptionBits, + stateName, + localityName, + organizationName, + organizationalUnit, + validForYears + }); + filePaths.key = options.key; + filePaths.cert = options.cert; + filePaths.ca = options.ca; + if (typeof requestCert === 'boolean') { + this.requestCert = requestCert; + } + if (typeof rejectUnauthorized === 'boolean') { + this.rejectUnauthorized = rejectUnauthorized; + } + } + try { + if (filePaths.key) { + this.key = fs.readFileSync(filePaths.key); + } + if (filePaths.cert) { + this.cert = fs.readFileSync(filePaths.cert); + } + if (filePaths.ca) { + this.ca = fs.readFileSync(filePaths.ca); + } + } + catch (e) { + this.logger.log({ + level: 'ERROR', + fileName: 'SSL', + msg: `Error reading certificate files - ${e.toString()}` + }); + throw e; + } + if (!this.key && !this.cert && !this.ca) { + try { + const selfSignedCertificateFactory = new CertificateFactory(factoryOptions); + assign(this, selfSignedCertificateFactory.getStaticBundle('buffer')); + } + catch (e) { + this.logger.log({ + level: 'ERROR', + fileName: 'SSL', + msg: `Error generating self-signed certificates - ${e.toString()}` + }); + throw e; + } + } + } + //--------------------------------------------------------------------------------------- + //Method: toObject + // + //Input: none + //Output: ssl server configuration object + // + //This method returns https server config options generated by this class + //--------------------------------------------------------------------------------------- + toObject() { + const { key, cert, ca, rejectUnauthorized, requestCert } = this; + return { + key, + cert, + ca, + rejectUnauthorized, + requestCert + }; + } +} +export default SSL; diff --git a/backend/utils/webSocketServer.js b/backend/utils/webSocketServer.js index 832c2aa47..74858e12d 100644 --- a/backend/utils/webSocketServer.js +++ b/backend/utils/webSocketServer.js @@ -28,8 +28,17 @@ export class WebSocketServer { } }, 1000 * 60 * 60); // Terminate broken connections every hour this.mount = (httpServer) => { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Connecting Websocket Server..' }); - this.webSocketServer = new WebSocket.Server({ noServer: true, path: this.common.baseHref + '/api/ws', verifyClient: (process.env.NODE_ENV === 'development') ? null : verifyWSUser }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'WebSocketServer', + msg: 'Connecting Websocket Server..' + }); + this.webSocketServer = new WebSocket.Server({ + noServer: true, + path: this.common.baseHref + '/api/ws', + verifyClient: process.env.NODE_ENV === 'development' ? null : verifyWSUser + }); httpServer.on('upgrade', (request, socket, head) => { if (request.headers['upgrade'] !== 'websocket') { socket.end('HTTP/1.1 400 Bad Request'); @@ -37,8 +46,15 @@ export class WebSocketServer { } const acceptKey = request.headers['sec-websocket-key']; const hash = this.generateAcceptValue(acceptKey); - const responseHeaders = ['HTTP/1.1 101 Web Socket Protocol Handshake', 'Upgrade: WebSocket', 'Connection: Upgrade', 'Sec-WebSocket-Accept: ' + hash]; - const protocols = !request.headers['sec-websocket-protocol'] ? [] : request.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); + const responseHeaders = [ + 'HTTP/1.1 101 Web Socket Protocol Handshake', + 'Upgrade: WebSocket', + 'Connection: Upgrade', + 'Sec-WebSocket-Accept: ' + hash + ]; + const protocols = !request.headers['sec-websocket-protocol'] + ? [] + : request.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); if (protocols.includes('json')) { responseHeaders.push('Sec-WebSocket-Protocol: json'); } @@ -46,25 +62,49 @@ export class WebSocketServer { }); this.webSocketServer.on('connection', this.mountEventsOnConnection); this.webSocketServer.on('close', () => clearInterval(this.pingInterval)); - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Websocket Server Connected' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'WebSocketServer', + msg: 'Websocket Server Connected' + }); }; this.upgradeCallback = (websocket, request) => { this.webSocketServer.emit('connection', websocket, request); }; this.mountEventsOnConnection = (websocket, request) => { - const protocols = !request.headers['sec-websocket-protocol'] ? [] : request.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); + const protocols = !request.headers['sec-websocket-protocol'] + ? [] + : request.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); const cookies = parse(request.headers.cookie); websocket.clientId = Date.now(); websocket.isAlive = true; websocket.sessionId = cookieParser.signedCookie(cookies['connect.sid'], this.common.secret_key); websocket.clientNodeIndex = +protocols[1]; - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Connected: ' + websocket.clientId + ', Total WS clients: ' + this.webSocketServer.clients.size }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'WebSocketServer', + msg: 'Connected: ' + websocket.clientId + ', Total WS clients: ' + this.webSocketServer.clients.size + }); websocket.on('error', this.sendErrorToAllLNClients); websocket.on('message', this.sendEventsToAllLNClients); - websocket.on('pong', () => { websocket.isAlive = true; }); + websocket.on('pong', () => { + websocket.isAlive = true; + }); websocket.on('close', (code, reason) => { this.updateLNWSClientDetails(websocket.sessionId, -1, websocket.clientNodeIndex); - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Disconnected due to ' + code + ' : ' + websocket.clientId + ', Total WS clients: ' + this.webSocketServer.clients.size }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'WebSocketServer', + msg: 'Disconnected due to ' + + code + + ' : ' + + websocket.clientId + + ', Total WS clients: ' + + this.webSocketServer.clients.size + }); }); }; this.updateLNWSClientDetails = (sessionId, currNodeIndex, prevNodeIndex) => { @@ -85,7 +125,12 @@ export class WebSocketServer { } else { const selectedNode = this.common.findNode(currNodeIndex); - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Invalid Node Selection. Previous and current node indices can not be less than zero.' }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'ERROR', + fileName: 'WebSocketServer', + msg: 'Invalid Node Selection. Previous and current node indices can not be less than zero.' + }); } }; this.disconnectFromNodeClient = (sessionId, prevNodeIndex) => { @@ -149,30 +194,53 @@ export class WebSocketServer { this.sendErrorToAllLNClients = (serverError, selectedNode) => { try { this.webSocketServer.clients.forEach((client) => { - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Broadcasting error to clients...: ' + serverError }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'ERROR', + fileName: 'WebSocketServer', + msg: 'Broadcasting error to clients...: ' + serverError + }); if (+client.clientNodeIndex === +selectedNode.index) { client.send(serverError); } }); } catch (err) { - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Error while broadcasting message: ' + JSON.stringify(err) }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'ERROR', + fileName: 'WebSocketServer', + msg: 'Error while broadcasting message: ' + JSON.stringify(err) + }); } }; this.sendEventsToAllLNClients = (newMessage, selectedNode) => { try { this.webSocketServer.clients.forEach((client) => { if (+client.clientNodeIndex === +selectedNode.index) { - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'DEBUG', fileName: 'WebSocketServer', msg: 'Broadcasting message to client...: ' + client.clientId }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'DEBUG', + fileName: 'WebSocketServer', + msg: 'Broadcasting message to client...: ' + client.clientId + }); client.send(newMessage); } }); } catch (err) { - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Error while broadcasting message: ' + JSON.stringify(err) }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'ERROR', + fileName: 'WebSocketServer', + msg: 'Error while broadcasting message: ' + JSON.stringify(err) + }); } }; - this.generateAcceptValue = (acceptKey) => crypto.createHash('sha1').update(acceptKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary').digest('base64'); + this.generateAcceptValue = (acceptKey) => crypto + .createHash('sha1') + .update(acceptKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary') + .digest('base64'); this.getClients = () => this.webSocketServer.clients; } } diff --git a/package-lock.json b/package-lock.json index 692f62d00..8e9e2e3dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "material-design-icons": "^3.0.1", "ng-qrcode": "^6.0.0", "ngx-perfect-scrollbar": "^10.0.1", + "node-forge": "^1.3.1", "otplib": "^12.0.1", "pdfmake": "^0.2.4", "request": "^2.88.2", @@ -78,6 +79,7 @@ "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "nodemon": "~2.0.6", + "prettier": "^2.6.2", "protractor": "~7.0.0", "stream-browserify": "^3.0.0", "ts-node": "~8.3.0" @@ -11262,7 +11264,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true, "engines": { "node": ">= 6.13.0" } @@ -12807,6 +12808,21 @@ "node": ">=4" } }, + "node_modules/prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -25109,8 +25125,7 @@ "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" }, "node-gyp": { "version": "8.4.1", @@ -26236,6 +26251,12 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, + "prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/package.json b/package.json index b0018a6e2..e0de7facf 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "buildfrontend": "ng build --configuration production", "buildbackend": "tsc --project tsconfig.json", "watchbackend": "tsc --project tsconfig.json --watch", + "formatserver": "node ./node_modules/.bin/prettier --single-quote --tab-width 2 --print-width 120 --trailing-comma none --write '{server/**/*.ts,rtl.js}'", "server": "set NODE_ENV=development&&nodemon ./rtl.js", "serverUbuntu": "NODE_ENV=development nodemon ./rtl.js", "testdev": "ng test --watch=true --code-coverage", @@ -52,6 +53,7 @@ "material-design-icons": "^3.0.1", "ng-qrcode": "^6.0.0", "ngx-perfect-scrollbar": "^10.0.1", + "node-forge": "^1.3.1", "otplib": "^12.0.1", "pdfmake": "^0.2.4", "request": "^2.88.2", @@ -89,6 +91,7 @@ "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "nodemon": "~2.0.6", + "prettier": "^2.6.2", "protractor": "~7.0.0", "stream-browserify": "^3.0.0", "ts-node": "~8.3.0" diff --git a/rtl.js b/rtl.js index 6fcdecacc..bf35a75d4 100644 --- a/rtl.js +++ b/rtl.js @@ -1,23 +1,34 @@ import http from 'http'; +import https from 'https'; import App from './backend/utils/app.js'; import { Logger } from './backend/utils/logger.js'; import { Common } from './backend/utils/common.js'; import { WSServer } from './backend/utils/webSocketServer.js'; +import SSL from './backend/utils/ssl.js'; const logger = Logger; const common = Common; const wsServer = WSServer; const app = new App(); +const appInstance = app.getApp(); +//Change 'global' to '127.0.0.1' for breaking change version release +const hostUndefined = common.host ? false : true; +const resolvedHost = hostUndefined ? 'global' : common.host; //'127.0.0.1' <-- this will become new default soon. swap with 'global' for breaking change +const resolvedPort = common.port; +const tcpModule = common.ssl ? https : http; +const fullUrl = `${common.ssl ? 'https' : 'http'}://${hostUndefined ? 'localhost' : resolvedHost}:${resolvedPort}`; const onError = (error) => { - if (error.syscall !== 'listen') { throw error; } + if (error.syscall !== 'listen') { + throw error; + } switch (error.code) { case 'EACCES': - logger.log({ level: 'ERROR', fileName: 'RTL', msg: 'http://' + (common.host ? common.host : 'localhost') + ':' + common.port + ' requires elevated privileges' }); + logger.log({ level: 'ERROR', fileName: 'RTL', msg: `${fullUrl} requires elevated privileges` }); process.exit(1); break; case 'EADDRINUSE': - logger.log({ level: 'ERROR', fileName: 'RTL', msg: 'http://' + (common.host ? common.host : 'localhost') + ':' + common.port + ' is already in use' }); + logger.log({ level: 'ERROR', fileName: 'RTL', msg: `${fullUrl} is already in use` }); process.exit(1); break; case 'ECONNREFUSED': @@ -35,18 +46,28 @@ const onError = (error) => { }; const onListening = () => { - logger.log({ level: 'INFO', fileName: 'RTL', msg: 'Server is up and running, please open the UI at http://' + (common.host ? common.host : 'localhost') + ':' + common.port }); + logger.log({ level: 'INFO', fileName: 'RTL', msg: `Server is up and running, please open the UI at ${fullUrl}` }); + //Remove the following IF block after sufficient version releases for users to update configs + if (hostUndefined) { + logger.log({ + level: 'WARN', + fileName: 'RTL', + msg: `DEPRECATION! Server defaulted to listening on external facing port ${resolvedPort} because "host" option is undefined. This feature will change soon. Please manually specify "host": "global" in RTL-Config.json if you want to continue exposing RTL to your entire LAN or gateway in future versions. The default value will change to '127.0.0.1' in the future so that RTL is only accessible on the RTL host machine by default` + }); + } }; -let server = http.createServer(app.getApp()); +let serverArgs = []; +if (common.ssl) { + let sslConfig = new SSL(common.ssl); + serverArgs.push(sslConfig.toObject()); +} +serverArgs.push(appInstance); +let server = tcpModule.createServer(...serverArgs); server.on('error', onError); server.on('listening', onListening); wsServer.mount(server); -if (common.host) { - server.listen(common.port, common.host); -} else { - server.listen(common.port); -} +server.listen(resolvedPort, resolvedHost === 'global' ? undefined : resolvedHost); diff --git a/server/controllers/cln/balance.ts b/server/controllers/cln/balance.ts index 786bfe04e..c6e55e834 100644 --- a/server/controllers/cln/balance.ts +++ b/server/controllers/cln/balance.ts @@ -8,16 +8,32 @@ const common: CommonService = Common; export const getBalance = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Getting Balance..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/getBalance'; - request(options).then((body) => { - if (!body.totalBalance) { body.totalBalance = 0; } - if (!body.confBalance) { body.confBalance = 0; } - if (!body.unconfBalance) { body.unconfBalance = 0; } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + if (!body.totalBalance) { + body.totalBalance = 0; + } + if (!body.confBalance) { + body.confBalance = 0; + } + if (!body.unconfBalance) { + body.unconfBalance = 0; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Balance', + msg: 'Balance Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/channels.ts b/server/controllers/cln/channels.ts index 4f4337428..8bb09a316 100644 --- a/server/controllers/cln/channels.ts +++ b/server/controllers/cln/channels.ts @@ -6,103 +6,217 @@ const logger: LoggerService = Logger; const common: CommonService = Common; export const listChannels = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Channels..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listChannels'; - request(options).then((body) => { - body.map((channel) => { - if (!channel.alias || channel.alias === '') { channel.alias = channel.id.substring(0, 20); } - const local = (channel.msatoshi_to_us) ? channel.msatoshi_to_us : 0; - const remote = (channel.msatoshi_to_them) ? channel.msatoshi_to_them : 0; - const total = channel.msatoshi_total ? channel.msatoshi_total : 0; - channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); - return channel; + request(options) + .then((body) => { + body.map((channel) => { + if (!channel.alias || channel.alias === '') { + channel.alias = channel.id.substring(0, 20); + } + const local = channel.msatoshi_to_us ? channel.msatoshi_to_us : 0; + const remote = channel.msatoshi_to_them ? channel.msatoshi_to_them : 0; + const total = channel.msatoshi_total ? channel.msatoshi_total : 0; + channel.balancedness = total === 0 ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); + return channel; + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channels List Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channels List Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; export const openChannel = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/openChannel'; options.body = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Options', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Open Channel Options', + data: options.body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Opened', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const setChannelFee = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Setting Channel Fee..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Setting Channel Fee..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/setChannelFee'; options.body = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updated Channel Policy', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Update Channel Policy Options', + data: options.body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Updated Channel Policy', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const closeChannel = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' }); req.setTimeout(60000 * 10); // timeout 10 mins options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } const unilateralTimeoutQuery = req.query.force ? '?unilateralTimeout=1' : ''; - options.url = req.session.selectedNode.ln_server_url + '/v1/channel/closeChannel/' + req.params.channelId + unilateralTimeoutQuery; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel', data: options.url }); - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed', data: body }); - res.status(204).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + options.url = + req.session.selectedNode.ln_server_url + + '/v1/channel/closeChannel/' + + req.params.channelId + + unilateralTimeoutQuery; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Closing Channel', + data: options.url }); + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Closed', + data: body + }); + res.status(204).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getLocalRemoteBalance = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Local & Remote Balances..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Local & Remote Balances..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/localremotebal'; - request(options).then((body) => { - if (!body.localBalance) { body.localBalance = 0; } - if (!body.remoteBalance) { body.remoteBalance = 0; } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Local Remote Balance Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Local Remote Balance Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + if (!body.localBalance) { + body.localBalance = 0; + } + if (!body.remoteBalance) { + body.remoteBalance = 0; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Local Remote Balance Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Local Remote Balance Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const listForwards = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channel List Forwards..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Channel List Forwards..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listForwards?status=' + req.query.status; - request.get(options).then((body) => { - if (body && body.length > 0) { body = common.sortDescByKey(body, 'received_time'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Forwarding History Received For Status' + req.query.status, data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Forwarding History Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .get(options) + .then((body) => { + if (body && body.length > 0) { + body = common.sortDescByKey(body, 'received_time'); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Forwarding History Received For Status' + req.query.status, + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Forwarding History Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/fees.ts b/server/controllers/cln/fees.ts index fd86e73a5..6ac96bfc2 100644 --- a/server/controllers/cln/fees.ts +++ b/server/controllers/cln/fees.ts @@ -8,14 +8,26 @@ const common: CommonService = Common; export const getFees = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/getFees'; - request(options).then((body) => { - if (!body.feeCollected) { body.feeCollected = 0; } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + if (!body.feeCollected) { + body.feeCollected = 0; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Fee Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/getInfo.ts b/server/controllers/cln/getInfo.ts index 496783aa5..423a46d0c 100644 --- a/server/controllers/cln/getInfo.ts +++ b/server/controllers/cln/getInfo.ts @@ -11,63 +11,100 @@ const clWsClient: CLWebSocketClient = CLWSClient; const databaseService: DatabaseService = Database; export const getInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting Core Lightning Node Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Getting Core Lightning Node Information..' + }); common.logEnvVariables(req); common.setOptions(req); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo'; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from Core Lightning server url ' + options.url }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Selected Node ' + req.session.selectedNode.ln_node + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Calling Info from Core Lightning server url ' + options.url + }); if (!options.headers || !options.headers.macaroon) { const errMsg = 'Core lightning get info failed due to bad or missing macaroon!'; - const err = common.handleError({ statusCode: 502, message: 'Bad Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 502, message: 'Bad Macaroon', error: errMsg }, + 'GetInfo', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { - return request(options).then((body) => { - const body_str = (!body) ? '' : JSON.stringify(body); - const search_idx = (!body) ? -1 : body_str.search('Not Found'); - if (!body || search_idx > -1 || body.error) { - if (body && !body.error) { body.error = 'Error From Server!'; } - const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - } else { - body.lnImplementation = 'Core Lightning'; - const chainObj = { chain: '', network: '' }; - if (body.network === 'testnet') { - chainObj.chain = 'Bitcoin'; - chainObj.network = 'Testnet'; - } else if (body.network === 'bitcoin') { - chainObj.chain = 'Bitcoin'; - chainObj.network = 'Mainnet'; - } else if (body.network === 'signet') { - chainObj.chain = 'Bitcoin'; - chainObj.network = 'Signet'; - } else if (body.network === 'litecoin') { - chainObj.chain = 'Litecoin'; - chainObj.network = 'Mainnet'; - } else if (body.network === 'litecoin-testnet') { - chainObj.chain = 'Litecoin'; - chainObj.network = 'Testnet'; - } - body.chains = [chainObj]; - body.uris = []; - if (body.address && body.address.length > 0) { - body.address.forEach((addr) => { - body.uris.push(body.id + '@' + addr.address + ':' + addr.port); + return request(options) + .then((body) => { + const body_str = !body ? '' : JSON.stringify(body); + const search_idx = !body ? -1 : body_str.search('Not Found'); + if (!body || search_idx > -1 || body.error) { + if (body && !body.error) { + body.error = 'Error From Server!'; + } + const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + } else { + body.lnImplementation = 'Core Lightning'; + const chainObj = { chain: '', network: '' }; + if (body.network === 'testnet') { + chainObj.chain = 'Bitcoin'; + chainObj.network = 'Testnet'; + } else if (body.network === 'bitcoin') { + chainObj.chain = 'Bitcoin'; + chainObj.network = 'Mainnet'; + } else if (body.network === 'signet') { + chainObj.chain = 'Bitcoin'; + chainObj.network = 'Signet'; + } else if (body.network === 'litecoin') { + chainObj.chain = 'Litecoin'; + chainObj.network = 'Mainnet'; + } else if (body.network === 'litecoin-testnet') { + chainObj.chain = 'Litecoin'; + chainObj.network = 'Testnet'; + } + body.chains = [chainObj]; + body.uris = []; + if (body.address && body.address.length > 0) { + body.address.forEach((addr) => { + body.uris.push(body.id + '@' + addr.address + ':' + addr.port); + }); + } + req.session.selectedNode.api_version = body.api_version || ''; + req.session.selectedNode.ln_version = body.version || ''; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: "Connecting to the Core Lightning's Websocket Server." }); + clWsClient.updateSelectedNode(req.session.selectedNode); + databaseService.loadDatabase(req.session.selectedNode); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Node Information Received', + data: body + }); + return res.status(200).json(body); } - req.session.selectedNode.api_version = body.api_version || ''; - req.session.selectedNode.ln_version = body.version || ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); - clWsClient.updateSelectedNode(req.session.selectedNode); - databaseService.loadDatabase(req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); - return res.status(200).json(body); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); } }; diff --git a/server/controllers/cln/invoices.ts b/server/controllers/cln/invoices.ts index f5a0aaf33..5bc35b00f 100644 --- a/server/controllers/cln/invoices.ts +++ b/server/controllers/cln/invoices.ts @@ -6,51 +6,110 @@ const logger: LoggerService = Logger; const common: CommonService = Common; export const deleteExpiredInvoice = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Deleting Expired Invoices..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Deleting Expired Invoices..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } const queryStr = req.query.maxExpiry ? '?maxexpiry=' + req.query.maxExpiry : ''; options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/delExpiredInvoice' + queryStr; - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoices Deleted', data: body }); - res.status(204).json({ status: 'Invoice Deleted Successfully' }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Invoice', 'Delete Invoice Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoices Deleted', + data: body + }); + res.status(204).json({ status: 'Invoice Deleted Successfully' }); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Invoice', 'Delete Invoice Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const listInvoices = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Getting Invoices..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Getting Invoices..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } const labelQuery = req.query.label ? '?label=' + req.query.label : ''; options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/listInvoices' + labelQuery; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url }); - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); - if (body.invoices && body.invoices.length > 0) { - body.invoices = common.sortDescByKey(body.invoices, 'expires_at'); - } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Invoice', + msg: 'Invoices List URL', + data: options.url }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Invoice', + msg: 'Invoices List Received', + data: body + }); + if (body.invoices && body.invoices.length > 0) { + body.invoices = common.sortDescByKey(body.invoices, 'expires_at'); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Sorted Invoices List Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const addInvoice = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Creating Invoice..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/genInvoice'; options.body = req.body; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Created', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Invoice', 'Add Invoice Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Created', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Invoice', 'Add Invoice Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/message.ts b/server/controllers/cln/message.ts index 109115c81..1d9e8db78 100644 --- a/server/controllers/cln/message.ts +++ b/server/controllers/cln/message.ts @@ -8,28 +8,55 @@ const common: CommonService = Common; export const signMessage = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Signing Message..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/utility/signMessage'; options.form = { message: req.body.message }; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Signed', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Message', 'Sign Message Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Message Signed', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Message', 'Sign Message Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const verifyMessage = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Verifying Message..' }); - options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/utility/checkMessage/' + req.body.message + '/' + req.body.signature; - request.get(options, (error, response, body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Message', 'Verify Message Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Verifying Message..' }); + options = common.getOptions(req); + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } + options.url = + req.session.selectedNode.ln_server_url + '/v1/utility/checkMessage/' + req.body.message + '/' + req.body.signature; + request + .get(options, (error, response, body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Message Verified', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Message', 'Verify Message Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/network.ts b/server/controllers/cln/network.ts index bb6be74d8..3d5fa6c8c 100644 --- a/server/controllers/cln/network.ts +++ b/server/controllers/cln/network.ts @@ -6,57 +6,108 @@ const logger: LoggerService = Logger; const common: CommonService = Common; export const getRoute = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Getting Network Routes..' }); - options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/network/getRoute/' + req.params.destPubkey + '/' + req.params.amount; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Routes Received', data: body }); - res.status(200).json({ routes: body }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Network', 'Query Routes Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Getting Network Routes..' }); + options = common.getOptions(req); + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } + options.url = + req.session.selectedNode.ln_server_url + '/v1/network/getRoute/' + req.params.destPubkey + '/' + req.params.amount; + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Network Routes Received', + data: body + }); + res.status(200).json({ routes: body }); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Network', 'Query Routes Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const listNode = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/network/listNode/' + req.params.id; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Node Lookup Finished', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const listChannel = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Channel Lookup..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/network/listChannel/' + req.params.channelShortId; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Channel Lookup Finished', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Network', 'Channel Lookup Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Channel Lookup Finished', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Network', 'Channel Lookup Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const feeRates = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Getting Network Fee Rates..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Getting Network Fee Rates..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/network/feeRates/' + req.params.feeRateStyle; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Fee Rates Received for ' + req.params.feeRateStyle, data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Network', 'Fee Rates Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Network Fee Rates Received for ' + req.params.feeRateStyle, + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Network', 'Fee Rates Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/offers.ts b/server/controllers/cln/offers.ts index 3d71dcfd0..d0a907e77 100644 --- a/server/controllers/cln/offers.ts +++ b/server/controllers/cln/offers.ts @@ -10,34 +10,64 @@ const common: CommonService = Common; const databaseService: DatabaseService = Database; export const listOfferBookmarks = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Bookmarks..' }); - databaseService.find(req.session.selectedNode, CollectionsEnum.OFFERS).then((offers: Offer[]) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmarks Received', data: offers }); - if (offers && offers.length > 0) { - offers = common.sortDescByKey(offers, 'lastUpdatedAt'); - } - res.status(200).json(offers); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Offers', 'Offer Bookmarks Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Getting Offer Bookmarks..' }); + databaseService + .find(req.session.selectedNode, CollectionsEnum.OFFERS) + .then((offers: Offer[]) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Bookmarks Received', + data: offers + }); + if (offers && offers.length > 0) { + offers = common.sortDescByKey(offers, 'lastUpdatedAt'); + } + res.status(200).json(offers); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Offers', 'Offer Bookmarks Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const deleteOfferBookmark = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' }); - databaseService.destroy(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmark Deleted', data: deleteRes }); - res.status(204).json(req.params.offerStr); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Offers', 'Offer Bookmark Delete Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Deleting Offer Bookmark..' }); + databaseService + .destroy(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr) + .then((deleteRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Bookmark Deleted', + data: deleteRes + }); + res.status(204).json(req.params.offerStr); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Offers', 'Offer Bookmark Delete Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const listOffers = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offers..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/offers/listoffers'; if (req.query.offer_id) { options.url = options.url + '?offer_id=' + req.query.offer_id; @@ -45,57 +75,115 @@ export const listOffers = (req, res, next) => { if (req.query.active_only) { options.url = options.url + '?active_only=' + req.query.active_only; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offers List URL', data: options.url }); - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offers List Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Offers', 'List Offers Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Offers', + msg: 'Offers List URL', + data: options.url }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offers List Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Offers', 'List Offers Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const createOffer = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Creating Offer..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/offers/offer'; options.body = req.body; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Created', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Offer', 'Create Offer Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Created', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Offer', 'Create Offer Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const fetchOfferInvoice = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Invoice..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Getting Offer Invoice..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/offers/fetchInvoice'; options.body = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offer Invoice Body', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Invoice Received', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Offers', 'Get Offer Invoice Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Offers', + msg: 'Offer Invoice Body', + data: options.body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Invoice Received', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Offers', 'Get Offer Invoice Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const disableOffer = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Disabling Offer..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/offers/disableOffer/' + req.params.offerID; - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Disabled', data: body }); - res.status(202).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Offers', 'Disable Offer Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Offers', + msg: 'Offer Disabled', + data: body + }); + res.status(202).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Offers', 'Disable Offer Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/onchain.ts b/server/controllers/cln/onchain.ts index 0242c788f..bbec97013 100644 --- a/server/controllers/cln/onchain.ts +++ b/server/controllers/cln/onchain.ts @@ -6,46 +6,95 @@ const logger: LoggerService = Logger; const common: CommonService = Common; export const getNewAddress = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Generating New Address..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Generating New Address..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/newaddr?addrType=' + req.query.type; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'OnChain', 'New Address Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'New Address Generated', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'OnChain', 'New Address Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const onChainWithdraw = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Withdrawing from On Chain..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Withdrawing from On Chain..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/withdraw'; options.body = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'OnChain', msg: 'OnChain Withdraw Options', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Withdraw Finished', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'OnChain', 'Withdraw Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'OnChain', + msg: 'OnChain Withdraw Options', + data: options.body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Withdraw Finished', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'OnChain', 'Withdraw Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getUTXOs = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Listing Funds..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/listFunds'; - request(options).then((body) => { - if (body.outputs) { body.outputs = common.sortDescByStrKey(body.outputs, 'status'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Funds List Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'OnChain', 'List Funds Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + if (body.outputs) { + body.outputs = common.sortDescByStrKey(body.outputs, 'status'); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Funds List Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'OnChain', 'List Funds Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/payments.ts b/server/controllers/cln/payments.ts index 8aafd9ef7..36a117e3a 100644 --- a/server/controllers/cln/payments.ts +++ b/server/controllers/cln/payments.ts @@ -11,7 +11,9 @@ const databaseService: DatabaseService = Database; function paymentReducer(accumulator, currentPayment) { const currPayHash = currentPayment.payment_hash; - if (!currentPayment.partid) { currentPayment.partid = 0; } + if (!currentPayment.partid) { + currentPayment.partid = 0; + } if (!accumulator[currPayHash]) { accumulator[currPayHash] = [currentPayment]; } else { @@ -26,14 +28,22 @@ function summaryReducer(accumulator, mpp) { accumulator.msatoshi_sent = accumulator.msatoshi_sent + mpp.msatoshi_sent; accumulator.status = mpp.status; } - if (mpp.bolt11) { accumulator.bolt11 = mpp.bolt11; } - if (mpp.bolt12) { accumulator.bolt12 = mpp.bolt12; } + if (mpp.bolt11) { + accumulator.bolt11 = mpp.bolt11; + } + if (mpp.bolt12) { + accumulator.bolt12 = mpp.bolt12; + } return accumulator; } function groupBy(payments) { const paymentsInGroups = payments.reduce(paymentReducer, {}); - const paymentsGrpArray = Object.keys(paymentsInGroups).map((key) => ((paymentsInGroups[key].length && paymentsInGroups[key].length > 1) ? common.sortDescByKey(paymentsInGroups[key], 'partid') : paymentsInGroups[key])); + const paymentsGrpArray = Object.keys(paymentsInGroups).map((key) => + paymentsInGroups[key].length && paymentsInGroups[key].length > 1 + ? common.sortDescByKey(paymentsInGroups[key], 'partid') + : paymentsInGroups[key] + ); return paymentsGrpArray.reduce((acc, curr) => { let temp: any = {}; if (curr.length && curr.length === 1) { @@ -43,14 +53,29 @@ function groupBy(payments) { temp.total_parts = 1; delete temp.partid; } else { - const paySummary = curr.reduce(summaryReducer, { msatoshi: 0, msatoshi_sent: 0, status: (curr[0] && curr[0].status) ? curr[0].status : 'failed' }); + const paySummary = curr.reduce(summaryReducer, { + msatoshi: 0, + msatoshi_sent: 0, + status: curr[0] && curr[0].status ? curr[0].status : 'failed' + }); temp = { - is_group: true, is_expanded: false, total_parts: (curr.length ? curr.length : 0), status: paySummary.status, payment_hash: curr[0].payment_hash, - destination: curr[0].destination, msatoshi: paySummary.msatoshi, msatoshi_sent: paySummary.msatoshi_sent, created_at: curr[0].created_at, + is_group: true, + is_expanded: false, + total_parts: curr.length ? curr.length : 0, + status: paySummary.status, + payment_hash: curr[0].payment_hash, + destination: curr[0].destination, + msatoshi: paySummary.msatoshi, + msatoshi_sent: paySummary.msatoshi_sent, + created_at: curr[0].created_at, mpps: curr }; - if (paySummary.bolt11) { temp.bolt11 = paySummary.bolt11; } - if (paySummary.bolt12) { temp.bolt12 = paySummary.bolt12; } + if (paySummary.bolt11) { + temp.bolt11 = paySummary.bolt11; + } + if (paySummary.bolt12) { + temp.bolt12 = paySummary.bolt12; + } } return acc.concat(temp); }, []); @@ -59,78 +84,159 @@ function groupBy(payments) { export const listPayments = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'List Payments..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments }); - if (body && body.payments && body.payments.length > 0) { - body.payments = common.sortDescByKey(body.payments, 'created_at'); - } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sorted Payments List Received', data: body.payments }); - res.status(200).json(groupBy(body.payments)); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Payment List Received', + data: body.payments + }); + if (body && body.payments && body.payments.length > 0) { + body.payments = common.sortDescByKey(body.payments, 'created_at'); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Sorted Payments List Received', + data: body.payments + }); + res.status(200).json(groupBy(body.payments)); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const decodePayment = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Decoding Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Decoding Payment..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/utility/decode/' + req.params.payReq; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Decoded', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const postPayment = (req, res, next) => { options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } if (req.body.paymentType === 'KEYSEND') { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Keysend Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Keysend Payment..' + }); options.url = req.session.selectedNode.ln_server_url + '/v1/pay/keysend'; options.body = req.body; } else { if (req.body.paymentType === 'OFFER') { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Offer Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Sending Offer Payment..' + }); options.body = { invoice: req.body.invoice }; } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Invoice Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Sending Invoice Payment..' + }); options.body = req.body; } options.url = req.session.selectedNode.ln_server_url + '/v1/pay'; } - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent', data: body }); - if (req.body.paymentType === 'OFFER') { - if (req.body.saveToDB && req.body.bolt12) { - const offerToUpdate: Offer = { bolt12: req.body.bolt12, amountmSat: (req.body.zeroAmtOffer ? 0 : req.body.amount), title: req.body.title, lastUpdatedAt: new Date(Date.now()).getTime() }; - if (req.body.vendor) { offerToUpdate['vendor'] = req.body.vendor; } - if (req.body.description) { offerToUpdate['description'] = req.body.description; } - return databaseService.update(req.session.selectedNode, CollectionsEnum.OFFERS, offerToUpdate, CollectionFieldsEnum.BOLT12, req.body.bolt12).then((updatedOffer) => { - logger.log({ level: 'DEBUG', fileName: 'Offer', msg: 'Offer Updated', data: updatedOffer }); - return res.status(201).json({ paymentResponse: body, saveToDBResponse: updatedOffer }); - }).catch((errDB) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Payments', msg: 'Offer DB update error', error: errDB }); - return res.status(201).json({ paymentResponse: body, saveToDBError: errDB }); - }); - } else { + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Sent', + data: body + }); + if (req.body.paymentType === 'OFFER') { + if (req.body.saveToDB && req.body.bolt12) { + const offerToUpdate: Offer = { + bolt12: req.body.bolt12, + amountmSat: req.body.zeroAmtOffer ? 0 : req.body.amount, + title: req.body.title, + lastUpdatedAt: new Date(Date.now()).getTime() + }; + if (req.body.vendor) { + offerToUpdate['vendor'] = req.body.vendor; + } + if (req.body.description) { + offerToUpdate['description'] = req.body.description; + } + return databaseService + .update( + req.session.selectedNode, + CollectionsEnum.OFFERS, + offerToUpdate, + CollectionFieldsEnum.BOLT12, + req.body.bolt12 + ) + .then((updatedOffer) => { + logger.log({ level: 'DEBUG', fileName: 'Offer', msg: 'Offer Updated', data: updatedOffer }); + return res.status(201).json({ paymentResponse: body, saveToDBResponse: updatedOffer }); + }) + .catch((errDB) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Payments', + msg: 'Offer DB update error', + error: errDB + }); + return res.status(201).json({ paymentResponse: body, saveToDBError: errDB }); + }); + } else { + return res.status(201).json({ paymentResponse: body, saveToDBResponse: 'NA' }); + } + } + if (req.body.paymentType === 'INVOICE') { return res.status(201).json({ paymentResponse: body, saveToDBResponse: 'NA' }); } - } - if (req.body.paymentType === 'INVOICE') { - return res.status(201).json({ paymentResponse: body, saveToDBResponse: 'NA' }); - } - if (req.body.paymentType === 'KEYSEND') { - return res.status(201).json(body); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + if (req.body.paymentType === 'KEYSEND') { + return res.status(201).json(body); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/peers.ts b/server/controllers/cln/peers.ts index 5229a6ec4..3de4ab18c 100644 --- a/server/controllers/cln/peers.ts +++ b/server/controllers/cln/peers.ts @@ -8,57 +8,98 @@ const common: CommonService = Common; export const getPeers = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'List Peers..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers'; - request(options).then((body) => { - body.forEach((peer) => { - if (!peer.alias || peer.alias === '') { - peer.alias = peer.id.substring(0, 20); - } + request(options) + .then((body) => { + body.forEach((peer) => { + if (!peer.alias || peer.alias === '') { + peer.alias = peer.id.substring(0, 20); + } + }); + const peers = body ? common.sortDescByStrKey(body, 'alias') : []; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peers with Alias Received', + data: peers + }); + res.status(200).json(peers); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - const peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers with Alias Received', data: peers }); - res.status(200).json(peers); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; export const postPeer = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Connecting Peer..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/peer/connect'; options.body = req.body; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body }); - options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers'; - request(options).then((body) => { - let peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; - peers = common.newestOnTop(peers, 'id', req.body.id); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers }); - res.status(201).json(peers); - }).catch((errRes) => { + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peer Connected', + data: body + }); + options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers'; + request(options) + .then((body) => { + let peers = body ? common.sortDescByStrKey(body, 'alias') : []; + peers = common.newestOnTop(peers, 'id', req.body.id); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peers List after Connect Received', + data: peers + }); + res.status(201).json(peers); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; export const deletePeer = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Disconnecting Peer..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/peer/disconnect/' + req.params.peerId + '?force=' + req.query.force; - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected', data: body }); - res.status(204).json({}); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Peers', 'Detach Peer Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } + options.url = + req.session.selectedNode.ln_server_url + '/v1/peer/disconnect/' + req.params.peerId + '?force=' + req.query.force; + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peer Disconnected', + data: body + }); + res.status(204).json({}); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Peers', 'Detach Peer Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/cln/webSocketClient.ts b/server/controllers/cln/webSocketClient.ts index cde6dd8f3..8ad1d41b6 100644 --- a/server/controllers/cln/webSocketClient.ts +++ b/server/controllers/cln/webSocketClient.ts @@ -8,11 +8,10 @@ import { WSServer } from '../../utils/webSocketServer.js'; import { CommonSelectedNode } from '../../models/config.model.js'; export class CLWebSocketClient { - public logger: LoggerService = Logger; public common: CommonService = Common; public wsServer = WSServer; - public webSocketClients: Array<{ selectedNode: CommonSelectedNode, reConnect: boolean, webSocketClient: any }> = []; + public webSocketClients: Array<{ selectedNode: CommonSelectedNode; reConnect: boolean; webSocketClient: any }> = []; public reconnectTimeOut = null; public waitTime = 0.5; @@ -26,11 +25,18 @@ export class CLWebSocketClient { } public reconnet = (clWsClt) => { - if (this.reconnectTimeOut) { return; } - this.waitTime = (this.waitTime >= 64) ? 64 : (this.waitTime * 2); + if (this.reconnectTimeOut) { + return; + } + this.waitTime = this.waitTime >= 64 ? 64 : this.waitTime * 2; this.reconnectTimeOut = setTimeout(() => { if (clWsClt.selectedNode) { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Reconnecting to the Core Lightning\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Reconnecting to the Core Lightning's Websocket Server.." + }); this.connect(clWsClt.selectedNode); } this.reconnectTimeOut = null; @@ -47,7 +53,10 @@ export class CLWebSocketClient { this.webSocketClients.push(newWebSocketClient); } } else { - if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) { + if ( + (!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && + selectedNode.ln_server_url + ) { clientExists.reConnect = true; this.connectWithClient(clientExists); } @@ -58,39 +67,81 @@ export class CLWebSocketClient { }; public connectWithClient = (clWsClt) => { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connecting to the Core Lightning\'s Websocket Server..' }); - const WS_LINK = (clWsClt.selectedNode.ln_server_url).replace(/^http/, 'ws') + '/v1/ws'; - const mcrnHexEncoded = Buffer.from(fs.readFileSync(join(clWsClt.selectedNode.macaroon_path, 'access.macaroon'))).toString('hex'); + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Connecting to the Core Lightning's Websocket Server.." + }); + const WS_LINK = clWsClt.selectedNode.ln_server_url.replace(/^http/, 'ws') + '/v1/ws'; + const mcrnHexEncoded = Buffer.from( + fs.readFileSync(join(clWsClt.selectedNode.macaroon_path, 'access.macaroon')) + ).toString('hex'); clWsClt.webSocketClient = new WebSocket(WS_LINK, [mcrnHexEncoded, 'hex'], { rejectUnauthorized: false }); clWsClt.webSocketClient.onopen = () => { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connected to the Core Lightning\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Connected to the Core Lightning's Websocket Server.." + }); this.waitTime = 0.5; }; clWsClt.webSocketClient.onclose = (e) => { if (clWsClt && clWsClt.selectedNode && clWsClt.selectedNode.ln_implementation === 'CLN') { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Web socket disconnected, will reconnect again...' }); + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: 'Web socket disconnected, will reconnect again...' + }); clWsClt.webSocketClient.close(); - if (clWsClt.reConnect) { this.reconnet(clWsClt); } + if (clWsClt.reConnect) { + this.reconnet(clWsClt); + } } }; clWsClt.webSocketClient.onmessage = (msg) => { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'DEBUG', fileName: 'CLWebSocket', msg: 'Received message from the server..', data: msg.data }); - msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data; + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'DEBUG', + fileName: 'CLWebSocket', + msg: 'Received message from the server..', + data: msg.data + }); + msg = typeof msg.data === 'string' ? JSON.parse(msg.data) : msg.data; msg['source'] = 'CLN'; const msgStr = JSON.stringify(msg); this.wsServer.sendEventsToAllLNClients(msgStr, clWsClt.selectedNode); }; clWsClt.webSocketClient.onerror = (err) => { - if (clWsClt.selectedNode.api_version === '' || !clWsClt.selectedNode.api_version || this.common.isVersionCompatible(clWsClt.selectedNode.api_version, '0.6.0')) { - this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'ERROR', fileName: 'CLWebSocket', msg: 'Web socket error', error: err }); - const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }')); + if ( + clWsClt.selectedNode.api_version === '' || + !clWsClt.selectedNode.api_version || + this.common.isVersionCompatible(clWsClt.selectedNode.api_version, '0.6.0') + ) { + this.logger.log({ + selectedNode: clWsClt.selectedNode, + level: 'ERROR', + fileName: 'CLWebSocket', + msg: 'Web socket error', + error: err + }); + const errStr = + typeof err === 'object' && err.message + ? JSON.stringify({ error: err.message }) + : typeof err === 'object' + ? JSON.stringify({ error: err }) + : '{ "error": ' + err + ' }'; this.wsServer.sendErrorToAllLNClients(errStr, clWsClt.selectedNode); clWsClt.webSocketClient.close(); - if (clWsClt.reConnect) { this.reconnet(clWsClt); } + if (clWsClt.reConnect) { + this.reconnet(clWsClt); + } } else { clWsClt.reConnect = false; } @@ -100,7 +151,12 @@ export class CLWebSocketClient { public disconnect = (selectedNode: CommonSelectedNode) => { const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index); if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) { - this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the Core Lightning\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clientExists.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Disconnecting from the Core Lightning's Websocket Server.." + }); clientExists.reConnect = false; clientExists.webSocketClient.close(); const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index); @@ -111,11 +167,12 @@ export class CLWebSocketClient { public updateSelectedNode = (newSelectedNode: CommonSelectedNode) => { const clientIdx = this.webSocketClients.findIndex((wsc) => +wsc.selectedNode.index === +newSelectedNode.index); let newClient = this.webSocketClients[clientIdx]; - if (!newClient) { newClient = { selectedNode: null, reConnect: true, webSocketClient: null }; } + if (!newClient) { + newClient = { selectedNode: null, reConnect: true, webSocketClient: null }; + } newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode)); this.webSocketClients[clientIdx] = newClient; - } - + }; } export const CLWSClient = new CLWebSocketClient(); diff --git a/server/controllers/eclair/channels.ts b/server/controllers/eclair/channels.ts index ef63ff51f..f3a828293 100644 --- a/server/controllers/eclair/channels.ts +++ b/server/controllers/eclair/channels.ts @@ -15,61 +15,142 @@ export const simplifyAllChannels = (selNode: CommonSelectedNode, channels) => { nodeId: channel.nodeId ? channel.nodeId : '', channelId: channel.channelId ? channel.channelId : '', state: channel.state ? channel.state : '', - channelFlags: channel.data && channel.data.commitments && channel.data.commitments.channelFlags ? channel.data.commitments.channelFlags : 0, - toLocal: (channel.data.commitments.localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.localCommit.spec.toLocal / 1000) : 0, - toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) : 0, + channelFlags: + channel.data && channel.data.commitments && channel.data.commitments.channelFlags + ? channel.data.commitments.channelFlags + : 0, + toLocal: channel.data.commitments.localCommit.spec.toLocal + ? Math.round(+channel.data.commitments.localCommit.spec.toLocal / 1000) + : 0, + toRemote: channel.data.commitments.localCommit.spec.toRemote + ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) + : 0, shortChannelId: channel.data && channel.data.shortChannelId ? channel.data.shortChannelId : '', - isFunder: channel.data && channel.data.commitments && channel.data.commitments.localParams && channel.data.commitments.localParams.isFunder ? channel.data.commitments.localParams.isFunder : false, + isFunder: + channel.data && + channel.data.commitments && + channel.data.commitments.localParams && + channel.data.commitments.localParams.isFunder + ? channel.data.commitments.localParams.isFunder + : false, buried: channel.data && channel.data.buried ? channel.data.buried : false, - feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0, - feeRatePerKw: (channel.data.commitments.localCommit.spec.feeratePerKw) ? channel.data.commitments.localCommit.spec.feeratePerKw : 0, - feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0, + feeBaseMsat: + channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat + ? channel.data.channelUpdate.feeBaseMsat + : 0, + feeRatePerKw: channel.data.commitments.localCommit.spec.feeratePerKw + ? channel.data.commitments.localCommit.spec.feeratePerKw + : 0, + feeProportionalMillionths: + channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths + ? channel.data.channelUpdate.feeProportionalMillionths + : 0, alias: '' }); }); channelNodeIds = channelNodeIds.substring(1); options.url = selNode.ln_server_url + '/nodes'; options.form = { nodeIds: channelNodeIds }; - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Node Ids to find alias', data: channelNodeIds }); - return request.post(options).then((nodes) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Filtered Nodes Received', data: nodes }); - let foundPeer = null; - simplifiedChannels.map((channel) => { - foundPeer = nodes.find((channelWithAlias) => channel.nodeId === channelWithAlias.nodeId); - channel.alias = foundPeer ? foundPeer.alias : channel.nodeId.substring(0, 20); - return channel; - }); - return simplifiedChannels; - }).catch((err) => simplifiedChannels); + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Node Ids to find alias', + data: channelNodeIds + }); + return request + .post(options) + .then((nodes) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Filtered Nodes Received', + data: nodes + }); + let foundPeer = null; + simplifiedChannels.map((channel) => { + foundPeer = nodes.find((channelWithAlias) => channel.nodeId === channelWithAlias.nodeId); + channel.alias = foundPeer ? foundPeer.alias : channel.nodeId.substring(0, 20); + return channel; + }); + return simplifiedChannels; + }) + .catch((err) => simplifiedChannels); }; export const getChannels = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'List Channels..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/channels'; options.form = {}; if (req.query && req.query.nodeId) { options.form = req.query; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels Node Id', data: options.form }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Channels Node Id', + data: options.form + }); } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Options', data: options }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Options', + data: options + }); if (common.read_dummy_data) { - common.getDummyData('Channels', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(data); }); + common.getDummyData('Channels', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(data); + }); } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels List Received', data: body }); - if (body && body.length) { - return simplifyAllChannels(req.session.selectedNode, body).then((simplifiedChannels) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Simplified Channels with Alias Received', data: simplifiedChannels }); - res.status(200).json(simplifiedChannels); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Channels List Received', + data: body }); - } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Empty Channels List Received' }); - res.status(200).json({ activeChannels: [], pendingChannels: [], inactiveChannels: [], lightningBalances: { localBalance: 0, remoteBalance: 0 }, channelStatus: { active: { channels: 0, capacity: 0 }, inactive: { channels: 0, capacity: 0 }, pending: { channels: 0, capacity: 0 } } }); - } - }). - catch((errRes) => { + if (body && body.length) { + return simplifyAllChannels(req.session.selectedNode, body).then((simplifiedChannels) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Simplified Channels with Alias Received', + data: simplifiedChannels + }); + res.status(200).json(simplifiedChannels); + }); + } else { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Empty Channels List Received' + }); + res.status(200).json({ + activeChannels: [], + pendingChannels: [], + inactiveChannels: [], + lightningBalances: { localBalance: 0, remoteBalance: 0 }, + channelStatus: { + active: { channels: 0, capacity: 0 }, + inactive: { channels: 0, capacity: 0 }, + pending: { channels: 0, capacity: 0 } + } + }); + } + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -77,71 +158,158 @@ export const getChannels = (req, res, next) => { }; export const getChannelStats = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channel States..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Channel States..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/channelstats'; options.form = {}; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel States Received', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Get Channel Stats Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel States Received', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Get Channel Stats Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const openChannel = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/open'; options.form = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Params', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Open Channel Params', + data: options.form }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Opened', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const updateChannelRelayFee = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updating Channel Relay Fee..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Updating Channel Relay Fee..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/updaterelayfee'; options.form = req.query; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Relay Fee Params', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Relay Fee Updated', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Update Relay Fee Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Update Relay Fee Params', + data: options.form }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Relay Fee Updated', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Update Relay Fee Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const closeChannel = (req, res, next) => { options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } if (req.query.force !== 'true') { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Closing Channel..' + }); options.url = req.session.selectedNode.ln_server_url + '/close'; } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Force Closing Channel..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Force Closing Channel..' + }); options.url = req.session.selectedNode.ln_server_url + '/forceclose'; } options.form = { channelId: req.query.channelId }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Close URL', data: options.url }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Close Params', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed', data: body }); - res.status(204).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Close URL', + data: options.url + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Close Params', + data: options.form }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Closed', + data: body + }); + res.status(204).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; - diff --git a/server/controllers/eclair/fees.ts b/server/controllers/eclair/fees.ts index f4cecf53f..60589737c 100644 --- a/server/controllers/eclair/fees.ts +++ b/server/controllers/eclair/fees.ts @@ -15,11 +15,11 @@ export const arrangeFees = (selNode: CommonSelectedNode, body, current_time) => fee = Math.round((relayedEle.amountIn - relayedEle.amountOut) / 1000); if (relayedEle.timestamp) { if (relayedEle.timestamp.unix) { - if ((relayedEle.timestamp.unix * 1000) >= day_start_time) { + if (relayedEle.timestamp.unix * 1000 >= day_start_time) { fees.daily_fee = fees.daily_fee + fee; fees.daily_txs = fees.daily_txs + 1; } - if ((relayedEle.timestamp.unix * 1000) >= week_start_time) { + if (relayedEle.timestamp.unix * 1000 >= week_start_time) { fees.weekly_fee = fees.weekly_fee + fee; fees.weekly_txs = fees.weekly_txs + 1; } @@ -48,11 +48,19 @@ export const arrangePayments = (selNode: CommonSelectedNode, body) => { relayed: body && body.relayed ? body.relayed : [] }; payments.sent.forEach((sentEle) => { - if (sentEle.recipientAmount) { sentEle.recipientAmount = Math.round(sentEle.recipientAmount / 1000); } + if (sentEle.recipientAmount) { + sentEle.recipientAmount = Math.round(sentEle.recipientAmount / 1000); + } sentEle.parts.forEach((part) => { - if (part.amount) { part.amount = Math.round(part.amount / 1000); } - if (part.feesPaid) { part.feesPaid = Math.round(part.feesPaid / 1000); } - if (part.timestamp.unix) { part.timestamp = part.timestamp.unix * 1000; } + if (part.amount) { + part.amount = Math.round(part.amount / 1000); + } + if (part.feesPaid) { + part.feesPaid = Math.round(part.feesPaid / 1000); + } + if (part.timestamp.unix) { + part.timestamp = part.timestamp.unix * 1000; + } }); if (sentEle.parts && sentEle.parts.length > 0) { sentEle.firstPartTimestamp = sentEle.parts[0].timestamp; @@ -60,65 +68,114 @@ export const arrangePayments = (selNode: CommonSelectedNode, body) => { }); payments.received.forEach((receivedEle) => { receivedEle.parts.forEach((part) => { - if (part.amount) { part.amount = Math.round(part.amount / 1000); } - if (part.timestamp.unix) { part.timestamp = part.timestamp.unix * 1000; } + if (part.amount) { + part.amount = Math.round(part.amount / 1000); + } + if (part.timestamp.unix) { + part.timestamp = part.timestamp.unix * 1000; + } }); if (receivedEle.parts && receivedEle.parts.length > 0) { receivedEle.firstPartTimestamp = receivedEle.parts[0].timestamp; } }); payments.relayed.forEach((relayedEle) => { - if (relayedEle.timestamp.unix) { relayedEle.timestamp = relayedEle.timestamp.unix * 1000; } - if (relayedEle.amountIn) { relayedEle.amountIn = Math.round(relayedEle.amountIn / 1000); } - if (relayedEle.amountOut) { relayedEle.amountOut = Math.round(relayedEle.amountOut / 1000); } + if (relayedEle.timestamp.unix) { + relayedEle.timestamp = relayedEle.timestamp.unix * 1000; + } + if (relayedEle.amountIn) { + relayedEle.amountIn = Math.round(relayedEle.amountIn / 1000); + } + if (relayedEle.amountOut) { + relayedEle.amountOut = Math.round(relayedEle.amountOut / 1000); + } }); payments.sent = common.sortDescByKey(payments.sent, 'firstPartTimestamp'); payments.received = common.sortDescByKey(payments.received, 'firstPartTimestamp'); payments.relayed = common.sortDescByKey(payments.relayed, 'timestamp'); - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Payments Received', data: payments }); + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Arranged Payments Received', + data: payments + }); return payments; }; export const getFees = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/audit'; const today = new Date(Date.now()); - const tillToday = (Math.round(today.getTime() / 1000)).toString(); - const fromLastMonth = (Math.round(new Date(today.getFullYear(), today.getMonth() - 1, today.getDate() + 1, 0, 0, 0).getTime() / 1000)).toString(); + const tillToday = Math.round(today.getTime() / 1000).toString(); + const fromLastMonth = Math.round( + new Date(today.getFullYear(), today.getMonth() - 1, today.getDate() + 1, 0, 0, 0).getTime() / 1000 + ).toString(); options.form = { from: fromLastMonth, to: tillToday }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Fee Audit Options', data: options.form }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Fee Audit Options', + data: options.form + }); if (common.read_dummy_data) { - common.getDummyData('Fees', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangeFees(req.session.selectedNode, data, Math.round((new Date().getTime())))); }); - } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body }); - res.status(200).json(arrangeFees(req.session.selectedNode, body, Math.round((new Date().getTime())))); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + common.getDummyData('Fees', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(arrangeFees(req.session.selectedNode, data, Math.round(new Date().getTime()))); }); + } else { + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Fee Received', + data: body + }); + res.status(200).json(arrangeFees(req.session.selectedNode, body, Math.round(new Date().getTime()))); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); } }; export const getPayments = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Payments..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/audit'; options.form = null; if (common.read_dummy_data) { - common.getDummyData('Payments', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangePayments(req.session.selectedNode, data)); }); + common.getDummyData('Payments', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(arrangePayments(req.session.selectedNode, data)); + }); } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Payments Received', data: body }); - res.status(200).json(arrangePayments(req.session.selectedNode, body)); - }). - catch((errRes) => { + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Payments Received', + data: body + }); + res.status(200).json(arrangePayments(req.session.selectedNode, body)); + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Fees', 'Get Payments Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/server/controllers/eclair/getInfo.ts b/server/controllers/eclair/getInfo.ts index 1707d178b..3af82c090 100644 --- a/server/controllers/eclair/getInfo.ts +++ b/server/controllers/eclair/getInfo.ts @@ -11,15 +11,32 @@ const eclWsClient: ECLWebSocketClient = ECLWSClient; const databaseService: DatabaseService = Database; export const getInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting Eclair Node Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Getting Eclair Node Information..' + }); common.logEnvVariables(req); common.setOptions(req); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/getinfo'; options.form = {}; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from Eclair server url ' + options.url }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Selected Node ' + req.session.selectedNode.ln_node + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Calling Info from Eclair server url ' + options.url + }); if (common.read_dummy_data) { common.getDummyData('GetInfo', req.session.selectedNode.ln_implementation).then((data: any) => { data.lnImplementation = 'Eclair'; @@ -28,21 +45,40 @@ export const getInfo = (req, res, next) => { } else { if (!options.headers || !options.headers.authorization) { const errMsg = 'Eclair Get info failed due to missing or wrong password!'; - const err = common.handleError({ statusCode: 502, message: 'Missing or Wrong Password', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 502, message: 'Missing or Wrong Password', error: errMsg }, + 'GetInfo', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { - return request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Eclair\'s Websocket Server.' }); - body.lnImplementation = 'Eclair'; - req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; - eclWsClient.updateSelectedNode(req.session.selectedNode); - databaseService.loadDatabase(req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); - return res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + return request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: "Connecting to the Eclair's Websocket Server." + }); + body.lnImplementation = 'Eclair'; + req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; + eclWsClient.updateSelectedNode(req.session.selectedNode); + databaseService.loadDatabase(req.session.selectedNode); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Node Information Received', + data: body + }); + return res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); } } }; diff --git a/server/controllers/eclair/invoices.ts b/server/controllers/eclair/invoices.ts index 90b587234..cfc763cab 100644 --- a/server/controllers/eclair/invoices.ts +++ b/server/controllers/eclair/invoices.ts @@ -8,23 +8,27 @@ let pendingInvoices = []; export const getReceivedPaymentInfo = (lnServerUrl, invoice) => { let idx = -1; - invoice.expiresAt = (!invoice.expiry) ? null : (+invoice.timestamp + +invoice.expiry); - if (invoice.amount) { invoice.amount = Math.round(invoice.amount / 1000); } + invoice.expiresAt = !invoice.expiry ? null : +invoice.timestamp + +invoice.expiry; + if (invoice.amount) { + invoice.amount = Math.round(invoice.amount / 1000); + } idx = pendingInvoices.findIndex((pendingInvoice) => invoice.serialized === pendingInvoice.serialized); if (idx < 0) { options.url = lnServerUrl + '/getreceivedinfo'; options.form = { paymentHash: invoice.paymentHash }; - return request(options).then((response) => { - invoice.status = response.status.type; - if (response.status && response.status.type === 'received') { - invoice.amountSettled = response.status.amount ? Math.round(response.status.amount / 1000) : 0; - invoice.receivedAt = response.status.receivedAt ? Math.round(response.status.receivedAt / 1000) : 0; - } - return invoice; - }).catch((err) => { - invoice.status = 'unknown'; - return invoice; - }); + return request(options) + .then((response) => { + invoice.status = response.status.type; + if (response.status && response.status.type === 'received') { + invoice.amountSettled = response.status.amount ? Math.round(response.status.amount / 1000) : 0; + invoice.receivedAt = response.status.receivedAt ? Math.round(response.status.receivedAt / 1000) : 0; + } + return invoice; + }) + .catch((err) => { + invoice.status = 'unknown'; + return invoice; + }); } else { pendingInvoices.splice(idx, 1); invoice.status = 'unpaid'; @@ -35,26 +39,44 @@ export const getReceivedPaymentInfo = (lnServerUrl, invoice) => { export const getInvoice = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Invoice..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/getinvoice'; options.form = { paymentHash: req.params.paymentHash }; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Found', data: body }); - const current_time = (Math.round(new Date(Date.now()).getTime() / 1000)); - body.amount = body.amount ? body.amount / 1000 : 0; - body.expiresAt = body.expiresAt ? body.expiresAt : (body.timestamp + body.expiry); - body.status = body.status ? body.status : (+body.expiresAt < current_time ? 'expired' : 'unknown'); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Invoices', 'Get Invoice Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Found', + data: body + }); + const current_time = Math.round(new Date(Date.now()).getTime() / 1000); + body.amount = body.amount ? body.amount / 1000 : 0; + body.expiresAt = body.expiresAt ? body.expiresAt : body.timestamp + body.expiry; + body.status = body.status ? body.status : +body.expiresAt < current_time ? 'expired' : 'unknown'; + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Invoices', 'Get Invoice Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const listInvoices = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Getting List Invoices..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Getting List Invoices..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.form = {}; const options1 = JSON.parse(JSON.stringify(options)); options1.url = req.session.selectedNode.ln_server_url + '/listinvoices'; @@ -64,37 +86,57 @@ export const listInvoices = (req, res, next) => { options2.form = {}; if (common.read_dummy_data) { return common.getDummyData('Invoices', req.session.selectedNode.ln_implementation).then((body) => { - const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0]; - pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1]; - return Promise.all(invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))). - then((values) => { - body = common.sortDescByKey(invoices, 'expiresAt'); - return res.status(200).json(invoices); - }); + const invoices = !body[0] || body[0].length <= 0 ? [] : body[0]; + pendingInvoices = !body[1] || body[1].length <= 0 ? [] : body[1]; + return Promise.all( + invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice)) + ).then((values) => { + body = common.sortDescByKey(invoices, 'expiresAt'); + return res.status(200).json(invoices); + }); }); } else { - return Promise.all([request(options1), request(options2)]). - then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); - const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0]; - pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1]; + return Promise.all([request(options1), request(options2)]) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Invoice', + msg: 'Invoices List Received', + data: body + }); + const invoices = !body[0] || body[0].length <= 0 ? [] : body[0]; + pendingInvoices = !body[1] || body[1].length <= 0 ? [] : body[1]; if (invoices && invoices.length > 0) { - return Promise.all(invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))). - then((values) => { + return Promise.all( + invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice)) + ) + .then((values) => { body = common.sortDescByKey(invoices, 'expiresAt'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Sorted Invoices List Received', data: invoices }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Sorted Invoices List Received', + data: invoices + }); return res.status(200).json(invoices); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Empty List Invoice Received' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Empty List Invoice Received' + }); return res.status(200).json([]); } - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -102,17 +144,35 @@ export const listInvoices = (req, res, next) => { }; export const createInvoice = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoices', + msg: 'Creating Invoice..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/createinvoice'; options.form = req.body; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Created', data: body }); - if (body.amount) { body.amount = Math.round(body.amount / 1000); } - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Invoices', 'Create Invoice Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Created', + data: body + }); + if (body.amount) { + body.amount = Math.round(body.amount / 1000); + } + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Invoices', 'Create Invoice Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/eclair/network.ts b/server/controllers/eclair/network.ts index f9165f2da..cc2e5e035 100644 --- a/server/controllers/eclair/network.ts +++ b/server/controllers/eclair/network.ts @@ -8,14 +8,25 @@ const common: CommonService = Common; export const getNodes = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/nodes'; options.form = { nodeIds: req.params.id }; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Network', + msg: 'Node Lookup Finished', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/eclair/onchain.ts b/server/controllers/eclair/onchain.ts index 03e38cc14..583d29098 100644 --- a/server/controllers/eclair/onchain.ts +++ b/server/controllers/eclair/onchain.ts @@ -6,42 +6,79 @@ const logger: LoggerService = Logger; const common: CommonService = Common; export const arrangeBalances = (body) => { - if (!body.confirmed) { body.confirmed = 0; } - if (!body.unconfirmed) { body.unconfirmed = 0; } + if (!body.confirmed) { + body.confirmed = 0; + } + if (!body.unconfirmed) { + body.unconfirmed = 0; + } body.total = +body.confirmed + +body.unconfirmed; body.btc_total = +body.btc_confirmed + +body.btc_unconfirmed; return body; }; export const getNewAddress = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Generating New Address..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Generating New Address..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/getnewaddress'; options.form = {}; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'OnChain', 'Get New Address Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'New Address Generated', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'OnChain', 'Get New Address Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getBalance = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Balance..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Getting On Chain Balance..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/onchainbalance'; options.form = {}; if (common.read_dummy_data) { - common.getDummyData('OnChainBalance', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangeBalances(data)); }); + common.getDummyData('OnChainBalance', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(arrangeBalances(data)); + }); } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Balance Received', data: body }); - res.status(200).json(arrangeBalances(body)); - }). - catch((errRes) => { + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'On Chain Balance Received', + data: body + }); + res.status(200).json(arrangeBalances(body)); + }) + .catch((errRes) => { const err = common.handleError(errRes, 'OnChain', 'Get Balance Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -49,41 +86,87 @@ export const getBalance = (req, res, next) => { }; export const getTransactions = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Getting On Chain Transactions..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/onchaintransactions'; options.form = { count: req.query.count, skip: req.query.skip }; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions Options', data: options.form }); - request.post(options).then((body) => { - if (body && body.length > 0) { body = common.sortDescByKey(body, 'timestamp'); } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Transactions Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'OnChain', 'Get Transactions Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Getting On Chain Transactions Options', + data: options.form }); + request + .post(options) + .then((body) => { + if (body && body.length > 0) { + body = common.sortDescByKey(body, 'timestamp'); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'On Chain Transactions Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'OnChain', 'Get Transactions Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const sendFunds = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Sending On Chain Funds..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'OnChain', + msg: 'Sending On Chain Funds..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/sendonchain'; options.form = { address: req.body.address, amountSatoshis: req.body.amount, confirmationTarget: req.body.blocks }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Onchain', msg: 'Send Funds Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Onchain', msg: 'On Chain Funds Sent', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'OnChain', 'Send Funds Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Onchain', + msg: 'Send Funds Options', + data: options.form }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Onchain', + msg: 'On Chain Funds Sent', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'OnChain', 'Send Funds Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/eclair/payments.ts b/server/controllers/eclair/payments.ts index 8eada4da3..3c28efb8d 100644 --- a/server/controllers/eclair/payments.ts +++ b/server/controllers/eclair/payments.ts @@ -9,111 +9,222 @@ const common: CommonService = Common; export const getSentInfoFromPaymentRequest = (selNode: CommonSelectedNode, payment) => { options.url = selNode.ln_server_url + '/getsentinfo'; options.form = { paymentHash: payment }; - return request.post(options).then((body) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Sent Information Received', data: body }); - body.forEach((sentPayment) => { - if (sentPayment.amount) { sentPayment.amount = Math.round(sentPayment.amount / 1000); } - if (sentPayment.recipientAmount) { sentPayment.recipientAmount = Math.round(sentPayment.recipientAmount / 1000); } - }); - return body; - }).catch((err) => err); + return request + .post(options) + .then((body) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Payment Sent Information Received', + data: body + }); + body.forEach((sentPayment) => { + if (sentPayment.amount) { + sentPayment.amount = Math.round(sentPayment.amount / 1000); + } + if (sentPayment.recipientAmount) { + sentPayment.recipientAmount = Math.round(sentPayment.recipientAmount / 1000); + } + }); + return body; + }) + .catch((err) => err); }; export const getQueryNodes = (selNode: CommonSelectedNode, nodeIds) => { options.url = selNode.ln_server_url + '/nodes'; options.form = { nodeIds: nodeIds.reduce((acc, curr) => acc + ',' + curr) }; - return request.post(options).then((nodes) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Nodes Received', data: nodes }); - return nodes; - }).catch((err) => []); + return request + .post(options) + .then((nodes) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Query Nodes Received', + data: nodes + }); + return nodes; + }) + .catch((err) => []); }; export const decodePayment = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Decoding Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Decoding Payment..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/parseinvoice'; options.form = { invoice: req.params.invoice }; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded', data: body }); - if (body.amount) { body.amount = Math.round(body.amount / 1000); } - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Decoded', + data: body + }); + if (body.amount) { + body.amount = Math.round(body.amount / 1000); + } + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const postPayment = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Paying Invoice..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/payinvoice'; options.form = req.body; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Send Payment Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Invoice Paid', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Send Payment Options', + data: options.form }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Invoice Paid', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const queryPaymentRoute = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Querying Payment Route..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Querying Payment Route..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/findroutetonode'; options.form = { nodeId: req.query.nodeId, amountMsat: req.query.amountMsat }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Payment Route Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Payment Route Received', data: body }); - if (body && body.routes && body.routes.length) { - let allRoutesNodeIds = []; - allRoutesNodeIds = body.routes.reduce((accRoutes, currRoute) => [...new Set([...accRoutes, ...currRoute.nodeIds])], []); - return getQueryNodes(req.session.selectedNode, allRoutesNodeIds).then((nodesWithAlias) => { - let foundPeer = null; - body.routes.forEach((route, i) => { - route.nodeIds.map((node, j) => { - foundPeer = nodesWithAlias.find((nodeWithAlias) => node === nodeWithAlias.nodeId); - body.routes[i].nodeIds[j] = { nodeId: node, alias: foundPeer ? foundPeer.alias : '' }; - return node; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Query Payment Route Options', + data: options.form + }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Query Payment Route Received', + data: body + }); + if (body && body.routes && body.routes.length) { + let allRoutesNodeIds = []; + allRoutesNodeIds = body.routes.reduce( + (accRoutes, currRoute) => [...new Set([...accRoutes, ...currRoute.nodeIds])], + [] + ); + return getQueryNodes(req.session.selectedNode, allRoutesNodeIds).then((nodesWithAlias) => { + let foundPeer = null; + body.routes.forEach((route, i) => { + route.nodeIds.map((node, j) => { + foundPeer = nodesWithAlias.find((nodeWithAlias) => node === nodeWithAlias.nodeId); + body.routes[i].nodeIds[j] = { nodeId: node, alias: foundPeer ? foundPeer.alias : '' }; + return node; + }); }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Query Routes with Alias Received', + data: body + }); + res.status(200).json(body); }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Query Routes with Alias Received', data: body }); - res.status(200).json(body); - }); - } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Empty Payment Route Information Received' }); - res.status(200).json({ routes: [] }); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'Payments', 'Query Route Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + } else { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Empty Payment Route Information Received' + }); + res.status(200).json({ routes: [] }); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Payments', 'Query Route Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getSentPaymentsInformation = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting Sent Payment Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Getting Sent Payment Information..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } if (req.body.payments) { const paymentsArr = req.body.payments.split(','); - return Promise.all(paymentsArr.map((payment) => getSentInfoFromPaymentRequest(req.session.selectedNode, payment))). - then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent Information Received', data: values }); + return Promise.all(paymentsArr.map((payment) => getSentInfoFromPaymentRequest(req.session.selectedNode, payment))) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Sent Information Received', + data: values + }); return res.status(200).json(values); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'Sent Payment Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Empty Sent Payment Information Received' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Empty Sent Payment Information Received' + }); return res.status(200).json([]); } }; diff --git a/server/controllers/eclair/peers.ts b/server/controllers/eclair/peers.ts index a3b20a990..b6eedab48 100644 --- a/server/controllers/eclair/peers.ts +++ b/server/controllers/eclair/peers.ts @@ -9,44 +9,78 @@ const common: CommonService = Common; export const getFilteredNodes = (selNode: CommonSelectedNode, peersNodeIds) => { options.url = selNode.ln_server_url + '/nodes'; options.form = { nodeIds: peersNodeIds }; - return request.post(options).then((nodes) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Peers', msg: 'Filtered Nodes Received', data: nodes }); - return nodes; - }).catch((err) => []); + return request + .post(options) + .then((nodes) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Filtered Nodes Received', + data: nodes + }); + return nodes; + }) + .catch((err) => []); }; export const getPeers = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Getting Peers..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/peers'; options.form = {}; if (common.read_dummy_data) { - common.getDummyData('Peers', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(data); }); + common.getDummyData('Peers', req.session.selectedNode.ln_implementation).then((data) => { + res.status(200).json(data); + }); } else { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body }); - if (body && body.length) { - let peersNodeIds = ''; - body.forEach((peer) => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; }); - peersNodeIds = peersNodeIds.substring(1); - return getFilteredNodes(req.session.selectedNode, peersNodeIds).then((peersWithAlias) => { - let foundPeer = null; - body.map((peer) => { - foundPeer = peersWithAlias.find((peerWithAlias) => peer.nodeId === peerWithAlias.nodeId); - peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20); - return peer; - }); - body = common.sortDescByStrKey(body, 'alias'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body }); - res.status(200).json(body); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peers List Received', + data: body }); - } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Empty Peers Received' }); - res.status(200).json([]); - } - }). - catch((errRes) => { + if (body && body.length) { + let peersNodeIds = ''; + body.forEach((peer) => { + peersNodeIds = peersNodeIds + ',' + peer.nodeId; + }); + peersNodeIds = peersNodeIds.substring(1); + return getFilteredNodes(req.session.selectedNode, peersNodeIds).then((peersWithAlias) => { + let foundPeer = null; + body.map((peer) => { + foundPeer = peersWithAlias.find((peerWithAlias) => peer.nodeId === peerWithAlias.nodeId); + peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20); + return peer; + }); + body = common.sortDescByStrKey(body, 'alias'); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Sorted Peers List Received', + data: body + }); + res.status(200).json(body); + }); + } else { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Empty Peers Received' + }); + res.status(200).json([]); + } + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -56,70 +90,139 @@ export const getPeers = (req, res, next) => { export const connectPeer = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Conneting Peer..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/connect'; options.form = {}; if (req.query) { options.form = req.query; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Connect Peer Params', data: options.form }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Connect Peer Params', + data: options.form + }); } - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body }); - if (typeof body === 'string' && body.includes('already connected')) { - const err = common.handleError({ statusCode: 500, message: 'Connect Peer Error', error: body }, 'Peers', body, req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - } else if (typeof body === 'string' && body.includes('connection failed')) { - const err = common.handleError({ statusCode: 500, message: 'Connect Peer Error', error: body }, 'Peers', body, req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - } - options.url = req.session.selectedNode.ln_server_url + '/peers'; - options.form = {}; - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List after Connect', data: body }); - if (body && body.length) { - let peersNodeIds = ''; - body.forEach((peer) => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; }); - peersNodeIds = peersNodeIds.substring(1); - return getFilteredNodes(req.session.selectedNode, peersNodeIds).then((peersWithAlias) => { - let foundPeer = null; - body.map((peer) => { - foundPeer = peersWithAlias.find((peerWithAlias) => peer.nodeId === peerWithAlias.nodeId); - peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20); - return peer; + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peer Connected', + data: body + }); + if (typeof body === 'string' && body.includes('already connected')) { + const err = common.handleError( + { statusCode: 500, message: 'Connect Peer Error', error: body }, + 'Peers', + body, + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + } else if (typeof body === 'string' && body.includes('connection failed')) { + const err = common.handleError( + { statusCode: 500, message: 'Connect Peer Error', error: body }, + 'Peers', + body, + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + } + options.url = req.session.selectedNode.ln_server_url + '/peers'; + options.form = {}; + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peers List after Connect', + data: body }); - let peers = (body) ? common.sortDescByStrKey(body, 'alias') : []; - peers = common.newestOnTop(peers, 'nodeId', req.query.nodeId ? req.query.nodeId : req.query.uri ? req.query.uri.substring(0, req.query.uri.indexOf('@')) : ''); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers }); - res.status(201).json(peers); + if (body && body.length) { + let peersNodeIds = ''; + body.forEach((peer) => { + peersNodeIds = peersNodeIds + ',' + peer.nodeId; + }); + peersNodeIds = peersNodeIds.substring(1); + return getFilteredNodes(req.session.selectedNode, peersNodeIds).then((peersWithAlias) => { + let foundPeer = null; + body.map((peer) => { + foundPeer = peersWithAlias.find((peerWithAlias) => peer.nodeId === peerWithAlias.nodeId); + peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20); + return peer; + }); + let peers = body ? common.sortDescByStrKey(body, 'alias') : []; + peers = common.newestOnTop( + peers, + 'nodeId', + req.query.nodeId + ? req.query.nodeId + : req.query.uri + ? req.query.uri.substring(0, req.query.uri.indexOf('@')) + : '' + ); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peers List after Connect Received', + data: peers + }); + res.status(201).json(peers); + }); + } else { + res.status(201).json([]); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - } else { - res.status(201).json([]); - } - }).catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; export const deletePeer = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Disconneting Peer..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/disconnect'; options.form = {}; if (req.params.nodeId) { options.form = { nodeId: req.params.nodeId }; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Disconnect Peer Params', data: options.form }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Disconnect Peer Params', + data: options.form + }); } - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected', data: body }); - res.status(204).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peer Disconnected', + data: body + }); + res.status(204).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/eclair/webSocketClient.ts b/server/controllers/eclair/webSocketClient.ts index f379fd786..0181d0de5 100644 --- a/server/controllers/eclair/webSocketClient.ts +++ b/server/controllers/eclair/webSocketClient.ts @@ -6,11 +6,10 @@ import { WSServer } from '../../utils/webSocketServer.js'; import { CommonSelectedNode } from '../../models/config.model.js'; export class ECLWebSocketClient { - public logger: LoggerService = Logger; public common: CommonService = Common; public wsServer = WSServer; - public webSocketClients: Array<{ selectedNode: CommonSelectedNode, reConnect: boolean, webSocketClient: any }> = []; + public webSocketClients: Array<{ selectedNode: CommonSelectedNode; reConnect: boolean; webSocketClient: any }> = []; public reconnectTimeOut = null; public waitTime = 0.5; @@ -24,11 +23,18 @@ export class ECLWebSocketClient { } public reconnet = (eclWsClt) => { - if (this.reconnectTimeOut) { return; } - this.waitTime = (this.waitTime >= 64) ? 64 : (this.waitTime * 2); + if (this.reconnectTimeOut) { + return; + } + this.waitTime = this.waitTime >= 64 ? 64 : this.waitTime * 2; this.reconnectTimeOut = setTimeout(() => { if (eclWsClt.selectedNode) { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Reconnecting to the Eclair\'s Websocket Server..' }); + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: "Reconnecting to the Eclair's Websocket Server.." + }); this.connect(eclWsClt.selectedNode); } this.reconnectTimeOut = null; @@ -45,7 +51,10 @@ export class ECLWebSocketClient { this.webSocketClients.push(newWebSocketClient); } } else { - if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) { + if ( + (!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && + selectedNode.ln_server_url + ) { clientExists.reConnect = true; this.connectWithClient(clientExists); } @@ -56,39 +65,85 @@ export class ECLWebSocketClient { }; public connectWithClient = (eclWsClt) => { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Connecting to the Eclair\'s Websocket Server..' }); - const UpdatedLNServerURL = (eclWsClt.selectedNode.ln_server_url).replace(/^http/, 'ws'); - const firstSubStrIndex = (UpdatedLNServerURL.indexOf('//') + 2); - const WS_LINK = UpdatedLNServerURL.slice(0, firstSubStrIndex) + ':' + eclWsClt.selectedNode.ln_api_password + '@' + UpdatedLNServerURL.slice(firstSubStrIndex) + '/ws'; + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: "Connecting to the Eclair's Websocket Server.." + }); + const UpdatedLNServerURL = eclWsClt.selectedNode.ln_server_url.replace(/^http/, 'ws'); + const firstSubStrIndex = UpdatedLNServerURL.indexOf('//') + 2; + const WS_LINK = + UpdatedLNServerURL.slice(0, firstSubStrIndex) + + ':' + + eclWsClt.selectedNode.ln_api_password + + '@' + + UpdatedLNServerURL.slice(firstSubStrIndex) + + '/ws'; eclWsClt.webSocketClient = new WebSocket(WS_LINK); eclWsClt.webSocketClient.onopen = () => { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Connected to the Eclair\'s Websocket Server..' }); + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: "Connected to the Eclair's Websocket Server.." + }); this.waitTime = 0.5; }; eclWsClt.webSocketClient.onclose = (e) => { if (eclWsClt && eclWsClt.selectedNode && eclWsClt.selectedNode.ln_implementation === 'ECL') { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Web socket disconnected, will reconnect again...' }); + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: 'Web socket disconnected, will reconnect again...' + }); eclWsClt.webSocketClient.close(); - if (eclWsClt.reConnect) { this.reconnet(eclWsClt); } + if (eclWsClt.reConnect) { + this.reconnet(eclWsClt); + } } }; eclWsClt.webSocketClient.onmessage = (msg) => { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'DEBUG', fileName: 'ECLWebSocket', msg: 'Received message from the server..', data: msg.data }); - msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data; + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'DEBUG', + fileName: 'ECLWebSocket', + msg: 'Received message from the server..', + data: msg.data + }); + msg = typeof msg.data === 'string' ? JSON.parse(msg.data) : msg.data; msg['source'] = 'ECL'; const msgStr = JSON.stringify(msg); this.wsServer.sendEventsToAllLNClients(msgStr, eclWsClt.selectedNode); }; eclWsClt.webSocketClient.onerror = (err) => { - if (eclWsClt.selectedNode.ln_version === '' || !eclWsClt.selectedNode.ln_version || this.common.isVersionCompatible(eclWsClt.selectedNode.ln, '0.5.0')) { - this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'ERROR', fileName: 'ECLWebSocket', msg: 'Web socket error', error: err }); - const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }')); + if ( + eclWsClt.selectedNode.ln_version === '' || + !eclWsClt.selectedNode.ln_version || + this.common.isVersionCompatible(eclWsClt.selectedNode.ln, '0.5.0') + ) { + this.logger.log({ + selectedNode: eclWsClt.selectedNode, + level: 'ERROR', + fileName: 'ECLWebSocket', + msg: 'Web socket error', + error: err + }); + const errStr = + typeof err === 'object' && err.message + ? JSON.stringify({ error: err.message }) + : typeof err === 'object' + ? JSON.stringify({ error: err }) + : '{ "error": ' + err + ' }'; this.wsServer.sendErrorToAllLNClients(errStr, eclWsClt.selectedNode); eclWsClt.webSocketClient.close(); - if (eclWsClt.reConnect) { this.reconnet(eclWsClt); } + if (eclWsClt.reConnect) { + this.reconnet(eclWsClt); + } } else { eclWsClt.reConnect = false; } @@ -98,7 +153,12 @@ export class ECLWebSocketClient { public disconnect = (selectedNode: CommonSelectedNode) => { const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index); if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) { - this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Disconnecting from the Eclair\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clientExists.selectedNode, + level: 'INFO', + fileName: 'ECLWebSocket', + msg: "Disconnecting from the Eclair's Websocket Server.." + }); clientExists.reConnect = false; clientExists.webSocketClient.close(); const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index); @@ -109,11 +169,12 @@ export class ECLWebSocketClient { public updateSelectedNode = (newSelectedNode: CommonSelectedNode) => { const clientIdx = this.webSocketClients.findIndex((wsc) => +wsc.selectedNode.index === +newSelectedNode.index); let newClient = this.webSocketClients[clientIdx]; - if (!newClient) { newClient = { selectedNode: null, reConnect: true, webSocketClient: null }; } + if (!newClient) { + newClient = { selectedNode: null, reConnect: true, webSocketClient: null }; + } newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode)); this.webSocketClients[clientIdx] = newClient; - } - + }; } export const ECLWSClient = new ECLWebSocketClient(); diff --git a/server/controllers/lnd/balance.ts b/server/controllers/lnd/balance.ts index 369fa4ad0..5d52eaf25 100644 --- a/server/controllers/lnd/balance.ts +++ b/server/controllers/lnd/balance.ts @@ -8,21 +8,49 @@ const common: CommonService = Common; export const getBlockchainBalance = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Getting Balance..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/balance/blockchain'; options.qs = req.query; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Balance', msg: 'Request params', data: req.params }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Balance', msg: 'Request Query', data: req.query }); - request(options).then((body) => { - if (body) { - if (!body.total_balance) { body.total_balance = 0; } - if (!body.confirmed_balance) { body.confirmed_balance = 0; } - if (!body.unconfirmed_balance) { body.unconfirmed_balance = 0; } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: body }); - res.status(200).json(body); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Balance', + msg: 'Request params', + data: req.params }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Balance', + msg: 'Request Query', + data: req.query + }); + request(options) + .then((body) => { + if (body) { + if (!body.total_balance) { + body.total_balance = 0; + } + if (!body.confirmed_balance) { + body.confirmed_balance = 0; + } + if (!body.unconfirmed_balance) { + body.unconfirmed_balance = 0; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Balance', + msg: 'Balance Received', + data: body + }); + res.status(200).json(body); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/lnd/channels.ts b/server/controllers/lnd/channels.ts index de87887da..42468504c 100644 --- a/server/controllers/lnd/channels.ts +++ b/server/controllers/lnd/channels.ts @@ -7,129 +7,223 @@ const logger: LoggerService = Logger; const common: CommonService = Common; export const getAliasForChannel = (selNode: CommonSelectedNode, channel) => { - const pubkey = (channel.remote_pubkey) ? channel.remote_pubkey : (channel.remote_node_pub) ? channel.remote_node_pub : ''; + const pubkey = channel.remote_pubkey ? channel.remote_pubkey : channel.remote_node_pub ? channel.remote_node_pub : ''; options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey; - return request(options).then((aliasBody) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Channels', msg: 'Alias Received', data: aliasBody.node.alias }); - channel.remote_alias = aliasBody.node.alias; - return aliasBody.node.alias; - }).catch((err) => { - channel.remote_alias = pubkey.slice(0, 10) + '...' + pubkey.slice(-10); - return pubkey; - }); + return request(options) + .then((aliasBody) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Alias Received', + data: aliasBody.node.alias + }); + channel.remote_alias = aliasBody.node.alias; + return aliasBody.node.alias; + }) + .catch((err) => { + channel.remote_alias = pubkey.slice(0, 10) + '...' + pubkey.slice(-10); + return pubkey; + }); }; export const getAllChannels = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Channels..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channels'; options.qs = req.query; let local = 0; let remote = 0; let total = 0; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels List Received', data: body }); - if (body.channels) { - return Promise.all( - body.channels.map((channel) => { - local = (channel.local_balance) ? +channel.local_balance : 0; - remote = (channel.remote_balance) ? +channel.remote_balance : 0; - total = local + remote; - channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); - return getAliasForChannel(req.session.selectedNode, channel); - }) - ).then((values) => { - body.channels = common.sortDescByKey(body.channels, 'balancedness'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body }); - return res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Get All Channel Aliases Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Channels List Received', + data: body }); - } else { - body.channels = []; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Empty Channels List Received' }); - return res.status(200).json(body); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + if (body.channels) { + return Promise.all( + body.channels.map((channel) => { + local = channel.local_balance ? +channel.local_balance : 0; + remote = channel.remote_balance ? +channel.remote_balance : 0; + total = local + remote; + channel.balancedness = total === 0 ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); + return getAliasForChannel(req.session.selectedNode, channel); + }) + ) + .then((values) => { + body.channels = common.sortDescByKey(body.channels, 'balancedness'); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Sorted Channels List Received', + data: body + }); + return res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError( + errRes, + 'Channels', + 'Get All Channel Aliases Error', + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); + } else { + body.channels = []; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Empty Channels List Received' + }); + return res.status(200).json(body); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getPendingChannels = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Pending Channels..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Pending Channels..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channels/pending'; options.qs = req.query; - request(options).then((body) => { - if (!body.total_limbo_balance) { - body.total_limbo_balance = 0; - } - const promises = []; - if (body.pending_open_channels && body.pending_open_channels.length > 0) { - body.pending_open_channels.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel))); - } - if (body.pending_closing_channels && body.pending_closing_channels.length > 0) { - body.pending_closing_channels.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel))); - } - if (body.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) { - body.pending_force_closing_channels.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel))); - } - if (body.waiting_close_channels && body.waiting_close_channels.length > 0) { - body.waiting_close_channels.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel))); - } - return Promise.all(promises).then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Pending Channels List Received', data: body }); - return res.status(200).json(body); - }). - catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Get Pending Channel Aliases Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'List Pending Channels Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + if (!body.total_limbo_balance) { + body.total_limbo_balance = 0; + } + const promises = []; + if (body.pending_open_channels && body.pending_open_channels.length > 0) { + body.pending_open_channels.map((channel) => + promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)) + ); + } + if (body.pending_closing_channels && body.pending_closing_channels.length > 0) { + body.pending_closing_channels.map((channel) => + promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)) + ); + } + if (body.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) { + body.pending_force_closing_channels.map((channel) => + promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)) + ); + } + if (body.waiting_close_channels && body.waiting_close_channels.length > 0) { + body.waiting_close_channels.map((channel) => + promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)) + ); + } + return Promise.all(promises) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Pending Channels List Received', + data: body + }); + return res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError( + errRes, + 'Channels', + 'Get Pending Channel Aliases Error', + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'List Pending Channels Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getClosedChannels = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Closed Channels..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Getting Closed Channels..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channels/closed'; options.qs = req.query; - request(options).then((body) => { - if (body.channels && body.channels.length > 0) { - return Promise.all( - body.channels.map((channel) => { - channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type; - return getAliasForChannel(req.session.selectedNode, channel); - }) - ).then((values) => { - body.channels = common.sortDescByKey(body.channels, 'close_height'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body }); + request(options) + .then((body) => { + if (body.channels && body.channels.length > 0) { + return Promise.all( + body.channels.map((channel) => { + channel.close_type = !channel.close_type ? 'COOPERATIVE_CLOSE' : channel.close_type; + return getAliasForChannel(req.session.selectedNode, channel); + }) + ) + .then((values) => { + body.channels = common.sortDescByKey(body.channels, 'close_height'); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Closed Channels List Received', + data: body + }); + return res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError( + errRes, + 'Channels', + 'Get Closed Channel Aliases Error', + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); + } else { + body.channels = []; return res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Get Closed Channel Aliases Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); - } else { - body.channels = []; - return res.status(200).json(body); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'List Closed Channels Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'List Closed Channels Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const postChannel = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channels'; options.form = { node_pubkey_string: req.body.node_pubkey, @@ -143,71 +237,145 @@ export const postChannel = (req, res, next) => { options.form.sat_per_byte = req.body.trans_type_value; } options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Opened', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const postTransactions = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sending Payment..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channels/transactions'; options.form = { payment_request: req.body.paymentReq }; if (req.body.paymentAmount) { options.form.amt = req.body.paymentAmount; } - if (req.body.feeLimit) { options.form.fee_limit = req.body.feeLimit; } - if (req.body.outgoingChannel) { options.form.outgoing_chan_id = req.body.outgoingChannel; } - if (req.body.allowSelfPayment) { options.form.allow_self_payment = req.body.allowSelfPayment; } - if (req.body.lastHopPubkey) { options.form.last_hop_pubkey = Buffer.from(req.body.lastHopPubkey, 'hex').toString('base64'); } + if (req.body.feeLimit) { + options.form.fee_limit = req.body.feeLimit; + } + if (req.body.outgoingChannel) { + options.form.outgoing_chan_id = req.body.outgoingChannel; + } + if (req.body.allowSelfPayment) { + options.form.allow_self_payment = req.body.allowSelfPayment; + } + if (req.body.lastHopPubkey) { + options.form.last_hop_pubkey = Buffer.from(req.body.lastHopPubkey, 'hex').toString('base64'); + } options.form = JSON.stringify(options.form); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Send Payment Options', data: options.form }); - request.post(options).then((body) => { - if (body.payment_error) { - const err = common.handleError(body.payment_error, 'Channels', 'Send Payment Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Payment Sent', data: body }); - res.status(201).json(body); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Send Payment Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Send Payment Options', + data: options.form }); + request + .post(options) + .then((body) => { + if (body.payment_error) { + const err = common.handleError(body.payment_error, 'Channels', 'Send Payment Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + } else { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Payment Sent', + data: body + }); + res.status(201).json(body); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Send Payment Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const closeChannel = (req, res, next) => { try { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Closing Channel..' + }); if (!req.session.selectedNode) { - const err = common.handleError({ message: 'Session Expired after a day\'s inactivity.', statusCode: 401 }, 'Session Expired', 'Session Expiry Error', null); + const err = common.handleError( + { message: "Session Expired after a day's inactivity.", statusCode: 401 }, + 'Session Expired', + 'Session Expiry Error', + null + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } const channelpoint = req.params.channelPoint.replace(':', '/'); options.url = req.session.selectedNode.ln_server_url + '/v1/channels/' + channelpoint + '?force=' + req.query.force; - if (req.query.target_conf) { options.url = options.url + '&target_conf=' + req.query.target_conf; } - if (req.query.sat_per_byte) { options.url = options.url + '&sat_per_byte=' + req.query.sat_per_byte; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel Options URL', data: options.url }); + if (req.query.target_conf) { + options.url = options.url + '&target_conf=' + req.query.target_conf; + } + if (req.query.sat_per_byte) { + options.url = options.url + '&sat_per_byte=' + req.query.sat_per_byte; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Closing Channel Options URL', + data: options.url + }); request.delete(options); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Close Requested' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Close Requested' + }); res.status(202).json({ message: 'Close channel request has been submitted.' }); } catch (error) { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Channels', msg: 'Close Channel Error', error: error.message }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Channels', + msg: 'Close Channel Error', + error: error.message + }); return res.status(500).json({ message: 'Close Channel Error', error: error.message }); } }; export const postChanPolicy = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updating Channel Policy..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Updating Channel Policy..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/chanpolicy'; if (req.body.chanPoint === 'all') { options.form = JSON.stringify({ @@ -227,12 +395,27 @@ export const postChanPolicy = (req, res, next) => { chan_point: { funding_txid_str: txid_str, output_index: parseInt(output_idx) } }); } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Policy Updated', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Channels', + msg: 'Update Channel Policy Options', + data: options.form }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Channels', + msg: 'Channel Policy Updated', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/lnd/channelsBackup.ts b/server/controllers/lnd/channelsBackup.ts index b7aace5ed..508868463 100644 --- a/server/controllers/lnd/channelsBackup.ts +++ b/server/controllers/lnd/channelsBackup.ts @@ -7,7 +7,6 @@ let options = null; const logger: LoggerService = Logger; const common: CommonService = Common; - function getFilesList(channelBackupPath, callback) { const files_list = []; let all_restore_exists = false; @@ -33,9 +32,16 @@ function getFilesList(channelBackupPath, callback) { } export const getBackup = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Getting Channel Backup..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Getting Channel Backup..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } let channel_backup_file = ''; let message = ''; if (req.params.channelPoint === 'ALL') { @@ -43,13 +49,18 @@ export const getBackup = (req, res, next) => { message = 'All Channels Backup Successful.'; options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup'; } else { - channel_backup_file = req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; + channel_backup_file = + req.session.selectedNode.channel_backup_path + + sep + + 'channel-' + + req.params.channelPoint.replace(':', '-') + + '.bak'; message = 'Channel Backup Successful.'; const channelpoint = req.params.channelPoint.replace(':', '/'); options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/' + channelpoint; const exists = fs.existsSync(channel_backup_file); if (exists) { - fs.writeFile(channel_backup_file, '', () => { }); + fs.writeFile(channel_backup_file, '', () => {}); } else { try { const createStream = fs.createWriteStream(channel_backup_file); @@ -60,27 +71,48 @@ export const getBackup = (req, res, next) => { } } } - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'ChannelsBackup', msg: 'Channel Backup Received', data: body }); - fs.writeFile(channel_backup_file, JSON.stringify(body), (errRes) => { - if (errRes) { - const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Backed up and Saved', data: body }); - res.status(200).json({ message: message }); - } + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'ChannelsBackup', + msg: 'Channel Backup Received', + data: body + }); + fs.writeFile(channel_backup_file, JSON.stringify(body), (errRes) => { + if (errRes) { + const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + } else { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Channel Backed up and Saved', + data: body + }); + res.status(200).json({ message: message }); + } + }); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; export const postBackupVerify = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Verifying Channel Backup..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Verifying Channel Backup..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/verify'; let channel_verify_file = ''; let message = ''; @@ -97,18 +129,33 @@ export const postBackupVerify = (req, res, next) => { options.form = JSON.stringify(verify_backup_json); } else { const errMsg = 'Channel backup to verify does not Exist.'; - const err = common.handleError({ statusCode: 404, message: 'Verify Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 404, message: 'Verify Channel Error', error: errMsg }, + 'ChannelBackup', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } } else { verify_backup = ''; const errMsg = 'Channel backup to verify does not Exist.'; - const err = common.handleError({ statusCode: 404, message: 'Verify Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 404, message: 'Verify Channel Error', error: errMsg }, + 'ChannelBackup', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } } else { message = 'Channel Verify Successful.'; - channel_verify_file = req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; + channel_verify_file = + req.session.selectedNode.channel_backup_path + + sep + + 'channel-' + + req.params.channelPoint.replace(':', '-') + + '.bak'; const exists = fs.existsSync(channel_verify_file); if (exists) { verify_backup = fs.readFileSync(channel_verify_file, 'utf-8'); @@ -116,16 +163,29 @@ export const postBackupVerify = (req, res, next) => { } else { verify_backup = ''; const errMsg = 'Channel backup to verify does not Exist.'; - const err = common.handleError({ statusCode: 404, message: 'Verify Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 404, message: 'Verify Channel Error', error: errMsg }, + 'ChannelBackup', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } } if (verify_backup !== '') { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Backup Verified', data: body }); - res.status(201).json({ message: message }); - }). - catch((errRes) => { + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Channel Backup Verified', + data: body + }); + res.status(201).json({ message: message }); + }) + .catch((errRes) => { const err = common.handleError(errRes, 'ChannelsBackup', 'Verify Channels Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); @@ -133,9 +193,16 @@ export const postBackupVerify = (req, res, next) => { }; export const postRestore = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Restoring Channel Backup..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Restoring Channel Backup..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/restore'; let channel_restore_file = ''; let message = ''; @@ -152,7 +219,12 @@ export const postRestore = (req, res, next) => { options.form = JSON.stringify({ multi_chan_backup: restore_backup_json.multi_chan_backup.multi_chan_backup }); } else { const errMsg = 'Channel backup to restore does not Exist.'; - const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 404, message: 'Restore Channel Error', error: errMsg }, + 'ChannelBackup', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } } else if (downloaded_exists) { @@ -162,18 +234,35 @@ export const postRestore = (req, res, next) => { options.form = JSON.stringify({ multi_chan_backup: restore_backup_json.multi_chan_backup.multi_chan_backup }); } else { const errMsg = 'Channel backup to restore does not Exist.'; - const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 404, message: 'Restore Channel Error', error: errMsg }, + 'ChannelBackup', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } } else { restore_backup = ''; const errMsg = 'Channel backup to restore does not Exist.'; - const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 404, message: 'Restore Channel Error', error: errMsg }, + 'ChannelBackup', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } } else { message = 'Channel Restore Successful.'; - channel_restore_file = req.session.selectedNode.channel_backup_path + sep + 'restore' + sep + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; + channel_restore_file = + req.session.selectedNode.channel_backup_path + + sep + + 'restore' + + sep + + 'channel-' + + req.params.channelPoint.replace(':', '-') + + '.bak'; const exists = fs.existsSync(channel_restore_file); if (exists) { restore_backup = fs.readFileSync(channel_restore_file, 'utf-8'); @@ -181,28 +270,53 @@ export const postRestore = (req, res, next) => { } else { restore_backup = ''; const errMsg = 'Channel backup to restore does not Exist.'; - const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 404, message: 'Restore Channel Error', error: errMsg }, + 'ChannelBackup', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } } if (restore_backup !== '') { - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'ChannelBackup', msg: 'Channel Restored', data: body }); - if (req.params.channelPoint === 'ALL') { channel_restore_file = channel_restore_file + 'channel-all.bak'; } - fs.rename(channel_restore_file, channel_restore_file + '.restored', () => { - getFilesList(req.session.selectedNode.channel_backup_path, (getFilesListRes) => { - if (getFilesListRes.error) { - const errMsg = getFilesListRes.error; - const err = common.handleError({ statusCode: 500, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.error, list: getFilesListRes }); - } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Restored and Saved' }); - return res.status(201).json({ message: message, list: getFilesListRes }); - } + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'ChannelBackup', + msg: 'Channel Restored', + data: body }); - }); - }). - catch((errRes) => { + if (req.params.channelPoint === 'ALL') { + channel_restore_file = channel_restore_file + 'channel-all.bak'; + } + fs.rename(channel_restore_file, channel_restore_file + '.restored', () => { + getFilesList(req.session.selectedNode.channel_backup_path, (getFilesListRes) => { + if (getFilesListRes.error) { + const errMsg = getFilesListRes.error; + const err = common.handleError( + { statusCode: 500, message: 'Restore Channel Error', error: errMsg }, + 'ChannelBackup', + errMsg, + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.error, list: getFilesListRes }); + } else { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'ChannelBackup', + msg: 'Channel Restored and Saved' + }); + return res.status(201).json({ message: message, list: getFilesListRes }); + } + }); + }); + }) + .catch((errRes) => { const err = common.handleError(errRes, 'ChannelsBackup', 'Restore Channel Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/server/controllers/lnd/fees.ts b/server/controllers/lnd/fees.ts index 0a62e44ca..e2c4dff79 100644 --- a/server/controllers/lnd/fees.ts +++ b/server/controllers/lnd/fees.ts @@ -9,39 +9,94 @@ const common: CommonService = Common; export const getFees = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/fees'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body }); - const today = new Date(Date.now()); - const start_date = new Date(today.getFullYear(), today.getMonth(), 1, 0, 0, 0); - const current_time = (Math.round(today.getTime() / 1000)); - const month_start_time = (Math.round(start_date.getTime() / 1000)); - const week_start_time = current_time - 604800; - const day_start_time = current_time - 86400; - return getAllForwardingEvents(req, month_start_time, current_time, 0, 'fees', (history) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Forwarding History Received', data: history }); - const daily_sum = history.forwarding_events.reduce((acc, curr) => ((curr.timestamp >= day_start_time) ? [(acc[0] + 1), (acc[1] + +curr.fee_msat)] : acc), [0, 0]); - const weekly_sum = history.forwarding_events.reduce((acc, curr) => ((curr.timestamp >= week_start_time) ? [(acc[0] + 1), (acc[1] + +curr.fee_msat)] : acc), [0, 0]); - const monthly_sum = history.forwarding_events.reduce((acc, curr) => [(acc[0] + 1), (acc[1] + +curr.fee_msat)], [0, 0]); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Daily Sum (Transactions, Fee)', data: daily_sum }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Weekly Sum (Transactions, Fee)', data: weekly_sum }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Monthly Sum (Transactions, Fee)', data: monthly_sum }); - body.daily_tx_count = daily_sum[0]; - body.weekly_tx_count = weekly_sum[0]; - body.monthly_tx_count = monthly_sum[0]; - body.day_fee_sum = (daily_sum[1] / 1000).toFixed(2); - body.week_fee_sum = (weekly_sum[1] / 1000).toFixed(2); - body.month_fee_sum = (monthly_sum[1] / 1000).toFixed(2); - body.forwarding_events_history = history; - if (history.error) { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Fees', msg: 'Fetch Forwarding Events Error', error: history.error }); - } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fees Received', data: body }); - res.status(200).json(body); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Fee Received', + data: body + }); + const today = new Date(Date.now()); + const start_date = new Date(today.getFullYear(), today.getMonth(), 1, 0, 0, 0); + const current_time = Math.round(today.getTime() / 1000); + const month_start_time = Math.round(start_date.getTime() / 1000); + const week_start_time = current_time - 604800; + const day_start_time = current_time - 86400; + return getAllForwardingEvents(req, month_start_time, current_time, 0, 'fees', (history) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Forwarding History Received', + data: history + }); + const daily_sum = history.forwarding_events.reduce( + (acc, curr) => (curr.timestamp >= day_start_time ? [acc[0] + 1, acc[1] + +curr.fee_msat] : acc), + [0, 0] + ); + const weekly_sum = history.forwarding_events.reduce( + (acc, curr) => (curr.timestamp >= week_start_time ? [acc[0] + 1, acc[1] + +curr.fee_msat] : acc), + [0, 0] + ); + const monthly_sum = history.forwarding_events.reduce( + (acc, curr) => [acc[0] + 1, acc[1] + +curr.fee_msat], + [0, 0] + ); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Daily Sum (Transactions, Fee)', + data: daily_sum + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Weekly Sum (Transactions, Fee)', + data: weekly_sum + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Fees', + msg: 'Monthly Sum (Transactions, Fee)', + data: monthly_sum + }); + body.daily_tx_count = daily_sum[0]; + body.weekly_tx_count = weekly_sum[0]; + body.monthly_tx_count = monthly_sum[0]; + body.day_fee_sum = (daily_sum[1] / 1000).toFixed(2); + body.week_fee_sum = (weekly_sum[1] / 1000).toFixed(2); + body.month_fee_sum = (monthly_sum[1] / 1000).toFixed(2); + body.forwarding_events_history = history; + if (history.error) { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Fees', + msg: 'Fetch Forwarding Events Error', + error: history.error + }); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Fees', + msg: 'Fees Received', + data: body + }); + res.status(200).json(body); + }); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Fees', 'Get Forwarding Events Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Fees', 'Get Forwarding Events Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; diff --git a/server/controllers/lnd/getInfo.ts b/server/controllers/lnd/getInfo.ts index f40ae8e20..84bc1843d 100644 --- a/server/controllers/lnd/getInfo.ts +++ b/server/controllers/lnd/getInfo.ts @@ -11,17 +11,40 @@ const lndWsClient: LNDWebSocketClient = LNDWSClient; const databaseService: DatabaseService = Database; export const getInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting LND Node Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Getting LND Node Information..' + }); common.logEnvVariables(req); common.setOptions(req); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo'; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from LND server url ' + options.url }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Selected Node ' + req.session.selectedNode.ln_node + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Calling Info from LND server url ' + options.url + }); if (!options.headers || !options.headers['Grpc-Metadata-macaroon']) { - const errMsg = 'LND Get info failed due to bad or missing macaroon! Please check RTL-Config.json to verify the setup!'; - const err = common.handleError({ statusCode: 502, message: 'Bad or Missing Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode); + const errMsg = + 'LND Get info failed due to bad or missing macaroon! Please check RTL-Config.json to verify the setup!'; + const err = common.handleError( + { statusCode: 502, message: 'Bad or Missing Macaroon', error: errMsg }, + 'GetInfo', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { common.nodes.map((node: any) => { @@ -30,23 +53,33 @@ export const getInfo = (req, res, next) => { } return node; }); - return request(options).then((body) => { - const body_str = (!body) ? '' : JSON.stringify(body); - const search_idx = (!body) ? -1 : body_str.search('Not Found'); - if (!body || search_idx > -1 || body.error) { - if (body && !body.error) { body.error = 'Error From Server!'; } - const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode); + return request(options) + .then((body) => { + const body_str = !body ? '' : JSON.stringify(body); + const search_idx = !body ? -1 : body_str.search('Not Found'); + if (!body || search_idx > -1 || body.error) { + if (body && !body.error) { + body.error = 'Error From Server!'; + } + const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + } else { + req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; + lndWsClient.updateSelectedNode(req.session.selectedNode); + databaseService.loadDatabase(req.session.selectedNode); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'GetInfo', + msg: 'Node Information Received', + data: body + }); + return res.status(200).json(body); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); - } else { - req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; - lndWsClient.updateSelectedNode(req.session.selectedNode); - databaseService.loadDatabase(req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); - return res.status(200).json(body); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + }); } }; diff --git a/server/controllers/lnd/graph.ts b/server/controllers/lnd/graph.ts index 2cf4fa71b..7b72b3dce 100644 --- a/server/controllers/lnd/graph.ts +++ b/server/controllers/lnd/graph.ts @@ -8,145 +8,284 @@ const common: CommonService = Common; export const getAliasFromPubkey = (selNode: CommonSelectedNode, pubkey) => { options.url = selNode.ln_server_url + '/v1/graph/node/' + pubkey; - return request(options).then((res) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Graph', msg: 'Alias Received', data: res.node.alias }); - return res.node.alias; - }). - catch((err) => pubkey.substring(0, 17) + '...'); + return request(options) + .then((res) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Graph', + msg: 'Alias Received', + data: res.node.alias + }); + return res.node.alias; + }) + .catch((err) => pubkey.substring(0, 17) + '...'); }; export const getDescribeGraph = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Network Graph..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Network Graph..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/graph'; - request.get(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Network Graph Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Graph', 'Describe Graph Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .get(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Network Graph Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Graph', 'Describe Graph Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getGraphInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Graph Information..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/graph/info'; - request.get(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Information Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Graph', 'Graph Information Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .get(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Information Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Graph', 'Graph Information Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getGraphNode = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Node Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Graph Node Information..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/graph/node/' + req.params.pubKey; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Node Information Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Graph', 'Get Node Info Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Node Information Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Graph', 'Get Node Info Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getGraphEdge = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Edge Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Graph Edge Information..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/graph/edge/' + req.params.chanid; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Edge Information Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Graph', 'Get Edge Info Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Edge Information Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Graph', 'Get Edge Info Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getQueryRoutes = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Routes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Graph Routes..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/graph/routes/' + req.params.destPubkey + '/' + req.params.amount; + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } + options.url = + req.session.selectedNode.ln_server_url + '/v1/graph/routes/' + req.params.destPubkey + '/' + req.params.amount; if (req.query.outgoing_chan_id) { options.url = options.url + '?outgoing_chan_id=' + req.query.outgoing_chan_id; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes URL', data: options.url }); - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes Received', data: body }); - if (body.routes && body.routes.length && body.routes.length > 0 && body.routes[0].hops && body.routes[0].hops.length && body.routes[0].hops.length > 0) { - return Promise.all(body.routes[0].hops.map((hop) => getAliasFromPubkey(req.session.selectedNode, hop.pub_key))). - then((values) => { - body.routes[0].hops.map((hop, i) => { - hop.hop_sequence = i + 1; - hop.pubkey_alias = values[i]; - return hop; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Graph', + msg: 'Query Routes URL', + data: options.url + }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Graph', + msg: 'Query Routes Received', + data: body + }); + if ( + body.routes && + body.routes.length && + body.routes.length > 0 && + body.routes[0].hops && + body.routes[0].hops.length && + body.routes[0].hops.length > 0 + ) { + return Promise.all(body.routes[0].hops.map((hop) => getAliasFromPubkey(req.session.selectedNode, hop.pub_key))) + .then((values) => { + body.routes[0].hops.map((hop, i) => { + hop.hop_sequence = i + 1; + hop.pubkey_alias = values[i]; + return hop; + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Routes with Alias Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes with Alias Received', data: body }); - res.status(200).json(body); - }). - catch((errRes) => { - const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + } else { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Graph Routes Received', + data: body }); - } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes Received', data: body }); - return res.status(200).json(body); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + return res.status(200).json(body); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getRemoteFeePolicy = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Remote Fee Policy..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Getting Remote Fee Policy..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/graph/edge/' + req.params.chanid; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Edge Info Received', data: body }); - let remoteNodeFee = {}; - if (body.node1_pub === req.params.localPubkey) { - remoteNodeFee = { - time_lock_delta: body.node2_policy.time_lock_delta, - fee_base_msat: body.node2_policy.fee_base_msat, - fee_rate_milli_msat: body.node2_policy.fee_rate_milli_msat - }; - } else if (body.node2_pub === req.params.localPubkey) { - remoteNodeFee = { - time_lock_delta: body.node1_policy.time_lock_delta, - fee_base_msat: body.node1_policy.fee_base_msat, - fee_rate_milli_msat: body.node1_policy.fee_rate_milli_msat - }; - } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Remote Fee Policy Received', data: remoteNodeFee }); - res.status(200).json(remoteNodeFee); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Graph', 'Remote Fee Policy Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Graph', + msg: 'Edge Info Received', + data: body + }); + let remoteNodeFee = {}; + if (body.node1_pub === req.params.localPubkey) { + remoteNodeFee = { + time_lock_delta: body.node2_policy.time_lock_delta, + fee_base_msat: body.node2_policy.fee_base_msat, + fee_rate_milli_msat: body.node2_policy.fee_rate_milli_msat + }; + } else if (body.node2_pub === req.params.localPubkey) { + remoteNodeFee = { + time_lock_delta: body.node1_policy.time_lock_delta, + fee_base_msat: body.node1_policy.fee_base_msat, + fee_rate_milli_msat: body.node1_policy.fee_rate_milli_msat + }; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Remote Fee Policy Received', + data: remoteNodeFee + }); + res.status(200).json(remoteNodeFee); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Graph', 'Remote Fee Policy Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getAliasesForPubkeys = (req, res, next) => { options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } if (req.query.pubkeys) { const pubkeyArr = req.query.pubkeys.split(','); - return Promise.all(pubkeyArr.map((pubkey) => getAliasFromPubkey(req.session.selectedNode, pubkey))). - then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Node Alias', data: values }); + return Promise.all(pubkeyArr.map((pubkey) => getAliasFromPubkey(req.session.selectedNode, pubkey))) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Graph', + msg: 'Node Alias', + data: values + }); res.status(200).json(values); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Graph', 'Get Aliases for Pubkeys Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); diff --git a/server/controllers/lnd/invoices.ts b/server/controllers/lnd/invoices.ts index bcb2f92c7..7c9676fd6 100644 --- a/server/controllers/lnd/invoices.ts +++ b/server/controllers/lnd/invoices.ts @@ -9,55 +9,103 @@ const common: CommonService = Common; const lndWsClient: LNDWebSocketClient = LNDWSClient; export const invoiceLookup = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Getting Invoice Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Getting Invoice Information..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v2/invoices/lookup'; if (req.query.payment_addr) { options.url = options.url + '?payment_addr=' + req.query.payment_addr; } else { options.url = options.url + '?payment_hash=' + req.query.payment_hash; } - request(options).then((body) => { - body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : ''; - body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : ''; - body.description_hash = body.description_hash ? Buffer.from(body.description_hash, 'base64').toString('hex') : null; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Information Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Invoices', 'Invoice Lookup Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : ''; + body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : ''; + body.description_hash = body.description_hash + ? Buffer.from(body.description_hash, 'base64').toString('hex') + : null; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Information Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Invoices', 'Invoice Lookup Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const listInvoices = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Getting List Invoices..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Getting List Invoices..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=' + req.query.num_max_invoices + '&index_offset=' + req.query.index_offset + - '&reversed=' + req.query.reversed; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); - if (body.invoices && body.invoices.length > 0) { - body.invoices.forEach((invoice) => { - invoice.r_preimage = invoice.r_preimage ? Buffer.from(invoice.r_preimage, 'base64').toString('hex') : ''; - invoice.r_hash = invoice.r_hash ? Buffer.from(invoice.r_hash, 'base64').toString('hex') : ''; - invoice.description_hash = invoice.description_hash ? Buffer.from(invoice.description_hash, 'base64').toString('hex') : null; + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } + options.url = + req.session.selectedNode.ln_server_url + + '/v1/invoices?num_max_invoices=' + + req.query.num_max_invoices + + '&index_offset=' + + req.query.index_offset + + '&reversed=' + + req.query.reversed; + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Invoice', + msg: 'Invoices List Received', + data: body }); - body.invoices = common.sortDescByKey(body.invoices, 'creation_date'); - } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Sorted Invoices List Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + if (body.invoices && body.invoices.length > 0) { + body.invoices.forEach((invoice) => { + invoice.r_preimage = invoice.r_preimage ? Buffer.from(invoice.r_preimage, 'base64').toString('hex') : ''; + invoice.r_hash = invoice.r_hash ? Buffer.from(invoice.r_hash, 'base64').toString('hex') : ''; + invoice.description_hash = invoice.description_hash + ? Buffer.from(invoice.description_hash, 'base64').toString('hex') + : null; + }); + body.invoices = common.sortDescByKey(body.invoices, 'creation_date'); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Sorted Invoices List Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const addInvoice = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Adding Invoice..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/invoices'; options.form = { memo: req.body.memo, @@ -70,22 +118,44 @@ export const addInvoice = (req, res, next) => { options.form.value = req.body.amount; } options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Added', data: body }); - try { - if (body.r_hash) { - lndWsClient.subscribeToInvoice(options, req.session.selectedNode, body.r_hash); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Invoice', + msg: 'Invoice Added', + data: body + }); + try { + if (body.r_hash) { + lndWsClient.subscribeToInvoice(options, req.session.selectedNode, body.r_hash); + } + } catch (errRes) { + const err = common.handleError( + errRes, + 'Invoices', + 'Subscribe to Newly Added Invoice Error', + req.session.selectedNode + ); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Invoice', + msg: 'Subscribe to Newly Added Invoice Error', + error: err + }); } - } catch (errRes) { - const err = common.handleError(errRes, 'Invoices', 'Subscribe to Newly Added Invoice Error', req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Invoice', msg: 'Subscribe to Newly Added Invoice Error', error: err }); - } - body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : ''; - body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : ''; - body.description_hash = body.description_hash ? Buffer.from(body.description_hash, 'base64').toString('hex') : null; - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Invoices', 'Add Invoice Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : ''; + body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : ''; + body.description_hash = body.description_hash + ? Buffer.from(body.description_hash, 'base64').toString('hex') + : null; + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Invoices', 'Add Invoice Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/lnd/message.ts b/server/controllers/lnd/message.ts index c859b9def..7bbfa60df 100644 --- a/server/controllers/lnd/message.ts +++ b/server/controllers/lnd/message.ts @@ -8,34 +8,61 @@ const common: CommonService = Common; export const signMessage = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Signing Message..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/signmessage'; options.form = JSON.stringify({ msg: Buffer.from(req.body.message).toString('base64') }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Signed', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Messages', 'Sign Message Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Message Signed', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Messages', 'Sign Message Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const verifyMessage = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Verifying Message..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Verifying Message..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/verifymessage'; options.form = JSON.stringify({ msg: Buffer.from(req.body.message).toString('base64'), signature: req.body.signature }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Messages', 'Verify Message Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Message', + msg: 'Message Verified', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Messages', 'Verify Message Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/lnd/newAddress.ts b/server/controllers/lnd/newAddress.ts index af53f9e2a..df7fb6e78 100644 --- a/server/controllers/lnd/newAddress.ts +++ b/server/controllers/lnd/newAddress.ts @@ -6,15 +6,30 @@ const logger: LoggerService = Logger; const common: CommonService = Common; export const getNewAddress = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'NewAddress', msg: 'Getting New Address..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'NewAddress', + msg: 'Getting New Address..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/newaddress?type=' + req.query.type; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'NewAddress', msg: 'New Address Generated', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'NewAddress', 'New Address Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'NewAddress', + msg: 'New Address Generated', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'NewAddress', 'New Address Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/lnd/payments.ts b/server/controllers/lnd/payments.ts index bc57de8a4..46b67b665 100644 --- a/server/controllers/lnd/payments.ts +++ b/server/controllers/lnd/payments.ts @@ -9,93 +9,203 @@ const common: CommonService = Common; export const decodePaymentFromPaymentRequest = (selNode: CommonSelectedNode, payment) => { options.url = selNode.ln_server_url + '/v1/payreq/' + payment; - return request(options).then((res) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'PayReq', msg: 'Description Received', data: res.description }); - return res; - }).catch((err) => { }); + return request(options) + .then((res) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'PayReq', + msg: 'Description Received', + data: res.description + }); + return res; + }) + .catch((err) => {}); }; export const decodePayment = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Decoding Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Decoding Payment..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/payreq/' + req.params.payRequest; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Payment Decoded', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'PayRequest', 'Decode Payment Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Payment Decoded', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'PayRequest', 'Decode Payment Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const decodePayments = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Decoding Payments List..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Decoding Payments List..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } if (req.body.payments) { const paymentsArr = req.body.payments.split(','); - return Promise.all(paymentsArr.map((payment) => decodePaymentFromPaymentRequest(req.session.selectedNode, payment))). - then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Payment List Decoded', data: values }); + return Promise.all(paymentsArr.map((payment) => decodePaymentFromPaymentRequest(req.session.selectedNode, payment))) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Payment List Decoded', + data: values + }); res.status(200).json(values); - }). - catch((errRes) => { + }) + .catch((errRes) => { const err = common.handleError(errRes, 'PayRequest', 'Decode Payments Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Empty Payment List Decoded' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'PayRequest', + msg: 'Empty Payment List Decoded' + }); return res.status(200).json([]); } }; export const getPayments = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting Payments List..' }); - options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } - options.url = req.session.selectedNode.ln_server_url + '/v1/payments?max_payments=' + req.query.max_payments + '&index_offset=' + req.query.index_offset + '&reversed=' + req.query.reversed; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body }); - if (body.payments && body.payments.length > 0) { - body.payments = common.sortDescByKey(body.payments, 'creation_date'); - } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sorted Payments List Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Getting Payments List..' }); + options = common.getOptions(req); + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } + options.url = + req.session.selectedNode.ln_server_url + + '/v1/payments?max_payments=' + + req.query.max_payments + + '&index_offset=' + + req.query.index_offset + + '&reversed=' + + req.query.reversed; + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'Payment List Received', + data: body + }); + if (body.payments && body.payments.length > 0) { + body.payments = common.sortDescByKey(body.payments, 'creation_date'); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Sorted Payments List Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getAllLightningTransactions = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting All Lightning Transactions..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Getting All Lightning Transactions..' + }); const options1 = JSON.parse(JSON.stringify(common.getOptions(req))); const options2 = JSON.parse(JSON.stringify(common.getOptions(req))); // options1.url = req.session.selectedNode.ln_server_url + '/v1/payments?max_payments=100000&index_offset=0&reversed=true'; - options2.url = req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=100000&index_offset=0&reversed=true'; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'All Payments Options', data: options1 }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'All Invoices Options', data: options2 }); - // return Promise.all([request(options1), request(options2)]).then((values) => { - return Promise.all([{ payments: [] }, request(options2)]).then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'All Lightning Transactions Received', data: ({ totalPayments: values[0].payments.length || 0, totalInvoices: values[1].invoices.length || 0 }) }); - res.status(200).json({ listPaymentsAll: values[0], listInvoicesAll: values[1] }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Payments', 'All Lightning Transactions Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + options2.url = + req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=100000&index_offset=0&reversed=true'; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'All Payments Options', + data: options1 }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Payments', + msg: 'All Invoices Options', + data: options2 + }); + // return Promise.all([request(options1), request(options2)]).then((values) => { + return Promise.all([{ payments: [] }, request(options2)]) + .then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'All Lightning Transactions Received', + data: { totalPayments: values[0].payments.length || 0, totalInvoices: values[1].invoices.length || 0 } + }); + res.status(200).json({ listPaymentsAll: values[0], listInvoicesAll: values[1] }); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Payments', 'All Lightning Transactions Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const paymentLookup = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Looking up Payment..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Looking up Payment..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v2/router/track/' + req.params.paymentHash; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Information Received for ' + req.params.paymentHash, data: body }); - res.status(200).json(body.result || body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Payments', 'Payment Lookup Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Payments', + msg: 'Payment Information Received for ' + req.params.paymentHash, + data: body + }); + res.status(200).json(body.result || body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Payments', 'Payment Lookup Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/lnd/peers.ts b/server/controllers/lnd/peers.ts index 274e39495..1d730b896 100644 --- a/server/controllers/lnd/peers.ts +++ b/server/controllers/lnd/peers.ts @@ -8,84 +8,152 @@ const common: CommonService = Common; export const getAliasForPeers = (selNode: CommonSelectedNode, peer) => { options.url = selNode.ln_server_url + '/v1/graph/node/' + peer.pub_key; - return request(options).then((aliasBody) => { - logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Peers', msg: 'Alias Received', data: aliasBody.node.alias }); - peer.alias = aliasBody.node.alias; - return aliasBody.node.alias; - }).catch((err) => { - peer.alias = peer.pub_key.slice(0, 10) + '...' + peer.pub_key.slice(-10); - return peer.pub_key; - }); + return request(options) + .then((aliasBody) => { + logger.log({ + selectedNode: selNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Alias Received', + data: aliasBody.node.alias + }); + peer.alias = aliasBody.node.alias; + return aliasBody.node.alias; + }) + .catch((err) => { + peer.alias = peer.pub_key.slice(0, 10) + '...' + peer.pub_key.slice(-10); + return peer.pub_key; + }); }; export const getPeers = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Getting Peers..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/peers'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body }); - const peers = !body.peers ? [] : body.peers; - return Promise.all(peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers with Alias before Sort', data: body }); - if (body.peers) { - body.peers = common.sortDescByStrKey(body.peers, 'alias'); - } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body.peers }); - res.status(200).json(body.peers); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peers List Received', + data: body + }); + const peers = !body.peers ? [] : body.peers; + return Promise.all(peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peers with Alias before Sort', + data: body + }); + if (body.peers) { + body.peers = common.sortDescByStrKey(body.peers, 'alias'); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Sorted Peers List Received', + data: body.peers + }); + res.status(200).json(body.peers); + }); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; export const postPeer = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Connecting Peer..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/peers'; options.form = JSON.stringify({ addr: { host: req.body.host, pubkey: req.body.pubkey }, perm: req.body.perm }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: body }); - options.url = req.session.selectedNode.ln_server_url + '/v1/peers'; - request(options).then((body) => { - const peers = (!body.peers) ? [] : body.peers; - return Promise.all(peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))).then((values) => { - if (body.peers) { - body.peers = common.sortDescByStrKey(body.peers, 'alias'); - body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: body }); - } - res.status(201).json(body.peers); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Peers', + msg: 'Peer Connected', + data: body }); - }).catch((errRes) => { + options.url = req.session.selectedNode.ln_server_url + '/v1/peers'; + request(options) + .then((body) => { + const peers = !body.peers ? [] : body.peers; + return Promise.all(peers.map((peer) => getAliasForPeers(req.session.selectedNode, peer))) + .then((values) => { + if (body.peers) { + body.peers = common.sortDescByStrKey(body.peers, 'alias'); + body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peers List after Connect Received', + data: body + }); + } + res.status(201).json(body.peers); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; export const deletePeer = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Disconnecting Peer..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/peers/' + req.params.peerPubKey; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnect Pubkey', data: req.params.peerPubKey }); - request.delete(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconneted', data: body }); - res.status(204).json({}); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peer Disconnect Pubkey', + data: req.params.peerPubKey }); + request + .delete(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Peers', + msg: 'Peer Disconneted', + data: body + }); + res.status(204).json({}); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/lnd/switch.ts b/server/controllers/lnd/switch.ts index 04477887e..2b175ad68 100644 --- a/server/controllers/lnd/switch.ts +++ b/server/controllers/lnd/switch.ts @@ -4,7 +4,10 @@ import { Common, CommonService } from '../../utils/common.js'; let options = null; const logger: LoggerService = Logger; const common: CommonService = Common; -const responseData = { switch: { forwarding_events: [], last_offset_index: 0 }, fees: { forwarding_events: [], last_offset_index: 0 } }; +const responseData = { + switch: { forwarding_events: [], last_offset_index: 0 }, + fees: { forwarding_events: [], last_offset_index: 0 } +}; const num_max_events = 100; export const forwardingHistory = (req, res, next) => { @@ -18,37 +21,72 @@ export const forwardingHistory = (req, res, next) => { }; export const getAllForwardingEvents = (req, start, end, offset, caller, callback) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Switch', msg: 'Getting Forwarding Events..' }); - if (offset === 0) { responseData[caller] = { forwarding_events: [], last_offset_index: 0 }; } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Switch', + msg: 'Getting Forwarding Events..' + }); + if (offset === 0) { + responseData[caller] = { forwarding_events: [], last_offset_index: 0 }; + } if (!req.session.selectedNode) { - const err = common.handleError({ message: 'Session Expired after a day\'s inactivity.', statusCode: 401 }, 'Balance', 'Get Balance Error', req.session.selectedNode); + const err = common.handleError( + { message: "Session Expired after a day's inactivity.", statusCode: 401 }, + 'Balance', + 'Get Balance Error', + req.session.selectedNode + ); return callback({ message: err.message, error: err.error, statusCode: err.statusCode }); - } options = common.getOptions(req); + } + options = common.getOptions(req); options.url = req.session.selectedNode.ln_server_url + '/v1/switch'; options.form = {}; - if (start) { options.form.start_time = start; } - if (end) { options.form.end_time = end; } + if (start) { + options.form.start_time = start; + } + if (end) { + options.form.end_time = end; + } options.form.num_max_events = num_max_events; options.form.index_offset = offset; options.form = JSON.stringify(options.form); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Switch', msg: 'Forwarding Events Params', data: options.form }); - return request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Switch', msg: 'Forwarding Events Received', data: body }); - if (body.forwarding_events) { - responseData[caller].forwarding_events.push(...body.forwarding_events); - responseData[caller].last_offset_index = body.last_offset_index ? body.last_offset_index : 0; - } - if (!body.last_offset_index || body.last_offset_index < offset + num_max_events) { - responseData[caller].last_offset_index = body.last_offset_index ? body.last_offset_index : 0; - if (responseData[caller].forwarding_events) { - responseData[caller].forwarding_events = common.sortDescByKey(responseData[caller].forwarding_events, 'timestamp'); - } - return callback(responseData[caller]); - } else { - return getAllForwardingEvents(req, start, end, offset + num_max_events, caller, callback); - } - }).catch((errRes) => { - const err = common.handleError(errRes, 'Switch', 'Get All Forwarding Events Error', req.session.selectedNode); - return callback({ message: err.message, error: err.error, statusCode: err.statusCode }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Switch', + msg: 'Forwarding Events Params', + data: options.form }); + return request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Switch', + msg: 'Forwarding Events Received', + data: body + }); + if (body.forwarding_events) { + responseData[caller].forwarding_events.push(...body.forwarding_events); + responseData[caller].last_offset_index = body.last_offset_index ? body.last_offset_index : 0; + } + if (!body.last_offset_index || body.last_offset_index < offset + num_max_events) { + responseData[caller].last_offset_index = body.last_offset_index ? body.last_offset_index : 0; + if (responseData[caller].forwarding_events) { + responseData[caller].forwarding_events = common.sortDescByKey( + responseData[caller].forwarding_events, + 'timestamp' + ); + } + return callback(responseData[caller]); + } else { + return getAllForwardingEvents(req, start, end, offset + num_max_events, caller, callback); + } + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Switch', 'Get All Forwarding Events Error', req.session.selectedNode); + return callback({ message: err.message, error: err.error, statusCode: err.statusCode }); + }); }; diff --git a/server/controllers/lnd/transactions.ts b/server/controllers/lnd/transactions.ts index 65f400d97..a5998aa32 100644 --- a/server/controllers/lnd/transactions.ts +++ b/server/controllers/lnd/transactions.ts @@ -6,27 +6,55 @@ const logger: LoggerService = Logger; const common: CommonService = Common; export const getTransactions = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Getting Transactions..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Transactions', + msg: 'Getting Transactions..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/transactions'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Transactions', msg: 'Transactions List Received', data: body }); - if (body.transactions && body.transactions.length > 0) { - body.transactions = common.sortDescByKey(body.transactions, 'time_stamp'); - } - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Sorted Transactions List Received', data: body.transactions }); - res.status(200).json(body.transactions); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Transactions', 'List Transactions Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Transactions', + msg: 'Transactions List Received', + data: body + }); + if (body.transactions && body.transactions.length > 0) { + body.transactions = common.sortDescByKey(body.transactions, 'time_stamp'); + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Transactions', + msg: 'Sorted Transactions List Received', + data: body.transactions + }); + res.status(200).json(body.transactions); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Transactions', 'List Transactions Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const postTransactions = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Sending Transaction..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Transactions', + msg: 'Sending Transaction..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v1/transactions'; options.form = { amount: req.body.amount, @@ -38,11 +66,20 @@ export const postTransactions = (req, res, next) => { options.form.send_all = req.body.sendAll; } options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Transaction Sent', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Transactions', 'Send Transaction Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Transactions', + msg: 'Transaction Sent', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Transactions', 'Send Transaction Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/lnd/wallet.ts b/server/controllers/lnd/wallet.ts index 063a2906c..8a46a3183 100644 --- a/server/controllers/lnd/wallet.ts +++ b/server/controllers/lnd/wallet.ts @@ -9,35 +9,60 @@ const common: CommonService = Common; export const genSeed = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Generating Seed..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } if (req.params.passphrase) { - options.url = req.session.selectedNode.ln_server_url + '/v1/genseed?aezeed_passphrase=' + Buffer.from(atob(req.params.passphrase)).toString('base64'); + options.url = + req.session.selectedNode.ln_server_url + + '/v1/genseed?aezeed_passphrase=' + + Buffer.from(atob(req.params.passphrase)).toString('base64'); } else { options.url = req.session.selectedNode.ln_server_url + '/v1/genseed'; } - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Seed Generated', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Wallet', 'Gen Seed Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Seed Generated', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Wallet', 'Gen Seed Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const operateWallet = (req, res, next) => { let err_message = ''; options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.method = 'POST'; if (!req.params.operation || req.params.operation === 'unlockwallet') { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Unlocking Wallet..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Unlocking Wallet..' + }); options.url = req.session.selectedNode.ln_server_url + '/v1/unlockwallet'; options.form = JSON.stringify({ wallet_password: Buffer.from(atob(req.body.wallet_password)).toString('base64') }); err_message = 'Unlocking wallet failed! Verify that lnd is running and the wallet is locked!'; } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Initializing Wallet..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Initializing Wallet..' + }); options.url = req.session.selectedNode.ln_server_url + '/v1/initwallet'; if (req.body.aezeed_passphrase && req.body.aezeed_passphrase !== '') { options.form = JSON.stringify({ @@ -53,35 +78,69 @@ export const operateWallet = (req, res, next) => { } err_message = 'Initializing wallet failed!'; } - request(options).then((body) => { - const body_str = (!body) ? '' : JSON.stringify(body); - const search_idx = (!body) ? -1 : body_str.search('Not Found'); - if (!body) { - const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: err_message }, 'Wallet', err_message, req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.error, error: err.error }); - } else if (search_idx > -1) { - const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: err_message }, 'Wallet', err_message, req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.error, error: err.error }); - } else if (body.error) { - if ((body.code === 1 && body.error === 'context canceled') || (body.code === 14 && body.error === 'transport is closing')) { + request(options) + .then((body) => { + const body_str = !body ? '' : JSON.stringify(body); + const search_idx = !body ? -1 : body_str.search('Not Found'); + if (!body) { + const err = common.handleError( + { statusCode: 500, message: 'Wallet Error', error: err_message }, + 'Wallet', + err_message, + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.error, error: err.error }); + } else if (search_idx > -1) { + const err = common.handleError( + { statusCode: 500, message: 'Wallet Error', error: err_message }, + 'Wallet', + err_message, + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.error, error: err.error }); + } else if (body.error) { + if ( + (body.code === 1 && body.error === 'context canceled') || + (body.code === 14 && body.error === 'transport is closing') + ) { + res.status(201).json('Successful'); + } else { + const errMsg = + body.error && typeof body.error === 'object' + ? JSON.stringify(body.error) + : body.error && typeof body.error === 'string' + ? body.error + : err_message; + const err = common.handleError( + { statusCode: 500, message: 'Wallet Error', error: errMsg }, + 'Wallet', + errMsg, + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.error, error: err.error }); + } + } else { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Wallet Unlocked/Initialized', + data: body + }); + res.status(201).json('Successful'); + } + }) + .catch((errRes) => { + if ( + (errRes.error.code === 1 && errRes.error.error === 'context canceled') || + (errRes.error.code === 14 && errRes.error.error === 'transport is closing') + ) { res.status(201).json('Successful'); } else { - const errMsg = (body.error && typeof body.error === 'object') ? JSON.stringify(body.error) : (body.error && typeof body.error === 'string') ? body.error : err_message; - const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: errMsg }, 'Wallet', errMsg, req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.error, error: err.error }); + const err = common.handleError(errRes, 'Wallet', err_message, req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); } - } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Wallet Unlocked/Initialized', data: body }); - res.status(201).json('Successful'); - } - }).catch((errRes) => { - if ((errRes.error.code === 1 && errRes.error.error === 'context canceled') || (errRes.error.code === 14 && errRes.error.error === 'transport is closing')) { - res.status(201).json('Successful'); - } else { - const err = common.handleError(errRes, 'Wallet', err_message, req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - } - }); + }); }; export const updateSelNodeOptions = (req, res, next) => { @@ -92,26 +151,39 @@ export const updateSelNodeOptions = (req, res, next) => { export const getUTXOs = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Getting UTXOs..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/utxos'; if (common.isVersionCompatible(req.session.selectedNode.ln_version, '0.14.0')) { options.form = JSON.stringify({ max_confs: req.query.max_confs }); } else { options.url = options.url + '?max_confs=' + req.query.max_confs; } - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXOs List Received', data: body }); - res.status(200).json(body.utxos ? body.utxos : []); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Wallet', 'List UTXOs Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'UTXOs List Received', + data: body + }); + res.status(200).json(body.utxos ? body.utxos : []); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Wallet', 'List UTXOs Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const bumpFee = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Bumping Fee..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/bumpfee'; options.form = {}; options.form.outpoint = { @@ -124,39 +196,72 @@ export const bumpFee = (req, res, next) => { options.form.sat_per_byte = req.body.satPerByte; } options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Fee Bumped', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Wallet', 'Bump Fee Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Fee Bumped', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Wallet', 'Bump Fee Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const labelTransaction = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Labelling Transaction..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Labelling Transaction..' + }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/tx/label'; options.form = {}; options.form.txid = req.body.txid; options.form.label = req.body.label; options.form.overwrite = req.body.overwrite; options.form = JSON.stringify(options.form); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'Label Transaction Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Transaction Labelled', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Wallet', 'Label Transaction Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Wallet', + msg: 'Label Transaction Options', + data: options.form }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'Transaction Labelled', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Wallet', 'Label Transaction Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const leaseUTXO = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Leasing UTXO..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/utxos/lease'; options.form = {}; options.form.id = req.body.txid; @@ -165,20 +270,37 @@ export const leaseUTXO = (req, res, next) => { output_index: req.body.outputIndex }; options.form = JSON.stringify(options.form); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'UTXO Lease Options', data: options.form }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXO Leased', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Wallet', 'Lease UTXO Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Wallet', + msg: 'UTXO Lease Options', + data: options.form }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'UTXO Leased', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Wallet', 'Lease UTXO Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const releaseUTXO = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Releasing UTXO..' }); options = common.getOptions(req); - if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } + if (options.error) { + return res.status(options.statusCode).json({ message: options.message, error: options.error }); + } options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/utxos/release'; options.form = {}; options.form.id = req.body.txid; @@ -187,11 +309,20 @@ export const releaseUTXO = (req, res, next) => { output_index: req.body.outputIndex }; options.form = JSON.stringify(options.form); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXO Released', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Wallet', 'Release UTXO Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Wallet', + msg: 'UTXO Released', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Wallet', 'Release UTXO Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/lnd/webSocketClient.ts b/server/controllers/lnd/webSocketClient.ts index efdadf256..d267a99ed 100644 --- a/server/controllers/lnd/webSocketClient.ts +++ b/server/controllers/lnd/webSocketClient.ts @@ -8,7 +8,6 @@ import { WSServer } from '../../utils/webSocketServer.js'; import { CommonSelectedNode } from '../../models/config.model.js'; export class LNDWebSocketClient { - public logger: LoggerService = Logger; public common: CommonService = Common; public wsServer = WSServer; @@ -36,73 +35,154 @@ export class LNDWebSocketClient { }; public fetchUnpaidInvoices = (selectedNode: CommonSelectedNode) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Getting Unpaid Invoices..' }); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Getting Unpaid Invoices..' + }); const options = this.setOptionsForSelNode(selectedNode); options.url = selectedNode.ln_server_url + '/v1/invoices?pending_only=true'; - return request(options).then((body) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Unpaid Invoices Received', data: body }); - if (body.invoices && body.invoices.length > 0) { - body.invoices.forEach((invoice) => { - if (invoice.state === 'OPEN') { - this.subscribeToInvoice(options, selectedNode, invoice.r_hash); - } + return request(options) + .then((body) => { + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Unpaid Invoices Received', + data: body }); - } - return null; - }).catch((errRes) => { - const err = this.common.handleError(errRes, 'WebSocketClient', 'Pending Invoices Error', selectedNode); - return ({ message: err.message, error: err.error }); - }); + if (body.invoices && body.invoices.length > 0) { + body.invoices.forEach((invoice) => { + if (invoice.state === 'OPEN') { + this.subscribeToInvoice(options, selectedNode, invoice.r_hash); + } + }); + } + return null; + }) + .catch((errRes) => { + const err = this.common.handleError(errRes, 'WebSocketClient', 'Pending Invoices Error', selectedNode); + return { message: err.message, error: err.error }; + }); }; public subscribeToInvoice = (options: any, selectedNode: CommonSelectedNode, rHash: string) => { rHash = rHash.replace(/\+/g, '-').replace(/[/]/g, '_'); - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Subscribing to Invoice ' + rHash + ' ..' }); - options.url = selectedNode.ln_server_url + '/v2/invoices/subscribe/' + rHash; - request(options).then((msg) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Invoice Information Received for ' + rHash }); - if (typeof msg === 'string') { - const results = msg.split('\n'); - msg = (results.length && results.length > 1) ? JSON.parse(results[1]) : JSON.parse(msg); - msg.result.r_preimage = msg.result.r_preimage ? Buffer.from(msg.result.r_preimage, 'base64').toString('hex') : ''; - msg.result.r_hash = msg.result.r_hash ? Buffer.from(msg.result.r_hash, 'base64').toString('hex') : ''; - msg.result.description_hash = msg.result.description_hash ? Buffer.from(msg.result.description_hash, 'base64').toString('hex') : null; - } - msg['type'] = 'invoice'; - msg['source'] = 'LND'; - const msgStr = JSON.stringify(msg); - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Invoice Info Received', data: msgStr }); - this.wsServer.sendEventsToAllLNClients(msgStr, selectedNode); - }).catch((errRes) => { - const err = this.common.handleError(errRes, 'Invoices', 'Subscribe to Invoice Error for ' + rHash, selectedNode); - const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message + ' ' + rHash }) : (typeof err === 'object') ? JSON.stringify({ error: err + ' ' + rHash }) : ('{ "error": ' + err + ' ' + rHash + ' }')); - this.wsServer.sendErrorToAllLNClients(errStr, selectedNode); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Subscribing to Invoice ' + rHash + ' ..' }); + options.url = selectedNode.ln_server_url + '/v2/invoices/subscribe/' + rHash; + request(options) + .then((msg) => { + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Invoice Information Received for ' + rHash + }); + if (typeof msg === 'string') { + const results = msg.split('\n'); + msg = results.length && results.length > 1 ? JSON.parse(results[1]) : JSON.parse(msg); + msg.result.r_preimage = msg.result.r_preimage + ? Buffer.from(msg.result.r_preimage, 'base64').toString('hex') + : ''; + msg.result.r_hash = msg.result.r_hash ? Buffer.from(msg.result.r_hash, 'base64').toString('hex') : ''; + msg.result.description_hash = msg.result.description_hash + ? Buffer.from(msg.result.description_hash, 'base64').toString('hex') + : null; + } + msg['type'] = 'invoice'; + msg['source'] = 'LND'; + const msgStr = JSON.stringify(msg); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Invoice Info Received', + data: msgStr + }); + this.wsServer.sendEventsToAllLNClients(msgStr, selectedNode); + }) + .catch((errRes) => { + const err = this.common.handleError( + errRes, + 'Invoices', + 'Subscribe to Invoice Error for ' + rHash, + selectedNode + ); + const errStr = + typeof err === 'object' && err.message + ? JSON.stringify({ error: err.message + ' ' + rHash }) + : typeof err === 'object' + ? JSON.stringify({ error: err + ' ' + rHash }) + : '{ "error": ' + err + ' ' + rHash + ' }'; + this.wsServer.sendErrorToAllLNClients(errStr, selectedNode); + }); }; public subscribeToPayment = (options: any, selectedNode: CommonSelectedNode, paymentHash: string) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Subscribing to Payment ' + paymentHash + ' ..' }); - options.url = selectedNode.ln_server_url + '/v2/router/track/' + paymentHash; - request(options).then((msg) => { - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Payment Information Received for ' + paymentHash }); - msg['type'] = 'payment'; - msg['source'] = 'LND'; - const msgStr = JSON.stringify(msg); - this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Payment Info Received', data: msgStr }); - this.wsServer.sendEventsToAllLNClients(msgStr, selectedNode); - }).catch((errRes) => { - const err = this.common.handleError(errRes, 'Payment', 'Subscribe to Payment Error for ' + paymentHash, selectedNode); - const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message + ' ' + paymentHash }) : (typeof err === 'object') ? JSON.stringify({ error: err + ' ' + paymentHash }) : ('{ "error": ' + err + ' ' + paymentHash + ' }')); - this.wsServer.sendErrorToAllLNClients(errStr, selectedNode); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Subscribing to Payment ' + paymentHash + ' ..' }); + options.url = selectedNode.ln_server_url + '/v2/router/track/' + paymentHash; + request(options) + .then((msg) => { + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Payment Information Received for ' + paymentHash + }); + msg['type'] = 'payment'; + msg['source'] = 'LND'; + const msgStr = JSON.stringify(msg); + this.logger.log({ + selectedNode: selectedNode, + level: 'INFO', + fileName: 'WebSocketClient', + msg: 'Payment Info Received', + data: msgStr + }); + this.wsServer.sendEventsToAllLNClients(msgStr, selectedNode); + }) + .catch((errRes) => { + const err = this.common.handleError( + errRes, + 'Payment', + 'Subscribe to Payment Error for ' + paymentHash, + selectedNode + ); + const errStr = + typeof err === 'object' && err.message + ? JSON.stringify({ error: err.message + ' ' + paymentHash }) + : typeof err === 'object' + ? JSON.stringify({ error: err + ' ' + paymentHash }) + : '{ "error": ' + err + ' ' + paymentHash + ' }'; + this.wsServer.sendErrorToAllLNClients(errStr, selectedNode); + }); }; public setOptionsForSelNode = (selectedNode: CommonSelectedNode) => { const options = { url: '', rejectUnauthorized: false, json: true, form: null }; try { - options['headers'] = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') }; + options['headers'] = { + 'Grpc-Metadata-macaroon': fs.readFileSync(join(selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') + }; } catch (err) { - this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'WebSocketClient', msg: 'Set Options Error', error: JSON.stringify(err) }); + this.logger.log({ + selectedNode: selectedNode, + level: 'ERROR', + fileName: 'WebSocketClient', + msg: 'Set Options Error', + error: JSON.stringify(err) + }); } return options; }; @@ -110,7 +190,12 @@ export class LNDWebSocketClient { public disconnect = (selectedNode: CommonSelectedNode) => { const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index); if (clientExists) { - this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the LND\'s Websocket Server..' }); + this.logger.log({ + selectedNode: clientExists.selectedNode, + level: 'INFO', + fileName: 'CLWebSocket', + msg: "Disconnecting from the LND's Websocket Server.." + }); const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index); this.webSocketClients.splice(clientIdx, 1); } @@ -119,14 +204,19 @@ export class LNDWebSocketClient { public updateSelectedNode = (newSelectedNode: CommonSelectedNode) => { const clientIdx = this.webSocketClients.findIndex((wsc) => +wsc.selectedNode.index === +newSelectedNode.index); let newClient = this.webSocketClients[clientIdx]; - if (!newClient) { newClient = { selectedNode: null }; } + if (!newClient) { + newClient = { selectedNode: null }; + } newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode)); this.webSocketClients[clientIdx] = newClient; - if (this.webSocketClients[clientIdx].selectedNode.ln_version === '' || !this.webSocketClients[clientIdx].selectedNode.ln_version || this.common.isVersionCompatible(this.webSocketClients[clientIdx].selectedNode.ln_version, '0.11.0')) { + if ( + this.webSocketClients[clientIdx].selectedNode.ln_version === '' || + !this.webSocketClients[clientIdx].selectedNode.ln_version || + this.common.isVersionCompatible(this.webSocketClients[clientIdx].selectedNode.ln_version, '0.11.0') + ) { this.fetchUnpaidInvoices(this.webSocketClients[clientIdx].selectedNode); } - } - + }; } export const LNDWSClient = new LNDWebSocketClient(); diff --git a/server/controllers/shared/RTLConf.ts b/server/controllers/shared/RTLConf.ts index 0c81a2957..428e2a9ed 100644 --- a/server/controllers/shared/RTLConf.ts +++ b/server/controllers/shared/RTLConf.ts @@ -16,8 +16,17 @@ const wsServer = WSServer; const databaseService: DatabaseService = Database; export const updateSelectedNode = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating Selected Node..' }); - const selNodeIndex = req.body.currNodeIndex ? req.body.currNodeIndex : common.initSelectedNode ? common.initSelectedNode.index : 1; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating Selected Node..' + }); + const selNodeIndex = req.body.currNodeIndex + ? req.body.currNodeIndex + : common.initSelectedNode + ? common.initSelectedNode.index + : 1; req.session.selectedNode = common.findNode(selNodeIndex); if (req.headers && req.headers.authorization && req.headers.authorization !== '') { wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.body.prevNodeIndex); @@ -26,21 +35,42 @@ export const updateSelectedNode = (req, res, next) => { } } const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Selected Node Updated To ' + responseVal }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Selected Node Updated To ' + responseVal + }); res.status(200).json({ status: 'Selected Node Updated To: ' + JSON.stringify(responseVal) + '!' }); }; export const getRTLConfigInitial = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting Initial RTL Configuration..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Getting Initial RTL Configuration..' + }); const confFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; fs.readFile(confFile, 'utf8', (errRes, data) => { if (errRes) { if (errRes.code === 'ENOENT') { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'RTLConf', msg: 'Node config does not exist!', error: { error: 'Node config does not exist.' } }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'RTLConf', + msg: 'Node config does not exist!', + error: { error: 'Node config does not exist.' } + }); res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] }); } else { const errMsg = 'Get Node Config Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } } else { @@ -53,9 +83,9 @@ export const getRTLConfigInitial = (req, res, next) => { common.nodes.forEach((node, i) => { const settings: NodeSettingsConfiguration = {}; settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT'; - settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY'; - settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE'; - settings.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false; + settings.themeMode = node.theme_mode ? node.theme_mode : 'DAY'; + settings.themeColor = node.theme_color ? node.theme_color : 'PURPLE'; + settings.fiatConversion = node.fiat_conversion ? !!node.fiat_conversion : false; settings.currencyUnit = node.currency_unit; nodesArr.push({ index: node.index, @@ -66,24 +96,56 @@ export const getRTLConfigInitial = (req, res, next) => { }); }); } - const body = { defaultNodeIndex: nodeConfData.defaultNodeIndex, selectedNodeIndex: (req.session.selectedNode && req.session.selectedNode.index ? req.session.selectedNode.index : common.initSelectedNode.index), sso: sso, enable2FA: enable2FA, allowPasswordUpdate: allowPasswordUpdate, nodes: nodesArr }; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Initial RTL Configuration Received', data: body }); + const body = { + defaultNodeIndex: nodeConfData.defaultNodeIndex, + selectedNodeIndex: + req.session.selectedNode && req.session.selectedNode.index + ? req.session.selectedNode.index + : common.initSelectedNode.index, + sso: sso, + enable2FA: enable2FA, + allowPasswordUpdate: allowPasswordUpdate, + nodes: nodesArr + }; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Initial RTL Configuration Received', + data: body + }); res.status(200).json(body); } }); }; export const getRTLConfig = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting RTL Configuration..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Getting RTL Configuration..' + }); const confFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; fs.readFile(confFile, 'utf8', (errRes, data) => { if (errRes) { if (errRes.code === 'ENOENT') { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'RTLConf', msg: 'Node config does not exist!', error: { error: 'Node config does not exist.' } }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'RTLConf', + msg: 'Node config does not exist!', + error: { error: 'Node config does not exist.' } + }); res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] }); } else { const errMsg = 'Get Node Config Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } } else { @@ -95,14 +157,14 @@ export const getRTLConfig = (req, res, next) => { if (common.nodes && common.nodes.length > 0) { common.nodes.forEach((node, i) => { const authentication: AuthenticationConfiguration = {}; - authentication.configPath = (node.config_path) ? node.config_path : ''; - authentication.swapMacaroonPath = (node.swap_macaroon_path) ? node.swap_macaroon_path : ''; - authentication.boltzMacaroonPath = (node.boltz_macaroon_path) ? node.boltz_macaroon_path : ''; + authentication.configPath = node.config_path ? node.config_path : ''; + authentication.swapMacaroonPath = node.swap_macaroon_path ? node.swap_macaroon_path : ''; + authentication.boltzMacaroonPath = node.boltz_macaroon_path ? node.boltz_macaroon_path : ''; const settings: NodeSettingsConfiguration = {}; settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT'; - settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY'; - settings.themeColor = (node.theme_color) ? node.theme_color : 'PURPLE'; - settings.fiatConversion = (node.fiat_conversion) ? !!node.fiat_conversion : false; + settings.themeMode = node.theme_mode ? node.theme_mode : 'DAY'; + settings.themeColor = node.theme_color ? node.theme_color : 'PURPLE'; + settings.fiatConversion = node.fiat_conversion ? !!node.fiat_conversion : false; settings.bitcoindConfigPath = node.bitcoind_config_path; settings.logLevel = node.log_level ? node.log_level : 'ERROR'; settings.lnServerUrl = node.ln_server_url; @@ -120,25 +182,48 @@ export const getRTLConfig = (req, res, next) => { }); }); } - const body = { defaultNodeIndex: nodeConfData.defaultNodeIndex, selectedNodeIndex: (req.session.selectedNode && req.session.selectedNode.index ? req.session.selectedNode.index : common.initSelectedNode.index), sso: sso, enable2FA: enable2FA, allowPasswordUpdate: allowPasswordUpdate, nodes: nodesArr }; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'RTL Configuration Received', data: body }); + const body = { + defaultNodeIndex: nodeConfData.defaultNodeIndex, + selectedNodeIndex: + req.session.selectedNode && req.session.selectedNode.index + ? req.session.selectedNode.index + : common.initSelectedNode.index, + sso: sso, + enable2FA: enable2FA, + allowPasswordUpdate: allowPasswordUpdate, + nodes: nodesArr + }; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'RTL Configuration Received', + data: body + }); res.status(200).json(body); } }); }; export const updateUISettings = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating UI Settings..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating UI Settings..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); - const node = config.nodes.find((node) => (node.index === req.session.selectedNode.index)); + const node = config.nodes.find((node) => node.index === req.session.selectedNode.index); if (node && node.Settings) { node.Settings.userPersona = req.body.updatedSettings.userPersona; node.Settings.themeMode = req.body.updatedSettings.themeMode; node.Settings.themeColor = req.body.updatedSettings.themeColor; node.Settings.fiatConversion = req.body.updatedSettings.fiatConversion; if (req.body.updatedSettings.fiatConversion) { - node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD'; + node.Settings.currencyUnit = req.body.updatedSettings.currencyUnit + ? req.body.updatedSettings.currencyUnit + : 'USD'; } else { delete node.Settings.currencyUnit; } @@ -148,7 +233,9 @@ export const updateUISettings = (req, res, next) => { selectedNode.theme_color = req.body.updatedSettings.themeColor; selectedNode.fiat_conversion = req.body.updatedSettings.fiatConversion; if (req.body.updatedSettings.fiatConversion) { - selectedNode.currency_unit = req.body.updatedSettings.currencyUnit ? req.body.updatedSettings.currencyUnit : 'USD'; + selectedNode.currency_unit = req.body.updatedSettings.currencyUnit + ? req.body.updatedSettings.currencyUnit + : 'USD'; } else { delete selectedNode.currency_unit; } @@ -156,17 +243,33 @@ export const updateUISettings = (req, res, next) => { } try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'UI Settings Updated', data: maskPasswords(config) }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'UI Settings Updated', + data: maskPasswords(config) + }); res.status(201).json({ message: 'Node Settings Updated Successfully' }); } catch (errRes) { const errMsg = 'Update Node Settings Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } }; export const update2FASettings = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating 2FA Settings..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating 2FA Settings..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); if (req.body.secret2fa && req.body.secret2fa.trim() !== '') { @@ -174,7 +277,10 @@ export const update2FASettings = (req, res, next) => { } else { delete config.secret2fa; } - const message = req.body.secret2fa.trim() === '' ? 'Two factor authentication disabled successfully.' : 'Two factor authentication enabled successfully.'; + const message = + req.body.secret2fa.trim() === '' + ? 'Two factor authentication disabled successfully.' + : 'Two factor authentication enabled successfully.'; try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); common.rtl_secret2fa = config.secret2fa; @@ -182,29 +288,55 @@ export const update2FASettings = (req, res, next) => { res.status(201).json({ message: message }); } catch (errRes) { const errMsg = 'Update 2FA Settings Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } }; export const updateDefaultNode = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating Default Node..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating Default Node..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); config.defaultNodeIndex = req.body.defaultNodeIndex; try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Default Node Updated', data: maskPasswords(config) }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Default Node Updated', + data: maskPasswords(config) + }); res.status(201).json({ message: 'Default Node Updated Successfully' }); } catch (errRes) { const errMsg = 'Update Default Node Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } }; export const getConfig = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Reading Configuration File..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Reading Configuration File..' + }); let confFile = ''; let fileFormat = 'INI'; switch (req.params.nodeType) { @@ -222,12 +354,29 @@ export const getConfig = (req, res, next) => { confFile = ''; break; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'Node Type', data: req.params.nodeType }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'File Path', data: confFile }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'RTLConf', + msg: 'Node Type', + data: req.params.nodeType + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'RTLConf', + msg: 'File Path', + data: confFile + }); fs.readFile(confFile, 'utf8', (errRes, data) => { if (errRes) { const errMsg = 'Reading Config Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } else { let jsonConfig = {}; @@ -246,8 +395,15 @@ export const getConfig = (req, res, next) => { } } jsonConfig = maskPasswords(jsonConfig); - const responseJSON = (fileFormat === 'JSON') ? jsonConfig : ini.stringify(jsonConfig).replace('color=\\#', 'color=#'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Configuration File Data Received', data: responseJSON }); + const responseJSON = + fileFormat === 'JSON' ? jsonConfig : ini.stringify(jsonConfig).replace('color=\\#', 'color=#'); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Configuration File Data Received', + data: responseJSON + }); res.status(200).json({ format: fileFormat, data: responseJSON }); } }); @@ -255,54 +411,120 @@ export const getConfig = (req, res, next) => { export const getFile = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting File..' }); - const file = req.query.path ? req.query.path : (req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.query.channel.replace(':', '-') + '.bak'); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'Channel Point', data: req.query.channel }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'RTLConf', msg: 'File Path', data: file }); + const file = req.query.path + ? req.query.path + : req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.query.channel.replace(':', '-') + '.bak'; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'RTLConf', + msg: 'Channel Point', + data: req.query.channel + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'RTLConf', + msg: 'File Path', + data: file + }); fs.readFile(file, 'utf8', (errRes, data) => { if (errRes) { - if (errRes.code && errRes.code === 'ENOENT') { errRes.code = 'File Not Found!'; } + if (errRes.code && errRes.code === 'ENOENT') { + errRes.code = 'File Not Found!'; + } const errMsg = 'Reading File Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'File Data Received', data: data }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'File Data Received', + data: data + }); res.status(200).json(data); } }); }; export const getCurrencyRates = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting Currency Rates..' }); - options.url = 'https://blockchain.info/ticker'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Currency Rates Received', data: body }); - res.status(200).json(JSON.parse(body)); - }).catch((errRes) => { - const errMsg = 'Get Rates Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.error, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Getting Currency Rates..' }); + options.url = 'https://blockchain.info/ticker'; + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Currency Rates Received', + data: body + }); + res.status(200).json(JSON.parse(body)); + }) + .catch((errRes) => { + const errMsg = 'Get Rates Error'; + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); + return res.status(err.statusCode).json({ message: err.error, error: err.error }); + }); }; export const updateSSO = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating SSO Settings..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating SSO Settings..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); delete config.SSO; config.SSO = req.body.SSO; try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'SSO Setting Updated', data: maskPasswords(config) }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'SSO Setting Updated', + data: maskPasswords(config) + }); res.status(201).json({ message: 'SSO Updated Successfully' }); } catch (errRes) { const errMsg = 'Update SSO Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } }; export const updateServiceSettings = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Updating Service Settings..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Updating Service Settings..' + }); const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); const selectedNode = common.findNode(req.session.selectedNode.index); @@ -351,11 +573,22 @@ export const updateServiceSettings = (req, res, next) => { }); try { fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Service Settings Updated', data: maskPasswords(config) }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'RTLConf', + msg: 'Service Settings Updated', + data: maskPasswords(config) + }); res.status(201).json({ message: 'Service Settings Updated Successfully' }); } catch (errRes) { const errMsg = 'Update Service Settings Error'; - const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: errMsg, error: errRes }, + 'RTLConf', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.error, error: err.error }); } }; @@ -368,9 +601,12 @@ export const maskPasswords = (obj) => { if (typeof obj[keys[i]] === 'object') { keys[keys[i]] = maskPasswords(obj[keys[i]]); } - if (typeof keys[i] === 'string' && - (keys[i].toLowerCase().includes('password') || keys[i].toLowerCase().includes('multipass') || - keys[i].toLowerCase().includes('rpcpass') || keys[i].toLowerCase().includes('rpcpassword')) + if ( + typeof keys[i] === 'string' && + (keys[i].toLowerCase().includes('password') || + keys[i].toLowerCase().includes('multipass') || + keys[i].toLowerCase().includes('rpcpass') || + keys[i].toLowerCase().includes('rpcpassword')) ) { obj[keys[i]] = '********************'; } diff --git a/server/controllers/shared/authenticate.ts b/server/controllers/shared/authenticate.ts index eb68f3584..1cdc02fb3 100644 --- a/server/controllers/shared/authenticate.ts +++ b/server/controllers/shared/authenticate.ts @@ -15,7 +15,7 @@ const databaseService: DatabaseService = Database; const loginInterval = setInterval(() => { for (const ip in failedLoginAttempts) { - if (new Date().getTime() > (failedLoginAttempts[ip].lastTried + LOCKING_PERIOD)) { + if (new Date().getTime() > failedLoginAttempts[ip].lastTried + LOCKING_PERIOD) { delete failedLoginAttempts[ip]; clearInterval(loginInterval); } @@ -24,7 +24,7 @@ const loginInterval = setInterval(() => { export const getFailedInfo = (reqIP, currentTime) => { let failed = { count: 0, lastTried: currentTime }; - if ((!failedLoginAttempts[reqIP]) || (currentTime > (failed.lastTried + LOCKING_PERIOD))) { + if (!failedLoginAttempts[reqIP] || currentTime > failed.lastTried + LOCKING_PERIOD) { failed = { count: 0, lastTried: currentTime }; failedLoginAttempts[reqIP] = failed; } else { @@ -34,37 +34,81 @@ export const getFailedInfo = (reqIP, currentTime) => { }; const handleMultipleFailedAttemptsError = (failed, currentTime, errMsg) => { - if (failed.count >= ALLOWED_LOGIN_ATTEMPTS && (currentTime <= (failed.lastTried + LOCKING_PERIOD))) { + if (failed.count >= ALLOWED_LOGIN_ATTEMPTS && currentTime <= failed.lastTried + LOCKING_PERIOD) { return { message: 'Multiple Failed Login Attempts!', - error: 'Application locked for ' + (LOCKING_PERIOD / ONE_MINUTE) + ' minutes due to multiple failed attempts!\nTry again after ' + common.convertTimestampToTime((failed.lastTried + LOCKING_PERIOD) / 1000) + '!' + error: + 'Application locked for ' + + LOCKING_PERIOD / ONE_MINUTE + + ' minutes due to multiple failed attempts!\nTry again after ' + + common.convertTimestampToTime((failed.lastTried + LOCKING_PERIOD) / 1000) + + '!' }; } else { return { message: 'Authentication Failed!', - error: errMsg + '\nApplication will be locked after ' + (ALLOWED_LOGIN_ATTEMPTS - failed.count) + ' more unsuccessful attempts!' + error: + errMsg + + '\nApplication will be locked after ' + + (ALLOWED_LOGIN_ATTEMPTS - failed.count) + + ' more unsuccessful attempts!' }; } }; -export const verifyToken = (twoFAToken) => !!(common.rtl_secret2fa && common.rtl_secret2fa !== '' && otplib.authenticator.check(twoFAToken, common.rtl_secret2fa)); +export const verifyToken = (twoFAToken) => + !!( + common.rtl_secret2fa && + common.rtl_secret2fa !== '' && + otplib.authenticator.check(twoFAToken, common.rtl_secret2fa) + ); export const authenticateUser = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Authenticating User..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'Authenticating User..' + }); if (+common.rtl_sso) { if (req.body.authenticateWith === 'JWT' && jwt.verify(req.body.authenticationValue, common.secret_key)) { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'User Authenticated' }); - res.status(406).json({ message: 'SSO Authentication Error', error: 'Login with Password is not allowed with SSO.' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'User Authenticated' + }); + res + .status(406) + .json({ message: 'SSO Authentication Error', error: 'Login with Password is not allowed with SSO.' }); } else if (req.body.authenticateWith === 'PASSWORD') { - if (common.cookie_value.trim().length >= 32 && crypto.timingSafeEqual(Buffer.from(crypto.createHash('sha256').update(common.cookie_value).digest('hex'), 'utf-8'), Buffer.from(req.body.authenticationValue, 'utf-8'))) { + if ( + common.cookie_value.trim().length >= 32 && + crypto.timingSafeEqual( + Buffer.from(crypto.createHash('sha256').update(common.cookie_value).digest('hex'), 'utf-8'), + Buffer.from(req.body.authenticationValue, 'utf-8') + ) + ) { common.refreshCookie(); - if (!req.session.selectedNode) { req.session.selectedNode = common.initSelectedNode; } + if (!req.session.selectedNode) { + req.session.selectedNode = common.initSelectedNode; + } const token = jwt.sign({ user: 'SSO_USER' }, common.secret_key); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'User Authenticated' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'User Authenticated' + }); res.status(200).json({ token: token }); } else { const errMsg = 'SSO Authentication Failed! Access key too short or does not match.'; - const err = common.handleError({ statusCode: 406, message: 'SSO Authentication Error', error: errMsg }, 'Authenticate', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 406, message: 'SSO Authentication Error', error: errMsg }, + 'Authenticate', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } } @@ -76,20 +120,39 @@ export const authenticateUser = (req, res, next) => { if (common.rtl_pass === password && failed.count < ALLOWED_LOGIN_ATTEMPTS) { if (req.body.twoFAToken && req.body.twoFAToken !== '') { if (!verifyToken(req.body.twoFAToken)) { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Authenticate', msg: 'Invalid Token! Failed IP ' + reqIP, error: { error: 'Invalid token.' } }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Authenticate', + msg: 'Invalid Token! Failed IP ' + reqIP, + error: { error: 'Invalid token.' } + }); failed.count = failed.count + 1; failed.lastTried = currentTime; return res.status(401).json(handleMultipleFailedAttemptsError(failed, currentTime, 'Invalid 2FA Token!')); } } - if (!req.session.selectedNode) { req.session.selectedNode = common.initSelectedNode; } + if (!req.session.selectedNode) { + req.session.selectedNode = common.initSelectedNode; + } delete failedLoginAttempts[reqIP]; const token = jwt.sign({ user: 'NODE_USER' }, common.secret_key); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'User Authenticated' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'User Authenticated' + }); res.status(200).json({ token: token }); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Authenticate', msg: 'Invalid Password! Failed IP ' + reqIP, error: { error: 'Invalid password.' } }); - failed.count = common.rtl_pass !== password ? (failed.count + 1) : failed.count; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'ERROR', + fileName: 'Authenticate', + msg: 'Invalid Password! Failed IP ' + reqIP, + error: { error: 'Invalid password.' } + }); + failed.count = common.rtl_pass !== password ? failed.count + 1 : failed.count; failed.lastTried = common.rtl_pass !== password ? currentTime : failed.lastTried; return res.status(401).json(handleMultipleFailedAttemptsError(failed, currentTime, 'Invalid Password!')); } @@ -97,21 +160,41 @@ export const authenticateUser = (req, res, next) => { }; export const resetPassword = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Resetting Password..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'Resetting Password..' + }); if (+common.rtl_sso) { const errMsg = 'Password cannot be reset for SSO authentication'; - const err = common.handleError({ statusCode: 401, message: 'Password Reset Error', error: errMsg }, 'Authenticate', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 401, message: 'Password Reset Error', error: errMsg }, + 'Authenticate', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { const currPassword = req.body.currPassword; if (common.rtl_pass === currPassword) { common.rtl_pass = common.replacePasswordWithHash(req.body.newPassword); const token = jwt.sign({ user: 'NODE_USER' }, common.secret_key); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Password Reset Successful' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Authenticate', + msg: 'Password Reset Successful' + }); res.status(200).json({ token: token }); } else { const errMsg = 'Incorrect Old Password'; - const err = common.handleError({ statusCode: 401, message: 'Password Reset Error', error: errMsg }, 'Authenticate', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 401, message: 'Password Reset Error', error: errMsg }, + 'Authenticate', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } } diff --git a/server/controllers/shared/boltz.ts b/server/controllers/shared/boltz.ts index 533f0d62a..77a0c1692 100644 --- a/server/controllers/shared/boltz.ts +++ b/server/controllers/shared/boltz.ts @@ -6,39 +6,75 @@ const logger: LoggerService = Logger; const common: CommonService = Common; export const getInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting Boltz Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Getting Boltz Information..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Get Info Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Get Info Error', error: errMsg }, + 'Boltz', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/info'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Information Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Boltz', 'Get Info Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Information Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Boltz', 'Get Info Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getServiceInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting Service Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Getting Service Information..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Get Service Information Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Get Service Information Error', error: errMsg }, + 'Boltz', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/serviceinfo'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Get Service Info Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Boltz', 'Get Service Information Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Get Service Info Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Boltz', 'Get Service Information Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const listSwaps = (req, res, next) => { @@ -46,37 +82,72 @@ export const listSwaps = (req, res, next) => { options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'List Swaps Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'List Swaps Error', error: errMsg }, + 'Boltz', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/listswaps'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz List Swaps Received', data: body }); - if (body && body.swaps && body.swaps.length && body.swaps.length > 0) { body.swaps = body.swaps.reverse(); } - if (body && body.reverseSwaps && body.reverseSwaps.length && body.reverseSwaps.length > 0) { body.reverseSwaps = body.reverseSwaps.reverse(); } - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Boltz', 'List Swaps Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz List Swaps Received', + data: body + }); + if (body && body.swaps && body.swaps.length && body.swaps.length > 0) { + body.swaps = body.swaps.reverse(); + } + if (body && body.reverseSwaps && body.reverseSwaps.length && body.reverseSwaps.length > 0) { + body.reverseSwaps = body.reverseSwaps.reverse(); + } + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Boltz', 'List Swaps Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const getSwapInfo = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Getting Swap Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Getting Swap Information..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Get Swap Information Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Get Swap Information Error', error: errMsg }, + 'Boltz', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/swap/' + req.params.swapId; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Swap Information Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Boltz', 'Swap Info Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Swap Information Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Boltz', 'Swap Info Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const createSwap = (req, res, next) => { @@ -84,78 +155,173 @@ export const createSwap = (req, res, next) => { options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Create Swap Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Create Swap Error', error: errMsg }, + 'Boltz', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/createswap'; options.body = { amount: req.body.amount }; - if (req.body.address !== '') { options.body.address = req.body.address; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Swap Options Body', data: options.body }); - request.post(options).then((createSwapRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Swap Created', data: createSwapRes }); - res.status(201).json(createSwapRes); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Boltz', 'Create Swap Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + if (req.body.address !== '') { + options.body.address = req.body.address; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Boltz', + msg: 'Create Swap Options Body', + data: options.body }); + request + .post(options) + .then((createSwapRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Swap Created', + data: createSwapRes + }); + res.status(201).json(createSwapRes); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Boltz', 'Create Swap Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const createReverseSwap = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Creating Reverse Swap..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Creating Reverse Swap..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Create Reverse Swap Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Create Reverse Swap Error', error: errMsg }, + 'Boltz', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/createreverseswap'; options.body = { amount: req.body.amount }; - if (req.body.address !== '') { options.body.address = req.body.address; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Reverse Swap Body', data: options.body }); - request.post(options).then((createReverseSwapRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Reverse Swap Created', data: createReverseSwapRes }); - res.status(201).json(createReverseSwapRes); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Boltz', 'Create Reverse Swap Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + if (req.body.address !== '') { + options.body.address = req.body.address; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Boltz', + msg: 'Create Reverse Swap Body', + data: options.body }); + request + .post(options) + .then((createReverseSwapRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Reverse Swap Created', + data: createReverseSwapRes + }); + res.status(201).json(createReverseSwapRes); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Boltz', 'Create Reverse Swap Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const createChannel = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Creating Boltz Channel..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Creating Boltz Channel..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Create Channel Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Create Channel Error', error: errMsg }, + 'Boltz', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/createchannel'; options.body = { amount: req.body.amount }; - if (req.body.address !== '') { options.body.address = req.body.address; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Channel Options Body', data: options.body }); - request.post(options).then((createChannelRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Channel Created', data: createChannelRes }); - res.status(201).json(createChannelRes); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Boltz', 'Create Channel Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + if (req.body.address !== '') { + options.body.address = req.body.address; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Boltz', + msg: 'Create Channel Options Body', + data: options.body }); + request + .post(options) + .then((createChannelRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Channel Created', + data: createChannelRes + }); + res.status(201).json(createChannelRes); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Boltz', 'Create Channel Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const deposit = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Deposit Start..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Deposit Start..' + }); options = common.getBoltzServerOptions(req); if (options.url === '') { const errMsg = 'Boltz Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Deposit Error', error: errMsg }, 'Boltz', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Deposit Error', error: errMsg }, + 'Boltz', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/deposit'; - request.post(options).then((depositRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Boltz Deposit Finished', data: depositRes }); - res.status(201).json(depositRes); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Boltz', 'Deposit Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request + .post(options) + .then((depositRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Boltz', + msg: 'Boltz Deposit Finished', + data: depositRes + }); + res.status(201).json(depositRes); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Boltz', 'Deposit Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/controllers/shared/loop.ts b/server/controllers/shared/loop.ts index 84d8953b2..4fc026f55 100644 --- a/server/controllers/shared/loop.ts +++ b/server/controllers/shared/loop.ts @@ -10,7 +10,12 @@ export const loopOut = (req, res, next) => { options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Loop Out Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Loop Out Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/out'; @@ -25,89 +30,222 @@ export const loopOut = (req, res, next) => { swap_publication_deadline: req.body.swapPublicationDeadline, initiator: 'RTL' }; - if (req.body.chanId !== '') { options.body['loop_out_channel'] = req.body.chanId; } - if (req.body.destAddress !== '') { options.body['dest'] = req.body.destAddress; } - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Body', data: options.body }); - request.post(options).then((loopOutRes) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looped Out', data: loopOutRes }); - res.status(201).json(loopOutRes); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'Loop Out Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + if (req.body.chanId !== '') { + options.body['loop_out_channel'] = req.body.chanId; + } + if (req.body.destAddress !== '') { + options.body['dest'] = req.body.destAddress; + } + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Body', + data: options.body }); + request + .post(options) + .then((loopOutRes) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Looped Out', + data: loopOutRes + }); + res.status(201).json(loopOutRes); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'Loop Out Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const loopOutTerms = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop Out Terms..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Loop Out Terms Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/out/terms'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Terms Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'Loop Out Terms Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Out Terms Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'Loop Out Terms Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const loopOutQuote = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Quotes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop Out Quotes..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Loop Out Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Loop Out Quotes Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } - options.url = options.url + '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Quote URL', data: options.url }); - request(options).then((quoteRes) => { - quoteRes.amount = +req.params.amount; - quoteRes.swap_payment_dest = quoteRes.swap_payment_dest ? Buffer.from(quoteRes.swap_payment_dest, 'base64').toString('hex') : ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Quote Received', data: quoteRes }); - res.status(200).json(quoteRes); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'Loop Out Quotes Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + options.url = + options.url + + '/v1/loop/out/quote/' + + req.params.amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Quote URL', + data: options.url }); + request(options) + .then((quoteRes) => { + quoteRes.amount = +req.params.amount; + quoteRes.swap_payment_dest = quoteRes.swap_payment_dest + ? Buffer.from(quoteRes.swap_payment_dest, 'base64').toString('hex') + : ''; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Out Quote Received', + data: quoteRes + }); + res.status(200).json(quoteRes); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'Loop Out Quotes Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const loopOutTermsAndQuotes = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms & Quotes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop Out Terms & Quotes..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Loop Out Terms & Quotes Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/out/terms'; - request(options).then((terms) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Terms Received', data: terms }); - const options1 = common.getSwapServerOptions(req); const options2 = common.getSwapServerOptions(req); - options1.url = options1.url + '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - options2.url = options2.url + '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Min Quote Options', data: options1 }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Max Quote Options', data: options2 }); - return Promise.all([request(options1), request(options2)]).then((values) => { - values[0].amount = +terms.min_swap_amount; - values[1].amount = +terms.max_swap_amount; - values[0].swap_payment_dest = values[0].swap_payment_dest ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') : ''; - values[1].swap_payment_dest = values[1].swap_payment_dest ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') : ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Quotes 1 Received', data: values[0] }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Quotes 2 Received', data: values[1] }); - res.status(200).json(values); - }).catch((errRes) => { + request(options) + .then((terms) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Terms Received', + data: terms + }); + const options1 = common.getSwapServerOptions(req); + const options2 = common.getSwapServerOptions(req); + options1.url = + options1.url + + '/v1/loop/out/quote/' + + terms.min_swap_amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + options2.url = + options2.url + + '/v1/loop/out/quote/' + + terms.max_swap_amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Min Quote Options', + data: options1 + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Out Max Quote Options', + data: options2 + }); + return Promise.all([request(options1), request(options2)]) + .then((values) => { + values[0].amount = +terms.min_swap_amount; + values[1].amount = +terms.max_swap_amount; + values[0].swap_payment_dest = values[0].swap_payment_dest + ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') + : ''; + values[1].swap_payment_dest = values[1].swap_payment_dest + ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') + : ''; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Out Quotes 1 Received', + data: values[0] + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Out Quotes 2 Received', + data: values[1] + }); + res.status(200).json(values); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'Loop Out Terms & Quotes Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop Out Terms & Quotes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'Loop Out Terms & Quotes Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; export const loopIn = (req, res, next) => { @@ -115,7 +253,12 @@ export const loopIn = (req, res, next) => { options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Loop In Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Loop In Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/in'; @@ -125,87 +268,216 @@ export const loopIn = (req, res, next) => { max_miner_fee: req.body.minerFee, initiator: 'RTL' }; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Body', data: options.body }); - request.post(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looped In', data: body }); - res.status(201).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'Loop In Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Body', + data: options.body }); + request + .post(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Looped In', + data: body + }); + res.status(201).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'Loop In Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const loopInTerms = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop In Terms..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Loop In Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Loop In Terms Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/in/terms'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Terms Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'Loop In Terms Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Terms Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'Loop In Terms Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const loopInQuote = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Quotes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop In Quotes..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Loop In Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Loop In Quotes Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } - options.url = options.url + '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Quote Options', data: options.url }); - request(options).then((body) => { - body.amount = +req.params.amount; - body.swap_payment_dest = body.swap_payment_dest ? Buffer.from(body.swap_payment_dest, 'base64').toString('hex') : ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Qoutes Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'Loop In Quote Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + options.url = + options.url + + '/v1/loop/in/quote/' + + req.params.amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop In Quote Options', + data: options.url }); + request(options) + .then((body) => { + body.amount = +req.params.amount; + body.swap_payment_dest = body.swap_payment_dest + ? Buffer.from(body.swap_payment_dest, 'base64').toString('hex') + : ''; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Qoutes Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'Loop In Quote Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const loopInTermsAndQuotes = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms & Quotes..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Loop In Terms & Quotes..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Loop In Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Loop In Terms & Quotes Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/in/terms'; - request(options).then((terms) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Terms Received', data: terms }); - const options1 = common.getSwapServerOptions(req); const options2 = common.getSwapServerOptions(req); - options1.url = options1.url + '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - options2.url = options2.url + '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline; - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Min Quote Options', data: options1 }); - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Max Quote Options', data: options2 }); - return Promise.all([request(options1), request(options2)]).then((values) => { - values[0].amount = +terms.min_swap_amount; - values[1].amount = +terms.max_swap_amount; - values[0].swap_payment_dest = values[0].swap_payment_dest ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') : ''; - values[1].swap_payment_dest = values[1].swap_payment_dest ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') : ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Quotes 1 Received', data: values[0] }); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Quotes 2 Received', data: values[1] }); - res.status(200).json(values); - }).catch((errRes) => { + request(options) + .then((terms) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop In Terms Received', + data: terms + }); + const options1 = common.getSwapServerOptions(req); + const options2 = common.getSwapServerOptions(req); + options1.url = + options1.url + + '/v1/loop/in/quote/' + + terms.min_swap_amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + options2.url = + options2.url + + '/v1/loop/in/quote/' + + terms.max_swap_amount + + '?conf_target=' + + (req.query.targetConf ? req.query.targetConf : '2') + + '&swap_publication_deadline=' + + req.query.swapPublicationDeadline; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop In Min Quote Options', + data: options1 + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop In Max Quote Options', + data: options2 + }); + return Promise.all([request(options1), request(options2)]) + .then((values) => { + values[0].amount = +terms.min_swap_amount; + values[1].amount = +terms.max_swap_amount; + values[0].swap_payment_dest = values[0].swap_payment_dest + ? Buffer.from(values[0].swap_payment_dest, 'base64').toString('hex') + : ''; + values[1].swap_payment_dest = values[1].swap_payment_dest + ? Buffer.from(values[1].swap_payment_dest, 'base64').toString('hex') + : ''; + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Quotes 1 Received', + data: values[0] + }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop In Quotes 2 Received', + data: values[1] + }); + res.status(200).json(values); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'Loop In Terms & Quotes Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); + }) + .catch((errRes) => { const err = common.handleError(errRes, 'Loop', 'Loop In Terms & Quotes Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); }); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'Loop In Terms & Quotes Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); }; export const swaps = (req, res, next) => { @@ -213,37 +485,74 @@ export const swaps = (req, res, next) => { options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'List Swaps Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'List Swaps Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/swaps'; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body }); - if (body.swaps && body.swaps.length > 0) { - body.swaps = common.sortDescByKey(body.swaps, 'initiation_time'); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Sorted Loop Swaps List Received', data: body }); - } - res.status(200).json(body.swaps); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'List Swaps Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'DEBUG', + fileName: 'Loop', + msg: 'Loop Swaps Received', + data: body + }); + if (body.swaps && body.swaps.length > 0) { + body.swaps = common.sortDescByKey(body.swaps, 'initiation_time'); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Sorted Loop Swaps List Received', + data: body + }); + } + res.status(200).json(body.swaps); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'List Swaps Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; export const swap = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Swap Information..' }); + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Getting Swap Information..' + }); options = common.getSwapServerOptions(req); if (options.url === '') { const errMsg = 'Loop Server URL is missing in the configuration.'; - const err = common.handleError({ statusCode: 500, message: 'Get Swap Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 500, message: 'Get Swap Error', error: errMsg }, + 'Loop', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } options.url = options.url + '/v1/loop/swap/' + req.params.id; - request(options).then((body) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body }); - res.status(200).json(body); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); + request(options) + .then((body) => { + logger.log({ + selectedNode: req.session.selectedNode, + level: 'INFO', + fileName: 'Loop', + msg: 'Loop Swap Information Received', + data: body + }); + res.status(200).json(body); + }) + .catch((errRes) => { + const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); }; diff --git a/server/models/config.model.ts b/server/models/config.model.ts index 45cb15e90..6edb27512 100644 --- a/server/models/config.model.ts +++ b/server/models/config.model.ts @@ -1,5 +1,4 @@ export class CommonSelectedNode { - constructor( public options?: any, public ln_server_url?: string, @@ -26,22 +25,14 @@ export class CommonSelectedNode { public ln_version?: string, public api_version?: string, public enable_offers?: boolean - ) { } - + ) {} } export class AuthenticationConfiguration { - - constructor( - public configPath?: string, - public swapMacaroonPath?: string, - public boltzMacaroonPath?: string - ) { } - + constructor(public configPath?: string, public swapMacaroonPath?: string, public boltzMacaroonPath?: string) {} } export class NodeSettingsConfiguration { - constructor( public userPersona?: string, public themeMode?: string, @@ -55,12 +46,10 @@ export class NodeSettingsConfiguration { public boltzServerUrl?: string, public channelBackupPath?: string, public enableOffers?: boolean - ) { } - + ) {} } export class LogJSONObj { - constructor( public level?: string, public msg?: string, @@ -68,6 +57,5 @@ export class LogJSONObj { public error?: string | any, public fileName?: string, public selectedNode?: CommonSelectedNode - ) { } - + ) {} } diff --git a/server/models/database.model.ts b/server/models/database.model.ts index 388bbb812..18a6a1397 100644 --- a/server/models/database.model.ts +++ b/server/models/database.model.ts @@ -4,7 +4,7 @@ export enum CollectionsEnum { export type Collections = { Offers: Offer[]; -} +}; export enum OfferFieldsEnum { BOLT12 = 'bolt12', @@ -17,7 +17,6 @@ export enum OfferFieldsEnum { export const CollectionFieldsEnum = { ...OfferFieldsEnum }; export class Offer { - constructor( public bolt12: string, public amountmSat: number, @@ -25,22 +24,21 @@ export class Offer { public vendor?: string, public description?: string, public lastUpdatedAt?: number - ) { } - + ) {} } export const validateOffer = (documentToValidate): any => { if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.BOLT12)) { - return ({ isValid: false, error: CollectionFieldsEnum.BOLT12 + 'is mandatory.' }); + return { isValid: false, error: CollectionFieldsEnum.BOLT12 + 'is mandatory.' }; } if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.AMOUNTMSAT)) { - return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'is mandatory.' }); + return { isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'is mandatory.' }; } if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TITLE)) { - return ({ isValid: false, error: CollectionFieldsEnum.TITLE + 'is mandatory.' }); + return { isValid: false, error: CollectionFieldsEnum.TITLE + 'is mandatory.' }; } - if ((typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number')) { - return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'should be a number.' }); + if (typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number') { + return { isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'should be a number.' }; } - return ({ isValid: true }); + return { isValid: true }; }; diff --git a/server/routes/cln/channels.ts b/server/routes/cln/channels.ts index 67fc2e28c..3daeaa000 100644 --- a/server/routes/cln/channels.ts +++ b/server/routes/cln/channels.ts @@ -1,7 +1,14 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { listChannels, openChannel, setChannelFee, closeChannel, getLocalRemoteBalance, listForwards } from '../../controllers/cln/channels.js'; +import { + listChannels, + openChannel, + setChannelFee, + closeChannel, + getLocalRemoteBalance, + listForwards +} from '../../controllers/cln/channels.js'; const router = Router(); diff --git a/server/routes/cln/offers.ts b/server/routes/cln/offers.ts index 5f1b9970d..7d57dba8e 100644 --- a/server/routes/cln/offers.ts +++ b/server/routes/cln/offers.ts @@ -1,7 +1,14 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { listOfferBookmarks, deleteOfferBookmark, listOffers, disableOffer, createOffer, fetchOfferInvoice } from '../../controllers/cln/offers.js'; +import { + listOfferBookmarks, + deleteOfferBookmark, + listOffers, + disableOffer, + createOffer, + fetchOfferInvoice +} from '../../controllers/cln/offers.js'; const router = Router(); diff --git a/server/routes/eclair/channels.ts b/server/routes/eclair/channels.ts index aa4de77b5..97f89f20b 100644 --- a/server/routes/eclair/channels.ts +++ b/server/routes/eclair/channels.ts @@ -1,7 +1,13 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { getChannels, getChannelStats, openChannel, updateChannelRelayFee, closeChannel } from '../../controllers/eclair/channels.js'; +import { + getChannels, + getChannelStats, + openChannel, + updateChannelRelayFee, + closeChannel +} from '../../controllers/eclair/channels.js'; const router = Router(); diff --git a/server/routes/eclair/payments.ts b/server/routes/eclair/payments.ts index 07b01d593..ef6043545 100644 --- a/server/routes/eclair/payments.ts +++ b/server/routes/eclair/payments.ts @@ -1,7 +1,12 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { queryPaymentRoute, decodePayment, getSentPaymentsInformation, postPayment } from '../../controllers/eclair/payments.js'; +import { + queryPaymentRoute, + decodePayment, + getSentPaymentsInformation, + postPayment +} from '../../controllers/eclair/payments.js'; const router = Router(); diff --git a/server/routes/lnd/channels.ts b/server/routes/lnd/channels.ts index d7f2745c7..f889e645c 100644 --- a/server/routes/lnd/channels.ts +++ b/server/routes/lnd/channels.ts @@ -1,7 +1,15 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { getAllChannels, getPendingChannels, getClosedChannels, postChannel, postTransactions, closeChannel, postChanPolicy } from '../../controllers/lnd/channels.js'; +import { + getAllChannels, + getPendingChannels, + getClosedChannels, + postChannel, + postTransactions, + closeChannel, + postChanPolicy +} from '../../controllers/lnd/channels.js'; const router = Router(); diff --git a/server/routes/lnd/graph.ts b/server/routes/lnd/graph.ts index eddfe0d6a..fe5b8b0ba 100644 --- a/server/routes/lnd/graph.ts +++ b/server/routes/lnd/graph.ts @@ -1,7 +1,15 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { getDescribeGraph, getGraphInfo, getAliasesForPubkeys, getGraphNode, getGraphEdge, getRemoteFeePolicy, getQueryRoutes } from '../../controllers/lnd/graph.js'; +import { + getDescribeGraph, + getGraphInfo, + getAliasesForPubkeys, + getGraphNode, + getGraphEdge, + getRemoteFeePolicy, + getQueryRoutes +} from '../../controllers/lnd/graph.js'; const router = Router(); diff --git a/server/routes/lnd/payments.ts b/server/routes/lnd/payments.ts index 272c62ba5..2c1807838 100644 --- a/server/routes/lnd/payments.ts +++ b/server/routes/lnd/payments.ts @@ -1,7 +1,13 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { decodePayment, decodePayments, getPayments, getAllLightningTransactions, paymentLookup } from '../../controllers/lnd/payments.js'; +import { + decodePayment, + decodePayments, + getPayments, + getAllLightningTransactions, + paymentLookup +} from '../../controllers/lnd/payments.js'; const router = Router(); diff --git a/server/routes/lnd/wallet.ts b/server/routes/lnd/wallet.ts index e02100050..46c4eb6ef 100644 --- a/server/routes/lnd/wallet.ts +++ b/server/routes/lnd/wallet.ts @@ -1,7 +1,16 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { genSeed, updateSelNodeOptions, getUTXOs, operateWallet, bumpFee, labelTransaction, leaseUTXO, releaseUTXO } from '../../controllers/lnd/wallet.js'; +import { + genSeed, + updateSelNodeOptions, + getUTXOs, + operateWallet, + bumpFee, + labelTransaction, + leaseUTXO, + releaseUTXO +} from '../../controllers/lnd/wallet.js'; const router = Router(); diff --git a/server/routes/shared/RTLConf.ts b/server/routes/shared/RTLConf.ts index bb3eccdab..5f581f686 100644 --- a/server/routes/shared/RTLConf.ts +++ b/server/routes/shared/RTLConf.ts @@ -1,7 +1,19 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { getRTLConfigInitial, getRTLConfig, updateUISettings, update2FASettings, getConfig, getFile, updateSelectedNode, updateDefaultNode, updateServiceSettings, updateSSO, getCurrencyRates } from '../../controllers/shared/RTLConf.js'; +import { + getRTLConfigInitial, + getRTLConfig, + updateUISettings, + update2FASettings, + getConfig, + getFile, + updateSelectedNode, + updateDefaultNode, + updateServiceSettings, + updateSSO, + getCurrencyRates +} from '../../controllers/shared/RTLConf.js'; const router = Router(); diff --git a/server/routes/shared/boltz.ts b/server/routes/shared/boltz.ts index ef6c8e227..5e374fb1c 100644 --- a/server/routes/shared/boltz.ts +++ b/server/routes/shared/boltz.ts @@ -1,7 +1,16 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { getInfo, getServiceInfo, listSwaps, getSwapInfo, createSwap, createReverseSwap, createChannel, deposit } from '../../controllers/shared/boltz.js'; +import { + getInfo, + getServiceInfo, + listSwaps, + getSwapInfo, + createSwap, + createReverseSwap, + createChannel, + deposit +} from '../../controllers/shared/boltz.js'; const router = Router(); diff --git a/server/routes/shared/loop.ts b/server/routes/shared/loop.ts index 21f82ec4a..eecea144b 100644 --- a/server/routes/shared/loop.ts +++ b/server/routes/shared/loop.ts @@ -1,7 +1,18 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js'; +import { + loopInTerms, + loopInQuote, + loopInTermsAndQuotes, + loopIn, + loopOutTerms, + loopOutQuote, + loopOutTermsAndQuotes, + loopOut, + swaps, + swap +} from '../../controllers/shared/loop.js'; const router = Router(); diff --git a/server/utils/app.ts b/server/utils/app.ts index f7c42e26f..a4837c9d8 100644 --- a/server/utils/app.ts +++ b/server/utils/app.ts @@ -6,6 +6,7 @@ import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import CORS from './cors.js'; import CSRF from './csrf.js'; +import CertificateIdentity from './certificateIdentity.js'; import sharedRoutes from '../routes/shared/index.js'; import lndRoutes from '../routes/lnd/index.js'; @@ -21,7 +22,6 @@ import { LNDWSClient, LNDWebSocketClient } from '../controllers/lnd/webSocketCli const ONE_DAY = 1000 * 60 * 60 * 24; export class ExpressApplication { - public app = express(); public logger: LoggerService = Logger; public common: CommonService = Common; @@ -32,14 +32,29 @@ export class ExpressApplication { public directoryName = dirname(fileURLToPath(import.meta.url)); constructor() { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Starting Express Application..' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'App', + msg: 'Starting Express Application..' + }); + this.loadConfiguration(); this.app.set('trust proxy', true); - this.app.use(sessions({ secret: this.common.secret_key, saveUninitialized: true, cookie: { secure: false, maxAge: ONE_DAY }, resave: false })); + if (this.common.ssl && this.common.ssl.requestCert) { + this.app.use(CertificateIdentity(this.common.ssl.rejectUnauthorized)); + } + this.app.use( + sessions({ + secret: this.common.secret_key, + saveUninitialized: true, + cookie: { secure: this.common.ssl ? true : false, maxAge: ONE_DAY }, + resave: false + }) + ); this.app.use(cookieParser(this.common.secret_key)); this.app.use(bodyParser.json({ limit: '25mb' })); this.app.use(bodyParser.urlencoded({ extended: false, limit: '25mb' })); - this.loadConfiguration(); this.setCORS(); this.setCSRF(); this.setApplicationRoutes(); @@ -49,52 +64,91 @@ export class ExpressApplication { public loadConfiguration = () => { this.config.setServerConfiguration(); - } + }; - public setCORS = () => { CORS.mount(this.app); } + public setCORS = () => { + CORS.mount(this.app); + }; - public setCSRF = () => { CSRF.mount(this.app); } + public setCSRF = () => { + CSRF.mount(this.app); + }; public setApplicationRoutes = () => { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Setting up Application Routes..' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'App', + msg: 'Setting up Application Routes..' + }); this.app.use(this.common.baseHref + '/api', sharedRoutes); this.app.use(this.common.baseHref + '/api/lnd', lndRoutes); this.app.use(this.common.baseHref + '/api/cln', clnRoutes); this.app.use(this.common.baseHref + '/api/ecl', eclRoutes); this.app.use(this.common.baseHref, express.static(join(this.directoryName, '../..', 'frontend'))); this.app.use((req: any, res, next) => { - res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : ''); + res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : '', { secure: this.common.ssl ? true : false }); res.sendFile(join(this.directoryName, '../..', 'frontend', 'index.html')); }); this.app.use((err, req, res, next) => this.handleApplicationErrors(err, res)); - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'App', msg: 'Application Routes Set' }); - } + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'App', + msg: 'Application Routes Set' + }); + }; public handleApplicationErrors = (err, res) => { switch (err.code) { case 'EACCES': - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Server requires elevated privileges' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'Server requires elevated privileges' + }); res.status(406).send('Server requires elevated privileges.'); break; case 'EADDRINUSE': - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Server is already in use' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'Server is already in use' + }); res.status(409).send('Server is already in use.'); break; case 'ECONNREFUSED': - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Server is down/locked' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'Server is down/locked' + }); res.status(401).send('Server is down/locked.'); break; case 'EBADCSRFTOKEN': - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'Invalid CSRF token. Form tempered.' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'Invalid CSRF token. Form tempered.' + }); res.status(403).send('Invalid CSRF token, form tempered.'); break; default: - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'App', msg: 'DEFUALT ERROR', error: err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'App', + msg: 'DEFUALT ERROR', + error: err + }); res.status(400).send(JSON.stringify(err)); break; } }; - } export default ExpressApplication; diff --git a/server/utils/authCheck.ts b/server/utils/authCheck.ts index bef8a631f..71fd86478 100644 --- a/server/utils/authCheck.ts +++ b/server/utils/authCheck.ts @@ -14,15 +14,22 @@ export const isAuthenticated = (req, res, next) => { next(); } catch (error) { const errMsg = 'Authentication Failed! Please Login First!'; - const err = common.handleError({ statusCode: 401, message: 'Authentication Error', error: errMsg }, 'AuthCheck', errMsg, req.session.selectedNode); + const err = common.handleError( + { statusCode: 401, message: 'Authentication Error', error: errMsg }, + 'AuthCheck', + errMsg, + req.session.selectedNode + ); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } }; export const verifyWSUser = (info, next) => { const headers = JSON.parse(JSON.stringify(info.req.headers)); - const protocols = !info.req.headers['sec-websocket-protocol'] ? [] : info.req.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); - const jwToken = (protocols && protocols.length > 0) ? protocols[0] : ''; + const protocols = !info.req.headers['sec-websocket-protocol'] + ? [] + : info.req.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); + const jwToken = protocols && protocols.length > 0 ? protocols[0] : ''; if (!jwToken || jwToken === '') { next(false, 401, 'Authentication Failed! Please Login First!'); } else { @@ -44,7 +51,13 @@ export const verifyWSUser = (info, next) => { } catch (err) { cookies = {}; updatedReq['cookies'] = JSON.parse(cookies); - logger.log({ selectedNode: common.initSelectedNode, level: 'WARN', fileName: 'AuthCheck', msg: '403 Unable to read CSRF token cookie', data: err }); + logger.log({ + selectedNode: common.initSelectedNode, + level: 'WARN', + fileName: 'AuthCheck', + msg: '403 Unable to read CSRF token cookie', + data: err + }); } csurfProtection(updatedReq, null, (err) => { if (err) { @@ -54,7 +67,13 @@ export const verifyWSUser = (info, next) => { } }); } catch (err) { - logger.log({ selectedNode: common.initSelectedNode, level: 'WARN', fileName: 'AuthCheck', msg: '403 Unable to verify CSRF token', data: err }); + logger.log({ + selectedNode: common.initSelectedNode, + level: 'WARN', + fileName: 'AuthCheck', + msg: '403 Unable to verify CSRF token', + data: err + }); next(true); } } diff --git a/server/utils/certificateFactory.ts b/server/utils/certificateFactory.ts new file mode 100644 index 000000000..dada0935e --- /dev/null +++ b/server/utils/certificateFactory.ts @@ -0,0 +1,321 @@ +import forge from 'node-forge'; +forge.options.usePureJavaScript = true; + +//--------------------------------------------------------------------------------------- +//Private Module Variables +//--------------------------------------------------------------------------------------- +const { assign, freeze, keys } = Object; +const max32BitInt = 2147483647; +//--------------------------------------------------------------------------------------- + +class CertificateFactory { + private forge: any = forge; + private certBundle: any = null; + private encryptionBits: Number = 2048; + private certType: String = 'component'; + private validForYears: Number = 10; + private commonName: String = 'localhost'; + private organizationalUnit: any = 'RTL'; + private organizationName: String = 'RTL'; + private countryName: String = 'US'; + private stateName: String = 'New York'; + private localityName: String = 'New York'; + private altName: String = 'https://localhost'; + private altIp: String = '127.0.0.1'; + + //--------------------------------------------------------------------------------------- + //Constructor + // + //Input: obj (options object) + //Output: certificate factory instance + //--------------------------------------------------------------------------------------- + constructor(opts: any = {}) { + const { + altIp = '127.0.0.1', + certType = 'component', + commonName = 'localhost', + countryName = 'US', + encryptionBits = '2048', + stateName = 'New York', + localityName = 'New York', + organizationName = 'RTL', + organizationalUnit = 'RTL', + validForYears = '10' + } = opts; + assign(this, { + certBundle: null, + altName: `https://${commonName}`, + altIp, + certType, + commonName, + countryName, + encryptionBits: parseInt(encryptionBits, 10), + stateName, + localityName, + organizationName, + organizationalUnit, + validForYears: parseInt(validForYears, 10) + }); + } + + //--------------------------------------------------------------------------------------- + //Method: getStaticBundle + // + //Input: type + //Output: bundle + // + //This method will generate a constant static cert bundle that can be "shared" between + //many classes that may require a common certificate set. It has a single input argument + //that allows external classes to specify which format they would like their cert bundle + //to be in. (string or buffer) + //--------------------------------------------------------------------------------------- + public getStaticBundle(type = 'string', password = null) { + if (!this.certBundle) { + this.certBundle = freeze(this.generateRandomCerts()); + } + + return this._convertBundle(this.certBundle, type, password); + } + + //--------------------------------------------------------------------------------------- + //Method: _convertBundle + // + //Input: bundle , type + //Output: bundle + // + //This method will converts all key value pairs of a certificate bundle to the desired + //type and returns a new bundle. This also performs a defacto copy of the bundle to + //prevent outside callers from tampering with each other's returned bundle instance, or + //the internal cached bundle referenced by getStaticBundle. + //--------------------------------------------------------------------------------------- + private _convertBundle(bundle, type, password) { + const newBundle = {}; + const bundleKeys = keys(bundle); + const converter = type === 'string' ? this._valueToString : this._valueToBuffer; + + if (type === 'p12') { + return this._bundleToP12(bundle, password); + } + + bundleKeys.forEach((k) => { + if (bundle[k]) { + newBundle[k] = converter(bundle[k]); + } else { + newBundle[k] = bundle[k]; + } + }); + + return newBundle; + } + + //--------------------------------------------------------------------------------------- + //Method: _valueToString + // + //Input: v + //Output: v + // + //This method converts its input value to a string and returns it. + //--------------------------------------------------------------------------------------- + private _valueToString(v) { + return `${v}`; + } + + //--------------------------------------------------------------------------------------- + //Method: _valueToBuffer + // + //Input: v + //Output: v + // + //This method converts its input value to a buffer and returns it. + //--------------------------------------------------------------------------------------- + private _valueToBuffer(v) { + return Buffer.from(v); + } + + //--------------------------------------------------------------------------------------- + //Method: _bundleToP12 + // + //Input: bundle + //Output: p12Base64 + // + //This method converts a certificate bundle to a .p12 file that can be used in a browser. + //--------------------------------------------------------------------------------------- + private _bundleToP12(bundle, password) { + const { + forge: { asn1, pki, pkcs12, util } + } = this; + const pemCertificate = pki.certificateFromPem(bundle.cert); + const pemKey = pki.privateKeyFromPem(bundle.key); + const p12Asn1 = pkcs12.toPkcs12Asn1(pemKey, pemCertificate, password, { + algorithm: '3des' + }); + const p12Bytes = asn1.toDer(p12Asn1).getBytes(); + const p12Base64 = util.encode64(p12Bytes); + + return p12Base64; + } + + //--------------------------------------------------------------------------------------- + //Method: generateRandomCerts + // + //Input: none + //Output: bundle + // + //This method utilizes node-forge to create a key/cert/ca bundle entirely in javascript. + //It uses the factory properties set in the factory constructor to configure the new + //key/cert/ca bundle, and then returns the newly generated bundle. Directly calling this + //method will result in new bundles with different private keys, but the certificate + //x509 attributes will match across all bundles generated with the same factory instance. + //--------------------------------------------------------------------------------------- + public generateRandomCerts() { + const { + forge: { pki } + } = this; + //generate a keypair and create an X.509v3 certificate + const keys = pki.rsa.generateKeyPair(this.encryptionBits); + const cert = pki.createCertificate(); + cert.publicKey = keys.publicKey; + cert.serialNumber = Math.round(Math.random() * max32BitInt).toString(16); + cert.validity.notBefore = new Date(); + cert.validity.notAfter = new Date(); + cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + this.validForYears); + const altNames = []; + let attrs; + if (this.certType === 'person') { + const OUArray = Array.isArray(this.organizationalUnit) + ? this.organizationalUnit.map((v) => { + return { + shortName: 'OU', + value: v + }; + }) + : [ + { + shortName: 'OU', + value: 'RTL' + }, + { + shortName: 'OU', + value: 'BTC' + }, + { + shortName: 'OU', + value: 'People' + } + ]; + attrs = [].concat( + [ + { + name: 'countryName', + value: this.countryName + }, + { + name: 'organizationName', + value: this.organizationName + } + ], + OUArray, + [ + { + name: 'commonName', + value: this.commonName + } + ] + ); + } else { + attrs = [ + { + name: 'commonName', + value: this.commonName + }, + { + name: 'countryName', + value: this.countryName + }, + { + shortName: 'ST', + value: this.stateName + }, + { + name: 'localityName', + value: this.localityName + }, + { + name: 'organizationName', + value: this.organizationName + }, + { + shortName: 'OU', + value: this.organizationalUnit + } + ]; + if (this.altName) { + altNames.push({ + type: 6, // URI + value: this.altName + }); + } + if (this.altIp) { + altNames.push({ + type: 7, // IP + ip: this.altIp + }); + } + } + cert.setSubject(attrs); + cert.setIssuer(attrs); + cert.setExtensions([ + { + name: 'basicConstraints', + cA: true + }, + { + name: 'keyUsage', + keyCertSign: true, + digitalSignature: true, + nonRepudiation: true, + keyEncipherment: true, + dataEncipherment: true + }, + { + name: 'extKeyUsage', + serverAuth: true, + clientAuth: true, + codeSigning: true, + emailProtection: true, + timeStamping: true + }, + { + name: 'nsCertType', + client: true, + server: true, + email: true, + objsign: true, + sslCA: true, + emailCA: true, + objCA: true + }, + { + name: 'subjectAltName', + altNames: altNames + }, + { + name: 'subjectKeyIdentifier' + } + ]); + cert.sign(keys.privateKey); + + // convert a Forge certificate to PEM + // for self generated certs, ca will be undefined + const pem = pki.certificateToPem(cert); + const certBundle = { + key: pki.privateKeyToPem(keys.privateKey), + cert: pem, + ca: undefined + }; + + return certBundle; + } +} + +export default CertificateFactory; diff --git a/server/utils/certificateIdentity.ts b/server/utils/certificateIdentity.ts new file mode 100644 index 000000000..12110b582 --- /dev/null +++ b/server/utils/certificateIdentity.ts @@ -0,0 +1,42 @@ +function subjectToString(subject) { + const aggr = []; + Object.keys(subject).forEach((k) => { + if (typeof subject[k] === 'string') { + aggr.push(`${k}=${subject[k]}`); + } else if (Array.isArray(subject[k]) && subject[k].length > 0) { + const temp = []; + subject[k].forEach((v) => { + temp.push(`${k}=${v}`); + }); + aggr.push(temp.reverse().join(',')); + } + }); + return aggr.reverse().join(','); +} + +export default (rejectUnauthorized = false) => { + return function (req, res, next) { + const { connection, protocol } = req; + + if (protocol === 'https' && (connection.authorized || rejectUnauthorized === false)) { + try { + const { subject } = connection.getPeerCertificate(); + const CN = subject.CN; + const DN = subjectToString(subject); + req.identity = { + DN, + CN + }; + return next(); + } catch (e) { + //if this server requsts certs, but there is an error getting them, DO NOT PASS GO! + return res.status(500).json({ + message: e.toString() + }); + } + } + return res.status(403).json({ + message: 'forbidden' + }); + }; +}; diff --git a/server/utils/common.ts b/server/utils/common.ts index d11d11b57..8a850adc7 100644 --- a/server/utils/common.ts +++ b/server/utils/common.ts @@ -7,13 +7,13 @@ import { Logger, LoggerService } from './logger.js'; import { CommonSelectedNode } from '../models/config.model.js'; export class CommonService { - public logger: LoggerService = Logger; public nodes: CommonSelectedNode[] = []; public initSelectedNode: CommonSelectedNode = null; public rtl_conf_file_path = ''; public port = 3000; public host = null; + public ssl: any = false; public rtl_pass = ''; public flg_allow_password_update = true; public rtl_secret2fa = ''; @@ -26,9 +26,22 @@ export class CommonService { public read_dummy_data = false; public baseHref = '/rtl'; private dummy_data_array_from_file = []; - private MONTHS = [{ name: 'JAN', days: 31 }, { name: 'FEB', days: 28 }, { name: 'MAR', days: 31 }, { name: 'APR', days: 30 }, { name: 'MAY', days: 31 }, { name: 'JUN', days: 30 }, { name: 'JUL', days: 31 }, { name: 'AUG', days: 31 }, { name: 'SEP', days: 30 }, { name: 'OCT', days: 31 }, { name: 'NOV', days: 30 }, { name: 'DEC', days: 31 }]; - - constructor() { } + private MONTHS = [ + { name: 'JAN', days: 31 }, + { name: 'FEB', days: 28 }, + { name: 'MAR', days: 31 }, + { name: 'APR', days: 30 }, + { name: 'MAY', days: 31 }, + { name: 'JUN', days: 30 }, + { name: 'JUL', days: 31 }, + { name: 'AUG', days: 31 }, + { name: 'SEP', days: 30 }, + { name: 'OCT', days: 31 }, + { name: 'NOV', days: 30 }, + { name: 'DEC', days: 31 } + ]; + + constructor() {} public getSwapServerOptions = (req) => { const swapOptions = { @@ -39,12 +52,28 @@ export class CommonService { }; if (req.session.selectedNode.swap_macaroon_path) { try { - swapOptions.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.swap_macaroon_path, 'loop.macaroon')).toString('hex') }; + swapOptions.headers = { + 'Grpc-Metadata-macaroon': fs + .readFileSync(join(req.session.selectedNode.swap_macaroon_path, 'loop.macaroon')) + .toString('hex') + }; } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Loop macaroon Error', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Loop macaroon Error', + error: err + }); } } - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Swap Options', data: swapOptions }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Swap Options', + data: swapOptions + }); return swapOptions; }; @@ -57,23 +86,47 @@ export class CommonService { }; if (req.session.selectedNode.boltz_macaroon_path) { try { - boltzOptions.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.boltz_macaroon_path, 'admin.macaroon')).toString('hex') }; + boltzOptions.headers = { + 'Grpc-Metadata-macaroon': fs + .readFileSync(join(req.session.selectedNode.boltz_macaroon_path, 'admin.macaroon')) + .toString('hex') + }; } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Boltz macaroon Error', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Boltz macaroon Error', + error: err + }); } } - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Boltz Options', data: boltzOptions }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Boltz Options', + data: boltzOptions + }); return boltzOptions; }; public getOptions = (req) => { if (req.session.selectedNode && req.session.selectedNode.options) { - req.session.selectedNode.options.method = (req.session.selectedNode.ln_implementation && req.session.selectedNode.ln_implementation.toUpperCase() !== 'ECL') ? 'GET' : 'POST'; + req.session.selectedNode.options.method = + req.session.selectedNode.ln_implementation && req.session.selectedNode.ln_implementation.toUpperCase() !== 'ECL' + ? 'GET' + : 'POST'; delete req.session.selectedNode.options.form; req.session.selectedNode.options.qs = {}; return req.session.selectedNode.options; } - return this.handleError({ statusCode: 401, message: 'Session expired after a day\'s inactivity' }, 'Session Expired', 'Session Expiry Error', this.initSelectedNode); + return this.handleError( + { statusCode: 401, message: "Session expired after a day's inactivity" }, + 'Session Expired', + 'Session Expiry Error', + this.initSelectedNode + ); }; public updateSelectedNodeOptions = (req) => { @@ -90,20 +143,36 @@ export class CommonService { if (req.session.selectedNode && req.session.selectedNode.ln_implementation) { switch (req.session.selectedNode.ln_implementation.toUpperCase()) { case 'CLN': - req.session.selectedNode.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'access.macaroon'))).toString('base64') }; + req.session.selectedNode.options.headers = { + macaroon: Buffer.from( + fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'access.macaroon')) + ).toString('base64') + }; break; case 'ECL': - req.session.selectedNode.options.headers = { authorization: 'Basic ' + Buffer.from(':' + req.session.selectedNode.ln_api_password).toString('base64') }; + req.session.selectedNode.options.headers = { + authorization: 'Basic ' + Buffer.from(':' + req.session.selectedNode.ln_api_password).toString('base64') + }; break; default: - req.session.selectedNode.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') }; + req.session.selectedNode.options.headers = { + 'Grpc-Metadata-macaroon': fs + .readFileSync(join(req.session.selectedNode.macaroon_path, 'admin.macaroon')) + .toString('hex') + }; break; } } if (req.session.selectedNode) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Updated Node Options for ' + req.session.selectedNode.ln_node, data: req.session.selectedNode.options }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Updated Node Options for ' + req.session.selectedNode.ln_node, + data: req.session.selectedNode.options + }); } return { status: 200, message: 'Updated Successfully' }; } catch (err) { @@ -113,13 +182,21 @@ export class CommonService { json: true, form: null }; - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Update Selected Node Options Error', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Update Selected Node Options Error', + error: err + }); return { status: 502, message: err }; } }; public setOptions = (req) => { - if (this.nodes[0].options && this.nodes[0].options.headers) { return; } + if (this.nodes[0].options && this.nodes[0].options.headers) { + return; + } if (this.nodes && this.nodes.length > 0) { this.nodes.forEach((node) => { node.options = { @@ -132,20 +209,32 @@ export class CommonService { if (node.ln_implementation) { switch (node.ln_implementation.toUpperCase()) { case 'CLN': - node.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(node.macaroon_path, 'access.macaroon'))).toString('base64') }; + node.options.headers = { + macaroon: Buffer.from(fs.readFileSync(join(node.macaroon_path, 'access.macaroon'))).toString('base64') + }; break; case 'ECL': - node.options.headers = { authorization: 'Basic ' + Buffer.from(':' + node.ln_api_password).toString('base64') }; + node.options.headers = { + authorization: 'Basic ' + Buffer.from(':' + node.ln_api_password).toString('base64') + }; break; default: - node.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(node.macaroon_path, 'admin.macaroon')).toString('hex') }; + node.options.headers = { + 'Grpc-Metadata-macaroon': fs.readFileSync(join(node.macaroon_path, 'admin.macaroon')).toString('hex') + }; break; } } } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Common Set Options Error', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Common Set Options Error', + error: err + }); node.options = { url: '', rejectUnauthorized: false, @@ -153,7 +242,13 @@ export class CommonService { form: '' }; } - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Set Node Options for ' + node.ln_node, data: node.options }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Set Node Options for ' + node.ln_node, + data: node.options + }); }); this.updateSelectedNodeOptions(req); } @@ -179,26 +274,40 @@ export class CommonService { minutes = +minutes < 10 ? '0' + minutes : minutes; let seconds = myDate.getSeconds().toString(); seconds = +seconds < 10 ? '0' + seconds : seconds; - return days + '/' + this.MONTHS[myDate.getMonth()].name + '/' + myDate.getFullYear() + ' ' + hours + ':' + minutes + ':' + seconds; + return ( + days + + '/' + + this.MONTHS[myDate.getMonth()].name + + '/' + + myDate.getFullYear() + + ' ' + + hours + + ':' + + minutes + + ':' + + seconds + ); }; - public sortAscByKey = (array, key) => array.sort((a, b) => { - const x = +a[key]; - const y = +b[key]; - return ((x < y) ? -1 : ((x > y) ? 1 : 0)); - }); + public sortAscByKey = (array, key) => + array.sort((a, b) => { + const x = +a[key]; + const y = +b[key]; + return x < y ? -1 : x > y ? 1 : 0; + }); - public sortAscByStrKey = (array, key) => array.sort((a, b) => { - const x = a[key] ? a[key].toUpperCase() : ''; - const y = b[key] ? b[key].toUpperCase() : ''; - return ((x < y) ? -1 : ((x > y) ? 1 : 0)); - }); + public sortAscByStrKey = (array, key) => + array.sort((a, b) => { + const x = a[key] ? a[key].toUpperCase() : ''; + const y = b[key] ? b[key].toUpperCase() : ''; + return x < y ? -1 : x > y ? 1 : 0; + }); public sortDescByKey = (array, key) => { const temp = array.sort((a, b) => { const x = +a[key] ? +a[key] : 0; const y = +b[key] ? +b[key] : 0; - return (x > y) ? -1 : ((x < y) ? 1 : 0); + return x > y ? -1 : x < y ? 1 : 0; }); return temp; }; @@ -207,26 +316,36 @@ export class CommonService { const temp = array.sort((a, b) => { const x = a[key] ? a[key].toUpperCase() : ''; const y = b[key] ? b[key].toUpperCase() : ''; - return (x > y) ? -1 : ((x < y) ? 1 : 0); + return x > y ? -1 : x < y ? 1 : 0; }); return temp; }; public newestOnTop = (array, key, value) => { - const newlyAddedRecord = array.splice(array.findIndex((item) => item[key] === value), 1); + const newlyAddedRecord = array.splice( + array.findIndex((item) => item[key] === value), + 1 + ); array.unshift(newlyAddedRecord[0]); return array; }; public handleError = (errRes, fileName, errMsg, selectedNode: CommonSelectedNode) => { const err = JSON.parse(JSON.stringify(errRes)); - if (!selectedNode) { selectedNode = this.initSelectedNode; } + if (!selectedNode) { + selectedNode = this.initSelectedNode; + } switch (selectedNode.ln_implementation) { case 'LND': if (err.options && err.options.headers && err.options.headers['Grpc-Metadata-macaroon']) { delete err.options.headers['Grpc-Metadata-macaroon']; } - if (err.response && err.response.request && err.response.request.headers && err.response.request.headers['Grpc-Metadata-macaroon']) { + if ( + err.response && + err.response.request && + err.response.request.headers && + err.response.request.headers['Grpc-Metadata-macaroon'] + ) { delete err.response.request.headers['Grpc-Metadata-macaroon']; } break; @@ -235,7 +354,12 @@ export class CommonService { if (err.options && err.options.headers && err.options.headers.macaroon) { delete err.options.headers.macaroon; } - if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.macaroon) { + if ( + err.response && + err.response.request && + err.response.request.headers && + err.response.request.headers.macaroon + ) { delete err.response.request.headers.macaroon; } break; @@ -244,36 +368,64 @@ export class CommonService { if (err.options && err.options.headers && err.options.headers.authorization) { delete err.options.headers.authorization; } - if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) { + if ( + err.response && + err.response.request && + err.response.request.headers && + err.response.request.headers.authorization + ) { delete err.response.request.headers.authorization; } break; default: - if (err.options && err.options.headers) { delete err.options.headers; } + if (err.options && err.options.headers) { + delete err.options.headers; + } break; } - this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: fileName, msg: errMsg, error: (typeof err === 'object' ? JSON.stringify(err) : (typeof err === 'string') ? err : 'Unknown Error') }); + this.logger.log({ + selectedNode: selectedNode, + level: 'ERROR', + fileName: fileName, + msg: errMsg, + error: typeof err === 'object' ? JSON.stringify(err) : typeof err === 'string' ? err : 'Unknown Error' + }); const newErrorObj = { - statusCode: err.statusCode ? err.statusCode : err.status ? err.status : (err.error && err.error.code && err.error.code === 'ECONNREFUSED') ? 503 : 500, - message: (err.error && err.error.message) ? err.error.message : err.message ? err.message : errMsg, - error: ( - (err.error && err.error.error && err.error.error.error && typeof err.error.error.error === 'string') ? err.error.error.error : - (err.error && err.error.error && typeof err.error.error === 'string') ? err.error.error : - (err.error && err.error.error && err.error.error.message && typeof err.error.error.message === 'string') ? err.error.error.message : - (err.error && err.error.message && typeof err.error.message === 'string') ? err.error.message : - (err.error && typeof err.error === 'string') ? err.error : - (err.message && typeof err.message === 'string') ? err.message : (typeof err === 'string') ? err : 'Unknown Error' - ) + statusCode: err.statusCode + ? err.statusCode + : err.status + ? err.status + : err.error && err.error.code && err.error.code === 'ECONNREFUSED' + ? 503 + : 500, + message: err.error && err.error.message ? err.error.message : err.message ? err.message : errMsg, + error: + err.error && err.error.error && err.error.error.error && typeof err.error.error.error === 'string' + ? err.error.error.error + : err.error && err.error.error && typeof err.error.error === 'string' + ? err.error.error + : err.error && err.error.error && err.error.error.message && typeof err.error.error.message === 'string' + ? err.error.error.message + : err.error && err.error.message && typeof err.error.message === 'string' + ? err.error.message + : err.error && typeof err.error === 'string' + ? err.error + : err.message && typeof err.message === 'string' + ? err.message + : typeof err === 'string' + ? err + : 'Unknown Error' }; return newErrorObj; }; - public getRequestIP = (req) => ((typeof req.headers['x-forwarded-for'] === 'string' && req.headers['x-forwarded-for'].split(',').shift()) || + public getRequestIP = (req) => + (typeof req.headers['x-forwarded-for'] === 'string' && req.headers['x-forwarded-for'].split(',').shift()) || req.ip || req.connection.remoteAddress || req.socket.remoteAddress || - (req.connection.socket ? req.connection.socket.remoteAddress : null)); + (req.connection.socket ? req.connection.socket.remoteAddress : null); public getDummyData = (dataKey, lnImplementation) => { const dummyDataFile = this.rtl_conf_file_path + sep + 'ECLDummyData.log'; @@ -282,9 +434,19 @@ export class CommonService { fs.readFile(dummyDataFile, 'utf8', (err, data) => { if (err) { if (err.code === 'ENOENT') { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Dummy data file does not exist' }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Dummy data file does not exist' + }); } else { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Getting dummy data failed' }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Getting dummy data failed' + }); } } else { this.dummy_data_array_from_file = data.split('\n'); @@ -303,7 +465,12 @@ export class CommonService { try { this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8'); } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while reading cookie: \n' + err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while reading cookie: \n' + err + }); throw new Error(err); } } else { @@ -313,21 +480,32 @@ export class CommonService { fs.writeFileSync(this.rtl_cookie_path, crypto.randomBytes(64).toString('hex')); this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8'); } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while reading the cookie: \n' + err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while reading the cookie: \n' + err + }); throw new Error(err); } } - } + }; public refreshCookie = () => { try { fs.writeFileSync(this.rtl_cookie_path, crypto.randomBytes(64).toString('hex')); this.cookie_value = fs.readFileSync(this.rtl_cookie_path, 'utf-8'); } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Something went wrong while refreshing cookie', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Something went wrong while refreshing cookie', + error: err + }); throw new Error(err); } - } + }; public createDirectory = (directoryName) => { const initDir = isAbsolute(directoryName) ? sep : ''; @@ -340,7 +518,9 @@ export class CommonService { } catch (err) { if (err.code !== 'EEXIST') { if (err.code === 'ENOENT') { - throw new Error(`ENOENT: No such file or directory, mkdir '${directoryName}'. Ensure that the path separator is '${sep}'`); + throw new Error( + `ENOENT: No such file or directory, mkdir '${directoryName}'. Ensure that the path separator is '${sep}'` + ); } else { throw err; } @@ -351,19 +531,32 @@ export class CommonService { }; public replacePasswordWithHash = (multiPassHashed) => { - this.rtl_conf_file_path = process.env.RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : join(dirname(fileURLToPath(import.meta.url)), '../..'); + this.rtl_conf_file_path = process.env.RTL_CONFIG_PATH + ? process.env.RTL_CONFIG_PATH + : join(dirname(fileURLToPath(import.meta.url)), '../..'); try { const RTLConfFile = this.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); config.multiPassHashed = multiPassHashed; delete config.multiPass; fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8'); - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Please note that, RTL has encrypted the plaintext password into its corresponding hash' }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Please note that, RTL has encrypted the plaintext password into its corresponding hash' + }); return config.multiPassHashed; } catch (err) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Password hashing failed', error: err }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Password hashing failed', + error: err + }); } - } + }; public getAllNodeAllChannelBackup = (node: CommonSelectedNode) => { const channel_backup_file = node.channel_backup_path + sep + 'channel-all.bak'; @@ -373,94 +566,235 @@ export class CommonService { json: true, headers: { 'Grpc-Metadata-macaroon': fs.readFileSync(node.macaroon_path + '/admin.macaroon').toString('hex') } }; - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Getting Channel Backup for Node ' + node.ln_node + '..' }); - request(options).then((body) => { - fs.writeFile(channel_backup_file, JSON.stringify(body), (err) => { - if (err) { - if (node.ln_node) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for Node ' + node.ln_node, error: err }); - } else { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for File ' + channel_backup_file, error: err }); - } - } else { - if (node.ln_node) { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Successful in Channel Backup for Node ' + node.ln_node, data: body }); + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Getting Channel Backup for Node ' + node.ln_node + '..' + }); + request(options).then( + (body) => { + fs.writeFile(channel_backup_file, JSON.stringify(body), (err) => { + if (err) { + if (node.ln_node) { + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Error in Channel Backup for Node ' + node.ln_node, + error: err + }); + } else { + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Error in Channel Backup for File ' + channel_backup_file, + error: err + }); + } } else { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'INFO', fileName: 'Common', msg: 'Successful in Channel Backup for File ' + channel_backup_file, data: body }); + if (node.ln_node) { + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Successful in Channel Backup for Node ' + node.ln_node, + data: body + }); + } else { + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'INFO', + fileName: 'Common', + msg: 'Successful in Channel Backup for File ' + channel_backup_file, + data: body + }); + } } - } - }); - }, (err) => { - this.logger.log({ selectedNode: this.initSelectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for Node ' + node.ln_node, error: err }); - fs.writeFile(channel_backup_file, '', () => { }); - }); + }); + }, + (err) => { + this.logger.log({ + selectedNode: this.initSelectedNode, + level: 'ERROR', + fileName: 'Common', + msg: 'Error in Channel Backup for Node ' + node.ln_node, + error: err + }); + fs.writeFile(channel_backup_file, '', () => {}); + } + ); }; public isVersionCompatible = (currentVersion, checkVersion) => { if (currentVersion) { const versionsArr = currentVersion.trim().replace('v', '').split('-')[0].split('.') || []; const checkVersionsArr = checkVersion.split('.'); - return (+versionsArr[0] > +checkVersionsArr[0]) || + return ( + +versionsArr[0] > +checkVersionsArr[0] || (+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] > +checkVersionsArr[1]) || - (+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] === +checkVersionsArr[1] && +versionsArr[2] >= +checkVersionsArr[2]); + (+versionsArr[0] === +checkVersionsArr[0] && + +versionsArr[1] === +checkVersionsArr[1] && + +versionsArr[2] >= +checkVersionsArr[2]) + ); } return false; - } + }; - public getMonthDays = (selMonth, selYear) => ((selMonth === 1 && selYear % 4 === 0) ? (this.MONTHS[selMonth].days + 1) : this.MONTHS[selMonth].days); + public getMonthDays = (selMonth, selYear) => + selMonth === 1 && selYear % 4 === 0 ? this.MONTHS[selMonth].days + 1 : this.MONTHS[selMonth].days; public logEnvVariables = (req) => { const selNode = req.session.selectedNode; if (selNode && selNode.index) { - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'PORT: ' + this.port }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'HOST: ' + this.host }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'SSO: ' + this.rtl_sso }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'DEFAULT NODE INDEX: ' + selNode.index }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'INDEX: ' + selNode.index }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LN NODE: ' + selNode.ln_node }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LN IMPLEMENTATION: ' + selNode.ln_implementation }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'FIAT CONVERSION: ' + selNode.fiat_conversion }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'CURRENCY UNIT: ' + selNode.currency_unit }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LN SERVER URL: ' + selNode.ln_server_url }); - this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'LOGOUT REDIRECT LINK: ' + this.logout_redirect_link + '\r\n' }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'PORT: ' + this.port + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'HOST: ' + this.host + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'SSO: ' + this.rtl_sso + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'DEFAULT NODE INDEX: ' + selNode.index + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'INDEX: ' + selNode.index + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'LN NODE: ' + selNode.ln_node + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'LN IMPLEMENTATION: ' + selNode.ln_implementation + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'FIAT CONVERSION: ' + selNode.fiat_conversion + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'CURRENCY UNIT: ' + selNode.currency_unit + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'LN SERVER URL: ' + selNode.ln_server_url + }); + this.logger.log({ + selectedNode: selNode, + level: 'INFO', + fileName: 'Config Setup Variable', + msg: 'LOGOUT REDIRECT LINK: ' + this.logout_redirect_link + '\r\n' + }); } - } + }; public filterData = (dataKey, lnImplementation) => { let search_string = ''; if (lnImplementation === 'ECL') { switch (dataKey) { - case 'GetInfo': search_string = 'INFO: GetInfo => Get Info Response: '; break; - case 'Fees': search_string = 'INFO: Fees => Fee Response: '; break; - case 'Payments': search_string = 'INFO: Fees => Payments Response: '; break; - case 'Invoices': search_string = 'INFO: Invoice => Invoices List Received: '; break; - case 'OnChainBalance': search_string = 'INFO: Onchain => Balance Received: '; break; - case 'Peers': search_string = 'INFO: Peers => Peers with Alias: '; break; - case 'Channels': search_string = 'INFO: Channels => Simplified Channels with Alias: '; break; - default: search_string = 'Random Line'; break; + case 'GetInfo': + search_string = 'INFO: GetInfo => Get Info Response: '; + break; + case 'Fees': + search_string = 'INFO: Fees => Fee Response: '; + break; + case 'Payments': + search_string = 'INFO: Fees => Payments Response: '; + break; + case 'Invoices': + search_string = 'INFO: Invoice => Invoices List Received: '; + break; + case 'OnChainBalance': + search_string = 'INFO: Onchain => Balance Received: '; + break; + case 'Peers': + search_string = 'INFO: Peers => Peers with Alias: '; + break; + case 'Channels': + search_string = 'INFO: Channels => Simplified Channels with Alias: '; + break; + default: + search_string = 'Random Line'; + break; } } else if (lnImplementation === 'CLN') { switch (dataKey) { - case 'GetInfo': search_string = 'DEBUG: GetInfo => Node Information. '; break; - case 'Fees': search_string = 'DEBUG: Fees => Fee Received. '; break; - case 'Payments': search_string = 'DEBUG: Payments => Payment List Received: '; break; - case 'Invoices': search_string = 'DEBUG: Invoice => Invoices List Received. '; break; - case 'ChannelBalance': search_string = 'DEBUG: Channels => Local Remote Balance. '; break; - case 'Peers': search_string = 'DEBUG: Peers => Peers with Alias: '; break; - case 'Channels': search_string = 'DEBUG: Channels => List Channels: '; break; - case 'Balance': search_string = 'DEBUG: Balance => Balance Received. '; break; - case 'ForwardingHistory': search_string = 'DEBUG: Channels => Forwarding History Received: '; break; - case 'UTXOs': search_string = 'DEBUG: OnChain => List Funds Received. '; break; - case 'FeeRateperkb': search_string = 'DEBUG: Network => Network Fee Rates Received for perkb. '; break; - case 'FeeRateperkw': search_string = 'DEBUG: Network => Network Fee Rates Received for perkw. '; break; - default: search_string = 'Random Line'; break; + case 'GetInfo': + search_string = 'DEBUG: GetInfo => Node Information. '; + break; + case 'Fees': + search_string = 'DEBUG: Fees => Fee Received. '; + break; + case 'Payments': + search_string = 'DEBUG: Payments => Payment List Received: '; + break; + case 'Invoices': + search_string = 'DEBUG: Invoice => Invoices List Received. '; + break; + case 'ChannelBalance': + search_string = 'DEBUG: Channels => Local Remote Balance. '; + break; + case 'Peers': + search_string = 'DEBUG: Peers => Peers with Alias: '; + break; + case 'Channels': + search_string = 'DEBUG: Channels => List Channels: '; + break; + case 'Balance': + search_string = 'DEBUG: Balance => Balance Received. '; + break; + case 'ForwardingHistory': + search_string = 'DEBUG: Channels => Forwarding History Received: '; + break; + case 'UTXOs': + search_string = 'DEBUG: OnChain => List Funds Received. '; + break; + case 'FeeRateperkb': + search_string = 'DEBUG: Network => Network Fee Rates Received for perkb. '; + break; + case 'FeeRateperkw': + search_string = 'DEBUG: Network => Network Fee Rates Received for perkw. '; + break; + default: + search_string = 'Random Line'; + break; } } const foundDataLine = this.dummy_data_array_from_file.find((dataItem) => dataItem.includes(search_string)); - const dataStr = foundDataLine ? foundDataLine.substring((foundDataLine.indexOf(search_string)) + search_string.length) : '{}'; + const dataStr = foundDataLine + ? foundDataLine.substring(foundDataLine.indexOf(search_string) + search_string.length) + : '{}'; return JSON.parse(dataStr); - } - + }; } export const Common = new CommonService(); diff --git a/server/utils/config.ts b/server/utils/config.ts index fe0d3f2b0..9d4bf9c6a 100644 --- a/server/utils/config.ts +++ b/server/utils/config.ts @@ -9,7 +9,6 @@ import { Common, CommonService } from './common.js'; import { Logger, LoggerService } from './logger.js'; export class ConfigService { - private platform = os.platform(); private hash = crypto.createHash('sha256'); private errMsg = ''; @@ -17,7 +16,7 @@ export class ConfigService { private common: CommonService = Common; private logger: LoggerService = Logger; - constructor() { } + constructor() {} private setDefaultConfig = () => { const homeDir = os.userInfo().homedir; @@ -92,9 +91,58 @@ export class ConfigService { return false; }; + private normalizeSSL = (val) => { + let ssl; + if (typeof val === 'string') { + ssl = val === 'true' ? true : val === 'false' ? false : JSON.parse(val); + } else if (typeof val === 'undefined') { + return false; + } else if (typeof val === 'boolean') { + return val; + } else { + ssl = val; + } + const { + key = undefined, + cert = undefined, + ca = undefined, + altIp = '127.0.0.1', + certType = 'component', + commonName = 'localhost', + countryName = 'US', + encryptionBits = 2048, + stateName = 'New York', + localityName = 'New York', + organizationName = 'RTL', + organizationalUnit = 'RTL', + validForYears = 10, + requestCert = false, + rejectUnauthorized = false + } = ssl; + return { + key, + cert, + ca, + altIp, + certType, + commonName, + countryName, + encryptionBits, + stateName, + localityName, + organizationName, + organizationalUnit, + validForYears, + requestCert, + rejectUnauthorized + }; + }; + private updateLogByLevel = () => { let updateLogFlag = false; - this.common.rtl_conf_file_path = process.env.RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : join(this.directoryName, '../..'); + this.common.rtl_conf_file_path = process.env.RTL_CONFIG_PATH + ? process.env.RTL_CONFIG_PATH + : join(this.directoryName, '../..'); try { const RTLConfFile = this.common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); @@ -114,7 +162,7 @@ export class ConfigService { }; private validateNodeConfig = (config) => { - if ((+process.env.RTL_SSO === 0) || (typeof process.env.RTL_SSO === 'undefined' && +config.SSO.rtlSSO === 0)) { + if (+process.env.RTL_SSO === 0 || (typeof process.env.RTL_SSO === 'undefined' && +config.SSO.rtlSSO === 0)) { if (process.env.APP_PASSWORD && process.env.APP_PASSWORD.trim() !== '') { this.common.rtl_pass = this.hash.update(process.env.APP_PASSWORD).digest('hex'); this.common.flg_allow_password_update = false; @@ -123,7 +171,8 @@ export class ConfigService { } else if (config.multiPass && config.multiPass !== '') { this.common.rtl_pass = this.common.replacePasswordWithHash(this.hash.update(config.multiPass).digest('hex')); } else { - this.errMsg = this.errMsg + '\nNode Authentication can be set with multiPass only. Please set multiPass in RTL-Config.json'; + this.errMsg = + this.errMsg + '\nNode Authentication can be set with multiPass only. Please set multiPass in RTL-Config.json'; } this.common.rtl_secret2fa = config.secret2fa; } else { @@ -131,18 +180,42 @@ export class ConfigService { this.errMsg = this.errMsg + '\nRTL Password cannot be set with SSO. Please set SSO as 0 or remove password.'; } } - this.common.port = (process.env.PORT) ? this.normalizePort(process.env.PORT) : (config.port) ? this.normalizePort(config.port) : 3000; - this.common.host = (process.env.HOST) ? process.env.HOST : (config.host) ? config.host : null; + this.common.port = process.env.PORT + ? this.normalizePort(process.env.PORT) + : config.port + ? this.normalizePort(config.port) + : 3000; + this.common.ssl = process.env.SSL + ? this.normalizeSSL(process.env.SSL) + : config.ssl + ? this.normalizeSSL(config.ssl) + : false; + this.common.host = process.env.HOST ? process.env.HOST : config.host ? config.host : null; if (config.nodes && config.nodes.length > 0) { config.nodes.forEach((node, idx) => { this.common.nodes[idx] = {}; this.common.nodes[idx].index = node.index; this.common.nodes[idx].ln_node = node.lnNode; - this.common.nodes[idx].ln_implementation = (process.env.LN_IMPLEMENTATION) ? process.env.LN_IMPLEMENTATION : node.lnImplementation ? node.lnImplementation : 'LND'; - if (this.common.nodes[idx].ln_implementation === 'CLT') { this.common.nodes[idx].ln_implementation = 'CLN'; } - if (this.common.nodes[idx].ln_implementation !== 'ECL' && process.env.MACAROON_PATH && process.env.MACAROON_PATH.trim() !== '') { + this.common.nodes[idx].ln_implementation = process.env.LN_IMPLEMENTATION + ? process.env.LN_IMPLEMENTATION + : node.lnImplementation + ? node.lnImplementation + : 'LND'; + if (this.common.nodes[idx].ln_implementation === 'CLT') { + this.common.nodes[idx].ln_implementation = 'CLN'; + } + if ( + this.common.nodes[idx].ln_implementation !== 'ECL' && + process.env.MACAROON_PATH && + process.env.MACAROON_PATH.trim() !== '' + ) { this.common.nodes[idx].macaroon_path = process.env.MACAROON_PATH; - } else if (this.common.nodes[idx].ln_implementation !== 'ECL' && node.Authentication && node.Authentication.macaroonPath && node.Authentication.macaroonPath.trim() !== '') { + } else if ( + this.common.nodes[idx].ln_implementation !== 'ECL' && + node.Authentication && + node.Authentication.macaroonPath && + node.Authentication.macaroonPath.trim() !== '' + ) { this.common.nodes[idx].macaroon_path = node.Authentication.macaroonPath; } else if (this.common.nodes[idx].ln_implementation !== 'ECL') { this.errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Config.json!'; @@ -164,14 +237,20 @@ export class ConfigService { } else { this.common.nodes[idx].config_path = ''; } - if (this.common.nodes[idx].ln_implementation === 'ECL' && this.common.nodes[idx].ln_api_password === '' && this.common.nodes[idx].config_path !== '') { + if ( + this.common.nodes[idx].ln_implementation === 'ECL' && + this.common.nodes[idx].ln_api_password === '' && + this.common.nodes[idx].config_path !== '' + ) { try { const exists = fs.existsSync(this.common.nodes[idx].config_path); if (exists) { try { const configFile = fs.readFileSync(this.common.nodes[idx].config_path, 'utf-8'); const iniParsed = ini.parse(configFile); - this.common.nodes[idx].ln_api_password = iniParsed['eclair.api.password'] ? iniParsed['eclair.api.password'] : parseHocon(configFile).eclair.api.password; + this.common.nodes[idx].ln_api_password = iniParsed['eclair.api.password'] + ? iniParsed['eclair.api.password'] + : parseHocon(configFile).eclair.api.password; } catch (err) { this.errMsg = this.errMsg + '\nSomething went wrong while reading config file: \n' + err; } @@ -183,19 +262,32 @@ export class ConfigService { } } if (this.common.nodes[idx].ln_implementation === 'ECL' && this.common.nodes[idx].ln_api_password === '') { - this.errMsg = this.errMsg + '\nPlease set config path Or api password for node index ' + node.index + ' in RTL-Config.json! It is mandatory for Eclair authentication!'; + this.errMsg = + this.errMsg + + '\nPlease set config path Or api password for node index ' + + node.index + + ' in RTL-Config.json! It is mandatory for Eclair authentication!'; } if (process.env.LN_SERVER_URL && process.env.LN_SERVER_URL.trim() !== '') { - this.common.nodes[idx].ln_server_url = process.env.LN_SERVER_URL.endsWith('/v1') ? process.env.LN_SERVER_URL.slice(0, -3) : process.env.LN_SERVER_URL; + this.common.nodes[idx].ln_server_url = process.env.LN_SERVER_URL.endsWith('/v1') + ? process.env.LN_SERVER_URL.slice(0, -3) + : process.env.LN_SERVER_URL; } else if (process.env.LND_SERVER_URL && process.env.LND_SERVER_URL.trim() !== '') { - this.common.nodes[idx].ln_server_url = process.env.LND_SERVER_URL.endsWith('/v1') ? process.env.LND_SERVER_URL.slice(0, -3) : process.env.LND_SERVER_URL; + this.common.nodes[idx].ln_server_url = process.env.LND_SERVER_URL.endsWith('/v1') + ? process.env.LND_SERVER_URL.slice(0, -3) + : process.env.LND_SERVER_URL; } else if (node.Settings.lnServerUrl && node.Settings.lnServerUrl.trim() !== '') { - this.common.nodes[idx].ln_server_url = node.Settings.lnServerUrl.endsWith('/v1') ? node.Settings.lnServerUrl.slice(0, -3) : node.Settings.lnServerUrl; + this.common.nodes[idx].ln_server_url = node.Settings.lnServerUrl.endsWith('/v1') + ? node.Settings.lnServerUrl.slice(0, -3) + : node.Settings.lnServerUrl; } else if (node.Settings.lndServerUrl && node.Settings.lndServerUrl.trim() !== '') { - this.common.nodes[idx].ln_server_url = node.Settings.lndServerUrl.endsWith('/v1') ? node.Settings.lndServerUrl.slice(0, -3) : node.Settings.lndServerUrl; + this.common.nodes[idx].ln_server_url = node.Settings.lndServerUrl.endsWith('/v1') + ? node.Settings.lndServerUrl.slice(0, -3) + : node.Settings.lndServerUrl; } else { - this.errMsg = this.errMsg + '\nPlease set LN Server URL for node index ' + node.index + ' in RTL-Config.json!'; + this.errMsg = + this.errMsg + '\nPlease set LN Server URL for node index ' + node.index + ' in RTL-Config.json!'; } this.common.nodes[idx].user_persona = node.Settings.userPersona ? node.Settings.userPersona : 'MERCHANT'; this.common.nodes[idx].theme_mode = node.Settings.themeMode ? node.Settings.themeMode : 'DAY'; @@ -206,28 +298,52 @@ export class ConfigService { this.common.nodes[idx].currency_unit = node.Settings.currencyUnit ? node.Settings.currencyUnit : 'USD'; } if (process.env.SWAP_SERVER_URL && process.env.SWAP_SERVER_URL.trim() !== '') { - this.common.nodes[idx].swap_server_url = process.env.SWAP_SERVER_URL.endsWith('/v1') ? process.env.SWAP_SERVER_URL.slice(0, -3) : process.env.SWAP_SERVER_URL; + this.common.nodes[idx].swap_server_url = process.env.SWAP_SERVER_URL.endsWith('/v1') + ? process.env.SWAP_SERVER_URL.slice(0, -3) + : process.env.SWAP_SERVER_URL; this.common.nodes[idx].swap_macaroon_path = process.env.SWAP_MACAROON_PATH; } else if (node.Settings.swapServerUrl && node.Settings.swapServerUrl.trim() !== '') { - this.common.nodes[idx].swap_server_url = node.Settings.swapServerUrl.endsWith('/v1') ? node.Settings.swapServerUrl.slice(0, -3) : node.Settings.swapServerUrl; - this.common.nodes[idx].swap_macaroon_path = node.Authentication.swapMacaroonPath ? node.Authentication.swapMacaroonPath : ''; + this.common.nodes[idx].swap_server_url = node.Settings.swapServerUrl.endsWith('/v1') + ? node.Settings.swapServerUrl.slice(0, -3) + : node.Settings.swapServerUrl; + this.common.nodes[idx].swap_macaroon_path = node.Authentication.swapMacaroonPath + ? node.Authentication.swapMacaroonPath + : ''; } else { this.common.nodes[idx].swap_server_url = ''; this.common.nodes[idx].swap_macaroon_path = ''; } if (process.env.BOLTZ_SERVER_URL && process.env.BOLTZ_SERVER_URL.trim() !== '') { - this.common.nodes[idx].boltz_server_url = process.env.BOLTZ_SERVER_URL.endsWith('/v1') ? process.env.BOLTZ_SERVER_URL.slice(0, -3) : process.env.BOLTZ_SERVER_URL; + this.common.nodes[idx].boltz_server_url = process.env.BOLTZ_SERVER_URL.endsWith('/v1') + ? process.env.BOLTZ_SERVER_URL.slice(0, -3) + : process.env.BOLTZ_SERVER_URL; this.common.nodes[idx].boltz_macaroon_path = process.env.BOLTZ_MACAROON_PATH; } else if (node.Settings.boltzServerUrl && node.Settings.boltzServerUrl.trim() !== '') { - this.common.nodes[idx].boltz_server_url = node.Settings.boltzServerUrl.endsWith('/v1') ? node.Settings.boltzServerUrl.slice(0, -3) : node.Settings.boltzServerUrl; - this.common.nodes[idx].boltz_macaroon_path = node.Authentication.boltzMacaroonPath ? node.Authentication.boltzMacaroonPath : ''; + this.common.nodes[idx].boltz_server_url = node.Settings.boltzServerUrl.endsWith('/v1') + ? node.Settings.boltzServerUrl.slice(0, -3) + : node.Settings.boltzServerUrl; + this.common.nodes[idx].boltz_macaroon_path = node.Authentication.boltzMacaroonPath + ? node.Authentication.boltzMacaroonPath + : ''; } else { this.common.nodes[idx].boltz_server_url = ''; this.common.nodes[idx].boltz_macaroon_path = ''; } - this.common.nodes[idx].enable_offers = process.env.ENABLE_OFFERS ? process.env.ENABLE_OFFERS : (node.Settings.enableOffers) ? node.Settings.enableOffers : false; - this.common.nodes[idx].bitcoind_config_path = process.env.BITCOIND_CONFIG_PATH ? process.env.BITCOIND_CONFIG_PATH : (node.Settings.bitcoindConfigPath) ? node.Settings.bitcoindConfigPath : ''; - this.common.nodes[idx].channel_backup_path = process.env.CHANNEL_BACKUP_PATH ? process.env.CHANNEL_BACKUP_PATH : (node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : this.common.rtl_conf_file_path + sep + 'channels-backup' + sep + 'node-' + node.index; + this.common.nodes[idx].enable_offers = process.env.ENABLE_OFFERS + ? process.env.ENABLE_OFFERS + : node.Settings.enableOffers + ? node.Settings.enableOffers + : false; + this.common.nodes[idx].bitcoind_config_path = process.env.BITCOIND_CONFIG_PATH + ? process.env.BITCOIND_CONFIG_PATH + : node.Settings.bitcoindConfigPath + ? node.Settings.bitcoindConfigPath + : ''; + this.common.nodes[idx].channel_backup_path = process.env.CHANNEL_BACKUP_PATH + ? process.env.CHANNEL_BACKUP_PATH + : node.Settings.channelBackupPath + ? node.Settings.channelBackupPath + : this.common.rtl_conf_file_path + sep + 'channels-backup' + sep + 'node-' + node.index; try { this.common.createDirectory(this.common.nodes[idx].channel_backup_path); const exists = fs.existsSync(this.common.nodes[idx].channel_backup_path + sep + 'channel-all.bak'); @@ -236,21 +352,38 @@ export class ConfigService { if (this.common.nodes[idx].ln_implementation === 'LND') { this.common.getAllNodeAllChannelBackup(this.common.nodes[idx]); } else { - const createStream = fs.createWriteStream(this.common.nodes[idx].channel_backup_path + sep + 'channel-all.bak'); + const createStream = fs.createWriteStream( + this.common.nodes[idx].channel_backup_path + sep + 'channel-all.bak' + ); createStream.end(); } } catch (err) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while creating backup file: \n' + err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while creating backup file: \n' + err + }); } } } catch (err) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while creating the backup directory: \n' + err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while creating the backup directory: \n' + err + }); } this.common.nodes[idx].log_file = this.common.rtl_conf_file_path + '/logs/RTL-Node-' + node.index + '.log'; - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'Config', msg: 'Node Config: ' + JSON.stringify(this.common.nodes[idx]) }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'Config', + msg: 'Node Config: ' + JSON.stringify(this.common.nodes[idx]) + }); const log_file = this.common.nodes[idx].log_file; if (fs.existsSync(log_file)) { - fs.writeFile(log_file, '', () => { }); + fs.writeFile(log_file, '', () => {}); } else { try { const directoryName = dirname(log_file); @@ -258,13 +391,20 @@ export class ConfigService { const createStream = fs.createWriteStream(log_file); createStream.end(); } catch (err) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while creating log file ' + log_file + ': \n' + err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while creating log file ' + log_file + ': \n' + err + }); } } }); } this.setSSOParams(config); - if (this.errMsg && this.errMsg.trim() !== '') { throw new Error(this.errMsg); } + if (this.errMsg && this.errMsg.trim() !== '') { + throw new Error(this.errMsg); + } }; private setSSOParams = (config) => { @@ -307,7 +447,9 @@ export class ConfigService { public setServerConfiguration = () => { try { - this.common.rtl_conf_file_path = (process.env.RTL_CONFIG_PATH) ? process.env.RTL_CONFIG_PATH : join(this.directoryName, '../..'); + this.common.rtl_conf_file_path = process.env.RTL_CONFIG_PATH + ? process.env.RTL_CONFIG_PATH + : join(this.directoryName, '../..'); const confFileFullPath = this.common.rtl_conf_file_path + sep + 'RTL-Config.json'; if (!fs.existsSync(confFileFullPath)) { fs.writeFileSync(confFileFullPath, JSON.stringify(this.setDefaultConfig())); @@ -317,11 +459,15 @@ export class ConfigService { this.validateNodeConfig(config); this.setSelectedNode(config); } catch (err) { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'ERROR', fileName: 'Config', msg: 'Something went wrong while configuring the node server: \n' + err }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'ERROR', + fileName: 'Config', + msg: 'Something went wrong while configuring the node server: \n' + err + }); throw new Error(err); } }; - } export const Config = new ConfigService(); diff --git a/server/utils/cors.ts b/server/utils/cors.ts index 57bec2d7c..81f981316 100644 --- a/server/utils/cors.ts +++ b/server/utils/cors.ts @@ -3,26 +3,35 @@ import { Logger, LoggerService } from './logger.js'; import { Common, CommonService } from './common.js'; class CORS { - public logger: LoggerService = Logger; public common: CommonService = Common; public mount(app: Application): Application { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CORS', msg: 'Setting up CORS..' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'CORS', + msg: 'Setting up CORS..' + }); app.use((req, res, next) => { res.setHeader('Cache-Control', 'no-cache'); - res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, filePath'); + res.setHeader( + 'Access-Control-Allow-Headers', + 'Origin, X-Requested-With, Content-Type, Accept, Authorization, filePath' + ); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE, OPTIONS'); if (process.env.NODE_ENV === 'development') { res.setHeader('Access-Control-Allow-Credentials', 'true'); - res.setHeader('Access-Control-Allow-Origin', req.headers.origin ? req.headers.origin : req.headers.host ? req.headers.host : ''); + res.setHeader( + 'Access-Control-Allow-Origin', + req.headers.origin ? req.headers.origin : req.headers.host ? req.headers.host : '' + ); } next(); }); this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CORS', msg: 'CORS Set' }); return app; - }; - + } } -export default new CORS; +export default new CORS(); diff --git a/server/utils/csrf.ts b/server/utils/csrf.ts index 7b12a82f6..b6026f054 100644 --- a/server/utils/csrf.ts +++ b/server/utils/csrf.ts @@ -4,20 +4,23 @@ import { Logger, LoggerService } from './logger.js'; import { Common, CommonService } from './common.js'; class CSRF { - - public csrfProtection = csurf({ cookie: true }); + public csrfProtection = csurf({ cookie: true, secure: Common.ssl ? true : false }); public logger: LoggerService = Logger; public common: CommonService = Common; public mount(app: Application): Application { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CSRF', msg: 'Setting up CSRF..' }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'CSRF', + msg: 'Setting up CSRF..' + }); if (process.env.NODE_ENV !== 'development') { app.use((req, res, next) => this.csrfProtection(req, res, next)); } this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'CSRF', msg: 'CSRF Set' }); return app; - }; - + } } -export default new CSRF; +export default new CSRF(); diff --git a/server/utils/database.ts b/server/utils/database.ts index 757855b5a..7243e11db 100644 --- a/server/utils/database.ts +++ b/server/utils/database.ts @@ -7,13 +7,12 @@ import { Collections, CollectionsEnum, validateOffer } from '../models/database. import { CommonSelectedNode } from '../models/config.model.js'; export class DatabaseService { - public common: CommonService = Common; public logger: LoggerService = Logger; public dbDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'database'); - public nodeDatabase: { id?: { adapter: DatabaseAdapter, data: Collections } } = {}; + public nodeDatabase: { id?: { adapter: DatabaseAdapter; data: Collections } } = {}; - constructor() { } + constructor() {} loadDatabase(selectedNode: CommonSelectedNode) { try { @@ -23,7 +22,13 @@ export class DatabaseService { this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, 'rtldb', selectedNode); this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData(); } catch (err) { - this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err }); + this.logger.log({ + selectedNode: selectedNode, + level: 'ERROR', + fileName: 'Database', + msg: 'Database Load Error', + error: err + }); } } @@ -47,7 +52,13 @@ export class DatabaseService { }); } - update(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, updatedDocument: any, documentFieldName: string, documentFieldValue: string) { + update( + selectedNode: CommonSelectedNode, + collectionName: CollectionsEnum, + updatedDocument: any, + documentFieldName: string, + documentFieldValue: string + ) { return new Promise((resolve, reject) => { try { if (!selectedNode || !selectedNode.index) { @@ -56,8 +67,13 @@ export class DatabaseService { let foundDocIdx = -1; let foundDoc = null; if (this.nodeDatabase[selectedNode.index].data[collectionName]) { - foundDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex((document: any) => document[documentFieldName] === documentFieldValue); - foundDoc = foundDocIdx > -1 ? JSON.parse(JSON.stringify(this.nodeDatabase[selectedNode.index].data[collectionName][foundDocIdx])) : null; + foundDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex( + (document: any) => document[documentFieldName] === documentFieldValue + ); + foundDoc = + foundDocIdx > -1 + ? JSON.parse(JSON.stringify(this.nodeDatabase[selectedNode.index].data[collectionName][foundDocIdx])) + : null; } if (foundDocIdx > -1 && foundDoc) { for (const docKey in updatedDocument) { @@ -88,14 +104,23 @@ export class DatabaseService { }); } - find(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, documentFieldName?: string, documentFieldValue?: string) { + find( + selectedNode: CommonSelectedNode, + collectionName: CollectionsEnum, + documentFieldName?: string, + documentFieldValue?: string + ) { return new Promise((resolve, reject) => { try { if (!selectedNode || !selectedNode.index) { reject(new Error('Selected Node Config Not Found.')); } if (documentFieldName && documentFieldValue) { - resolve(this.nodeDatabase[selectedNode.index].data[collectionName].find((document: any) => document[documentFieldName] === documentFieldValue)); + resolve( + this.nodeDatabase[selectedNode.index].data[collectionName].find( + (document: any) => document[documentFieldName] === documentFieldValue + ) + ); } else { resolve(this.nodeDatabase[selectedNode.index].data[collectionName]); } @@ -105,13 +130,20 @@ export class DatabaseService { }); } - destroy(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, documentFieldName: string, documentFieldValue: string) { + destroy( + selectedNode: CommonSelectedNode, + collectionName: CollectionsEnum, + documentFieldName: string, + documentFieldValue: string + ) { return new Promise((resolve, reject) => { try { if (!selectedNode || !selectedNode.index) { reject(new Error('Selected Node Config Not Found.')); } - const removeDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex((document) => document[documentFieldName] === documentFieldValue); + const removeDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex( + (document) => document[documentFieldName] === documentFieldValue + ); if (removeDocIdx > -1) { this.nodeDatabase[selectedNode.index].data[collectionName].splice(removeDocIdx, 1); } else { @@ -131,23 +163,49 @@ export class DatabaseService { return validateOffer(documentToValidate); default: - return ({ isValid: false, error: 'Collection does not exist' }); + return { isValid: false, error: 'Collection does not exist' }; } } saveDatabase(nodeIndex: number) { try { - const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null; + const selNode = + this.nodeDatabase[nodeIndex] && + this.nodeDatabase[nodeIndex].adapter && + this.nodeDatabase[nodeIndex].adapter.selNode + ? this.nodeDatabase[nodeIndex].adapter.selNode + : null; if (!this.nodeDatabase[nodeIndex]) { - this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error: Selected Node Setup Not Found.' }); + this.logger.log({ + selectedNode: selNode, + level: 'ERROR', + fileName: 'Database', + msg: 'Database Save Error: Selected Node Setup Not Found.' + }); throw new Error('Database Save Error: Selected Node Setup Not Found.'); } this.nodeDatabase[nodeIndex].adapter.saveData(this.nodeDatabase[nodeIndex].data); - this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'INFO', fileName: 'Database', msg: 'Database Saved' }); + this.logger.log({ + selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, + level: 'INFO', + fileName: 'Database', + msg: 'Database Saved' + }); return true; } catch (err) { - const selNode = this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter && this.nodeDatabase[nodeIndex].adapter.selNode ? this.nodeDatabase[nodeIndex].adapter.selNode : null; - this.logger.log({ selectedNode: selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error', error: err }); + const selNode = + this.nodeDatabase[nodeIndex] && + this.nodeDatabase[nodeIndex].adapter && + this.nodeDatabase[nodeIndex].adapter.selNode + ? this.nodeDatabase[nodeIndex].adapter.selNode + : null; + this.logger.log({ + selectedNode: selNode, + level: 'ERROR', + fileName: 'Database', + msg: 'Database Save Error', + error: err + }); return new Error(err); } } @@ -156,11 +214,9 @@ export class DatabaseService { this.saveDatabase(nodeIndex); this.nodeDatabase[nodeIndex] = null; } - } export class DatabaseAdapter { - private dbFile = ''; constructor(public dbDirectoryPath: string, public fileName: string, private selNode: CommonSelectedNode = null) { @@ -184,7 +240,7 @@ export class DatabaseAdapter { } try { const dataFromFile = fs.readFileSync(this.dbFile, 'utf-8'); - return !dataFromFile ? null : (JSON.parse(dataFromFile)); + return !dataFromFile ? null : JSON.parse(dataFromFile); } catch (err) { return new Error('Database Read Error ' + JSON.stringify(err)); } @@ -206,7 +262,6 @@ export class DatabaseAdapter { return new Error('Database Write Error ' + JSON.stringify(err)); } } - } export const Database = new DatabaseService(); diff --git a/server/utils/logger.ts b/server/utils/logger.ts index c3f1da360..15428fbe2 100644 --- a/server/utils/logger.ts +++ b/server/utils/logger.ts @@ -3,29 +3,47 @@ import * as fs from 'fs'; import { LogJSONObj } from '../models/config.model.js'; export class LoggerService { - log = (msgJSON: LogJSONObj) => { - let msgStr = '[' + new Date().toLocaleString() + '] ' + msgJSON.level + ': ' + msgJSON.fileName + ' => ' + msgJSON.msg; + let msgStr = + '[' + new Date().toLocaleString() + '] ' + msgJSON.level + ': ' + msgJSON.fileName + ' => ' + msgJSON.msg; switch (msgJSON.level) { case 'ERROR': if (msgJSON.error) { - msgStr = msgStr + ': ' + ((msgJSON.error.error && msgJSON.error.error.message && typeof msgJSON.error.error.message === 'string') ? msgJSON.error.error.message : (typeof msgJSON.error === 'object' && msgJSON.error.message && typeof msgJSON.error.message === 'string') ? msgJSON.error.message : (typeof msgJSON.error === 'object' && msgJSON.error.stack && typeof msgJSON.error.stack === 'string') ? - msgJSON.error.stack : (typeof msgJSON.error === 'object') ? JSON.stringify(msgJSON.error) : (typeof msgJSON.error === 'string') ? msgJSON.error : '') + '\r\n'; + msgStr = + msgStr + + ': ' + + (msgJSON.error.error && msgJSON.error.error.message && typeof msgJSON.error.error.message === 'string' + ? msgJSON.error.error.message + : typeof msgJSON.error === 'object' && msgJSON.error.message && typeof msgJSON.error.message === 'string' + ? msgJSON.error.message + : typeof msgJSON.error === 'object' && msgJSON.error.stack && typeof msgJSON.error.stack === 'string' + ? msgJSON.error.stack + : typeof msgJSON.error === 'object' + ? JSON.stringify(msgJSON.error) + : typeof msgJSON.error === 'string' + ? msgJSON.error + : '') + + '\r\n'; } else { msgStr = msgStr + '.\r\n'; } console.error(msgStr); if (msgJSON.selectedNode && msgJSON.selectedNode.log_file) { - fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { }); + fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => {}); } break; case 'WARN': msgStr = prepMsgData(msgJSON, msgStr); - if (!msgJSON.selectedNode || msgJSON.selectedNode.log_level === 'WARN' || msgJSON.selectedNode.log_level === 'INFO' || msgJSON.selectedNode.log_level === 'DEBUG') { + if ( + !msgJSON.selectedNode || + msgJSON.selectedNode.log_level === 'WARN' || + msgJSON.selectedNode.log_level === 'INFO' || + msgJSON.selectedNode.log_level === 'DEBUG' + ) { console.warn(msgStr); if (msgJSON.selectedNode && msgJSON.selectedNode.log_file) { - fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { }); + fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => {}); } } break; @@ -37,13 +55,13 @@ export class LoggerService { msgStr = msgStr + '.\r\n'; console.log(msgStr); if (msgJSON.selectedNode.log_file) { - fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { }); + fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => {}); } } else if (msgJSON.selectedNode && msgJSON.selectedNode.log_level === 'DEBUG') { msgStr = prepMsgData(msgJSON, msgStr); console.log(msgStr); if (msgJSON.selectedNode.log_file) { - fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { }); + fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => {}); } } break; @@ -55,7 +73,7 @@ export class LoggerService { msgStr = prepMsgData(msgJSON, msgStr); console.log(msgStr); if (msgJSON.selectedNode.log_file) { - fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => { }); + fs.appendFile(msgJSON.selectedNode.log_file, msgStr, () => {}); } } break; @@ -65,12 +83,23 @@ export class LoggerService { break; } }; - -}; +} const prepMsgData = (msgJSON, msgStr) => { if (msgJSON.data) { - msgStr = msgStr + ': ' + (typeof msgJSON.data === 'object' ? (msgJSON.data.message && typeof msgJSON.data.message === 'string') ? msgJSON.data.message : (msgJSON.data.stack && typeof msgJSON.data.stack === 'string') ? msgJSON.data.stack : JSON.stringify(msgJSON.data) : (typeof msgJSON.data === 'string') ? msgJSON.data : '') + '\r\n'; + msgStr = + msgStr + + ': ' + + (typeof msgJSON.data === 'object' + ? msgJSON.data.message && typeof msgJSON.data.message === 'string' + ? msgJSON.data.message + : msgJSON.data.stack && typeof msgJSON.data.stack === 'string' + ? msgJSON.data.stack + : JSON.stringify(msgJSON.data) + : typeof msgJSON.data === 'string' + ? msgJSON.data + : '') + + '\r\n'; } else { msgStr = msgStr + '.\r\n'; } diff --git a/server/utils/ssl.ts b/server/utils/ssl.ts new file mode 100644 index 000000000..b26341547 --- /dev/null +++ b/server/utils/ssl.ts @@ -0,0 +1,108 @@ +import * as fs from 'fs'; +import CertificateFactory from './certificateFactory.js'; +import { Logger, LoggerService } from './logger.js'; + +const { assign } = Object; + +class SSL { + private logger: LoggerService = Logger; + private key: Buffer = undefined; + private cert: Buffer = undefined; + private ca: Buffer = undefined; + private requestCert: Boolean = false; + private rejectUnauthorized: Boolean = false; + + constructor(options: any = true) { + const factoryOptions: any = {}; + const filePaths: any = {}; + if (typeof options === 'object') { + const { + altIp, + commonName, + countryName, + encryptionBits, + stateName, + localityName, + organizationName, + organizationalUnit, + validForYears, + requestCert, + rejectUnauthorized + } = options; + assign(factoryOptions, { + altIp, + commonName, + countryName, + encryptionBits, + stateName, + localityName, + organizationName, + organizationalUnit, + validForYears + }); + filePaths.key = options.key; + filePaths.cert = options.cert; + filePaths.ca = options.ca; + if (typeof requestCert === 'boolean') { + this.requestCert = requestCert; + } + if (typeof rejectUnauthorized === 'boolean') { + this.rejectUnauthorized = rejectUnauthorized; + } + } + try { + if (filePaths.key) { + this.key = fs.readFileSync(filePaths.key); + } + if (filePaths.cert) { + this.cert = fs.readFileSync(filePaths.cert); + } + if (filePaths.ca) { + this.ca = fs.readFileSync(filePaths.ca); + } + } catch (e) { + this.logger.log({ + level: 'ERROR', + fileName: 'SSL', + msg: `Error reading certificate files - ${e.toString()}` + }); + throw e; + } + + if (!this.key && !this.cert && !this.ca) { + try { + const selfSignedCertificateFactory = new CertificateFactory(factoryOptions); + assign(this, selfSignedCertificateFactory.getStaticBundle('buffer')); + } catch (e) { + this.logger.log({ + level: 'ERROR', + fileName: 'SSL', + msg: `Error generating self-signed certificates - ${e.toString()}` + }); + throw e; + } + } + } + + //--------------------------------------------------------------------------------------- + //Method: toObject + // + //Input: none + //Output: ssl server configuration object + // + //This method returns https server config options generated by this class + //--------------------------------------------------------------------------------------- + public toObject() { + const { key, cert, ca, rejectUnauthorized, requestCert } = this; + + return { + key, + cert, + ca, + rejectUnauthorized, + requestCert + }; + } +} + +export default SSL; diff --git a/server/utils/webSocketServer.ts b/server/utils/webSocketServer.ts index 48c473e4c..cc4c7de90 100644 --- a/server/utils/webSocketServer.ts +++ b/server/utils/webSocketServer.ts @@ -2,7 +2,6 @@ import { parse } from 'cookie'; import * as cookieParser from 'cookie-parser'; import * as crypto from 'crypto'; import WebSocket from 'ws'; -import { Application } from 'express'; import { Logger, LoggerService } from './logger.js'; import { Common, CommonService } from './common.js'; import { verifyWSUser } from './authCheck.js'; @@ -10,10 +9,9 @@ import { EventEmitter } from 'events'; import { CommonSelectedNode } from '../models/config.model.js'; export class WebSocketServer { - public logger: LoggerService = Logger; public common: CommonService = Common; - public clientDetails: Array<{ index: number, sessionIds: Array }> = []; + public clientDetails: Array<{ index: number; sessionIds: Array }> = []; public eventEmitterCLN = new EventEmitter(); public eventEmitterECL = new EventEmitter(); public eventEmitterLND = new EventEmitter(); @@ -33,8 +31,17 @@ export class WebSocketServer { }, 1000 * 60 * 60); // Terminate broken connections every hour public mount = (httpServer) => { - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Connecting Websocket Server..' }); - this.webSocketServer = new WebSocket.Server({ noServer: true, path: this.common.baseHref + '/api/ws', verifyClient: (process.env.NODE_ENV === 'development') ? null : verifyWSUser }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'WebSocketServer', + msg: 'Connecting Websocket Server..' + }); + this.webSocketServer = new WebSocket.Server({ + noServer: true, + path: this.common.baseHref + '/api/ws', + verifyClient: process.env.NODE_ENV === 'development' ? null : verifyWSUser + }); httpServer.on('upgrade', (request, socket, head) => { if (request.headers['upgrade'] !== 'websocket') { socket.end('HTTP/1.1 400 Bad Request'); @@ -42,34 +49,68 @@ export class WebSocketServer { } const acceptKey = request.headers['sec-websocket-key']; const hash = this.generateAcceptValue(acceptKey); - const responseHeaders = ['HTTP/1.1 101 Web Socket Protocol Handshake', 'Upgrade: WebSocket', 'Connection: Upgrade', 'Sec-WebSocket-Accept: ' + hash]; - const protocols = !request.headers['sec-websocket-protocol'] ? [] : request.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); - if (protocols.includes('json')) { responseHeaders.push('Sec-WebSocket-Protocol: json'); } + const responseHeaders = [ + 'HTTP/1.1 101 Web Socket Protocol Handshake', + 'Upgrade: WebSocket', + 'Connection: Upgrade', + 'Sec-WebSocket-Accept: ' + hash + ]; + const protocols = !request.headers['sec-websocket-protocol'] + ? [] + : request.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); + if (protocols.includes('json')) { + responseHeaders.push('Sec-WebSocket-Protocol: json'); + } this.webSocketServer.handleUpgrade(request, socket, head, this.upgradeCallback); }); this.webSocketServer.on('connection', this.mountEventsOnConnection); this.webSocketServer.on('close', () => clearInterval(this.pingInterval)); - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Websocket Server Connected' }); - } + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'WebSocketServer', + msg: 'Websocket Server Connected' + }); + }; public upgradeCallback = (websocket, request) => { this.webSocketServer.emit('connection', websocket, request); }; public mountEventsOnConnection = (websocket, request) => { - const protocols = !request.headers['sec-websocket-protocol'] ? [] : request.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); + const protocols = !request.headers['sec-websocket-protocol'] + ? [] + : request.headers['sec-websocket-protocol'].split(',').map((s) => s.trim()); const cookies = parse(request.headers.cookie); websocket.clientId = Date.now(); websocket.isAlive = true; websocket.sessionId = cookieParser.signedCookie(cookies['connect.sid'], this.common.secret_key); websocket.clientNodeIndex = +protocols[1]; - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Connected: ' + websocket.clientId + ', Total WS clients: ' + this.webSocketServer.clients.size }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'WebSocketServer', + msg: 'Connected: ' + websocket.clientId + ', Total WS clients: ' + this.webSocketServer.clients.size + }); websocket.on('error', this.sendErrorToAllLNClients); websocket.on('message', this.sendEventsToAllLNClients); - websocket.on('pong', () => { websocket.isAlive = true; }); + websocket.on('pong', () => { + websocket.isAlive = true; + }); websocket.on('close', (code, reason) => { this.updateLNWSClientDetails(websocket.sessionId, -1, websocket.clientNodeIndex); - this.logger.log({ selectedNode: this.common.initSelectedNode, level: 'INFO', fileName: 'WebSocketServer', msg: 'Disconnected due to ' + code + ' : ' + websocket.clientId + ', Total WS clients: ' + this.webSocketServer.clients.size }); + this.logger.log({ + selectedNode: this.common.initSelectedNode, + level: 'INFO', + fileName: 'WebSocketServer', + msg: + 'Disconnected due to ' + + code + + ' : ' + + websocket.clientId + + ', Total WS clients: ' + + this.webSocketServer.clients.size + }); }); }; @@ -88,9 +129,14 @@ export class WebSocketServer { this.connectToNodeClient(sessionId, currNodeIndex); } else { const selectedNode = this.common.findNode(currNodeIndex); - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Invalid Node Selection. Previous and current node indices can not be less than zero.' }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'ERROR', + fileName: 'WebSocketServer', + msg: 'Invalid Node Selection. Previous and current node indices can not be less than zero.' + }); } - } + }; public disconnectFromNodeClient = (sessionId: string, prevNodeIndex: number) => { const foundClient = this.clientDetails.find((clientDetail) => clientDetail.index === +prevNodeIndex); @@ -120,7 +166,7 @@ export class WebSocketServer { } } } - } + }; public connectToNodeClient = (sessionId: string, currNodeIndex: number) => { let foundClient = this.clientDetails.find((clientDetail) => clientDetail.index === +currNodeIndex); @@ -149,18 +195,28 @@ export class WebSocketServer { } } } - } + }; public sendErrorToAllLNClients = (serverError, selectedNode: CommonSelectedNode) => { try { this.webSocketServer.clients.forEach((client) => { - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Broadcasting error to clients...: ' + serverError }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'ERROR', + fileName: 'WebSocketServer', + msg: 'Broadcasting error to clients...: ' + serverError + }); if (+client.clientNodeIndex === +selectedNode.index) { client.send(serverError); } }); } catch (err) { - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Error while broadcasting message: ' + JSON.stringify(err) }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'ERROR', + fileName: 'WebSocketServer', + msg: 'Error while broadcasting message: ' + JSON.stringify(err) + }); } }; @@ -168,20 +224,32 @@ export class WebSocketServer { try { this.webSocketServer.clients.forEach((client) => { if (+client.clientNodeIndex === +selectedNode.index) { - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'DEBUG', fileName: 'WebSocketServer', msg: 'Broadcasting message to client...: ' + client.clientId }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'DEBUG', + fileName: 'WebSocketServer', + msg: 'Broadcasting message to client...: ' + client.clientId + }); client.send(newMessage); } }); } catch (err) { - this.logger.log({ selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, level: 'ERROR', fileName: 'WebSocketServer', msg: 'Error while broadcasting message: ' + JSON.stringify(err) }); + this.logger.log({ + selectedNode: !selectedNode ? this.common.initSelectedNode : selectedNode, + level: 'ERROR', + fileName: 'WebSocketServer', + msg: 'Error while broadcasting message: ' + JSON.stringify(err) + }); } }; - public generateAcceptValue = (acceptKey) => crypto.createHash('sha1').update(acceptKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary').digest('base64'); + public generateAcceptValue = (acceptKey) => + crypto + .createHash('sha1') + .update(acceptKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary') + .digest('base64'); public getClients = () => this.webSocketServer.clients; - } export const WSServer = new WebSocketServer(); -