Merge branch 'master' into UX

pull/260/head
Shahana Farooqui 5 years ago
commit 9690222e66

@ -28,8 +28,6 @@ Pre-requisite for running RTL is a functioning and synced LND node. You can setu
* Windows/Mac users can follow Pierre Rochard's [Node Launcher](https://github.com/lightning-power-users/node-launcher)
* Linux or Raspberry Pi users can follow Stadicus's [guide](https://github.com/Stadicus/guides/blob/master/raspibolt/README.md)
RTL source code is available at this [repo](https://github.com/ShahanaFarooqui/RTLFullApplication)
For detailed screenshots and UI operation guide you can visit our [medium post](https://medium.com/@suheb.khan/how-to-ride-the-lightning-447af999dcd2)
RTL is available on the below platforms/services:

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

@ -5,12 +5,9 @@
<title>RTL</title>
<base href="/rtl/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" sizes="180x180" href="assets/images/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon/favicon-16x16.png">
<link rel="manifest" href="assets/images/favicon/site.webmanifest">
<link rel="stylesheet" href="styles.ee2f5e1373bdbaacbb41.css"></head>
<link rel="icon" type="image/x-icon" href="assets/images/favicon.ico">
<link rel="stylesheet" href="styles.486014dd6111683683a1.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.f3bbd9f1dcc793925cae.js"></script><script src="polyfills-es5.92f4069201c83f4833ef.js" nomodule></script><script src="polyfills.5ddcccdb990eb395f306.js"></script><script src="main.21175c3b9bf3c4c8939e.js"></script></body>
<script src="runtime.feee63dac552d8844834.js"></script><script src="polyfills-es5.92f4069201c83f4833ef.js" nomodule></script><script src="polyfills.5ddcccdb990eb395f306.js"></script><script src="main.bf4aafcd2364c6e5a38d.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
!function(e){function r(r){for(var n,c,i=r[0],a=r[1],f=r[2],p=0,s=[];p<i.length;p++)o[c=i[p]]&&s.push(o[c][0]),o[c]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[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=c(c.s=t[0]))}return e}var n={},o={0:0},u=[];function c(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,c),t.l=!0,t.exports}c.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,c.nc&&i.setAttribute("nonce",c.nc),i.src=function(e){return c.p+""+({}[e]||e)+"."+{1:"dc65164c2cc38ecb3ec0",6:"8f2384e487c219c3bbe9",7:"5b9eec5877f949c48a68"}[e]+".js"}(e);var a=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;a.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",a.name="ChunkLoadError",a.type=n,a.request=u,t[1](a)}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)},c.m=e,c.c=n,c.d=function(e,r,t){c.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},c.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(e,r){if(1&r&&(e=c(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(c.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)c.d(t,n,(function(r){return e[r]}).bind(null,n));return t},c.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(r,"a",r),r},c.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},c.p="",c.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=a;t()}([]);

File diff suppressed because one or more lines are too long

@ -24,36 +24,35 @@ common.getOptions = () => {
return common.selectedNode.options;
};
common.setOptions = () => {
if (undefined !== common.nodes[0].options && undefined !== common.nodes[0].options.headers) { return; }
common.updateSelectedNodeOptions = () => {
common.selectedNode.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: ''
};
try {
common.nodes.forEach(node => {
node.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: ''
};
if (node.ln_implementation && node.ln_implementation.toUpperCase() === 'CLT') {
node.options.headers = { 'macaroon': Buffer.from(fs.readFileSync(path.join(node.macaroon_path, 'access.macaroon'))).toString("base64") };
} else {
node.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(path.join(node.macaroon_path, 'admin.macaroon')).toString('hex') };
}
});
// Options cannot be set before selected node initializes. Updating selected node's options separatly
if (common.selectedNode && common.selectedNode.ln_implementation && common.selectedNode.ln_implementation.toUpperCase() === 'CLT') {
common.selectedNode.options.headers = { 'macaroon': Buffer.from(fs.readFileSync(path.join(common.selectedNode.macaroon_path, 'access.macaroon'))).toString("base64") };
} else {
common.selectedNode.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(path.join(common.selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') };
}
return { status: 200, message: 'Updated Successfully!' };
} catch(err) {
common.selectedNode.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: ''
};
if (common.selectedNode && common.selectedNode.ln_implementation && common.selectedNode.ln_implementation.toUpperCase() === 'CLT') {
common.selectedNode.options.headers = { 'macaroon': Buffer.from(fs.readFileSync(path.join(common.selectedNode.macaroon_path, 'access.macaroon'))).toString("base64") };
} else {
common.selectedNode.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(path.join(common.selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') };
}
} catch (err) {
console.error('Common Set Options Error:' + JSON.stringify(err));
console.error('Common Update Selected Node Options Error:' + JSON.stringify(err));
return { status: 502, message: err };
}
}
common.setOptions = () => {
if (undefined !== common.nodes[0].options && undefined !== common.nodes[0].options.headers) { return; }
if (common.nodes && common.nodes.length > 0) {
common.nodes.forEach(node => {
node.options = {
url: '',
@ -61,14 +60,23 @@ common.setOptions = () => {
json: true,
form: ''
};
try {
if (node.ln_implementation && node.ln_implementation.toUpperCase() === 'CLT') {
node.options.headers = { 'macaroon': Buffer.from(fs.readFileSync(path.join(node.macaroon_path, 'access.macaroon'))).toString("base64") };
} else {
node.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(path.join(node.macaroon_path, 'admin.macaroon')).toString('hex') };
}
} catch (err) {
console.error('Common Set Options Error:' + JSON.stringify(err));
node.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: ''
};
}
});
// Options cannot be set before selected node initializes. Updating selected node's options separatly
common.selectedNode.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: ''
};
common.updateSelectedNodeOptions();
}
}

