Release 0.7.1 (#340)

* Peers And Channels Redesign (#294)
* Send payment without confirmation
* Removed vulnerabilities & added missing 2FAuth button
* Invoice Redesign with invoice info modal opening
* Firefox Payment Failure Issue #283 (#309)
* On chain redesign (#315)
* Bug fix #312 Bogus warning about unsecure connection (#316)
* Peer Channels Redesign #290 (#317)
* CLT transactions redesign #290 (#319)
* CLT On-chain redesign #290 (#320)
* Wallet error alert bug fix #298 & #299 (#321)
* Configurable server host #303 (#322)
* Receive tab on top #292 (#323)
* Settings Layout Cleanup (#325)
* Alias Autocomplete (#326)
* Redirect to Dashboard after first RTL login
* Bug fix Channel Uptime wrong #284 (#329)
* Channel info redesigned Issue #328 (#331)
* Fixed Transaction Info Scroll #324 (#333)
* Remove macaroon info before logging #306
* Added Peer Alias for Closed And Pending Channels Tabs #275 & #285 (#336)
* Show multiple URIs on show pubkey modal #337 (#338)
pull/351/head
ShahanaFarooqui 4 years ago committed by GitHub
parent 685372fc5b
commit a980d9c3fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

File diff suppressed because one or more lines are too long

@ -110,6 +110,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
@angular/material/autocomplete
@angular/material/button @angular/material/button
@angular/material/card @angular/material/card

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -12,8 +12,8 @@
<link rel="mask-icon" href="assets/images/favicon-light/safari-pinned-tab.svg" color="#5bbad5"> <link rel="mask-icon" href="assets/images/favicon-light/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c"> <meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="styles.807ae95794a05be97fba.css"></head> <link rel="stylesheet" href="styles.da5d02955b06fbd16881.css"></head>
<body> <body>
<rtl-app></rtl-app> <rtl-app></rtl-app>
<script src="runtime.2cfa778f51a601dbdb7e.js" defer></script><script src="polyfills-es5.2ae7ace69949ec0a3f00.js" nomodule defer></script><script src="polyfills.3302e98effc5e50a54c2.js" defer></script><script src="main.4b92e283188428242458.js" defer></script></body> <script src="runtime.9c469fe6b4e77e4e8274.js" defer></script><script src="polyfills-es5.2ae7ace69949ec0a3f00.js" nomodule defer></script><script src="polyfills.3302e98effc5e50a54c2.js" defer></script><script src="main.3cc0daec879a4055c999.js" defer></script></body>
</html> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"."+{1:"55621ac84bdf6c786e25",6:"3228fcaf0890e1d596e3",7:"6fecb498f36cf3efcace"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]); !function(e){function r(r){for(var n,a,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"."+{1:"9f5a5551675b6e3c39b6",6:"23ff3e765459f2da33da",7:"417208cf5491f2b427d2"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -8,6 +8,7 @@ common.rtl_pass = '';
common.rtl_secret2fa = ''; common.rtl_secret2fa = '';
common.rtl_sso = 0; common.rtl_sso = 0;
common.port = 3000; common.port = 3000;
common.host = 'localhost';
common.rtl_cookie_path = ''; common.rtl_cookie_path = '';
common.logout_redirect_link = '/login'; common.logout_redirect_link = '/login';
common.cookie = ''; common.cookie = '';
@ -115,7 +116,22 @@ common.sortAscByKey = (array, key) => {
common.sortDescByKey = (array, key) => { common.sortDescByKey = (array, key) => {
const temp = array.sort(function (a, b) { const temp = array.sort(function (a, b) {
var x = +a[key]; var y = +b[key]; var x = +a[key]; var y = +b[key];
return ((x > y) ? -1 : ((x < y) ? 1 : 0)); return (x > y) ? -1 : ((x < y) ? 1 : 0);
});
return temp;
}
common.sortAscByStrKey = (array, key) => {
return array.sort(function (a, b) {
var x = a[key] ? a[key].toUpperCase() : ''; var y = b[key] ? b[key].toUpperCase() : '';
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
common.sortDescByStrKey = (array, key) => {
const temp = array.sort(function (a, b) {
var x = a[key] ? a[key].toUpperCase() : ''; var y = b[key] ? b[key].toUpperCase() : '';
return (x > y) ? -1 : ((x < y) ? 1 : 0);
}); });
return temp; return temp;
} }

@ -42,6 +42,7 @@ connect.setDefaultConfig = () => {
return { return {
multiPass: "password", multiPass: "password",
port: "3000", port: "3000",
host: "localhost",
defaultNodeIndex: 1, defaultNodeIndex: 1,
SSO: { SSO: {
rtlSSO: 0, rtlSSO: 0,
@ -99,7 +100,7 @@ connect.replacePasswordWithHash = (multiPassHashed) => {
} }
connect.validateNodeConfig = (config) => { connect.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 (config.multiPassHashed !== '' && config.multiPassHashed) { if (config.multiPassHashed !== '' && config.multiPassHashed) {
common.rtl_pass = config.multiPassHashed; common.rtl_pass = config.multiPassHashed;
} else if (config.multiPass !== '' && config.multiPass) { } else if (config.multiPass !== '' && config.multiPass) {
@ -110,6 +111,7 @@ connect.validateNodeConfig = (config) => {
common.rtl_secret2fa = config.secret2fa; common.rtl_secret2fa = config.secret2fa;
} }
common.port = (process.env.PORT) ? connect.normalizePort(process.env.PORT) : (config.port) ? connect.normalizePort(config.port) : 3000; common.port = (process.env.PORT) ? connect.normalizePort(process.env.PORT) : (config.port) ? connect.normalizePort(config.port) : 3000;
common.host = (process.env.HOST) ? process.env.HOST : (config.host) ? config.host : 'localhost';
if (config.nodes && config.nodes.length > 0) { if (config.nodes && config.nodes.length > 0) {
config.nodes.forEach((node, idx) => { config.nodes.forEach((node, idx) => {
common.nodes[idx] = {}; common.nodes[idx] = {};
@ -286,6 +288,7 @@ connect.logEnvVariables = () => {
common.nodes.forEach((node, idx) => { common.nodes.forEach((node, idx) => {
if (!node.enable_logging) { return; } if (!node.enable_logging) { return; }
logger.info({fileName: 'Config Setup Variable', msg: 'PORT: ' + common.port, node}); logger.info({fileName: 'Config Setup Variable', msg: 'PORT: ' + common.port, node});
logger.info({fileName: 'Config Setup Variable', msg: 'HOST: ' + common.host, node});
logger.info({fileName: 'Config Setup Variable', msg: 'DEFAULT NODE INDEX: ' + common.selectedNode.index}); logger.info({fileName: 'Config Setup Variable', msg: 'DEFAULT NODE INDEX: ' + common.selectedNode.index});
logger.info({fileName: 'Config Setup Variable', msg: 'SSO: ' + common.rtl_sso, node}); logger.info({fileName: 'Config Setup Variable', msg: 'SSO: ' + common.rtl_sso, node});
logger.info({fileName: 'Config Setup Variable', msg: 'LOGOUT REDIRECT LINK: ' + common.logout_redirect_link + '\r\n', node}); logger.info({fileName: 'Config Setup Variable', msg: 'LOGOUT REDIRECT LINK: ' + common.logout_redirect_link + '\r\n', node});
@ -344,6 +347,7 @@ connect.modifyJsonMultiNodeConfig = (confFileFullPath) => {
if (!config.SSO) { config.SSO = {}; } if (!config.SSO) { config.SSO = {}; }
var newConfig = { var newConfig = {
port: config.port ? config.port : 3000, port: config.port ? config.port : 3000,
host: config.host ? config.host : 'localhost',
defaultNodeIndex: config.defaultNodeIndex ? config.defaultNodeIndex : 1, defaultNodeIndex: config.defaultNodeIndex ? config.defaultNodeIndex : 1,
SSO: { SSO: {
rtlSSO: config.SSO.rtlSSO ? config.SSO.rtlSSO : 0, rtlSSO: config.SSO.rtlSSO ? config.SSO.rtlSSO : 0,
@ -414,6 +418,7 @@ connect.modifyIniSingleNodeConfig = (confFileFullPath) => {
if (!config.Settings) { config.Settings = {}; } if (!config.Settings) { config.Settings = {}; }
var newConfig = { var newConfig = {
port: config.Settings.port ? config.Settings.port : 3000, port: config.Settings.port ? config.Settings.port : 3000,
host: 'localhost',
defaultNodeIndex: 1, defaultNodeIndex: 1,
SSO: { SSO: {
rtlSSO: config.SSO.rtlSSO ? config.SSO.rtlSSO : 0, rtlSSO: config.SSO.rtlSSO ? config.SSO.rtlSSO : 0,

@ -19,10 +19,10 @@ exports.getRTLConfig = (req, res, next) => {
fs.readFile(confFile, 'utf8', function(err, data) { fs.readFile(confFile, 'utf8', function(err, data) {
if (err) { if (err) {
if (err.code === 'ENOENT') { if (err.code === 'ENOENT') {
logger.error({fileName: 'RTLConf', lineNum: 46, msg: 'Node config does not exist!'}); logger.error({fileName: 'RTLConf', lineNum: 22, msg: 'Node config does not exist!'});
res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] }); res.status(200).json({ defaultNodeIndex: 0, selectedNodeIndex: 0, sso: {}, nodes: [] });
} else { } else {
logger.error({fileName: 'RTLConf', lineNum: 49, msg: 'Getting Node Config Failed!'}); logger.error({fileName: 'RTLConf', lineNum: 25, msg: 'Getting Node Config Failed!'});
res.status(500).json({ res.status(500).json({
message: "Reading Node Config Failed!", message: "Reading Node Config Failed!",
error: err error: err
@ -98,7 +98,7 @@ exports.updateUISettings = (req, res, next) => {
res.status(201).json({message: 'Application Node Settings Updated Successfully'}); res.status(201).json({message: 'Application Node Settings Updated Successfully'});
} }
catch (err) { catch (err) {
logger.error({fileName: 'Conf', lineNum: 102, msg: 'Updating Application Node Settings Failed!'}); logger.error({fileName: 'Conf', lineNum: 101, msg: 'Updating Application Node Settings Failed!'});
res.status(500).json({ res.status(500).json({
message: "Updating Application Node Settings Failed!", message: "Updating Application Node Settings Failed!",
error: 'Updating Application Node Settings Failed!' error: 'Updating Application Node Settings Failed!'
@ -118,7 +118,7 @@ exports.update2FASettings = (req, res, next) => {
res.status(201).json({message: message}); res.status(201).json({message: message});
} }
catch (err) { catch (err) {
logger.error({fileName: 'Conf', lineNum: 102, msg: 'Updating 2FA Settings Failed!'}); logger.error({fileName: 'Conf', lineNum: 121, msg: 'Updating 2FA Settings Failed!'});
res.status(500).json({ res.status(500).json({
message: "Updating 2FA Settings Failed!", message: "Updating 2FA Settings Failed!",
error: 'Updating 2FA Settings Failed!' error: 'Updating 2FA Settings Failed!'
@ -136,7 +136,7 @@ exports.updateDefaultNode = (req, res, next) => {
res.status(201).json({message: 'Default Node Updated Successfully'}); res.status(201).json({message: 'Default Node Updated Successfully'});
} }
catch (err) { catch (err) {
logger.error({fileName: 'Conf', lineNum: 102, msg: 'Updating Default Node Failed!'}); logger.error({fileName: 'Conf', lineNum: 139, msg: 'Updating Default Node Failed!'});
res.status(500).json({ res.status(500).json({
message: "Updating Default Node Failed!", message: "Updating Default Node Failed!",
error: 'Updating Default Node Failed!' error: 'Updating Default Node Failed!'
@ -168,7 +168,7 @@ exports.getConfig = (req, res, next) => {
logger.info({fileName: 'RTLConf', msg: 'Node Type: ' + req.params.nodeType + ', File Path: ' + confFile}); logger.info({fileName: 'RTLConf', msg: 'Node Type: ' + req.params.nodeType + ', File Path: ' + confFile});
fs.readFile(confFile, 'utf8', function(err, data) { fs.readFile(confFile, 'utf8', function(err, data) {
if (err) { if (err) {
logger.error({fileName: 'Conf', lineNum: 159, msg: 'Reading Conf Failed!'}); logger.error({fileName: 'Conf', lineNum: 171, msg: 'Reading Conf Failed!'});
res.status(500).json({ res.status(500).json({
message: "Reading Config File Failed!", message: "Reading Config File Failed!",
error: err error: err
@ -207,7 +207,7 @@ exports.getCurrencyRates = (req, res, next) => {
} }
}) })
.catch(function (err) { .catch(function (err) {
logger.error({fileName: 'Conf', lineNum: 241, msg: 'Fetching Rates Failed! ' + JSON.stringify(err)}); logger.error({fileName: 'Conf', lineNum: 210, msg: 'Fetching Rates Failed! ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Rates Failed!", message: "Fetching Rates Failed!",
error: err.error error: err.error

@ -17,7 +17,7 @@ exports.authenticateUser = (req, res, next) => {
); );
res.status(200).json({ token: token }); res.status(200).json({ token: token });
} else { } else {
logger.error({fileName: 'Authenticate', lineNum: 21, msg: 'Password Validation Failed!'}); logger.error({fileName: 'Authenticate', lineNum: 20, msg: 'SSO Authentication Failed!'});
res.status(401).json({ res.status(401).json({
message: "Login Failure!", message: "Login Failure!",
error: "SSO Authentication Failed!" error: "SSO Authentication Failed!"
@ -33,10 +33,10 @@ exports.authenticateUser = (req, res, next) => {
); );
res.status(200).json({ token: token }); res.status(200).json({ token: token });
} else { } else {
logger.error({fileName: 'Authenticate', lineNum: 38, msg: 'Password Validation Failed!'}); logger.error({fileName: 'Authenticate', lineNum: 36, msg: 'Invalid Password!'});
res.status(401).json({ res.status(401).json({
message: "Authentication Failed!", message: "Authentication Failed!",
error: "Password Validation Failed!" error: "Invalid Password!"
}); });
} }
} }
@ -44,9 +44,9 @@ exports.authenticateUser = (req, res, next) => {
exports.resetPassword = (req, res, next) => { exports.resetPassword = (req, res, next) => {
if(+common.rtl_sso) { if(+common.rtl_sso) {
logger.error({fileName: 'Authenticate', lineNum: 46, msg: 'Password Reset Failed!'}); logger.error({fileName: 'Authenticate', lineNum: 47, msg: 'Password Reset Failed!'});
res.status(402).json({ res.status(401).json({
message: "Password Reset Failure!", message: "Password Reset Failed!",
error: "Password cannot be reset for SSO authentication!" error: "Password cannot be reset for SSO authentication!"
}); });
} else { } else {
@ -60,8 +60,8 @@ exports.resetPassword = (req, res, next) => {
); );
res.status(200).json({ token: token }); res.status(200).json({ token: token });
} else { } else {
logger.error({fileName: 'Authenticate', lineNum: 62, msg: 'Password Reset Failed!'}); logger.error({fileName: 'Authenticate', lineNum: 63, msg: 'Password Reset Failed!'});
res.status(402).json({ res.status(401).json({
message: "Password Reset Failed!", message: "Password Reset Failed!",
error: "Old password is not correct!" error: "Old password is not correct!"
}); });
@ -80,4 +80,4 @@ exports.verifyToken = (req, res, next) => {
error: "Token Verification Failed!" error: "Token Verification Failed!"
}); });
} }
}; };

@ -7,7 +7,7 @@ exports.getBalance = (req, res, next) => {
options = common.getOptions(); options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getBalance'; options.url = common.getSelLNServerUrl() + '/getBalance';
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Balance', msg: ' Balance Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Balance', msg: 'Balance Received: ' + JSON.stringify(body)});
if(!body.totalBalance) { if(!body.totalBalance) {
body.totalBalance = 0; body.totalBalance = 0;
body.btc_totalBalance = 0; body.btc_totalBalance = 0;
@ -26,10 +26,17 @@ exports.getBalance = (req, res, next) => {
} else { } else {
body.btc_unconfBalance = common.convertToBTC(body.unconfBalance); body.btc_unconfBalance = common.convertToBTC(body.unconfBalance);
} }
res.status(200).json(body); res.status(200).json(body);
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Balance', lineNum: 38, msg: 'Balance Fetch Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching balance failed!", message: "Fetching balance failed!",
error: err.error error: err.error

@ -16,8 +16,15 @@ exports.listChannels = (req, res, next) => {
}) })
res.status(200).json(body); res.status(200).json(body);
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 14, msg: 'List Channels: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Channels', lineNum: 26, msg: 'List Channels: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Fetching List Channels Failed!', message: 'Fetching List Channels Failed!',
error: err.error error: err.error
@ -33,6 +40,7 @@ exports.openChannel = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Open Channel Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Channels', msg: 'Open Channel Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Channels', lineNum: 42, msg: 'Open Channel Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: 'Open Channel Failed!', message: 'Open Channel Failed!',
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -41,8 +49,15 @@ exports.openChannel = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 39, msg: 'Open Channel Failed: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Channels', lineNum: 58, msg: 'Open Channel Failed: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Open Channel Failed!', message: 'Open Channel Failed!',
error: err.error error: err.error
@ -58,6 +73,7 @@ exports.setChannelFee = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Update Channel Policy: ' + JSON.stringify(body)}); logger.info({fileName: 'Channels', msg: 'Update Channel Policy: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Channels', lineNum: 74, msg: 'Update Channel Policy Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: 'Update Channel Policy Failed!', message: 'Update Channel Policy Failed!',
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -66,8 +82,15 @@ exports.setChannelFee = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 211, msg: 'Update Channel Policy: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Channels', lineNum: 90, msg: 'Update Channel Policy: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Update Channel Policy Failed!', message: 'Update Channel Policy Failed!',
error: err.error error: err.error
@ -83,6 +106,7 @@ exports.closeChannel = (req, res, next) => {
request.delete(options).then((body) => { request.delete(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Close Channel Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Channels', msg: 'Close Channel Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Channels', lineNum: 106, msg: 'Close Channel Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: 'Close Channel Failed!', message: 'Close Channel Failed!',
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -91,8 +115,15 @@ exports.closeChannel = (req, res, next) => {
res.status(204).json(body); res.status(204).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 41, msg: 'Close Channel: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Channels', lineNum: 122, msg: 'Close Channel Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Close Channel Failed!', message: 'Close Channel Failed!',
error: err.error error: err.error
@ -117,11 +148,17 @@ exports.getLocalRemoteBalance = (req, res, next) => {
} else { } else {
body.btc_remoteBalance = common.convertToBTC(body.remoteBalance); body.btc_remoteBalance = common.convertToBTC(body.remoteBalance);
} }
res.status(200).json(body); res.status(200).json(body);
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 14, msg: 'Local Remote Balance: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Channels', lineNum: 156, msg: 'Local Remote Balance Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Fetching Local Remote Balance Failed!', message: 'Fetching Local Remote Balance Failed!',
error: err.error error: err.error
@ -135,7 +172,7 @@ exports.listForwards = (req, res, next) => {
request.get(options).then((body) => { request.get(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Forwarding History Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Channels', msg: 'Forwarding History Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Channels', lineNum: 27, msg: 'Forwarding History Error: ' + JSON.stringify((!body) ? 'Error From Server!' : body.error)}); logger.error({fileName: 'Channels', lineNum: 170, msg: 'Forwarding History Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Forwarding History Failed!", message: "Forwarding History Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -152,8 +189,15 @@ exports.listForwards = (req, res, next) => {
res.status(200).json({ last_offset_index: 0, forwarding_events: body }); res.status(200).json({ last_offset_index: 0, forwarding_events: body });
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 44, msg: 'Forwarding History Error: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Channels', lineNum: 194, msg: 'Forwarding History Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Forwarding History Failed!", message: "Forwarding History Failed!",
error: err.error error: err.error

@ -9,6 +9,7 @@ exports.getFees = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Fees', msg: 'Fee Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Fees', msg: 'Fee Received: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Fees', lineNum: 12, msg: 'Get Fee Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching fee failed!", message: "Fetching fee failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -23,7 +24,15 @@ exports.getFees = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Fees', lineNum: 34, msg: 'Get Fees Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching fee failed!", message: "Fetching fee failed!",
error: err.error error: err.error

@ -8,48 +8,65 @@ exports.getInfo = (req, res, next) => {
options = common.getOptions(); options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getinfo'; options.url = common.getSelLNServerUrl() + '/getinfo';
logger.info({fileName:'GetInfo', msg: 'Selected Node: ' + JSON.stringify(common.selectedNode.ln_node)}); logger.info({fileName:'GetInfo', msg: 'Selected Node: ' + JSON.stringify(common.selectedNode.ln_node)});
logger.info({fileName: 'GetInfo', msg: 'Calling getinfo from c-lightning server url: ' + options.url}); logger.info({fileName: 'GetInfo', msg: 'Calling Info from C-Lightning server url: ' + options.url});
request(options).then((body) => { if (!options.headers || !options.headers.macaroon) {
logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)}); logger.error({fileName: 'GetInfo', lineNum: 13, msg: 'C-Lightning Get info failed due to bad or missing macaroon!'});
const body_str = (!body) ? '' : JSON.stringify(body); res.status(502).json({
const search_idx = (!body) ? -1 : body_str.search('Not Found'); message: "Fetching Info Failed!",
if(!body || search_idx > -1 || body.error) { error: "Bad Macaroon"
res.status(500).json({ });
message: "Fetching Info failed!", } else {
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error request(options).then((body) => {
}); logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)});
} else { const body_str = (!body) ? '' : JSON.stringify(body);
body.currency_unit = 'BTC'; const search_idx = (!body) ? -1 : body_str.search('Not Found');
body.smaller_currency_unit = 'Sats'; if(!body || search_idx > -1 || body.error) {
body.lnImplementation = 'C-Lightning'; logger.error({fileName: 'GetInfo', lineNum: 24, msg: 'Get Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
let chainObj = { chain: '', network: '' }; res.status(500).json({
if (body.network === 'testnet') { message: "Fetching Info failed!",
chainObj.chain = 'Bitcoin'; error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
chainObj.network = 'Testnet';
} else if (body.network === 'bitcoin') {
chainObj.chain = 'Bitcoin';
chainObj.network = 'Mainnet';
} 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);
}); });
} else {
body.currency_unit = 'BTC';
body.smaller_currency_unit = 'Sats';
body.lnImplementation = 'C-Lightning';
let 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 === '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);
});
}
res.status(200).json(body);
} }
res.status(200).json(body); })
} .catch(errRes => {
}) let err = JSON.parse(JSON.stringify(errRes));
.catch(function (err) { if (err.options && err.options.headers && err.options.headers.macaroon) {
return res.status(500).json({ delete err.options.headers.macaroon;
message: "Fetching Info failed!", }
error: err.error if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.macaroon) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'GetInfo', lineNum: 57, msg: 'Get Info Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: "Fetching Info failed!",
error: err.error
});
}); });
}); }
}; };

@ -10,6 +10,7 @@ exports.deleteExpiredInvoice = (req, res, next) => {
request.delete(options).then((body) => { request.delete(options).then((body) => {
logger.info({fileName: 'Invoice', msg: 'Invoices Deleted: ' + JSON.stringify(body)}); logger.info({fileName: 'Invoice', msg: 'Invoices Deleted: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Invoice', lineNum: 13, msg: 'Invoice Delete Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Deleting Invoice Failed!", message: "Deleting Invoice Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -17,7 +18,15 @@ exports.deleteExpiredInvoice = (req, res, next) => {
} }
res.status(204).json({status: 'Invoice Deleted Successfully'}); res.status(204).json({status: 'Invoice Deleted Successfully'});
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Invoice', lineNum: 28, msg: 'Invoice Delete Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Deleting Invoice Failed!", message: "Deleting Invoice Failed!",
error: err.error error: err.error
@ -32,6 +41,7 @@ exports.listInvoices = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Invoice', msg: 'Invoices List Received: ' + body}); logger.info({fileName: 'Invoice', msg: 'Invoices List Received: ' + body});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Invoice', lineNum: 43, msg: 'List Invoice Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Invoice Info failed!", message: "Fetching Invoice Info failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -48,7 +58,15 @@ exports.listInvoices = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Invoice', lineNum: 67, msg: 'List Invoice Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Invoice Info failed!", message: "Fetching Invoice Info failed!",
error: err.error error: err.error
@ -63,6 +81,7 @@ exports.addInvoice = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'Invoice', msg: 'Add Invoice Responce: ' + JSON.stringify(body)}); logger.info({fileName: 'Invoice', msg: 'Add Invoice Responce: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Invoice', lineNum: 82, msg: 'Add Invoice Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Add Invoice Failed!", message: "Add Invoice Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -71,7 +90,15 @@ exports.addInvoice = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Invoice', lineNum: 98, msg: 'Add Invoice Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Add Invoice Failed!", message: "Add Invoice Failed!",
error: err.error error: err.error

@ -10,6 +10,7 @@ exports.signMessage = (req, res, next) => {
request.post(options, (error, response, body) => { request.post(options, (error, response, body) => {
logger.info({fileName: 'Messages', msg: 'Message Signed: ' + JSON.stringify(body)}); logger.info({fileName: 'Messages', msg: 'Message Signed: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Messages', lineNum: 13, msg: 'Message Sign Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Sign message failed!", message: "Sign message failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -18,8 +19,15 @@ exports.signMessage = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Messages', lineNum: 24, msg: 'Sign Message Failed: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Messages', lineNum: 29, msg: 'Sign Message Failed: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Sign Message Failed!', message: 'Sign Message Failed!',
error: err.error error: err.error
@ -33,6 +41,7 @@ exports.verifyMessage = (req, res, next) => {
request.get(options, (error, response, body) => { request.get(options, (error, response, body) => {
logger.info({fileName: 'Messages', msg: 'Message Verified: ' + JSON.stringify(body)}); logger.info({fileName: 'Messages', msg: 'Message Verified: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Messages', lineNum: 43, msg: 'Verify Message Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Verify message failed!", message: "Verify message failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -41,8 +50,15 @@ exports.verifyMessage = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Messages', lineNum: 24, msg: 'Message Verification Failed: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Messages', lineNum: 59, msg: 'Message Verification Failed: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Verify Message Failed!', message: 'Verify Message Failed!',
error: err.error error: err.error

@ -9,6 +9,7 @@ exports.getRoute = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Network', msg: 'Query Routes Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Network', msg: 'Query Routes Received: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Network', lineNum: 12, msg: 'Query Routes Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Query Routes Failed!", message: "Fetching Query Routes Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -16,7 +17,15 @@ exports.getRoute = (req, res, next) => {
} }
res.status(200).json({routes: body}); res.status(200).json({routes: body});
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Network', lineNum: 27, msg: 'Query Routes Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Query Routes Failed!", message: "Fetching Query Routes Failed!",
error: err.error error: err.error
@ -34,7 +43,15 @@ exports.listNode = (req, res, next) => {
}); });
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Network', lineNum: 52, msg: 'Node Lookup Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Node Lookup Failed!", message: "Node Lookup Failed!",
error: err.error error: err.error
@ -51,7 +68,15 @@ exports.listChannel = (req, res, next) => {
body[1].last_update_str = (body.length > 1 && body[1].last_update) ? common.convertTimestampToDate(body[1].last_update) : ''; body[1].last_update_str = (body.length > 1 && body[1].last_update) ? common.convertTimestampToDate(body[1].last_update) : '';
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Network', lineNum: 76, msg: 'Channel Lookup Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Channel Lookup Failed!", message: "Channel Lookup Failed!",
error: err.error error: err.error
@ -65,7 +90,15 @@ exports.feeRates = (req, res, next) => {
request(options).then(function (body) { request(options).then(function (body) {
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Network', lineNum: 97, msg: 'Fee Rates Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fee Rates Failed!", message: "Fee Rates Failed!",
error: err.error error: err.error

@ -9,7 +9,15 @@ exports.getNewAddress = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
res.status(200).json(body); res.status(200).json(body);
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'OnChain', lineNum: 19, msg: 'OnChain New Address Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching new address failed!", message: "Fetching new address failed!",
error: err.error error: err.error
@ -25,6 +33,7 @@ exports.onChainWithdraw = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'OnChain', msg: 'OnChain Withdraw Response: ' + JSON.stringify(body)}); logger.info({fileName: 'OnChain', msg: 'OnChain Withdraw Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'OnChain', lineNum: 35, msg: 'OnChain Withdraw Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: 'OnChain Withdraw Failed!', message: 'OnChain Withdraw Failed!',
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -33,8 +42,15 @@ exports.onChainWithdraw = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'OnChain', lineNum: 211, msg: 'OnChain Withdraw Response: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'OnChain', lineNum: 51, msg: 'OnChain Withdraw Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'OnChain Withdraw Failed!', message: 'OnChain Withdraw Failed!',
error: err.error error: err.error

@ -9,6 +9,7 @@ exports.listPayments = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Payments', msg: 'Payment List Received: ' + JSON.stringify(body.payments)}); logger.info({fileName: 'Payments', msg: 'Payment List Received: ' + JSON.stringify(body.payments)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Payments', lineNum: 12, msg: 'Payments List Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Payments List Failed!", message: "Payments List Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -23,7 +24,15 @@ exports.listPayments = (req, res, next) => {
res.status(200).json(body.payments); res.status(200).json(body.payments);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Payments', lineNum: 34, msg: 'Payments List Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Payments List Failed!", message: "Payments List Failed!",
error: err.error error: err.error
@ -37,6 +46,7 @@ exports.decodePayment = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Payments', msg: 'Payment Decode Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Payments', msg: 'Payment Decode Received: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Payments', lineNum: 48, msg: 'Payment Decode Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Payment Request Decode Failed!", message: "Payment Request Decode Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -47,7 +57,15 @@ exports.decodePayment = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Payments', lineNum: 66, msg: 'Payment Decode Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Payment Request Decode Failed!", message: "Payment Request Decode Failed!",
error: err.error error: err.error
@ -62,6 +80,7 @@ exports.postPayment = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'Payments', msg: 'Payment Post Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Payments', msg: 'Payment Post Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Payments', lineNum: 81, msg: 'Payment Post Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Payment Post Failed!", message: "Payment Post Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -70,7 +89,15 @@ exports.postPayment = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Payments', lineNum: 97, msg: 'Payments Post Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Payment Post Failed!", message: "Payment Post Failed!",
error: err.error error: err.error

@ -7,11 +7,19 @@ exports.getPeers = (req, res, next) => {
options = common.getOptions(); options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/peer/listPeers'; options.url = common.getSelLNServerUrl() + '/peer/listPeers';
request(options).then(function (body) { request(options).then(function (body) {
let peers = ( body) ? common.sortDescByKey(body, 'alias') : []; let peers = ( body) ? common.sortDescByStrKey(body, 'alias') : [];
logger.info({fileName: 'Peers', msg: 'Peers with Alias: ' + JSON.stringify(peers)}); logger.info({fileName: 'Peers', msg: 'Peers with Alias: ' + JSON.stringify(peers)});
res.status(200).json(peers); res.status(200).json(peers);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Peers', lineNum: 21, msg: 'Peers List Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Peers Fetch Failed!", message: "Peers Fetch Failed!",
error: err.error error: err.error
@ -25,6 +33,7 @@ exports.postPeer = (req, res, next) => {
options.body = req.body; options.body = req.body;
request.post(options, (error, response, body) => { request.post(options, (error, response, body) => {
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Peers', lineNum: 35, msg: 'Connect Peer Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Adding peer failed!", message: "Adding peer failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -33,12 +42,20 @@ exports.postPeer = (req, res, next) => {
logger.info({fileName: 'Peers', msg: 'Peer Added: ' + JSON.stringify(body)}); logger.info({fileName: 'Peers', msg: 'Peer Added: ' + JSON.stringify(body)});
options.url = common.getSelLNServerUrl() + '/peer/listPeers'; options.url = common.getSelLNServerUrl() + '/peer/listPeers';
request(options).then(function (body) { request(options).then(function (body) {
let peers = ( body) ? common.sortDescByKey(body, 'alias') : []; let peers = ( body) ? common.sortDescByStrKey(body, 'alias') : [];
peers = common.newestOnTop(peers, 'id', req.body.id); peers = common.newestOnTop(peers, 'id', req.body.id);
logger.info({fileName: 'Peers', msg: 'Peer with Newest On Top: ' + JSON.stringify(peers)}); logger.info({fileName: 'Peers', msg: 'Peer with Newest On Top: ' + JSON.stringify(peers)});
logger.info({fileName: 'Peers', msg: 'Peer Added Successfully'}); logger.info({fileName: 'Peers', msg: 'Peer Added Successfully'});
res.status(201).json(peers); res.status(201).json(peers);
}).catch((err) => { }).catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Peers', lineNum: 56, msg: 'Connect Peer Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Peer Add Failed!", message: "Peer Add Failed!",
error: err.error error: err.error
@ -54,6 +71,7 @@ exports.deletePeer = (req, res, next) => {
request.delete(options).then((body) => { request.delete(options).then((body) => {
logger.info({fileName: 'Peers', msg: 'Detach Peer Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Peers', msg: 'Detach Peer Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Peers', lineNum: 72, msg: 'Detach Peer Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Detach peer failed!", message: "Detach peer failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -63,10 +81,18 @@ exports.deletePeer = (req, res, next) => {
res.status(204).json({}); res.status(204).json({});
} }
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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) {
delete err.response.request.headers.macaroon;
}
logger.error({fileName: 'Peers', lineNum: 89, msg: 'Detach Peer Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Detach Peer Failed!", message: "Detach Peer Failed!",
error: err.error error: err.error
}); });
}); });
}; };

@ -10,7 +10,7 @@ exports.getBalance = (req, res, next) => {
options.qs = req.query; options.qs = req.query;
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Balance', msg: 'Request params: ' + JSON.stringify(req.params) + 'Request Query: ' + JSON.stringify(req.query) + ' Balance Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Balance', msg: 'Request params: ' + JSON.stringify(req.params) + 'Request Query: ' + JSON.stringify(req.query) + ' Balance Received: ' + JSON.stringify(body)});
if( body) { if (body) {
if (upperCase(req.params.source) === 'BLOCKCHAIN') { if (upperCase(req.params.source) === 'BLOCKCHAIN') {
if (!body.total_balance) { body.total_balance = 0; } if (!body.total_balance) { body.total_balance = 0; }
if (!body.confirmed_balance) { body.confirmed_balance = 0; } if (!body.confirmed_balance) { body.confirmed_balance = 0; }
@ -28,7 +28,15 @@ exports.getBalance = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Balance', lineNum: 38, msg: 'Fetch Balance Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching balance failed!", message: "Fetching balance failed!",
error: err.error error: err.error

@ -5,13 +5,17 @@ var options = {};
getAliasForChannel = (channel) => { getAliasForChannel = (channel) => {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
options.url = common.getSelLNServerUrl() + '/graph/node/' + channel.remote_pubkey; let pubkey = (channel.remote_pubkey) ? channel.remote_pubkey : (channel.remote_node_pub) ? channel.remote_node_pub : '';
options.url = common.getSelLNServerUrl() + '/graph/node/' + pubkey;
request(options).then(function(aliasBody) { request(options).then(function(aliasBody) {
logger.info({fileName: 'Channels', msg: 'Alias: ' + JSON.stringify(aliasBody.node.alias)}); logger.info({fileName: 'Channels', msg: 'Alias: ' + JSON.stringify(aliasBody.node.alias)});
channel.remote_alias = aliasBody.node.alias; channel.remote_alias = aliasBody.node.alias;
resolve(aliasBody.node.alias); resolve(aliasBody.node.alias);
}) })
.catch(err => resolve('')); .catch(err => {
channel.remote_alias = pubkey.slice(0, 10) + '...' + pubkey.slice(-10);
resolve(pubkey);
});
}); });
} }
@ -38,8 +42,16 @@ exports.getAllChannels = (req, res, next) => {
body.channels = common.sortDescByKey(body.channels, 'balancedness'); body.channels = common.sortDescByKey(body.channels, 'balancedness');
logger.info({fileName: 'Channels', msg: 'All Channels with Alias: ' + JSON.stringify(body)}); logger.info({fileName: 'Channels', msg: 'All Channels with Alias: ' + JSON.stringify(body)});
res.status(200).json(body); res.status(200).json(body);
}).catch(err => { })
logger.error({fileName: 'Channels', lineNum: 49, msg: 'Get All Channel Alias: ' + JSON.stringify(err)}); .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 48, msg: 'Get All Channel Alias Error: ' + JSON.stringify(err)});
res.status(500).json({ res.status(500).json({
message: 'Fetching Channels Alias Failed!', message: 'Fetching Channels Alias Failed!',
error: err.error error: err.error
@ -50,8 +62,15 @@ exports.getAllChannels = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 54, msg: 'Get All Channel: ' + JSON.stringify(err.error)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 66, msg: 'Get All Channels Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Fetching All Channels Failed!', message: 'Fetching All Channels Failed!',
error: err.error error: err.error
@ -64,18 +83,53 @@ exports.getPendingChannels = (req, res, next) => {
options.url = common.getSelLNServerUrl() + '/channels/pending'; options.url = common.getSelLNServerUrl() + '/channels/pending';
options.qs = req.query; options.qs = req.query;
request(options).then(function (body) { request(options).then(function (body) {
let channels = [];
if (!body.total_limbo_balance) { if (!body.total_limbo_balance) {
body.total_limbo_balance = 0; body.total_limbo_balance = 0;
body.btc_total_limbo_balance = 0; body.btc_total_limbo_balance = 0;
} else { } else {
body.btc_total_limbo_balance = common.convertToBTC(body.total_limbo_balance); body.btc_total_limbo_balance = common.convertToBTC(body.total_limbo_balance);
} }
logger.info({fileName: 'Channels', msg: 'Pending Channels: ' + JSON.stringify(body)}); const promises = [];
res.status(200).json(body); if(body.pending_open_channels && body.pending_open_channels.length > 0) {
body.pending_open_channels.map(channel => { return promises.push(getAliasForChannel(channel.channel))});
}
if(body.pending_closing_channels && body.pending_closing_channels.length > 0) {
body.pending_closing_channels.map(channel => { return promises.push(getAliasForChannel(channel.channel))});
}
if(body.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) {
body.pending_force_closing_channels.map(channel => { return promises.push(getAliasForChannel(channel.channel))});
}
if(body.waiting_close_channels && body.waiting_close_channels.length > 0) {
body.waiting_close_channels.map(channel => { return promises.push(getAliasForChannel(channel.channel))});
}
Promise.all(promises).then(function(values) {
logger.info({fileName: 'Channels', msg: 'Pending Channels: ' + JSON.stringify(body)});
res.status(200).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 106, msg: 'Get Pending Channel Alias Error: ' + JSON.stringify(err)});
res.status(500).json({
message: 'Fetching Pending Channels Failed!',
error: err.error
});
});
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 78, msg: 'Get Pending Channel: ' + JSON.stringify(err.error)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 97, msg: 'Get Pending Channel Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Fetching Pending Channels Failed!', message: 'Fetching Pending Channels Failed!',
error: err.error error: err.error
@ -88,18 +142,46 @@ exports.getClosedChannels = (req, res, next) => {
options.url = common.getSelLNServerUrl() + '/channels/closed'; options.url = common.getSelLNServerUrl() + '/channels/closed';
options.qs = req.query; options.qs = req.query;
request(options).then(function (body) { request(options).then(function (body) {
let channels = [];
if (body.channels && body.channels.length > 0) { if (body.channels && body.channels.length > 0) {
body.channels.forEach(channel => { Promise.all(
channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type; body.channels.map(channel => {
channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type;
return getAliasForChannel(channel);
})
)
.then(function(values) {
body.channels = common.sortDescByKey(body.channels, 'close_height');
logger.info({fileName: 'Channels', msg: 'Closed Channels: ' + JSON.stringify(body)});
res.status(200).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 48, msg: 'Get All Channel Alias Error: ' + JSON.stringify(err)});
res.status(500).json({
message: 'Fetching Channels Alias Failed!',
error: err.error
});
}); });
body.channels = common.sortDescByKey(body.channels, 'close_height'); } else {
} body.channels = [];
logger.info({fileName: 'Channels', msg: 'Closed Channels: ' + JSON.stringify(body)}); res.status(200).json(body);
res.status(200).json(body); }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 102, msg: 'Get Closed Channel: ' + JSON.stringify(err.error)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 126, msg: 'Get Closed Channel Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Fetching Closed Channels Failed!', message: 'Fetching Closed Channels Failed!',
error: err.error error: err.error
@ -125,6 +207,7 @@ exports.postChannel = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Channel Open Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Channels', msg: 'Channel Open Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Channels', lineNum: 152, msg: 'Open New Channel Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: 'Open Channel Failed!', message: 'Open Channel Failed!',
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -133,8 +216,15 @@ exports.postChannel = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 103, msg: 'Open Channel: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 168, msg: 'Open New Channel Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Open Channel Failed!', message: 'Open Channel Failed!',
error: err.error error: err.error
@ -166,11 +256,13 @@ exports.postTransactions = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Send Payment Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Channels', msg: 'Send Payment Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Channels', lineNum: 200, msg: 'Send Payment Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: 'Send Payment Failed!', message: 'Send Payment Failed!',
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
}); });
} else if (body.payment_error) { } else if (body.payment_error) {
logger.error({fileName: 'Channels', lineNum: 206, msg: 'Send Payment Error: ' + JSON.stringify(body.payment_error)});
res.status(500).json({ res.status(500).json({
message: 'Send Payment Failed!', message: 'Send Payment Failed!',
error: (!body) ? 'Error From Server!' : body.payment_error error: (!body) ? 'Error From Server!' : body.payment_error
@ -179,8 +271,15 @@ exports.postTransactions = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 143, msg: 'Send Payment: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 222, msg: 'Send Payment Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Send Payment Failed!', message: 'Send Payment Failed!',
error: err.error error: err.error
@ -199,6 +298,7 @@ exports.closeChannel = (req, res, next) => {
request.delete(options).then((body) => { request.delete(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Close Channel Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Channels', msg: 'Close Channel Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Channels', lineNum: 241, msg: 'Close Channel Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: 'Close Channel Failed!', message: 'Close Channel Failed!',
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -207,8 +307,15 @@ exports.closeChannel = (req, res, next) => {
res.status(204).json({message: 'Channel Closed!'}); res.status(204).json({message: 'Channel Closed!'});
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 169, msg: 'Close Channel: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 257, msg: 'Close Channel Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Close Channel Failed!', message: 'Close Channel Failed!',
error: err.error error: err.error
@ -241,6 +348,7 @@ exports.postChanPolicy = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Update Channel Policy: ' + JSON.stringify(body)}); logger.info({fileName: 'Channels', msg: 'Update Channel Policy: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Channels', lineNum: 290, msg: 'Update Channel Policy Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: 'Update Channel Failed!', message: 'Update Channel Failed!',
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -249,8 +357,15 @@ exports.postChanPolicy = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels', lineNum: 211, msg: 'Update Channel Policy: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Channels', lineNum: 306, msg: 'Update Channel Policy Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Update Channel Failed!', message: 'Update Channel Failed!',
error: err.error error: err.error

@ -21,7 +21,7 @@ function getFilesList(callback) {
} }
}); });
} }
response = {all_restore_exists: all_restore_exists, files: files_list}; response = {all_restore_exists: all_restore_exists, files: files_list};
callback(response); callback(response);
}); });
} }
@ -33,12 +33,10 @@ exports.getBackup = (req, res, next) => {
if (req.params.channelPoint === 'ALL') { if (req.params.channelPoint === 'ALL') {
channel_backup_file = common.selectedNode.channel_backup_path + common.path_separator + 'channel-all.bak'; channel_backup_file = common.selectedNode.channel_backup_path + common.path_separator + 'channel-all.bak';
message = 'All Channels Backup Successful.'; message = 'All Channels Backup Successful.';
// message = 'All Channels Backup Successful at: ' + channel_backup_file + ' !';
options.url = common.getSelLNServerUrl() + '/channels/backup'; options.url = common.getSelLNServerUrl() + '/channels/backup';
} else { } else {
channel_backup_file = common.selectedNode.channel_backup_path + common.path_separator + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; channel_backup_file = common.selectedNode.channel_backup_path + common.path_separator + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak';
message = 'Channel Backup Successful.'; message = 'Channel Backup Successful.';
// message = 'Channel Backup Successful at: ' + channel_backup_file + ' !';
let channelpoint = req.params.channelPoint.replace(':', '/'); let channelpoint = req.params.channelPoint.replace(':', '/');
options.url = common.getSelLNServerUrl() + '/channels/backup/' + channelpoint; options.url = common.getSelLNServerUrl() + '/channels/backup/' + channelpoint;
let exists = fs.existsSync(channel_backup_file); let exists = fs.existsSync(channel_backup_file);
@ -49,23 +47,46 @@ exports.getBackup = (req, res, next) => {
var createStream = fs.createWriteStream(channel_backup_file); var createStream = fs.createWriteStream(channel_backup_file);
createStream.end(); createStream.end();
} }
catch (err) { catch (errRes) {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'ChannelsBackup', lineNum: 57, msg: 'Channels Backup Error: ' + JSON.stringify(err)});
return res.status(500).json({ message: 'Channels Backup Failed!', error: err }); return res.status(500).json({ message: 'Channels Backup Failed!', error: err });
} }
} }
} }
request(options).then(function (body) { request(options).then(function (body) {
logger.info({fileName: 'Channels Backup', msg: 'Channel Backup: ' + JSON.stringify(body)}); logger.info({fileName: 'ChannelsBackup', msg: 'Channel Backup: ' + JSON.stringify(body)});
fs.writeFile(channel_backup_file, JSON.stringify(body), function(err) { fs.writeFile(channel_backup_file, JSON.stringify(body), function(errRes) {
if (err) { if (errRes) {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'ChannelsBackup', lineNum: 72, msg: 'Channels Backup Error: ' + JSON.stringify(err)});
return res.status(500).json({ message: 'Channels Backup Failed!', error: err.error }); return res.status(500).json({ message: 'Channels Backup Failed!', error: err.error });
} else { } else {
res.status(200).json({ message: message }); res.status(200).json({ message: message });
} }
}); });
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels Backup', lineNum: 44, msg: 'Channel Backup: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'ChannelsBackup', lineNum: 86, msg: 'Channel Backup Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Channels Backup Failed!', message: 'Channels Backup Failed!',
error: err.error error: err.error
@ -98,7 +119,6 @@ exports.postBackupVerify = (req, res, next) => {
} }
} else { } else {
message = 'Channel Verify Successful.'; message = 'Channel Verify Successful.';
// message = 'Channel ' + req.params.channelPoint + ' Verify Successful!';
channel_verify_file = common.selectedNode.channel_backup_path + common.path_separator + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; channel_verify_file = common.selectedNode.channel_backup_path + common.path_separator + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak';
let exists = fs.existsSync(channel_verify_file); let exists = fs.existsSync(channel_verify_file);
if (exists) { if (exists) {
@ -111,11 +131,18 @@ exports.postBackupVerify = (req, res, next) => {
} }
if (verify_backup !== '') { if (verify_backup !== '') {
request.post(options).then(function (body) { request.post(options).then(function (body) {
logger.info({fileName: 'Channels Backup Verify', msg: 'Channel Backup Verify: ' + JSON.stringify(body)}); logger.info({fileName: 'BackupVerify', msg: 'Channel Backup Verify: ' + JSON.stringify(body)});
res.status(201).json({ message: message }); res.status(201).json({ message: message });
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels Backup Verify', lineNum: 93, msg: 'Channel Backup Verify: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'BackupVerify', lineNum: 141, msg: 'Channel Backup Verify Error: ' + JSON.stringify(err)});
return res.status(404).json({ return res.status(404).json({
message: 'Channel backup to Verify failed!', message: 'Channel backup to Verify failed!',
error: err.error error: err.error
@ -148,7 +175,6 @@ exports.postRestore = (req, res, next) => {
} }
} else { } else {
message = 'Channel Restore Successful.'; message = 'Channel Restore Successful.';
// message = 'Channel ' + req.params.channelPoint + ' Restore Successful!';
channel_restore_file = common.selectedNode.channel_backup_path + common.path_separator + 'restore' + common.path_separator + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; channel_restore_file = common.selectedNode.channel_backup_path + common.path_separator + 'restore' + common.path_separator + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak';
let exists = fs.existsSync(channel_restore_file); let exists = fs.existsSync(channel_restore_file);
if (exists) { if (exists) {
@ -161,10 +187,11 @@ exports.postRestore = (req, res, next) => {
} }
if (restore_backup !== '') { if (restore_backup !== '') {
request.post(options).then(function (body) { request.post(options).then(function (body) {
logger.info({fileName: 'Channels Backup Restore', msg: 'Channel Backup Restore: ' + JSON.stringify(body)}); logger.info({fileName: 'ChannelRestore', msg: 'Channel Backup Restore: ' + JSON.stringify(body)});
fs.rename(channel_restore_file, channel_restore_file + '.restored', () => { fs.rename(channel_restore_file, channel_restore_file + '.restored', () => {
getFilesList(getFilesListRes => { getFilesList(getFilesListRes => {
if (getFilesListRes.error) { if (getFilesListRes.error) {
logger.error({fileName: 'ChannelRestore', lineNum: 190, msg: 'Channel Restore Error: ' + JSON.stringify(getFilesListRes.error)});
return res.status(500).json({ message: 'Channel restore failed!', list: getFilesListRes }); return res.status(500).json({ message: 'Channel restore failed!', list: getFilesListRes });
} else { } else {
return res.status(201).json({ message: message, list: getFilesListRes }); return res.status(201).json({ message: message, list: getFilesListRes });
@ -172,8 +199,15 @@ exports.postRestore = (req, res, next) => {
}); });
}); });
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Channels Backup Restore', lineNum: 143, msg: 'Channel Backup Restore: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'ChannelRestore', lineNum: 205, msg: 'Channel Restore Error: ' + JSON.stringify(err)});
return res.status(404).json({ return res.status(404).json({
message: 'Channel restore failed!', message: 'Channel restore failed!',
error: err.error.error error: err.error.error

@ -10,6 +10,7 @@ exports.getFees = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Fees', msg: 'Fee Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Fees', msg: 'Fee Received: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Fees', lineNum: 13, msg: 'Get Fee Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching fee failed!", message: "Fetching fee failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -45,12 +46,22 @@ exports.getFees = (req, res, next) => {
body.weekly_tx_count = weekly_tx_count && weekly_tx_count.length ? weekly_tx_count.length : 0; body.weekly_tx_count = weekly_tx_count && weekly_tx_count.length ? weekly_tx_count.length : 0;
body.monthly_tx_count = history.forwarding_events && history.forwarding_events.length ? history.forwarding_events.length : 0; body.monthly_tx_count = history.forwarding_events && history.forwarding_events.length ? history.forwarding_events.length : 0;
body.forwarding_events_history = history; body.forwarding_events_history = history;
if (history.error) { logger.error({fileName: 'Fees', lineNum: 48, msg: 'Fetch Forwarding Events Error: ' + JSON.stringify(err)}); } if (history.error) {
logger.error({fileName: 'Fees', lineNum: 50, msg: 'Fetch Forwarding Events Error: ' + JSON.stringify(history.error)});
}
res.status(200).json(body); res.status(200).json(body);
}) })
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Fees', lineNum: 63, msg: 'Fetch Forwarding Events Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching fee failed!", message: "Fetching fee failed!",
error: err.error error: err.error

@ -9,6 +9,7 @@ exports.getInfo = (req, res, next) => {
options = common.getOptions(); options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getinfo'; options.url = common.getSelLNServerUrl() + '/getinfo';
logger.info({fileName:'GetInfo', msg: 'Selected Node: ' + JSON.stringify(common.selectedNode.ln_node)}); logger.info({fileName:'GetInfo', msg: 'Selected Node: ' + JSON.stringify(common.selectedNode.ln_node)});
logger.info({fileName: 'GetInfo', msg: 'Calling Info from LND server url: ' + options.url});
if (!options.headers || !options.headers['Grpc-Metadata-macaroon']) { if (!options.headers || !options.headers['Grpc-Metadata-macaroon']) {
logger.error({fileName: 'GetInfo', lineNum: 17, msg: 'LND Get info failed due to bad or missing macaroon!'}); logger.error({fileName: 'GetInfo', lineNum: 17, msg: 'LND Get info failed due to bad or missing macaroon!'});
res.status(502).json({ res.status(502).json({
@ -17,12 +18,12 @@ exports.getInfo = (req, res, next) => {
}); });
} else { } else {
common.nodes.map(node => { if (node.lnImplementation === 'LND') { connect.getAllNodeAllChannelBackup(node); }}); common.nodes.map(node => { if (node.lnImplementation === 'LND') { connect.getAllNodeAllChannelBackup(node); }});
logger.info({fileName: 'GetInfo', msg: 'Calling getinfo from lnd server url: ' + options.url});
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)}); logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)});
const body_str = (!body) ? '' : JSON.stringify(body); const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
if(!body || search_idx > -1 || body.error) { if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'GetInfo', lineNum: 26, msg: 'Get Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Info Failed!", message: "Fetching Info Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -31,7 +32,15 @@ exports.getInfo = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'GetInfo', lineNum: 42, msg: 'Get Info Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Info Failed!", message: "Fetching Info Failed!",
error: err.error error: err.error

@ -24,6 +24,7 @@ exports.getDescribeGraph = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'Graph', msg: 'Describe Graph Received: ' + body_str}); logger.info({fileName: 'Graph', msg: 'Describe Graph Received: ' + body_str});
if(!body || search_idx > -1 || body.error) { if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'Graph', lineNum: 27, msg: 'Describe Graph Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Describe Graph Failed!", message: "Fetching Describe Graph Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -32,7 +33,15 @@ exports.getDescribeGraph = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Graph', lineNum: 43, msg: 'Describe Graph Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Describe Graph Failed!", message: "Fetching Describe Graph Failed!",
error: err.error error: err.error
@ -48,6 +57,7 @@ exports.getGraphInfo = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'Graph', msg: 'Network Info Received: ' + body_str}); logger.info({fileName: 'Graph', msg: 'Network Info Received: ' + body_str});
if(!body || search_idx > -1 || body.error) { if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'Graph', lineNum: 59, msg: 'Network Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching network Info failed!", message: "Fetching network Info failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -61,7 +71,15 @@ exports.getGraphInfo = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Graph', lineNum: 80, msg: 'Fetch Network Info Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching network Info failed!", message: "Fetching network Info failed!",
error: err.error error: err.error
@ -75,17 +93,26 @@ exports.getGraphNode = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Graph', msg: 'Node Info Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Graph', msg: 'Node Info Received: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Graph', lineNum: 94, msg: 'Fetch Node Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching node Info failed!", message: "Fetching node Info failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
}); });
} }
if ( body) { if (body) {
body.node.last_update_str = (!body.node.last_update) ? '' : common.convertTimestampToDate(body.node.last_update); body.node.last_update_str = (!body.node.last_update) ? '' : common.convertTimestampToDate(body.node.last_update);
} }
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Graph', lineNum: 112, msg: 'Fetch Node Info Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching node Info failed!", message: "Fetching node Info failed!",
error: err.error error: err.error
@ -99,17 +126,26 @@ exports.getGraphEdge = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Graph', msg: 'Edge Info Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Graph', msg: 'Edge Info Received: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Graph', lineNum: 126, msg: 'Fetch Edge Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Edge Info Failed!", message: "Fetching Edge Info Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
}); });
} }
if ( body) { if (body) {
body.last_update_str = (!body.last_update) ? '' : common.convertTimestampToDate(body.last_update); body.last_update_str = (!body.last_update) ? '' : common.convertTimestampToDate(body.last_update);
} }
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Graph', lineNum: 144, msg: 'Fetch Edge Info Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Edge Info Failed!", message: "Fetching Edge Info Failed!",
error: err.error error: err.error
@ -127,12 +163,13 @@ exports.getQueryRoutes = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Graph', msg: 'Query Routes Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Graph', msg: 'Query Routes Received: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Graph', lineNum: 162, msg: 'Fetch Query Routes Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Query Routes Failed!", message: "Fetching Query Routes Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
}); });
} }
if ( body.routes && body.routes.length > 0) { if (body.routes && body.routes.length > 0) {
body.routes.forEach(route => { body.routes.forEach(route => {
if ( route.hops) { if ( route.hops) {
Promise.all( Promise.all(
@ -144,7 +181,16 @@ exports.getQueryRoutes = (req, res, next) => {
.then(function(values) { .then(function(values) {
logger.info({fileName: 'Graph', msg: 'Hops with Alias: ' + JSON.stringify(body)}); logger.info({fileName: 'Graph', msg: 'Hops with Alias: ' + JSON.stringify(body)});
res.status(200).json(body); res.status(200).json(body);
}).catch(err => { })
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Graph', lineNum: 187, msg: 'Fetch Query Routes Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Query Routes Failed!", message: "Fetching Query Routes Failed!",
error: err.error error: err.error
@ -154,7 +200,15 @@ exports.getQueryRoutes = (req, res, next) => {
}); });
} }
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Graph', lineNum: 204, msg: 'Fetch Query Routes Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Query Routes Failed!", message: "Fetching Query Routes Failed!",
error: err.error error: err.error
@ -168,15 +222,15 @@ exports.getRemoteFeePolicy = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Graph', msg: 'Edge Info Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Graph', msg: 'Edge Info Received: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Graph', lineNum: 218, msg: 'Fetch Edge Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Edge Info Failed!", message: "Fetching Edge Info Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
}); });
} }
if ( body) { if (body) {
body.last_update_str = (!body.last_update) ? '' : common.convertTimestampToDate(body.last_update); body.last_update_str = (!body.last_update) ? '' : common.convertTimestampToDate(body.last_update);
} }
remoteNodeFee = {}; remoteNodeFee = {};
if(body.node1_pub === req.params.localPubkey){ if(body.node1_pub === req.params.localPubkey){
remoteNodeFee = { remoteNodeFee = {
@ -184,21 +238,27 @@ exports.getRemoteFeePolicy = (req, res, next) => {
fee_base_msat: body.node2_policy.fee_base_msat, fee_base_msat: body.node2_policy.fee_base_msat,
fee_rate_milli_msat: body.node2_policy.fee_rate_milli_msat fee_rate_milli_msat: body.node2_policy.fee_rate_milli_msat
}; };
} } else if(body.node2_pub === req.params.localPubkey) {
else if(body.node2_pub === req.params.localPubkey) {
remoteNodeFee = { remoteNodeFee = {
time_lock_delta: body.node1_policy.time_lock_delta, time_lock_delta: body.node1_policy.time_lock_delta,
fee_base_msat: body.node1_policy.fee_base_msat, fee_base_msat: body.node1_policy.fee_base_msat,
fee_rate_milli_msat: body.node1_policy.fee_rate_milli_msat fee_rate_milli_msat: body.node1_policy.fee_rate_milli_msat
}; };
} }
res.status(200).json(remoteNodeFee); res.status(200).json(remoteNodeFee);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Graph', lineNum: 250, msg: 'Fetch Edge Info Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Edge Info Failed!", message: "Fetching Edge Info Failed!",
error: err.error error: err.error
}); });
}); });
}; };

@ -9,6 +9,7 @@ exports.getGraphInfo = (req, res, next) => {
const body_str = (!body) ? '' : JSON.stringify(body); const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
if(!body || search_idx > -1 || body.error) { if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'GraphInfo', lineNum: 12, msg: 'Fetch Network Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching network Info failed!", message: "Fetching network Info failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -20,5 +21,19 @@ exports.getGraphInfo = (req, res, next) => {
body.btc_max_channel_size = (!body.max_channel_size) ? 0 : common.convertToBTC(body.max_channel_size); body.btc_max_channel_size = (!body.max_channel_size) ? 0 : common.convertToBTC(body.max_channel_size);
res.status(200).json(body); res.status(200).json(body);
} }
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'GraphInfo', lineNum: 32, msg: 'Fetch Network Info Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: "Fetching Network Info Failed!",
error: err.error
});
}); });
}; };

@ -9,6 +9,7 @@ exports.getInvoice = (req, res, next) => {
request(options).then((body) => { request(options).then((body) => {
logger.info({fileName: 'Invoice', msg: 'Invoice Info Received: ' + JSON.stringify(body)}); logger.info({fileName: 'Invoice', msg: 'Invoice Info Received: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Invoice', lineNum: 12, msg: 'Invoice Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Invoice Info Failed!", message: "Fetching Invoice Info Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -16,7 +17,15 @@ exports.getInvoice = (req, res, next) => {
} }
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Invoice', lineNum: 27, msg: 'Fetch Invoice Info Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Invoice Info Failed!", message: "Fetching Invoice Info Failed!",
error: err.error error: err.error
@ -33,6 +42,7 @@ exports.listInvoices = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'Invoice', msg: 'Invoices List Received: ' + body_str}); logger.info({fileName: 'Invoice', msg: 'Invoices List Received: ' + body_str});
if(!body || search_idx > -1 || body.error) { if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'Invoice', lineNum: 44, msg: 'List Invoices Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Invoice Info failed!", message: "Fetching Invoice Info failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -51,9 +61,17 @@ exports.listInvoices = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Invoice', lineNum: 70, msg: 'List Invoice Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Invoice Info failed!", message: "Fetching Invoices failed!",
error: err.error error: err.error
}); });
}); });
@ -76,6 +94,7 @@ exports.addInvoice = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'Invoice', msg: 'Add Invoice Responce: ' + JSON.stringify(body)}); logger.info({fileName: 'Invoice', msg: 'Add Invoice Responce: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Invoice', lineNum: 95, msg: 'Add Invoice Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Add Invoice Failed!", message: "Add Invoice Failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -84,7 +103,15 @@ exports.addInvoice = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Invoice', lineNum: 111, msg: 'Add Invoice Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Add Invoice Failed!", message: "Add Invoice Failed!",
error: err.error error: err.error

@ -1,8 +1,16 @@
var fs = require('fs'); var fs = require('fs');
exports.getLNDSettings = (req, res, next) => { exports.getLNDSettings = (req, res, next) => {
fs.readFile(req.headers.filepath, function(err, data) { fs.readFile(req.headers.filepath, function(errRes, data) {
if (err) { if (errRes) {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'LNDConfSetting', lineNum: 12, msg: 'Reading Config File Error: ' + JSON.stringify(err)});
res.status(500).json({ res.status(500).json({
message: "Reading Config File Failed!", message: "Reading Config File Failed!",
error: err error: err

@ -25,6 +25,7 @@ exports.loopOut = (req, res, next) => {
request.post(options).then(function (body) { request.post(options).then(function (body) {
logger.info({fileName: 'Loop', msg: 'Loop Out: ' + JSON.stringify(body)}); logger.info({fileName: 'Loop', msg: 'Loop Out: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Loop', lineNum: 28, msg: 'Loop Out Error: ' + JSON.stringify(body.error)});
res.status(500).json({ res.status(500).json({
message: 'Loop Out Failed!', message: 'Loop Out Failed!',
error: (!body) ? 'Error From Server!' : body.error.message error: (!body) ? 'Error From Server!' : body.error.message
@ -33,11 +34,18 @@ exports.loopOut = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Loop Out', lineNum: 33, msg: 'Loop Out Failed: ' + JSON.stringify(err.error)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 44, msg: 'Loop Out Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop Out Failed!", message: "Loop Out Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };
@ -50,10 +58,18 @@ exports.loopOutTerms = (req, res, next) => {
logger.info({fileName: 'Loop', msg: 'Loop Out Terms: ' + JSON.stringify(body)}); logger.info({fileName: 'Loop', msg: 'Loop Out Terms: ' + JSON.stringify(body)});
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 67, msg: 'Loop Out Terms Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop Out Terms Failed!", message: "Loop Out Terms Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };
@ -70,10 +86,18 @@ exports.loopOutQuote = (req, res, next) => {
body.swap_payment_dest = body.swap_payment_dest ? Buffer.from(body.swap_payment_dest, 'base64').toString('hex') : ''; body.swap_payment_dest = body.swap_payment_dest ? Buffer.from(body.swap_payment_dest, 'base64').toString('hex') : '';
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 94, msg: 'Loop Out Quote Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop Out Quote Failed!", message: "Loop Out Quote Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };
@ -101,17 +125,33 @@ exports.loopOutTermsAndQuotes = (req, res, next) => {
logger.info({fileName: 'Loop', msg: 'Loop Out Quotes 2: ' + JSON.stringify(values[1])}); logger.info({fileName: 'Loop', msg: 'Loop Out Quotes 2: ' + JSON.stringify(values[1])});
res.status(200).json(values); res.status(200).json(values);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 132, msg: 'Loop Out Quotes Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop Out Quotes Failed!", message: "Loop Out Quotes Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 146, msg: 'Loop Out Terms Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop Out Terms Failed!", message: "Loop Out Terms Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };
@ -124,25 +164,31 @@ exports.loopIn = (req, res, next) => {
amt: req.body.amount, amt: req.body.amount,
max_swap_fee: req.body.swapFee, max_swap_fee: req.body.swapFee,
max_miner_fee: req.body.minerFee max_miner_fee: req.body.minerFee
// last_hop: req.body.lastHop,
// external_htlc: req.body.externalHtlc
}); });
request.post(options).then(function (body) { request.post(options).then(function (body) {
logger.info({fileName: 'Loop', msg: 'Loop In: ' + JSON.stringify(body)}); logger.info({fileName: 'Loop', msg: 'Loop In: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Loop', lineNum: 166, msg: 'Loop In Error: ' + JSON.stringify(body.error)});
res.status(500).json({ res.status(500).json({
message: 'Loop In Failed!', message: 'Loop In Failed!',
error: (!body) ? 'Error From Server!' : body.error.message error: (body.error && body.error.message) ? body.error.message : 'Error From Server!'
}); });
} else { } else {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Loop In', lineNum: 134, msg: 'Loop In Failed: ' + JSON.stringify(err.error)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 182, msg: 'Loop In Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop In Failed!", message: "Loop In Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };
@ -155,10 +201,18 @@ exports.loopInTerms = (req, res, next) => {
logger.info({fileName: 'Loop', msg: 'Loop In Terms: ' + JSON.stringify(body)}); logger.info({fileName: 'Loop', msg: 'Loop In Terms: ' + JSON.stringify(body)});
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 205, msg: 'Loop In Terms Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop In Terms Failed!", message: "Loop In Terms Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };
@ -175,10 +229,18 @@ exports.loopInQuote = (req, res, next) => {
body.swap_payment_dest = body.swap_payment_dest ? Buffer.from(body.swap_payment_dest, 'base64').toString('hex') : ''; body.swap_payment_dest = body.swap_payment_dest ? Buffer.from(body.swap_payment_dest, 'base64').toString('hex') : '';
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 232, msg: 'Loop In Quote Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop In Quote Failed!", message: "Loop In Quote Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };
@ -206,17 +268,33 @@ exports.loopInTermsAndQuotes = (req, res, next) => {
logger.info({fileName: 'Loop', msg: 'Loop In Quotes 2: ' + JSON.stringify(values[1])}); logger.info({fileName: 'Loop', msg: 'Loop In Quotes 2: ' + JSON.stringify(values[1])});
res.status(200).json(values); res.status(200).json(values);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 270, msg: 'Loop In Quotes Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop In Quotes Failed!", message: "Loop In Quotes Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 284, msg: 'Loop In Terms Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop In Terms Failed!", message: "Loop In Terms Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };
@ -238,10 +316,18 @@ exports.swaps = (req, res, next) => {
} }
res.status(200).json(body.swaps); res.status(200).json(body.swaps);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 316, msg: 'List Swaps Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop Swaps Failed!", message: "Loop Swaps Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };
@ -257,11 +343,18 @@ exports.swap = (req, res, next) => {
body.last_update_time_str = (!body.last_update_time) ? '' : common.convertTimestampToDate(Math.round(body.last_update_time/1000000000)); body.last_update_time_str = (!body.last_update_time) ? '' : common.convertTimestampToDate(Math.round(body.last_update_time/1000000000));
res.status(200).json(body); res.status(200).json(body);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Loop', lineNum: 342, msg: 'Swap Info Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Loop Swap Failed!", message: "Loop Swap Failed!",
error: err.error.error ? err.error.error : err.error error: (err.error && err.error.error) ? err.error.error : (err.error) ? err.error : err
}); });
}); });
}; };

@ -12,6 +12,7 @@ exports.signMessage = (req, res, next) => {
request.post(options, (error, response, body) => { request.post(options, (error, response, body) => {
logger.info({fileName: 'Messages', msg: 'Message Signed: ' + JSON.stringify(body)}); logger.info({fileName: 'Messages', msg: 'Message Signed: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Messages', lineNum: 15, msg: 'Sign Message Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Sign message failed!", message: "Sign message failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -20,8 +21,15 @@ exports.signMessage = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Messages', lineNum: 24, msg: 'Sign Message Failed: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Messages', lineNum: 31, msg: 'Sign Message Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Sign Message Failed!', message: 'Sign Message Failed!',
error: err.error error: err.error
@ -39,6 +47,7 @@ exports.verifyMessage = (req, res, next) => {
request.post(options, (error, response, body) => { request.post(options, (error, response, body) => {
logger.info({fileName: 'Messages', msg: 'Message Verified: ' + JSON.stringify(body)}); logger.info({fileName: 'Messages', msg: 'Message Verified: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Messages', lineNum: 49, msg: 'Verify Message Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Verify message failed!", message: "Verify message failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -47,8 +56,15 @@ exports.verifyMessage = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
logger.error({fileName: 'Messages', lineNum: 24, msg: 'Message Verification Failed: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Messages', lineNum: 65, msg: 'Message Verification Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: 'Verify Message Failed!', message: 'Verify Message Failed!',
error: err.error error: err.error

@ -11,15 +11,24 @@ exports.getNewAddress = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'NewAddress', msg: 'New Address Received: ' + body_str}); logger.info({fileName: 'NewAddress', msg: 'New Address Received: ' + body_str});
if(!body || search_idx > -1 || body.error) { if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'NewAddress', lineNum: 14, msg: 'New Address Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching new address failed!", message: "Fetching new address failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
}); });
} else { } else {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'NewAddress', lineNum: 30, msg: 'New Address Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching new address failed!", message: "Fetching new address failed!",
error: err.error error: err.error

@ -11,6 +11,7 @@ exports.decodePayment = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'PayReq', msg: 'Payment Decode Received: ' + body_str}); logger.info({fileName: 'PayReq', msg: 'Payment Decode Received: ' + body_str});
if(!body || search_idx > -1 || body.error) { if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'PayReq', lineNum: 14, msg: 'Payment Decode Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Payment Request Decode Failed!", message: "Payment Request Decode Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -21,7 +22,15 @@ exports.decodePayment = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'PayReq', lineNum: 32, msg: 'Payment Decode Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Payment Request Decode Failed!", message: "Payment Request Decode Failed!",
error: err.error error: err.error

@ -11,6 +11,7 @@ exports.getPayments = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'Payments', msg: 'Payment Decoded Received: ' + body_str}); logger.info({fileName: 'Payments', msg: 'Payment Decoded Received: ' + body_str});
if(!body || search_idx > -1 || body.error) { if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'Payments', lineNum: 14, msg: 'List Payments Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Payments List Failed!", message: "Payments List Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -25,7 +26,15 @@ exports.getPayments = (req, res, next) => {
res.status(200).json(body.payments); res.status(200).json(body.payments);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Payments', lineNum: 36, msg: 'List Payments Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Payments List Failed!", message: "Payments List Failed!",
error: err.error error: err.error

@ -12,7 +12,10 @@ getAliasForPeers = (peer) => {
peer.alias = aliasBody.node.alias; peer.alias = aliasBody.node.alias;
resolve(aliasBody.node.alias); resolve(aliasBody.node.alias);
}) })
.catch(err => resolve('')); .catch(err => {
peer.alias = peer.pub_key.slice(0, 10) + '...' + peer.pub_key.slice(-10);
resolve(peer.pub_key);
});
}); });
} }
@ -29,14 +32,21 @@ exports.getPeers = (req, res, next) => {
.then(function(values) { .then(function(values) {
logger.info({fileName: 'Peers', msg: 'Peers with Alias before Sort: ' + JSON.stringify(body)}); logger.info({fileName: 'Peers', msg: 'Peers with Alias before Sort: ' + JSON.stringify(body)});
if (body.peers) { if (body.peers) {
body.peers = common.sortDescByKey(body.peers, 'alias'); body.peers = common.sortDescByStrKey(body.peers, 'alias');
} }
logger.info({fileName: 'Peers', msg: 'Peers with Alias after Sort: ' + JSON.stringify(body)}); logger.info({fileName: 'Peers', msg: 'Peers with Alias after Sort: ' + JSON.stringify(body)});
res.status(200).json(body.peers); res.status(200).json(body.peers);
}); });
}) })
.catch((err) => { .catch(errRes => {
logger.error({fileName: 'Peers', lineNum: 39, msg: 'Peers List Failed: ' + JSON.stringify(err)}); let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Peers', lineNum: 39, msg: 'List Peers Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Peers List Failed!", message: "Peers List Failed!",
error: err.error error: err.error
@ -54,6 +64,7 @@ exports.postPeer = (req, res, next) => {
request.post(options, (error, response, body) => { request.post(options, (error, response, body) => {
logger.info({fileName: 'Peers', msg: 'Peer Added: ' + JSON.stringify(body)}); logger.info({fileName: 'Peers', msg: 'Peer Added: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Peers', lineNum: 63, msg: 'Add Peer Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Adding peer failed!", message: "Adding peer failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -67,8 +78,8 @@ exports.postPeer = (req, res, next) => {
return getAliasForPeers(peer); return getAliasForPeers(peer);
})) }))
.then(function(values) { .then(function(values) {
if ( body.peers) { if (body.peers) {
body.peers = common.sortDescByKey(body.peers, 'alias'); body.peers = common.sortDescByStrKey(body.peers, 'alias');
logger.info({fileName: 'Peers', msg: 'Peer with Alias: ' + JSON.stringify(body)}); logger.info({fileName: 'Peers', msg: 'Peer with Alias: ' + JSON.stringify(body)});
body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey); body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey);
logger.info({fileName: 'Peers', msg: 'Peer with Newest On Top: ' + JSON.stringify(body)}); logger.info({fileName: 'Peers', msg: 'Peer with Newest On Top: ' + JSON.stringify(body)});
@ -76,7 +87,15 @@ exports.postPeer = (req, res, next) => {
logger.info({fileName: 'Peers', msg: 'Peer Added Successfully'}); logger.info({fileName: 'Peers', msg: 'Peer Added Successfully'});
res.status(201).json(body.peers); res.status(201).json(body.peers);
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Peer', lineNum: 93, msg: 'Add Peer Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Peer Add Failed!", message: "Peer Add Failed!",
error: err.error error: err.error
@ -93,6 +112,7 @@ exports.deletePeer = (req, res, next) => {
request.delete(options).then((body) => { request.delete(options).then((body) => {
logger.info({fileName: 'Peers', msg: 'Detach Peer Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Peers', msg: 'Detach Peer Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Peers', lineNum: 110, msg: 'Detach Peer Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Detach peer failed!", message: "Detach peer failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -102,7 +122,15 @@ exports.deletePeer = (req, res, next) => {
res.status(204).json({}); res.status(204).json({});
} }
}) })
.catch((err) => { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Peers', lineNum: 127, msg: 'Detach Peer Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Detach Peer Failed!", message: "Detach Peer Failed!",
error: err.error error: err.error

@ -44,8 +44,16 @@ exports.getAllForwardingEvents = (start, end, offset, callback) => {
} else { } else {
this.getAllForwardingEvents(start, end, offset + num_max_events, callback); this.getAllForwardingEvents(start, end, offset + num_max_events, callback);
} }
}).catch(err => { })
logger.error({fileName: 'Switch', lineNum: 32, msg: 'Get All Forwarding Events Failed: ' + JSON.stringify(err)}); .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Switch', lineNum: 54, msg: 'Get All Forwarding Events Error: ' + JSON.stringify(err)});
callback({ callback({
message: "Forwarding Events Failed!", message: "Forwarding Events Failed!",
error: err.error error: err.error

@ -10,13 +10,14 @@ exports.getTransactions = (req, res, next) => {
const body_str = (!body) ? '' : JSON.stringify(body); const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'Transactions', msg: 'Transaction Received: ' + body_str}); logger.info({fileName: 'Transactions', msg: 'Transaction Received: ' + body_str});
if(!body || search_idx > -1 || body.error) { if (!body || search_idx > -1 || body.error) {
logger.error({fileName: 'Transactions', lineNum: 14, msg: 'List Transactions Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Fetching Transactions Failed!", message: "Fetching Transactions Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
}); });
} else { } else {
if ( body.transactions && body.transactions.length > 0) { if (body.transactions && body.transactions.length > 0) {
body.transactions.forEach(transaction => { body.transactions.forEach(transaction => {
transaction.time_stamp_str = (!transaction.time_stamp) ? '' : common.convertTimestampToDate(transaction.time_stamp); transaction.time_stamp_str = (!transaction.time_stamp) ? '' : common.convertTimestampToDate(transaction.time_stamp);
}); });
@ -25,7 +26,15 @@ exports.getTransactions = (req, res, next) => {
res.status(200).json(body.transactions); res.status(200).json(body.transactions);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Transactions', lineNum: 36, msg: 'List Transactions Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Fetching Transactions Failed!", message: "Fetching Transactions Failed!",
error: err.error error: err.error
@ -49,6 +58,7 @@ exports.postTransactions = (req, res, next) => {
request.post(options).then((body) => { request.post(options).then((body) => {
logger.info({fileName: 'Transactions', msg: 'Transaction Post Response: ' + JSON.stringify(body)}); logger.info({fileName: 'Transactions', msg: 'Transaction Post Response: ' + JSON.stringify(body)});
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Transactions', lineNum: 60, msg: 'Post Transaction Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Transactions post failed!", message: "Transactions post failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -57,7 +67,15 @@ exports.postTransactions = (req, res, next) => {
res.status(201).json(body); res.status(201).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Transactions', lineNum: 76, msg: 'Transaction Post Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Transactions post failed!", message: "Transactions post failed!",
error: err.error error: err.error

@ -13,6 +13,7 @@ exports.genSeed = (req, res, next) => {
} }
request(options).then((body) => { request(options).then((body) => {
if(!body || body.error) { if(!body || body.error) {
logger.error({fileName: 'Wallet', lineNum: 16, msg: 'Gen Seed Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({ res.status(500).json({
message: "Genseed failed!", message: "Genseed failed!",
error: (!body) ? 'Error From Server!' : body.error error: (!body) ? 'Error From Server!' : body.error
@ -21,7 +22,15 @@ exports.genSeed = (req, res, next) => {
res.status(200).json(body); res.status(200).json(body);
} }
}) })
.catch(function (err) { .catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Wallet', lineNum: 32, msg: 'Gen Seed Error: ' + JSON.stringify(err)});
return res.status(500).json({ return res.status(500).json({
message: "Genseed failed!", message: "Genseed failed!",
error: err.error error: err.error
@ -59,11 +68,13 @@ exports.operateWallet = (req, res, next) => {
const body_str = (!body) ? '' : JSON.stringify(body); const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found'); const search_idx = (!body) ? -1 : body_str.search('Not Found');
if(!body) { if(!body) {
logger.error({fileName: 'Wallet', lineNum: 70, msg: 'Wallet Error: ' + ((error) ? JSON.stringify(error) : err_message)});
res.status(500).json({ res.status(500).json({
message: err_message, message: err_message,
error: (error) ? error : err_message error: (error) ? error : err_message
}); });
} else if(search_idx > -1) { } else if(search_idx > -1) {
logger.error({fileName: 'Wallet', lineNum: 76, msg: 'Wallet Error: ' + err_message});
res.status(500).json({ res.status(500).json({
message: err_message, message: err_message,
error: err_message error: err_message
@ -72,6 +83,7 @@ exports.operateWallet = (req, res, next) => {
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'); res.status(201).json('Successful');
} else { } else {
logger.error({fileName: 'Wallet', lineNum: 85, msg: 'Wallet Error: ' + JSON.stringify(body.error)});
res.status(500).json({ res.status(500).json({
message: err_message, message: err_message,
error: body.error error: body.error
@ -80,20 +92,28 @@ exports.operateWallet = (req, res, next) => {
} else { } else {
res.status(201).json('Successful'); res.status(201).json('Successful');
} }
}).catch(error => { })
logger.error({fileName: 'Wallet', lineNum: 83, msg: 'Wallet Response: ' + JSON.stringify(error.error)}); .catch(errRes => {
if((error.error.code === 1 && error.error.error === 'context canceled') || (error.error.code === 14 && error.error.error === 'transport is closing')) { let err = JSON.parse(JSON.stringify(errRes));
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']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Wallet', lineNum: 101, msg: 'Wallet Error: ' + JSON.stringify(err)});
if((err.error.code === 1 && err.error.error === 'context canceled') || (err.error.code === 14 && err.error.error === 'transport is closing')) {
res.status(201).json('Successful'); res.status(201).json('Successful');
} else { } else {
res.status(500).json({ res.status(500).json({
message: err_message, message: err_message,
error: error.error.message ? error.error.message : error.message error: err.error.message ? err.error.message : err.message ? err.message : err_message
}); });
} }
}); });
}; };
exports.updateSelNodeOptions = (req, res, next) => { exports.updateSelNodeOptions = (req, res, next) => {
let response = common.updateSelectedNodeOptions(); let response = common.updateSelectedNodeOptions();
res.status(response.status).json({updateMessage: response.message}); res.status(response.status).json({updateMessage: response.message});
} }

@ -6,6 +6,7 @@ parameters have `default` values for initial setup and can be updated after RTL
{ {
"multiPass": "<The password in plain text, default 'password', Required>", "multiPass": "<The password in plain text, default 'password', Required>",
"port": "<port number for the rtl node server, default '3000', Required>", "port": "<port number for the rtl node server, default '3000', Required>",
"host": "<host for the rtl node server, default 'localhost', Optional>",
"defaultNodeIndex": <Default index to load when rtl server starts, default 1, Optional>, "defaultNodeIndex": <Default index to load when rtl server starts, default 1, Optional>,
"SSO": { "SSO": {
"rtlSSO": <parameter to turn SSO off/on. Allowed values - 1 (single sign on via an external cookie), 0 (stand alone RTL authentication), default 0, Required>, "rtlSSO": <parameter to turn SSO off/on. Allowed values - 1 (single sign on via an external cookie), 0 (stand alone RTL authentication), default 0, Required>,
@ -41,6 +42,7 @@ parameters have `default` values for initial setup and can be updated after RTL
;The environment variable can also be used for all of the above configurations except the UI settings. ;The environment variable can also be used for all of the above configurations except the UI settings.
;If the environment variables are set, it will take precedence over the parameters in the RTL-Config.json file. ;If the environment variables are set, it will take precedence over the parameters in the RTL-Config.json file.
PORT (port number for the rtl node server, default 3000, Required) PORT (port number for the rtl node server, default 3000, Required)
HOST (host for the rtl node server, default localhost, Optional)
LN_IMPLEMENTATION (LND, CLT. Default 'LND', Required) LN_IMPLEMENTATION (LND, CLT. Default 'LND', Required)
LN_SERVER_URL (LND server URL for REST APIs, default https://localhost:8080/v1) OR LN_SERVER_URL (LN server URL for LNP REST APIs) (Required) LN_SERVER_URL (LND server URL for REST APIs, default https://localhost:8080/v1) OR LN_SERVER_URL (LN server URL for LNP REST APIs) (Required)
SWAP_SERVER_URL (Swap server URL for REST APIs, default http://localhost:8081/v1) (Optional) SWAP_SERVER_URL (Swap server URL for REST APIs, default http://localhost:8081/v1) (Optional)

@ -14,6 +14,7 @@ If your running RTL and LND on different devices on your local LAN, certain conf
{ {
"multiPass": "<password in plain text, Default 'password'>", "multiPass": "<password in plain text, Default 'password'>",
"port": "3000", "port": "3000",
"host": "localhost",
"defaultNodeIndex": 1, "defaultNodeIndex": 1,
"SSO": { "SSO": {
"rtlSSO": 0, "rtlSSO": 0,

2
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "rtl", "name": "rtl",
"version": "0.7.0-beta", "version": "0.7.1-beta",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

@ -1,6 +1,6 @@
{ {
"name": "rtl", "name": "rtl",
"version": "0.7.0-beta", "version": "0.7.1-beta",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",

@ -31,11 +31,11 @@ const onListening = () => {
const addr = server.address(); const addr = server.address();
const bind = typeof addr === "string" ? "pipe " + addr : "port " + common.port; const bind = typeof addr === "string" ? "pipe " + addr : "port " + common.port;
debug("Listening on " + bind); debug("Listening on " + bind);
console.log('Server is up and running, please open the UI at http://localhost:' + common.port); console.log('Server is up and running, please open the UI at http://' + common.host + ':' + common.port);
}; };
const server = http.createServer(app); const server = http.createServer(app);
server.on("error", onError); server.on("error", onError);
server.on("listening", onListening); server.on("listening", onListening);
server.listen(common.port); server.listen(common.port, common.host);

@ -102,8 +102,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
this.userIdle.onTimeout().pipe(takeUntil(this.unSubs[3])).subscribe(() => { this.userIdle.onTimeout().pipe(takeUntil(this.unSubs[3])).subscribe(() => {
if (this.sessionService.getItem('token')) { if (this.sessionService.getItem('token')) {
this.logger.warn('Time limit exceeded for session inactivity.'); this.logger.warn('Time limit exceeded for session inactivity.');
this.store.dispatch(new RTLActions.CloseAlert()); this.store.dispatch(new RTLActions.CloseAllDialogs());
this.store.dispatch(new RTLActions.CloseConfirmation(false));
this.store.dispatch(new RTLActions.OpenAlert({ data: { this.store.dispatch(new RTLActions.OpenAlert({ data: {
type: AlertTypeEnum.WARNING, type: AlertTypeEnum.WARNING,
alertTitle: 'Logging out', alertTitle: 'Logging out',

@ -30,11 +30,21 @@ import { LNDEffects } from './lnd/store/lnd.effects';
import { CLEffects } from './clightning/store/cl.effects'; import { CLEffects } from './clightning/store/cl.effects';
import { LayoutModule } from '@angular/cdk/layout'; import { LayoutModule } from '@angular/cdk/layout';
import { CLOpenChannelComponent } from './clightning/peers-channels/channels/open-channel-modal/open-channel.component'; import { CLOpenChannelComponent } from './clightning/peers-channels/channels/open-channel-modal/open-channel.component';
import { CLChannelInformationComponent } from './clightning/peers-channels/channels/channel-information-modal/channel-information.component';
import { CLInvoiceInformationComponent } from './clightning/transactions/invoice-information-modal/invoice-information.component'; import { CLInvoiceInformationComponent } from './clightning/transactions/invoice-information-modal/invoice-information.component';
import { CLConnectPeerComponent } from './clightning/peers-channels/connect-peer/connect-peer.component';
import { CLLightningSendPaymentsComponent } from './clightning/transactions/send-payment-modal/send-payment.component';
import { CLCreateInvoiceComponent } from './clightning/transactions/create-invoice-modal/create-invoice.component';
import { CLOnChainSendComponent } from './clightning/on-chain/on-chain-send-modal/on-chain-send.component';
import { InvoiceInformationComponent } from './lnd/transactions/invoice-information-modal/invoice-information.component'; import { InvoiceInformationComponent } from './lnd/transactions/invoice-information-modal/invoice-information.component';
import { ChannelRebalanceComponent } from './lnd/peers-channels/channels/channel-rebalance-modal/channel-rebalance.component'; import { ChannelRebalanceComponent } from './lnd/peers-channels/channels/channel-rebalance-modal/channel-rebalance.component';
import { CloseChannelComponent } from './lnd/peers-channels/channels/close-channel-modal/close-channel.component'; import { CloseChannelComponent } from './lnd/peers-channels/channels/close-channel-modal/close-channel.component';
import { OpenChannelComponent } from './lnd/peers-channels/channels/open-channel-modal/open-channel.component'; import { OpenChannelComponent } from './lnd/peers-channels/channels/open-channel-modal/open-channel.component';
import { ChannelInformationComponent } from './lnd/peers-channels/channels/channel-information-modal/channel-information.component';
import { OnChainSendComponent } from './lnd/on-chain/on-chain-send-modal/on-chain-send.component';
import { LightningSendPaymentsComponent } from './lnd/transactions/send-payment-modal/send-payment.component';
import { CreateInvoiceComponent } from './lnd/transactions/create-invoice-modal/create-invoice.component';
import { ConnectPeerComponent } from './lnd/peers-channels/connect-peer/connect-peer.component';
import { ShowPubkeyComponent } from './shared/components/data-modal/show-pubkey/show-pubkey.component'; import { ShowPubkeyComponent } from './shared/components/data-modal/show-pubkey/show-pubkey.component';
import { OnChainGeneratedAddressComponent } from './shared/components/data-modal/on-chain-generated-address/on-chain-generated-address.component'; import { OnChainGeneratedAddressComponent } from './shared/components/data-modal/on-chain-generated-address/on-chain-generated-address.component';
import { SpinnerDialogComponent } from './shared/components/data-modal/spinner-dialog/spinner-dialog.component'; import { SpinnerDialogComponent } from './shared/components/data-modal/spinner-dialog/spinner-dialog.component';
@ -65,7 +75,15 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
ChannelRebalanceComponent, ChannelRebalanceComponent,
OnChainGeneratedAddressComponent, OnChainGeneratedAddressComponent,
CLOpenChannelComponent, CLOpenChannelComponent,
CLConnectPeerComponent,
CLLightningSendPaymentsComponent,
CLCreateInvoiceComponent,
CLOnChainSendComponent,
CLChannelInformationComponent,
OpenChannelComponent, OpenChannelComponent,
ChannelInformationComponent,
LightningSendPaymentsComponent,
ConnectPeerComponent,
ShowPubkeyComponent, ShowPubkeyComponent,
SpinnerDialogComponent, SpinnerDialogComponent,
AlertMessageComponent, AlertMessageComponent,
@ -74,7 +92,9 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
CloseChannelComponent, CloseChannelComponent,
LoopModalComponent, LoopModalComponent,
TwoFactorAuthComponent, TwoFactorAuthComponent,
LoginTokenComponent LoginTokenComponent,
CreateInvoiceComponent,
OnChainSendComponent
], ],
entryComponents: [ entryComponents: [
CLInvoiceInformationComponent, CLInvoiceInformationComponent,
@ -82,7 +102,15 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
ChannelRebalanceComponent, ChannelRebalanceComponent,
OnChainGeneratedAddressComponent, OnChainGeneratedAddressComponent,
CLOpenChannelComponent, CLOpenChannelComponent,
CLConnectPeerComponent,
CLLightningSendPaymentsComponent,
CLCreateInvoiceComponent,
CLOnChainSendComponent,
CLChannelInformationComponent,
OpenChannelComponent, OpenChannelComponent,
ChannelInformationComponent,
LightningSendPaymentsComponent,
ConnectPeerComponent,
ShowPubkeyComponent, ShowPubkeyComponent,
SpinnerDialogComponent, SpinnerDialogComponent,
AlertMessageComponent, AlertMessageComponent,
@ -91,7 +119,9 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
CloseChannelComponent, CloseChannelComponent,
LoopModalComponent, LoopModalComponent,
TwoFactorAuthComponent, TwoFactorAuthComponent,
LoginTokenComponent LoginTokenComponent,
CreateInvoiceComponent,
OnChainSendComponent
], ],
providers: [ providers: [
{ provide: LoggerService, useClass: ConsoleLoggerService }, { provide: LoggerService, useClass: ConsoleLoggerService },

@ -10,11 +10,9 @@ import { CLPeersChannelsComponent } from './peers-channels/peers-channels.compon
import { CLChannelsTablesComponent } from './peers-channels/channels/channels-tables/channels-tables.component'; import { CLChannelsTablesComponent } from './peers-channels/channels/channels-tables/channels-tables.component';
import { CLPeersComponent } from './peers-channels/peers/peers.component'; import { CLPeersComponent } from './peers-channels/peers/peers.component';
import { CLLightningInvoicesComponent } from './transactions/invoices/lightning-invoices.component'; import { CLLightningInvoicesComponent } from './transactions/invoices/lightning-invoices.component';
import { CLOnChainSendComponent } from './on-chain/on-chain-send/on-chain-send.component';
import { CLOnChainReceiveComponent } from './on-chain/on-chain-receive/on-chain-receive.component'; import { CLOnChainReceiveComponent } from './on-chain/on-chain-receive/on-chain-receive.component';
import { CLOnChainComponent } from './on-chain/on-chain.component'; import { CLOnChainComponent } from './on-chain/on-chain.component';
import { CLLightningPaymentsComponent } from './transactions/payments/lightning-payments.component'; import { CLLightningPaymentsComponent } from './transactions/payments/lightning-payments.component';
import { CLChannelManageComponent } from './peers-channels/channels/channel-manage/channel-manage.component';
import { CLTransactionsComponent } from './transactions/transactions.component'; import { CLTransactionsComponent } from './transactions/transactions.component';
import { CLLookupsComponent } from './lookups/lookups.component'; import { CLLookupsComponent } from './lookups/lookups.component';
import { CLRoutingComponent } from './routing/routing.component'; import { CLRoutingComponent } from './routing/routing.component';
@ -52,7 +50,6 @@ import { CLUnlockedGuard } from '../shared/services/auth.guard';
CLPeersChannelsComponent, CLPeersChannelsComponent,
CLLightningInvoicesComponent, CLLightningInvoicesComponent,
CLLightningPaymentsComponent, CLLightningPaymentsComponent,
CLChannelManageComponent,
CLTransactionsComponent, CLTransactionsComponent,
CLLookupsComponent, CLLookupsComponent,
CLRoutingComponent, CLRoutingComponent,
@ -61,7 +58,6 @@ import { CLUnlockedGuard } from '../shared/services/auth.guard';
CLChannelLookupComponent, CLChannelLookupComponent,
CLNodeLookupComponent, CLNodeLookupComponent,
CLQueryRoutesComponent, CLQueryRoutesComponent,
CLOnChainSendComponent,
CLOnChainReceiveComponent, CLOnChainReceiveComponent,
CLOnChainComponent, CLOnChainComponent,
CLChannelsTablesComponent, CLChannelsTablesComponent,

@ -32,6 +32,6 @@
<ng-template #noChannelBlock> <ng-template #noChannelBlock>
<div fxLayout="row" fxFlex="10" fxLayoutAlign="space-between center" class="w-100 mt-1"> <div fxLayout="row" fxFlex="10" fxLayoutAlign="space-between center" class="w-100 mt-1">
No channels available. No channels available.
<button fxFlex="33" fxLayoutAlign="center center" mat-stroked-button color="primary" (click)="goToChannels()" tabindex="1">Open Channel</button> <button mat-stroked-button color="primary" (click)="goToChannels()" tabindex="1">Open Channel</button>
</div> </div>
</ng-template> </ng-template>

@ -22,6 +22,6 @@
<ng-template #noChannelBlock> <ng-template #noChannelBlock>
<div fxLayout="row" fxFlex="10" fxLayoutAlign="space-between center" class="w-100 mt-1"> <div fxLayout="row" fxFlex="10" fxLayoutAlign="space-between center" class="w-100 mt-1">
No channels available. No channels available.
<button *ngIf="direction === 'Out'" fxFlex="33" fxLayoutAlign="center center" mat-stroked-button color="primary" (click)="goToChannels()" tabindex="1">Open Channel</button> <button *ngIf="direction === 'Out'" mat-stroked-button color="primary" (click)="goToChannels()" tabindex="1">Open Channel</button>
</div> </div>
</ng-template> </ng-template>

@ -18,9 +18,9 @@
<input matInput name="lookupKey" [placeholder]="lookupFields[selectedFieldId]?.placeholder || 'Lookup Key'" (change)="clearLookupValue()" [(ngModel)]="lookupKey" tabindex="2" required #key> <input matInput name="lookupKey" [placeholder]="lookupFields[selectedFieldId]?.placeholder || 'Lookup Key'" (change)="clearLookupValue()" [(ngModel)]="lookupKey" tabindex="2" required #key>
<mat-error *ngIf="!lookupKey">{{lookupFields[selectedFieldId]?.placeholder}} is required.</mat-error> <mat-error *ngIf="!lookupKey">{{lookupFields[selectedFieldId]?.placeholder}} is required.</mat-error>
</mat-form-field> </mat-form-field>
<div fxFlex="30" fxLayoutAlign="space-between stretch" class="mt-2"> <div fxLayout="row" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="3" type="button" (click)="resetData()">Clear</button> <button class="mr-1" mat-stroked-button color="primary" tabindex="3" type="button" (click)="resetData()">Clear</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" tabindex="4" type="submit" (click)="onLookup()">Lookup</button> <button mat-flat-button color="primary" tabindex="4" type="submit" (click)="onLookup()">Lookup</button>
</div> </div>
</form> </form>
<div fxFlex="100" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch" *ngIf="flgSetLookupValue" class="w-100 mt-2"> <div fxFlex="100" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch" *ngIf="flgSetLookupValue" class="w-100 mt-2">

@ -7,8 +7,8 @@
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<div class="mt-2" fxFlex="48" fxFlex.gt-md="25"> <div class="mt-2">
<button fxFlex="100" fxLayoutAlign="center center" mat-flat-button color="primary" (click)="onGenerateAddress()" tabindex="2" class="top-minus-15px">Generate Address</button> <button mat-flat-button color="primary" (click)="onGenerateAddress()" tabindex="2" class="top-minus-15px">Generate Address</button>
</div> </div>
</div> </div>
</div> </div>

@ -0,0 +1,53 @@
<div fxLayout="row">
<div fxFlex="100" class="padding-gap-large">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="95" fxLayoutAlign="start start">
<span class="page-title">Send Payment</span>
</div>
<button tabindex="8" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" [mat-dialog-close]="false" default mat-button>X</button>
</mat-card-header>
<mat-card-content class="mt-5px">
<form fxLayout="row wrap" fxFlex="100" fxLayoutAlign="space-between start" class="padding-gap overflow-x-hidden" (submit)="onSendFunds()" (reset)="resetData()" #form="ngForm">
<mat-form-field fxFlex="55">
<input matInput autoFocus [(ngModel)]="transaction.address" placeholder="Bitcoin Address" tabindex="1" name="address" required #address="ngModel">
<mat-error *ngIf="!transaction.address">Bitcoin address is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="30">
<input matInput [(ngModel)]="transaction.satoshis" placeholder="Amount" name="amount" type="number" step="100" min="0" tabindex="2" required #amount="ngModel">
<span matSuffix> {{selAmountUnit}} </span>
<mat-error *ngIf="!transaction.satoshis">Amount is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="10" fxLayoutAlign="start end">
<mat-select [value]="selAmountUnit" tabindex="3" required name="amountUnit" (selectionChange)="onAmountUnitChange($event)">
<mat-option *ngFor="let amountUnit of amountUnits" [value]="amountUnit">{{amountUnit}}</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="60" fxLayoutAlign="space-between stretch" fxLayout="row wrap">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="6" placeholder="Fee Rate" [(value)]="transaction.feeRate" [disabled]="flgMinConf">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="2" tabindex="7" color="primary" [(ngModel)]="flgMinConf" (change)="transaction.feeRate=null" name="flgMinConf" fxLayoutAlign="stretch start" class="mr-2"></mat-checkbox>
<mat-form-field fxFlex="98">
<input matInput [(ngModel)]="transaction.minconf" placeholder="Min Confirmation Blocks" type="number" name="blocks" step="1" min="0" tabindex="8" #blocks="ngModel" [required]="flgMinConf" [disabled]="!flgMinConf">
<mat-error *ngIf="flgMinConf && !transaction.minconf">Min Confirmation Blocks is required.</mat-error>
</mat-form-field>
</div>
</div>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch"></div>
<div fxFlex="100" class="alert alert-danger mt-1" *ngIf="sendFundError !== ''">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span *ngIf="sendFundError !== ''">{{sendFundError}}</span>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="end center">
<button class="mr-1" mat-stroked-button color="primary" tabindex="7" type="reset">Clear Fields</button>
<button mat-flat-button color="primary" type="submit" tabindex="8">Send Funds</button>
</div>
</form>
</mat-card-content>
</div>
</div>

@ -1,8 +1,11 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { DecimalPipe } from '@angular/common'; import { DecimalPipe } from '@angular/common';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatDialogRef } from '@angular/material';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { SelNodeChild, GetInfoRoot } from '../../../shared/models/RTLconfig'; import { SelNodeChild, GetInfoRoot } from '../../../shared/models/RTLconfig';
import { GetInfoCL, BalanceCL, OnChainCL } from '../../../shared/models/clModels'; import { GetInfoCL, BalanceCL, OnChainCL } from '../../../shared/models/clModels';
@ -11,10 +14,8 @@ import { RTLConfiguration } from '../../../shared/models/RTLconfig';
import { CommonService } from '../../../shared/services/common.service'; import { CommonService } from '../../../shared/services/common.service';
import { LoggerService } from '../../../shared/services/logger.service'; import { LoggerService } from '../../../shared/services/logger.service';
import { RTLEffects } from '../../../store/rtl.effects';
import * as RTLActions from '../../../store/rtl.actions'; import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers'; import * as fromRTLReducer from '../../../store/rtl.reducers';
import { MessageDataField } from '../../../shared/models/alertData';
@Component({ @Component({
selector: 'rtl-cl-on-chain-send', selector: 'rtl-cl-on-chain-send',
@ -22,7 +23,8 @@ import { MessageDataField } from '../../../shared/models/alertData';
styleUrls: ['./on-chain-send.component.scss'] styleUrls: ['./on-chain-send.component.scss']
}) })
export class CLOnChainSendComponent implements OnInit, OnDestroy { export class CLOnChainSendComponent implements OnInit, OnDestroy {
@ViewChild('form', { static: false }) form: any; @ViewChild('form', { static: false }) form: any;
public faExclamationTriangle = faExclamationTriangle;
public selNode: SelNodeChild = {}; public selNode: SelNodeChild = {};
public appConfig: RTLConfiguration; public appConfig: RTLConfiguration;
public nodeData: GetInfoRoot; public nodeData: GetInfoRoot;
@ -35,6 +37,7 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
public transaction: OnChainCL = {}; public transaction: OnChainCL = {};
public feeRateTypes = FEE_RATE_TYPES; public feeRateTypes = FEE_RATE_TYPES;
public flgMinConf = false; public flgMinConf = false;
public sendFundError = '';
public fiatConversion = false; public fiatConversion = false;
public amountUnits = CURRENCY_UNITS; public amountUnits = CURRENCY_UNITS;
public selAmountUnit = CURRENCY_UNITS[0]; public selAmountUnit = CURRENCY_UNITS[0];
@ -43,7 +46,7 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
public currencyUnitFormats = CURRENCY_UNIT_FORMATS; public currencyUnitFormats = CURRENCY_UNIT_FORMATS;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject()]; private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private commonService: CommonService, private decimalPipe: DecimalPipe) {} constructor(public dialogRef: MatDialogRef<CLOnChainSendComponent>, private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private commonService: CommonService, private decimalPipe: DecimalPipe, private actions$: Actions) {}
ngOnInit() { ngOnInit() {
this.store.select('root') this.store.select('root')
@ -55,51 +58,35 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
this.nodeData = rootStore.nodeData; this.nodeData = rootStore.nodeData;
this.logger.info(rootStore); this.logger.info(rootStore);
}); });
this.actions$.pipe(takeUntil(this.unSubs[1]),
filter(action => action.type === RTLActions.EFFECT_ERROR_CL || action.type === RTLActions.SET_CHANNEL_TRANSACTION_RES_CL))
.subscribe((action: RTLActions.EffectErrorCl | RTLActions.SetChannelTransactionResCL) => {
if (action.type === RTLActions.SET_CHANNEL_TRANSACTION_RES_CL) {
this.store.dispatch(new RTLActions.OpenSnackBar('Fund Sent Successfully!'));
this.dialogRef.close();
}
if (action.type === RTLActions.EFFECT_ERROR_CL && action.payload.action === 'SetChannelTransactionCL') {
this.sendFundError = action.payload.message;
}
});
} }
onSendFunds() { onSendFunds() {
if(this.invalidValues) { return true; } if(this.invalidValues) { return true; }
this.sendFundError = '';
this.store.dispatch(new RTLActions.OpenSpinner('Sending Funds...'));
if(this.transaction.satoshis && this.selAmountUnit !== CurrencyUnitEnum.SATS) { if(this.transaction.satoshis && this.selAmountUnit !== CurrencyUnitEnum.SATS) {
this.commonService.convertCurrency(this.transaction.satoshis, this.selAmountUnit === this.amountUnits[2] ? CurrencyUnitEnum.OTHER : this.selAmountUnit, this.amountUnits[2], this.fiatConversion) this.commonService.convertCurrency(this.transaction.satoshis, this.selAmountUnit === this.amountUnits[2] ? CurrencyUnitEnum.OTHER : this.selAmountUnit, this.amountUnits[2], this.fiatConversion)
.pipe(takeUntil(this.unSubs[1])) .pipe(takeUntil(this.unSubs[2]))
.subscribe(data => { .subscribe(data => {
this.transaction.satoshis = parseInt(data[CurrencyUnitEnum.SATS]); this.transaction.satoshis = parseInt(data[CurrencyUnitEnum.SATS]);
this.selAmountUnit = CurrencyUnitEnum.SATS; this.selAmountUnit = CurrencyUnitEnum.SATS;
this.confirmSend(); this.store.dispatch(new RTLActions.SetChannelTransactionCL(this.transaction));
}); });
} else { } else {
this.confirmSend(); this.store.dispatch(new RTLActions.SetChannelTransactionCL(this.transaction));
} }
this.rtlEffects.closeConfirm
.pipe(takeUntil(this.unSubs[2]))
.subscribe(pwdConfirmRes => {
if (pwdConfirmRes) {
this.dispatchToSendFunds();
}
});
}
confirmSend() {
const confirmationMsg: Array<Array<MessageDataField>> = [
[{key: 'address', value: this.transaction.address, title: 'BTC Address', width: 100}],
[{key: 'satoshi', value: this.transaction.satoshis, title: 'Amount (Sats)', width: 100}],
[{key: 'feeRate', value: this.transaction.feeRate, title: 'Fee Rate', width: 100}],
[{key: 'minconf', value: this.transaction.minconf, title: 'Min Confirmation Blocks', width: 100}]
];
this.store.dispatch(new RTLActions.OpenConfirmation({ data: {
type: AlertTypeEnum.CONFIRM,
alertTitle: 'Confirm Payment',
message: confirmationMsg,
noBtnText: 'Cancel',
yesBtnText: 'Send'
}}));
}
dispatchToSendFunds() {
this.store.dispatch(new RTLActions.OpenSpinner('Sending Funds...'));
this.store.dispatch(new RTLActions.SetChannelTransactionCL(this.transaction));
this.transaction = {};
this.form.resetForm();
} }
get invalidValues(): boolean { get invalidValues(): boolean {
@ -109,6 +96,7 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
} }
resetData() { resetData() {
this.sendFundError = '';
this.transaction = {}; this.transaction = {};
this.flgMinConf = false; this.flgMinConf = false;
} }
@ -119,7 +107,7 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
let currSelectedUnit = event.value === this.amountUnits[2] ? CurrencyUnitEnum.OTHER : event.value; let currSelectedUnit = event.value === this.amountUnits[2] ? CurrencyUnitEnum.OTHER : event.value;
if(this.transaction.satoshis && this.selAmountUnit !== event.value) { if(this.transaction.satoshis && this.selAmountUnit !== event.value) {
this.commonService.convertCurrency(this.transaction.satoshis, prevSelectedUnit, this.amountUnits[2], this.fiatConversion) this.commonService.convertCurrency(this.transaction.satoshis, prevSelectedUnit, this.amountUnits[2], this.fiatConversion)
.pipe(takeUntil(this.unSubs[4])) .pipe(takeUntil(this.unSubs[3]))
.subscribe(data => { .subscribe(data => {
self.transaction.satoshis = +self.decimalPipe.transform(data[currSelectedUnit], self.currencyUnitFormats[currSelectedUnit]).replace(/,/g, ''); self.transaction.satoshis = +self.decimalPipe.transform(data[currSelectedUnit], self.currencyUnitFormats[currSelectedUnit]).replace(/,/g, '');
}); });

@ -1,37 +0,0 @@
<form fxLayout="column" fxFlex="98" fxLayout.gt-sm="row wrap" fxLayoutAlign="start stretch" fxLayoutAlign.gt-sm="space-between start" class="padding-gap overflow-x-hidden" (submit)="onSendFunds()" (reset)="resetData()" #form="ngForm">
<mat-form-field fxFlex.gt-sm="55">
<input matInput [(ngModel)]="transaction.address" placeholder="Bitcoin Address" tabindex="1" name="address" required #address="ngModel">
<mat-error *ngIf="!transaction.address">Bitcoin address is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex.gt-sm="30">
<input matInput [(ngModel)]="transaction.satoshis" placeholder="Amount" name="amount" type="number" step="100" min="0" tabindex="2" required #amount="ngModel">
<span matSuffix> {{selAmountUnit}} </span>
<mat-error *ngIf="!transaction.satoshis">Amount is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex.gt-sm="10" fxLayoutAlign="start end">
<mat-select [value]="selAmountUnit" tabindex="3" required name="amountUnit" (selectionChange)="onAmountUnitChange($event)">
<mat-option *ngFor="let amountUnit of amountUnits" [value]="amountUnit">{{amountUnit}}</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="60" fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="6" placeholder="Fee Rate" [(value)]="transaction.feeRate">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="2" tabindex="7" color="primary" [(ngModel)]="flgMinConf" name="flgMinConf" fxLayoutAlign="stretch start" class="mr-2"></mat-checkbox>
<mat-form-field fxFlex="98">
<input matInput [(ngModel)]="transaction.minconf" placeholder="Min Confirmation Blocks" type="number" name="blocks" step="1" min="0" tabindex="8" #blocks="ngModel" [required]="flgMinConf" [disabled]="!flgMinConf">
<mat-error *ngIf="flgMinConf && !transaction.minconf">Min Confirmation Blocks is required.</mat-error>
</mat-form-field>
</div>
</div>
<div fxLayout="column" fxFlex="100" fxFlex.gt-sm="40" fxLayout.gt-sm="row wrap" fxLayoutAlign="start stretch" fxLayoutAlign.gt-sm="space-between start"></div>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="7" type="reset">Clear Fields</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" type="submit" tabindex="8">Send Funds</button>
</div>
</form>

@ -17,12 +17,16 @@
<mat-card> <mat-card>
<mat-card-content fxLayout="column"> <mat-card-content fxLayout="column">
<mat-tab-group> <mat-tab-group>
<mat-tab label="Send">
<rtl-cl-on-chain-send></rtl-cl-on-chain-send>
</mat-tab>
<mat-tab label="Receive"> <mat-tab label="Receive">
<rtl-cl-on-chain-receive></rtl-cl-on-chain-receive> <rtl-cl-on-chain-receive></rtl-cl-on-chain-receive>
</mat-tab> </mat-tab>
<mat-tab label="Send">
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap">
<div fxLayout="row">
<button mat-flat-button color="primary" type="button" tabindex="1" (click)="openSendFundsModal()">Send Funds</button>
</div>
</div>
</mat-tab>
</mat-tab-group> </mat-tab-group>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>

@ -5,7 +5,9 @@ import { Store } from '@ngrx/store';
import { faExchangeAlt, faChartPie } from '@fortawesome/free-solid-svg-icons'; import { faExchangeAlt, faChartPie } from '@fortawesome/free-solid-svg-icons';
import { SelNodeChild } from '../../shared/models/RTLconfig'; import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers'; import * as fromRTLReducer from '../../store/rtl.reducers';
import { CLOnChainSendComponent } from './on-chain-send-modal/on-chain-send.component';
@Component({ @Component({
selector: 'rtl-cl-on-chain', selector: 'rtl-cl-on-chain',
@ -30,6 +32,12 @@ export class CLOnChainComponent implements OnInit, OnDestroy {
}); });
} }
openSendFundsModal() {
this.store.dispatch(new RTLActions.OpenAlert({ data: {
component: CLOnChainSendComponent
}}));
}
ngOnDestroy() { ngOnDestroy() {
this.unSubs.forEach(completeSub => { this.unSubs.forEach(completeSub => {
completeSub.next(); completeSub.next();

@ -0,0 +1,95 @@
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign="space-between stretch">
<div fxFlex="100" class="padding-gap-large pl-3">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header mb-1">
<div fxFlex="95" fxLayoutAlign="start start">
<fa-icon [icon]="faReceipt" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Channel Information</span>
</div>
<button tabindex="3" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" (click)="onClose()" mat-button>X</button>
</mat-card-header>
<mat-card-content [ngClass]="{'xs-scroll-y': screenSize === screenSizeEnum.XS}">
<div fxLayout="column">
<div fxLayout="row">
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Short Channel ID</h4>
<span class="foreground-secondary-text">{{channel.short_channel_id}}</span>
</div>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Peer Alias</h4>
<span class="foreground-secondary-text">{{channel.alias}}</span>
</div>
</div>
<mat-divider [inset]="true" class="my-1"></mat-divider>
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Channel ID</h4>
<span class="foreground-secondary-text">{{channel.channel_id}}</span>
</div>
</div>
<mat-divider [inset]="true" class="my-1"></mat-divider>
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Peer Public Key</h4>
<span class="foreground-secondary-text">{{channel.id}}</span>
</div>
</div>
<mat-divider [inset]="true" class="my-1"></mat-divider>
<div fxLayout="row">
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">mSatoshi to Us</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.msatoshi_to_us | number}}</span>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Spendable (mSats)</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.spendable_msatoshi | number}}</span>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Total (mSats)</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.msatoshi_total | number}}</span>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">State</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.state}}</span>
</div>
</div>
<mat-divider [inset]="true" class="my-1"></mat-divider>
<div fxLayout="row">
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Our Reserve (Sats)</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.our_channel_reserve_satoshis | number}}</span>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Their Reserve (Sats)</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.their_channel_reserve_satoshis | number}}</span>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Connected</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.connected ? 'Yes' : 'No'}}</span>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Private</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.private ? 'Yes' : 'No'}}</span>
</div>
</div>
<div *ngIf="showAdvanced">
<mat-divider [inset]="true" class="my-1"></mat-divider>
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Funding Transaction Id</h4>
<span class="foreground-secondary-text">{{channel.funding_txid}}</span>
</div>
</div>
<mat-divider [inset]="true" class="my-1"></mat-divider>
</div>
<div [ngClass]="{'mt-2': !showAdvanced, 'mt-1': showAdvanced}" fxLayout="row" fxLayoutAlign="end center" fxFlex="100">
<button mat-stroked-button color="primary" type="reset" (click)="onShowAdvanced()" tabindex="1" class="mr-1">
<p *ngIf="!showAdvanced; else hideAdvancedText">Show Advanced</p>
<ng-template #hideAdvancedText><p>Hide Advanced</p></ng-template>
</button>
<button *ngIf="showCopy" autoFocus mat-flat-button color="primary" tabindex="2" type="submit" rtlClipboard [payload]="channel.short_channel_id" (copied)="onCopyChanID($event)">Copy Short Channel ID</button>
<button *ngIf="!showCopy" autoFocus mat-flat-button color="primary" tabindex="2" type="button" (click)="onClose()">OK</button>
</div>
</div>
</mat-card-content>
</div>
</div>

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CLChannelInformationComponent } from './channel-information.component';
describe('CLChannelInformationComponent', () => {
let component: CLChannelInformationComponent;
let fixture: ComponentFixture<CLChannelInformationComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CLChannelInformationComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CLChannelInformationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,46 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { faReceipt } from '@fortawesome/free-solid-svg-icons';
import { MatSnackBar } from '@angular/material/snack-bar';
import { LoggerService } from '../../../../shared/services/logger.service';
import { CommonService } from '../../../../shared/services/common.service';
import { CLChannelInformation } from '../../../../shared/models/alertData';
import { ChannelCL } from '../../../../shared/models/clModels';
import { ScreenSizeEnum } from '../../../../shared/services/consts-enums-functions';
@Component({
selector: 'rtl-cl-channel-information',
templateUrl: './channel-information.component.html',
styleUrls: ['./channel-information.component.scss']
})
export class CLChannelInformationComponent implements OnInit {
public faReceipt = faReceipt;
public showAdvanced = false;
public showCopy = true;
public showCopyField = null;
public channel: ChannelCL;
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
constructor(public dialogRef: MatDialogRef<CLChannelInformationComponent>, @Inject(MAT_DIALOG_DATA) public data: CLChannelInformation, private logger: LoggerService, private commonService: CommonService, private snackBar: MatSnackBar) { }
ngOnInit() {
this.channel = this.data.channel;
this.showCopy = this.data.showCopy;
this.screenSize = this.commonService.getScreenSize();
}
onClose() {
this.dialogRef.close(false);
}
onShowAdvanced() {
this.showAdvanced = !this.showAdvanced;
}
onCopyChanID(payload: string) {
this.snackBar.open('Short channel ID ' + payload + ' copied.');
this.logger.info('Copied Text: ' + payload);
}
}

@ -1,54 +0,0 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap">
<form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" #form="ngForm">
<mat-form-field fxFlex="40" fxFlex.gt-sm="30" fxLayoutAlign="start end">
<mat-select [(ngModel)]="selectedPeer" placeholder="Alias" name="peer_alias" tabindex="1" required name="selPeer" #selPeer="ngModel">
<mat-option (click)="addNewPeer()" [value]="'new'">
ADD PEER
</mat-option>
<mat-option *ngFor="let peer of peers" [value]="peer.id">
{{peer.alias}}
</mat-option>
</mat-select>
<mat-error *ngIf="!selectedPeer">Alias is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="25" fxFlex.gt-sm="30" fxLayoutAlign="start end">
<input matInput [(ngModel)]="fundingAmount" placeholder="Amount" type="number" step="1000" min="1" tabindex="2" required name="amount" #amount="ngModel" max="{{totalBalance}}">
<mat-hint>(Wallet Bal: {{totalBalance}}, Remaining Bal: {{totalBalance - ((fundingAmount) ? fundingAmount : 0)}})</mat-hint>
<span matSuffix> {{information?.smaller_currency_unit | titlecase}} </span>
<mat-error *ngIf="!fundingAmount">Amount is required.</mat-error>
<mat-error *ngIf="amount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error>
</mat-form-field>
<div fxFlex="15" fxFlex.gt-sm="20" fxLayoutAlign="start center" [ngClass]="{'mt-2': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">
<mat-slide-toggle tabindex="3" color="primary" [(ngModel)]="isPrivate" name="isPrivate">Private Channel</mat-slide-toggle>
</div>
<div fxFlex="100" fxFlex.gt-sm="17" fxLayoutAlign="start center" [ngClass]="{'mt-2': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">
<button fxFlex="48" fxFlex.gt-sm="100" fxLayoutAlign="center center" mat-stroked-button color="primary" type="button" (click)="onShowAdvanced()" tabindex="4">
<p *ngIf="!showAdvanced; else hideAdvancedText">Show Advanced</p>
<ng-template #hideAdvancedText><p>Hide Advanced</p></ng-template>
</button>
</div>
<div *ngIf="showAdvanced" fxFlex="60" fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="4" placeholder="Fee Rate" [(value)]="selFeeRate">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="2" tabindex="5" color="primary" [(ngModel)]="flgMinConf" name="flgMinConf" fxLayoutAlign="stretch start" class="mr-2"></mat-checkbox>
<mat-form-field fxFlex="98">
<input matInput [(ngModel)]="minConfValue" placeholder="Min Confirmation Blocks" type="number" name="blocks" step="1" min="0" tabindex="8" #blocks="ngModel" [required]="flgMinConf" [disabled]="!flgMinConf">
<mat-error *ngIf="flgMinConf && !minConfValue">Min Confirmation Blocks is required.</mat-error>
</mat-form-field>
</div>
</div>
<div *ngIf="showAdvanced" fxLayout="column" fxFlex="100" fxFlex.gt-sm="40" fxLayout.gt-sm="row wrap" fxLayoutAlign="start stretch" fxLayoutAlign.gt-sm="space-between start"></div>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="10" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" (click)="onOpenChannel()" type="submit" tabindex="11">Open Channel</button>
</div>
</form>
<rtl-cl-channels-tables fxLayout="row" fxFlex="100"></rtl-cl-channels-tables>
</div>

@ -1,67 +0,0 @@
.mat-column-close, .mat-column-update, .mat-column-active, .mat-column-private {
flex: 0 0 6%;
min-width: 60px;
}
.mat-column-private {
padding-left: 10px;
}
.mat-cell.mat-column-close, .mat-column-update {
cursor: pointer;
}
.mat-column-chan_id {
flex: 0 0 16%;
min-width: 160px;
}
.mat-checkbox-inner-container:focus, .mat-checkbox-input:focus {
outline: none !important;
}
.size-40 {
font-size: 40px;
margin-left: -30%;
}
.mat-button-text {
font-size: 24px;
padding-bottom: 20px;
}
.flex-ellipsis {
padding-right: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
table {
width:100%;
}
.chkbox-options:focus {
outline: none !important;
}
.flip {
position: relative;
backface-visibility: hidden;
animation: spin 1.5s cubic-bezier(.175, .885, .32, 1.275) 2;
transform-style: preserve-3d;
transform: rotateX(0deg);
}
@-moz-keyframes spin {
from { -moz-transform: rotateX(0deg); }
to { -moz-transform: rotateX(360deg); }
}
@-webkit-keyframes spin {
from { -webkit-transform: rotateX(0deg); }
to { -webkit-transform: rotateX(360deg); }
}
@keyframes spin {
from { transform:rotateX(0deg); }
to { transform:rotateX(360deg); }
}

@ -1,143 +0,0 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Subject } from 'rxjs';
import { take, takeUntil, filter } from 'rxjs/operators';
import { Actions } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { MatSort, MatSnackBar } from '@angular/material';
import { PeerCL, GetInfoCL } from '../../../../shared/models/clModels';
import { ScreenSizeEnum, AlertTypeEnum, DataTypeEnum, FEE_RATE_TYPES } from '../../../../shared/services/consts-enums-functions';
import { LoggerService } from '../../../../shared/services/logger.service';
import { CommonService } from '../../../../shared/services/common.service';
import { RTLEffects } from '../../../../store/rtl.effects';
import { CLEffects } from '../../../store/cl.effects';
import * as RTLActions from '../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
@Component({
selector: 'rtl-cl-channel-manage',
templateUrl: './channel-manage.component.html',
styleUrls: ['./channel-manage.component.scss']
})
export class CLChannelManageComponent implements OnInit, OnDestroy {
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild('form', {static: true}) form: any;
public totalBalance = 0;
public selectedPeer = '';
public fundingAmount: number;
public peers: PeerCL[] = [];
public information: GetInfoCL = {};
public myChanPolicy: any = {};
public isPrivate = false;
public feeRateTypes = FEE_RATE_TYPES;
public showAdvanced = false;
public peerAddress = '';
public newlyAddedPeer = '';
public selFeeRate = null;
public flgMinConf = false;
public minConfValue = null;
public moreOptions = false;
public screenSizeEnum = ScreenSizeEnum;
public screenSize = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private lndEffects: CLEffects, private commonService: CommonService, private actions$: Actions, private snackBar: MatSnackBar) {
this.screenSize = this.commonService.getScreenSize();
}
ngOnInit() {
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
this.information = rtlStore.information;
this.peers = rtlStore.peers;
this.peers.forEach(peer => {
if (!peer.alias || peer.alias === '') {
peer.alias = peer.id.substring(0, 15) + '...';
}
});
this.totalBalance = rtlStore.balance.totalBalance;
this.logger.info(rtlStore);
});
this.actions$.pipe(takeUntil(this.unSubs[1]),
filter((action) => action.type === RTLActions.SET_PEERS_CL || action.type === RTLActions.FETCH_CHANNELS_CL))
.subscribe((action: RTLActions.SetPeersCL | RTLActions.FetchChannelsCL) => {
if(action.type === RTLActions.SET_PEERS_CL) {
if(this.newlyAddedPeer !== '') {
this.snackBar.open('Peer added successfully. Proceed to open the channel.');
this.selectedPeer = this.newlyAddedPeer;
this.newlyAddedPeer = '';
}
}
if(action.type === RTLActions.FETCH_CHANNELS_CL) {
this.form.resetForm();
}
});
}
onOpenChannel() {
if (!this.selectedPeer || this.selectedPeer === '' || !this.fundingAmount || (this.totalBalance - ((this.fundingAmount) ? this.fundingAmount : 0) < 0)) { return true; }
this.store.dispatch(new RTLActions.OpenSpinner('Opening Channel...'));
this.store.dispatch(new RTLActions.SaveNewChannelCL({
peerId: this.selectedPeer, satoshis: this.fundingAmount, announce: !this.isPrivate, feeRate: this.selFeeRate, minconf: this.flgMinConf ? this.minConfValue : null
}));
}
resetData() {
this.selectedPeer = '';
this.fundingAmount = 0;
this.moreOptions = false;
this.flgMinConf = false;
this.isPrivate = false;
this.selFeeRate = null;
this.minConfValue = null;
this.showAdvanced = false;
this.form.resetForm();
}
onShowAdvanced() {
this.showAdvanced = !this.showAdvanced;
if (!this.showAdvanced) {
this.flgMinConf = false;
this.selFeeRate = null;
this.minConfValue = null;
}
}
addNewPeer() {
this.store.dispatch(new RTLActions.OpenConfirmation({ data: {
type: AlertTypeEnum.CONFIRM,
alertTitle: 'Add peer',
message: '',
noBtnText: 'Do it Later',
yesBtnText: 'Add Peer',
flgShowInput: true,
titleMessage: 'Enter Lightning Address',
getInputs: [
{placeholder: 'Lightning Address (pubkey OR pubkey@ip:port)', inputType: DataTypeEnum.STRING, inputValue: '', width: 100}
]
}}));
this.rtlEffects.closeConfirm
.pipe(take(1))
.subscribe(confirmRes => {
if (confirmRes) {
this.peerAddress = confirmRes[0].inputValue;
const deviderIndex = this.peerAddress.search('@');
this.newlyAddedPeer = (deviderIndex<0) ? 'none' : this.peerAddress.substring(0, deviderIndex);
this.store.dispatch(new RTLActions.OpenSpinner('Adding Peer...'));
this.store.dispatch(new RTLActions.SaveNewPeerCL({id: this.peerAddress, showOpenChannelModal: false}));
} else {
this.selectedPeer = '';
}
});
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -81,7 +81,7 @@
</ng-container> </ng-container>
<ng-container matColumnDef="no_peer"> <ng-container matColumnDef="no_peer">
<td mat-footer-cell *matFooterCellDef colspan="4"> <td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="numPeers<1">No peers connected. Add a peer in order to open a channel.</p> <p *ngIf="numPeers<1 && (!channels.data || channels.data.length<1)">No peers connected. Add a peer in order to open a channel.</p>
<p *ngIf="numPeers>0 && (!channels.data || channels.data.length<1)">No channels available.</p> <p *ngIf="numPeers>0 && (!channels.data || channels.data.length<1)">No channels available.</p>
</td> </td>
</ng-container> </ng-container>

@ -9,6 +9,7 @@ import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTyp
import { LoggerService } from '../../../../../shared/services/logger.service'; import { LoggerService } from '../../../../../shared/services/logger.service';
import { CommonService } from '../../../../../shared/services/common.service'; import { CommonService } from '../../../../../shared/services/common.service';
import { CLChannelInformationComponent } from '../../channel-information-modal/channel-information.component';
import { CLEffects } from '../../../../store/cl.effects'; import { CLEffects } from '../../../../store/cl.effects';
import { RTLEffects } from '../../../../../store/rtl.effects'; import { RTLEffects } from '../../../../../store/rtl.effects';
import * as RTLActions from '../../../../../store/rtl.actions'; import * as RTLActions from '../../../../../store/rtl.actions';
@ -206,29 +207,12 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
} }
onChannelClick(selChannel: ChannelCL, event: any) { onChannelClick(selChannel: ChannelCL, event: any) {
const reorderedChannel = [ this.store.dispatch(new RTLActions.OpenAlert({ data: {
[{key: 'channel_id', value: selChannel.channel_id, title: 'Channel ID', width: 100, type: DataTypeEnum.STRING}], channel: selChannel,
[{key: 'id', value: selChannel.id, title: 'Peer Public Key', width: 100, type: DataTypeEnum.STRING}], showCopy: true,
[{key: 'funding_txid', value: selChannel.funding_txid, title: 'Funding Transaction Id', width: 100, type: DataTypeEnum.STRING}], component: CLChannelInformationComponent
[{key: 'short_channel_id', value: selChannel.short_channel_id, title: 'Short Channel ID', width: 34, type: DataTypeEnum.STRING}, }}));
{key: 'alias', value: selChannel.alias, title: 'Peer Alias', width: 66, type: DataTypeEnum.STRING}], }
[{key: 'connected', value: selChannel.connected, title: 'Connected', width: 34, type: DataTypeEnum.BOOLEAN},
{key: 'private', value: selChannel.private, title: 'Private', width: 33, type: DataTypeEnum.BOOLEAN},
{key: 'state', value: selChannel.state, title: 'State', width: 33, type: DataTypeEnum.STRING}],
[{key: 'our_channel_reserve_satoshis', value: selChannel.our_channel_reserve_satoshis, title: 'Our Channel Reserve (Sats)', width: 34, type: DataTypeEnum.NUMBER},
{key: 'their_channel_reserve_satoshis', value: selChannel.their_channel_reserve_satoshis, title: 'Their Channel Reserve (Sats)', width: 33, type: DataTypeEnum.NUMBER},
{key: 'msatoshi_to_us', value: selChannel.msatoshi_to_us, title: 'mSatoshi to Us', width: 33, type: DataTypeEnum.NUMBER}],
[{key: 'spendable_msatoshi', value: selChannel.spendable_msatoshi, title: 'Spendable (mSats)', width: 34, type: DataTypeEnum.NUMBER},
{key: 'msatoshi_total', value: selChannel.msatoshi_total, title: 'Total (mSats)', width: 66, type: DataTypeEnum.NUMBER}]
];
this.store.dispatch(new RTLActions.OpenAlert({ data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Channel Information',
showCopyName: 'Short Channel ID',
showCopyField: selChannel.short_channel_id,
message: reorderedChannel
}}));
}
loadChannelsTable(mychannels) { loadChannelsTable(mychannels) {
mychannels.sort(function(a, b) { mychannels.sort(function(a, b) {

@ -58,7 +58,7 @@
</ng-container> </ng-container>
<ng-container matColumnDef="no_peer"> <ng-container matColumnDef="no_peer">
<td mat-footer-cell *matFooterCellDef colspan="4"> <td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="numPeers<1">No peers connected. Add a peer in order to open a channel.</p> <p *ngIf="numPeers<1 && (!channels.data || channels.data.length<1)">No peers connected. Add a peer in order to open a channel.</p>
<p *ngIf="numPeers>0 && (!channels.data || channels.data.length<1)">No channels available.</p> <p *ngIf="numPeers>0 && (!channels.data || channels.data.length<1)">No channels available.</p>
</td> </td>
</ng-container> </ng-container>

@ -9,6 +9,7 @@ import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTyp
import { LoggerService } from '../../../../../shared/services/logger.service'; import { LoggerService } from '../../../../../shared/services/logger.service';
import { CommonService } from '../../../../../shared/services/common.service'; import { CommonService } from '../../../../../shared/services/common.service';
import { CLChannelInformationComponent } from '../../channel-information-modal/channel-information.component';
import { CLEffects } from '../../../../store/cl.effects'; import { CLEffects } from '../../../../store/cl.effects';
import { RTLEffects } from '../../../../../store/rtl.effects'; import { RTLEffects } from '../../../../../store/rtl.effects';
import * as RTLActions from '../../../../../store/rtl.actions'; import * as RTLActions from '../../../../../store/rtl.actions';
@ -87,27 +88,10 @@ export class CLChannelPendingTableComponent implements OnInit, OnDestroy {
} }
onChannelClick(selChannel: ChannelCL, event: any) { onChannelClick(selChannel: ChannelCL, event: any) {
const reorderedChannel = [ this.store.dispatch(new RTLActions.OpenAlert({ data: {
[{key: 'channel_id', value: selChannel.channel_id, title: 'Channel ID', width: 100, type: DataTypeEnum.STRING}], channel: selChannel,
[{key: 'id', value: selChannel.id, title: 'Peer Public Key', width: 100, type: DataTypeEnum.STRING}], showCopy: true,
[{key: 'funding_txid', value: selChannel.funding_txid, title: 'Funding Transaction Id', width: 100, type: DataTypeEnum.STRING}], component: CLChannelInformationComponent
[{key: 'short_channel_id', value: selChannel.short_channel_id, title: 'Short Channel ID', width: 34, type: DataTypeEnum.STRING},
{key: 'alias', value: selChannel.alias, title: 'Peer Alias', width: 66, type: DataTypeEnum.STRING}],
[{key: 'connected', value: selChannel.connected, title: 'Connected', width: 34, type: DataTypeEnum.BOOLEAN},
{key: 'private', value: selChannel.private, title: 'Private', width: 33, type: DataTypeEnum.BOOLEAN},
{key: 'state', value: selChannel.state, title: 'State', width: 33, type: DataTypeEnum.STRING}],
[{key: 'our_channel_reserve_satoshis', value: selChannel.our_channel_reserve_satoshis, title: 'Our Channel Reserve (Sats)', width: 34, type: DataTypeEnum.NUMBER},
{key: 'their_channel_reserve_satoshis', value: selChannel.their_channel_reserve_satoshis, title: 'Their Channel Reserve (Sats)', width: 33, type: DataTypeEnum.NUMBER},
{key: 'msatoshi_to_us', value: selChannel.msatoshi_to_us, title: 'mSatoshi to Us', width: 33, type: DataTypeEnum.NUMBER}],
[{key: 'spendable_msatoshi', value: selChannel.spendable_msatoshi, title: 'Spendable (mSats)', width: 34, type: DataTypeEnum.NUMBER},
{key: 'msatoshi_total', value: selChannel.msatoshi_total, title: 'Total (mSats)', width: 66, type: DataTypeEnum.NUMBER}]
];
this.store.dispatch(new RTLActions.OpenAlert({ data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Channel Information',
showCopyName: 'Channel ID',
showCopyField: selChannel.channel_id,
message: reorderedChannel
}})); }}));
} }

@ -7,85 +7,98 @@
<button tabindex="8" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" (click)="onClose()" mat-button>X</button> <button tabindex="8" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" (click)="onClose()" mat-button>X</button>
</mat-card-header> </mat-card-header>
<mat-card-content class="mt-5px"> <mat-card-content class="mt-5px">
<form fxLayout="column" (ngSubmit)="openChannelForm.form.valid && onOpenChannel()" #openChannelForm="ngForm"> <form fxLayout="column" (submit)="onOpenChannel()" (reset)="resetData()" #form="ngForm">
<mat-expansion-panel class="flat-expansion-panel"> <div fxLayout="column">
<mat-expansion-panel-header> <mat-form-field fxFlex="100" *ngIf="!peer && peers && peers.length > 0">
<mat-panel-title> <input type="text" placeholder="Peer Alias" aria-label="Peers" matInput [formControl]="selectedPeer" (change)="onSelectedPeerChanged()" [matAutocomplete]="auto" tabindex="1" required>
<span>{{newlyAdded ? '' : 'Open channel with'}}&nbsp;</span><strong class="font-weight-900">{{peer.alias || peer.id}}</strong>&nbsp;{{newlyAdded ? 'added as a peer.' : '.'}} <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn" (optionSelected)="onSelectedPeerChanged()">
</mat-panel-title> <mat-option *ngFor="let peer of filteredPeers | async" [value]="peer">{{peer.alias ? peer.alias : peer.id ? peer.id : ''}}</mat-option>
</mat-expansion-panel-header> </mat-autocomplete>
<div fxLayout="column"> <mat-error *ngIf="selectedPeer.errors?.required">Peer alias is required.</mat-error>
<div fxLayout="row"> <mat-error *ngIf="selectedPeer.errors?.notfound">Peer not found in the list.</mat-error>
<div fxFlex="100"> </mat-form-field>
<h4 fxLayoutAlign="start" class="font-bold-500">Pubkey</h4> </div>
<span class="foreground-secondary-text">{{peer.id}}</span> <ng-container *ngTemplateOutlet="peerDetailsExpansionBlock"></ng-container>
</div> <ng-container *ngTemplateOutlet="openChannelBlock"></ng-container>
</div> <div fxFlex="100" class="alert alert-danger mt-1" *ngIf="channelConnectionError !== ''">
<mat-divider class="w-100 my-1"></mat-divider> <fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<div fxLayout="row"> <span *ngIf="channelConnectionError !== ''">{{channelConnectionError}}</span>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Address</h4>
<span class="overflow-wrap foreground-secondary-text">{{peer.netaddr}}</span>
</div>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Connected</h4>
<span class="overflow-wrap foreground-secondary-text">{{peer.connected ? 'True' : 'False'}}</span>
</div>
</div>
</div>
</mat-expansion-panel>
<div fxLayout="column" class="bordered-box mt-2 open-inputs-box">
<div fxLayout="row" fxFlex="100">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="100" fxLayoutAlign="start start">
<span class="page-title">Open Channel</span>
</div>
</mat-card-header>
</div>
<div fxLayout="row" fxFlex="100">
Available balance: {{totalBalance | number}} {{information.smaller_currency_unit}}
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center" class="mt-1">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<input matInput [(ngModel)]="fundingAmount" placeholder="Amount" type="number" step="1000" min="1" tabindex="2" required name="amount" #amount="ngModel" max="{{totalBalance}}">
<mat-hint>(Wallet Bal: {{totalBalance}}, Remaining Bal: {{totalBalance - ((fundingAmount) ? fundingAmount : 0)}})</mat-hint>
<span matSuffix> {{information?.smaller_currency_unit | titlecase}} </span>
<mat-error *ngIf="!fundingAmount">Amount is required.</mat-error>
<mat-error *ngIf="amount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error>
</mat-form-field>
<div fxFlex="48" fxLayoutAlign="start center">
<mat-slide-toggle tabindex="3" color="primary" [(ngModel)]="isPrivate" name="isPrivate">Private Channel</mat-slide-toggle>
</div>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="4" placeholder="Fee Rate" [(value)]="selFeeRate">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="2" tabindex="5" color="primary" [(ngModel)]="flgMinConf" name="flgMinConf" fxLayoutAlign="stretch start" class="mr-2"></mat-checkbox>
<mat-form-field fxFlex="98">
<input matInput [(ngModel)]="minConfValue" placeholder="Min Confirmation Blocks" type="number" name="blocks" step="1" min="0" tabindex="8" #blocks="ngModel" [required]="flgMinConf" [disabled]="!flgMinConf">
<mat-error *ngIf="flgMinConf && !minConfValue">Min Confirmation Blocks is required.</mat-error>
</mat-form-field>
</div>
</div>
</div> </div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="end center"> <div class="mt-2" fxLayout="row" fxLayoutAlign="end center">
<div *ngIf="newlyAdded" fxLayoutAlign="space-between center" fxFlex="60"> <button mat-stroked-button color="primary" class="mr-1" tabindex="7" type="reset">Clear Fields</button>
<button fxFlex="33" fxLayoutAlign="center center" mat-stroked-button color="warn" (click)="onClose()" tabindex="7">Do It Later</button> <button autoFocus mat-flat-button color="primary" type="submit" tabindex="9">Open Channel</button>
<button fxFlex="32" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="8" type="reset" (click)="resetData()">Clear Field</button>
<button autoFocus fxFlex="33" fxLayoutAlign="center center" mat-flat-button color="primary" type="submit" tabindex="9">Open Channel</button>
</div>
<div *ngIf="!newlyAdded" fxLayout="row" fxLayoutAlign="space-between stretch" fxFlex="30" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="7" type="reset" (click)="resetData()">Clear Field</button>
<button autoFocus fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" type="submit" tabindex="8">Open Channel</button>
</div>
</div> </div>
</form> </form>
</mat-card-content> </mat-card-content>
</div> </div>
</div> </div>
<ng-template #peerDetailsExpansionBlock>
<mat-expansion-panel class="flat-expansion-panel my-1" *ngIf="peer" expanded="false">
<mat-expansion-panel-header>
<mat-panel-title>
<span>Peer: &nbsp;</span><strong class="font-weight-900">{{peer?.alias || peer?.id}}</strong>
</mat-panel-title>
</mat-expansion-panel-header>
<div fxLayout="column">
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Pubkey</h4>
<span class="foreground-secondary-text">{{peer.id}}</span>
</div>
</div>
<mat-divider class="w-100 my-1"></mat-divider>
<div fxLayout="row">
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Address</h4>
<span class="overflow-wrap foreground-secondary-text">{{peer?.netaddr}}</span>
</div>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Connected</h4>
<span class="overflow-wrap foreground-secondary-text">{{peer.connected ? 'True' : 'False'}}</span>
</div>
</div>
</div>
</mat-expansion-panel>
</ng-template>
<ng-template #openChannelBlock>
<form fxLayout="column" #form="ngForm">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<mat-form-field fxFlex="70" fxLayoutAlign="start end">
<input matInput [(ngModel)]="fundingAmount" placeholder="Amount" type="number" step="1000" min="1" max="{{totalBalance}}" tabindex="1" required name="amount" #amount="ngModel">
<mat-hint>Remaining Bal: {{totalBalance - ((fundingAmount) ? fundingAmount : 0) | number}}</mat-hint>
<span matSuffix> {{information?.smaller_currency_unit}} </span>
<mat-error *ngIf="amount.errors?.required">Amount is required.</mat-error>
<mat-error *ngIf="amount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error>
</mat-form-field>
<div fxFlex="25" fxLayoutAlign="start center">
<mat-slide-toggle tabindex="2" color="primary" [(ngModel)]="isPrivate" name="isPrivate">Private Channel</mat-slide-toggle>
</div>
</div>
<mat-expansion-panel class="flat-expansion-panel mt-2" expanded="false" (closed)="onAdvancedPanelToggle(true)" (opened)="onAdvancedPanelToggle(false)">
<mat-expansion-panel-header>
<mat-panel-title>
<span>{{advancedTitle}}</span>
</mat-panel-title>
</mat-expansion-panel-header>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="4" placeholder="Fee Rate" [(value)]="selFeeRate" [disabled]="flgMinConf">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="2" tabindex="5" color="primary" [(ngModel)]="flgMinConf" (change)="selFeeRate=null;" name="flgMinConf" fxLayoutAlign="stretch start" class="mr-2"></mat-checkbox>
<mat-form-field fxFlex="98">
<input matInput [(ngModel)]="minConfValue" placeholder="Min Confirmation Blocks" type="number" name="blocks" step="1" min="0" tabindex="8" #blocks="ngModel" [required]="flgMinConf" [disabled]="!flgMinConf">
<mat-error *ngIf="flgMinConf && !minConfValue">Min Confirmation Blocks is required.</mat-error>
</mat-form-field>
</div>
</div>
</div>
</mat-expansion-panel>
</form>
</ng-template>

@ -1,6 +1,11 @@
import { Component, OnInit, Inject } from '@angular/core'; import { Component, OnInit, Inject, OnDestroy, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { Subject, Observable, of } from 'rxjs';
import { takeUntil, filter, startWith, map } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { PeerCL, GetInfoCL } from '../../../../shared/models/clModels'; import { PeerCL, GetInfoCL } from '../../../../shared/models/clModels';
import { CLOpenChannelAlert } from '../../../../shared/models/alertData'; import { CLOpenChannelAlert } from '../../../../shared/models/alertData';
@ -14,29 +19,78 @@ import * as fromRTLReducer from '../../../../store/rtl.reducers';
templateUrl: './open-channel.component.html', templateUrl: './open-channel.component.html',
styleUrls: ['./open-channel.component.scss'] styleUrls: ['./open-channel.component.scss']
}) })
export class CLOpenChannelComponent implements OnInit { export class CLOpenChannelComponent implements OnInit, OnDestroy {
@ViewChild('form', { static: false }) form: any;
public selectedPeer = new FormControl();
public faExclamationTriangle = faExclamationTriangle;
public alertTitle: string; public alertTitle: string;
public peer: PeerCL; public peer: PeerCL;
public peers: PeerCL[];
public sortedPeers: PeerCL[];
public filteredPeers: Observable<PeerCL[]>;
public channelConnectionError = '';
public advancedTitle = 'Advanced Options';
public information: GetInfoCL; public information: GetInfoCL;
public totalBalance = 0;
public fundingAmount: number; public fundingAmount: number;
public myChanPolicy: any = {}; public selectedPubkey = '';
public isPrivate = false; public isPrivate = false;
public feeRateTypes = FEE_RATE_TYPES; public feeRateTypes = FEE_RATE_TYPES;
public totalBalance = 0;
public newlyAdded = false;
public selFeeRate = ''; public selFeeRate = '';
public flgMinConf = false; public flgMinConf = false;
public minConfValue = null; public minConfValue = null;
public moreOptions = false; private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(public dialogRef: MatDialogRef<CLOpenChannelComponent>, @Inject(MAT_DIALOG_DATA) public data: CLOpenChannelAlert, private store: Store<fromRTLReducer.RTLState>) {} constructor(public dialogRef: MatDialogRef<CLOpenChannelComponent>, @Inject(MAT_DIALOG_DATA) public data: CLOpenChannelAlert, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions) {}
ngOnInit() { ngOnInit() {
this.peer = this.data.message.peer;
this.information = this.data.message.information; this.information = this.data.message.information;
this.totalBalance = this.data.message.balance; this.totalBalance = this.data.message.balance;
this.newlyAdded = this.data.newlyAdded;
this.alertTitle = this.data.alertTitle; this.alertTitle = this.data.alertTitle;
this.peer = this.data.message.peer ? this.data.message.peer : null;
this.peers = this.data.message.peers && this.data.message.peers.length ? this.data.message.peers : [];
this.actions$.pipe(takeUntil(this.unSubs[0]),
filter(action => action.type === RTLActions.EFFECT_ERROR_CL || action.type === RTLActions.FETCH_CHANNELS_CL))
.subscribe((action: RTLActions.EffectErrorCl | RTLActions.FetchChannelsCL) => {
if (action.type === RTLActions.EFFECT_ERROR_CL && action.payload.action === 'SaveNewChannelCL') {
this.channelConnectionError = action.payload.message;
}
if (action.type === RTLActions.FETCH_CHANNELS_CL) {
this.dialogRef.close();
}
});
let x = '', y = '';
this.sortedPeers = this.peers.sort((p1, p2) => {
x = p1.alias ? p1.alias.toLowerCase() : p1.id ? p1.id.toLowerCase() : '';
y = p2.alias ? p2.alias.toLowerCase() : p1.id.toLowerCase();
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
this.filteredPeers = this.selectedPeer.valueChanges.pipe(takeUntil(this.unSubs[1]), startWith(''),
map(peer => typeof peer === 'string' ? peer : peer.alias ? peer.alias : peer.id),
map(alias => alias ? this.filterPeers(alias) : this.sortedPeers.slice())
);
}
private filterPeers(newlySelectedPeer: string): PeerCL[] {
return this.sortedPeers.filter(peer => peer.alias.toLowerCase().indexOf(newlySelectedPeer ? newlySelectedPeer.toLowerCase() : '') === 0);
}
displayFn(peer: PeerCL): string {
return (peer && peer.alias) ? peer.alias : (peer && peer.id) ? peer.id : '';
}
onSelectedPeerChanged() {
this.channelConnectionError = '';
this.selectedPubkey = (this.selectedPeer.value && this.selectedPeer.value.id) ? this.selectedPeer.value.id : undefined;
if (typeof this.selectedPeer.value === 'string') {
let selPeer = this.peers.filter(peer => peer.alias.length === this.selectedPeer.value.length && peer.alias.toLowerCase().indexOf(this.selectedPeer.value ? this.selectedPeer.value.toLowerCase() : '') === 0);
if (selPeer.length === 1 && selPeer[0].id) { this.selectedPubkey = selPeer[0].id; }
}
if (this.selectedPeer.value && !this.selectedPubkey) {
this.selectedPeer.setErrors({notfound: true});
} else {
this.selectedPeer.setErrors(null);
}
} }
onClose() { onClose() {
@ -44,21 +98,37 @@ export class CLOpenChannelComponent implements OnInit {
} }
resetData() { resetData() {
this.fundingAmount = 0;
this.moreOptions = false;
this.flgMinConf = false; this.flgMinConf = false;
this.isPrivate = false;
this.selFeeRate = ''; this.selFeeRate = '';
this.minConfValue = null; this.minConfValue = null;
this.selectedPeer.setValue('');
this.fundingAmount = null;
this.isPrivate = false;
this.channelConnectionError = '';
this.advancedTitle = 'Advanced Options';
this.form.resetForm();
}
onAdvancedPanelToggle(isClosed: boolean) {
if (isClosed) {
this.advancedTitle = (!this.flgMinConf && !this.selFeeRate) ? 'Advanced Options' : 'Advanced Options | ' + (this.flgMinConf ? 'Min Confirmation Blocks: ' : 'Fee Rate: ') + (this.flgMinConf ? this.minConfValue : (this.selFeeRate ? this.feeRateTypes.find(feeRateType => feeRateType.feeRateId === this.selFeeRate).feeRateType : ''));
} else {
this.advancedTitle = 'Advanced Options';
}
} }
onOpenChannel() { onOpenChannel() {
if (!this.fundingAmount || (this.totalBalance - ((this.fundingAmount) ? this.fundingAmount : 0) < 0)) { return true; } if ((!this.peer && !this.selectedPubkey) || (!this.fundingAmount || ((this.totalBalance - this.fundingAmount) < 0) || (this.flgMinConf && !this.minConfValue))) { return true; }
this.store.dispatch(new RTLActions.OpenSpinner('Opening Channel...')); this.store.dispatch(new RTLActions.OpenSpinner('Opening Channel...'));
this.store.dispatch(new RTLActions.SaveNewChannelCL({ this.store.dispatch(new RTLActions.SaveNewChannelCL({
peerId: this.peer.id, satoshis: this.fundingAmount, announce: !this.isPrivate, feeRate: this.selFeeRate, minconf: this.flgMinConf ? this.minConfValue : null peerId: ((!this.peer || !this.peer.id) ? this.selectedPubkey : this.peer.id), satoshis: this.fundingAmount, announce: !this.isPrivate, feeRate: this.selFeeRate, minconf: this.flgMinConf ? this.minConfValue : null
})); }));
this.dialogRef.close(false);
} }
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
} }

@ -0,0 +1,78 @@
<div fxLayout="row">
<div fxFlex="100" class="padding-gap-large">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="95" fxLayoutAlign="start start">
<span class="page-title">Connect to a new peer</span>
</div>
<button tabindex="8" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" (click)="onClose()" mat-button>X</button>
</mat-card-header>
<mat-card-content class="mt-5px">
<div fxLayout="column">
<mat-vertical-stepper [linear]="true" #stepper (selectionChange)="stepSelectionChanged($event)">
<mat-step [stepControl]="peerFormGroup" [editable]="flgEditable">
<form [formGroup]="peerFormGroup" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign="start" fxLayoutAlign.gt-sm="space-between" class="my-1">
<ng-template matStepLabel>{{peerFormLabel}}</ng-template>
<mat-form-field fxFlex="100">
<input autoFocus matInput placeholder="Lightning Address (pubkey OR pubkey@ip:port)" formControlName="peerAddress" tabindex="1" required>
<mat-error *ngIf="peerFormGroup.controls.peerAddress.errors?.required">Address is required.</mat-error>
</mat-form-field>
<div fxFlex="100" class="alert alert-danger mt-1" *ngIf="peerConnectionError !== ''">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span>{{peerConnectionError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">
<button mat-stroked-button color="primary" tabindex="3" type="button" (click)="onConnectPeer()">{{peerConnectionError !== '' ? 'Retry' : 'Add Peer'}}</button>
</div>
</form>
</mat-step>
<mat-step [stepControl]="channelFormGroup" [editable]="flgEditable">
<form [formGroup]="channelFormGroup" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign="start" fxLayoutAlign.gt-sm="space-between" class="mb-1">
<ng-template matStepLabel disabled="true">{{channelFormLabel}}</ng-template>
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxFlex="100" fxLayoutAlign="space-between stretch">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<mat-form-field fxFlex="60" fxLayoutAlign="start end">
<input matInput autoFocus formControlName="fundingAmount" placeholder="Amount" type="number" step="1000" tabindex="1" required>
<mat-hint>Remaining Bal: {{totalBalance - ((channelFormGroup.controls.fundingAmount.value) ? channelFormGroup.controls.fundingAmount.value : 0)}}</mat-hint>
<span matSuffix> Sats </span>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.required">Amount is required.</mat-error>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.min">Amount must be a positive number.</mat-error>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error>
</mat-form-field>
<div fxFlex="35" fxLayoutAlign="start center">
<mat-slide-toggle tabindex="2" color="primary" formControlName="isPrivate" name="isPrivate">Private Channel</mat-slide-toggle>
</div>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center" class="mt-1">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="4" placeholder="Fee Rate" formControlName="selFeeRate">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="2" tabindex="5" color="primary" formControlName="flgMinConf" name="flgMinConf" fxLayoutAlign="stretch start" class="mr-2"></mat-checkbox>
<mat-form-field fxFlex="98">
<input matInput formControlName="minConfValue" placeholder="Min Confirmation Blocks" type="number" name="blocks" step="1" min="0" tabindex="8" [required]="channelFormGroup.controls.flgMinConf.value">
<mat-error *ngIf="channelFormGroup.controls.flgMinConf.value && !channelFormGroup.controls.minConfValue.value">Min Confirmation Blocks is required.</mat-error>
</mat-form-field>
</div>
</div>
</div>
<div fxFlex="100" class="alert alert-danger mt-1" *ngIf="channelConnectionError !== ''">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span>{{channelConnectionError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">
<button mat-stroked-button color="primary" tabindex="8" type="button" (click)="onOpenChannel()">{{channelConnectionError !== '' ? 'Retry' : 'Open Channel'}}</button>
</div>
</form>
</mat-step>
</mat-vertical-stepper>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="end center">
<button mat-stroked-button color="primary" tabindex="12" type="button" [mat-dialog-close]="false" default>{{newlyAddedPeer?.id ? 'Do It Later' : 'Close'}}</button>
</div>
</div>
</mat-card-content>
</div>
</div>

@ -1,20 +1,20 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ChannelManageComponent } from './channel-manage.component'; import { CLConnectPeerComponent } from './connect-peer.component';
describe('ChannelManageComponent', () => { describe('CLConnectPeerComponent', () => {
let component: ChannelManageComponent; let component: CLConnectPeerComponent;
let fixture: ComponentFixture<ChannelManageComponent>; let fixture: ComponentFixture<CLConnectPeerComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ ChannelManageComponent ] declarations: [ CLConnectPeerComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ChannelManageComponent); fixture = TestBed.createComponent(CLConnectPeerComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });

@ -0,0 +1,166 @@
import { Component, OnInit, OnDestroy, ViewChild, Inject } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatDialogRef, MAT_DIALOG_DATA, MatVerticalStepper } from '@angular/material';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { LoggerService } from '../../../shared/services/logger.service';
import { PeerCL } from '../../../shared/models/clModels';
import { CLOpenChannelAlert } from '../../../shared/models/alertData';
import { FEE_RATE_TYPES } from '../../../shared/services/consts-enums-functions';
import { CLEffects } from '../../store/cl.effects';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@Component({
selector: 'rtl-cl-connect-peer',
templateUrl: './connect-peer.component.html',
styleUrls: ['./connect-peer.component.scss']
})
export class CLConnectPeerComponent implements OnInit, OnDestroy {
@ViewChild('peersForm', {static: true}) form: any;
@ViewChild('stepper', { static: false }) stepper: MatVerticalStepper;
public faExclamationTriangle = faExclamationTriangle;
public peerAddress = '';
public totalBalance = 0;
public feeRateTypes = FEE_RATE_TYPES;
public flgChannelOpened = false;
public channelOpenStatus = null;
public newlyAddedPeer: PeerCL = null;
public flgEditable = true;
public peerConnectionError = '';
public channelConnectionError = '';
public peerFormLabel = 'Peer Details';
public channelFormLabel = 'Open Channel (Optional)';
peerFormGroup: FormGroup;
channelFormGroup: FormGroup;
statusFormGroup: FormGroup;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(public dialogRef: MatDialogRef<CLConnectPeerComponent>, @Inject(MAT_DIALOG_DATA) public data: CLOpenChannelAlert, private store: Store<fromRTLReducer.RTLState>, private clEffects: CLEffects, private formBuilder: FormBuilder, private actions$: Actions, private logger: LoggerService) {}
ngOnInit() {
this.totalBalance = this.data.message.balance;
this.peerFormGroup = this.formBuilder.group({
hiddenAddress: ['', [Validators.required]],
peerAddress: ['', [Validators.required]]
});
this.channelFormGroup = this.formBuilder.group({
fundingAmount: ['', [Validators.required, Validators.min(1), Validators.max(this.totalBalance)]],
isPrivate: [false],
selFeeRate: [null],
flgMinConf: [false],
minConfValue: [null],
hiddenAmount: ['', [Validators.required]]
});
this.statusFormGroup = this.formBuilder.group({});
this.channelFormGroup.controls.flgMinConf.valueChanges.pipe(takeUntil(this.unSubs[0])).subscribe(flg => {
if (flg) {
this.channelFormGroup.controls.selFeeRate.setValue(null);
this.channelFormGroup.controls.selFeeRate.disable();
this.channelFormGroup.controls.minConfValue.enable();
this.channelFormGroup.controls.minConfValue.setValidators([Validators.required]);
} else {
this.channelFormGroup.controls.selFeeRate.enable();
this.channelFormGroup.controls.minConfValue.disable();
this.channelFormGroup.controls.minConfValue.setValidators(null);
}
});
this.actions$.pipe(takeUntil(this.unSubs[1]),
filter((action) => action.type === RTLActions.NEWLY_ADDED_PEER_CL || action.type === RTLActions.FETCH_CHANNELS_CL || action.type === RTLActions.EFFECT_ERROR_CL))
.subscribe((action: (RTLActions.NewlyAddedPeerCL | RTLActions.FetchChannelsCL | RTLActions.EffectErrorCl)) => {
if (action.type === RTLActions.NEWLY_ADDED_PEER_CL) {
this.logger.info(action.payload);
this.flgEditable = false;
this.newlyAddedPeer = action.payload.peer;
this.peerFormGroup.controls.hiddenAddress.setValue(this.peerFormGroup.controls.peerAddress.value);
this.stepper.next();
}
if (action.type === RTLActions.FETCH_CHANNELS_CL) {
this.dialogRef.close();
}
if (action.type === RTLActions.EFFECT_ERROR_CL) {
if (action.payload.action === 'SaveNewPeerCL') {
this.peerConnectionError = action.payload.message;
} else if (action.payload.action === 'SaveNewChannelCL') {
this.channelConnectionError = action.payload.message;
}
}
});
}
onConnectPeer() {
if(!this.peerFormGroup.controls.peerAddress.value) { return true; }
this.peerConnectionError = '';
this.store.dispatch(new RTLActions.OpenSpinner('Adding Peer...'));
this.store.dispatch(new RTLActions.SaveNewPeerCL({id: this.peerFormGroup.controls.peerAddress.value}));
}
onOpenChannel() {
if (!this.channelFormGroup.controls.fundingAmount.value || ((this.totalBalance - this.channelFormGroup.controls.fundingAmount.value) < 0) || (this.channelFormGroup.controls.flgMinConf.value && !this.channelFormGroup.controls.minConfValue.value)) { return true; }
this.channelConnectionError = '';
this.store.dispatch(new RTLActions.OpenSpinner('Opening Channel...'));
this.store.dispatch(new RTLActions.SaveNewChannelCL({
peerId: this.newlyAddedPeer.id, satoshis: this.channelFormGroup.controls.fundingAmount.value, announce: !this.channelFormGroup.controls.isPrivate.value, feeRate: this.channelFormGroup.controls.selFeeRate.value, minconf: this.channelFormGroup.controls.flgMinConf.value ? this.channelFormGroup.controls.minConfValue.value : null
}));
}
onClose() {
this.dialogRef.close(false);
}
stepSelectionChanged(event: any) {
switch (event.selectedIndex) {
case 0:
this.peerFormLabel = 'Peer Details';
this.channelFormLabel = 'Open Channel (Optional)';
break;
case 1:
if (this.peerFormGroup.controls.peerAddress.value) {
this.peerFormLabel = 'Peer Added: ' + (this.newlyAddedPeer.alias ? this.newlyAddedPeer.alias : this.newlyAddedPeer.id);
} else {
this.peerFormLabel = 'Peer Details';
}
this.channelFormLabel = 'Open Channel (Optional)';
break;
case 2:
if (this.peerFormGroup.controls.peerAddress.value) {
this.peerFormLabel = 'Peer Added: ' + (this.newlyAddedPeer.alias ? this.newlyAddedPeer.alias : this.newlyAddedPeer.id);
} else {
this.peerFormLabel = 'Peer Details';
}
if (this.channelFormGroup.controls.fundingAmount.value) {
this.channelFormLabel = 'Opening Channel for ' + this.channelFormGroup.controls.fundingAmount.value + ' Sats';
} else {
this.channelFormLabel = 'Open Channel (Optional)';
}
break;
default:
this.peerFormLabel = 'Peer Details';
this.channelFormLabel = 'Open Channel (Optional)';
break;
}
if (event.selectedIndex < event.previouslySelectedIndex) {
if (event.selectedIndex === 0) {
this.peerFormGroup.controls.hiddenAddress.setValue('');
} else if (event.selectedIndex === 1) {
this.channelFormGroup.controls.hiddenAmount.setValue('');
}
}
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -21,7 +21,12 @@
<ng-template mat-tab-label> <ng-template mat-tab-label>
<span matBadge="{{activeChannels}}" matBadgeOverlap="false" class="tab-badge">Channels</span> <span matBadge="{{activeChannels}}" matBadgeOverlap="false" class="tab-badge">Channels</span>
</ng-template> </ng-template>
<rtl-cl-channel-manage></rtl-cl-channel-manage> <div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap">
<div fxLayout="row">
<button mat-flat-button color="primary" (click)="onOpenChannel()" type="submit" tabindex="1">Open Channel</button>
</div>
<rtl-cl-channels-tables fxLayout="row" fxFlex="100"></rtl-cl-channels-tables>
</div>
</mat-tab> </mat-tab>
<mat-tab> <mat-tab>
<ng-template mat-tab-label> <ng-template mat-tab-label>

@ -5,7 +5,11 @@ import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects'; import { Actions } from '@ngrx/effects';
import { faUsers, faChartPie } from '@fortawesome/free-solid-svg-icons'; import { faUsers, faChartPie } from '@fortawesome/free-solid-svg-icons';
import { GetInfoCL, PeerCL } from '../../shared/models/clModels';
import { CLOpenChannelComponent } from './channels/open-channel-modal/open-channel.component';
import { SelNodeChild } from '../../shared/models/RTLconfig'; import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers'; import * as fromRTLReducer from '../../store/rtl.reducers';
@Component({ @Component({
@ -15,6 +19,9 @@ import * as fromRTLReducer from '../../store/rtl.reducers';
}) })
export class CLPeersChannelsComponent implements OnInit, OnDestroy { export class CLPeersChannelsComponent implements OnInit, OnDestroy {
public selNode: SelNodeChild = {}; public selNode: SelNodeChild = {};
public information: GetInfoCL = {};
public peers: PeerCL[] = [];
public totalBalance = 0;
public activePeers = 0; public activePeers = 0;
public activeChannels = 0; public activeChannels = 0;
public faUsers = faUsers; public faUsers = faUsers;
@ -29,12 +36,28 @@ export class CLPeersChannelsComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.unSubs[1])) .pipe(takeUntil(this.unSubs[1]))
.subscribe((rtlStore) => { .subscribe((rtlStore) => {
this.selNode = rtlStore.nodeSettings; this.selNode = rtlStore.nodeSettings;
this.information = rtlStore.information;
this.peers = rtlStore.peers;
this.activePeers = (rtlStore.peers && rtlStore.peers.length) ? rtlStore.peers.length : 0; this.activePeers = (rtlStore.peers && rtlStore.peers.length) ? rtlStore.peers.length : 0;
this.activeChannels = rtlStore.information.num_active_channels; this.activeChannels = rtlStore.information.num_active_channels;
this.totalBalance = rtlStore.balance.totalBalance;
this.balances = [{title: 'Total Balance', dataValue: rtlStore.balance.totalBalance || 0}, {title: 'Confirmed', dataValue: rtlStore.balance.confBalance}, {title: 'Unconfirmed', dataValue: rtlStore.balance.unconfBalance}]; this.balances = [{title: 'Total Balance', dataValue: rtlStore.balance.totalBalance || 0}, {title: 'Confirmed', dataValue: rtlStore.balance.confBalance}, {title: 'Unconfirmed', dataValue: rtlStore.balance.unconfBalance}];
}); });
} }
onOpenChannel() {
const peerToAddChannelMessage = {
peers: this.peers,
information: this.information,
balance: this.totalBalance
};
this.store.dispatch(new RTLActions.OpenAlert({ data: {
alertTitle: 'Open Channel',
message: peerToAddChannelMessage,
component: CLOpenChannelComponent
}}));
}
ngOnDestroy() { ngOnDestroy() {
this.unSubs.forEach(completeSub => { this.unSubs.forEach(completeSub => {
completeSub.next(); completeSub.next();

@ -1,13 +1,6 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap"> <div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap">
<form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" #peersForm="ngForm"> <form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" #peersForm="ngForm">
<mat-form-field fxFlex="100" fxLayoutAlign="start end"> <button mat-flat-button color="primary" type="submit" tabindex="1" (click)="onConnectPeer()">Add Peer</button>
<input matInput placeholder="Lightning Address (pubkey OR pubkey@ip:port)" name="peerAddress" [(ngModel)]="peerAddress" tabindex="1" required #peerAdd="ngModel">
<mat-error *ngIf="!peerAddress">Lightning address is required.</mat-error>
</mat-form-field>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" type="submit" tabindex="3" (click)="onConnectPeer()">Add Peer</button>
</div>
</form> </form>
<div fxLayout="column"> <div fxLayout="column">
<div fxLayout="column" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="padding-gap-x page-sub-title-container mt-2"> <div fxLayout="column" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="padding-gap-x page-sub-title-container mt-2">

@ -17,6 +17,7 @@ import { CLEffects } from '../../store/cl.effects';
import { RTLEffects } from '../../../store/rtl.effects'; import { RTLEffects } from '../../../store/rtl.effects';
import * as RTLActions from '../../../store/rtl.actions'; import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers'; import * as fromRTLReducer from '../../../store/rtl.reducers';
import { CLConnectPeerComponent } from '../connect-peer/connect-peer.component';
@Component({ @Component({
selector: 'rtl-cl-peers', selector: 'rtl-cl-peers',
@ -30,7 +31,6 @@ import * as fromRTLReducer from '../../../store/rtl.reducers';
export class CLPeersComponent implements OnInit, OnDestroy { export class CLPeersComponent implements OnInit, OnDestroy {
@ViewChild(MatSort, { static: true }) sort: MatSort; @ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator; @ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
@ViewChild('peersForm', {static: true}) form: any;
public faUsers = faUsers; public faUsers = faUsers;
public newlyAddedPeer = ''; public newlyAddedPeer = '';
public flgAnimate = true; public flgAnimate = true;
@ -96,18 +96,9 @@ export class CLPeersComponent implements OnInit, OnDestroy {
).subscribe((setPeers: RTLActions.SetPeersCL) => { ).subscribe((setPeers: RTLActions.SetPeersCL) => {
this.peerAddress = undefined; this.peerAddress = undefined;
this.flgAnimate = true; this.flgAnimate = true;
this.form.resetForm();
}); });
} }
onConnectPeer() {
if(!this.peerAddress) { return true; }
this.flgAnimate = true;
this.newlyAddedPeer = this.peerAddress;
this.store.dispatch(new RTLActions.OpenSpinner('Adding Peer...'));
this.store.dispatch(new RTLActions.SaveNewPeerCL({id: this.peerAddress, showOpenChannelModal: true}));
}
onPeerClick(selPeer: PeerCL, event: any) { onPeerClick(selPeer: PeerCL, event: any) {
const reorderedPeer = [ const reorderedPeer = [
[{key: 'id', value: selPeer.id, title: 'Public Key', width: 100}], [{key: 'id', value: selPeer.id, title: 'Public Key', width: 100}],
@ -126,9 +117,11 @@ export class CLPeersComponent implements OnInit, OnDestroy {
}})); }}));
} }
resetData() { onConnectPeer() {
this.peerAddress = ''; this.store.dispatch(new RTLActions.OpenAlert({ data: {
this.form.resetForm(); message: { peer: null, information: this.information, balance: this.availableBalance },
component: CLConnectPeerComponent
}}));
} }
onOpenChannel(peerToAddChannel: PeerCL) { onOpenChannel(peerToAddChannel: PeerCL) {

@ -6,27 +6,6 @@
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start start" class="padding-gap-x"> <div fxLayout="row" fxFlex="100" fxLayoutAlign="start start" class="padding-gap-x">
<mat-card fxLayout="row" fxFlex="100" fxLayoutAlign="start start"> <mat-card fxLayout="row" fxFlex="100" fxLayoutAlign="start start">
<mat-card-content fxLayout="column" fxFlex="100" fxLayoutAlign="start start"> <mat-card-content fxLayout="column" fxFlex="100" fxLayoutAlign="start start">
<!-- <mat-card-content fxLayout="column" fxFlex="100" fxLayoutAlign="start start" class="card-content-gap mt-1"> -->
<!-- <form fxFlex="100" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch" class="w-100 mb-1" (ngSubmit)="onEventsFetch()" #routingForm="ngForm">
<div fxFlex="100" fxLayoutAlign="space-between stretch">
<mat-form-field fxFlex="49" fxLayoutAlign="start">
<input matInput [matDatepicker]="startDatepicker" placeholder="Start Date" [max]="yesterday"
name="startDate" [(ngModel)]="startDate" tabindex="1" #strtDate="ngModel">
<mat-datepicker-toggle matSuffix [for]="startDatepicker"></mat-datepicker-toggle>
<mat-datepicker #startDatepicker [startAt]="startDate"></mat-datepicker>
</mat-form-field>
<mat-form-field fxFlex="49" fxLayoutAlign="start">
<input matInput [matDatepicker]="endDatepicker" [max]="today" placeholder="End Date" name="endDate"
[(ngModel)]="endDate" tabindex="2" #enDate="ngModel">
<mat-datepicker-toggle matSuffix [for]="endDatepicker"></mat-datepicker-toggle>
<mat-datepicker #endDatepicker [startAt]="endDate"></mat-datepicker>
</mat-form-field>
</div>
<div fxFlex="30" fxLayoutAlign="space-between stretch" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="resetData()">Clear</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" type="submit" tabindex="4">Fetch Events</button>
</div>
</form> -->
<div *ngIf="filteredData" fxLayout="row" fxFlex="100" [ngClass]="{'error-border w-100 routing-tabs-block': flgLoading[0]==='error', 'w-100 routing-tabs-block': true}"> <div *ngIf="filteredData" fxLayout="row" fxFlex="100" [ngClass]="{'error-border w-100 routing-tabs-block': flgLoading[0]==='error', 'w-100 routing-tabs-block': true}">
<mat-tab-group fxLayout="column" fxFlex="100"> <mat-tab-group fxLayout="column" fxFlex="100">
<mat-tab label="Forwarding History"> <mat-tab label="Forwarding History">

@ -4,15 +4,15 @@
<textarea autoFocus matInput [(ngModel)]="message" rows="1" placeholder="Message to sign" required tabindex="1" (keyup)="onMessageChange()" name="message"></textarea> <textarea autoFocus matInput [(ngModel)]="message" rows="1" placeholder="Message to sign" required tabindex="1" (keyup)="onMessageChange()" name="message"></textarea>
<mat-error *ngIf="!message">Message is required.</mat-error> <mat-error *ngIf="!message">Message is required.</mat-error>
</mat-form-field> </mat-form-field>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="my-1"> <div fxLayout="row" class="my-1">
<button fxFlex="48" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()" type="reset">Clear Field</button> <button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" mat-flat-button color="primary" (click)="onSign()" tabindex="3" type="submit">Sign</button> <button mat-flat-button color="primary" (click)="onSign()" tabindex="3" type="submit">Sign</button>
</div> </div>
<mat-divider [inset]="true" class="my-2"></mat-divider> <mat-divider [inset]="true" class="my-2"></mat-divider>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start center"><p>Generated Signature</p></div> <div fxLayout="row" fxFlex="100" fxLayoutAlign="start center"><p>Generated Signature</p></div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start center" class="bordered-box read-only h-4 padding-gap">{{signature}}</div> <div fxLayout="row" fxFlex="100" fxLayoutAlign="start center" class="bordered-box read-only h-4 padding-gap">{{signature}}</div>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2"> <div fxLayout="row" class="mt-2">
<button fxFlex="48" mat-stroked-button color="primary" tabindex="4" rtlClipboard [payload]="signature" (copied)="onCopyField($event)" type="button">Copy Signature</button> <button mat-stroked-button color="primary" tabindex="4" rtlClipboard [payload]="signature" (copied)="onCopyField($event)" type="button">Copy Signature</button>
</div> </div>
</form> </form>
</div> </div>

@ -24,7 +24,6 @@ export class CLSignComponent implements OnInit, OnDestroy {
onSign() { onSign() {
if (!this.message || this.message === '') { return true; } if (!this.message || this.message === '') { return true; }
this.dataService.signMessage(this.message).pipe(takeUntil(this.unSubs[0])).subscribe(res => { this.dataService.signMessage(this.message).pipe(takeUntil(this.unSubs[0])).subscribe(res => {
console.warn(res);
this.signedMessage = this.message; this.signedMessage = this.message;
this.signature = res.zbase; this.signature = res.zbase;
}); });

@ -9,9 +9,9 @@
<mat-error *ngIf="!signature">Signature is required.</mat-error> <mat-error *ngIf="!signature">Signature is required.</mat-error>
</mat-form-field> </mat-form-field>
<p *ngIf="showVerifyStatus && !verifyRes.verified" fxFlex="100" class="color-warn" fxLayoutAlign="start center"><mat-icon class="mr-1 icon-small">close</mat-icon>Verification failed, please double check message and signature</p> <p *ngIf="showVerifyStatus && !verifyRes.verified" fxFlex="100" class="color-warn" fxLayoutAlign="start center"><mat-icon class="mr-1 icon-small">close</mat-icon>Verification failed, please double check message and signature</p>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="my-1"> <div fxLayout="row" class="my-1">
<button fxFlex="48" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="resetData()" type="reset">Clear Fields</button> <button class="mr-1" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="resetData()">Clear Fields</button>
<button fxFlex="48" mat-flat-button color="primary" (click)="onVerify()" tabindex="4" type="submit">Verify</button> <button mat-flat-button color="primary" (click)="onVerify()" tabindex="4" type="submit">Verify</button>
</div> </div>
<div *ngIf="showVerifyStatus && verifyRes.verified" fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap"> <div *ngIf="showVerifyStatus && verifyRes.verified" fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap">
<mat-divider [inset]="true" class="my-2"></mat-divider> <mat-divider [inset]="true" class="my-2"></mat-divider>
@ -21,8 +21,8 @@
<div *ngIf="verifyRes.verified" fxLayout="column" fxFlex="100" fxLayoutAlign="start start" class="bordered-box read-only h-4 padding-gap"> <div *ngIf="verifyRes.verified" fxLayout="column" fxFlex="100" fxLayoutAlign="start start" class="bordered-box read-only h-4 padding-gap">
<p>{{verifyRes?.pubkey}}</p> <p>{{verifyRes?.pubkey}}</p>
</div> </div>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2" *ngIf="verifyRes.verified"> <div fxLayout="row" class="mt-2" *ngIf="verifyRes.verified">
<button fxFlex="48" mat-stroked-button color="primary" tabindex="5" rtlClipboard [payload]="verifyRes?.pubkey" (copied)="onCopyField($event)" type="button">Copy Pubkey</button> <button mat-stroked-button color="primary" tabindex="5" rtlClipboard [payload]="verifyRes?.pubkey" (copied)="onCopyField($event)" type="button">Copy Pubkey</button>
</div> </div>
</div> </div>
</form> </form>

@ -14,7 +14,7 @@ import { CommonService } from '../../shared/services/common.service';
import { ErrorMessageComponent } from '../../shared/components/data-modal/error-message/error-message.component'; import { ErrorMessageComponent } from '../../shared/components/data-modal/error-message/error-message.component';
import { CLInvoiceInformationComponent } from '../transactions/invoice-information-modal/invoice-information.component'; import { CLInvoiceInformationComponent } from '../transactions/invoice-information-modal/invoice-information.component';
import { CLOpenChannelComponent } from '../peers-channels/channels/open-channel-modal/open-channel.component'; import { CLOpenChannelComponent } from '../peers-channels/channels/open-channel-modal/open-channel.component';
import { GetInfoCL, FeesCL, BalanceCL, LocalRemoteBalanceCL, PaymentCL, FeeRatesCL, ListInvoicesCL, InvoiceCL } from '../../shared/models/clModels'; import { GetInfoCL, FeesCL, BalanceCL, LocalRemoteBalanceCL, PaymentCL, FeeRatesCL, ListInvoicesCL, InvoiceCL, PeerCL } from '../../shared/models/clModels';
import { AlertTypeEnum } from '../../shared/services/consts-enums-functions'; import { AlertTypeEnum } from '../../shared/services/consts-enums-functions';
import * as fromRTLReducer from '../../store/rtl.reducers'; import * as fromRTLReducer from '../../store/rtl.reducers';
import * as RTLActions from '../../store/rtl.actions'; import * as RTLActions from '../../store/rtl.actions';
@ -53,24 +53,10 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err) => { catchError((err) => {
let code = err.status ? err.status : ''; const code = (err.error && err.error.error && err.error.error.message && err.error.error.message.code) ? err.error.error.message.code : (err.error && err.error.error && err.error.error.code) ? err.error.error.code : err.status ? err.status : '';
let message = err.error.message ? err.error.message + ' ' : ''; const message = ((err.error && err.error.message) ? err.error.message + ' ' : '') + ((err.error && err.error.error && err.error.error.error && typeof err.error.error.error === 'string') ? err.error.error.error : (err.error && err.error.error && err.error.error.errno && typeof err.error.error.errno === 'string') ? err.error.error.errno : (err.error && err.error.error && typeof err.error.error === 'string') ? err.error.error : (err.error && typeof err.error === 'string') ? err.error : 'Unknown Error');
if (err.error && err.error.error) {
if (err.error.error.code) {
code = err.error.error.code;
} else if (err.error.error.message && err.error.error.message.code) {
code = err.error.error.message.code;
}
if (typeof err.error.error === 'string') {
message = message + err.error.error;
} else if (err.error.error.error) {
message = message + err.error.error.error;
} else if (err.error.error.errno) {
message = message + err.error.error.errno;
}
}
this.router.navigate(['/error'], { state: { errorCode: code, errorMessage: message }}); this.router.navigate(['/error'], { state: { errorCode: code, errorMessage: message }});
this.handleErrorWithoutAlert('FetchInfoCL', err); this.handleErrorWithoutAlert('FetchInfoCL', 'Fetching Node Info Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
}) })
); );
@ -92,7 +78,7 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithoutAlert('FetchFeesCL', err); this.handleErrorWithoutAlert('FetchFeesCL', 'Fetching Fees Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
} }
)); ));
@ -112,7 +98,7 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithoutAlert('FetchFeeRatesCL', err); this.handleErrorWithoutAlert('FetchFeeRatesCL', 'Fetching Fee Rates Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
} }
)); ));
@ -132,7 +118,7 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithoutAlert('FetchBalanceCL', err); this.handleErrorWithoutAlert('FetchBalanceCL', 'Fetching Balances Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
} }
)); ));
@ -152,7 +138,7 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithoutAlert('FetchLocalRemoteBalanceCL', err); this.handleErrorWithoutAlert('FetchLocalRemoteBalanceCL', 'Fetching Balances Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
} }
)); ));
@ -201,7 +187,7 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithoutAlert('FetchPeersCL', err); this.handleErrorWithoutAlert('FetchPeersCL', 'Fetching Peers Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
}) })
); );
@ -213,36 +199,20 @@ export class CLEffects implements OnDestroy {
ofType(RTLActions.SAVE_NEW_PEER_CL), ofType(RTLActions.SAVE_NEW_PEER_CL),
withLatestFrom(this.store.select('cl')), withLatestFrom(this.store.select('cl')),
mergeMap(([action, clData]: [RTLActions.SaveNewPeerCL, fromCLReducers.CLState]) => { mergeMap(([action, clData]: [RTLActions.SaveNewPeerCL, fromCLReducers.CLState]) => {
this.store.dispatch(new RTLActions.ClearEffectErrorCl('SaveNewPeerCL'));
return this.httpClient.post(this.CHILD_API_URL + environment.PEERS_API, { id: action.payload.id }) return this.httpClient.post(this.CHILD_API_URL + environment.PEERS_API, { id: action.payload.id })
.pipe( .pipe(
map((postRes: any) => { map((postRes: PeerCL[]) => {
this.logger.info(postRes); this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.SetPeersCL((postRes && postRes.length > 0) ? postRes : [])); this.store.dispatch(new RTLActions.SetPeersCL((postRes && postRes.length > 0) ? postRes : []));
if(action.payload.showOpenChannelModal) { return {
const peerToAddChannelMessage = { type: RTLActions.NEWLY_ADDED_PEER_CL,
peer: postRes[0], payload: {peer: postRes.find(peer => action.payload.id.indexOf(peer.id) === 0)}
information: clData.information, };
balance: clData.balance.totalBalance || 0
};
return {
type: RTLActions.OPEN_ALERT,
payload: { data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Peer Connected',
message: peerToAddChannelMessage,
newlyAdded: true,
component: CLOpenChannelComponent
}}
};
} else {
return {
type: RTLActions.VOID
}
}
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithAlert('ERROR', 'Add Peer Failed', this.CHILD_API_URL + environment.PEERS_API, err); this.handleErrorWithoutAlert('SaveNewPeerCL', 'Peer Connection Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
}) })
); );
@ -287,7 +257,7 @@ export class CLEffects implements OnDestroy {
}; };
}, },
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithoutAlert('FetchChannelsCL', err); this.handleErrorWithoutAlert('FetchChannelsCL', 'Fetching Channels Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
}) })
)); ));
@ -298,6 +268,7 @@ export class CLEffects implements OnDestroy {
openNewChannelCL = this.actions$.pipe( openNewChannelCL = this.actions$.pipe(
ofType(RTLActions.SAVE_NEW_CHANNEL_CL), ofType(RTLActions.SAVE_NEW_CHANNEL_CL),
mergeMap((action: RTLActions.SaveNewChannelCL) => { mergeMap((action: RTLActions.SaveNewChannelCL) => {
this.store.dispatch(new RTLActions.ClearEffectErrorCl('SaveNewChannelCL'));
return this.httpClient.post(this.CHILD_API_URL + environment.CHANNELS_API, { return this.httpClient.post(this.CHILD_API_URL + environment.CHANNELS_API, {
id: action.payload.peerId, satoshis: action.payload.satoshis, feeRate: action.payload.feeRate, announce: action.payload.announce, minconf: (action.payload.minconf) ? action.payload.minconf : null id: action.payload.peerId, satoshis: action.payload.satoshis, feeRate: action.payload.feeRate, announce: action.payload.announce, minconf: (action.payload.minconf) ? action.payload.minconf : null
}) })
@ -312,7 +283,7 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithAlert('ERROR', 'Open Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API, err); this.handleErrorWithoutAlert('SaveNewChannelCL', 'Opening Channel Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
}) })
); );
@ -386,7 +357,7 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithoutAlert('FetchPaymentsCL', err); this.handleErrorWithoutAlert('FetchPaymentsCL', 'Fetching Payments Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
} }
)); ));
@ -395,7 +366,8 @@ export class CLEffects implements OnDestroy {
decodePaymentCL = this.actions$.pipe( decodePaymentCL = this.actions$.pipe(
ofType(RTLActions.DECODE_PAYMENT_CL), ofType(RTLActions.DECODE_PAYMENT_CL),
mergeMap((action: RTLActions.DecodePaymentCL) => { mergeMap((action: RTLActions.DecodePaymentCL) => {
return this.httpClient.get(this.CHILD_API_URL + environment.PAYMENTS_API + '/' + action.payload) this.store.dispatch(new RTLActions.ClearEffectErrorCl('DecodePaymentCL'));
return this.httpClient.get(this.CHILD_API_URL + environment.PAYMENTS_API + '/' + action.payload.routeParam)
.pipe( .pipe(
map((decodedPayment) => { map((decodedPayment) => {
this.logger.info(decodedPayment); this.logger.info(decodedPayment);
@ -406,7 +378,11 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithAlert('ERROR', 'Decode Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API + '/' + action.payload, err); if (action.payload.fromDialog) {
this.handleErrorWithoutAlert('DecodePaymentCL', 'Decode Payment Failed.', err);
} else {
this.handleErrorWithAlert('ERROR', 'Decode Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API + '/' + action.payload, err);
}
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
}) })
); );
@ -427,26 +403,41 @@ export class CLEffects implements OnDestroy {
ofType(RTLActions.SEND_PAYMENT_CL), ofType(RTLActions.SEND_PAYMENT_CL),
withLatestFrom(this.store.select('root')), withLatestFrom(this.store.select('root')),
mergeMap(([action, store]: [RTLActions.SendPaymentCL, any]) => { mergeMap(([action, store]: [RTLActions.SendPaymentCL, any]) => {
this.store.dispatch(new RTLActions.ClearEffectErrorCl('SendPaymentCL'));
return this.httpClient.post(this.CHILD_API_URL + environment.PAYMENTS_API, action.payload) return this.httpClient.post(this.CHILD_API_URL + environment.PAYMENTS_API, action.payload)
.pipe( .pipe(
map((sendRes: any) => { map((sendRes: any) => {
this.logger.info(sendRes); this.logger.info(sendRes);
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
if (sendRes.error) { if (sendRes.error) {
this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API, { status: sendRes.status, error: sendRes.error.message }); this.logger.error('Error: ' + sendRes.payment_error);
const myErr = {status: sendRes.payment_error.status, error: sendRes.payment_error.error && sendRes.payment_error.error.error && typeof(sendRes.payment_error.error.error) === 'object' ? sendRes.payment_error.error.error : {error: sendRes.payment_error.error && sendRes.payment_error.error.error ? sendRes.payment_error.error.error : 'Unknown Error'}};
if (action.payload.fromDialog) {
this.handleErrorWithoutAlert('SendPaymentCL', 'Send Payment Failed.', myErr);
} else {
this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/transactions', myErr);
}
return of({type: RTLActions.VOID});
} else { } else {
this.store.dispatch(new RTLActions.OpenSnackBar('Payment Sent Successfully!')); this.store.dispatch(new RTLActions.OpenSnackBar('Payment Sent Successfully!'));
this.store.dispatch(new RTLActions.FetchChannelsCL()); this.store.dispatch(new RTLActions.FetchChannelsCL());
this.store.dispatch(new RTLActions.FetchBalanceCL()); this.store.dispatch(new RTLActions.FetchBalanceCL());
this.store.dispatch(new RTLActions.FetchPaymentsCL()); this.store.dispatch(new RTLActions.FetchPaymentsCL());
this.store.dispatch(new RTLActions.SetDecodedPaymentCL({}));
return { return {
type: RTLActions.SET_DECODED_PAYMENT_CL, type: RTLActions.SEND_PAYMENT_STATUS_CL,
payload: {} payload: sendRes
}; };
} }
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API, err.error ? err.error : err); this.logger.error('Error: ' + JSON.stringify(err));
const myErr = {status: err.status, error: err.error && err.error.error && typeof(err.error.error) === 'object' ? err.error.error : {error: err.error && err.error.error ? err.error.error : 'Unknown Error'}};
if (action.payload.fromDialog) {
this.handleErrorWithoutAlert('SendPaymentCL', 'Send Payment Failed.', myErr);
} else {
this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/transactions', myErr);
}
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
}) })
); );
@ -624,6 +615,7 @@ export class CLEffects implements OnDestroy {
saveNewInvoiceCL = this.actions$.pipe( saveNewInvoiceCL = this.actions$.pipe(
ofType(RTLActions.SAVE_NEW_INVOICE_CL), ofType(RTLActions.SAVE_NEW_INVOICE_CL),
mergeMap((action: RTLActions.SaveNewInvoiceCL) => { mergeMap((action: RTLActions.SaveNewInvoiceCL) => {
this.store.dispatch(new RTLActions.ClearEffectErrorCl('SaveNewInvoiceCL'));
return this.httpClient.post(this.CHILD_API_URL + environment.INVOICES_API, { return this.httpClient.post(this.CHILD_API_URL + environment.INVOICES_API, {
label: action.payload.label, amount: action.payload.amount, description: action.payload.description, expiry: action.payload.expiry, private: action.payload.private label: action.payload.label, amount: action.payload.amount, description: action.payload.description, expiry: action.payload.expiry, private: action.payload.private
}) })
@ -648,7 +640,7 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithAlert('ERROR', 'Add Invoice Failed', this.CHILD_API_URL + environment.INVOICES_API, err); this.handleErrorWithoutAlert('SaveNewInvoiceCL', 'Add Invoice Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
}) })
); );
@ -673,7 +665,7 @@ export class CLEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.handleErrorWithoutAlert('FetchInvoicesCL', err); this.handleErrorWithoutAlert('FetchInvoicesCL', 'Fetching Invoices Failed.', err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
} }
)); ));
@ -690,12 +682,13 @@ export class CLEffects implements OnDestroy {
this.logger.info(postRes); this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.FetchBalanceCL()); this.store.dispatch(new RTLActions.FetchBalanceCL());
this.store.dispatch(new RTLActions.OpenSnackBar('Fund Sent Successfully.')); return {
return { type: RTLActions.VOID }; type: RTLActions.SET_CHANNEL_TRANSACTION_RES_CL,
payload: postRes
};
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorCl({ action: 'SetChannelTransactionCL', code: err.status, message: err.error.error })); this.handleErrorWithoutAlert('SetChannelTransactionCL', 'Sending Fund Failed.', err);
this.handleErrorWithAlert('ERROR', 'Sending Fund Failed', this.CHILD_API_URL + environment.ON_CHAIN_API, err);
return of({type: RTLActions.VOID}); return of({type: RTLActions.VOID});
})); }));
}) })
@ -734,13 +727,16 @@ export class CLEffects implements OnDestroy {
this.router.navigate([newRoute]); this.router.navigate([newRoute]);
} }
handleErrorWithoutAlert(actionName: string, err: { status: number, error: any }) { handleErrorWithoutAlert(actionName: string, genericErrorMessage: string, err: { status: number, error: any }) {
this.logger.error('ERROR IN: ' + actionName + '\n' + JSON.stringify(err)); this.logger.error('ERROR IN: ' + actionName + '\n' + JSON.stringify(err));
if (err.status === 401) { if (err.status === 401) {
this.logger.info('Redirecting to Login'); this.logger.info('Redirecting to Login');
this.store.dispatch(new RTLActions.CloseAllDialogs());
this.store.dispatch(new RTLActions.Logout()); this.store.dispatch(new RTLActions.Logout());
this.store.dispatch(new RTLActions.OpenSnackBar('Authentication Failed. Redirecting to Login.'));
} else { } else {
this.store.dispatch(new RTLActions.EffectErrorCl({ action: actionName, code: err.status.toString(), message: err.error.error })); this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.EffectErrorCl({ action: actionName, code: err.status.toString(), message: (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.error && err.error.error.error && err.error.error.error.message && typeof err.error.error.error.message === 'string') ? err.error.error.error.message : (err.error.error && err.error.error.message && typeof err.error.error.message === 'string') ? err.error.error.message : (err.error.message && typeof err.error.message === 'string') ? err.error.message : typeof err.error === 'string' ? err.error : genericErrorMessage }));
} }
} }
@ -748,14 +744,16 @@ export class CLEffects implements OnDestroy {
this.logger.error(err); this.logger.error(err);
if (err.status === 401) { if (err.status === 401) {
this.logger.info('Redirecting to Login'); this.logger.info('Redirecting to Login');
this.store.dispatch(new RTLActions.CloseAllDialogs());
this.store.dispatch(new RTLActions.Logout()); this.store.dispatch(new RTLActions.Logout());
this.store.dispatch(new RTLActions.OpenSnackBar('Authentication Failed. Redirecting to Login.'));
} else { } else {
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenAlert({ this.store.dispatch(new RTLActions.OpenAlert({
data: { data: {
type: alerType, type: alerType,
alertTitle: alertTitle, alertTitle: alertTitle,
message: { code: err.status, message: err.error.error, URL: errURL }, message: { code: err.status, message: (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.error && err.error.error.error && err.error.error.error.message && typeof err.error.error.error.message === 'string') ? err.error.error.error.message : (err.error.error && err.error.error.message && typeof err.error.error.message === 'string') ? err.error.error.message : (err.error.message && typeof err.error.message === 'string') ? err.error.message : typeof err.error === 'string' ? err.error : 'Unknown Error', URL: errURL },
component: ErrorMessageComponent component: ErrorMessageComponent
} }
})); }));

@ -0,0 +1,45 @@
<div fxLayout="row">
<div fxFlex="100" class="padding-gap-large">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="95" fxLayoutAlign="start start">
<span class="page-title">Create Invoice</span>
</div>
<button tabindex="8" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" [mat-dialog-close]="false" default mat-button>X</button>
</mat-card-header>
<mat-card-content class="mt-5px">
<form fxLayout="row wrap" fxLayoutAlign="start space-between" fxFlex="100" #addInvoiceForm="ngForm">
<mat-form-field fxFlex="100" fxLayoutAlign="start end">
<input matInput autoFocus [(ngModel)]="description" placeholder="Description" tabindex="2" name="description">
</mat-form-field>
<div fxLayout="row" fxLayoutAlign="space-between start" fxFlex="100">
<mat-form-field fxFlex="40">
<input matInput [(ngModel)]="invoiceValue" (keyup)="onInvoiceValueChange()" placeholder="Amount" type="number" step="100" min="1" tabindex="3" name="invoiceValue" required>
<span matSuffix> {{information?.smaller_currency_unit}} </span>
<mat-hint>{{invoiceValueHint}}</mat-hint>
<mat-error *ngIf="!invoiceValue">Amount is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="30">
<input matInput [(ngModel)]="expiry" placeholder="Expiry" type="number" step="{{selTimeUnit === timeUnitEnum.SECS ? 300 : selTimeUnit === timeUnitEnum.MINS ? 10 : selTimeUnit === timeUnitEnum.HOURS ? 2 : 1}}" min="1" tabindex="4" name="expiry">
<span matSuffix> {{selTimeUnit | titlecase}} </span>
</mat-form-field>
<mat-form-field fxFlex="26">
<mat-select [value]="selTimeUnit" tabindex="5" name="timeUnit" (selectionChange)="onTimeUnitChange($event)">
<mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">{{timeUnit | titlecase}}</mat-option>
</mat-select>
</mat-form-field>
</div>
<div fxFlex="50" fxLayoutAlign="start center" class="mt-2">
<mat-slide-toggle tabindex="6" color="primary" [(ngModel)]="private" matTooltip="Include routing hints for private channels" [matTooltipPosition]="'above'" name="private">Private Routing Hints</mat-slide-toggle>
</div>
<div fxFlex="100" class="alert alert-danger mt-1" *ngIf="invoiceError !== ''">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span *ngIf="invoiceError !== ''">{{invoiceError}}</span>
</div>
<div fxLayout="row" fxFlex="100" class="mt-2" fxLayoutAlign="end center">
<button class="mr-1" mat-stroked-button color="primary" tabindex="7" type="reset" (click)="resetData()">Clear Field</button>
<button mat-flat-button color="primary" (click)="onAddInvoice(addInvoiceForm)" tabindex="8">Create Invoice</button>
</div>
</form>
</mat-card-content>
</div>
</div>

@ -0,0 +1,113 @@
import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
import { DecimalPipe } from '@angular/common';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { InvoiceInformation } from '../../../shared/models/alertData';
import { TimeUnitEnum, CurrencyUnitEnum, TIME_UNITS, CURRENCY_UNIT_FORMATS, PAGE_SIZE } from '../../../shared/services/consts-enums-functions';
import { SelNodeChild } from '../../../shared/models/RTLconfig';
import { GetInfoCL } from '../../../shared/models/clModels';
import { CommonService } from '../../../shared/services/common.service';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@Component({
selector: 'rtl-cl-create-invoices',
templateUrl: './create-invoice.component.html',
styleUrls: ['./create-invoice.component.scss']
})
export class CLCreateInvoiceComponent implements OnInit, OnDestroy {
public faExclamationTriangle = faExclamationTriangle;
public selNode: SelNodeChild = {};
public description = '';
public expiry: number;
public invoiceValue: number;
public invoiceValueHint = '';
public invoicePaymentReq = '';
public invoices: any;
public information: GetInfoCL = {};
public private = false;
public expiryStep = 100;
public pageSize = PAGE_SIZE;
public timeUnitEnum = TimeUnitEnum;
public timeUnits = TIME_UNITS;
public selTimeUnit = TimeUnitEnum.SECS;
public invoiceError = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
constructor(public dialogRef: MatDialogRef<CLCreateInvoiceComponent>, @Inject(MAT_DIALOG_DATA) public data: InvoiceInformation, private store: Store<fromRTLReducer.RTLState>, private decimalPipe: DecimalPipe, private commonService: CommonService, private actions$: Actions) {}
ngOnInit() {
this.pageSize = this.data.pageSize;
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
this.selNode = rtlStore.nodeSettings;
this.information = rtlStore.information;
});
this.actions$.pipe(takeUntil(this.unSubs[1]),
filter(action => action.type === RTLActions.EFFECT_ERROR_CL || action.type === RTLActions.FETCH_INVOICES_CL))
.subscribe((action: RTLActions.EffectErrorCl | RTLActions.FetchInvoicesCL) => {
if (action.type === RTLActions.FETCH_INVOICES_CL) {
this.dialogRef.close();
}
if (action.type === RTLActions.EFFECT_ERROR_CL && action.payload.action === 'SaveNewInvoiceCL') {
this.invoiceError = action.payload.message;
}
});
}
onAddInvoice(form: any) {
this.invoiceError = '';
if(!this.invoiceValue) { return true; }
let expiryInSecs = (this.expiry ? this.expiry : 3600);
if (this.selTimeUnit !== TimeUnitEnum.SECS) {
expiryInSecs = this.commonService.convertTime(this.expiry, this.selTimeUnit, TimeUnitEnum.SECS);
}
this.store.dispatch(new RTLActions.OpenSpinner('Adding Invoice...'));
this.store.dispatch(new RTLActions.SaveNewInvoiceCL({
label: ('ulbl' + Math.random().toString(36).slice(2) + Date.now()), amount: this.invoiceValue*1000, description: this.description, expiry: expiryInSecs, private: this.private
}));
}
resetData() {
this.description = '';
this.invoiceValue = undefined;
this.private = false;
this.expiry = undefined;
this.invoiceValueHint = '';
this.selTimeUnit = TimeUnitEnum.SECS;
this.invoiceError = '';
}
onInvoiceValueChange() {
if(this.selNode.fiatConversion && this.invoiceValue > 99) {
this.invoiceValueHint = '';
this.commonService.convertCurrency(this.invoiceValue, CurrencyUnitEnum.SATS, this.selNode.currencyUnits[2], this.selNode.fiatConversion)
.pipe(takeUntil(this.unSubs[2]))
.subscribe(data => {
this.invoiceValueHint = '= ' + data.symbol + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.unit;
});
}
}
onTimeUnitChange(event: any) {
if(this.expiry && this.selTimeUnit !== event.value) {
this.expiry = this.commonService.convertTime(this.expiry, this.selTimeUnit, event.value);
}
this.selTimeUnit = event.value;
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -77,12 +77,12 @@
</div> </div>
<mat-divider class="w-100 my-1"></mat-divider> <mat-divider class="w-100 my-1"></mat-divider>
</div> </div>
<div [ngClass]="{'mt-2': !showAdvanced, 'mt-1': showAdvanced}" fxLayout="row" fxLayoutAlign="end center" fxFlex="100"> <div [ngClass]="{'mt-2': !showAdvanced, 'mt-1': showAdvanced}" fxLayout="row" fxLayoutAlign="end center">
<button fxFlex="50" fxFlex.gt-sm="25" fxLayoutAlign="center center" mat-stroked-button color="primary" type="reset" (click)="onShowAdvanced()" tabindex="1" class="mr-2"> <button class="mr-1" mat-stroked-button color="primary" type="reset" (click)="onShowAdvanced()" tabindex="1">
<p *ngIf="!showAdvanced; else hideAdvancedText">Show Advanced</p> <p *ngIf="!showAdvanced; else hideAdvancedText">Show Advanced</p>
<ng-template #hideAdvancedText><p>Hide Advanced</p></ng-template> <ng-template #hideAdvancedText><p>Hide Advanced</p></ng-template>
</button> </button>
<button autoFocus fxFlex="50" fxFlex.gt-sm="33" fxLayoutAlign="center center" mat-flat-button color="primary" tabindex="2" type="submit" rtlClipboard [payload]="invoice.bolt11" (copied)="onCopyPayment($event)">Copy Invoice</button> <button autoFocus mat-flat-button color="primary" tabindex="2" type="submit" rtlClipboard [payload]="invoice.bolt11" (copied)="onCopyPayment($event)">Copy Invoice</button>
</div> </div>
</div> </div>
</mat-card-content> </mat-card-content>

@ -1,36 +1,23 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap"> <div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap">
<form [fxLayout]="showDetails ? 'column' : 'row wrap'" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" fxFlex="100" #addInvoiceForm="ngForm"> <form *ngIf="!showDetails" fxLayout="column" fxLayoutAlign="space-between stretch" fxFlex="100" #addInvoiceForm="ngForm">
<mat-form-field [fxFlex]="showDetails ? '45' : '100'" fxLayoutAlign="space-between stretch"> <mat-form-field fxFlex="100" fxLayoutAlign="space-between stretch">
<input matInput [(ngModel)]="description" placeholder="Description" tabindex="2" name="description"> <input matInput [(ngModel)]="description" placeholder="Description" tabindex="2" name="description">
</mat-form-field> </mat-form-field>
<mat-form-field [fxFlex]="showDetails ? '18' : '100'" fxLayoutAlign="start end"> <mat-form-field fxFlex="100" fxLayoutAlign="start end">
<input matInput [(ngModel)]="invoiceValue" (keyup)="onInvoiceValueChange()" placeholder="Amount" type="number" step="100" min="1" tabindex="3" name="invoiceValue" required> <input matInput [(ngModel)]="invoiceValue" (keyup)="onInvoiceValueChange()" placeholder="Amount" type="number" step="100" min="1" tabindex="3" name="invoiceValue" required>
<span matSuffix> {{information?.smaller_currency_unit}} </span> <span matSuffix> {{information?.smaller_currency_unit}} </span>
<mat-hint>{{invoiceValueHint}}</mat-hint> <mat-hint>{{invoiceValueHint}}</mat-hint>
<mat-error *ngIf="!invoiceValue">Amount is required.</mat-error> <mat-error *ngIf="!invoiceValue">Amount is required.</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex="10" fxLayoutAlign="start end" *ngIf="showDetails" [ngClass]="{'mt-2': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}"> <div fxLayout="row" class="mt-1">
<input matInput [(ngModel)]="expiry" placeholder="Expiry" type="number" step="{{selTimeUnit === timeUnitEnum.SECS ? 300 : selTimeUnit === timeUnitEnum.MINS ? 10 : selTimeUnit === timeUnitEnum.HOURS ? 2 : 1}}" min="1" tabindex="4" name="expiry"> <button class="mr-1" mat-stroked-button color="primary" tabindex="9" type="reset" (click)="resetData()">Clear Field</button>
<span matSuffix> {{selTimeUnit | titlecase}} </span> <button mat-flat-button color="primary" (click)="onAddInvoice(addInvoiceForm)" tabindex="10">Create Invoice</button>
</mat-form-field>
<mat-form-field fxFlex="6" fxLayoutAlign="start end" *ngIf="showDetails" [ngClass]="{'mt-2': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">
<mat-select [value]="selTimeUnit" tabindex="5" name="timeUnit" (selectionChange)="onTimeUnitChange($event)">
<mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">{{timeUnit | titlecase}}</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="16" tabindex="6" fxLayoutAlign="start center" *ngIf="showDetails">
<mat-slide-toggle color="primary" [(ngModel)]="private" matTooltip="Include routing hints for private channels" [matTooltipPosition]="'above'" name="private">Private Routing Hints</mat-slide-toggle>
</div>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="40" fxLayoutAlign="space-between start" *ngIf="showDetails" class="mt-2">
<button fxFlex="31" fxLayoutAlign="center center" mat-stroked-button color="warn" tabindex="11" type="button" (click)="onDeleteExpiredInvoices()">Delete Expired</button>
<button fxFlex="31" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="7" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="31" fxLayoutAlign="center center" mat-flat-button color="primary" (click)="onAddInvoice(addInvoiceForm)" tabindex="8">Create Invoice</button>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between stretch" *ngIf="!showDetails" class="mt-1">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="9" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" (click)="onAddInvoice(addInvoiceForm)" tabindex="10">Create Invoice</button>
</div> </div>
</form> </form>
<div fxLayout="row" *ngIf="showDetails">
<button class="mr-1" mat-stroked-button color="warn" tabindex="7" type="button" (click)="onDeleteExpiredInvoices()">Delete Expired</button>
<button mat-flat-button color="primary" (click)="openCreateInvoiceModal()" tabindex="8">Create Invoice</button>
</div>
<div fxLayout="column" fxLayoutAlign="start stretch" [ngClass]="{'display-none': !showDetails}"> <div fxLayout="column" fxLayoutAlign="start stretch" [ngClass]="{'display-none': !showDetails}">
<div fxLayout="column" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="padding-gap-x page-sub-title-container mt-2"> <div fxLayout="column" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="padding-gap-x page-sub-title-container mt-2">
<div fxFlex="70"> <div fxFlex="70">

@ -17,6 +17,7 @@ import { newlyAddedRowAnimation } from '../../../shared/animation/row-animation'
import { RTLEffects } from '../../../store/rtl.effects'; import { RTLEffects } from '../../../store/rtl.effects';
import * as RTLActions from '../../../store/rtl.actions'; import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers'; import * as fromRTLReducer from '../../../store/rtl.reducers';
import { CLCreateInvoiceComponent } from '../create-invoice-modal/create-invoice.component';
@Component({ @Component({
selector: 'rtl-cl-lightning-invoices', selector: 'rtl-cl-lightning-invoices',
@ -105,6 +106,13 @@ export class CLLightningInvoicesComponent implements OnInit, OnDestroy {
} }
openCreateInvoiceModal() {
this.store.dispatch(new RTLActions.OpenAlert({ data: {
pageSize: this.pageSize,
component: CLCreateInvoiceComponent
}}));
}
onAddInvoice(form: any) { onAddInvoice(form: any) {
if(!this.invoiceValue) { return true; } if(!this.invoiceValue) { return true; }
let expiryInSecs = (this.expiry ? this.expiry : 3600); let expiryInSecs = (this.expiry ? this.expiry : 3600);

@ -1,17 +1,16 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap"> <div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap">
<form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" #sendPaymentForm="ngForm"> <form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" #sendPaymentForm="ngForm">
<mat-form-field fxFlex="100"> <mat-form-field fxFlex="100" *ngIf="!showDetails">
<input matInput placeholder="Payment Request" name="paymentRequest" [(ngModel)]="paymentRequest" tabindex="1" (keyup)="onPaymentRequestEntry()" required #paymentReq="ngModel"> <textarea matInput placeholder="Payment Request" name="paymentRequest" tabindex="1" [ngModel]="paymentRequest" (ngModelChange)="onPaymentRequestEntry($event)" required (matTextareaAutosize)="true" #paymentReq="ngModel"></textarea>
<mat-hint *ngIf="paymentRequest && paymentDecodedHint !== ''">{{paymentDecodedHint}}</mat-hint> <mat-hint *ngIf="paymentRequest && paymentDecodedHint !== ''">{{paymentDecodedHint}}</mat-hint>
<mat-error *ngIf="!paymentRequest">Payment request is required.</mat-error> <mat-error *ngIf="!paymentRequest">Payment request is required.</mat-error>
</mat-form-field> </mat-form-field>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" *ngIf="showDetails" class="mt-2"> <div fxLayout="row" *ngIf="!showDetails" class="mt-1">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Field</button> <button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" (click)="onSendPayment();" tabindex="3">Send Payment</button> <button mat-flat-button color="primary" (click)="onSendPayment()" tabindex="3">Send Payment</button>
</div> </div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between stretch" *ngIf="!showDetails" class="mt-1"> <div fxLayout="row" *ngIf="showDetails" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Field</button> <button mat-flat-button color="primary" (click)="openSendPaymentModal()" tabindex="3">Send Payment</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" (click)="onSendPayment();" tabindex="3">Send Payment</button>
</div> </div>
</form> </form>
<div fxLayout="column" fxLayoutAlign="start stretch" [ngClass]="{'display-none': !showDetails}"> <div fxLayout="column" fxLayoutAlign="start stretch" [ngClass]="{'display-none': !showDetails}">

@ -12,11 +12,12 @@ import { LoggerService } from '../../../shared/services/logger.service';
import { CommonService } from '../../../shared/services/common.service'; import { CommonService } from '../../../shared/services/common.service';
import { newlyAddedRowAnimation } from '../../../shared/animation/row-animation'; import { newlyAddedRowAnimation } from '../../../shared/animation/row-animation';
import { CLLightningSendPaymentsComponent } from '../send-payment-modal/send-payment.component';
import { SelNodeChild } from '../../../shared/models/RTLconfig';
import { CLEffects } from '../../store/cl.effects'; import { CLEffects } from '../../store/cl.effects';
import { RTLEffects } from '../../../store/rtl.effects'; import { RTLEffects } from '../../../store/rtl.effects';
import * as RTLActions from '../../../store/rtl.actions'; import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers'; import * as fromRTLReducer from '../../../store/rtl.reducers';
import { SelNodeChild } from '../../../shared/models/RTLconfig';
@Component({ @Component({
selector: 'rtl-cl-lightning-payments', selector: 'rtl-cl-lightning-payments',
@ -51,7 +52,7 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
public screenSizeEnum = ScreenSizeEnum; public screenSizeEnum = ScreenSizeEnum;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()]; private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private lndEffects: CLEffects, private decimalPipe: DecimalPipe, private titleCasePipe: TitleCasePipe) { constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private clEffects: CLEffects, private decimalPipe: DecimalPipe, private titleCasePipe: TitleCasePipe) {
this.screenSize = this.commonService.getScreenSize(); this.screenSize = this.commonService.getScreenSize();
if(this.screenSize === ScreenSizeEnum.XS) { if(this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false; this.flgSticky = false;
@ -100,8 +101,8 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
this.sendPayment(); this.sendPayment();
} else { } else {
this.store.dispatch(new RTLActions.OpenSpinner('Decoding Payment...')); this.store.dispatch(new RTLActions.OpenSpinner('Decoding Payment...'));
this.store.dispatch(new RTLActions.DecodePaymentCL(this.paymentRequest)); this.store.dispatch(new RTLActions.DecodePaymentCL({routeParam: this.paymentRequest, fromDialog: false}));
this.lndEffects.setDecodedPaymentCL this.clEffects.setDecodedPaymentCL
.pipe(take(1)) .pipe(take(1))
.subscribe(decodedPayment => { .subscribe(decodedPayment => {
this.paymentDecoded = decodedPayment; this.paymentDecoded = decodedPayment;
@ -149,7 +150,7 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
if (confirmRes) { if (confirmRes) {
this.paymentDecoded.msatoshi = confirmRes[0].inputValue; this.paymentDecoded.msatoshi = confirmRes[0].inputValue;
this.store.dispatch(new RTLActions.OpenSpinner('Sending Payment...')); this.store.dispatch(new RTLActions.OpenSpinner('Sending Payment...'));
this.store.dispatch(new RTLActions.SendPaymentCL({invoice: this.paymentRequest, amount: confirmRes[0].inputValue*1000})); this.store.dispatch(new RTLActions.SendPaymentCL({invoice: this.paymentRequest, amount: confirmRes[0].inputValue*1000, fromDialog: false}));
this.resetData(); this.resetData();
} }
}); });
@ -175,19 +176,20 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
.subscribe(confirmRes => { .subscribe(confirmRes => {
if (confirmRes) { if (confirmRes) {
this.store.dispatch(new RTLActions.OpenSpinner('Sending Payment...')); this.store.dispatch(new RTLActions.OpenSpinner('Sending Payment...'));
this.store.dispatch(new RTLActions.SendPaymentCL({invoice: this.paymentRequest})); this.store.dispatch(new RTLActions.SendPaymentCL({invoice: this.paymentRequest, fromDialog: false}));
this.resetData(); this.resetData();
} }
}); });
} }
} }
onPaymentRequestEntry() { onPaymentRequestEntry(event: any) {
this.paymentRequest = event;
this.paymentDecodedHint = ''; this.paymentDecodedHint = '';
if(this.paymentRequest.length > 100) { if(this.paymentRequest && this.paymentRequest.length > 100) {
this.store.dispatch(new RTLActions.OpenSpinner('Decoding Payment...')); this.store.dispatch(new RTLActions.OpenSpinner('Decoding Payment...'));
this.store.dispatch(new RTLActions.DecodePaymentCL(this.paymentRequest)); this.store.dispatch(new RTLActions.DecodePaymentCL({routeParam: this.paymentRequest, fromDialog: false}));
this.lndEffects.setDecodedPaymentCL.subscribe(decodedPayment => { this.clEffects.setDecodedPaymentCL.subscribe(decodedPayment => {
this.paymentDecoded = decodedPayment; this.paymentDecoded = decodedPayment;
if(this.paymentDecoded.msatoshi) { if(this.paymentDecoded.msatoshi) {
this.commonService.convertCurrency(+this.paymentDecoded.msatoshi, CurrencyUnitEnum.SATS, this.selNode.currencyUnits[2], this.selNode.fiatConversion) this.commonService.convertCurrency(+this.paymentDecoded.msatoshi, CurrencyUnitEnum.SATS, this.selNode.currencyUnits[2], this.selNode.fiatConversion)
@ -206,6 +208,12 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
} }
} }
openSendPaymentModal() {
this.store.dispatch(new RTLActions.OpenAlert({ data: {
component: CLLightningSendPaymentsComponent
}}));
}
resetData() { resetData() {
this.paymentDecoded = {}; this.paymentDecoded = {};
this.paymentRequest = ''; this.paymentRequest = '';

@ -12,9 +12,9 @@
<input matInput placeholder="Amount (Sats)" name="amount" [(ngModel)]="amount" tabindex="2" type="number" step="1000" min="0" required #destAmount="ngModel"> <input matInput placeholder="Amount (Sats)" name="amount" [(ngModel)]="amount" tabindex="2" type="number" step="1000" min="0" required #destAmount="ngModel">
<mat-error *ngIf="!amount">Amount is required.</mat-error> <mat-error *ngIf="!amount">Amount is required.</mat-error>
</mat-form-field> </mat-form-field>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2"> <div fxLayout="row" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="resetData()">Clear</button> <button class="mr-1" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="resetData()">Clear</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" type="submit" tabindex="4">Query Route</button> <button mat-flat-button color="primary" type="submit" tabindex="4">Query Route</button>
</div> </div>
</form> </form>
<div fxLayout="row" fxLayoutAlign="start center" class="padding-gap-x page-sub-title-container mt-2 mb-1"> <div fxLayout="row" fxLayoutAlign="start center" class="padding-gap-x page-sub-title-container mt-2 mb-1">

@ -0,0 +1,33 @@
<div fxLayout="row">
<div fxFlex="100" class="padding-gap-large">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="95" fxLayoutAlign="start start">
<span class="page-title">Send Payment</span>
</div>
<button tabindex="8" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" [mat-dialog-close]="false" default mat-button>X</button>
</mat-card-header>
<mat-card-content class="mt-5px">
<form fxLayoutAlign="space-between stretch" fxLayout="column" #sendPaymentForm="ngForm">
<mat-form-field fxFlex="100">
<textarea autoFocus matInput placeholder="Payment Request" name="paymentRequest" tabindex="1" [ngModel]="paymentRequest" (ngModelChange)="onPaymentRequestEntry($event)" (matTextareaAutosize)="true" required #paymentReq="ngModel"></textarea>
<mat-hint *ngIf="paymentRequest && paymentDecodedHint !== ''">{{paymentDecodedHint}}</mat-hint>
<mat-error *ngIf="!paymentRequest">Payment request is required.</mat-error>
<mat-error *ngIf="paymentReq.errors?.decodeError">{{paymentDecodedHint}}</mat-error>
</mat-form-field>
<mat-form-field fxFlex="100" *ngIf="zeroAmtInvoice">
<input matInput placeholder="Amount (Sats)" name="amount" [(ngModel)]="paymentAmount" (change)="onAmountChange($event)" tabindex="2" required #paymentAmt="ngModel">
<mat-hint>It is a zero amount invoice, enter amount to be paid.</mat-hint>
<mat-error *ngIf="!paymentAmount">Payment amount is required.</mat-error>
</mat-form-field>
<div fxFlex="100" class="alert alert-danger mt-1" *ngIf="paymentError !== ''">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span *ngIf="paymentError !== ''">{{paymentError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="end center">
<button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Fields</button>
<button mat-flat-button color="primary" (click)="onSendPayment();" tabindex="3">Send Payment</button>
</div>
</form>
</mat-card-content>
</div>
</div>

@ -0,0 +1,10 @@
.mat-column-actions {
min-height: 4.8rem;
}
.mat-column-payment_hash {
flex: 1 1 20%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save