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.
@angular/material/autocomplete
@angular/material/button
@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">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="styles.807ae95794a05be97fba.css"></head>
<link rel="stylesheet" href="styles.da5d02955b06fbd16881.css"></head>
<body>
<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>

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_sso = 0;
common.port = 3000;
common.host = 'localhost';
common.rtl_cookie_path = '';
common.logout_redirect_link = '/login';
common.cookie = '';
@ -115,7 +116,22 @@ common.sortAscByKey = (array, key) => {
common.sortDescByKey = (array, key) => {
const temp = array.sort(function (a, b) {
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;
}

@ -42,6 +42,7 @@ connect.setDefaultConfig = () => {
return {
multiPass: "password",
port: "3000",
host: "localhost",
defaultNodeIndex: 1,
SSO: {
rtlSSO: 0,
@ -99,7 +100,7 @@ connect.replacePasswordWithHash = (multiPassHashed) => {
}
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) {
common.rtl_pass = config.multiPassHashed;
} else if (config.multiPass !== '' && config.multiPass) {
@ -110,6 +111,7 @@ connect.validateNodeConfig = (config) => {
common.rtl_secret2fa = config.secret2fa;
}
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) {
config.nodes.forEach((node, idx) => {
common.nodes[idx] = {};
@ -286,6 +288,7 @@ connect.logEnvVariables = () => {
common.nodes.forEach((node, idx) => {
if (!node.enable_logging) { return; }
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: 'SSO: ' + common.rtl_sso, 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 = {}; }
var newConfig = {
port: config.port ? config.port : 3000,
host: config.host ? config.host : 'localhost',
defaultNodeIndex: config.defaultNodeIndex ? config.defaultNodeIndex : 1,
SSO: {
rtlSSO: config.SSO.rtlSSO ? config.SSO.rtlSSO : 0,
@ -414,6 +418,7 @@ connect.modifyIniSingleNodeConfig = (confFileFullPath) => {
if (!config.Settings) { config.Settings = {}; }
var newConfig = {
port: config.Settings.port ? config.Settings.port : 3000,
host: 'localhost',
defaultNodeIndex: 1,
SSO: {
rtlSSO: config.SSO.rtlSSO ? config.SSO.rtlSSO : 0,

@ -19,10 +19,10 @@ exports.getRTLConfig = (req, res, next) => {
fs.readFile(confFile, 'utf8', function(err, data) {
if (err) {
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: [] });
} 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({
message: "Reading Node Config Failed!",
error: err
@ -98,7 +98,7 @@ exports.updateUISettings = (req, res, next) => {
res.status(201).json({message: 'Application Node Settings Updated Successfully'});
}
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({
message: "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});
}
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({
message: "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'});
}
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({
message: "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});
fs.readFile(confFile, 'utf8', function(err, data) {
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({
message: "Reading Config File Failed!",
error: err
@ -207,7 +207,7 @@ exports.getCurrencyRates = (req, res, next) => {
}
})
.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({
message: "Fetching Rates Failed!",
error: err.error

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

@ -7,7 +7,7 @@ exports.getBalance = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getBalance';
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) {
body.totalBalance = 0;
body.btc_totalBalance = 0;
@ -26,10 +26,17 @@ exports.getBalance = (req, res, next) => {
} else {
body.btc_unconfBalance = common.convertToBTC(body.unconfBalance);
}
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({
message: "Fetching balance failed!",
error: err.error

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

@ -9,6 +9,7 @@ exports.getFees = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Fees', msg: 'Fee Received: ' + JSON.stringify(body)});
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({
message: "Fetching fee failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -23,7 +24,15 @@ exports.getFees = (req, res, next) => {
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({
message: "Fetching fee failed!",
error: err.error

@ -8,48 +8,65 @@ exports.getInfo = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getinfo';
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});
request(options).then((body) => {
logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)});
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
if(!body || search_idx > -1 || body.error) {
res.status(500).json({
message: "Fetching Info failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
});
} 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);
logger.info({fileName: 'GetInfo', msg: 'Calling Info from C-Lightning server url: ' + options.url});
if (!options.headers || !options.headers.macaroon) {
logger.error({fileName: 'GetInfo', lineNum: 13, msg: 'C-Lightning Get info failed due to bad or missing macaroon!'});
res.status(502).json({
message: "Fetching Info Failed!",
error: "Bad Macaroon"
});
} else {
request(options).then((body) => {
logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)});
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'GetInfo', lineNum: 24, msg: 'Get Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({
message: "Fetching Info failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
});
} 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(function (err) {
return res.status(500).json({
message: "Fetching Info failed!",
error: err.error
})
.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: '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) => {
logger.info({fileName: 'Invoice', msg: 'Invoices Deleted: ' + JSON.stringify(body)});
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({
message: "Deleting Invoice Failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -17,7 +18,15 @@ exports.deleteExpiredInvoice = (req, res, next) => {
}
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({
message: "Deleting Invoice Failed!",
error: err.error
@ -32,6 +41,7 @@ exports.listInvoices = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Invoice', msg: 'Invoices List Received: ' + body});
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({
message: "Fetching Invoice Info failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -48,7 +58,15 @@ exports.listInvoices = (req, res, next) => {
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({
message: "Fetching Invoice Info failed!",
error: err.error
@ -63,6 +81,7 @@ exports.addInvoice = (req, res, next) => {
request.post(options).then((body) => {
logger.info({fileName: 'Invoice', msg: 'Add Invoice Responce: ' + JSON.stringify(body)});
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({
message: "Add Invoice Failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -71,7 +90,15 @@ exports.addInvoice = (req, res, next) => {
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({
message: "Add Invoice Failed!",
error: err.error

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

@ -9,6 +9,7 @@ exports.getRoute = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Network', msg: 'Query Routes Received: ' + JSON.stringify(body)});
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({
message: "Fetching Query Routes Failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -16,7 +17,15 @@ exports.getRoute = (req, res, next) => {
}
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({
message: "Fetching Query Routes Failed!",
error: err.error
@ -34,7 +43,15 @@ exports.listNode = (req, res, next) => {
});
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({
message: "Node Lookup Failed!",
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) : '';
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({
message: "Channel Lookup Failed!",
error: err.error
@ -65,7 +90,15 @@ exports.feeRates = (req, res, next) => {
request(options).then(function (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({
message: "Fee Rates Failed!",
error: err.error

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

@ -9,6 +9,7 @@ exports.listPayments = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Payments', msg: 'Payment List Received: ' + JSON.stringify(body.payments)});
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({
message: "Payments List Failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -23,7 +24,15 @@ exports.listPayments = (req, res, next) => {
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({
message: "Payments List Failed!",
error: err.error
@ -37,6 +46,7 @@ exports.decodePayment = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Payments', msg: 'Payment Decode Received: ' + JSON.stringify(body)});
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({
message: "Payment Request Decode Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -47,7 +57,15 @@ exports.decodePayment = (req, res, next) => {
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({
message: "Payment Request Decode Failed!",
error: err.error
@ -62,6 +80,7 @@ exports.postPayment = (req, res, next) => {
request.post(options).then((body) => {
logger.info({fileName: 'Payments', msg: 'Payment Post Response: ' + JSON.stringify(body)});
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({
message: "Payment Post Failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -70,7 +89,15 @@ exports.postPayment = (req, res, next) => {
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({
message: "Payment Post Failed!",
error: err.error

@ -7,11 +7,19 @@ exports.getPeers = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/peer/listPeers';
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)});
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({
message: "Peers Fetch Failed!",
error: err.error
@ -25,6 +33,7 @@ exports.postPeer = (req, res, next) => {
options.body = req.body;
request.post(options, (error, response, body) => {
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({
message: "Adding peer failed!",
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)});
options.url = common.getSelLNServerUrl() + '/peer/listPeers';
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);
logger.info({fileName: 'Peers', msg: 'Peer with Newest On Top: ' + JSON.stringify(peers)});
logger.info({fileName: 'Peers', msg: 'Peer Added Successfully'});
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({
message: "Peer Add Failed!",
error: err.error
@ -54,6 +71,7 @@ exports.deletePeer = (req, res, next) => {
request.delete(options).then((body) => {
logger.info({fileName: 'Peers', msg: 'Detach Peer Response: ' + JSON.stringify(body)});
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({
message: "Detach peer failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -63,10 +81,18 @@ exports.deletePeer = (req, res, next) => {
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({
message: "Detach Peer Failed!",
error: err.error
});
});
};
};

@ -10,7 +10,7 @@ exports.getBalance = (req, res, next) => {
options.qs = req.query;
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)});
if( body) {
if (body) {
if (upperCase(req.params.source) === 'BLOCKCHAIN') {
if (!body.total_balance) { body.total_balance = 0; }
if (!body.confirmed_balance) { body.confirmed_balance = 0; }
@ -28,7 +28,15 @@ exports.getBalance = (req, res, next) => {
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({
message: "Fetching balance failed!",
error: err.error

@ -5,13 +5,17 @@ var options = {};
getAliasForChannel = (channel) => {
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) {
logger.info({fileName: 'Channels', msg: 'Alias: ' + JSON.stringify(aliasBody.node.alias)});
channel.remote_alias = 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');
logger.info({fileName: 'Channels', msg: 'All Channels with Alias: ' + JSON.stringify(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({
message: 'Fetching Channels Alias Failed!',
error: err.error
@ -50,8 +62,15 @@ exports.getAllChannels = (req, res, next) => {
res.status(200).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Channels', lineNum: 54, msg: 'Get All Channel: ' + JSON.stringify(err.error)});
.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: 66, msg: 'Get All Channels Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Fetching All Channels Failed!',
error: err.error
@ -64,18 +83,53 @@ exports.getPendingChannels = (req, res, next) => {
options.url = common.getSelLNServerUrl() + '/channels/pending';
options.qs = req.query;
request(options).then(function (body) {
let channels = [];
if (!body.total_limbo_balance) {
body.total_limbo_balance = 0;
body.btc_total_limbo_balance = 0;
} else {
body.btc_total_limbo_balance = common.convertToBTC(body.total_limbo_balance);
}
logger.info({fileName: 'Channels', msg: 'Pending Channels: ' + JSON.stringify(body)});
res.status(200).json(body);
const promises = [];
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) {
logger.error({fileName: 'Channels', lineNum: 78, msg: 'Get Pending Channel: ' + JSON.stringify(err.error)});
.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: 97, msg: 'Get Pending Channel Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Fetching Pending Channels Failed!',
error: err.error
@ -88,18 +142,46 @@ exports.getClosedChannels = (req, res, next) => {
options.url = common.getSelLNServerUrl() + '/channels/closed';
options.qs = req.query;
request(options).then(function (body) {
let channels = [];
if (body.channels && body.channels.length > 0) {
body.channels.forEach(channel => {
channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type;
Promise.all(
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');
}
logger.info({fileName: 'Channels', msg: 'Closed Channels: ' + JSON.stringify(body)});
res.status(200).json(body);
} else {
body.channels = [];
res.status(200).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Channels', lineNum: 102, msg: 'Get Closed Channel: ' + JSON.stringify(err.error)});
.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: 126, msg: 'Get Closed Channel Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Fetching Closed Channels Failed!',
error: err.error
@ -125,6 +207,7 @@ exports.postChannel = (req, res, next) => {
request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Channel Open Response: ' + JSON.stringify(body)});
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({
message: 'Open Channel Failed!',
error: (!body) ? 'Error From Server!' : body.error
@ -133,8 +216,15 @@ exports.postChannel = (req, res, next) => {
res.status(201).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Channels', lineNum: 103, msg: 'Open Channel: ' + 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: 168, msg: 'Open New Channel Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Open Channel Failed!',
error: err.error
@ -166,11 +256,13 @@ exports.postTransactions = (req, res, next) => {
request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Send Payment Response: ' + JSON.stringify(body)});
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({
message: 'Send Payment Failed!',
error: (!body) ? 'Error From Server!' : body.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({
message: 'Send Payment Failed!',
error: (!body) ? 'Error From Server!' : body.payment_error
@ -179,8 +271,15 @@ exports.postTransactions = (req, res, next) => {
res.status(201).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Channels', lineNum: 143, msg: 'Send Payment: ' + 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: 222, msg: 'Send Payment Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Send Payment Failed!',
error: err.error
@ -199,6 +298,7 @@ exports.closeChannel = (req, res, next) => {
request.delete(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Close Channel Response: ' + JSON.stringify(body)});
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({
message: 'Close Channel Failed!',
error: (!body) ? 'Error From Server!' : body.error
@ -207,8 +307,15 @@ exports.closeChannel = (req, res, next) => {
res.status(204).json({message: 'Channel Closed!'});
}
})
.catch(function (err) {
logger.error({fileName: 'Channels', lineNum: 169, msg: 'Close Channel: ' + 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: 257, msg: 'Close Channel Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Close Channel Failed!',
error: err.error
@ -241,6 +348,7 @@ exports.postChanPolicy = (req, res, next) => {
request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Update Channel Policy: ' + JSON.stringify(body)});
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({
message: 'Update Channel Failed!',
error: (!body) ? 'Error From Server!' : body.error
@ -249,8 +357,15 @@ exports.postChanPolicy = (req, res, next) => {
res.status(201).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Channels', lineNum: 211, msg: 'Update Channel Policy: ' + 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: 306, msg: 'Update Channel Policy Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Update Channel Failed!',
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);
});
}
@ -33,12 +33,10 @@ exports.getBackup = (req, res, next) => {
if (req.params.channelPoint === 'ALL') {
channel_backup_file = common.selectedNode.channel_backup_path + common.path_separator + 'channel-all.bak';
message = 'All Channels Backup Successful.';
// message = 'All Channels Backup Successful at: ' + channel_backup_file + ' !';
options.url = common.getSelLNServerUrl() + '/channels/backup';
} else {
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 at: ' + channel_backup_file + ' !';
let channelpoint = req.params.channelPoint.replace(':', '/');
options.url = common.getSelLNServerUrl() + '/channels/backup/' + channelpoint;
let exists = fs.existsSync(channel_backup_file);
@ -49,23 +47,46 @@ exports.getBackup = (req, res, next) => {
var createStream = fs.createWriteStream(channel_backup_file);
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 });
}
}
}
request(options).then(function (body) {
logger.info({fileName: 'Channels Backup', msg: 'Channel Backup: ' + JSON.stringify(body)});
fs.writeFile(channel_backup_file, JSON.stringify(body), function(err) {
if (err) {
logger.info({fileName: 'ChannelsBackup', msg: 'Channel Backup: ' + JSON.stringify(body)});
fs.writeFile(channel_backup_file, JSON.stringify(body), function(errRes) {
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 });
} else {
res.status(200).json({ message: message });
}
});
})
.catch(function (err) {
logger.error({fileName: 'Channels Backup', lineNum: 44, msg: 'Channel Backup: ' + 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: 'ChannelsBackup', lineNum: 86, msg: 'Channel Backup Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Channels Backup Failed!',
error: err.error
@ -98,7 +119,6 @@ exports.postBackupVerify = (req, res, next) => {
}
} else {
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';
let exists = fs.existsSync(channel_verify_file);
if (exists) {
@ -111,11 +131,18 @@ exports.postBackupVerify = (req, res, next) => {
}
if (verify_backup !== '') {
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 });
})
.catch(function (err) {
logger.error({fileName: 'Channels Backup Verify', lineNum: 93, msg: 'Channel Backup Verify: ' + 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: 'BackupVerify', lineNum: 141, msg: 'Channel Backup Verify Error: ' + JSON.stringify(err)});
return res.status(404).json({
message: 'Channel backup to Verify failed!',
error: err.error
@ -148,7 +175,6 @@ exports.postRestore = (req, res, next) => {
}
} else {
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';
let exists = fs.existsSync(channel_restore_file);
if (exists) {
@ -161,10 +187,11 @@ exports.postRestore = (req, res, next) => {
}
if (restore_backup !== '') {
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', () => {
getFilesList(getFilesListRes => {
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 });
} else {
return res.status(201).json({ message: message, list: getFilesListRes });
@ -172,8 +199,15 @@ exports.postRestore = (req, res, next) => {
});
});
})
.catch(function (err) {
logger.error({fileName: 'Channels Backup Restore', lineNum: 143, msg: 'Channel Backup Restore: ' + 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: 'ChannelRestore', lineNum: 205, msg: 'Channel Restore Error: ' + JSON.stringify(err)});
return res.status(404).json({
message: 'Channel restore failed!',
error: err.error.error

@ -10,6 +10,7 @@ exports.getFees = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Fees', msg: 'Fee Received: ' + JSON.stringify(body)});
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({
message: "Fetching fee failed!",
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.monthly_tx_count = history.forwarding_events && history.forwarding_events.length ? history.forwarding_events.length : 0;
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);
})
}
})
.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({
message: "Fetching fee failed!",
error: err.error

@ -9,6 +9,7 @@ exports.getInfo = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getinfo';
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']) {
logger.error({fileName: 'GetInfo', lineNum: 17, msg: 'LND Get info failed due to bad or missing macaroon!'});
res.status(502).json({
@ -17,12 +18,12 @@ exports.getInfo = (req, res, next) => {
});
} else {
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) => {
logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)});
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
if(!body || search_idx > -1 || body.error) {
logger.error({fileName: 'GetInfo', lineNum: 26, msg: 'Get Info Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({
message: "Fetching Info Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -31,7 +32,15 @@ exports.getInfo = (req, res, next) => {
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({
message: "Fetching Info Failed!",
error: err.error

@ -24,6 +24,7 @@ exports.getDescribeGraph = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'Graph', msg: 'Describe Graph Received: ' + body_str});
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({
message: "Fetching Describe Graph Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -32,7 +33,15 @@ exports.getDescribeGraph = (req, res, next) => {
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({
message: "Fetching Describe Graph Failed!",
error: err.error
@ -48,6 +57,7 @@ exports.getGraphInfo = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'Graph', msg: 'Network Info Received: ' + body_str});
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({
message: "Fetching network Info failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -61,7 +71,15 @@ exports.getGraphInfo = (req, res, next) => {
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({
message: "Fetching network Info failed!",
error: err.error
@ -75,17 +93,26 @@ exports.getGraphNode = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Graph', msg: 'Node Info Received: ' + JSON.stringify(body)});
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({
message: "Fetching node Info failed!",
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);
}
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({
message: "Fetching node Info failed!",
error: err.error
@ -99,17 +126,26 @@ exports.getGraphEdge = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Graph', msg: 'Edge Info Received: ' + JSON.stringify(body)});
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({
message: "Fetching Edge Info Failed!",
error: (!body) ? 'Error From Server!' : body.error
});
}
if ( body) {
if (body) {
body.last_update_str = (!body.last_update) ? '' : common.convertTimestampToDate(body.last_update);
}
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({
message: "Fetching Edge Info Failed!",
error: err.error
@ -127,12 +163,13 @@ exports.getQueryRoutes = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Graph', msg: 'Query Routes Received: ' + JSON.stringify(body)});
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({
message: "Fetching Query Routes Failed!",
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 => {
if ( route.hops) {
Promise.all(
@ -144,7 +181,16 @@ exports.getQueryRoutes = (req, res, next) => {
.then(function(values) {
logger.info({fileName: 'Graph', msg: 'Hops with Alias: ' + JSON.stringify(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({
message: "Fetching Query Routes Failed!",
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({
message: "Fetching Query Routes Failed!",
error: err.error
@ -168,15 +222,15 @@ exports.getRemoteFeePolicy = (req, res, next) => {
request(options).then((body) => {
logger.info({fileName: 'Graph', msg: 'Edge Info Received: ' + JSON.stringify(body)});
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({
message: "Fetching Edge Info Failed!",
error: (!body) ? 'Error From Server!' : body.error
});
}
if ( body) {
if (body) {
body.last_update_str = (!body.last_update) ? '' : common.convertTimestampToDate(body.last_update);
}
remoteNodeFee = {};
if(body.node1_pub === req.params.localPubkey){
remoteNodeFee = {
@ -184,21 +238,27 @@ exports.getRemoteFeePolicy = (req, res, next) => {
fee_base_msat: body.node2_policy.fee_base_msat,
fee_rate_milli_msat: body.node2_policy.fee_rate_milli_msat
};
}
else if(body.node2_pub === req.params.localPubkey) {
} else if(body.node2_pub === req.params.localPubkey) {
remoteNodeFee = {
time_lock_delta: body.node1_policy.time_lock_delta,
fee_base_msat: body.node1_policy.fee_base_msat,
fee_rate_milli_msat: body.node1_policy.fee_rate_milli_msat
};
}
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({
message: "Fetching Edge Info Failed!",
error: err.error
});
});
};
};

@ -9,6 +9,7 @@ exports.getGraphInfo = (req, res, next) => {
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
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({
message: "Fetching network Info failed!",
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);
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) => {
logger.info({fileName: 'Invoice', msg: 'Invoice Info Received: ' + JSON.stringify(body)});
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({
message: "Fetching Invoice Info Failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -16,7 +17,15 @@ exports.getInvoice = (req, res, next) => {
}
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({
message: "Fetching Invoice Info Failed!",
error: err.error
@ -33,6 +42,7 @@ exports.listInvoices = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'Invoice', msg: 'Invoices List Received: ' + body_str});
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({
message: "Fetching Invoice Info failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -51,9 +61,17 @@ exports.listInvoices = (req, res, next) => {
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({
message: "Fetching Invoice Info failed!",
message: "Fetching Invoices failed!",
error: err.error
});
});
@ -76,6 +94,7 @@ exports.addInvoice = (req, res, next) => {
request.post(options).then((body) => {
logger.info({fileName: 'Invoice', msg: 'Add Invoice Responce: ' + JSON.stringify(body)});
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({
message: "Add Invoice Failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -84,7 +103,15 @@ exports.addInvoice = (req, res, next) => {
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({
message: "Add Invoice Failed!",
error: err.error

@ -1,8 +1,16 @@
var fs = require('fs');
exports.getLNDSettings = (req, res, next) => {
fs.readFile(req.headers.filepath, function(err, data) {
if (err) {
fs.readFile(req.headers.filepath, function(errRes, data) {
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({
message: "Reading Config File Failed!",
error: err

@ -25,6 +25,7 @@ exports.loopOut = (req, res, next) => {
request.post(options).then(function (body) {
logger.info({fileName: 'Loop', msg: 'Loop Out: ' + JSON.stringify(body)});
if(!body || body.error) {
logger.error({fileName: 'Loop', lineNum: 28, msg: 'Loop Out Error: ' + JSON.stringify(body.error)});
res.status(500).json({
message: 'Loop Out Failed!',
error: (!body) ? 'Error From Server!' : body.error.message
@ -33,11 +34,18 @@ exports.loopOut = (req, res, next) => {
res.status(201).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Loop Out', lineNum: 33, msg: 'Loop Out Failed: ' + JSON.stringify(err.error)});
.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: 44, msg: 'Loop Out Error: ' + JSON.stringify(err)});
return res.status(500).json({
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)});
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({
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') : '';
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({
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])});
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({
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({
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,
max_swap_fee: req.body.swapFee,
max_miner_fee: req.body.minerFee
// last_hop: req.body.lastHop,
// external_htlc: req.body.externalHtlc
});
request.post(options).then(function (body) {
logger.info({fileName: 'Loop', msg: 'Loop In: ' + JSON.stringify(body)});
if(!body || body.error) {
logger.error({fileName: 'Loop', lineNum: 166, msg: 'Loop In Error: ' + JSON.stringify(body.error)});
res.status(500).json({
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 {
res.status(201).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Loop In', lineNum: 134, msg: 'Loop In Failed: ' + JSON.stringify(err.error)});
.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: 182, msg: 'Loop In Error: ' + JSON.stringify(err)});
return res.status(500).json({
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)});
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({
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') : '';
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({
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])});
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({
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({
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);
})
.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({
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));
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({
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) => {
logger.info({fileName: 'Messages', msg: 'Message Signed: ' + JSON.stringify(body)});
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({
message: "Sign message failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -20,8 +21,15 @@ exports.signMessage = (req, res, next) => {
res.status(201).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Messages', lineNum: 24, msg: 'Sign Message 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: 'Messages', lineNum: 31, msg: 'Sign Message Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Sign Message Failed!',
error: err.error
@ -39,6 +47,7 @@ exports.verifyMessage = (req, res, next) => {
request.post(options, (error, response, body) => {
logger.info({fileName: 'Messages', msg: 'Message Verified: ' + JSON.stringify(body)});
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({
message: "Verify message failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -47,8 +56,15 @@ exports.verifyMessage = (req, res, next) => {
res.status(201).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Messages', lineNum: 24, msg: 'Message Verification 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: 'Messages', lineNum: 65, msg: 'Message Verification Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Verify Message Failed!',
error: err.error

@ -11,15 +11,24 @@ exports.getNewAddress = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'NewAddress', msg: 'New Address Received: ' + body_str});
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({
message: "Fetching new address failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
});
} 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({
message: "Fetching new address failed!",
error: err.error

@ -11,6 +11,7 @@ exports.decodePayment = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'PayReq', msg: 'Payment Decode Received: ' + body_str});
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({
message: "Payment Request Decode Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
@ -21,7 +22,15 @@ exports.decodePayment = (req, res, next) => {
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({
message: "Payment Request Decode Failed!",
error: err.error

@ -11,6 +11,7 @@ exports.getPayments = (req, res, next) => {
const search_idx = (!body) ? -1 : body_str.search('Not Found');
logger.info({fileName: 'Payments', msg: 'Payment Decoded Received: ' + body_str});
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({
message: "Payments List Failed!",
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);
}
})
.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({
message: "Payments List Failed!",
error: err.error

@ -12,7 +12,10 @@ getAliasForPeers = (peer) => {
peer.alias = 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) {
logger.info({fileName: 'Peers', msg: 'Peers with Alias before Sort: ' + JSON.stringify(body)});
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)});
res.status(200).json(body.peers);
});
})
.catch((err) => {
logger.error({fileName: 'Peers', lineNum: 39, msg: 'Peers List 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: 'Peers', lineNum: 39, msg: 'List Peers Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: "Peers List Failed!",
error: err.error
@ -54,6 +64,7 @@ exports.postPeer = (req, res, next) => {
request.post(options, (error, response, body) => {
logger.info({fileName: 'Peers', msg: 'Peer Added: ' + JSON.stringify(body)});
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({
message: "Adding peer failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -67,8 +78,8 @@ exports.postPeer = (req, res, next) => {
return getAliasForPeers(peer);
}))
.then(function(values) {
if ( body.peers) {
body.peers = common.sortDescByKey(body.peers, 'alias');
if (body.peers) {
body.peers = common.sortDescByStrKey(body.peers, 'alias');
logger.info({fileName: 'Peers', msg: 'Peer with Alias: ' + JSON.stringify(body)});
body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey);
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'});
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({
message: "Peer Add Failed!",
error: err.error
@ -93,6 +112,7 @@ exports.deletePeer = (req, res, next) => {
request.delete(options).then((body) => {
logger.info({fileName: 'Peers', msg: 'Detach Peer Response: ' + JSON.stringify(body)});
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({
message: "Detach peer failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -102,7 +122,15 @@ exports.deletePeer = (req, res, next) => {
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({
message: "Detach Peer Failed!",
error: err.error

@ -44,8 +44,16 @@ exports.getAllForwardingEvents = (start, end, offset, callback) => {
} else {
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({
message: "Forwarding Events Failed!",
error: err.error

@ -10,13 +10,14 @@ exports.getTransactions = (req, res, next) => {
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
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({
message: "Fetching Transactions Failed!",
error: (!body || search_idx > -1) ? 'Error From Server!' : body.error
});
} else {
if ( body.transactions && body.transactions.length > 0) {
if (body.transactions && body.transactions.length > 0) {
body.transactions.forEach(transaction => {
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);
}
})
.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({
message: "Fetching Transactions Failed!",
error: err.error
@ -49,6 +58,7 @@ exports.postTransactions = (req, res, next) => {
request.post(options).then((body) => {
logger.info({fileName: 'Transactions', msg: 'Transaction Post Response: ' + JSON.stringify(body)});
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({
message: "Transactions post failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -57,7 +67,15 @@ exports.postTransactions = (req, res, next) => {
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({
message: "Transactions post failed!",
error: err.error

@ -13,6 +13,7 @@ exports.genSeed = (req, res, next) => {
}
request(options).then((body) => {
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({
message: "Genseed failed!",
error: (!body) ? 'Error From Server!' : body.error
@ -21,7 +22,15 @@ exports.genSeed = (req, res, next) => {
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({
message: "Genseed failed!",
error: err.error
@ -59,11 +68,13 @@ exports.operateWallet = (req, res, next) => {
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
if(!body) {
logger.error({fileName: 'Wallet', lineNum: 70, msg: 'Wallet Error: ' + ((error) ? JSON.stringify(error) : err_message)});
res.status(500).json({
message: err_message,
error: (error) ? error : err_message
});
} else if(search_idx > -1) {
logger.error({fileName: 'Wallet', lineNum: 76, msg: 'Wallet Error: ' + err_message});
res.status(500).json({
message: 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')) {
res.status(201).json('Successful');
} else {
logger.error({fileName: 'Wallet', lineNum: 85, msg: 'Wallet Error: ' + JSON.stringify(body.error)});
res.status(500).json({
message: err_message,
error: body.error
@ -80,20 +92,28 @@ exports.operateWallet = (req, res, next) => {
} else {
res.status(201).json('Successful');
}
}).catch(error => {
logger.error({fileName: 'Wallet', lineNum: 83, msg: 'Wallet Response: ' + JSON.stringify(error.error)});
if((error.error.code === 1 && error.error.error === 'context canceled') || (error.error.code === 14 && error.error.error === 'transport is closing')) {
})
.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: 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');
} else {
res.status(500).json({
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) => {
let response = common.updateSelectedNodeOptions();
res.status(response.status).json({updateMessage: response.message});
let response = common.updateSelectedNodeOptions();
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>",
"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>,
"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>,
@ -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.
;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)
HOST (host for the rtl node server, default localhost, Optional)
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)
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'>",
"port": "3000",
"host": "localhost",
"defaultNodeIndex": 1,
"SSO": {
"rtlSSO": 0,

2
package-lock.json generated

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

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

@ -31,11 +31,11 @@ const onListening = () => {
const addr = server.address();
const bind = typeof addr === "string" ? "pipe " + addr : "port " + common.port;
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);
server.on("error", onError);
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(() => {
if (this.sessionService.getItem('token')) {
this.logger.warn('Time limit exceeded for session inactivity.');
this.store.dispatch(new RTLActions.CloseAlert());
this.store.dispatch(new RTLActions.CloseConfirmation(false));
this.store.dispatch(new RTLActions.CloseAllDialogs());
this.store.dispatch(new RTLActions.OpenAlert({ data: {
type: AlertTypeEnum.WARNING,
alertTitle: 'Logging out',

@ -30,11 +30,21 @@ import { LNDEffects } from './lnd/store/lnd.effects';
import { CLEffects } from './clightning/store/cl.effects';
import { LayoutModule } from '@angular/cdk/layout';
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 { 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 { 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 { 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 { 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';
@ -65,7 +75,15 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
ChannelRebalanceComponent,
OnChainGeneratedAddressComponent,
CLOpenChannelComponent,
CLConnectPeerComponent,
CLLightningSendPaymentsComponent,
CLCreateInvoiceComponent,
CLOnChainSendComponent,
CLChannelInformationComponent,
OpenChannelComponent,
ChannelInformationComponent,
LightningSendPaymentsComponent,
ConnectPeerComponent,
ShowPubkeyComponent,
SpinnerDialogComponent,
AlertMessageComponent,
@ -74,7 +92,9 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
CloseChannelComponent,
LoopModalComponent,
TwoFactorAuthComponent,
LoginTokenComponent
LoginTokenComponent,
CreateInvoiceComponent,
OnChainSendComponent
],
entryComponents: [
CLInvoiceInformationComponent,
@ -82,7 +102,15 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
ChannelRebalanceComponent,
OnChainGeneratedAddressComponent,
CLOpenChannelComponent,
CLConnectPeerComponent,
CLLightningSendPaymentsComponent,
CLCreateInvoiceComponent,
CLOnChainSendComponent,
CLChannelInformationComponent,
OpenChannelComponent,
ChannelInformationComponent,
LightningSendPaymentsComponent,
ConnectPeerComponent,
ShowPubkeyComponent,
SpinnerDialogComponent,
AlertMessageComponent,
@ -91,7 +119,9 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
CloseChannelComponent,
LoopModalComponent,
TwoFactorAuthComponent,
LoginTokenComponent
LoginTokenComponent,
CreateInvoiceComponent,
OnChainSendComponent
],
providers: [
{ 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 { CLPeersComponent } from './peers-channels/peers/peers.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 { CLOnChainComponent } from './on-chain/on-chain.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 { CLLookupsComponent } from './lookups/lookups.component';
import { CLRoutingComponent } from './routing/routing.component';
@ -52,7 +50,6 @@ import { CLUnlockedGuard } from '../shared/services/auth.guard';
CLPeersChannelsComponent,
CLLightningInvoicesComponent,
CLLightningPaymentsComponent,
CLChannelManageComponent,
CLTransactionsComponent,
CLLookupsComponent,
CLRoutingComponent,
@ -61,7 +58,6 @@ import { CLUnlockedGuard } from '../shared/services/auth.guard';
CLChannelLookupComponent,
CLNodeLookupComponent,
CLQueryRoutesComponent,
CLOnChainSendComponent,
CLOnChainReceiveComponent,
CLOnChainComponent,
CLChannelsTablesComponent,

@ -32,6 +32,6 @@
<ng-template #noChannelBlock>
<div fxLayout="row" fxFlex="10" fxLayoutAlign="space-between center" class="w-100 mt-1">
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>
</ng-template>

@ -22,6 +22,6 @@
<ng-template #noChannelBlock>
<div fxLayout="row" fxFlex="10" fxLayoutAlign="space-between center" class="w-100 mt-1">
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>
</ng-template>

@ -18,9 +18,9 @@
<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-form-field>
<div fxFlex="30" fxLayoutAlign="space-between stretch" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" 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>
<div fxLayout="row" class="mt-2">
<button class="mr-1" mat-stroked-button color="primary" tabindex="3" type="button" (click)="resetData()">Clear</button>
<button mat-flat-button color="primary" tabindex="4" type="submit" (click)="onLookup()">Lookup</button>
</div>
</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">

@ -7,8 +7,8 @@
</mat-option>
</mat-select>
</mat-form-field>
<div class="mt-2" fxFlex="48" fxFlex.gt-md="25">
<button fxFlex="100" fxLayoutAlign="center center" mat-flat-button color="primary" (click)="onGenerateAddress()" tabindex="2" class="top-minus-15px">Generate Address</button>
<div class="mt-2">
<button mat-flat-button color="primary" (click)="onGenerateAddress()" tabindex="2" class="top-minus-15px">Generate Address</button>
</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 { DecimalPipe } from '@angular/common';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { takeUntil, filter } from 'rxjs/operators';
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 { 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 { LoggerService } from '../../../shared/services/logger.service';
import { RTLEffects } from '../../../store/rtl.effects';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
import { MessageDataField } from '../../../shared/models/alertData';
@Component({
selector: 'rtl-cl-on-chain-send',
@ -22,7 +23,8 @@ import { MessageDataField } from '../../../shared/models/alertData';
styleUrls: ['./on-chain-send.component.scss']
})
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 appConfig: RTLConfiguration;
public nodeData: GetInfoRoot;
@ -35,6 +37,7 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
public transaction: OnChainCL = {};
public feeRateTypes = FEE_RATE_TYPES;
public flgMinConf = false;
public sendFundError = '';
public fiatConversion = false;
public amountUnits = CURRENCY_UNITS;
public selAmountUnit = CURRENCY_UNITS[0];
@ -43,7 +46,7 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
public currencyUnitFormats = CURRENCY_UNIT_FORMATS;
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() {
this.store.select('root')
@ -55,51 +58,35 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
this.nodeData = rootStore.nodeData;
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() {
if(this.invalidValues) { return true; }
this.sendFundError = '';
this.store.dispatch(new RTLActions.OpenSpinner('Sending Funds...'));
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)
.pipe(takeUntil(this.unSubs[1]))
.pipe(takeUntil(this.unSubs[2]))
.subscribe(data => {
this.transaction.satoshis = parseInt(data[CurrencyUnitEnum.SATS]);
this.selAmountUnit = CurrencyUnitEnum.SATS;
this.confirmSend();
this.store.dispatch(new RTLActions.SetChannelTransactionCL(this.transaction));
});
} 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 {
@ -109,6 +96,7 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
}
resetData() {
this.sendFundError = '';
this.transaction = {};
this.flgMinConf = false;
}
@ -119,7 +107,7 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
let currSelectedUnit = event.value === this.amountUnits[2] ? CurrencyUnitEnum.OTHER : event.value;
if(this.transaction.satoshis && this.selAmountUnit !== event.value) {
this.commonService.convertCurrency(this.transaction.satoshis, prevSelectedUnit, this.amountUnits[2], this.fiatConversion)
.pipe(takeUntil(this.unSubs[4]))
.pipe(takeUntil(this.unSubs[3]))
.subscribe(data => {
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-content fxLayout="column">
<mat-tab-group>
<mat-tab label="Send">
<rtl-cl-on-chain-send></rtl-cl-on-chain-send>
</mat-tab>
<mat-tab label="Receive">
<rtl-cl-on-chain-receive></rtl-cl-on-chain-receive>
</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-card-content>
</mat-card>

@ -5,7 +5,9 @@ import { Store } from '@ngrx/store';
import { faExchangeAlt, faChartPie } from '@fortawesome/free-solid-svg-icons';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
import { CLOnChainSendComponent } from './on-chain-send-modal/on-chain-send.component';
@Component({
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() {
this.unSubs.forEach(completeSub => {
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 matColumnDef="no_peer">
<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>
</td>
</ng-container>

@ -9,6 +9,7 @@ import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTyp
import { LoggerService } from '../../../../../shared/services/logger.service';
import { CommonService } from '../../../../../shared/services/common.service';
import { CLChannelInformationComponent } from '../../channel-information-modal/channel-information.component';
import { CLEffects } from '../../../../store/cl.effects';
import { RTLEffects } from '../../../../../store/rtl.effects';
import * as RTLActions from '../../../../../store/rtl.actions';
@ -206,29 +207,12 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
}
onChannelClick(selChannel: ChannelCL, event: any) {
const reorderedChannel = [
[{key: 'channel_id', value: selChannel.channel_id, title: 'Channel ID', width: 100, type: DataTypeEnum.STRING}],
[{key: 'id', value: selChannel.id, title: 'Peer Public Key', width: 100, type: DataTypeEnum.STRING}],
[{key: 'funding_txid', value: selChannel.funding_txid, title: 'Funding Transaction Id', width: 100, type: DataTypeEnum.STRING}],
[{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
}}));
}
this.store.dispatch(new RTLActions.OpenAlert({ data: {
channel: selChannel,
showCopy: true,
component: CLChannelInformationComponent
}}));
}
loadChannelsTable(mychannels) {
mychannels.sort(function(a, b) {

@ -58,7 +58,7 @@
</ng-container>
<ng-container matColumnDef="no_peer">
<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>
</td>
</ng-container>

@ -9,6 +9,7 @@ import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTyp
import { LoggerService } from '../../../../../shared/services/logger.service';
import { CommonService } from '../../../../../shared/services/common.service';
import { CLChannelInformationComponent } from '../../channel-information-modal/channel-information.component';
import { CLEffects } from '../../../../store/cl.effects';
import { RTLEffects } from '../../../../../store/rtl.effects';
import * as RTLActions from '../../../../../store/rtl.actions';
@ -87,27 +88,10 @@ export class CLChannelPendingTableComponent implements OnInit, OnDestroy {
}
onChannelClick(selChannel: ChannelCL, event: any) {
const reorderedChannel = [
[{key: 'channel_id', value: selChannel.channel_id, title: 'Channel ID', width: 100, type: DataTypeEnum.STRING}],
[{key: 'id', value: selChannel.id, title: 'Peer Public Key', width: 100, type: DataTypeEnum.STRING}],
[{key: 'funding_txid', value: selChannel.funding_txid, title: 'Funding Transaction Id', width: 100, type: DataTypeEnum.STRING}],
[{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
this.store.dispatch(new RTLActions.OpenAlert({ data: {
channel: selChannel,
showCopy: true,
component: CLChannelInformationComponent
}}));
}

@ -7,85 +7,98 @@
<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">
<form fxLayout="column" (ngSubmit)="openChannelForm.form.valid && onOpenChannel()" #openChannelForm="ngForm">
<mat-expansion-panel class="flat-expansion-panel">
<mat-expansion-panel-header>
<mat-panel-title>
<span>{{newlyAdded ? '' : 'Open channel with'}}&nbsp;</span><strong class="font-weight-900">{{peer.alias || peer.id}}</strong>&nbsp;{{newlyAdded ? 'added as a peer.' : '.'}}
</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>
<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>
<form fxLayout="column" (submit)="onOpenChannel()" (reset)="resetData()" #form="ngForm">
<div fxLayout="column">
<mat-form-field fxFlex="100" *ngIf="!peer && peers && peers.length > 0">
<input type="text" placeholder="Peer Alias" aria-label="Peers" matInput [formControl]="selectedPeer" (change)="onSelectedPeerChanged()" [matAutocomplete]="auto" tabindex="1" required>
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn" (optionSelected)="onSelectedPeerChanged()">
<mat-option *ngFor="let peer of filteredPeers | async" [value]="peer">{{peer.alias ? peer.alias : peer.id ? peer.id : ''}}</mat-option>
</mat-autocomplete>
<mat-error *ngIf="selectedPeer.errors?.required">Peer alias is required.</mat-error>
<mat-error *ngIf="selectedPeer.errors?.notfound">Peer not found in the list.</mat-error>
</mat-form-field>
</div>
<ng-container *ngTemplateOutlet="peerDetailsExpansionBlock"></ng-container>
<ng-container *ngTemplateOutlet="openChannelBlock"></ng-container>
<div fxFlex="100" class="alert alert-danger mt-1" *ngIf="channelConnectionError !== ''">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span *ngIf="channelConnectionError !== ''">{{channelConnectionError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="end center">
<div *ngIf="newlyAdded" fxLayoutAlign="space-between center" fxFlex="60">
<button fxFlex="33" fxLayoutAlign="center center" mat-stroked-button color="warn" (click)="onClose()" tabindex="7">Do It Later</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>
<button mat-stroked-button color="primary" class="mr-1" tabindex="7" type="reset">Clear Fields</button>
<button autoFocus mat-flat-button color="primary" type="submit" tabindex="9">Open Channel</button>
</div>
</form>
</mat-card-content>
</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 { Subject, Observable, of } from 'rxjs';
import { takeUntil, filter, startWith, map } from 'rxjs/operators';
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 { CLOpenChannelAlert } from '../../../../shared/models/alertData';
@ -14,29 +19,78 @@ import * as fromRTLReducer from '../../../../store/rtl.reducers';
templateUrl: './open-channel.component.html',
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 peer: PeerCL;
public peers: PeerCL[];
public sortedPeers: PeerCL[];
public filteredPeers: Observable<PeerCL[]>;
public channelConnectionError = '';
public advancedTitle = 'Advanced Options';
public information: GetInfoCL;
public totalBalance = 0;
public fundingAmount: number;
public myChanPolicy: any = {};
public selectedPubkey = '';
public isPrivate = false;
public feeRateTypes = FEE_RATE_TYPES;
public totalBalance = 0;
public newlyAdded = false;
public selFeeRate = '';
public flgMinConf = false;
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() {
this.peer = this.data.message.peer;
this.information = this.data.message.information;
this.totalBalance = this.data.message.balance;
this.newlyAdded = this.data.newlyAdded;
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() {
@ -44,21 +98,37 @@ export class CLOpenChannelComponent implements OnInit {
}
resetData() {
this.fundingAmount = 0;
this.moreOptions = false;
this.flgMinConf = false;
this.isPrivate = false;
this.selFeeRate = '';
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() {
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.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 { ChannelManageComponent } from './channel-manage.component';
import { CLConnectPeerComponent } from './connect-peer.component';
describe('ChannelManageComponent', () => {
let component: ChannelManageComponent;
let fixture: ComponentFixture<ChannelManageComponent>;
describe('CLConnectPeerComponent', () => {
let component: CLConnectPeerComponent;
let fixture: ComponentFixture<CLConnectPeerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ChannelManageComponent ]
declarations: [ CLConnectPeerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ChannelManageComponent);
fixture = TestBed.createComponent(CLConnectPeerComponent);
component = fixture.componentInstance;
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>
<span matBadge="{{activeChannels}}" matBadgeOverlap="false" class="tab-badge">Channels</span>
</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>
<ng-template mat-tab-label>

@ -5,7 +5,11 @@ import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
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 * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
@Component({
@ -15,6 +19,9 @@ import * as fromRTLReducer from '../../store/rtl.reducers';
})
export class CLPeersChannelsComponent implements OnInit, OnDestroy {
public selNode: SelNodeChild = {};
public information: GetInfoCL = {};
public peers: PeerCL[] = [];
public totalBalance = 0;
public activePeers = 0;
public activeChannels = 0;
public faUsers = faUsers;
@ -29,12 +36,28 @@ export class CLPeersChannelsComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.unSubs[1]))
.subscribe((rtlStore) => {
this.selNode = rtlStore.nodeSettings;
this.information = rtlStore.information;
this.peers = rtlStore.peers;
this.activePeers = (rtlStore.peers && rtlStore.peers.length) ? rtlStore.peers.length : 0;
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}];
});
}
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() {
this.unSubs.forEach(completeSub => {
completeSub.next();

@ -1,13 +1,6 @@
<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">
<mat-form-field fxFlex="100" fxLayoutAlign="start end">
<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>
<button mat-flat-button color="primary" type="submit" tabindex="1" (click)="onConnectPeer()">Add Peer</button>
</form>
<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">

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

@ -6,27 +6,6 @@
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start start" class="padding-gap-x">
<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" 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}">
<mat-tab-group fxLayout="column" fxFlex="100">
<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>
<mat-error *ngIf="!message">Message is required.</mat-error>
</mat-form-field>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="my-1">
<button fxFlex="48" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()" type="reset">Clear Field</button>
<button fxFlex="48" mat-flat-button color="primary" (click)="onSign()" tabindex="3" type="submit">Sign</button>
<div fxLayout="row" class="my-1">
<button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Field</button>
<button mat-flat-button color="primary" (click)="onSign()" tabindex="3" type="submit">Sign</button>
</div>
<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" 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">
<button fxFlex="48" mat-stroked-button color="primary" tabindex="4" rtlClipboard [payload]="signature" (copied)="onCopyField($event)" type="button">Copy Signature</button>
<div fxLayout="row" class="mt-2">
<button mat-stroked-button color="primary" tabindex="4" rtlClipboard [payload]="signature" (copied)="onCopyField($event)" type="button">Copy Signature</button>
</div>
</form>
</div>

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

@ -9,9 +9,9 @@
<mat-error *ngIf="!signature">Signature is required.</mat-error>
</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>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="my-1">
<button fxFlex="48" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="resetData()" type="reset">Clear Fields</button>
<button fxFlex="48" mat-flat-button color="primary" (click)="onVerify()" tabindex="4" type="submit">Verify</button>
<div fxLayout="row" class="my-1">
<button class="mr-1" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="resetData()">Clear Fields</button>
<button mat-flat-button color="primary" (click)="onVerify()" tabindex="4" type="submit">Verify</button>
</div>
<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>
@ -21,8 +21,8 @@
<div *ngIf="verifyRes.verified" fxLayout="column" fxFlex="100" fxLayoutAlign="start start" class="bordered-box read-only h-4 padding-gap">
<p>{{verifyRes?.pubkey}}</p>
</div>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" 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>
<div fxLayout="row" class="mt-2" *ngIf="verifyRes.verified">
<button mat-stroked-button color="primary" tabindex="5" rtlClipboard [payload]="verifyRes?.pubkey" (copied)="onCopyField($event)" type="button">Copy Pubkey</button>
</div>
</div>
</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 { CLInvoiceInformationComponent } from '../transactions/invoice-information-modal/invoice-information.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 * as fromRTLReducer from '../../store/rtl.reducers';
import * as RTLActions from '../../store/rtl.actions';
@ -53,24 +53,10 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err) => {
let code = err.status ? err.status : '';
let message = err.error.message ? err.error.message + ' ' : '';
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;
}
}
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 : '';
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');
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});
})
);
@ -92,7 +78,7 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchFeesCL', err);
this.handleErrorWithoutAlert('FetchFeesCL', 'Fetching Fees Failed.', err);
return of({type: RTLActions.VOID});
}
));
@ -112,7 +98,7 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchFeeRatesCL', err);
this.handleErrorWithoutAlert('FetchFeeRatesCL', 'Fetching Fee Rates Failed.', err);
return of({type: RTLActions.VOID});
}
));
@ -132,7 +118,7 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchBalanceCL', err);
this.handleErrorWithoutAlert('FetchBalanceCL', 'Fetching Balances Failed.', err);
return of({type: RTLActions.VOID});
}
));
@ -152,7 +138,7 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchLocalRemoteBalanceCL', err);
this.handleErrorWithoutAlert('FetchLocalRemoteBalanceCL', 'Fetching Balances Failed.', err);
return of({type: RTLActions.VOID});
}
));
@ -201,7 +187,7 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchPeersCL', err);
this.handleErrorWithoutAlert('FetchPeersCL', 'Fetching Peers Failed.', err);
return of({type: RTLActions.VOID});
})
);
@ -213,36 +199,20 @@ export class CLEffects implements OnDestroy {
ofType(RTLActions.SAVE_NEW_PEER_CL),
withLatestFrom(this.store.select('cl')),
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 })
.pipe(
map((postRes: any) => {
map((postRes: PeerCL[]) => {
this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.SetPeersCL((postRes && postRes.length > 0) ? postRes : []));
if(action.payload.showOpenChannelModal) {
const peerToAddChannelMessage = {
peer: postRes[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
}
}
return {
type: RTLActions.NEWLY_ADDED_PEER_CL,
payload: {peer: postRes.find(peer => action.payload.id.indexOf(peer.id) === 0)}
};
}),
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});
})
);
@ -287,7 +257,7 @@ export class CLEffects implements OnDestroy {
};
},
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchChannelsCL', err);
this.handleErrorWithoutAlert('FetchChannelsCL', 'Fetching Channels Failed.', err);
return of({type: RTLActions.VOID});
})
));
@ -298,6 +268,7 @@ export class CLEffects implements OnDestroy {
openNewChannelCL = this.actions$.pipe(
ofType(RTLActions.SAVE_NEW_CHANNEL_CL),
mergeMap((action: RTLActions.SaveNewChannelCL) => {
this.store.dispatch(new RTLActions.ClearEffectErrorCl('SaveNewChannelCL'));
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
})
@ -312,7 +283,7 @@ export class CLEffects implements OnDestroy {
};
}),
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});
})
);
@ -386,7 +357,7 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchPaymentsCL', err);
this.handleErrorWithoutAlert('FetchPaymentsCL', 'Fetching Payments Failed.', err);
return of({type: RTLActions.VOID});
}
));
@ -395,7 +366,8 @@ export class CLEffects implements OnDestroy {
decodePaymentCL = this.actions$.pipe(
ofType(RTLActions.DECODE_PAYMENT_CL),
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(
map((decodedPayment) => {
this.logger.info(decodedPayment);
@ -406,7 +378,11 @@ export class CLEffects implements OnDestroy {
};
}),
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});
})
);
@ -427,26 +403,41 @@ export class CLEffects implements OnDestroy {
ofType(RTLActions.SEND_PAYMENT_CL),
withLatestFrom(this.store.select('root')),
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)
.pipe(
map((sendRes: any) => {
this.logger.info(sendRes);
this.store.dispatch(new RTLActions.CloseSpinner());
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 {
this.store.dispatch(new RTLActions.OpenSnackBar('Payment Sent Successfully!'));
this.store.dispatch(new RTLActions.FetchChannelsCL());
this.store.dispatch(new RTLActions.FetchBalanceCL());
this.store.dispatch(new RTLActions.FetchPaymentsCL());
this.store.dispatch(new RTLActions.SetDecodedPaymentCL({}));
return {
type: RTLActions.SET_DECODED_PAYMENT_CL,
payload: {}
type: RTLActions.SEND_PAYMENT_STATUS_CL,
payload: sendRes
};
}
}),
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});
})
);
@ -624,6 +615,7 @@ export class CLEffects implements OnDestroy {
saveNewInvoiceCL = this.actions$.pipe(
ofType(RTLActions.SAVE_NEW_INVOICE_CL),
mergeMap((action: RTLActions.SaveNewInvoiceCL) => {
this.store.dispatch(new RTLActions.ClearEffectErrorCl('SaveNewInvoiceCL'));
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
})
@ -648,7 +640,7 @@ export class CLEffects implements OnDestroy {
};
}),
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});
})
);
@ -673,7 +665,7 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchInvoicesCL', err);
this.handleErrorWithoutAlert('FetchInvoicesCL', 'Fetching Invoices Failed.', err);
return of({type: RTLActions.VOID});
}
));
@ -690,12 +682,13 @@ export class CLEffects implements OnDestroy {
this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.FetchBalanceCL());
this.store.dispatch(new RTLActions.OpenSnackBar('Fund Sent Successfully.'));
return { type: RTLActions.VOID };
return {
type: RTLActions.SET_CHANNEL_TRANSACTION_RES_CL,
payload: postRes
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorCl({ action: 'SetChannelTransactionCL', code: err.status, message: err.error.error }));
this.handleErrorWithAlert('ERROR', 'Sending Fund Failed', this.CHILD_API_URL + environment.ON_CHAIN_API, err);
this.handleErrorWithoutAlert('SetChannelTransactionCL', 'Sending Fund Failed.', err);
return of({type: RTLActions.VOID});
}));
})
@ -734,13 +727,16 @@ export class CLEffects implements OnDestroy {
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));
if (err.status === 401) {
this.logger.info('Redirecting to Login');
this.store.dispatch(new RTLActions.CloseAllDialogs());
this.store.dispatch(new RTLActions.Logout());
this.store.dispatch(new RTLActions.OpenSnackBar('Authentication Failed. Redirecting to Login.'));
} 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);
if (err.status === 401) {
this.logger.info('Redirecting to Login');
this.store.dispatch(new RTLActions.CloseAllDialogs());
this.store.dispatch(new RTLActions.Logout());
this.store.dispatch(new RTLActions.OpenSnackBar('Authentication Failed. Redirecting to Login.'));
} else {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenAlert({
data: {
type: alerType,
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
}
}));

@ -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>
<mat-divider class="w-100 my-1"></mat-divider>
</div>
<div [ngClass]="{'mt-2': !showAdvanced, 'mt-1': showAdvanced}" fxLayout="row" fxLayoutAlign="end center" fxFlex="100">
<button fxFlex="50" fxFlex.gt-sm="25" fxLayoutAlign="center center" mat-stroked-button color="primary" type="reset" (click)="onShowAdvanced()" tabindex="1" class="mr-2">
<div [ngClass]="{'mt-2': !showAdvanced, 'mt-1': showAdvanced}" fxLayout="row" fxLayoutAlign="end center">
<button class="mr-1" mat-stroked-button color="primary" type="reset" (click)="onShowAdvanced()" tabindex="1">
<p *ngIf="!showAdvanced; else hideAdvancedText">Show Advanced</p>
<ng-template #hideAdvancedText><p>Hide Advanced</p></ng-template>
</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>
</mat-card-content>

@ -1,36 +1,23 @@
<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">
<mat-form-field [fxFlex]="showDetails ? '45' : '100'" fxLayoutAlign="space-between stretch">
<form *ngIf="!showDetails" fxLayout="column" fxLayoutAlign="space-between stretch" fxFlex="100" #addInvoiceForm="ngForm">
<mat-form-field fxFlex="100" fxLayoutAlign="space-between stretch">
<input matInput [(ngModel)]="description" placeholder="Description" tabindex="2" name="description">
</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>
<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="10" fxLayoutAlign="start end" *ngIf="showDetails" [ngClass]="{'mt-2': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">
<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="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 fxLayout="row" class="mt-1">
<button class="mr-1" mat-stroked-button color="primary" tabindex="9" type="reset" (click)="resetData()">Clear Field</button>
<button mat-flat-button color="primary" (click)="onAddInvoice(addInvoiceForm)" tabindex="10">Create Invoice</button>
</div>
</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" 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">

@ -17,6 +17,7 @@ import { newlyAddedRowAnimation } from '../../../shared/animation/row-animation'
import { RTLEffects } from '../../../store/rtl.effects';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
import { CLCreateInvoiceComponent } from '../create-invoice-modal/create-invoice.component';
@Component({
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) {
if(!this.invoiceValue) { return true; }
let expiryInSecs = (this.expiry ? this.expiry : 3600);

@ -1,17 +1,16 @@
<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">
<mat-form-field fxFlex="100">
<input matInput placeholder="Payment Request" name="paymentRequest" [(ngModel)]="paymentRequest" tabindex="1" (keyup)="onPaymentRequestEntry()" required #paymentReq="ngModel">
<mat-form-field fxFlex="100" *ngIf="!showDetails">
<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-error *ngIf="!paymentRequest">Payment request is required.</mat-error>
</mat-form-field>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" *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 fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" (click)="onSendPayment();" tabindex="3">Send Payment</button>
<div fxLayout="row" *ngIf="!showDetails" class="mt-1">
<button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Field</button>
<button mat-flat-button color="primary" (click)="onSendPayment()" tabindex="3">Send Payment</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="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>
<div fxLayout="row" *ngIf="showDetails" class="mt-2">
<button mat-flat-button color="primary" (click)="openSendPaymentModal()" tabindex="3">Send Payment</button>
</div>
</form>
<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 { 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 { RTLEffects } from '../../../store/rtl.effects';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
import { SelNodeChild } from '../../../shared/models/RTLconfig';
@Component({
selector: 'rtl-cl-lightning-payments',
@ -51,7 +52,7 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
public screenSizeEnum = ScreenSizeEnum;
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();
if(this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
@ -100,8 +101,8 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
this.sendPayment();
} else {
this.store.dispatch(new RTLActions.OpenSpinner('Decoding Payment...'));
this.store.dispatch(new RTLActions.DecodePaymentCL(this.paymentRequest));
this.lndEffects.setDecodedPaymentCL
this.store.dispatch(new RTLActions.DecodePaymentCL({routeParam: this.paymentRequest, fromDialog: false}));
this.clEffects.setDecodedPaymentCL
.pipe(take(1))
.subscribe(decodedPayment => {
this.paymentDecoded = decodedPayment;
@ -149,7 +150,7 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
if (confirmRes) {
this.paymentDecoded.msatoshi = confirmRes[0].inputValue;
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();
}
});
@ -175,19 +176,20 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
.subscribe(confirmRes => {
if (confirmRes) {
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();
}
});
}
}
onPaymentRequestEntry() {
onPaymentRequestEntry(event: any) {
this.paymentRequest = event;
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.DecodePaymentCL(this.paymentRequest));
this.lndEffects.setDecodedPaymentCL.subscribe(decodedPayment => {
this.store.dispatch(new RTLActions.DecodePaymentCL({routeParam: this.paymentRequest, fromDialog: false}));
this.clEffects.setDecodedPaymentCL.subscribe(decodedPayment => {
this.paymentDecoded = decodedPayment;
if(this.paymentDecoded.msatoshi) {
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() {
this.paymentDecoded = {};
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">
<mat-error *ngIf="!amount">Amount 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="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>
<div fxLayout="row" class="mt-2">
<button class="mr-1" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="resetData()">Clear</button>
<button mat-flat-button color="primary" type="submit" tabindex="4">Query Route</button>
</div>
</form>
<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