@ -219,14 +219,16 @@ connect.validateSingleNodeConfig = (config) => {
common.ln_implementation = 'LND';
}
if (undefined !== process.env.RTL_PASS) {
common.rtl_pass = hash.update(process.env.RTL_PASS).digest('hex');
} else if (config.Authentication.rtlPassHashed !== '' && undefined !== config.Authentication.rtlPassHashed) {
common.rtl_pass = config.Authentication.rtlPassHashed;
} else if (config.Authentication.rtlPass !== '' && undefined !== config.Authentication.rtlPass) {
common.rtl_pass = connect.convertCustomToHash('SINGLE');
}
if(!+config.SSO.rtlSSO) {
if (undefined !== process.env.RTL_PASS) {
common.rtl_pass = hash.update(process.env.RTL_PASS).digest('hex');
} else if (config.Authentication.rtlPassHashed !== '' && undefined !== config.Authentication.rtlPassHashed) {
common.rtl_pass = config.Authentication.rtlPassHashed;
} else if (config.Authentication.rtlPass !== '' && undefined !== config.Authentication.rtlPass) {
common.rtl_pass = connect.convertCustomToHash('SINGLE');
}
}
if (upperCase(common.node_auth_type) === 'CUSTOM' && (common.rtl_pass === '' || undefined === common.rtl_pass)) {
errMsg = errMsg + '\nCustom Node Authentication can be set with RTL password only. Please set RTL Password through environment or RTL.conf';
}
@ -270,82 +272,84 @@ connect.validateSingleNodeConfig = (config) => {
}
connect.validateMultiNodeConfig = (config) => {
common.node_auth_type = 'CUSTOM';
if (undefined !== process.env.RTL_PASS) {
common.rtl_pass = hash.update(process.env.RTL_PASS).digest('hex');
} else if (config.multiPassHashed !== '' && undefined !== config.multiPassHashed) {
common.rtl_pass = config.multiPassHashed;
} else if (config.multiPass !== '' && undefined !== config.multiPass) {
common.rtl_pass = connect.convertCustomToHash('MULTI');
} else {
errMsg = errMsg + '\nMulti Node Authentication can be set with multiPass only. Please set MultiPass in RTL-Multi-Node-Conf.json';
}
common.port = (undefined !== config.port) ? connect.normalizePort(config.port) : 3000;
config.nodes.forEach((node, idx) => {
common.nodes[idx] = {};
if(node.Authentication.macaroonPath === '' || undefined === node.Authentication.macaroonPath) {
errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Multi-Node-Conf.json!';
} else {
common.nodes[idx].macaroon_path = node.Authentication.macaroonPath;
}
if(
(node.Settings.lndServerUrl === '' || undefined === node.Settings.lndServerUrl)
&& (node.Settings.lnServerUrl === '' || undefined === node.Settings.lnServerUrl)
) {
errMsg = errMsg + '\nPlease set server URL for node index ' + node.index + ' in RTL-Multi-Node-Conf.json!';
if(!+config.SSO.rtlSSO) {
common.node_auth_type = 'CUSTOM';
if (undefined !== process.env.RTL_PASS) {
common.rtl_pass = hash.update(process.env.RTL_PASS).digest('hex');
} else if (config.multiPassHashed !== '' && undefined !== config.multiPassHashed) {
common.rtl_pass = config.multiPassHashed;
} else if (config.multiPass !== '' && undefined !== config.multiPass) {
common.rtl_pass = connect.convertCustomToHash('MULTI');
} else {
common.nodes[idx].ln_server_url = node.Settings.lndServerUrl ? node.Settings.lndServerUrl : node.Settings.lnServerUrl;
errMsg = errMsg + '\nMulti Node Authentication can be set with multiPass only. Please set MultiPass in RTL-Multi-Node-Conf.json';
}
}
common.port = (undefined !== config.port) ? connect.normalizePort(config.port) : 3000;
if (config.nodes && config.nodes.length > 0) {
config.nodes.forEach((node, idx) => {
common.nodes[idx] = {};
if(node.Authentication.macaroonPath === '' || undefined === node.Authentication.macaroonPath) {
errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Multi-Node-Conf.json!';
} else {
common.nodes[idx].macaroon_path = node.Authentication.macaroonPath;
}
common.nodes[idx].index = node.index;
common.nodes[idx].ln_node = node.lnNode;
common.nodes[idx].ln_implementation = node.lnImplementation;
if (undefined !== node.Authentication && undefined !== node.Authentication.lndConfigPath) {
common.nodes[idx].config_path = node.Authentication.lndConfigPath;
} else if (undefined !== node.Authentication && undefined !== node.Authentication.configPath) {
common.nodes[idx].config_path = node.Authentication.configPath;
} else {
common.nodes[idx].config_path = '';
}
common.nodes[idx].bitcoind_config_path = (undefined !== node.Settings.bitcoindConfigPath) ? node.Settings.bitcoindConfigPath : '';
common.nodes[idx].enable_logging = (undefined !== node.Settings.enableLogging) ? node.Settings.enableLogging : false;
common.nodes[idx].channel_backup_path = (undefined !== node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : common.rtl_conf_file_path + common.path_separator + 'backup' + common.path_separator + 'node-' + node.index;
try {
connect.createDirectory(common.nodes[idx].channel_backup_path);
let exists = fs.existsSync(common.nodes[idx].channel_backup_path + common.path_separator + 'channel-all.bak');
if (!exists) {
try {
var createStream = fs.createWriteStream(common.nodes[idx].channel_backup_path + common.path_separator + 'channel-all.bak');
createStream.end();
} catch (err) {
console.error('Something went wrong while creating backup file: \n' + err);
}
}
} catch (err) {
console.error('Something went wrong while creating backup file: \n' + err);
}
if(
(node.Settings.lndServerUrl === '' || undefined === node.Settings.lndServerUrl)
&& (node.Settings.lnServerUrl === '' || undefined === node.Settings.lnServerUrl)
) {
errMsg = errMsg + '\nPlease set server URL for node index ' + node.index + ' in RTL-Multi-Node-Conf.json!';
} else {
common.nodes[idx].ln_server_url = node.Settings.lndServerUrl ? node.Settings.lndServerUrl : node.Settings.lnServerUrl;
}
if (common.nodes[idx].enable_logging) {
common.nodes[idx].log_file = common.rtl_conf_file_path + '/logs/RTL-Node-' + node.index + '.log';
const log_file = common.nodes[idx].log_file;
if (fs.existsSync(log_file)) {
fs.writeFile(log_file, '', () => { });
common.nodes[idx].index = node.index;
common.nodes[idx].ln_node = node.lnNode;
common.nodes[idx].ln_implementation = node.lnImplementation;
if (undefined !== node.Authentication && undefined !== node.Authentication.lndConfigPath) {
common.nodes[idx].config_path = node.Authentication.lndConfigPath;
} else if (undefined !== node.Authentication && undefined !== node.Authentication.configPath) {
common.nodes[idx].config_path = node.Authentication.configPath;
} else {
try {
var dirname = path.dirname(log_file);
connect.createDirectory(dirname);
var createStream = fs.createWriteStream(log_file);
createStream.end();
}
catch (err) {
console.error('Something went wrong while creating log file ' + log_file + ': \n' + err);
common.nodes[idx].config_path = '';
}
common.nodes[idx].bitcoind_config_path = (undefined !== node.Settings.bitcoindConfigPath) ? node.Settings.bitcoindConfigPath : '';
common.nodes[idx].enable_logging = (undefined !== node.Settings.enableLogging) ? node.Settings.enableLogging : false;
common.nodes[idx].channel_backup_path = (undefined !== node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : common.rtl_conf_file_path + common.path_separator + 'backup' + common.path_separator + 'node-' + node.index;
try {
connect.createDirectory(common.nodes[idx].channel_backup_path);
let exists = fs.existsSync(common.nodes[idx].channel_backup_path + common.path_separator + 'channel-all.bak');
if (!exists) {
try {
var createStream = fs.createWriteStream(common.nodes[idx].channel_backup_path + common.path_separator + 'channel-all.bak');
createStream.end();
} catch (err) {
console.error('Something went wrong while creating backup file: \n' + err);
}
}
} catch (err) {
console.error('Something went wrong while creating backup file: \n' + err);
}
if (common.nodes[idx].enable_logging) {
common.nodes[idx].log_file = common.rtl_conf_file_path + '/logs/RTL-Node-' + node.index + '.log';
const log_file = common.nodes[idx].log_file;
if (fs.existsSync(log_file)) {
fs.writeFile(log_file, '', () => { });
} else {
try {
var dirname = path.dirname(log_file);
connect.createDirectory(dirname);
var createStream = fs.createWriteStream(log_file);
createStream.end();
}
catch (err) {
console.error('Something went wrong while creating log file ' + log_file + ': \n' + err);
}
}
}
}
});
});
}
connect.setSSOParams(config);
if (errMsg !== '') { throw new Error(errMsg); }
@ -372,7 +376,11 @@ connect.setSSOParams = (config) => {
} else {
common.rtl_cookie_path = common.rtl_conf_file_path + '/cookies/auth.cookie';
}
connect.readCookie(common.rtl_cookie_path);
if (common.rtl_cookie_path === '') {
errMsg = 'Please set rtlCookiePath value for single sign on option!';
} else {
connect.readCookie(common.rtl_cookie_path);
}
}
};
@ -400,7 +408,12 @@ connect.createDirectory = (dirname) => {
connect.readCookie = (cookieFile) => {
let exists = fs.existsSync(cookieFile);
if (exists) {
common.cookie = fs.readFileSync(cookieFile, 'utf-8');
try {
common.cookie = fs.readFileSync(cookieFile, 'utf-8');
} catch (err) {
console.error('Something went wrong while reading cookie: \n' + err);
throw new Error(err);
}
} else {
try {
var dirname = path.dirname(cookieFile);
@ -409,7 +422,7 @@ connect.readCookie = (cookieFile) => {
common.cookie = fs.readFileSync(cookieFile, 'utf-8');
}
catch(err) {
console.error('Something went wrong while reading cookie: \n' + err);
console.error('Something went wrong while reading the cookie: \n' + err);
throw new Error(err);
}
}
@ -427,7 +440,7 @@ connect.refreshCookie = (cookieFile) => {
}
connect.logEnvVariables = () => {
if (common.multi_node_setup) {
if (common.multi_node_setup && common.nodes && common.nodes.length > 0) {
common.nodes.forEach((node, idx) => {
if (!node.enable_logging) { return; }
logger.info({fileName: 'Config Setup Variable', msg: 'DEFAULT_NODE_INDEX: ' + common.selectedNode.index});

@ -58,28 +58,30 @@ exports.getRTLConfig = (req, res, next) => {
const multiNodeConfig = JSON.parse(data);
const sso = { rtlSSO: common.rtl_sso, logoutRedirectLink: common.logout_redirect_link };
var nodesArr = [];
multiNodeConfig.nodes.forEach((node, i) => {
const authentication = {};
authentication.nodeAuthType = 'CUSTOM';
if(node.Authentication && node.Authentication.lndConfigPath) {
authentication.configPath = node.Authentication.lndConfigPath;
} else if(node.Authentication && node.Authentication.configPath) {
authentication.configPath = node.Authentication.configPath;
} else {
authentication.configPath = '';
}
if(node.Settings.bitcoindConfigPath) {
authentication.bitcoindConfigPath = node.Settings.bitcoindConfigPath;
}
node.Settings.channelBackupPath = (undefined !== node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : common.nodes[i].channel_backup_path;
nodesArr.push({
index: node.index,
lnNode: node.lnNode,
lnImplementation: node.lnImplementation,
settings: node.Settings,
authentication: authentication})
});
if (multiNodeConfig.nodes && multiNodeConfig.nodes.length > 0) {
multiNodeConfig.nodes.forEach((node, i) => {
const authentication = {};
authentication.nodeAuthType = 'CUSTOM';
if(node.Authentication && node.Authentication.lndConfigPath) {
authentication.configPath = node.Authentication.lndConfigPath;
} else if(node.Authentication && node.Authentication.configPath) {
authentication.configPath = node.Authentication.configPath;
} else {
authentication.configPath = '';
}
if(node.Settings.bitcoindConfigPath) {
authentication.bitcoindConfigPath = node.Settings.bitcoindConfigPath;
}
node.Settings.channelBackupPath = (undefined !== node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : common.nodes[i].channel_backup_path;
nodesArr.push({
index: node.index,
lnNode: node.lnNode,
lnImplementation: node.lnImplementation,
settings: node.Settings,
authentication: authentication})
});
}
res.status(200).json({ selectedNodeIndex: common.selectedNode.index, sso: sso, nodes: nodesArr });
}
});

@ -135,7 +135,7 @@ exports.listForwards = (req, res, next) => {
error: (undefined === body) ? 'Error From Server!' : body.error
});
} else {
if (body.length > 0) {
if (body && body.length > 0) {
body.forEach(event => {
event.received_time_str = (undefined === event.received_time) ? '' : common.convertTimestampToDate(event.received_time);
event.resolved_time_str = (undefined === event.resolved_time) ? '' : common.convertTimestampToDate(event.resolved_time);

@ -37,7 +37,7 @@ exports.listInvoices = (req, res, next) => {
error: (undefined === body) ? 'Error From Server!' : body.error
});
} else {
if (undefined !== body.invoices) {
if (undefined !== body.invoices && body.invoices.length > 0) {
body.invoices.forEach(invoice => {
invoice.paid_at_str = (undefined === invoice.paid_at) ? '' : common.convertTimestampToDate(invoice.paid_at);
invoice.expires_at_str = (undefined === invoice.expires_at) ? '' : common.convertTimestampToDate(invoice.expires_at);

@ -14,7 +14,7 @@ exports.listPayments = (req, res, next) => {
error: (undefined === body) ? 'Error From Server!' : body.error
});
} else {
if (undefined !== body && undefined !== body.payments) {
if (undefined !== body && undefined !== body.payments && body.payments.length > 0) {
body.payments.forEach(payment => {
payment.created_at_str = (undefined === payment.created_at) ? '' : common.convertTimestampToDate(payment.created_at);
});

@ -1,4 +1,5 @@
var request = require('request-promise');
var upperCase = require('upper-case');
var common = require('../../common');
var logger = require('../logger');
var options = {};
@ -10,10 +11,20 @@ exports.getBalance = (req, res, next) => {
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(undefined !== body) {
body.btc_balance = (undefined === body.balance) ? 0 : common.convertToBTC(body.balance);
body.btc_total_balance = (undefined === body.total_balance) ? 0 : common.convertToBTC(body.total_balance);
body.btc_confirmed_balance = (undefined === body.confirmed_balance) ? 0 : common.convertToBTC(body.confirmed_balance);
body.btc_unconfirmed_balance = (undefined === body.unconfirmed_balance) ? 0 : common.convertToBTC(body.unconfirmed_balance);
if (upperCase(req.params.source) === 'BLOCKCHAIN') {
if (!body.total_balance) { body.total_balance = 0; }
if (!body.confirmed_balance) { body.confirmed_balance = 0; }
if (!body.unconfirmed_balance) { body.unconfirmed_balance = 0; }
body.btc_total_balance = common.convertToBTC(body.total_balance);
body.btc_confirmed_balance = common.convertToBTC(body.confirmed_balance);
body.btc_unconfirmed_balance = common.convertToBTC(body.unconfirmed_balance);
}
if (upperCase(req.params.source) === 'CHANNELS') {
if (!body.balance) { body.balance = 0; }
if (!body.pending_open_balance) { body.pending_open_balance = 0; }
body.btc_balance = common.convertToBTC(body.balance);
body.btc_pending_open_balance = common.convertToBTC(body.pending_open_balance);
}
res.status(200).json(body);
}
})

@ -54,7 +54,7 @@ exports.getChannels = (req, res, next) => {
} else {
body.btc_total_limbo_balance = common.convertToBTC(body.total_limbo_balance);
}
if (req.params.channelType === 'closed') {
if (req.params.channelType === 'closed' && body.channels && body.channels.length > 0) {
body.channels.forEach(channel => {
channel.close_type = (undefined === channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type;
});

@ -10,15 +10,17 @@ function getFilesList(callback) {
let response = {all_restore_exists: false, files: []};
fs.readdir(common.selectedNode.channel_backup_path + common.path_separator + 'restore', function (err, files) {
if (err && err.code !== 'ENOENT' && err.errno !== -4058) { response = { message: 'Channels Restore List Failed!', error: err } }
files.forEach(file => {
if (!file.includes('.restored')) {
if (file === 'channel-all.bak') {
all_restore_exists = true;
} else {
files_list.push({channel_point: file.substring(8, file.length - 4).replace('-', ':')});
if(undefined !== files && files.length > 0) {
files.forEach(file => {
if (!file.includes('.restored')) {
if (file === 'channel-all.bak') {
all_restore_exists = true;
} else {
files_list.push({channel_point: file.substring(8, file.length - 4).replace('-', ':')});
}
}
}
});
});
}
response = {all_restore_exists: all_restore_exists, files: files_list};
callback(response);
});

@ -13,25 +13,33 @@ exports.getInfo = (req, res, next) => {
} else {
logger.info({fileName:'GetInfo', msg: 'Single Node Setup!'});
}
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 = (undefined === body) ? '' : JSON.stringify(body);
const search_idx = (undefined === body) ? -1 : body_str.search('Not Found');
if(undefined === body || search_idx > -1 || body.error) {
res.status(500).json({
message: "Fetching Info failed!",
error: (undefined === body || search_idx > -1) ? 'Error From Server!' : body.error
if (!options.headers || !options.headers['Grpc-Metadata-macaroon']) {
logger.error({fileName: 'GetInfo', lineNum: 17, msg: 'Get info failed due to bad or missing macaroon!'});
res.status(502).json({
message: "Fetching Info Failed!",
error: "Bad Macaroon"
});
} 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 = (undefined === body) ? '' : JSON.stringify(body);
const search_idx = (undefined === body) ? -1 : body_str.search('Not Found');
if(undefined === body || search_idx > -1 || body.error) {
res.status(500).json({
message: "Fetching Info Failed!",
error: (undefined === body || search_idx > -1) ? 'Error From Server!' : body.error
});
} else {
res.status(200).json(body);
}
})
.catch(function (err) {
return res.status(500).json({
message: "Fetching Info Failed!",
error: err.error
});
} else {
res.status(200).json(body);
}
})
.catch(function (err) {
return res.status(500).json({
message: "Fetching Info failed!",
error: err.error
});
});
}
};

@ -128,7 +128,7 @@ exports.getQueryRoutes = (req, res, next) => {
error: (undefined === body) ? 'Error From Server!' : body.error
});
}
if (undefined !== body.routes) {
if (undefined !== body.routes && body.routes.length > 0) {
body.routes.forEach(route => {
if (undefined !== route.hops) {
Promise.all(

@ -38,7 +38,7 @@ exports.listInvoices = (req, res, next) => {
error: (undefined === body || search_idx > -1) ? 'Error From Server!' : body.error
});
} else {
if (undefined !== body.invoices) {
if (undefined !== body.invoices && body.invoices.length > 0) {
body.invoices.forEach(invoice => {
invoice.creation_date_str = (undefined === invoice.creation_date) ? '' : common.convertTimestampToDate(invoice.creation_date);
invoice.settle_date_str = (undefined === invoice.settle_date) ? '' : common.convertTimestampToDate(invoice.settle_date);

@ -16,7 +16,7 @@ exports.getPayments = (req, res, next) => {
error: (undefined === body || search_idx > -1) ? 'Error From Server!' : body.error
});
} else {
if (undefined !== body.payments) {
if (undefined !== body.payments && body.payments.length > 0) {
body.payments.forEach(payment => {
payment.creation_date_str = (undefined === payment.creation_date) ? '' : common.convertTimestampToDate(payment.creation_date);
});

@ -30,7 +30,7 @@ exports.forwardingHistory = (req, res, next) => {
error: (undefined === body) ? 'Error From Server!' : body.error
});
} else {
if (undefined !== body.forwarding_events) {
if (undefined !== body.forwarding_events && body.forwarding_events.length > 0) {
body.forwarding_events.forEach(event => {
event.timestamp_str = (undefined === event.timestamp) ? '' : common.convertTimestampToDate(event.timestamp);
});

@ -16,7 +16,7 @@ exports.getTransactions = (req, res, next) => {
error: (undefined === body || search_idx > -1) ? 'Error From Server!' : body.error
});
} else {
if (undefined !== body.transactions) {
if (undefined !== body.transactions && body.transactions.length > 0) {
body.transactions.forEach(transaction => {
transaction.time_stamp_str = (undefined === transaction.time_stamp) ? '' : common.convertTimestampToDate(transaction.time_stamp);
});

@ -6,9 +6,10 @@ var options = {};
exports.genSeed = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/genseed';
if (undefined !== req.params.passphrase) {
options.form = JSON.stringify({aezeed_passphrase: atob(req.params.passphrase)});
options.url = common.getSelLNServerUrl() + '/genseed?aezeed_passphrase=' + Buffer.from(atob(req.params.passphrase)).toString('base64');
} else {
options.url = common.getSelLNServerUrl() + '/genseed';
}
request(options).then((body) => {
if(undefined === body || body.error) {
@ -86,8 +87,13 @@ exports.operateWallet = (req, res, next) => {
} else {
res.status(500).json({
message: err_message,
error: error.message
error: error.error.message ? error.error.message : error.message
});
}
});
};
exports.updateSelNodeOptions = (req, res, next) => {
let response = common.updateSelectedNodeOptions();
res.status(response.status).json({updateMessage: response.message});
}

@ -4,29 +4,25 @@
## High Level Goals
### Multi-node Management
We want to provide users an ability to manage multiple-nodes via a single UI. The idea is to provide a top-level page, which will list all the nodes which are configured for RTL. User would be able drill down to each node from that page and manage nodes from a single RTL instance.
### Improved UX
Current focus is on complete over-haul of the UX, including the mobile interfaces. Primary user persona for RTL is 'Routing Node Operator'. The UX will be optimized for effective node operation. There will be special focus on Dashboard, Channel Management and Routing tracking.
### RTL installer
Automate RTL setup so that installation process is simpler than the current method of following the steps provided in the Readme file. This should also help with configuration of nginx and letsencrypt, to enable access via https. Contribution on this is more than welcome.
We believe UX improvement is a never-ending cycle. And, we must keep the UI/UX fresh and optimal with ongoing user feedback and inputs from UX subject-matter-experts. Contribution on UX suggestions is always welcome.
### Better Mobile UX
Current UX for mobile users is less than optimal. We are attempting to provide a responsive UI, so that users can access the same app via browser on any device. This requires more UX optimization for mobile resolution.
### Automated Testing
As the functional complexity increases, we need to add automated testing to ensure quality and less bugs. Another area, where developer contribution is more than welcome.
### C-Lightning
We want to extend the RTL UI framework to other lightning node implementations as well. The current architecture can support it without major re-engineering, as we have an api abstraction layer written in nodejs.
### LND Loop Integration
LND's Loop-Out and Loop-In are important tools for channel re-balancing and will be integrated with RTL UI in the near future.
### Multi-Language Support
We can provide a customizable framework for multi-language support. But to extend support for other languages would require contribution from the development community, to use the framework and create multi-language support.
### Ongoing UX improvement
We believe UX improvement is a never-ending cycle. And, we must keep the UI/UX fresh and optimal with ongoing user feedback and inputs from UX subject-matter-experts. Contribution on UX suggestions is more than welcome and we have created a project to exclusively focus on UX issues and priorities.
Automated Testing As the functional complexity increases, we need to add automated testing to ensure quality and less bugs. Another area, where developer contribution is more than welcome.
## Feature Backlog
### Channel re-balancing
- Loop Out
### Invoice Management
- Lookup Invoice
### Network
- Network explorer
We can provide a customizable framework for multi-language support. But to extend support for other languages would require contribution from the development community, to use the framework and create multi-language support
### Advanced Node Monitoring
Active node monitoring may be required to ensure reliability of routing nodes. Monitoring can include generating alerts for out-of-balance channels, inactive channels, disconnected peers, low activity channels etc. This feature will be required for professional node operaters running commercial routing nodes with a need to react to signals, requiring specific action to be taken.
### Advanced Multi-node Management
RTL currently allows managing multiple nodes (LND or C-Lightning), via single UI. More sophistication can be built on multi-node management, with advanced top level dashboards, which summarize node level summary in a single dashboard. This feature may be required for professional node operators, who are running commercial routing nodes.
### RTL installer
Automate RTL setup so that installation process is simpler than the current method of following the steps provided in the Readme file. This should also help with configuration of nginx and letsencrypt, to enable access via https. Contribution on this is more than welcome.

623
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "rtl",
"version": "0.5.4-beta",
"version": "0.5.5-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -192,12 +192,6 @@
"requires": {
"tslib": "^1.9.0"
}
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
}
}
},
@ -2907,12 +2901,6 @@
"yallist": "^3.0.2"
}
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@ -2968,9 +2956,10 @@
"dev": true
},
"camelcase": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
"integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"camelcase-keys": {
"version": "2.1.0",
@ -2979,6 +2968,13 @@
"requires": {
"camelcase": "^2.0.0",
"map-obj": "^1.0.0"
},
"dependencies": {
"camelcase": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
"integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
}
}
},
"caniuse-lite": {
@ -3143,13 +3139,47 @@
"dev": true
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
"integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
"dev": true,
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"string-width": "^2.1.1",
"strip-ansi": "^4.0.0",
"wrap-ansi": "^2.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
"clone": {
@ -3182,9 +3212,9 @@
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"codelyzer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.1.2.tgz",
"integrity": "sha512-1z7mtpwxcz5uUqq0HLO0ifj/tz2dWEmeaK+8c5TEZXAwwVxrjjg0118ODCOCCOcpfYaaEHxStNCaWVYo9FUPXw==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.0.tgz",
"integrity": "sha512-izfUfhEOOgAizszPlEDxo71DK/C4wprZw0vkY6UWcOSTQvN1JyfXf9DXwaV7WX+/JC+hH0ShXfdtGLA9Rca7LA==",
"dev": true,
"requires": {
"app-root-path": "^2.2.1",
@ -3507,18 +3537,6 @@
"is-directory": "^0.3.1",
"js-yaml": "^3.13.1",
"parse-json": "^4.0.0"
},
"dependencies": {
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
}
}
},
"create-ecdh": {
@ -4257,23 +4275,6 @@
"dev": true,
"requires": {
"path-type": "^3.0.0"
},
"dependencies": {
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"requires": {
"pify": "^3.0.0"
}
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
}
}
},
"dns-equal": {
@ -4367,9 +4368,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"electron-to-chromium": {
"version": "1.3.287",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.287.tgz",
"integrity": "sha512-0XlYcw6fJQ5Vh735iYJTMHysTMYjCwBFdOtyxhGbE3I9MQ2Wk2WXYIXjY1w8votvzh0fsQRaSVJ/bKUj073E9w==",
"version": "1.3.296",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.296.tgz",
"integrity": "sha512-s5hv+TSJSVRsxH190De66YHb50pBGTweT9XGWYu/LMR20KX6TsjFzObo36CjVAzM+PUeeKSBRtm/mISlCzeojQ==",
"dev": true
},
"elliptic": {
@ -4969,12 +4970,12 @@
}
},
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"path-exists": "^2.0.0",
"pinkie-promise": "^2.0.0"
"locate-path": "^3.0.0"
}
},
"flatted": {
@ -5495,9 +5496,9 @@
}
},
"hosted-git-info": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
"integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w=="
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
"integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg=="
},
"hpack.js": {
"version": "2.1.6",
@ -5608,9 +5609,9 @@
"dev": true
},
"https-proxy-agent": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz",
"integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==",
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"dev": true,
"requires": {
"agent-base": "^4.3.0",
@ -5904,9 +5905,10 @@
}
},
"invert-kv": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
"dev": true
},
"ip": {
"version": "1.1.5",
@ -7415,11 +7417,12 @@
}
},
"lcid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
"integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"dev": true,
"requires": {
"invert-kv": "^1.0.0"
"invert-kv": "^2.0.0"
}
},
"less": {
@ -7496,6 +7499,16 @@
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0",
"strip-bom": "^2.0.0"
},
"dependencies": {
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
"requires": {
"error-ex": "^1.2.0"
}
}
}
},
"loader-runner": {
@ -8219,6 +8232,16 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
},
"tar": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"requires": {
"block-stream": "*",
"fstream": "^1.0.12",
"inherits": "2"
}
}
}
},
@ -8262,9 +8285,9 @@
}
},
"node-releases": {
"version": "1.1.36",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.36.tgz",
"integrity": "sha512-ggXhX6QGyJSjj3r+6ml2LqqC28XOWmKtpb+a15/Zpr9V3yoNazxJNlcQDS9bYaid5FReEWHEgToH1mwoUceWwg==",
"version": "1.1.39",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.39.tgz",
"integrity": "sha512-8MRC/ErwNCHOlAFycy9OPca46fQYUjbJRDcZTHVWIGXIjYLM73k70vv3WkYutVnM4cCo4hE0MqBVVZjP6vjISA==",
"dev": true,
"requires": {
"semver": "^6.3.0"
@ -8279,9 +8302,9 @@
}
},
"node-sass": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
"integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==",
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.0.tgz",
"integrity": "sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==",
"requires": {
"async-foreach": "^0.1.3",
"chalk": "^1.1.1",
@ -8290,7 +8313,7 @@
"get-stdin": "^4.0.1",
"glob": "^7.0.3",
"in-publish": "^2.0.0",
"lodash": "^4.17.11",
"lodash": "^4.17.15",
"meow": "^3.7.0",
"mkdirp": "^0.5.1",
"nan": "^2.13.2",
@ -9286,11 +9309,14 @@
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
},
"os-locale": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"dev": true,
"requires": {
"lcid": "^1.0.0"
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
}
},
"os-tmpdir": {
@ -9418,21 +9444,6 @@
"yallist": "^3.0.2"
}
},
"tar": {
"version": "4.4.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"dev": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.8.6",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
}
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@ -9473,11 +9484,13 @@
}
},
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.2.0"
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"parse5": {
@ -9559,13 +9572,20 @@
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0"
"pify": "^3.0.0"
},
"dependencies": {
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
}
}
},
"pbkdf2": {
@ -9622,17 +9642,6 @@
"dev": true,
"requires": {
"find-up": "^3.0.0"
},
"dependencies": {
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
}
}
},
"portfinder": {
@ -10167,6 +10176,18 @@
"load-json-file": "^1.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^1.0.0"
},
"dependencies": {
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
"requires": {
"graceful-fs": "^4.1.2",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
}
}
},
"read-pkg-up": {
@ -10176,6 +10197,17 @@
"requires": {
"find-up": "^1.0.0",
"read-pkg": "^1.0.0"
},
"dependencies": {
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
"requires": {
"path-exists": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
}
}
},
"readable-stream": {
@ -10556,6 +10588,82 @@
"lodash": "^4.0.0",
"scss-tokenizer": "^0.2.3",
"yargs": "^7.0.0"
},
"dependencies": {
"camelcase": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo="
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wrap-ansi": "^2.0.0"
}
},
"invert-kv": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
},
"lcid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
"requires": {
"invert-kv": "^1.0.0"
}
},
"os-locale": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
"requires": {
"lcid": "^1.0.0"
}
},
"which-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8="
},
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
},
"yargs": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
"integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
"requires": {
"camelcase": "^3.0.0",
"cliui": "^3.2.0",
"decamelize": "^1.1.1",
"get-caller-file": "^1.0.1",
"os-locale": "^1.4.0",
"read-pkg-up": "^1.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^1.0.2",
"which-module": "^1.0.0",
"y18n": "^3.2.1",
"yargs-parser": "^5.0.0"
}
},
"yargs-parser": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
"integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
"requires": {
"camelcase": "^3.0.0"
}
}
}
},
"sass-loader": {
@ -10613,6 +10721,16 @@
"requires": {
"js-base64": "^2.1.8",
"source-map": "^0.4.2"
},
"dependencies": {
"source-map": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"requires": {
"amdefine": ">=0.0.4"
}
}
}
},
"select-hose": {
@ -11177,12 +11295,10 @@
"dev": true
},
"source-map": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"requires": {
"amdefine": ">=0.0.4"
}
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
},
"source-map-loader": {
"version": "0.2.4",
@ -11676,13 +11792,26 @@
"dev": true
},
"tar": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"version": "4.4.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"dev": true,
"requires": {
"block-stream": "*",
"fstream": "^1.0.12",
"inherits": "2"
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.8.6",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
},
"dependencies": {
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
}
}
},
"term-size": {
@ -13165,6 +13294,15 @@
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz",
"integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=",
"dev": true
},
"source-map": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"dev": true,
"requires": {
"amdefine": ">=0.0.4"
}
}
}
},
@ -13227,12 +13365,6 @@
"yargs": "12.0.5"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"anymatch": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
@ -13278,12 +13410,6 @@
"to-regex": "^3.0.1"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"chokidar": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
@ -13304,28 +13430,6 @@
"upath": "^1.1.1"
}
},
"cliui": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
"integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
"dev": true,
"requires": {
"string-width": "^2.1.1",
"strip-ansi": "^4.0.0",
"wrap-ansi": "^2.0.0"
},
"dependencies": {
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@ -13356,15 +13460,6 @@
"to-regex-range": "^2.1.0"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"fsevents": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
@ -13913,12 +14008,6 @@
}
}
},
"invert-kv": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
"dev": true
},
"is-binary-path": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
@ -13928,12 +14017,6 @@
"binary-extensions": "^1.0.0"
}
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"is-number": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
@ -13952,32 +14035,12 @@
"is-buffer": "^1.1.5"
}
},
"lcid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
"integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"dev": true,
"requires": {
"invert-kv": "^2.0.0"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"os-locale": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"dev": true,
"requires": {
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
}
},
"readdirp": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
@ -13995,27 +14058,6 @@
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
},
"dependencies": {
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
@ -14034,42 +14076,6 @@
"is-number": "^3.0.0",
"repeat-string": "^1.6.1"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
},
"yargs": {
"version": "12.0.5",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
"integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
"dev": true,
"requires": {
"cliui": "^4.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^1.0.1",
"os-locale": "^3.0.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^2.0.0",
"which-module": "^2.0.0",
"y18n": "^3.2.1 || ^4.0.0",
"yargs-parser": "^11.1.1"
}
},
"yargs-parser": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
"integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
@ -14151,9 +14157,10 @@
}
},
"which-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
},
"wide-align": {
"version": "1.1.3",
@ -14305,9 +14312,10 @@
"dev": true
},
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yallist": {
"version": "2.1.2",
@ -14315,45 +14323,66 @@
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
},
"yargs": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
"integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
"version": "12.0.5",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
"integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
"dev": true,
"requires": {
"camelcase": "^3.0.0",
"cliui": "^3.2.0",
"decamelize": "^1.1.1",
"cliui": "^4.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^1.0.1",
"os-locale": "^1.4.0",
"read-pkg-up": "^1.0.1",
"os-locale": "^3.0.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^1.0.2",
"which-module": "^1.0.0",
"y18n": "^3.2.1",
"yargs-parser": "^5.0.0"
"string-width": "^2.0.0",
"which-module": "^2.0.0",
"y18n": "^3.2.1 || ^4.0.0",
"yargs-parser": "^11.1.1"
},
"dependencies": {
"camelcase": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo="
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
"yargs-parser": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
"integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
"integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
"dev": true,
"requires": {
"camelcase": "^3.0.0"
},
"dependencies": {
"camelcase": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo="
}
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
},
"yeast": {

@ -1,6 +1,6 @@
{
"name": "rtl",
"version": "0.5.4-beta",
"version": "0.5.5-beta",
"license": "MIT",
"scripts": {
"ng": "ng",
@ -45,7 +45,7 @@
"jsonwebtoken": "^8.4.0",
"material-design-icons": "^3.0.1",
"ngx-perfect-scrollbar": "^6.3.1",
"node-sass": "^4.12.0",
"node-sass": "^4.13.0",
"nodemon": "^1.19.4",
"optimist": "^0.6.1",
"request-promise": "^4.2.2",
@ -65,7 +65,7 @@
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "^2.0.8",
"@types/node": "~8.9.4",
"codelyzer": "^5.1.2",
"codelyzer": "^5.2.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",

@ -4,6 +4,7 @@ const router = express.Router();
const authCheck = require("../authCheck");
router.get("/genseed/:passphrase?", authCheck, WalletController.genSeed);
router.get("/updateSelNodeOptions", authCheck, WalletController.updateSelNodeOptions);
router.post("/:operation", authCheck, WalletController.operateWallet);
module.exports = router;

@ -9,6 +9,7 @@ import { UserIdleService } from 'angular-user-idle';
import * as sha256 from 'sha256';
import { LoggerService } from './shared/services/logger.service';
import { SessionService } from './shared/services/session.service';
import { RTLConfiguration, Settings, LightningNode, GetInfoRoot } from './shared/models/RTLconfig';
import * as RTLActions from './store/rtl.actions';
@ -33,7 +34,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
unsubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions,
private userIdle: UserIdleService, private router: Router, private activatedRoute: ActivatedRoute) {}
private userIdle: UserIdleService, private router: Router, private sessionService: SessionService) {}
ngOnInit() {
this.store.dispatch(new RTLActions.FetchRTLConfig());
@ -55,15 +56,16 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
this.smallScreen = true;
}
this.logger.info(this.settings);
if (!sessionStorage.getItem('token')) {
if (!this.sessionService.getItem('token')) {
this.flgLoading[0] = false;
}
});
this.actions$.pipe(takeUntil(this.unsubs[1]),
filter((action) => action.type === RTLActions.SET_RTL_CONFIG))
.subscribe((action: (RTLActions.SetRTLConfig)) => {
if (action.type === RTLActions.SET_RTL_CONFIG) {
if (!sessionStorage.getItem('token')) {
if (!this.sessionService.getItem('token')) {
if (+action.payload.sso.rtlSSO) {
this.store.dispatch(new RTLActions.Signin(sha256(this.accessKey)));
} else {
@ -82,7 +84,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
this.userIdle.startWatching();
this.userIdle.onTimerStart().subscribe(count => {});
this.userIdle.onTimeout().subscribe(() => {
if (sessionStorage.getItem('token')) {
if (this.sessionService.getItem('token')) {
this.logger.warn('Time limit exceeded for session inactivity! Logging out!');
this.store.dispatch(new RTLActions.OpenAlert({ width: '75%', data: {
type: 'WARN',

@ -23,7 +23,7 @@ import { ThemeOverlay } from './shared/theme/overlay-container/theme-overlay';
import { AppComponent } from './app.component';
import { environment } from '../environments/environment';
import { CommonService } from './shared/services/common.service';
import { SessionService } from './shared/services/session.service';
import { LoggerService, ConsoleLoggerService } from './shared/services/logger.service';
import { AuthGuard } from './shared/services/auth.guard';
import { AuthInterceptor } from './shared/services/auth.interceptor';
@ -53,7 +53,7 @@ import { CLEffects } from './clightning/store/cl.effects';
{ provide: OverlayContainer, useClass: ThemeOverlay },
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: DEFAULT_PERFECT_SCROLLBAR_CONFIG },
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
AuthGuard, CommonService
AuthGuard, SessionService
],
bootstrap: [AppComponent]
})

@ -6,7 +6,7 @@ import { NotFoundComponent } from './shared/components/not-found/not-found.compo
import { ServerConfigComponent } from './shared/components/server-config/server-config.component';
import { HelpComponent } from './shared/components/help/help.component';
import { SigninComponent } from './shared/components/signin/signin.component';
import { SsoFailedComponent } from './shared/components/sso-failed/sso-failed.component';
import { ErrorComponent } from './shared/components/error/error.component';
import { AuthGuard } from './shared/services/auth.guard';
export const routes: Routes = [
@ -16,7 +16,7 @@ export const routes: Routes = [
{ path: 'settings', component: AppSettingsComponent, canActivate: [AuthGuard] },
{ path: 'help', component: HelpComponent },
{ path: 'login', component: SigninComponent },
{ path: 'ssoerror', component: SsoFailedComponent },
{ path: 'error', component: ErrorComponent },
{ path: '**', component: NotFoundComponent }
];

@ -1,46 +1,10 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import * as RTLActions from '../store/rtl.actions';
import * as fromRTLReducer from '../store/rtl.reducers';
import { Component } from '@angular/core';
@Component({
selector: 'rtl-cl-root',
templateUrl: './cl-root.component.html',
styleUrls: ['./cl-root.component.scss']
})
export class CLRootComponent implements OnInit, OnDestroy {
unsubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private store: Store<fromRTLReducer.RTLState>, private actions$: Actions, private router: Router, private activatedRoute: ActivatedRoute) {}
ngOnInit() {
this.store.dispatch(new RTLActions.FetchInfoCL());
this.actions$.pipe(takeUntil(this.unsubs[0]), filter((action) => action.type === RTLActions.SET_INFO_CL))
.subscribe((infoData: RTLActions.SetInfoCL) => {
if(infoData.type === RTLActions.SET_INFO_CL && undefined !== infoData.payload.id) {
this.initializeRemainingData();
}
});
}
initializeRemainingData() {
this.store.dispatch(new RTLActions.FetchFeesCL());
this.store.dispatch(new RTLActions.FetchBalanceCL());
this.store.dispatch(new RTLActions.FetchLocalRemoteBalanceCL());
this.store.dispatch(new RTLActions.FetchFeeRatesCL('perkw'));
this.store.dispatch(new RTLActions.FetchFeeRatesCL('perkb'));
this.store.dispatch(new RTLActions.FetchPeersCL());
}
ngOnDestroy() {
this.unsubs.forEach(unsub => {
unsub.next();
unsub.complete();
});
}
export class CLRootComponent {
constructor() {}
}

@ -1,12 +1,14 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
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 { LoggerService } from '../../shared/services/logger.service';
import { GetInfoCL, FeesCL, BalanceCL, LocalRemoteBalanceCL, FeeRatesCL } from '../../shared/models/clModels';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
@Component({
@ -19,14 +21,14 @@ export class CLHomeComponent implements OnInit, OnDestroy {
public fees: FeesCL;
public information: GetInfoCL = {};
public totalBalance: BalanceCL = {};
public lrBalance: LocalRemoteBalanceCL = {};
public lrBalance: LocalRemoteBalanceCL = { localBalance: 0, remoteBalance: 0 };
public flgLoading: Array<Boolean | 'error'> = [true, true, true, true, true];
private unsub: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
public position = 'below';
barPadding = 0;
maxBalanceValue = 0;
lrBalances = [...[{'name': 'Local Balance', 'value': 0}, {'name': 'Remote Balance', 'value': 0}]];
lrBalances = [{'name': 'Local Balance', 'value': 0}, {'name': 'Remote Balance', 'value': 0}];
flgTotalCalculated = false;
view = [];
yAxisLabel = 'Balance';
@ -34,7 +36,7 @@ export class CLHomeComponent implements OnInit, OnDestroy {
feeRatesPerKB: FeeRatesCL = {};
feeRatesPerKW: FeeRatesCL = {};
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>) {
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions) {
switch (true) {
case (window.innerWidth <= 730):
this.view = [250, 352];
@ -56,7 +58,11 @@ export class CLHomeComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.flgTotalCalculated = false;
this.actions$.pipe(takeUntil(this.unsub[0]),
filter(action => action.type === RTLActions.SET_SELECTED_NODE))
.subscribe((data) => {
this.flgTotalCalculated = false;
});
this.store.select('cl')
.pipe(takeUntil(this.unsub[0]))
.subscribe((rtlStore) => {
@ -96,9 +102,9 @@ export class CLHomeComponent implements OnInit, OnDestroy {
this.lrBalance = rtlStore.localRemoteBalance;
this.maxBalanceValue = (rtlStore.localRemoteBalance.localBalance > rtlStore.localRemoteBalance.remoteBalance) ? rtlStore.localRemoteBalance.localBalance : rtlStore.localRemoteBalance.remoteBalance;
this.lrBalances = [...[{'name': 'Local Balance', 'value': +rtlStore.localRemoteBalance.localBalance}, {'name': 'Remote Balance', 'value': +rtlStore.localRemoteBalance.remoteBalance}]];
this.lrBalances = [{'name': 'Local Balance', 'value': +rtlStore.localRemoteBalance.localBalance}, {'name': 'Remote Balance', 'value': +rtlStore.localRemoteBalance.remoteBalance}];
if (this.flgLoading[3] !== 'error') {
this.flgLoading[3] = ('' !== this.lrBalance) ? false : true;
this.flgLoading[3] = (this.lrBalance.localBalance >= 0) ? false : true;
}
this.feeRatesPerKB = rtlStore.feeRatesPerKB;

@ -1,13 +1,16 @@
import { Injectable, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { Subject, of } from 'rxjs';
import { map, mergeMap, catchError, withLatestFrom } from 'rxjs/operators';
import { formatDate } from '@angular/common';
import { Location } from '@angular/common';
import { environment, API_URL } from '../../../environments/environment';
import { LoggerService } from '../../shared/services/logger.service';
import { SessionService } from '../../shared/services/session.service';
import { GetInfoCL, FeesCL, BalanceCL, LocalRemoteBalanceCL, PaymentCL, FeeRatesCL, ListInvoicesCL, InvoiceCL } from '../../shared/models/clModels';
import * as fromRTLReducer from '../../store/rtl.reducers';
@ -22,51 +25,47 @@ export class CLEffects implements OnDestroy {
private actions$: Actions,
private httpClient: HttpClient,
private store: Store<fromRTLReducer.RTLState>,
private logger: LoggerService) { }
private sessionService: SessionService,
private logger: LoggerService,
private router: Router,
private location: Location) { }
@Effect()
infoFetchCL = this.actions$.pipe(
ofType(RTLActions.FETCH_INFO_CL),
withLatestFrom(this.store.select('root')),
mergeMap(([action, store]) => {
mergeMap(([action, store]: [RTLActions.FetchInfoCL, fromRTLReducer.RootState]) => {
this.store.dispatch(new RTLActions.ClearEffectErrorCl('FetchInfoCL'));
return this.httpClient.get<GetInfoCL>(this.CHILD_API_URL + environment.GETINFO_API)
.pipe(
map((info) => {
this.logger.info(info);
let chainObj = { chain: '', network: '' };
if (info.network === 'testnet') {
chainObj.chain = 'Bitcoin';
chainObj.network = 'Testnet';
} else if (info.network === 'bitcoin') {
chainObj.chain = 'Bitcoin';
chainObj.network = 'Mainnet';
} else if (info.network === 'litecoin') {
chainObj.chain = 'Litecoin';
chainObj.network = 'Mainnet';
} else if (info.network === 'litecoin-testnet') {
chainObj.chain = 'Litecoin';
chainObj.network = 'Testnet';
}
sessionStorage.setItem('clUnlocked', 'true');
const node_data = {
identity_pubkey: info.id,
alias: info.alias,
testnet: (info.network === 'testnet' || info.network === 'litecoin-testnet') ? true : false,
chains: [chainObj],
version: info.version,
currency_unit: 'BTC',
smaller_currency_unit: 'Sats',
numberOfPendingChannels: info.num_pending_channels
};
this.store.dispatch(new RTLActions.SetNodeData(node_data));
this.initializeRemainingData(info, action.payload.loadPage);
return {
type: RTLActions.SET_INFO_CL,
payload: (undefined !== info) ? info : {}
};
}),
catchError((err) => {
return this.handleErrorWithAlert('ERROR', 'Get Info Failed', this.CHILD_API_URL + environment.GETINFO_API, 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;
}
}
this.router.navigate(['/error'], { state: { errorCode: code, errorMessage: message }});
this.handleErrorWithoutAlert('FetchInfoCL', err);
return of({type: RTLActions.VOID});
})
);
}
@ -87,7 +86,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithoutAlert('FetchFeesCL', err);
this.handleErrorWithoutAlert('FetchFeesCL', err);
return of({type: RTLActions.VOID});
}
));
@ -106,7 +106,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithoutAlert('FetchFeeRatesCL', err);
this.handleErrorWithoutAlert('FetchFeeRatesCL', err);
return of({type: RTLActions.VOID});
}
));
@ -125,7 +126,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithoutAlert('FetchBalanceCL', err);
this.handleErrorWithoutAlert('FetchBalanceCL', err);
return of({type: RTLActions.VOID});
}
));
@ -144,7 +146,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithoutAlert('FetchLocalRemoteBalanceCL', err);
this.handleErrorWithoutAlert('FetchLocalRemoteBalanceCL', err);
return of({type: RTLActions.VOID});
}
));
@ -162,7 +165,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Generate New Address Failed', this.CHILD_API_URL + environment.ON_CHAIN_API + '?type=' + action.payload.addressId, err);
this.handleErrorWithAlert('ERROR', 'Generate New Address Failed', this.CHILD_API_URL + environment.ON_CHAIN_API + '?type=' + action.payload.addressId, err);
return of({type: RTLActions.VOID});
}));
})
);
@ -191,7 +195,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithoutAlert('FetchPeersCL', err);
this.handleErrorWithoutAlert('FetchPeersCL', err);
return of({type: RTLActions.VOID});
})
);
}
@ -213,7 +218,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Add Peer Failed', this.CHILD_API_URL + environment.PEERS_API, err);
this.handleErrorWithAlert('ERROR', 'Add Peer Failed', this.CHILD_API_URL + environment.PEERS_API, err);
return of({type: RTLActions.VOID});
})
);
}
@ -235,7 +241,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Unable to Detach Peer. Try again later.', this.CHILD_API_URL + environment.PEERS_API + '/' + action.payload.id, err);
this.handleErrorWithAlert('ERROR', 'Unable to Detach Peer. Try again later.', this.CHILD_API_URL + environment.PEERS_API + '/' + action.payload.id, err);
return of({type: RTLActions.VOID});
})
);
}
@ -256,7 +263,8 @@ export class CLEffects implements OnDestroy {
};
},
catchError((err: any) => {
return this.handleErrorWithoutAlert('FetchChannelsCL', err);
this.handleErrorWithoutAlert('FetchChannelsCL', err);
return of({type: RTLActions.VOID});
})
));
}
@ -278,7 +286,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Open Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API, err);
this.handleErrorWithAlert('ERROR', 'Open Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API, err);
return of({type: RTLActions.VOID});
})
);
}
@ -300,7 +309,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Update Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API, err);
this.handleErrorWithAlert('ERROR', 'Update Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API, err);
return of({type: RTLActions.VOID});
})
);
}
@ -323,7 +333,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Unable to Close Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_API, err);
this.handleErrorWithAlert('ERROR', 'Unable to Close Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_API, err);
return of({type: RTLActions.VOID});
})
);
}
@ -344,9 +355,10 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithoutAlert('FetchPaymentsCL', err);
this.handleErrorWithoutAlert('FetchPaymentsCL', err);
return of({type: RTLActions.VOID});
}
));
));
@Effect()
decodePaymentCL = this.actions$.pipe(
@ -363,7 +375,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Decode Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API + '/' + action.payload, err);
this.handleErrorWithAlert('ERROR', 'Decode Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API + '/' + action.payload, err);
return of({type: RTLActions.VOID});
})
);
})
@ -389,7 +402,7 @@ export class CLEffects implements OnDestroy {
this.logger.info(sendRes);
this.store.dispatch(new RTLActions.CloseSpinner());
if (sendRes.error) {
return this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API, { status: sendRes.status, error: sendRes.error.message });
this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API, { status: sendRes.status, error: sendRes.error.message });
} else {
this.store.dispatch(new RTLActions.OpenAlert({
width: '70%',
@ -405,7 +418,8 @@ export class CLEffects implements OnDestroy {
}
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API, err);
this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.PAYMENTS_API, err);
return of({type: RTLActions.VOID});
})
);
})
@ -426,7 +440,8 @@ export class CLEffects implements OnDestroy {
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.SetQueryRoutesCL({ routes: [] }));
return this.handleErrorWithAlert('ERROR', 'Get Query Routes Failed', this.CHILD_API_URL + environment.NETWORK_API + '/getRoute/' + action.payload.destPubkey + '/' + action.payload.amount, err);
this.handleErrorWithAlert('ERROR', 'Get Query Routes Failed', this.CHILD_API_URL + environment.NETWORK_API + '/getRoute/' + action.payload.destPubkey + '/' + action.payload.amount, err);
return of({type: RTLActions.VOID});
})
);
}
@ -457,7 +472,8 @@ export class CLEffects implements OnDestroy {
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorCl({ action: 'LookupCL', code: err.status, message: err.error.message }));
return this.handleErrorWithAlert('ERROR', 'Peer Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/listNode/' + action.payload, err);
this.handleErrorWithAlert('ERROR', 'Peer Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/listNode/' + action.payload, err);
return of({type: RTLActions.VOID});
})
);
})
@ -480,7 +496,8 @@ export class CLEffects implements OnDestroy {
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorCl({ action: 'LookupCL', code: err.status, message: err.error.message }));
return this.handleErrorWithAlert('ERROR', 'Channel Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/listChannel/' + action.payload, err);
this.handleErrorWithAlert('ERROR', 'Channel Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/listChannel/' + action.payload, err);
return of({type: RTLActions.VOID});
})
);
})
@ -503,7 +520,8 @@ export class CLEffects implements OnDestroy {
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorCl({ action: 'LookupCL', code: err.status, message: err.error.message }));
return this.handleErrorWithAlert('ERROR', 'Invoice Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/listInvoice?label=' + action.payload, err);
this.handleErrorWithAlert('ERROR', 'Invoice Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/listInvoice?label=' + action.payload, err);
return of({type: RTLActions.VOID});
})
);
})
@ -538,7 +556,8 @@ export class CLEffects implements OnDestroy {
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorCl({ action: 'GetForwardingHistory', code: err.status, message: err.error.error }));
return this.handleErrorWithAlert('ERROR', 'Get Forwarding History Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/listForwards', err);
this.handleErrorWithAlert('ERROR', 'Get Forwarding History Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/listForwards', err);
return of({type: RTLActions.VOID});
})
);
})
@ -564,7 +583,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Delete Invoice Failed', this.CHILD_API_URL + environment.INVOICES_API, err);
this.handleErrorWithAlert('ERROR', 'Delete Invoice Failed', this.CHILD_API_URL + environment.INVOICES_API, err);
return of({type: RTLActions.VOID});
})
);
}
@ -596,7 +616,8 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithAlert('ERROR', 'Add Invoice Failed', this.CHILD_API_URL + environment.INVOICES_API, err);
this.handleErrorWithAlert('ERROR', 'Add Invoice Failed', this.CHILD_API_URL + environment.INVOICES_API, err);
return of({type: RTLActions.VOID});
})
);
}
@ -620,9 +641,10 @@ export class CLEffects implements OnDestroy {
};
}),
catchError((err: any) => {
return this.handleErrorWithoutAlert('FetchInvoicesCL', err);
this.handleErrorWithoutAlert('FetchInvoicesCL', err);
return of({type: RTLActions.VOID});
}
));
));
}));
@Effect()
@ -643,20 +665,63 @@ export class CLEffects implements OnDestroy {
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorCl({ action: 'SetChannelTransactionCL', code: err.status, message: err.error.error }));
return this.handleErrorWithAlert('ERROR', 'Sending Fund Failed', this.CHILD_API_URL + environment.ON_CHAIN_API, err);
this.handleErrorWithAlert('ERROR', 'Sending Fund Failed', this.CHILD_API_URL + environment.ON_CHAIN_API, err);
return of({type: RTLActions.VOID});
}));
})
);
initializeRemainingData(info: any, landingPage: string) {
this.sessionService.setItem('clUnlocked', 'true');
let chainObj = { chain: '', network: '' };
if (info.network === 'testnet') {
chainObj.chain = 'Bitcoin';
chainObj.network = 'Testnet';
} else if (info.network === 'bitcoin') {
chainObj.chain = 'Bitcoin';
chainObj.network = 'Mainnet';
} else if (info.network === 'litecoin') {
chainObj.chain = 'Litecoin';
chainObj.network = 'Mainnet';
} else if (info.network === 'litecoin-testnet') {
chainObj.chain = 'Litecoin';
chainObj.network = 'Testnet';
}
const node_data = {
identity_pubkey: info.id,
alias: info.alias,
testnet: (info.network === 'testnet' || info.network === 'litecoin-testnet') ? true : false,
chains: [chainObj],
version: info.version,
currency_unit: 'BTC',
smaller_currency_unit: 'Sats',
numberOfPendingChannels: info.num_pending_channels
};
this.store.dispatch(new RTLActions.SetNodeData(node_data));
this.store.dispatch(new RTLActions.FetchFeesCL());
this.store.dispatch(new RTLActions.FetchBalanceCL());
this.store.dispatch(new RTLActions.FetchLocalRemoteBalanceCL());
this.store.dispatch(new RTLActions.FetchFeeRatesCL('perkw'));
this.store.dispatch(new RTLActions.FetchFeeRatesCL('perkb'));
this.store.dispatch(new RTLActions.FetchPeersCL());
let newRoute = this.location.path();
if (newRoute.includes('/login') || newRoute.includes('/error') || newRoute === '' || landingPage === 'HOME' || newRoute.includes('?access-key=')) {
newRoute = '/cl/home';
} else {
if(newRoute.includes('/lnd/')) {
newRoute = newRoute.replace('/lnd/', '/cl/');
}
}
this.router.navigate([newRoute]);
}
handleErrorWithoutAlert(actionName: string, err: { status: number, error: any }) {
this.logger.error(err);
this.logger.error('ERROR IN: ' + actionName + '\n' + JSON.stringify(err));
if (err.status === 401) {
this.logger.info('Redirecting to Signin');
return of({ type: RTLActions.SIGNOUT });
this.store.dispatch(new RTLActions.Signout());
} else {
this.store.dispatch(new RTLActions.EffectErrorCl({ action: actionName, code: err.status.toString(), message: err.error.error }));
this.logger.error(err);
return of({ type: RTLActions.VOID });
}
}
@ -664,21 +729,15 @@ export class CLEffects implements OnDestroy {
this.logger.error(err);
if (err.status === 401) {
this.logger.info('Redirecting to Signin');
return of({ type: RTLActions.SIGNOUT });
this.store.dispatch(new RTLActions.Signout());
} else {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: alerType, titleMessage: alertTitle,
message: JSON.stringify({ code: err.status, Message: err.error.error, URL: errURL })
}
}
this.store.dispatch(new RTLActions.OpenAlert({
width: '70%', data: {
type: alerType, titleMessage: alertTitle,
message: JSON.stringify({ code: err.status, Message: err.error.error, URL: errURL })
}
);
}));
}
}

