From 1e6786a850861b6f939a97949c3c0b2e3eea0476 Mon Sep 17 00:00:00 2001 From: Shahana Farooqui Date: Mon, 16 May 2022 14:45:41 -0400 Subject: [PATCH] Enable/Disable with listConfigs Enable/Disable with listConfigs --- backend/controllers/cln/message.js | 37 -- backend/controllers/cln/payments.js | 15 - backend/controllers/cln/utility.js | 97 ++++ backend/routes/cln/index.js | 6 +- backend/routes/cln/message.js | 8 - backend/routes/cln/payments.js | 3 +- backend/routes/cln/utility.js | 11 + server/controllers/cln/message.ts | 35 -- server/controllers/cln/payments.ts | 14 - server/controllers/cln/utility.ts | 94 ++++ server/routes/cln/index.ts | 6 +- server/routes/cln/message.ts | 11 - server/routes/cln/payments.ts | 3 +- server/routes/cln/utility.ts | 14 + .../experimental-settings.component.html | 2 +- .../experimental-settings.component.ts | 59 ++- src/app/shared/services/common.service.ts | 22 +- .../shared/services/consts-enums-functions.ts | 1 + src/app/shared/services/data.service.ts | 436 ++++++++++-------- src/app/shared/test-helpers/mock-services.ts | 31 +- src/app/store/rtl.effects.ts | 2 +- src/environments/environment.prod.ts | 1 + src/environments/environment.ts | 1 + 23 files changed, 521 insertions(+), 388 deletions(-) delete mode 100644 backend/controllers/cln/message.js create mode 100644 backend/controllers/cln/utility.js delete mode 100644 backend/routes/cln/message.js create mode 100644 backend/routes/cln/utility.js delete mode 100644 server/controllers/cln/message.ts create mode 100644 server/controllers/cln/utility.ts delete mode 100644 server/routes/cln/message.ts create mode 100644 server/routes/cln/utility.ts diff --git a/backend/controllers/cln/message.js b/backend/controllers/cln/message.js deleted file mode 100644 index c9331679..00000000 --- a/backend/controllers/cln/message.js +++ /dev/null @@ -1,37 +0,0 @@ -import request from 'request-promise'; -import { Logger } from '../../utils/logger.js'; -import { Common } from '../../utils/common.js'; -let options = null; -const logger = Logger; -const common = 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 }); - } - 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 }); - }); -}; -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 }); - }); -}; diff --git a/backend/controllers/cln/payments.js b/backend/controllers/cln/payments.js index 30852f6f..0bc3b24d 100644 --- a/backend/controllers/cln/payments.js +++ b/backend/controllers/cln/payments.js @@ -82,21 +82,6 @@ export const listPayments = (req, res, next) => { 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..' }); - 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 }); - 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) { diff --git a/backend/controllers/cln/utility.js b/backend/controllers/cln/utility.js new file mode 100644 index 00000000..b3f7c7fd --- /dev/null +++ b/backend/controllers/cln/utility.js @@ -0,0 +1,97 @@ +import request from 'request-promise'; +import { Logger } from '../../utils/logger.js'; +import { Common } from '../../utils/common.js'; +let options = null; +const logger = Logger; +const common = Common; +export const decodePaymentFromPaymentRequest = (selNode, payment) => { + options.url = selNode.ln_server_url + '/v1/utility/decode/' + payment; + return request(options).then((res) => { + logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Decode Received', data: res }); + return res; + }).catch((err) => { }); +}; +export const decodePayments = (req, res, next) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', 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: 'Payments', msg: 'Payment List Decoded', data: values }); + res.status(200).json(values); + }). + catch((errRes) => { + const err = common.handleError(errRes, 'Payments', '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: 'Payments', msg: 'Empty Payment List Decoded' }); + return res.status(200).json([]); + } +}; +export const decodePayment = (req, res, next) => { + 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 }); + 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 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 }); + } + 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 }); + }); +}; +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 }); + }); +}; +export const listConfigs = (req, res, next) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Utility', msg: 'List Configs..' }); + 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/listConfigs'; + request(options).then((body) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Utility', msg: 'List Configs Received', data: body }); + res.status(200).json(body); + }).catch((errRes) => { + const err = common.handleError(errRes, 'Utility', 'List Configs Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); +}; diff --git a/backend/routes/cln/index.js b/backend/routes/cln/index.js index eb5fd03d..e679145f 100644 --- a/backend/routes/cln/index.js +++ b/backend/routes/cln/index.js @@ -9,8 +9,8 @@ import onChainCLRoutes from './onchain.js'; import paymentsCLRoutes from './payments.js'; import peersCLRoutes from './peers.js'; import networkCLRoutes from './network.js'; -import messageCLRoutes from './message.js'; import offersCLRoutes from './offers.js'; +import utilityCLRoutes from './utility.js'; const router = Router(); const clRoutes = [ { path: '/getinfo', route: infoCLRoutes }, @@ -22,8 +22,8 @@ const clRoutes = [ { path: '/payments', route: paymentsCLRoutes }, { path: '/peers', route: peersCLRoutes }, { path: '/network', route: networkCLRoutes }, - { path: '/message', route: messageCLRoutes }, - { path: '/offers', route: offersCLRoutes } + { path: '/offers', route: offersCLRoutes }, + { path: '/utility', route: utilityCLRoutes } ]; clRoutes.forEach((route) => { router.use(route.path, route.route); diff --git a/backend/routes/cln/message.js b/backend/routes/cln/message.js deleted file mode 100644 index 3e936413..00000000 --- a/backend/routes/cln/message.js +++ /dev/null @@ -1,8 +0,0 @@ -import exprs from 'express'; -const { Router } = exprs; -import { isAuthenticated } from '../../utils/authCheck.js'; -import { signMessage, verifyMessage } from '../../controllers/cln/message.js'; -const router = Router(); -router.post('/sign', isAuthenticated, signMessage); -router.post('/verify', isAuthenticated, verifyMessage); -export default router; diff --git a/backend/routes/cln/payments.js b/backend/routes/cln/payments.js index b1f0b04a..2f4c6cab 100644 --- a/backend/routes/cln/payments.js +++ b/backend/routes/cln/payments.js @@ -1,9 +1,8 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { listPayments, decodePayment, postPayment } from '../../controllers/cln/payments.js'; +import { listPayments, postPayment } from '../../controllers/cln/payments.js'; const router = Router(); router.get('/', isAuthenticated, listPayments); -router.get('/decode/:payReq', isAuthenticated, decodePayment); router.post('/', isAuthenticated, postPayment); export default router; diff --git a/backend/routes/cln/utility.js b/backend/routes/cln/utility.js new file mode 100644 index 00000000..a8ef800d --- /dev/null +++ b/backend/routes/cln/utility.js @@ -0,0 +1,11 @@ +import exprs from 'express'; +const { Router } = exprs; +import { isAuthenticated } from '../../utils/authCheck.js'; +import { decodePayments, decodePayment, signMessage, verifyMessage, listConfigs } from '../../controllers/cln/utility.js'; +const router = Router(); +router.get('/', isAuthenticated, decodePayments); +router.get('/decode/:payReq', isAuthenticated, decodePayment); +router.post('/sign', isAuthenticated, signMessage); +router.post('/verify', isAuthenticated, verifyMessage); +router.get('/listConfigs', isAuthenticated, listConfigs); +export default router; diff --git a/server/controllers/cln/message.ts b/server/controllers/cln/message.ts deleted file mode 100644 index 109115c8..00000000 --- a/server/controllers/cln/message.ts +++ /dev/null @@ -1,35 +0,0 @@ -import request from 'request-promise'; -import { Logger, LoggerService } from '../../utils/logger.js'; -import { Common, CommonService } from '../../utils/common.js'; -let options = null; -const logger: LoggerService = Logger; -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 }); } - 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 }); - }); -}; - -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 }); - }); -}; diff --git a/server/controllers/cln/payments.ts b/server/controllers/cln/payments.ts index 8aafd9ef..65c7c914 100644 --- a/server/controllers/cln/payments.ts +++ b/server/controllers/cln/payments.ts @@ -74,20 +74,6 @@ export const listPayments = (req, res, next) => { }); }; -export const decodePayment = (req, res, next) => { - 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 }); - 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 }); } diff --git a/server/controllers/cln/utility.ts b/server/controllers/cln/utility.ts new file mode 100644 index 00000000..17728a8f --- /dev/null +++ b/server/controllers/cln/utility.ts @@ -0,0 +1,94 @@ +import request from 'request-promise'; +import { Logger, LoggerService } from '../../utils/logger.js'; +import { Common, CommonService } from '../../utils/common.js'; +import { CommonSelectedNode } from '../../models/config.model.js'; + +let options = null; +const logger: LoggerService = Logger; +const common: CommonService = Common; + +export const decodePaymentFromPaymentRequest = (selNode: CommonSelectedNode, payment) => { + options.url = selNode.ln_server_url + '/v1/utility/decode/' + payment; + return request(options).then((res) => { + logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Decode Received', data: res }); + return res; + }).catch((err) => { }); +}; + +export const decodePayments = (req, res, next) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', 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: 'Payments', msg: 'Payment List Decoded', data: values }); + res.status(200).json(values); + }). + catch((errRes) => { + const err = common.handleError(errRes, 'Payments', '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: 'Payments', msg: 'Empty Payment List Decoded' }); + return res.status(200).json([]); + } +}; + +export const decodePayment = (req, res, next) => { + 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 }); + 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 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 }); } + 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 }); + }); +}; + +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 }); + }); +}; + +export const listConfigs = (req, res, next) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Utility', msg: 'List Configs..' }); + 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/listConfigs'; + request(options).then((body) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Utility', msg: 'List Configs Received', data: body }); + res.status(200).json(body); + }).catch((errRes) => { + const err = common.handleError(errRes, 'Utility', 'List Configs Error', req.session.selectedNode); + return res.status(err.statusCode).json({ message: err.message, error: err.error }); + }); +}; diff --git a/server/routes/cln/index.ts b/server/routes/cln/index.ts index 3d830b99..2e574c2d 100644 --- a/server/routes/cln/index.ts +++ b/server/routes/cln/index.ts @@ -9,8 +9,8 @@ import onChainCLRoutes from './onchain.js'; import paymentsCLRoutes from './payments.js'; import peersCLRoutes from './peers.js'; import networkCLRoutes from './network.js'; -import messageCLRoutes from './message.js'; import offersCLRoutes from './offers.js'; +import utilityCLRoutes from './utility.js'; const router = Router(); @@ -24,8 +24,8 @@ const clRoutes = [ { path: '/payments', route: paymentsCLRoutes }, { path: '/peers', route: peersCLRoutes }, { path: '/network', route: networkCLRoutes }, - { path: '/message', route: messageCLRoutes }, - { path: '/offers', route: offersCLRoutes } + { path: '/offers', route: offersCLRoutes }, + { path: '/utility', route: utilityCLRoutes } ]; clRoutes.forEach((route) => { diff --git a/server/routes/cln/message.ts b/server/routes/cln/message.ts deleted file mode 100644 index 3e828043..00000000 --- a/server/routes/cln/message.ts +++ /dev/null @@ -1,11 +0,0 @@ -import exprs from 'express'; -const { Router } = exprs; -import { isAuthenticated } from '../../utils/authCheck.js'; -import { signMessage, verifyMessage } from '../../controllers/cln/message.js'; - -const router = Router(); - -router.post('/sign', isAuthenticated, signMessage); -router.post('/verify', isAuthenticated, verifyMessage); - -export default router; diff --git a/server/routes/cln/payments.ts b/server/routes/cln/payments.ts index c2472543..d9c01d5b 100644 --- a/server/routes/cln/payments.ts +++ b/server/routes/cln/payments.ts @@ -1,12 +1,11 @@ import exprs from 'express'; const { Router } = exprs; import { isAuthenticated } from '../../utils/authCheck.js'; -import { listPayments, decodePayment, postPayment } from '../../controllers/cln/payments.js'; +import { listPayments, postPayment } from '../../controllers/cln/payments.js'; const router = Router(); router.get('/', isAuthenticated, listPayments); -router.get('/decode/:payReq', isAuthenticated, decodePayment); router.post('/', isAuthenticated, postPayment); export default router; diff --git a/server/routes/cln/utility.ts b/server/routes/cln/utility.ts new file mode 100644 index 00000000..3ff993a4 --- /dev/null +++ b/server/routes/cln/utility.ts @@ -0,0 +1,14 @@ +import exprs from 'express'; +const { Router } = exprs; +import { isAuthenticated } from '../../utils/authCheck.js'; +import { decodePayments, decodePayment, signMessage, verifyMessage, listConfigs } from '../../controllers/cln/utility.js'; + +const router = Router(); + +router.get('/', isAuthenticated, decodePayments); +router.get('/decode/:payReq', isAuthenticated, decodePayment); +router.post('/sign', isAuthenticated, signMessage); +router.post('/verify', isAuthenticated, verifyMessage); +router.get('/listConfigs', isAuthenticated, listConfigs); + +export default router; diff --git a/src/app/shared/components/node-config/experimental-settings/experimental-settings.component.html b/src/app/shared/components/node-config/experimental-settings/experimental-settings.component.html index cb94da62..de5d883d 100644 --- a/src/app/shared/components/node-config/experimental-settings/experimental-settings.component.html +++ b/src/app/shared/components/node-config/experimental-settings/experimental-settings.component.html @@ -9,7 +9,7 @@ Features - +

{{feature.name}}

diff --git a/src/app/shared/components/node-config/experimental-settings/experimental-settings.component.ts b/src/app/shared/components/node-config/experimental-settings/experimental-settings.component.ts index 0896d8d4..c0e4e33f 100644 --- a/src/app/shared/components/node-config/experimental-settings/experimental-settings.component.ts +++ b/src/app/shared/components/node-config/experimental-settings/experimental-settings.component.ts @@ -47,21 +47,15 @@ export class ExperimentalSettingsComponent implements OnInit, OnDestroy { constructor(private logger: LoggerService, private store: Store, private dataService: DataService) { } ngOnInit() { - if (!this.fundingPolicy) { - this.dataService.getOrUpdateFunderPolicy().pipe(takeUntil(this.unSubs[0])).subscribe((res: any) => { - this.logger.info('Received Funder Update Policy: ' + JSON.stringify(res)); - this.fundingPolicy = res; - this.features[1].enabled = !!(this.fundingPolicy && this.fundingPolicy.summary); - if (this.fundingPolicy.policy) { - this.selPolicyType = LADS_POLICY.find((policy) => policy.id === this.fundingPolicy.policy); - } - this.policyMod = this.fundingPolicy.policy_mod || this.fundingPolicy.policy_mod === 0 ? this.fundingPolicy.policy_mod : null; - this.leaseFeeBaseSat = this.fundingPolicy.lease_fee_base_msat ? this.fundingPolicy.lease_fee_base_msat / 1000 : this.fundingPolicy.lease_fee_base_msat === 0 ? 0 : null; - this.leaseFeeBasis = this.fundingPolicy.lease_fee_basis || this.fundingPolicy.lease_fee_basis === 0 ? this.fundingPolicy.lease_fee_basis : null; - this.channelFeeMaxBaseSat = this.fundingPolicy.channel_fee_max_base_msat ? this.fundingPolicy.channel_fee_max_base_msat / 1000 : this.fundingPolicy.channel_fee_max_base_msat === 0 ? 0 : null; - this.channelFeeMaxProportional = this.fundingPolicy.channel_fee_max_proportional_thousandths || this.fundingPolicy.channel_fee_max_proportional_thousandths === 0 ? this.fundingPolicy.channel_fee_max_proportional_thousandths : null; - }); - } + this.dataService.listConfigs().pipe(takeUntil(this.unSubs[0])).subscribe({ + next: (res: any) => { + this.logger.info('Received List Configs: ' + JSON.stringify(res)); + this.features[1].enabled = !!res['experimental-dual-fund']; + }, error: (err) => { + this.logger.error('List Config Error: ' + JSON.stringify(err)); + this.features[1].enabled = false; + } + }); this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[1])). subscribe((selNode) => { this.selNode = selNode; @@ -75,6 +69,23 @@ export class ExperimentalSettingsComponent implements OnInit, OnDestroy { }); } + onPanelExpanded(panelId: number) { + if (panelId === 1 && !this.fundingPolicy) { + this.dataService.getOrUpdateFunderPolicy().pipe(takeUntil(this.unSubs[3])).subscribe((res: any) => { + this.logger.info('Received Funder Update Policy: ' + JSON.stringify(res)); + this.fundingPolicy = res; + if (this.fundingPolicy.policy) { + this.selPolicyType = LADS_POLICY.find((policy) => policy.id === this.fundingPolicy.policy); + } + this.policyMod = this.fundingPolicy.policy_mod || this.fundingPolicy.policy_mod === 0 ? this.fundingPolicy.policy_mod : null; + this.leaseFeeBaseSat = this.fundingPolicy.lease_fee_base_msat ? this.fundingPolicy.lease_fee_base_msat / 1000 : this.fundingPolicy.lease_fee_base_msat === 0 ? 0 : null; + this.leaseFeeBasis = this.fundingPolicy.lease_fee_basis || this.fundingPolicy.lease_fee_basis === 0 ? this.fundingPolicy.lease_fee_basis : null; + this.channelFeeMaxBaseSat = this.fundingPolicy.channel_fee_max_base_msat ? this.fundingPolicy.channel_fee_max_base_msat / 1000 : this.fundingPolicy.channel_fee_max_base_msat === 0 ? 0 : null; + this.channelFeeMaxProportional = this.fundingPolicy.channel_fee_max_proportional_thousandths || this.fundingPolicy.channel_fee_max_proportional_thousandths === 0 ? this.fundingPolicy.channel_fee_max_proportional_thousandths : null; + }); + } + } + onUpdateFeature(): boolean | void { this.logger.info(this.selNode); this.selNode.settings.enableOffers = this.enableOffers; @@ -104,7 +115,7 @@ export class ExperimentalSettingsComponent implements OnInit, OnDestroy { this.flgUpdateCalled = false; this.updateMsg = ''; this.dataService.getOrUpdateFunderPolicy(this.selPolicyType.id, this.policyMod, this.leaseFeeBaseSat, this.leaseFeeBasis, this.channelFeeMaxBaseSat * 1000, this.channelFeeMaxProportional). - pipe(takeUntil(this.unSubs[3])). + pipe(takeUntil(this.unSubs[4])). subscribe({ next: (updatePolicyRes: any) => { this.logger.info(updatePolicyRes); @@ -121,12 +132,16 @@ export class ExperimentalSettingsComponent implements OnInit, OnDestroy { onResetPolicy() { this.flgUpdateCalled = false; this.updateMsg = ''; - this.selPolicyType = LADS_POLICY[0]; - this.policyMod = this.fundingPolicy.policy_mod !== 0 && !this.fundingPolicy.policy_mod ? null : this.fundingPolicy.policy_mod; - this.leaseFeeBaseSat = this.fundingPolicy.lease_fee_base_msat !== 0 && !this.fundingPolicy.lease_fee_base_msat ? null : this.fundingPolicy.lease_fee_base_msat / 1000; - this.leaseFeeBasis = this.fundingPolicy.lease_fee_basis !== 0 && !this.fundingPolicy.lease_fee_basis ? null : this.fundingPolicy.lease_fee_basis; - this.channelFeeMaxBaseSat = this.fundingPolicy.channel_fee_max_base_msat !== 0 && !this.fundingPolicy.channel_fee_max_base_msat ? null : this.fundingPolicy.channel_fee_max_base_msat / 1000; - this.channelFeeMaxProportional = this.fundingPolicy.channel_fee_max_proportional_thousandths !== 0 && !this.fundingPolicy.channel_fee_max_proportional_thousandths ? null : this.fundingPolicy.channel_fee_max_proportional_thousandths; + if (this.fundingPolicy.policy) { + this.selPolicyType = LADS_POLICY.find((policy) => policy.id === this.fundingPolicy.policy); + } else { + this.selPolicyType = LADS_POLICY[0]; + } + this.policyMod = this.fundingPolicy.policy_mod || this.fundingPolicy.policy_mod === 0 ? this.fundingPolicy.policy_mod : null; + this.leaseFeeBaseSat = this.fundingPolicy.lease_fee_base_msat ? this.fundingPolicy.lease_fee_base_msat / 1000 : this.fundingPolicy.lease_fee_base_msat === 0 ? 0 : null; + this.leaseFeeBasis = this.fundingPolicy.lease_fee_basis || this.fundingPolicy.lease_fee_basis === 0 ? this.fundingPolicy.lease_fee_basis : null; + this.channelFeeMaxBaseSat = this.fundingPolicy.channel_fee_max_base_msat ? this.fundingPolicy.channel_fee_max_base_msat / 1000 : this.fundingPolicy.channel_fee_max_base_msat === 0 ? 0 : null; + this.channelFeeMaxProportional = this.fundingPolicy.channel_fee_max_proportional_thousandths || this.fundingPolicy.channel_fee_max_proportional_thousandths === 0 ? this.fundingPolicy.channel_fee_max_proportional_thousandths : null; } ngOnDestroy() { diff --git a/src/app/shared/services/common.service.ts b/src/app/shared/services/common.service.ts index f90e4afd..6d04d726 100644 --- a/src/app/shared/services/common.service.ts +++ b/src/app/shared/services/common.service.ts @@ -307,16 +307,18 @@ export class CommonService implements OnDestroy { } extractErrorMessage(err: any, genericErrorMessage: string = 'Unknown Error.') { - const msg = this.titleCase((err.error && err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.error && typeof err.error.error.error.error.error === 'string') ? err.error.error.error.error.error : - (err.error && err.error.error && err.error.error.error && err.error.error.error.error && typeof err.error.error.error.error === 'string') ? err.error.error.error.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 && typeof err.error === 'string') ? err.error : - (err.error && err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.message && typeof err.error.error.error.error.message === 'string') ? err.error.error.error.error.message : - (err.error && err.error.error && err.error.error.error && err.error.error.error.message && typeof err.error.error.error.message === 'string') ? err.error.error.error.message : - (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.message && typeof err.message === 'string') ? err.message : genericErrorMessage); + const msg = this.titleCase( + (err.error.text && typeof err.error.text === 'string' && err.error.text.includes('')) ? 'API Route Does Not Exist.' : + (err.error && err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.error && typeof err.error.error.error.error.error === 'string') ? err.error.error.error.error.error : + (err.error && err.error.error && err.error.error.error && err.error.error.error.error && typeof err.error.error.error.error === 'string') ? err.error.error.error.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 && typeof err.error === 'string') ? err.error : + (err.error && err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.message && typeof err.error.error.error.error.message === 'string') ? err.error.error.error.error.message : + (err.error && err.error.error && err.error.error.error && err.error.error.error.message && typeof err.error.error.error.message === 'string') ? err.error.error.error.message : + (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.message && typeof err.message === 'string') ? err.message : genericErrorMessage); this.logger.info('Error Message: ' + msg); return msg; } diff --git a/src/app/shared/services/consts-enums-functions.ts b/src/app/shared/services/consts-enums-functions.ts index c7949f21..32977778 100644 --- a/src/app/shared/services/consts-enums-functions.ts +++ b/src/app/shared/services/consts-enums-functions.ts @@ -318,6 +318,7 @@ export const UI_MESSAGES = { CREATE_OFFER: 'Creating Offer...', DELETE_OFFER_BOOKMARK: 'Deleting Bookmark...', GET_FUNDER_POLICY: 'Getting Or Updating Funder Policy...', + GET_LIST_CONFIGS: 'Getting List Configurations...', LOG_OUT: 'Logging Out...' }; diff --git a/src/app/shared/services/data.service.ts b/src/app/shared/services/data.service.ts index 68d06be3..11f292c5 100644 --- a/src/app/shared/services/data.service.ts +++ b/src/app/shared/services/data.service.ts @@ -1,8 +1,8 @@ import { Injectable, OnDestroy } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { TitleCasePipe } from '@angular/common'; -import { Subject, throwError, of } from 'rxjs'; -import { map, takeUntil, catchError, mergeMap, withLatestFrom } from 'rxjs/operators'; +import { Subject, throwError, of, BehaviorSubject } from 'rxjs'; +import { map, takeUntil, catchError, mergeMap, withLatestFrom, first } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -20,35 +20,16 @@ import { allChannels } from '../../lnd/store/lnd.selector'; @Injectable() export class DataService implements OnDestroy { - private lnImplementation = 'LND'; - private childAPIUrl = API_URL; + private APIUrl = API_URL; + private lnImplementation = ''; + public lnImplementationUpdated: BehaviorSubject = new BehaviorSubject(null); private unSubs: Array> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject()]; constructor(private httpClient: HttpClient, private store: Store, private logger: LoggerService, private snackBar: MatSnackBar, private titleCasePipe: TitleCasePipe) { } - getChildAPIUrl() { - return this.childAPIUrl; - } - - getLnImplementation() { - return this.lnImplementation; - } - - setChildAPIUrl(lnImplementation: string) { - this.lnImplementation = lnImplementation; - switch (lnImplementation) { - case 'CLN': - this.childAPIUrl = API_URL + '/cln'; - break; - - case 'ECL': - this.childAPIUrl = API_URL + '/ecl'; - break; - - default: - this.childAPIUrl = API_URL + '/lnd'; - break; - } + setLnImplementation(lnImplementation: string) { + this.lnImplementation = lnImplementation.toLowerCase(); + this.lnImplementationUpdated.next(this.lnImplementation); } getFiatRates() { @@ -56,130 +37,161 @@ export class DataService implements OnDestroy { } decodePayment(payment: string, fromDialog: boolean) { - const url = this.childAPIUrl + environment.PAYMENTS_API + '/decode/' + payment; - this.store.dispatch(openSpinner({ payload: UI_MESSAGES.DECODE_PAYMENT })); - return this.httpClient.get(url).pipe( - takeUntil(this.unSubs[0]), - map((res: any) => { - this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.DECODE_PAYMENT })); - return res; - }), - catchError((err) => { - if (fromDialog) { - this.handleErrorWithoutAlert('Decode Payment', UI_MESSAGES.DECODE_PAYMENT, err); - } else { - this.handleErrorWithAlert('decodePaymentData', UI_MESSAGES.DECODE_PAYMENT, 'Decode Payment Failed', this.childAPIUrl + environment.PAYMENTS_API + '/decode/', err); - } - return throwError(() => new Error(this.extractErrorMessage(err))); - }) - ); + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + let url = this.APIUrl + '/' + updatedLnImplementation + environment.PAYMENTS_API + '/decode/' + payment; + if (updatedLnImplementation === 'cln') { + url = this.APIUrl + '/' + updatedLnImplementation + environment.UTILITY_API + '/decode/' + payment; + } + this.store.dispatch(openSpinner({ payload: UI_MESSAGES.DECODE_PAYMENT })); + return this.httpClient.get(url).pipe( + takeUntil(this.unSubs[0]), + map((res: any) => { + this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.DECODE_PAYMENT })); + return res; + }), + catchError((err) => { + if (fromDialog) { + this.handleErrorWithoutAlert('Decode Payment', UI_MESSAGES.DECODE_PAYMENT, err); + } else { + this.handleErrorWithAlert('decodePaymentData', UI_MESSAGES.DECODE_PAYMENT, 'Decode Payment Failed', url, err); + } + return throwError(() => new Error(this.extractErrorMessage(err))); + }) + ); + })); } decodePayments(payments: string) { - let url = this.childAPIUrl + environment.PAYMENTS_API; - let msg = UI_MESSAGES.DECODE_PAYMENTS; - if (this.getLnImplementation() === 'ECL') { - url = this.childAPIUrl + environment.PAYMENTS_API + '/getsentinfos'; - msg = UI_MESSAGES.GET_SENT_PAYMENTS; - } - this.store.dispatch(openSpinner({ payload: msg })); - return this.httpClient.post(url, { payments: payments }).pipe( - takeUntil(this.unSubs[1]), - map((res: any) => { - this.store.dispatch(closeSpinner({ payload: msg })); - return res; - }), - catchError((err) => { - this.handleErrorWithAlert('decodePaymentsData', msg, msg + ' Failed', url, err); - return throwError(() => new Error(this.extractErrorMessage(err))); - }) - ); + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + let url = ''; + let msg = ''; + if (updatedLnImplementation === 'ecl') { + url = this.APIUrl + '/' + updatedLnImplementation + environment.PAYMENTS_API + '/getsentinfos'; + msg = UI_MESSAGES.GET_SENT_PAYMENTS; + } else if (updatedLnImplementation === 'cln') { + url = this.APIUrl + '/' + updatedLnImplementation + environment.UTILITY_API; + msg = UI_MESSAGES.DECODE_PAYMENTS; + } else { + url = this.APIUrl + '/' + updatedLnImplementation + environment.PAYMENTS_API; + msg = UI_MESSAGES.DECODE_PAYMENTS; + } + this.store.dispatch(openSpinner({ payload: msg })); + return this.httpClient.post(url, { payments: payments }).pipe( + takeUntil(this.unSubs[1]), + map((res: any) => { + this.store.dispatch(closeSpinner({ payload: msg })); + return res; + }), + catchError((err) => { + this.handleErrorWithAlert('decodePaymentsData', msg, msg + ' Failed', url, err); + return throwError(() => new Error(this.extractErrorMessage(err))); + }) + ); + })); } getAliasesFromPubkeys(pubkey: string, multiple: boolean) { - if (multiple) { - const pubkey_params = new HttpParams().set('pubkeys', pubkey); - return this.httpClient.get(this.childAPIUrl + environment.NETWORK_API + '/nodes', { params: pubkey_params }); - } else { - return this.httpClient.get(this.childAPIUrl + environment.NETWORK_API + '/node/' + pubkey); - } + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + if (multiple) { + const pubkey_params = new HttpParams().set('pubkeys', pubkey); + return this.httpClient.get(this.APIUrl + '/' + updatedLnImplementation + environment.NETWORK_API + '/nodes', { params: pubkey_params }); + } else { + return this.httpClient.get(this.APIUrl + '/' + updatedLnImplementation + environment.NETWORK_API + '/node/' + pubkey); + } + })); } signMessage(msg: string) { - this.store.dispatch(openSpinner({ payload: UI_MESSAGES.SIGN_MESSAGE })); - return this.httpClient.post(this.childAPIUrl + environment.MESSAGE_API + '/sign', { message: msg }).pipe( - takeUntil(this.unSubs[2]), - map((res: any) => { - this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.SIGN_MESSAGE })); - return res; - }), - catchError((err) => { - this.handleErrorWithAlert('signMessageData', UI_MESSAGES.SIGN_MESSAGE, 'Sign Message Failed', this.childAPIUrl + environment.MESSAGE_API + '/sign', err); - return throwError(() => new Error(this.extractErrorMessage(err))); - }) - ); + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + let url = this.APIUrl + '/' + updatedLnImplementation + environment.MESSAGE_API + '/sign'; + if (updatedLnImplementation === 'cln') { + url = this.APIUrl + '/' + updatedLnImplementation + environment.UTILITY_API + '/sign'; + } + this.store.dispatch(openSpinner({ payload: UI_MESSAGES.SIGN_MESSAGE })); + return this.httpClient.post(url, { message: msg }).pipe( + takeUntil(this.unSubs[2]), + map((res: any) => { + this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.SIGN_MESSAGE })); + return res; + }), + catchError((err) => { + this.handleErrorWithAlert('signMessageData', UI_MESSAGES.SIGN_MESSAGE, 'Sign Message Failed', url, err); + return throwError(() => new Error(this.extractErrorMessage(err))); + }) + ); + })); } verifyMessage(msg: string, sign: string) { - this.store.dispatch(openSpinner({ payload: UI_MESSAGES.VERIFY_MESSAGE })); - return this.httpClient.post(this.childAPIUrl + environment.MESSAGE_API + '/verify', { message: msg, signature: sign }).pipe( - takeUntil(this.unSubs[3]), - map((res: any) => { - this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.VERIFY_MESSAGE })); - return res; - }), - catchError((err) => { - this.handleErrorWithAlert('verifyMessageData', UI_MESSAGES.VERIFY_MESSAGE, 'Verify Message Failed', this.childAPIUrl + environment.MESSAGE_API + '/verify', err); - return throwError(() => new Error(this.extractErrorMessage(err))); - }) - ); + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + let url = this.APIUrl + '/' + updatedLnImplementation + environment.MESSAGE_API + '/verify'; + if (updatedLnImplementation === 'cln') { + url = this.APIUrl + '/' + updatedLnImplementation + environment.UTILITY_API + '/verify'; + } + this.store.dispatch(openSpinner({ payload: UI_MESSAGES.VERIFY_MESSAGE })); + return this.httpClient.post(url, { message: msg, signature: sign }).pipe( + takeUntil(this.unSubs[3]), + map((res: any) => { + this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.VERIFY_MESSAGE })); + return res; + }), + catchError((err) => { + this.handleErrorWithAlert('verifyMessageData', UI_MESSAGES.VERIFY_MESSAGE, 'Verify Message Failed', url, err); + return throwError(() => new Error(this.extractErrorMessage(err))); + }) + ); + })); } bumpFee(txid: string, outputIndex: number, targetConf: number, satPerByte: number) { - const bumpFeeBody: any = { txid: txid, outputIndex: outputIndex }; - if (targetConf) { - bumpFeeBody.targetConf = targetConf; - } - if (satPerByte) { - bumpFeeBody.satPerByte = satPerByte; - } - this.store.dispatch(openSpinner({ payload: UI_MESSAGES.BUMP_FEE })); - return this.httpClient.post(this.childAPIUrl + environment.WALLET_API + '/bumpfee', bumpFeeBody).pipe( - takeUntil(this.unSubs[4]), - map((res: any) => { - this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.BUMP_FEE })); - this.snackBar.open('Successfully bumped the fee. Use the block explorer to verify transaction.'); - return res; - }), - catchError((err) => { - this.handleErrorWithoutAlert('Bump Fee', UI_MESSAGES.BUMP_FEE, err); - return throwError(() => new Error(this.extractErrorMessage(err))); - }) - ); + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + const bumpFeeBody: any = { txid: txid, outputIndex: outputIndex }; + if (targetConf) { + bumpFeeBody.targetConf = targetConf; + } + if (satPerByte) { + bumpFeeBody.satPerByte = satPerByte; + } + this.store.dispatch(openSpinner({ payload: UI_MESSAGES.BUMP_FEE })); + return this.httpClient.post(this.APIUrl + '/' + updatedLnImplementation + environment.WALLET_API + '/bumpfee', bumpFeeBody).pipe( + takeUntil(this.unSubs[4]), + map((res: any) => { + this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.BUMP_FEE })); + this.snackBar.open('Successfully bumped the fee. Use the block explorer to verify transaction.'); + return res; + }), + catchError((err) => { + this.handleErrorWithoutAlert('Bump Fee', UI_MESSAGES.BUMP_FEE, err); + return throwError(() => new Error(this.extractErrorMessage(err))); + }) + ); + })); } labelUTXO(txid: string, label: string, overwrite: boolean = true) { - const labelBody = { txid: txid, label: label, overwrite: overwrite }; - this.store.dispatch(openSpinner({ payload: UI_MESSAGES.LABEL_UTXO })); - return this.httpClient.post(this.childAPIUrl + environment.WALLET_API + '/label', labelBody).pipe( - takeUntil(this.unSubs[5]), - map((res) => { - this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.LABEL_UTXO })); - return res; - }), catchError((err) => { - this.handleErrorWithoutAlert('Lease UTXO', UI_MESSAGES.LABEL_UTXO, err); - return throwError(() => new Error(this.extractErrorMessage(err))); - }) - ); + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + const labelBody = { txid: txid, label: label, overwrite: overwrite }; + this.store.dispatch(openSpinner({ payload: UI_MESSAGES.LABEL_UTXO })); + return this.httpClient.post(this.APIUrl + '/' + updatedLnImplementation + environment.WALLET_API + '/label', labelBody).pipe( + takeUntil(this.unSubs[5]), + map((res) => { + this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.LABEL_UTXO })); + return res; + }), catchError((err) => { + this.handleErrorWithoutAlert('Lease UTXO', UI_MESSAGES.LABEL_UTXO, err); + return throwError(() => new Error(this.extractErrorMessage(err))); + }) + ); + })); } leaseUTXO(txid: string, output_index: number) { - const leaseBody: any = { txid: txid, outputIndex: output_index }; - this.store.dispatch(openSpinner({ payload: UI_MESSAGES.LEASE_UTXO })); - return this.httpClient.post(this.childAPIUrl + environment.WALLET_API + '/lease', leaseBody).pipe( - takeUntil(this.unSubs[6])). - subscribe({ - next: (res: any) => { + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + const leaseBody: any = { txid: txid, outputIndex: output_index }; + this.store.dispatch(openSpinner({ payload: UI_MESSAGES.LEASE_UTXO })); + return this.httpClient.post(this.APIUrl + '/' + updatedLnImplementation + environment.WALLET_API + '/lease', leaseBody).pipe( + takeUntil(this.unSubs[6]), + map((res: any) => { this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.LEASE_UTXO })); this.store.dispatch(fetchTransactions()); this.store.dispatch(fetchUTXOs()); @@ -190,92 +202,118 @@ export class DataService implements OnDestroy { replace(' ', '/'). replace(' ', '/'). toUpperCase() + '.'); - }, error: (err) => { + }), catchError((err) => { this.handleErrorWithoutAlert('Lease UTXO', UI_MESSAGES.LEASE_UTXO, err); return throwError(() => new Error(this.extractErrorMessage(err))); - } - }); + }) + ); + })); } getForwardingHistory(start: string, end: string) { - const queryHeaders: SwitchReq = { end_time: end, start_time: start }; - this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_FORWARDING_HISTORY })); - return this.httpClient.post(this.childAPIUrl + environment.SWITCH_API, queryHeaders).pipe( - takeUntil(this.unSubs[7]), - withLatestFrom(this.store.select(allChannels)), - mergeMap(([res, allChannelsSelector]: [any, { channels: Channel[], pendingChannels: PendingChannels, closedChannels: ClosedChannel[] }]) => { - if (res.forwarding_events) { - const storedChannels = [...allChannelsSelector.channels, ...allChannelsSelector.closedChannels]; - res.forwarding_events.forEach((event) => { - if (storedChannels && storedChannels.length > 0) { - for (let idx = 0; idx < storedChannels.length; idx++) { - if (storedChannels[idx].chan_id.toString() === event.chan_id_in) { - event.alias_in = storedChannels[idx].remote_alias ? storedChannels[idx].remote_alias : event.chan_id_in; - if (event.alias_out) { - return; - } - } - if (storedChannels[idx].chan_id.toString() === event.chan_id_out) { - event.alias_out = storedChannels[idx].remote_alias ? storedChannels[idx].remote_alias : event.chan_id_out; - if (event.alias_in) { - return; - } - } - if (idx === storedChannels.length - 1) { - if (!event.alias_in) { - event.alias_in = event.chan_id_in; - } - if (!event.alias_out) { - event.alias_out = event.chan_id_out; + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + const queryHeaders: SwitchReq = { end_time: end, start_time: start }; + this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_FORWARDING_HISTORY })); + return this.httpClient.post(this.APIUrl + '/' + updatedLnImplementation + environment.SWITCH_API, queryHeaders).pipe( + takeUntil(this.unSubs[7]), + withLatestFrom(this.store.select(allChannels)), + mergeMap(([res, allChannelsSelector]: [any, { channels: Channel[], pendingChannels: PendingChannels, closedChannels: ClosedChannel[] }]) => { + if (res.forwarding_events) { + const storedChannels = [...allChannelsSelector.channels, ...allChannelsSelector.closedChannels]; + res.forwarding_events.forEach((event) => { + if (storedChannels && storedChannels.length > 0) { + for (let idx = 0; idx < storedChannels.length; idx++) { + if (storedChannels[idx].chan_id.toString() === event.chan_id_in) { + event.alias_in = storedChannels[idx].remote_alias ? storedChannels[idx].remote_alias : event.chan_id_in; + if (event.alias_out) { + return; + } + } + if (storedChannels[idx].chan_id.toString() === event.chan_id_out) { + event.alias_out = storedChannels[idx].remote_alias ? storedChannels[idx].remote_alias : event.chan_id_out; + if (event.alias_in) { + return; + } + } + if (idx === storedChannels.length - 1) { + if (!event.alias_in) { + event.alias_in = event.chan_id_in; + } + if (!event.alias_out) { + event.alias_out = event.chan_id_out; + } } } + } else { + event.alias_in = event.chan_id_in; + event.alias_out = event.chan_id_out; } - } else { - event.alias_in = event.chan_id_in; - event.alias_out = event.chan_id_out; - } - }); - } else { - res = {}; - } - this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_FORWARDING_HISTORY })); - return of(res); - }), - catchError((err) => { - this.handleErrorWithAlert('getForwardingHistoryData', UI_MESSAGES.GET_FORWARDING_HISTORY, 'Forwarding History Failed', this.childAPIUrl + environment.SWITCH_API, err); - return throwError(() => new Error(this.extractErrorMessage(err))); - })); + }); + } else { + res = {}; + } + this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_FORWARDING_HISTORY })); + return of(res); + }), + catchError((err) => { + this.handleErrorWithAlert('getForwardingHistoryData', UI_MESSAGES.GET_FORWARDING_HISTORY, 'Forwarding History Failed', this.APIUrl + '/' + updatedLnImplementation + environment.SWITCH_API, err); + return throwError(() => new Error(this.extractErrorMessage(err))); + })); + })); + } + + listConfigs() { + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_LIST_CONFIGS })); + return this.httpClient.get(this.APIUrl + '/' + updatedLnImplementation + environment.UTILITY_API + '/listConfigs').pipe( + takeUntil(this.unSubs[9]), + mergeMap((res) => { + this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_LIST_CONFIGS })); + return of(res); + }), catchError((err) => { + this.handleErrorWithoutAlert('List Configurations', UI_MESSAGES.GET_LIST_CONFIGS, err); + return throwError(() => this.extractErrorMessage(err)); + }) + ); + })); } getOrUpdateFunderPolicy(policy?: any, policyMod?: any, leaseFeeBaseMsat?: any, leaseFeeBasis?: any, channelFeeMaxBaseMsat?: any, channelFeeMaxProportional?: any) { - const postParams = policy ? { policy: policy, policyMod: policyMod, leaseFeeBaseMsat: leaseFeeBaseMsat, leaseFeeBasis: leaseFeeBasis, channelFeeMaxBaseMsat: channelFeeMaxBaseMsat, channelFeeMaxProportional: channelFeeMaxProportional } : null; - this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_FUNDER_POLICY })); - return this.httpClient.post(this.childAPIUrl + environment.CHANNELS_API + '/funderUpdate', postParams).pipe( - takeUntil(this.unSubs[8]), - map((res) => { - this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_FUNDER_POLICY })); - return res; - }), catchError((err) => { - this.handleErrorWithoutAlert('Funder Policy', UI_MESSAGES.GET_FUNDER_POLICY, err); - return throwError(() => new Error(this.extractErrorMessage(err))); - }) - ); + return this.lnImplementationUpdated.pipe(first((val) => val !== null), mergeMap((updatedLnImplementation) => { + const postParams = policy ? { policy: policy, policyMod: policyMod, leaseFeeBaseMsat: leaseFeeBaseMsat, leaseFeeBasis: leaseFeeBasis, channelFeeMaxBaseMsat: channelFeeMaxBaseMsat, channelFeeMaxProportional: channelFeeMaxProportional } : null; + this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_FUNDER_POLICY })); + return this.httpClient.post(this.APIUrl + '/' + updatedLnImplementation + environment.CHANNELS_API + '/funderUpdate', postParams).pipe( + takeUntil(this.unSubs[8]), + map((res) => { + this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_FUNDER_POLICY })); + return res; + }), catchError((err) => { + this.handleErrorWithoutAlert('Funder Policy', UI_MESSAGES.GET_FUNDER_POLICY, err); + return throwError(() => new Error(this.extractErrorMessage(err))); + }) + ); + })); } extractErrorMessage(err: any, genericErrorMessage: string = 'Unknown Error.') { - return this.titleCasePipe.transform((err.error && err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.error && typeof err.error.error.error.error.error === 'string') ? err.error.error.error.error.error : - (err.error && err.error.error && err.error.error.error && err.error.error.error.error && typeof err.error.error.error.error === 'string') ? err.error.error.error.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 && typeof err.error === 'string') ? err.error : - (err.error && err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.message && typeof err.error.error.error.error.message === 'string') ? err.error.error.error.error.message : - (err.error && err.error.error && err.error.error.error && err.error.error.error.message && typeof err.error.error.error.message === 'string') ? err.error.error.error.message : - (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.message && typeof err.message === 'string') ? err.message : genericErrorMessage); + return this.titleCasePipe.transform( + (err.error.text && typeof err.error.text === 'string' && err.error.text.includes('')) ? 'API Route Does Not Exist.' : + (err.error && err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.error && typeof err.error.error.error.error.error === 'string') ? err.error.error.error.error.error : + (err.error && err.error.error && err.error.error.error && err.error.error.error.error && typeof err.error.error.error.error === 'string') ? err.error.error.error.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 && typeof err.error === 'string') ? err.error : + (err.error && err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.message && typeof err.error.error.error.error.message === 'string') ? err.error.error.error.error.message : + (err.error && err.error.error && err.error.error.error && err.error.error.error.message && typeof err.error.error.error.message === 'string') ? err.error.error.error.message : + (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.message && typeof err.message === 'string') ? err.message : genericErrorMessage); } handleErrorWithoutAlert(actionName: string, uiMessage: string, err: { status: number, error: any }) { + if (err.error.text && typeof err.error.text === 'string' && err.error.text.includes('')) { + err = { status: 403, error: { message: 'API Route Does Not Exist.' } }; + } this.logger.error('ERROR IN: ' + actionName + '\n' + JSON.stringify(err)); if (err.status === 401) { this.logger.info('Redirecting to Login'); diff --git a/src/app/shared/test-helpers/mock-services.ts b/src/app/shared/test-helpers/mock-services.ts index 5fa9a6b8..84787cd0 100644 --- a/src/app/shared/test-helpers/mock-services.ts +++ b/src/app/shared/test-helpers/mock-services.ts @@ -45,32 +45,13 @@ export class mockRouter { export class mockDataService { private lnImplementation = 'LND'; - private childAPIUrl = API_URL; + private APIUrl = API_URL; + public lnImplementationUpdated: BehaviorSubject = new BehaviorSubject(this.lnImplementation); - getChildAPIUrl() { - return of(this.childAPIUrl); - }; - - getLnImplementation() { - return of(this.lnImplementation); - }; - - setChildAPIUrl(lnImplementation: string) { - this.lnImplementation = lnImplementation; - switch (lnImplementation) { - case 'CLN': - this.childAPIUrl = API_URL + '/cln'; - break; - - case 'ECL': - this.childAPIUrl = API_URL + '/ecl'; - break; - - default: - this.childAPIUrl = API_URL + '/lnd'; - break; - } - }; + setLnImplementation(lnImplementation: string) { + this.lnImplementation = lnImplementation.toLowerCase(); + this.lnImplementationUpdated.next(this.lnImplementation); + } getFiatRates() { return of(mockResponseData.fiatRates); diff --git a/src/app/store/rtl.effects.ts b/src/app/store/rtl.effects.ts index 56c5597c..f29617a4 100644 --- a/src/app/store/rtl.effects.ts +++ b/src/app/store/rtl.effects.ts @@ -564,7 +564,7 @@ export class RTLEffects implements OnDestroy { this.store.dispatch(resetECLStore({ payload: selNode })); if (this.sessionService.getItem('token')) { const nodeLnImplementation = node.lnImplementation ? node.lnImplementation.toUpperCase() : 'LND'; - this.dataService.setChildAPIUrl(nodeLnImplementation); + this.dataService.setLnImplementation(nodeLnImplementation); const apiUrl = (environment.production && window.location.origin) ? (window.location.origin + '/rtl/api') : API_URL; this.wsService.connectWebSocket(apiUrl.replace(/^http/, 'ws') + environment.Web_SOCKET_API, node.index.toString()); switch (nodeLnImplementation) { diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index c930f96a..cecb16fe 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -21,6 +21,7 @@ export const environment = { ON_CHAIN_API: '/onchain', MESSAGE_API: '/message', OFFERS_API: '/offers', + UTILITY_API: '/utility', LOOP_API: '/loop', BOLTZ_API: '/boltz', Web_SOCKET_API: '/ws' diff --git a/src/environments/environment.ts b/src/environments/environment.ts index a9931473..0f96f14a 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -21,6 +21,7 @@ export const environment = { ON_CHAIN_API: '/onchain', MESSAGE_API: '/message', OFFERS_API: '/offers', + UTILITY_API: '/utility', LOOP_API: '/loop', BOLTZ_API: '/boltz', Web_SOCKET_API: '/ws'