@ -30,7 +30,7 @@ export const initCLState: CLState = {
feeRatesPerKB: {},
feeRatesPerKW: {},
balance: {},
localRemoteBalance: {},
localRemoteBalance: { localBalance: -1, remoteBalance: -1 },
peers: [],
allChannels: [],
payments: [],

@ -1,10 +1,8 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Router, NavigationStart, ActivatedRoute } from '@angular/router';
import { Subject, Observable } from 'rxjs';
import { takeUntil, filter, map, subscribeOn } from 'rxjs/operators';
import { ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatTableDataSource, MatSort } from '@angular/material';
import { Channel, Peer, GetInfo } from '../../../shared/models/lndModels';

@ -40,11 +40,6 @@ export class ChannelRestoreComponent implements OnInit {
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
this.selNode = rtlStore.nodeSettings;
// rtlStore.effectErrorsLnd.forEach(effectsErr => {
// if (effectsErr.action === 'RestoreChannelsList') {
// this.flgLoading[0] = 'error';
// }
// });
this.logger.info(rtlStore);
});
this.lndEffects.setRestoreChannelList

@ -135,7 +135,7 @@
<mat-divider></mat-divider>
</mat-list>
</div>
<mat-progress-bar *ngIf="flgLoading[6]===true" mode="indeterminate" class="mt-minus-5"></mat-progress-bar>
<mat-progress-bar *ngIf="flgLoading[5]===true" mode="indeterminate" class="mt-minus-5"></mat-progress-bar>
<mat-divider></mat-divider>
</mat-card-content>
</mat-card>

@ -1,12 +1,14 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
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 { LoggerService } from '../../shared/services/logger.service';
import { GetInfo, NetworkInfo, Fees, Peer } from '../../shared/models/lndModels';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
@Component({
@ -20,10 +22,10 @@ export class HomeComponent implements OnInit, OnDestroy {
public information: GetInfo = {};
public remainder = 0;
public totalPeers = -1;
public totalBalance = '';
public channelBalance = '';
public BTCtotalBalance = '';
public BTCchannelBalance = '';
public totalBalance = 0;
public channelBalance = 0;
public BTCtotalBalance = 0;
public BTCchannelBalance = 0;
public networkInfo: NetworkInfo = {};
public flgLoading: Array<Boolean | 'error'> = [true, true, true, true, true, true, true, true]; // 0: Info, 1: Fee, 2: Wallet, 3: Channel, 4: Network
private unsub: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
@ -35,13 +37,13 @@ export class HomeComponent implements OnInit, OnDestroy {
public peers: Peer[] = [];
barPadding = 0;
maxBalanceValue = 0;
totalBalances = [...[{'name': 'Local Balance', 'value': 0}, {'name': 'Remote Balance', 'value': 0}]];
totalBalances = [{'name': 'Local Balance', 'value': 0}, {'name': 'Remote Balance', 'value': 0}];
flgTotalCalculated = false;
view = [];
yAxisLabel = 'Balance';
colorScheme = {domain: ['#FFFFFF']};
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>) {
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions) {
switch (true) {
case (window.innerWidth <= 730):
this.view = [250, 352];
@ -63,9 +65,13 @@ export class HomeComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.flgTotalCalculated = false;
this.actions$.pipe(takeUntil(this.unsub[0]),
filter(action => action.type === RTLActions.SET_SELECTED_NODE))
.subscribe((data) => {
this.flgTotalCalculated = false;
});
this.store.select('lnd')
.pipe(takeUntil(this.unsub[0]))
.pipe(takeUntil(this.unsub[1]))
.subscribe((rtlStore) => {
rtlStore.effectErrorsLnd.forEach(effectsErr => {
if (effectsErr.action === 'FetchInfo') {
@ -102,13 +108,13 @@ export class HomeComponent implements OnInit, OnDestroy {
this.totalBalance = rtlStore.blockchainBalance.total_balance;
this.BTCtotalBalance = rtlStore.blockchainBalance.btc_total_balance;
if (this.flgLoading[2] !== 'error') {
this.flgLoading[2] = ('' !== this.totalBalance) ? false : true;
this.flgLoading[2] = (this.totalBalance >= 0) ? false : true;
}
this.channelBalance = rtlStore.channelBalance.balance;
this.BTCchannelBalance = rtlStore.channelBalance.btc_balance;
if (this.flgLoading[3] !== 'error') {
this.flgLoading[3] = ('' !== this.channelBalance) ? false : true;
this.flgLoading[3] = (this.channelBalance >= 0) ? false : true;
}
this.networkInfo = rtlStore.networkInfo;
@ -116,9 +122,9 @@ export class HomeComponent implements OnInit, OnDestroy {
this.flgLoading[4] = (undefined !== this.networkInfo.num_nodes) ? false : true;
}
this.totalBalances = [...[{'name': 'Local Balance', 'value': +rtlStore.totalLocalBalance}, {'name': 'Remote Balance', 'value': +rtlStore.totalRemoteBalance}]];
this.maxBalanceValue = (rtlStore.totalLocalBalance > rtlStore.totalRemoteBalance) ? rtlStore.totalLocalBalance : rtlStore.totalRemoteBalance;
if (rtlStore.totalLocalBalance >= 0 && rtlStore.totalRemoteBalance >= 0) {
this.totalBalances = [{'name': 'Local Balance', 'value': rtlStore.totalLocalBalance}, {'name': 'Remote Balance', 'value': rtlStore.totalRemoteBalance}];
this.maxBalanceValue = (rtlStore.totalLocalBalance > rtlStore.totalRemoteBalance) ? rtlStore.totalLocalBalance : rtlStore.totalRemoteBalance;
this.flgTotalCalculated = true;
if (this.flgLoading[5] !== 'error') {
this.flgLoading[5] = false;

@ -80,7 +80,7 @@ export class InvoicesComponent implements OnInit, OnDestroy {
this.firstOffset = +rtlStore.invoices.first_index_offset;
this.lastOffset = +rtlStore.invoices.last_index_offset;
this.logger.info(rtlStore);
this.loadInvoicesTable(rtlStore.invoices.invoices);
this.loadInvoicesTable(rtlStore.invoices.invoices ? rtlStore.invoices.invoices : []);
if (this.flgLoading[0] !== 'error') {
this.flgLoading[0] = (undefined !== rtlStore.invoices) ? false : true;
}

@ -1,48 +1,10 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import * as RTLActions from '../store/rtl.actions';
import * as fromRTLReducer from '../store/rtl.reducers';
import { Component } from '@angular/core';
@Component({
selector: 'rtl-lnd-root',
templateUrl: './lnd-root.component.html',
styleUrls: ['./lnd-root.component.scss']
})
export class LNDRootComponent implements OnInit, OnDestroy {
unsubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private store: Store<fromRTLReducer.RTLState>, private actions$: Actions, private router: Router, private activatedRoute: ActivatedRoute) {}
ngOnInit() {
this.store.dispatch(new RTLActions.FetchInfo());
this.actions$.pipe(takeUntil(this.unsubs[0]), filter((action) => action.type === RTLActions.SET_INFO))
.subscribe((infoData: RTLActions.SetInfo) => {
if(infoData.type === RTLActions.SET_INFO && undefined !== infoData.payload.identity_pubkey) {
this.initializeRemainingData();
}
});
}
initializeRemainingData() {
this.store.dispatch(new RTLActions.FetchFees());
this.store.dispatch(new RTLActions.FetchPeers());
this.store.dispatch(new RTLActions.FetchBalance('channels'));
this.store.dispatch(new RTLActions.FetchNetwork());
this.store.dispatch(new RTLActions.FetchChannels({routeParam: 'all'}));
this.store.dispatch(new RTLActions.FetchChannels({routeParam: 'pending'}));
this.store.dispatch(new RTLActions.FetchInvoices({num_max_invoices: 25, reversed: true}));
this.store.dispatch(new RTLActions.FetchPayments());
}
ngOnDestroy() {
this.unsubs.forEach(unsub => {
unsub.next();
unsub.complete();
});
}
export class LNDRootComponent {
constructor() {}
}

@ -19,13 +19,13 @@ import { RoutingPeersComponent } from './routing-peers/routing-peers.component';
import { ChannelBackupComponent } from './channels/channel-backup/channel-backup.component';
import { ChannelRestoreComponent } from './channels/channel-restore/channel-restore.component';
import { LNDUnlockedGuard } from '../shared/services/auth.guard';
import { AuthGuard, LNDUnlockedGuard } from '../shared/services/auth.guard';
import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
export const LndRoutes: Routes = [
{ path: '', component: LNDRootComponent,
children: [
{ path: 'unlocklnd', component: UnlockLNDComponent },
{ path: 'unlocklnd', component: UnlockLNDComponent, canActivate: [AuthGuard] },
{ path: 'home', component: HomeComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'peers', component: PeersComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'chnlclosed', component: ChannelClosedComponent, canActivate: [LNDUnlockedGuard] },

@ -4,12 +4,14 @@ import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { of, Subject } from 'rxjs';
import { map, mergeMap, catchError, take, withLatestFrom } from 'rxjs/operators';
import { map, mergeMap, catchError, withLatestFrom } from 'rxjs/operators';
import { Location } from '@angular/common';
import { MatDialog } from '@angular/material';
import { environment, API_URL } from '../../../environments/environment';
import { LoggerService } from '../../shared/services/logger.service';
import { SessionService } from '../../shared/services/session.service';
import { GetInfo, GetInfoChain, Fees, Balance, NetworkInfo, Payment, GraphNode, Transaction, SwitchReq, ListInvoices } from '../../shared/models/lndModels';
import * as RTLActions from '../../store/rtl.actions';
@ -26,22 +28,23 @@ export class LNDEffects implements OnDestroy {
private httpClient: HttpClient,
private store: Store<fromRTLReducer.RTLState>,
private logger: LoggerService,
private sessionService: SessionService,
public dialog: MatDialog,
private router: Router) { }
private router: Router,
private location: Location) { }
@Effect()
infoFetch = this.actions$.pipe(
ofType(RTLActions.FETCH_INFO),
withLatestFrom(this.store.select('root')),
mergeMap(([action, store]) => {
mergeMap(([action, store]: [RTLActions.FetchInfo, fromRTLReducer.RootState]) => {
this.store.dispatch(new RTLActions.ClearEffectErrorLnd('FetchInfo'));
return this.httpClient.get<GetInfo>(this.CHILD_API_URL + environment.GETINFO_API)
.pipe(
map((info) => {
this.logger.info(info);
if (undefined === info.identity_pubkey) {
sessionStorage.removeItem('lndUnlocked');
this.sessionService.removeItem('lndUnlocked');
this.logger.info('Redirecting to Unlock');
this.router.navigate(['/lnd/unlocklnd']);
return {
@ -49,33 +52,7 @@ export class LNDEffects implements OnDestroy {
payload: {}
};
} else {
sessionStorage.setItem('lndUnlocked', 'true');
if (undefined !== info.chains) {
if (typeof info.chains[0] === 'string') {
info.smaller_currency_unit = (info.chains[0].toString().toLowerCase().indexOf('bitcoin') < 0) ? 'Litoshis' : 'Sats';
info.currency_unit = (info.chains[0].toString().toLowerCase().indexOf('bitcoin') < 0) ? 'LTC' : 'BTC';
} else if (typeof info.chains[0] === 'object' && info.chains[0].hasOwnProperty('chain')) {
const getInfoChain = <GetInfoChain>info.chains[0];
info.smaller_currency_unit = (getInfoChain.chain.toLowerCase().indexOf('bitcoin') < 0) ? 'Litoshis' : 'Sats';
info.currency_unit = (getInfoChain.chain.toLowerCase().indexOf('bitcoin') < 0) ? 'LTC' : 'BTC';
}
info.version = (undefined === info.version) ? '' : info.version.split(' ')[0];
} else {
info.smaller_currency_unit = 'Sats';
info.currency_unit = 'BTC';
info.version = (undefined === info.version) ? '' : info.version.split(' ')[0];
}
const node_data = {
identity_pubkey: info.identity_pubkey,
alias: info.alias,
testnet: info.testnet,
chains: info.chains,
version: info.version,
currency_unit: info.currency_unit,
smaller_currency_unit: info.smaller_currency_unit,
numberOfPendingChannels: info.num_pending_channels
};
this.store.dispatch(new RTLActions.SetNodeData(node_data));
this.initializeRemainingData(info, action.payload.loadPage);
return {
type: RTLActions.SET_INFO,
payload: (undefined !== info) ? info : {}
@ -83,14 +60,31 @@ export class LNDEffects implements OnDestroy {
}
}),
catchError((err) => {
this.logger.error(err);
if (err.status === 401) {
this.logger.info('Redirecting to Signin');
return of({ type: RTLActions.SIGNOUT });
} else {
if ((typeof err.error.error === 'string' && err.error.error.includes('Not Found')) || err.status === 502) {
this.sessionService.removeItem('lndUnlocked');
this.logger.info('Redirecting to Unlock');
this.router.navigate(['/lnd/unlocklnd']);
return of();
this.handleErrorWithoutAlert('FetchInfo', err);
} else {
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;
}
}
this.router.navigate(['/error'], { state: { errorCode: code, errorMessage: message }});
this.handleErrorWithoutAlert('FetchInfo', err);
return of({type: RTLActions.VOID});
}
})
);
@ -112,12 +106,11 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchPeers', code: err.status, message: err.error.error }));
this.logger.error(err);
return of();
this.handleErrorWithoutAlert('FetchPeers', err);
return of({type: RTLActions.VOID});
})
);
}
}
));
@Effect()
@ -136,22 +129,11 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Add Peer Failed',
message: JSON.stringify({ code: err.status, Message: err.error.error })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Add Peer Failed', this.CHILD_API_URL + environment.PEERS_API, err);
return of({type: RTLActions.VOID});
})
);
}
}
));
@Effect()
@ -170,22 +152,11 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Unable to Detach Peer. Try again later.',
message: JSON.stringify({ code: err.status, Message: err.error.error })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Unable to Detach Peer. Try again later.', this.CHILD_API_URL + environment.PEERS_API + '/' + action.payload.pubkey, err);
return of({type: RTLActions.VOID});
})
);
}
}
));
@Effect()
@ -216,19 +187,8 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Add Invoice Failed',
message: JSON.stringify({ code: err.status, Message: err.error.error })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Add Invoice Failed', this.CHILD_API_URL + environment.INVOICES_API, err);
return of({type: RTLActions.VOID});
})
);
}
@ -255,23 +215,12 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Open Channel Failed',
message: JSON.stringify({ code: err.status, Message: err.error.error })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Open Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API, err);
return of({type: RTLActions.VOID});
})
);
}
));
}
));
@Effect()
updateChannel = this.actions$.pipe(
@ -290,23 +239,12 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Update Channel Failed',
message: JSON.stringify({ code: err.status, Message: err.error.error })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Update Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/chanPolicy', err);
return of({type: RTLActions.VOID});
})
);
}
));
}
));
@Effect()
closeChannel = this.actions$.pipe(
@ -332,23 +270,12 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Unable to Close Channel. Try again later.',
message: JSON.stringify({ code: err.status, Message: err.error.error.message })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Unable to Close Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_API + '/' + action.payload.channelPoint + '?force=' + action.payload.forcibly, err);
return of({type: RTLActions.VOID});
})
);
}
));
}
));
@Effect()
backupChannels = this.actions$.pipe(
@ -367,24 +294,13 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'BackupChannels', code: err.status, message: err.error.error }));
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: action.payload.showMessage + ' ' + 'Unable to Backup Channel. Try again later.',
message: JSON.stringify({ code: err.status, Message: err.error.message })
}
}
}
);
this.handleErrorWithAlert('ERROR', action.payload.showMessage + ' ' + 'Unable to Backup Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/' + action.payload.channelPoint, err);
return of({type: RTLActions.VOID});
})
);
}
));
}
));
@Effect()
verifyChannels = this.actions$.pipe(
@ -403,23 +319,12 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'VerifyChannels', code: err.status, message: err.error.error }));
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Unable to Verify Channel. Try again later.',
message: JSON.stringify({ code: err.status, Message: err.error.message })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Unable to Verify Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/verify/' + action.payload.channelPoint, err);
return of({type: RTLActions.VOID});
})
);
}
}
));
@Effect()
@ -440,20 +345,9 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'RestoreChannels', code: err.status, message: err.error.error }));
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Unable to Restore Channel. Try again later.',
message: JSON.stringify({ code: err.status, Message: err.error.error })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Unable to Restore Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/restore/' + action.payload.channelPoint, err);
return of({type: RTLActions.VOID});
})
);
}
@ -474,11 +368,10 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchFees', code: err.status, message: err.error.error }));
return of();
}
));
this.handleErrorWithoutAlert('FetchFees', err);
return of({type: RTLActions.VOID});
})
);
@Effect()
balanceFetch = this.actions$.pipe(
@ -499,13 +392,12 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchBalance/' + action.payload, code: err.status, message: err.error.error }));
return of();
this.handleErrorWithoutAlert('FetchBalance/' + action.payload, err);
return of({type: RTLActions.VOID});
}
));
));
}
));
));
@Effect()
networkInfoFetch = this.actions$.pipe(
@ -522,11 +414,10 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchNetwork', code: err.status, message: err.error.error }));
return of();
this.handleErrorWithoutAlert('FetchNetwork', err);
return of({type: RTLActions.VOID});
}
));
));
@Effect()
channelsFetch = this.actions$.pipe(
@ -571,14 +462,13 @@ export class LNDEffects implements OnDestroy {
};
}
},
catchError((err: any) => {
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchChannels/' + action.payload.routeParam, code: err.status, message: err.error.error }));
return of();
})
));
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchChannels/' + action.payload.routeParam, err);
return of({type: RTLActions.VOID});
})
));
}
));
));
@Effect()
invoicesFetch = this.actions$.pipe(
@ -599,13 +489,12 @@ export class LNDEffects implements OnDestroy {
payload: res
};
}),
catchError((err: any) => {
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchInvoices', code: err.status, message: err.error.error }));
return of();
}
));
}));
catchError((err: any) => {
this.handleErrorWithoutAlert('FetchInvoices', err);
return of({type: RTLActions.VOID});
}
));
}));
@Effect()
transactionsFetch = this.actions$.pipe(
@ -622,11 +511,10 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchTransactions', code: err.status, message: err.error.error }));
return of();
this.handleErrorWithoutAlert('FetchTransactions', err);
return of({type: RTLActions.VOID});
}
));
));
@Effect()
paymentsFetch = this.actions$.pipe(
@ -643,11 +531,10 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.logger.error(err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchPayments', code: err.status, message: err.error.error }));
return of();
this.handleErrorWithoutAlert('FetchPayments', err);
return of({type: RTLActions.VOID});
}
));
));
@Effect()
decodePayment = this.actions$.pipe(
@ -664,19 +551,8 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Decode Payment Failed',
message: JSON.stringify({ Code: err.status, Message: err.error.error, URL: this.CHILD_API_URL + environment.PAYREQUEST_API + '/' + action.payload })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Decode Payment Failed', this.CHILD_API_URL + environment.PAYREQUEST_API + '/' + action.payload, err);
return of({type: RTLActions.VOID});
})
);
})
@ -709,7 +585,7 @@ export class LNDEffects implements OnDestroy {
this.store.dispatch(new RTLActions.CloseSpinner());
if (sendRes.payment_error) {
this.logger.error('Error: ' + sendRes.payment_error);
return of({
return {
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
@ -719,7 +595,7 @@ export class LNDEffects implements OnDestroy {
)
}
}
});
};
} else {
const confirmationMsg = { 'Destination': action.payload[1].destination, 'Timestamp': action.payload[1].timestamp_str, 'Expiry': action.payload[1].expiry };
confirmationMsg['Amount (' + ((undefined === store.nodeData.smaller_currency_unit) ?
@ -742,19 +618,8 @@ export class LNDEffects implements OnDestroy {
}
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Send Payment Failed',
message: JSON.stringify({ code: err.status, Message: err.error.error, URL: this.CHILD_API_URL + environment.CHANNELS_API + '/transactions/' + action.payload[0] })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/transactions', err);
return of({type: RTLActions.VOID});
})
);
})
@ -773,23 +638,12 @@ export class LNDEffects implements OnDestroy {
payload: (undefined !== graphNode) ? graphNode : {}
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Get Node Address Failed',
message: JSON.stringify({ Code: err.status, Message: err.error.error })
}
}
}
);
}));
}
));
catchError((err: any) => {
this.handleErrorWithAlert('ERROR', 'Get Node Address Failed', this.CHILD_API_URL + environment.NETWORK_API + '/node/' + action.payload, err);
return of({type: RTLActions.VOID});
}));
}
));
@Effect({ dispatch: false })
setGraphNode = this.actions$.pipe(
@ -813,21 +667,10 @@ export class LNDEffects implements OnDestroy {
payload: (undefined !== newAddress && undefined !== newAddress.address) ? newAddress.address : {}
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Generate New Address Failed',
message: JSON.stringify({ Code: err.status, Message: err.error.error, URL: this.CHILD_API_URL + environment.NEW_ADDRESS_API + '?type=' + action.payload.addressId })
}
}
}
);
}));
catchError((err: any) => {
this.handleErrorWithAlert('ERROR', 'Generate New Address Failed', this.CHILD_API_URL + environment.NEW_ADDRESS_API + '?type=' + action.payload.addressId, err);
return of({type: RTLActions.VOID});
}));
})
);
@ -859,22 +702,11 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'SetChannelTransaction', code: err.status, message: err.error.error }));
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Sending Fund Failed',
message: JSON.stringify({ Code: err.status, Message: err.error.error })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Sending Fund Failed', this.CHILD_API_URL + environment.TRANSACTIONS_API, err);
return of({type: RTLActions.VOID});
}));
})
})
);
@Effect()
@ -896,18 +728,8 @@ export class LNDEffects implements OnDestroy {
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'GetForwardingHistory', code: err.status, message: err.error.error }));
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Get Forwarding History Failed',
message: JSON.stringify({ code: err.status, Message: err.error.error, URL: this.CHILD_API_URL + environment.SWITCH_API })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Get Forwarding History Failed', this.CHILD_API_URL + environment.SWITCH_API, err);
return of({type: RTLActions.VOID});
})
);
})
@ -928,18 +750,8 @@ export class LNDEffects implements OnDestroy {
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.SetQueryRoutes({}));
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Get Query Routes Failed',
message: JSON.stringify({ code: err.status, Message: err.error.error.error, URL: this.CHILD_API_URL + environment.NETWORK_API })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Get Query Routes Failed', this.CHILD_API_URL + environment.NETWORK_API, err);
return of({type: RTLActions.VOID});
})
);
}
@ -969,14 +781,34 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err) => {
this.handleErrorWithAlert('ERROR', err.error.message + ' ' + err.error.error.code, this.CHILD_API_URL + environment.WALLET_API + '/genseed/' + action.payload, err);
return of({type: RTLActions.VOID});
})
);
}
));
@Effect()
updateSelNodeOptions = this.actions$.pipe(
ofType(RTLActions.UPDATE_SELECTED_NODE_OPTIONS),
mergeMap((action: RTLActions.UpdateSelectedNodeOptions) => {
return this.httpClient.get(this.CHILD_API_URL + environment.WALLET_API + '/updateSelNodeOptions')
.pipe(
map((postRes: any) => {
this.logger.info('Update Sel Node Successfull');
this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenAlert({ width: '70%', data: { type: 'ERROR', titleMessage: err.error.message + ' ' + err.error.error.code } }));
this.logger.error(err.error.error);
return of();
return {
type: RTLActions.VOID
};
}),
catchError((err) => {
this.handleErrorWithAlert('ERROR', 'Update macaroon for newly initialized node failed! Please check the macaroon path and restart the server!', 'Update Macaroon', err);
return of({type: RTLActions.VOID});
})
);
}
));
}
));
@Effect({ dispatch: false })
genSeedResponse = this.actions$.pipe(
@ -1014,14 +846,12 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenAlert({ width: '70%', data: { type: 'ERROR', titleMessage: err.error.error } }));
this.logger.error(err.error.error);
return of();
this.handleErrorWithAlert('ERROR', err.error.error, this.CHILD_API_URL + environment.WALLET_API + '/initwallet', err);
return of({type: RTLActions.VOID});
})
);
}
));
}
));
@Effect({ dispatch: false })
unlockWallet = this.actions$.pipe(
@ -1034,20 +864,17 @@ export class LNDEffects implements OnDestroy {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenSpinner('Initializing Node...'));
this.logger.info('Successfully Unlocked!');
sessionStorage.setItem('lndUnlocked', 'true');
this.sessionService.setItem('lndUnlocked', 'true');
setTimeout(() => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.info('Successfully Initialized!');
this.store.dispatch(new RTLActions.InitAppData());
this.router.navigate(['/lnd/']);
this.store.dispatch(new RTLActions.FetchInfo({loadPage: 'HOME'}));
}, 1000 * 90);
return of({});
return { type: RTLActions.VOID };
}),
catchError((err) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenAlert({ width: '70%', data: { type: 'ERROR', titleMessage: err.error.error } }));
this.logger.error(err.error.error);
return of();
this.handleErrorWithAlert('ERROR', err.error.error, this.CHILD_API_URL + environment.WALLET_API + '/unlockwallet', err);
return of({ type: RTLActions.VOID });
})
);
}
@ -1069,20 +896,9 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'Lookup', code: err.status, message: err.error.message }));
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Peer Lookup Failed',
message: JSON.stringify({ Code: err.status, Message: err.error.error, URL: this.CHILD_API_URL + environment.NETWORK_API + '/node/' + action.payload })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Peer Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/node/' + action.payload, err);
return of({type: RTLActions.VOID});
})
);
})
@ -1104,20 +920,9 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'Lookup', code: err.status, message: err.error.message }));
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Channel Lookup Failed',
message: JSON.stringify({ Code: err.status, Message: err.error.error, URL: this.CHILD_API_URL + environment.NETWORK_API + '/edge/' + action.payload })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Channel Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/edge/' + action.payload, err);
return of({type: RTLActions.VOID});
})
);
})
@ -1139,20 +944,9 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'Lookup', code: err.status, message: err.error.message }));
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Invoice Lookup Failed',
message: JSON.stringify({ Code: err.status, Message: err.error.error, URL: this.CHILD_API_URL + environment.INVOICES_API + '/' + action.payload })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Invoice Lookup Failed', this.CHILD_API_URL + environment.INVOICES_API + '/' + action.payload, err);
return of({type: RTLActions.VOID});
})
);
})
@ -1184,20 +978,9 @@ export class LNDEffects implements OnDestroy {
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'RestoreChannelsList', code: err.status, message: err.error.message }));
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Restore Channels List Failed',
message: JSON.stringify({ Code: err.status, Message: err.error, URL: this.CHILD_API_URL + environment.CHANNELS_BACKUP_API })
}
}
}
);
this.handleErrorWithAlert('ERROR', 'Restore Channels List Failed', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API, err);
return of({type: RTLActions.VOID});
})
);
})
@ -1212,6 +995,79 @@ export class LNDEffects implements OnDestroy {
})
);
initializeRemainingData(info: any, landingPage: string) {
this.sessionService.setItem('lndUnlocked', 'true');
if (undefined !== info.chains) {
if (typeof info.chains[0] === 'string') {
info.smaller_currency_unit = (info.chains[0].toString().toLowerCase().indexOf('bitcoin') < 0) ? 'Litoshis' : 'Sats';
info.currency_unit = (info.chains[0].toString().toLowerCase().indexOf('bitcoin') < 0) ? 'LTC' : 'BTC';
} else if (typeof info.chains[0] === 'object' && info.chains[0].hasOwnProperty('chain')) {
const getInfoChain = <GetInfoChain>info.chains[0];
info.smaller_currency_unit = (getInfoChain.chain.toLowerCase().indexOf('bitcoin') < 0) ? 'Litoshis' : 'Sats';
info.currency_unit = (getInfoChain.chain.toLowerCase().indexOf('bitcoin') < 0) ? 'LTC' : 'BTC';
}
info.version = (undefined === info.version) ? '' : info.version.split(' ')[0];
} else {
info.smaller_currency_unit = 'Sats';
info.currency_unit = 'BTC';
info.version = (undefined === info.version) ? '' : info.version.split(' ')[0];
}
const node_data = {
identity_pubkey: info.identity_pubkey,
alias: info.alias,
testnet: info.testnet,
chains: info.chains,
version: info.version,
currency_unit: info.currency_unit,
smaller_currency_unit: info.smaller_currency_unit,
numberOfPendingChannels: info.num_pending_channels
};
this.store.dispatch(new RTLActions.SetNodeData(node_data));
this.store.dispatch(new RTLActions.FetchFees());
this.store.dispatch(new RTLActions.FetchPeers());
this.store.dispatch(new RTLActions.FetchBalance('channels'));
this.store.dispatch(new RTLActions.FetchNetwork());
this.store.dispatch(new RTLActions.FetchChannels({routeParam: 'all'}));
this.store.dispatch(new RTLActions.FetchChannels({routeParam: 'pending'}));
this.store.dispatch(new RTLActions.FetchInvoices({num_max_invoices: 25, reversed: true}));
this.store.dispatch(new RTLActions.FetchPayments());
let newRoute = this.location.path();
if (newRoute.includes('/unlock') || newRoute.includes('/login') || newRoute.includes('/error') || newRoute === '' || landingPage === 'HOME' || newRoute.includes('?access-key=')) {
newRoute = '/lnd/home';
} else {
if(newRoute.includes('/cl/')) {
newRoute = newRoute.replace('/cl/', '/lnd/');
}
}
this.router.navigate([newRoute]);
}
handleErrorWithoutAlert(actionName: 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 Signin');
this.store.dispatch(new RTLActions.Signout());
} else {
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: actionName, code: err.status.toString(), message: err.error.error }));
}
}
handleErrorWithAlert(alertType: string, alertTitle: string, errURL: string, err: { status: number, error: any }) {
this.logger.error(err);
if (err.status === 401) {
this.logger.info('Redirecting to Signin');
this.store.dispatch(new RTLActions.Signout());
} else {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenAlert({
width: '70%', data: {
type: alertType, titleMessage: alertTitle,
message: JSON.stringify({ code: err.status, Message: err.error.error, URL: errURL })
}
}));
}
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();

@ -37,8 +37,8 @@ export const initLNDState: LNDState = {
peers: [],
fees: {},
networkInfo: {},
channelBalance: {balance: '', btc_balance: ''},
blockchainBalance: { total_balance: '', btc_total_balance: ''},
channelBalance: { balance: -1, btc_balance: -1 },
blockchainBalance: { total_balance: -1, btc_total_balance: -1 },
allChannels: [],
closedChannels: [],
pendingChannels: {},

@ -58,13 +58,13 @@ export class SendReceiveTransComponent implements OnInit, OnDestroy {
this.blockchainBalance = rtlStore.blockchainBalance;
if (undefined === this.blockchainBalance.total_balance) {
this.blockchainBalance.total_balance = '0';
this.blockchainBalance.total_balance = 0;
}
if (undefined === this.blockchainBalance.confirmed_balance) {
this.blockchainBalance.confirmed_balance = '0';
this.blockchainBalance.confirmed_balance = 0;
}
if (undefined === this.blockchainBalance.unconfirmed_balance) {
this.blockchainBalance.unconfirmed_balance = '0';
this.blockchainBalance.unconfirmed_balance = 0;
}
if (this.flgLoadingWallet !== 'error') {
this.flgLoadingWallet = false;

@ -95,18 +95,18 @@ export class UnlockLNDComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.unsubs[3]))
.subscribe(genSeedRes => {
this.genSeedResponse = genSeedRes;
// if (this.passphraseFormGroup.controls.enterPassphrase.value) {
// this.store.dispatch(new RTLActions.InitWallet({
// pwd: window.btoa(this.passwordFormGroup.controls.initWalletPassword.value),
// cipher: this.genSeedResponse,
// passphrase: window.btoa(this.passphraseFormGroup.controls.passphrase.value)
// }));
// } else {
if (this.passphraseFormGroup.controls.enterPassphrase.value) {
this.store.dispatch(new RTLActions.InitWallet({
pwd: window.btoa(this.passwordFormGroup.controls.initWalletPassword.value),
cipher: this.genSeedResponse,
passphrase: window.btoa(this.passphraseFormGroup.controls.passphrase.value)
}));
} else {
this.store.dispatch(new RTLActions.InitWallet({
pwd: window.btoa(this.passwordFormGroup.controls.initWalletPassword.value),
cipher: this.genSeedResponse
}));
// }
}
});
}
@ -143,8 +143,8 @@ export class UnlockLNDComponent implements OnInit, OnDestroy {
onGoToHome() {
setTimeout(() => {
this.store.dispatch(new RTLActions.InitAppData());
this.router.navigate(['/lnd/']);
this.store.dispatch(new RTLActions.UpdateSelectedNodeOptions());
this.store.dispatch(new RTLActions.FetchInfo({loadPage:'HOME'}));
}, 1000 * 1);
}

@ -1,14 +1,14 @@
<div fxLayout="column" fxLayoutAlign="center center">
<mat-card class="mat-elevation-z24 not-found-box">
<div fxLayout="column" fxLayoutAlign="center center" class="bg-primary pt-2">
<div fxLayout="column" fxLayoutAlign="center center" class="bg-primary py-2">
<button mat-fab color="accent" class="mat-elevation-z12">
<mat-icon>error</mat-icon>
</button>
<h1 class="error">401</h1>
<h1 class="error">{{error.errorCode}}</h1>
</div>
<mat-card-content fxLayout="row" fxLayoutAlign="center center">
<mat-card fxLayout="column" fxLayoutAlign="center center" class="mat-elevation-z12 w-100">
<div class="box-text">Single Sign On Failed!</div>
<div class="box-text">{{error.errorMessage}}</div>
</mat-card>
</mat-card-content>
</mat-card>

@ -0,0 +1,25 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
import { Subject } from 'rxjs';
import { filter, map, takeUntil } from 'rxjs/operators';
@Component({
selector: 'rtl-error',
templateUrl: './error.component.html'
})
export class ErrorComponent implements OnInit {
error = {errorCode: '', errorMessage: ''};
private unsubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private router: Router, private activatedRoute: ActivatedRoute) { }
ngOnInit() {
this.activatedRoute.paramMap
.pipe(takeUntil(this.unsubs[0]))
.subscribe(data => {
this.error = window.history.state;
});
}
}

@ -1,12 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faEject } from '@fortawesome/free-solid-svg-icons';
import { LoggerService } from '../../../services/logger.service';
import { SessionService } from '../../../services/session.service';
import { MENU_DATA } from '../../../models/navMenu';
import { RTLEffects } from '../../../../store/rtl.effects';
@ -18,14 +15,14 @@ import * as fromRTLReducer from '../../../../store/rtl.reducers';
templateUrl: './horizontal-navigation.component.html',
styleUrls: ['./horizontal-navigation.component.scss']
})
export class HorizontalNavigationComponent implements OnInit {
export class HorizontalNavigationComponent implements OnInit, OnDestroy {
public menuNodes = [];
public logoutNode = [];
public showLogout = false;
public numPendingChannels = 0;
private unSubs = [new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions, private rtlEffects: RTLEffects) {
constructor(private sessionService: SessionService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects) {
}
ngOnInit() {
@ -39,21 +36,15 @@ export class HorizontalNavigationComponent implements OnInit {
this.menuNodes = MENU_DATA.LNDChildren;
}
});
this.actions$
.pipe(
takeUntil(this.unSubs[2]),
filter((action) => action.type === RTLActions.SIGNOUT || action.type === RTLActions.SIGNIN)
).subscribe((action) => {
if (action.type === RTLActions.SIGNIN) {
this.menuNodes.push({id: 200, parentId: 0, name: 'Logout', iconType: 'SVG', icon: 'logout'});
}
if (action.type === RTLActions.SIGNOUT) {
this.sessionService.watchSession()
.pipe(takeUntil(this.unSubs[1]))
.subscribe(session => {
if(session.token) {
this.menuNodes.push({id: 200, parentId: 0, name: 'Logout', icon: 'eject'});
} else {
this.menuNodes.pop();
}
});
if (sessionStorage.getItem('token')) {
this.menuNodes.push({id: 200, parentId: 0, name: 'Logout', iconType: 'SVG', icon: 'logout'});
}
}
onClick(node) {
@ -62,7 +53,7 @@ export class HorizontalNavigationComponent implements OnInit {
width: '70%', data: { type: 'CONFIRM', titleMessage: 'Logout from this device?', noBtnText: 'Cancel', yesBtnText: 'Logout'
}}));
this.rtlEffects.closeConfirm
.pipe(takeUntil(this.unSubs[1]))
.pipe(takeUntil(this.unSubs[3]))
.subscribe(confirmRes => {
if (confirmRes) {
this.showLogout = false;
@ -71,4 +62,11 @@ export class HorizontalNavigationComponent implements OnInit {
});
}
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -12,6 +12,7 @@ import { faEject } from '@fortawesome/free-solid-svg-icons';
import { LightningNode, Settings, GetInfoRoot } from '../../../models/RTLconfig';
import { LoggerService } from '../../../services/logger.service';
import { SessionService } from '../../../services/session.service';
import { GetInfoChain } from '../../../models/lndModels';
import { MenuChildNode, MENU_DATA } from '../../../models/navMenu';
@ -38,13 +39,13 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
public numPendingChannels = 0;
public smallScreen = false;
public childRootRoute = '';
private unSubs = [new Subject(), new Subject(), new Subject()];
private unSubs = [new Subject(), new Subject(), new Subject(), new Subject()];
treeControlLogout = new NestedTreeControl<MenuChildNode>(node => node.children);
treeControlNested = new NestedTreeControl<MenuChildNode>(node => node.children);
navMenus = new MatTreeNestedDataSource<MenuChildNode>();
navMenusLogout = new MatTreeNestedDataSource<MenuChildNode>();
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions, private rtlEffects: RTLEffects, private router: Router, private activatedRoute: ActivatedRoute) {
constructor(private logger: LoggerService, private sessionService: SessionService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions, private rtlEffects: RTLEffects, private router: Router, private activatedRoute: ActivatedRoute) {
this.version = environment.VERSION;
if (MENU_DATA.LNDChildren[MENU_DATA.LNDChildren.length - 1].id === 200) {
MENU_DATA.LNDChildren.pop();
@ -77,10 +78,6 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
}
this.flgLoading = (undefined !== this.information.identity_pubkey) ? false : true;
this.showLogout = (sessionStorage.getItem('token')) ? true : false;
if (!sessionStorage.getItem('token')) {
this.flgLoading = false;
}
if (window.innerWidth <= 414) {
this.smallScreen = true;
}
@ -91,6 +88,12 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
}
this.logger.info(rtlStore);
});
this.sessionService.watchSession()
.pipe(takeUntil(this.unSubs[1]))
.subscribe(session => {
this.showLogout = session.token ? true : false;
this.flgLoading = session.token ? true : false;
});
this.actions$.pipe(takeUntil(this.unSubs[2]),
filter((action) => action.type === RTLActions.SIGNOUT))
.subscribe((action: RTLActions.Signout) => {
@ -106,7 +109,7 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
width: '70%', data: { type: 'CONFIRM', titleMessage: 'Logout from this device?', noBtnText: 'Cancel', yesBtnText: 'Logout'
}}));
this.rtlEffects.closeConfirm
.pipe(takeUntil(this.unSubs[1]))
.pipe(takeUntil(this.unSubs[3]))
.subscribe(confirmRes => {
if (confirmRes) {
this.showLogout = false;

@ -6,6 +6,7 @@ import { Actions } from '@ngrx/effects';
import { GetInfoRoot, LightningNode } from '../../../models/RTLconfig';
import { LoggerService } from '../../../services/logger.service';
import { SessionService } from '../../../services/session.service';
import { GetInfoChain } from '../../../models/lndModels';
import { environment } from '../../../../../environments/environment';
@ -30,9 +31,9 @@ export class TopMenuComponent implements OnInit, OnDestroy {
public informationChain: GetInfoChain = {};
public flgLoading = true;
public showLogout = false;
private unSubs = [new Subject(), new Subject(), new Subject()];
private unSubs = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private actions$: Actions) {
constructor(private logger: LoggerService, private sessionService: SessionService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private actions$: Actions) {
this.version = environment.VERSION;
}
@ -58,12 +59,13 @@ export class TopMenuComponent implements OnInit, OnDestroy {
this.informationChain.chain = '';
this.informationChain.network = '';
}
this.showLogout = (sessionStorage.getItem('token')) ? true : false;
this.logger.info(rtlStore);
if (!sessionStorage.getItem('token')) {
this.flgLoading = false;
}
});
this.sessionService.watchSession()
.pipe(takeUntil(this.unSubs[1]))
.subscribe(session => {
this.showLogout = session.token ? true : false;
this.flgLoading = session.token ? true : false;
});
this.actions$
.pipe(
@ -79,7 +81,7 @@ export class TopMenuComponent implements OnInit, OnDestroy {
width: '70%', data: { type: 'CONFIRM', titleMessage: 'Logout from this device?', noBtnText: 'Cancel', yesBtnText: 'Logout'
}}));
this.rtlEffects.closeConfirm
.pipe(takeUntil(this.unSubs[1]))
.pipe(takeUntil(this.unSubs[3]))
.subscribe(confirmRes => {
if (confirmRes) {
this.showLogout = false;

@ -1,14 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'rtl-sso-failed',
templateUrl: './sso-failed.component.html'
})
export class SsoFailedComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

@ -43,8 +43,8 @@ export interface BalanceCL {
}
export interface LocalRemoteBalanceCL {
localBalance?: number;
remoteBalance?: number;
localBalance: number;
remoteBalance: number;
btc_localBalance?: number;
btc_remoteBalance?: number;
}

@ -5,16 +5,16 @@ export interface AddressType {
}
export interface Balance {
btc_balance?: string;
balance?: string;
btc_pending_open_balance?: string;
pending_open_balance?: string;
btc_total_balance?: string;
total_balance?: string;
btc_confirmed_balance?: string;
confirmed_balance?: string;
btc_unconfirmed_balance?: string;
unconfirmed_balance?: string;
btc_balance?: number;
balance?: number;
btc_pending_open_balance?: number;
pending_open_balance?: number;
btc_total_balance?: number;
total_balance?: number;
btc_confirmed_balance?: number;
confirmed_balance?: number;
btc_unconfirmed_balance?: number;
unconfirmed_balance?: number;
}
export interface ChannelFeeReport {

@ -2,12 +2,14 @@ import { CanActivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { SessionService } from './session.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor() {}
constructor(private sessionService: SessionService) {}
canActivate(): boolean | Observable<boolean> | Promise<boolean> {
if (!sessionStorage.getItem('token')) {
if (!this.sessionService.getItem('token')) {
return false;
} else {
return true;
@ -17,10 +19,10 @@ export class AuthGuard implements CanActivate {
@Injectable()
export class LNDUnlockedGuard implements CanActivate {
constructor() {}
constructor(private sessionService: SessionService) {}
canActivate(): boolean | Observable<boolean> | Promise<boolean> {
if (!sessionStorage.getItem('lndUnlocked')) {
if (!this.sessionService.getItem('lndUnlocked')) {
return false;
} else {
return true;
@ -30,11 +32,11 @@ export class LNDUnlockedGuard implements CanActivate {
@Injectable()
export class CLUnlockedGuard implements CanActivate {
constructor() {}
constructor(private sessionService: SessionService) {}
canActivate(): boolean | Observable<boolean> | Promise<boolean> {
return true;
if (!sessionStorage.getItem('clUnlocked')) {
if (!this.sessionService.getItem('clUnlocked')) {
return false;
} else {
return true;

@ -2,15 +2,17 @@ import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
import { SessionService } from './session.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor() {}
constructor(private sessionService: SessionService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (sessionStorage.getItem('token')) {
if (this.sessionService.getItem('token')) {
const cloned = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + sessionStorage.getItem('token'))
headers: req.headers.set('Authorization', 'Bearer ' + this.sessionService.getItem('token'))
});
return next.handle(cloned);
} else {

@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';
@Injectable()
export class SessionService {
private sessionSub= new Subject<any>();
watchSession(): Observable<any> {
return this.sessionSub.asObservable();
}
getItem(key) {
return sessionStorage.getItem(key);
}
setItem(key: string, data: any) {
sessionStorage.setItem(key, data);
this.sessionSub.next(sessionStorage);
}
removeItem(key) {
sessionStorage.removeItem(key);
this.sessionSub.next(sessionStorage);
}
}

@ -27,7 +27,7 @@ import { TopMenuComponent } from './components/navigation/top-menu/top-menu.comp
import { HorizontalNavigationComponent } from './components/navigation/horizontal-navigation/horizontal-navigation.component';
import { SettingsNavComponent } from './components/settings-nav/settings-nav.component';
import { ServerConfigComponent } from './components/server-config/server-config.component';
import { SsoFailedComponent } from './components/sso-failed/sso-failed.component';
import { ErrorComponent } from './components/error/error.component';
import { ClipboardDirective } from './directive/clipboard.directive';
import { RemoveLeadingZerosPipe } from './pipes/remove-leading-zero.pipe';
@ -135,7 +135,7 @@ import { RemoveLeadingZerosPipe } from './pipes/remove-leading-zero.pipe';
HelpComponent,
ServerConfigComponent,
ClipboardDirective,
SsoFailedComponent,
ErrorComponent,
RemoveLeadingZerosPipe
],
entryComponents: [

@ -10,6 +10,7 @@ import {
} from '../shared/models/lndModels';
export const VOID = 'VOID';
export const UPDATE_SELECTED_NODE_OPTIONS = 'UPDATE_SELECTED_NODE_OPTIONS';
export const RESET_ROOT_STORE = 'RESET_ROOT_STORE';
export const CLEAR_EFFECT_ERROR_ROOT = 'CLEAR_EFFECT_ERROR_ROOT';
export const EFFECT_ERROR_ROOT = 'EFFECT_ERROR_ROOT';
@ -90,7 +91,6 @@ export const IS_AUTHORIZED = 'IS_AUTHORIZED';
export const IS_AUTHORIZED_RES = 'IS_AUTHORIZED_RES';
export const SIGNIN = 'SIGNIN';
export const SIGNOUT = 'SIGNOUT';
export const INIT_APP_DATA = 'INIT_APP_DATA';
export const PEER_LOOKUP = 'PEER_LOOKUP';
export const CHANNEL_LOOKUP = 'CHANNEL_LOOKUP';
export const INVOICE_LOOKUP = 'INVOICE_LOOKUP';
@ -211,6 +211,10 @@ export class CloseConfirmation implements Action {
constructor(public payload: boolean) {}
}
export class UpdateSelectedNodeOptions implements Action {
readonly type = UPDATE_SELECTED_NODE_OPTIONS;
}
export class ResetRootStore implements Action {
readonly type = RESET_ROOT_STORE;
constructor(public payload: LightningNode) {}
@ -262,6 +266,7 @@ export class SetChildNodeSettings implements Action {
export class FetchInfo implements Action {
readonly type = FETCH_INFO;
constructor(public payload: {loadPage: string}) {}
}
export class SetInfo implements Action {
@ -583,10 +588,6 @@ export class Signout implements Action {
constructor() {}
}
export class InitAppData implements Action {
readonly type = INIT_APP_DATA;
}
export class SetChildNodeSettingsCL implements Action {
readonly type = SET_CHILD_NODE_SETTINGS_CL;
constructor(public payload: SelNodeChild) {}
@ -594,6 +595,7 @@ export class SetChildNodeSettingsCL implements Action {
export class FetchInfoCL implements Action {
readonly type = FETCH_INFO_CL;
constructor(public payload: {loadPage: string}) {}
}
export class SetInfoCL implements Action {
@ -635,7 +637,7 @@ export class FetchLocalRemoteBalanceCL implements Action {
export class SetLocalRemoteBalanceCL implements Action {
readonly type = SET_LOCAL_REMOTE_BALANCE_CL;
constructor(public payload: {}) {}
constructor(public payload: {localBalance: number, remoteBalance: number}) {}
}
export class GetNewAddressCL implements Action {
@ -809,7 +811,7 @@ export type RTLActions =
ClearEffectErrorRoot | EffectErrorRoot | ClearEffectErrorLnd | EffectErrorLnd | ClearEffectErrorCl | EffectErrorCl |
VoidAction | OpenSpinner | CloseSpinner | FetchRTLConfig | SetRTLConfig | SaveSettings |
OpenAlert | CloseAlert | OpenConfirmation | CloseConfirmation |
ResetRootStore | ResetLNDStore | ResetCLStore |
UpdateSelectedNodeOptions | ResetRootStore | ResetLNDStore | ResetCLStore |
SetSelelectedNode | SetNodeData | SetNodePendingChannelsData | SetChildNodeSettings | FetchInfo | SetInfo |
FetchPeers | SetPeers | AddPeer | DetachPeer | SaveNewPeer | RemovePeer |
AddInvoice | SaveNewInvoice | GetForwardingHistory | SetForwardingHistory |
@ -828,7 +830,7 @@ export type RTLActions =
GetNewAddress | SetNewAddress | SetChannelTransaction |
GenSeed | GenSeedResponse | InitWallet | InitWalletResponse | UnlockWallet |
FetchConfig | ShowConfig | PeerLookup | ChannelLookup | InvoiceLookup | SetLookup |
IsAuthorized | IsAuthorizedRes | Signin | Signout | InitAppData |
IsAuthorized | IsAuthorizedRes | Signin | Signout |
SetChildNodeSettingsCL | FetchInfoCL | SetInfoCL | FetchFeesCL | SetFeesCL | FetchFeeRatesCL | SetFeeRatesCL |
FetchBalanceCL | SetBalanceCL | FetchLocalRemoteBalanceCL | SetLocalRemoteBalanceCL |
GetNewAddressCL | SetNewAddressCL |

@ -11,6 +11,7 @@ import { MatDialog } from '@angular/material';
import { environment, API_URL } from '../../environments/environment';
import { LoggerService } from '../shared/services/logger.service';
import { SessionService } from '../shared/services/session.service';
import { Settings, RTLConfiguration, AuthenticateWith } from '../shared/models/RTLconfig';
import { SpinnerDialogComponent } from '../shared/components/spinner-dialog/spinner-dialog.component';
@ -31,6 +32,7 @@ export class RTLEffects implements OnDestroy {
private httpClient: HttpClient,
private store: Store<fromRTLReducer.RTLState>,
private logger: LoggerService,
private sessionService: SessionService,
public dialog: MatDialog,
private router: Router,
private location: Location) { }
@ -162,7 +164,7 @@ export class RTLEffects implements OnDestroy {
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('IsAuthorized'));
return this.httpClient.post(environment.AUTHENTICATE_API, {
authenticateWith: (undefined === action.payload || action.payload == null || action.payload === '') ? AuthenticateWith.TOKEN : AuthenticateWith.PASSWORD,
authenticationValue: (undefined === action.payload || action.payload == null || action.payload === '') ? (sessionStorage.getItem('token') ? sessionStorage.getItem('token') : '') : action.payload
authenticationValue: (undefined === action.payload || action.payload == null || action.payload === '') ? (this.sessionService.getItem('token') ? this.sessionService.getItem('token') : '') : action.payload
})
.pipe(
map((postRes: any) => {
@ -202,18 +204,14 @@ export class RTLEffects implements OnDestroy {
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('Signin'));
return this.httpClient.post(environment.AUTHENTICATE_API, {
authenticateWith: (undefined === action.payload || action.payload == null || action.payload === '') ? AuthenticateWith.TOKEN : AuthenticateWith.PASSWORD,
authenticationValue: (undefined === action.payload || action.payload == null || action.payload === '') ? (sessionStorage.getItem('token') ? sessionStorage.getItem('token') : '') : action.payload
authenticationValue: (undefined === action.payload || action.payload == null || action.payload === '') ? (this.sessionService.getItem('token') ? this.sessionService.getItem('token') : '') : action.payload
})
.pipe(
map((postRes: any) => {
this.logger.info(postRes);
this.logger.info('Successfully Authorized!');
this.SetToken(postRes.token);
if(rootStore.selNode.lnImplementation.toUpperCase() === 'CLT') {
this.router.navigate(['/cl/home']);
} else {
this.router.navigate(['/lnd/home']);
}
this.store.dispatch(new RTLActions.SetSelelectedNode({lnNode: rootStore.selNode, isInitialSetup: true}))
}),
catchError((err) => {
this.store.dispatch(new RTLActions.OpenAlert({ width: '70%', data: {type: 'ERROR', message: JSON.stringify(err.error)}}));
@ -221,7 +219,7 @@ export class RTLEffects implements OnDestroy {
this.logger.error(err.error);
this.logger.info('Redirecting to Signin Error Page');
if (+rootStore.appConfig.sso.rtlSSO) {
this.router.navigate(['/ssoerror']);
this.router.navigate(['/error'], { state: { errorCode: '401', errorMessage: 'Single Sign On Failed!' }});
} else {
this.router.navigate([rootStore.appConfig.sso.logoutRedirectLink]);
}
@ -240,9 +238,9 @@ export class RTLEffects implements OnDestroy {
} else {
this.router.navigate([store.appConfig.sso.logoutRedirectLink]);
}
sessionStorage.removeItem('clUnlocked');
sessionStorage.removeItem('lndUnlocked');
sessionStorage.removeItem('token');
this.sessionService.removeItem('clUnlocked');
this.sessionService.removeItem('lndUnlocked');
this.sessionService.removeItem('token');
this.logger.warn('LOGGED OUT');
return of();
}));
@ -257,45 +255,8 @@ export class RTLEffects implements OnDestroy {
map((postRes: any) => {
this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner());
let selNode = { channelBackupPath: action.payload.lnNode.settings.channelBackupPath, satsToBTC: action.payload.lnNode.settings.satsToBTC };
this.store.dispatch(new RTLActions.ResetRootStore(action.payload.lnNode));
this.store.dispatch(new RTLActions.ResetLNDStore(selNode));
this.store.dispatch(new RTLActions.ResetCLStore(selNode));
if (sessionStorage.getItem('token')) {
let newRoute = this.location.path();
if(action.payload.lnNode.lnImplementation.toUpperCase() === 'CLT') {
if(newRoute.includes('/lnd/')) {
newRoute = newRoute.replace('/lnd/', '/cl/');
} else if(newRoute === '/') {
newRoute = '/cl/home';
}
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate([newRoute]);
this.CHILD_API_URL = API_URL + '/cl';
return { type: RTLActions.VOID };
} else {
if(newRoute.includes('/cl/')) {
newRoute = newRoute.replace('/cl/', '/lnd/');
} else if(newRoute === '/') {
newRoute = '/lnd/home';
}
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate([newRoute]);
this.CHILD_API_URL = API_URL + '/lnd';
return { type: RTLActions.VOID };
}
} else {
if (!action.payload.isInitialSetup) {
return {
type: RTLActions.OPEN_ALERT,
payload: { width: '70%', data: {type: 'WARN', titleMessage: 'Authorization required to get the data from the node!' }}
};
} else {
return { type: RTLActions.VOID };
}
}
this.initializeNode(action.payload.lnNode, action.payload.isInitialSetup);
return { type: RTLActions.VOID };
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
@ -312,16 +273,32 @@ export class RTLEffects implements OnDestroy {
})
);
}
));
));
initializeNode(node: any, isInitialSetup: boolean) {
const landingPage = isInitialSetup ? '' : 'HOME';
let selNode = { channelBackupPath: node.settings.channelBackupPath, satsToBTC: node.settings.satsToBTC };
this.store.dispatch(new RTLActions.ResetRootStore(node));
this.store.dispatch(new RTLActions.ResetLNDStore(selNode));
this.store.dispatch(new RTLActions.ResetCLStore(selNode));
if(this.sessionService.getItem('token')) {
if(node.lnImplementation.toUpperCase() === 'CLT') {
this.CHILD_API_URL = API_URL + '/cl';
this.store.dispatch(new RTLActions.FetchInfoCL({loadPage: landingPage}));
} else {
this.CHILD_API_URL = API_URL + '/lnd';
this.store.dispatch(new RTLActions.FetchInfo({loadPage: landingPage}));
}
}
}
SetToken(token: string) {
if (token) {
sessionStorage.setItem('lndUnlocked', 'true');
sessionStorage.setItem('token', token);
this.store.dispatch(new RTLActions.InitAppData());
this.sessionService.setItem('lndUnlocked', 'true');
this.sessionService.setItem('token', token);
} else {
sessionStorage.removeItem('lndUnlocked');
sessionStorage.removeItem('token');
this.sessionService.removeItem('lndUnlocked');
this.sessionService.removeItem('token');
}
}

@ -1 +1 @@
export const VERSION = '0.5.4-beta';
export const VERSION = '0.5.5-beta';
Loading…
Cancel
Save