Multinode, getInfo errors and other bug fixes

Multinode, getInfo errors and other bug fixes
pull/209/head
Shahana Farooqui 5 years ago
parent f9a236c362
commit c6b15bd1a9

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -9,5 +9,5 @@
<link rel="stylesheet" href="styles.486014dd6111683683a1.css"></head> <link rel="stylesheet" href="styles.486014dd6111683683a1.css"></head>
<body> <body>
<rtl-app></rtl-app> <rtl-app></rtl-app>
<script src="runtime.195ce5421929c30fb476.js"></script><script src="polyfills-es5.92f4069201c83f4833ef.js" nomodule></script><script src="polyfills.5ddcccdb990eb395f306.js"></script><script src="main.d117828b66dfda0ef358.js"></script></body> <script src="runtime.5a8254d67b3de36d3a01.js"></script><script src="polyfills-es5.92f4069201c83f4833ef.js" nomodule></script><script src="polyfills.5ddcccdb990eb395f306.js"></script><script src="main.0e632b40c560865bfa63.js"></script></body>
</html> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

@ -0,0 +1 @@
!function(e){function r(r){for(var n,i,c=r[0],a=r[1],f=r[2],p=0,s=[];p<c.length;p++)o[i=c[p]]&&s.push(o[i][0]),o[i]=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,c=1;c<t.length;c++)0!==o[t[c]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(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,i),t.l=!0,t.exports}i.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,c=document.createElement("script");c.charset="utf-8",c.timeout=120,i.nc&&c.setAttribute("nonce",i.nc),c.src=function(e){return i.p+""+({}[e]||e)+"."+{1:"dc65164c2cc38ecb3ec0",6:"ea6e222fdf7e19de0f64",7:"799b397cc45f59fefe32"}[e]+".js"}(e);var a=new Error;u=function(r){c.onerror=c.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:c})}),12e4);c.onerror=c.onload=u,document.head.appendChild(c)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var c=window.webpackJsonp=window.webpackJsonp||[],a=c.push.bind(c);c.push=r,c=c.slice();for(var f=0;f<c.length;f++)r(c[f]);var l=a;t()}([]);

@ -26,53 +26,51 @@ common.getOptions = () => {
common.setOptions = () => { common.setOptions = () => {
if (undefined !== common.nodes[0].options && undefined !== common.nodes[0].options.headers) { return; } if (undefined !== common.nodes[0].options && undefined !== common.nodes[0].options.headers) { return; }
try { if (common.nodes && common.nodes.length > 0) {
if (common.nodes && common.nodes.length > 0) { common.nodes.forEach(node => {
common.nodes.forEach(node => { node.options = {
node.options = { url: '',
url: '', rejectUnauthorized: false,
rejectUnauthorized: false, json: true,
json: true, form: ''
form: '' };
}; try {
if (node.ln_implementation && node.ln_implementation.toUpperCase() === 'CLT') { 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") }; node.options.headers = { 'macaroon': Buffer.from(fs.readFileSync(path.join(node.macaroon_path, 'access.macaroon'))).toString("base64") };
} else { } else {
node.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(path.join(node.macaroon_path, 'admin.macaroon')).toString('hex') }; 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));
// Options cannot be set before selected node initializes. Updating selected node's options separatly
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));
if (common.nodes && common.nodes.length > 0) {
common.nodes.forEach(node => {
node.options = { node.options = {
url: '', url: '',
rejectUnauthorized: false, rejectUnauthorized: false,
json: true, json: true,
form: '' form: ''
}; };
}); }
} });
// Options cannot be set before selected node initializes. Updating selected node's options separatly
common.selectedNode.options = { common.selectedNode.options = {
url: '', url: '',
rejectUnauthorized: false, rejectUnauthorized: false,
json: true, json: true,
form: '' form: ''
}; };
try {
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));
common.selectedNode.options = {
url: '',
rejectUnauthorized: false,
json: true,
form: ''
};
}
} }
} }

@ -372,7 +372,11 @@ connect.setSSOParams = (config) => {
} else { } else {
common.rtl_cookie_path = common.rtl_conf_file_path + '/cookies/auth.cookie'; 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 +404,12 @@ connect.createDirectory = (dirname) => {
connect.readCookie = (cookieFile) => { connect.readCookie = (cookieFile) => {
let exists = fs.existsSync(cookieFile); let exists = fs.existsSync(cookieFile);
if (exists) { 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 { } else {
try { try {
var dirname = path.dirname(cookieFile); var dirname = path.dirname(cookieFile);

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

@ -13,25 +13,33 @@ exports.getInfo = (req, res, next) => {
} else { } else {
logger.info({fileName:'GetInfo', msg: 'Single Node Setup!'}); logger.info({fileName:'GetInfo', msg: 'Single Node Setup!'});
} }
common.nodes.map(node => { if (node.lnImplementation === 'LND') { connect.getAllNodeAllChannelBackup(node); }}); if (!options.headers || !options.headers['Grpc-Metadata-macaroon']) {
logger.info({fileName: 'GetInfo', msg: 'Calling getinfo from lnd server url: ' + options.url}); logger.error({fileName: 'GetInfo', msg: 'Get info failed due to bad or missing macaroon!'});
request(options).then((body) => { res.status(500).json({
logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)}); message: "Fetching Info Failed!",
const body_str = (undefined === body) ? '' : JSON.stringify(body); error: "Bad Macaroon"
const search_idx = (undefined === body) ? -1 : body_str.search('Not Found'); });
if(undefined === body || search_idx > -1 || body.error) { } else {
res.status(500).json({ common.nodes.map(node => { if (node.lnImplementation === 'LND') { connect.getAllNodeAllChannelBackup(node); }});
message: "Fetching Info failed!", logger.info({fileName: 'GetInfo', msg: 'Calling getinfo from lnd server url: ' + options.url});
error: (undefined === body || search_idx > -1) ? 'Error From Server!' : body.error 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
}); });
}); }
}; };

@ -8,7 +8,7 @@ exports.genSeed = (req, res, next) => {
options = common.getOptions(); options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/genseed'; options.url = common.getSelLNServerUrl() + '/genseed';
if (undefined !== req.params.passphrase) { if (undefined !== req.params.passphrase) {
options.form = JSON.stringify({aezeed_passphrase: atob(req.params.passphrase)}); options.form = JSON.stringify({aezeed_passphrase: Buffer.from(atob(req.params.passphrase)).toString('base64')});
} }
request(options).then((body) => { request(options).then((body) => {
if(undefined === body || body.error) { if(undefined === body || body.error) {

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

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

@ -5,17 +5,16 @@ import { NotFoundComponent } from './shared/components/not-found/not-found.compo
import { ServerConfigComponent } from './shared/components/server-config/server-config.component'; import { ServerConfigComponent } from './shared/components/server-config/server-config.component';
import { HelpComponent } from './shared/components/help/help.component'; import { HelpComponent } from './shared/components/help/help.component';
import { SigninComponent } from './shared/components/signin/signin.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'; import { AuthGuard } from './shared/services/auth.guard';
export const routes: Routes = [ export const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
{ path: 'lnd', loadChildren: () => import('./lnd/lnd.module').then(childModule => childModule.LNDModule), canActivate: [AuthGuard] }, { path: 'lnd', loadChildren: () => import('./lnd/lnd.module').then(childModule => childModule.LNDModule), canActivate: [AuthGuard] },
{ path: 'cl', loadChildren: () => import('./clightning/cl.module').then(childModule => childModule.CLModule), canActivate: [AuthGuard] }, { path: 'cl', loadChildren: () => import('./clightning/cl.module').then(childModule => childModule.CLModule), canActivate: [AuthGuard] },
{ path: 'sconfig', component: ServerConfigComponent, canActivate: [AuthGuard] }, { path: 'sconfig', component: ServerConfigComponent, canActivate: [AuthGuard] },
{ path: 'help', component: HelpComponent }, { path: 'help', component: HelpComponent },
{ path: 'login', component: SigninComponent }, { path: 'login', component: SigninComponent },
{ path: 'ssoerror', component: SsoFailedComponent }, { path: 'error', component: ErrorComponent },
{ path: '**', component: NotFoundComponent } { path: '**', component: NotFoundComponent }
]; ];

@ -5,4 +5,6 @@ import { Component } from '@angular/core';
templateUrl: './cl-root.component.html', templateUrl: './cl-root.component.html',
styleUrls: ['./cl-root.component.scss'] styleUrls: ['./cl-root.component.scss']
}) })
export class CLRootComponent { constructor() {} } export class CLRootComponent {
constructor() {}
}

@ -4,7 +4,6 @@ import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects'; import { Actions } from '@ngrx/effects';
import { LoggerService } from '../../shared/services/logger.service'; import { LoggerService } from '../../shared/services/logger.service';
import { GetInfoCL, FeesCL, BalanceCL, LocalRemoteBalanceCL, FeeRatesCL } from '../../shared/models/clModels'; import { GetInfoCL, FeesCL, BalanceCL, LocalRemoteBalanceCL, FeeRatesCL } from '../../shared/models/clModels';
import { SelNodeChild } from '../../shared/models/RTLconfig'; import { SelNodeChild } from '../../shared/models/RTLconfig';
@ -22,14 +21,14 @@ export class CLHomeComponent implements OnInit, OnDestroy {
public fees: FeesCL; public fees: FeesCL;
public information: GetInfoCL = {}; public information: GetInfoCL = {};
public totalBalance: BalanceCL = {}; public totalBalance: BalanceCL = {};
public lrBalance: LocalRemoteBalanceCL = {}; public lrBalance: LocalRemoteBalanceCL = { localBalance: 0, remoteBalance: 0 };
public flgLoading: Array<Boolean | 'error'> = [true, true, true, true, true]; public flgLoading: Array<Boolean | 'error'> = [true, true, true, true, true];
private unsub: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()]; private unsub: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
public position = 'below'; public position = 'below';
barPadding = 0; barPadding = 0;
maxBalanceValue = 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; flgTotalCalculated = false;
view = []; view = [];
yAxisLabel = 'Balance'; yAxisLabel = 'Balance';
@ -59,17 +58,13 @@ export class CLHomeComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
this.store.dispatch(new RTLActions.FetchInfoCL()); this.actions$.pipe(takeUntil(this.unsub[0]),
this.actions$.pipe(takeUntil(this.unsub[0]), filter((action) => action.type === RTLActions.SET_INFO_CL)) filter(action => action.type === RTLActions.SET_SELECTED_NODE))
.subscribe((infoData: RTLActions.SetInfoCL) => { .subscribe((data) => {
if(infoData.type === RTLActions.SET_INFO_CL && undefined !== infoData.payload.id) { this.flgTotalCalculated = false;
this.initializeRemainingData();
}
}); });
this.flgTotalCalculated = false;
this.store.select('cl') this.store.select('cl')
.pipe(takeUntil(this.unsub[1])) .pipe(takeUntil(this.unsub[0]))
.subscribe((rtlStore) => { .subscribe((rtlStore) => {
rtlStore.effectErrorsCl.forEach(effectsErr => { rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'FetchInfoCL') { if (effectsErr.action === 'FetchInfoCL') {
@ -107,9 +102,9 @@ export class CLHomeComponent implements OnInit, OnDestroy {
this.lrBalance = rtlStore.localRemoteBalance; this.lrBalance = rtlStore.localRemoteBalance;
this.maxBalanceValue = (rtlStore.localRemoteBalance.localBalance > rtlStore.localRemoteBalance.remoteBalance) ? rtlStore.localRemoteBalance.localBalance : rtlStore.localRemoteBalance.remoteBalance; 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') { 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; this.feeRatesPerKB = rtlStore.feeRatesPerKB;
@ -122,15 +117,6 @@ export class CLHomeComponent implements OnInit, OnDestroy {
}); });
} }
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() { ngOnDestroy() {
this.unsub.forEach(completeSub => { this.unsub.forEach(completeSub => {
completeSub.next(); completeSub.next();

@ -1,13 +1,16 @@
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, Effect, ofType } from '@ngrx/effects';
import { Subject, of } from 'rxjs'; import { Subject, of } from 'rxjs';
import { map, mergeMap, catchError, withLatestFrom } from 'rxjs/operators'; import { map, mergeMap, catchError, withLatestFrom } from 'rxjs/operators';
import { formatDate } from '@angular/common'; import { formatDate } from '@angular/common';
import { Location } from '@angular/common';
import { environment, API_URL } from '../../../environments/environment'; import { environment, API_URL } from '../../../environments/environment';
import { LoggerService } from '../../shared/services/logger.service'; 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 { GetInfoCL, FeesCL, BalanceCL, LocalRemoteBalanceCL, PaymentCL, FeeRatesCL, ListInvoicesCL, InvoiceCL } from '../../shared/models/clModels';
import * as fromRTLReducer from '../../store/rtl.reducers'; import * as fromRTLReducer from '../../store/rtl.reducers';
@ -22,7 +25,10 @@ export class CLEffects implements OnDestroy {
private actions$: Actions, private actions$: Actions,
private httpClient: HttpClient, private httpClient: HttpClient,
private store: Store<fromRTLReducer.RTLState>, private store: Store<fromRTLReducer.RTLState>,
private logger: LoggerService) { } private sessionService: SessionService,
private logger: LoggerService,
private router: Router,
private location: Location) { }
@Effect() @Effect()
infoFetchCL = this.actions$.pipe( infoFetchCL = this.actions$.pipe(
@ -34,39 +40,31 @@ export class CLEffects implements OnDestroy {
.pipe( .pipe(
map((info) => { map((info) => {
this.logger.info(info); this.logger.info(info);
let chainObj = { chain: '', network: '' }; this.initializeRemainingData(info);
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));
return { return {
type: RTLActions.SET_INFO_CL, type: RTLActions.SET_INFO_CL,
payload: (undefined !== info) ? info : {} payload: (undefined !== info) ? info : {}
}; };
}), }),
catchError((err) => { 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 }});
return this.handleErrorWithoutAlert('FetchInfoCL', err);
}) })
); );
} }
@ -346,7 +344,7 @@ export class CLEffects implements OnDestroy {
catchError((err: any) => { catchError((err: any) => {
return this.handleErrorWithoutAlert('FetchPaymentsCL', err); return this.handleErrorWithoutAlert('FetchPaymentsCL', err);
} }
)); ));
@Effect() @Effect()
decodePaymentCL = this.actions$.pipe( decodePaymentCL = this.actions$.pipe(
@ -647,6 +645,50 @@ export class CLEffects implements OnDestroy {
})); }));
}) })
); );
initializeRemainingData(info: any) {
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 === '') {
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 }) { handleErrorWithoutAlert(actionName: string, err: { status: number, error: any }) {
this.logger.error(err); this.logger.error(err);
@ -668,17 +710,15 @@ export class CLEffects implements OnDestroy {
} else { } else {
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error(err); this.logger.error(err);
return of( return of({
{ type: RTLActions.OPEN_ALERT,
type: RTLActions.OPEN_ALERT, payload: {
payload: { width: '70%', data: {
width: '70%', data: { type: alerType, titleMessage: alertTitle,
type: alerType, titleMessage: alertTitle, message: JSON.stringify({ code: err.status, Message: err.error.error, URL: errURL })
message: JSON.stringify({ code: err.status, Message: err.error.error, URL: errURL })
}
} }
} }
); });
} }
} }

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

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

@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { Store } from '@ngrx/store';
import { takeUntil, filter } from 'rxjs/operators'; import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects'; import { Actions } from '@ngrx/effects';
import { LoggerService } from '../../shared/services/logger.service'; import { LoggerService } from '../../shared/services/logger.service';
@ -22,10 +22,10 @@ export class HomeComponent implements OnInit, OnDestroy {
public information: GetInfo = {}; public information: GetInfo = {};
public remainder = 0; public remainder = 0;
public totalPeers = -1; public totalPeers = -1;
public totalBalance = ''; public totalBalance = 0;
public channelBalance = ''; public channelBalance = 0;
public BTCtotalBalance = ''; public BTCtotalBalance = 0;
public BTCchannelBalance = ''; public BTCchannelBalance = 0;
public networkInfo: NetworkInfo = {}; 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 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()]; private unsub: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
@ -37,7 +37,7 @@ export class HomeComponent implements OnInit, OnDestroy {
public peers: Peer[] = []; public peers: Peer[] = [];
barPadding = 0; barPadding = 0;
maxBalanceValue = 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; flgTotalCalculated = false;
view = []; view = [];
yAxisLabel = 'Balance'; yAxisLabel = 'Balance';
@ -65,15 +65,11 @@ export class HomeComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
this.store.dispatch(new RTLActions.FetchInfo()); this.actions$.pipe(takeUntil(this.unsub[0]),
this.actions$.pipe(takeUntil(this.unsub[0]), filter((action) => action.type === RTLActions.SET_INFO)) filter(action => action.type === RTLActions.SET_SELECTED_NODE))
.subscribe((infoData: RTLActions.SetInfo) => { .subscribe((data) => {
if(infoData.type === RTLActions.SET_INFO && undefined !== infoData.payload.identity_pubkey) { this.flgTotalCalculated = false;
this.initializeRemainingData();
}
}); });
this.flgTotalCalculated = false;
this.store.select('lnd') this.store.select('lnd')
.pipe(takeUntil(this.unsub[1])) .pipe(takeUntil(this.unsub[1]))
.subscribe((rtlStore) => { .subscribe((rtlStore) => {
@ -112,13 +108,13 @@ export class HomeComponent implements OnInit, OnDestroy {
this.totalBalance = rtlStore.blockchainBalance.total_balance; this.totalBalance = rtlStore.blockchainBalance.total_balance;
this.BTCtotalBalance = rtlStore.blockchainBalance.btc_total_balance; this.BTCtotalBalance = rtlStore.blockchainBalance.btc_total_balance;
if (this.flgLoading[2] !== 'error') { 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.channelBalance = rtlStore.channelBalance.balance;
this.BTCchannelBalance = rtlStore.channelBalance.btc_balance; this.BTCchannelBalance = rtlStore.channelBalance.btc_balance;
if (this.flgLoading[3] !== 'error') { if (this.flgLoading[3] !== 'error') {
this.flgLoading[3] = ('' !== this.channelBalance) ? false : true; this.flgLoading[3] = (this.channelBalance >= 0) ? false : true;
} }
this.networkInfo = rtlStore.networkInfo; this.networkInfo = rtlStore.networkInfo;
@ -126,9 +122,9 @@ export class HomeComponent implements OnInit, OnDestroy {
this.flgLoading[4] = (undefined !== this.networkInfo.num_nodes) ? false : true; 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) { 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; this.flgTotalCalculated = true;
if (this.flgLoading[5] !== 'error') { if (this.flgLoading[5] !== 'error') {
this.flgLoading[5] = false; this.flgLoading[5] = false;
@ -148,17 +144,6 @@ export class HomeComponent implements OnInit, OnDestroy {
}); });
} }
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() { ngOnDestroy() {
this.unsub.forEach(completeSub => { this.unsub.forEach(completeSub => {
completeSub.next(); completeSub.next();

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

@ -1,9 +1,10 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@Component({ @Component({
selector: 'rtl-lnd-root', selector: 'rtl-lnd-root',
templateUrl: './lnd-root.component.html', templateUrl: './lnd-root.component.html',
styleUrls: ['./lnd-root.component.scss'] styleUrls: ['./lnd-root.component.scss']
}) })
export class LNDRootComponent { constructor() {} } 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 { ChannelBackupComponent } from './channels/channel-backup/channel-backup.component';
import { ChannelRestoreComponent } from './channels/channel-restore/channel-restore.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'; import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
export const LndRoutes: Routes = [ export const LndRoutes: Routes = [
{ path: '', component: LNDRootComponent, { path: '', component: LNDRootComponent,
children: [ children: [
{ path: 'unlocklnd', component: UnlockLNDComponent }, { path: 'unlocklnd', component: UnlockLNDComponent, canActivate: [AuthGuard] },
{ path: 'home', component: HomeComponent, canActivate: [LNDUnlockedGuard] }, { path: 'home', component: HomeComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'peers', component: PeersComponent, canActivate: [LNDUnlockedGuard] }, { path: 'peers', component: PeersComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'chnlclosed', component: ChannelClosedComponent, canActivate: [LNDUnlockedGuard] }, { path: 'chnlclosed', component: ChannelClosedComponent, canActivate: [LNDUnlockedGuard] },

@ -4,12 +4,14 @@ import { Router } from '@angular/router';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, Effect, ofType } from '@ngrx/effects';
import { of, Subject } from 'rxjs'; 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 { MatDialog } from '@angular/material';
import { environment, API_URL } from '../../../environments/environment'; import { environment, API_URL } from '../../../environments/environment';
import { LoggerService } from '../../shared/services/logger.service'; 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 { GetInfo, GetInfoChain, Fees, Balance, NetworkInfo, Payment, GraphNode, Transaction, SwitchReq, ListInvoices } from '../../shared/models/lndModels';
import * as RTLActions from '../../store/rtl.actions'; import * as RTLActions from '../../store/rtl.actions';
@ -26,9 +28,10 @@ export class LNDEffects implements OnDestroy {
private httpClient: HttpClient, private httpClient: HttpClient,
private store: Store<fromRTLReducer.RTLState>, private store: Store<fromRTLReducer.RTLState>,
private logger: LoggerService, private logger: LoggerService,
private sessionService: SessionService,
public dialog: MatDialog, public dialog: MatDialog,
private router: Router) { } private router: Router,
private location: Location) { }
@Effect() @Effect()
infoFetch = this.actions$.pipe( infoFetch = this.actions$.pipe(
@ -41,7 +44,7 @@ export class LNDEffects implements OnDestroy {
map((info) => { map((info) => {
this.logger.info(info); this.logger.info(info);
if (undefined === info.identity_pubkey) { if (undefined === info.identity_pubkey) {
sessionStorage.removeItem('lndUnlocked'); this.sessionService.removeItem('lndUnlocked');
this.logger.info('Redirecting to Unlock'); this.logger.info('Redirecting to Unlock');
this.router.navigate(['/lnd/unlocklnd']); this.router.navigate(['/lnd/unlocklnd']);
return { return {
@ -49,33 +52,7 @@ export class LNDEffects implements OnDestroy {
payload: {} payload: {}
}; };
} else { } else {
sessionStorage.setItem('lndUnlocked', 'true'); this.initializeRemainingData(info);
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));
return { return {
type: RTLActions.SET_INFO, type: RTLActions.SET_INFO,
payload: (undefined !== info) ? info : {} payload: (undefined !== info) ? info : {}
@ -83,14 +60,30 @@ export class LNDEffects implements OnDestroy {
} }
}), }),
catchError((err) => { catchError((err) => {
this.logger.error(err); if (typeof err.error.error === 'string' && err.error.error.includes('Not Found')) {
if (err.status === 401) { this.sessionService.removeItem('lndUnlocked');
this.logger.info('Redirecting to Signin');
return of({ type: RTLActions.SIGNOUT });
} else {
this.logger.info('Redirecting to Unlock'); this.logger.info('Redirecting to Unlock');
this.router.navigate(['/lnd/unlocklnd']); this.router.navigate(['/lnd/unlocklnd']);
return of(); return 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 }});
return this.handleErrorWithoutAlert('FetchInfo', err);
} }
}) })
); );
@ -112,12 +105,10 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchPeers', code: err.status, message: err.error.error })); return this.handleErrorWithoutAlert('FetchPeers', err);
this.logger.error(err);
return of();
}) })
); );
} }
)); ));
@Effect() @Effect()
@ -136,22 +127,10 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'Add Peer Failed', this.CHILD_API_URL + environment.PEERS_API, err);
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 })
}
}
}
);
}) })
); );
} }
)); ));
@Effect() @Effect()
@ -170,22 +149,10 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'Unable to Detach Peer. Try again later.', this.CHILD_API_URL + environment.PEERS_API + '/' + action.payload.pubkey, err);
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 })
}
}
}
);
}) })
); );
} }
)); ));
@Effect() @Effect()
@ -216,19 +183,7 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'Add Invoice Failed', this.CHILD_API_URL + environment.INVOICES_API, err);
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 })
}
}
}
);
}) })
); );
} }
@ -255,23 +210,11 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'Open Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API, err);
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 })
}
}
}
);
}) })
); );
} }
)); ));
@Effect() @Effect()
updateChannel = this.actions$.pipe( updateChannel = this.actions$.pipe(
@ -290,23 +233,11 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'Update Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/chanPolicy', err);
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 })
}
}
}
);
}) })
); );
} }
)); ));
@Effect() @Effect()
closeChannel = this.actions$.pipe( closeChannel = this.actions$.pipe(
@ -332,23 +263,11 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return 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);
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 })
}
}
}
);
}) })
); );
} }
)); ));
@Effect() @Effect()
backupChannels = this.actions$.pipe( backupChannels = this.actions$.pipe(
@ -367,24 +286,12 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { 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 })); this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'BackupChannels', code: err.status, message: err.error.error }));
return of( return 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);
{
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 })
}
}
}
);
}) })
); );
} }
)); ));
@Effect() @Effect()
verifyChannels = this.actions$.pipe( verifyChannels = this.actions$.pipe(
@ -403,23 +310,11 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { 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 })); this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'VerifyChannels', code: err.status, message: err.error.error }));
return of( return this.handleErrorWithAlert('ERROR', 'Unable to Verify Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/verify/' + action.payload.channelPoint, err);
{
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 })
}
}
}
);
}) })
); );
} }
)); ));
@Effect() @Effect()
@ -440,20 +335,8 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { 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 })); this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'RestoreChannels', code: err.status, message: err.error.error }));
return of( return this.handleErrorWithAlert('ERROR', 'Unable to Restore Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/restore/' + action.payload.channelPoint, err);
{
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 })
}
}
}
);
}) })
); );
} }
@ -474,11 +357,9 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.logger.error(err); return this.handleErrorWithoutAlert('FetchFees', err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchFees', code: err.status, message: err.error.error })); })
return of(); );
}
));
@Effect() @Effect()
balanceFetch = this.actions$.pipe( balanceFetch = this.actions$.pipe(
@ -499,13 +380,11 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.logger.error(err); return this.handleErrorWithoutAlert('FetchBalance/' + action.payload, err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchBalance/' + action.payload, code: err.status, message: err.error.error }));
return of();
} }
)); ));
} }
)); ));
@Effect() @Effect()
networkInfoFetch = this.actions$.pipe( networkInfoFetch = this.actions$.pipe(
@ -522,11 +401,9 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.logger.error(err); return this.handleErrorWithoutAlert('FetchNetwork', err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchNetwork', code: err.status, message: err.error.error }));
return of();
} }
)); ));
@Effect() @Effect()
channelsFetch = this.actions$.pipe( channelsFetch = this.actions$.pipe(
@ -571,14 +448,12 @@ export class LNDEffects implements OnDestroy {
}; };
} }
}, },
catchError((err: any) => { catchError((err: any) => {
this.logger.error(err); return this.handleErrorWithoutAlert('FetchChannels/' + action.payload.routeParam, err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchChannels/' + action.payload.routeParam, code: err.status, message: err.error.error })); })
return of(); ));
})
));
} }
)); ));
@Effect() @Effect()
invoicesFetch = this.actions$.pipe( invoicesFetch = this.actions$.pipe(
@ -599,13 +474,11 @@ export class LNDEffects implements OnDestroy {
payload: res payload: res
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.logger.error(err); return this.handleErrorWithoutAlert('FetchInvoices', err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchInvoices', code: err.status, message: err.error.error })); }
return of(); ));
} }));
));
}));
@Effect() @Effect()
transactionsFetch = this.actions$.pipe( transactionsFetch = this.actions$.pipe(
@ -622,11 +495,9 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.logger.error(err); return this.handleErrorWithoutAlert('FetchTransactions', err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchTransactions', code: err.status, message: err.error.error }));
return of();
} }
)); ));
@Effect() @Effect()
paymentsFetch = this.actions$.pipe( paymentsFetch = this.actions$.pipe(
@ -643,11 +514,9 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.logger.error(err); return this.handleErrorWithoutAlert('FetchPayments', err);
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'FetchPayments', code: err.status, message: err.error.error }));
return of();
} }
)); ));
@Effect() @Effect()
decodePayment = this.actions$.pipe( decodePayment = this.actions$.pipe(
@ -664,19 +533,7 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'Decode Payment Failed', this.CHILD_API_URL + environment.PAYREQUEST_API + '/' + action.payload, err);
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 })
}
}
}
);
}) })
); );
}) })
@ -709,7 +566,7 @@ export class LNDEffects implements OnDestroy {
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
if (sendRes.payment_error) { if (sendRes.payment_error) {
this.logger.error('Error: ' + sendRes.payment_error); this.logger.error('Error: ' + sendRes.payment_error);
return of({ return {
type: RTLActions.OPEN_ALERT, type: RTLActions.OPEN_ALERT,
payload: { payload: {
width: '70%', data: { width: '70%', data: {
@ -719,7 +576,7 @@ export class LNDEffects implements OnDestroy {
) )
} }
} }
}); };
} else { } else {
const confirmationMsg = { 'Destination': action.payload[1].destination, 'Timestamp': action.payload[1].timestamp_str, 'Expiry': action.payload[1].expiry }; 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) ? confirmationMsg['Amount (' + ((undefined === store.nodeData.smaller_currency_unit) ?
@ -742,19 +599,7 @@ export class LNDEffects implements OnDestroy {
} }
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/transactions', err);
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] })
}
}
}
);
}) })
); );
}) })
@ -773,23 +618,11 @@ export class LNDEffects implements OnDestroy {
payload: (undefined !== graphNode) ? graphNode : {} payload: (undefined !== graphNode) ? graphNode : {}
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'Get Node Address Failed', this.CHILD_API_URL + environment.NETWORK_API + '/node/' + action.payload, err);
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 })
}
}
}
);
}));
}
));
@Effect({ dispatch: false }) @Effect({ dispatch: false })
setGraphNode = this.actions$.pipe( setGraphNode = this.actions$.pipe(
@ -813,21 +646,9 @@ export class LNDEffects implements OnDestroy {
payload: (undefined !== newAddress && undefined !== newAddress.address) ? newAddress.address : {} payload: (undefined !== newAddress && undefined !== newAddress.address) ? newAddress.address : {}
}; };
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'Generate New Address Failed', this.CHILD_API_URL + environment.NEW_ADDRESS_API + '?type=' + action.payload.addressId, err);
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 })
}
}
}
);
}));
}) })
); );
@ -859,22 +680,10 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { 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.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'SetChannelTransaction', code: err.status, message: err.error.error }));
this.logger.error(err); return this.handleErrorWithAlert('ERROR', 'Sending Fund Failed', this.CHILD_API_URL + environment.TRANSACTIONS_API, 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 })
}
}
}
);
})); }));
}) })
); );
@Effect() @Effect()
@ -896,18 +705,7 @@ export class LNDEffects implements OnDestroy {
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'GetForwardingHistory', code: err.status, message: err.error.error })); this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'GetForwardingHistory', code: err.status, message: err.error.error }));
this.logger.error(err); return this.handleErrorWithAlert('ERROR', 'Get Forwarding History Failed', this.CHILD_API_URL + environment.SWITCH_API, 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 })
}
}
}
);
}) })
); );
}) })
@ -928,18 +726,7 @@ export class LNDEffects implements OnDestroy {
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.SetQueryRoutes({})); this.store.dispatch(new RTLActions.SetQueryRoutes({}));
this.logger.error(err); return this.handleErrorWithAlert('ERROR', 'Get Query Routes Failed', this.CHILD_API_URL + environment.NETWORK_API, 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 })
}
}
}
);
}) })
); );
} }
@ -969,14 +756,11 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err) => { catchError((err) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', err.error.message + ' ' + err.error.error.code, this.CHILD_API_URL + environment.WALLET_API + '/genseed/' + action.payload, err);
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();
}) })
); );
} }
)); ));
@Effect({ dispatch: false }) @Effect({ dispatch: false })
genSeedResponse = this.actions$.pipe( genSeedResponse = this.actions$.pipe(
@ -1014,14 +798,11 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err) => { catchError((err) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', err.error.error, this.CHILD_API_URL + environment.WALLET_API + '/initwallet', err);
this.store.dispatch(new RTLActions.OpenAlert({ width: '70%', data: { type: 'ERROR', titleMessage: err.error.error } }));
this.logger.error(err.error.error);
return of();
}) })
); );
} }
)); ));
@Effect({ dispatch: false }) @Effect({ dispatch: false })
unlockWallet = this.actions$.pipe( unlockWallet = this.actions$.pipe(
@ -1034,20 +815,17 @@ export class LNDEffects implements OnDestroy {
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenSpinner('Initializing Node...')); this.store.dispatch(new RTLActions.OpenSpinner('Initializing Node...'));
this.logger.info('Successfully Unlocked!'); this.logger.info('Successfully Unlocked!');
sessionStorage.setItem('lndUnlocked', 'true'); this.sessionService.setItem('lndUnlocked', 'true');
setTimeout(() => { setTimeout(() => {
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.info('Successfully Initialized!'); this.logger.info('Successfully Initialized!');
this.store.dispatch(new RTLActions.InitAppData()); this.store.dispatch(new RTLActions.FetchInfo());
this.router.navigate(['/lnd/']); this.router.navigate(['/lnd/home']);
}, 1000 * 90); }, 1000 * 90);
return of({}); return { type: RTLActions.VOID };
}), }),
catchError((err) => { catchError((err) => {
this.store.dispatch(new RTLActions.CloseSpinner()); return this.handleErrorWithAlert('ERROR', 'err.error.error', this.CHILD_API_URL + environment.WALLET_API + '/unlockwallet', err);
this.store.dispatch(new RTLActions.OpenAlert({ width: '70%', data: { type: 'ERROR', titleMessage: err.error.error } }));
this.logger.error(err.error.error);
return of();
}) })
); );
} }
@ -1069,20 +847,8 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { 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.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'Lookup', code: err.status, message: err.error.message }));
this.logger.error(err); return this.handleErrorWithAlert('ERROR', 'Peer Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/node/' + action.payload, 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 })
}
}
}
);
}) })
); );
}) })
@ -1104,20 +870,8 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { 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.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'Lookup', code: err.status, message: err.error.message }));
this.logger.error(err); return this.handleErrorWithAlert('ERROR', 'Channel Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/edge/' + action.payload, 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 })
}
}
}
);
}) })
); );
}) })
@ -1139,20 +893,8 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { 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.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'Lookup', code: err.status, message: err.error.message }));
this.logger.error(err); return this.handleErrorWithAlert('ERROR', 'Invoice Lookup Failed', this.CHILD_API_URL + environment.INVOICES_API + '/' + action.payload, 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 })
}
}
}
);
}) })
); );
}) })
@ -1184,20 +926,8 @@ export class LNDEffects implements OnDestroy {
}; };
}), }),
catchError((err: any) => { 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.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'RestoreChannelsList', code: err.status, message: err.error.message }));
this.logger.error(err); return this.handleErrorWithAlert('ERROR', 'Restore Channels List Failed', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API, 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 })
}
}
}
);
}) })
); );
}) })
@ -1212,6 +942,83 @@ export class LNDEffects implements OnDestroy {
}) })
); );
initializeRemainingData(info: any) {
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 === '') {
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(err);
if (err.status === 401) {
this.logger.info('Redirecting to Signin');
return of({ type: RTLActions.SIGNOUT });
} else {
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: actionName, code: err.status.toString(), message: err.error.error }));
return of({ type: RTLActions.VOID });
}
}
handleErrorWithAlert(alerType: string, alertTitle: string, errURL: string, err: { status: number, error: any }) {
this.logger.error(err);
if (err.status === 401) {
this.logger.info('Redirecting to Signin');
return of({ type: RTLActions.SIGNOUT });
} else {
this.store.dispatch(new RTLActions.CloseSpinner());
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 })
}
}
});
}
}
ngOnDestroy() { ngOnDestroy() {
this.unSubs.forEach(completeSub => { this.unSubs.forEach(completeSub => {
completeSub.next(); completeSub.next();

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

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

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

@ -1,14 +1,14 @@
<div fxLayout="column" fxLayoutAlign="center center"> <div fxLayout="column" fxLayoutAlign="center center">
<mat-card class="mat-elevation-z24 not-found-box"> <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"> <button mat-fab color="accent" class="mat-elevation-z12">
<mat-icon>error</mat-icon> <mat-icon>error</mat-icon>
</button> </button>
<h1 class="error">401</h1> <h1 class="error">{{error.errorCode}}</h1>
</div> </div>
<mat-card-content fxLayout="row" fxLayoutAlign="center center"> <mat-card-content fxLayout="row" fxLayoutAlign="center center">
<mat-card fxLayout="column" fxLayoutAlign="center center" class="mat-elevation-z12 w-100"> <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>
</mat-card-content> </mat-card-content>
</mat-card> </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,10 +1,9 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { LoggerService } from '../../../services/logger.service'; import { SessionService } from '../../../services/session.service';
import { MENU_DATA } from '../../../models/navMenu'; import { MENU_DATA } from '../../../models/navMenu';
import { RTLEffects } from '../../../../store/rtl.effects'; import { RTLEffects } from '../../../../store/rtl.effects';
@ -16,14 +15,14 @@ import * as fromRTLReducer from '../../../../store/rtl.reducers';
templateUrl: './horizontal-navigation.component.html', templateUrl: './horizontal-navigation.component.html',
styleUrls: ['./horizontal-navigation.component.scss'] styleUrls: ['./horizontal-navigation.component.scss']
}) })
export class HorizontalNavigationComponent implements OnInit { export class HorizontalNavigationComponent implements OnInit, OnDestroy {
public menuNodes = []; public menuNodes = [];
public logoutNode = []; public logoutNode = [];
public showLogout = false; public showLogout = false;
public numPendingChannels = 0; public numPendingChannels = 0;
private unSubs = [new Subject(), new Subject(), new Subject()]; 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() { ngOnInit() {
@ -37,21 +36,15 @@ export class HorizontalNavigationComponent implements OnInit {
this.menuNodes = MENU_DATA.LNDChildren; this.menuNodes = MENU_DATA.LNDChildren;
} }
}); });
this.actions$ this.sessionService.watchSession()
.pipe( .pipe(takeUntil(this.unSubs[1]))
takeUntil(this.unSubs[2]), .subscribe(session => {
filter((action) => action.type === RTLActions.SIGNOUT || action.type === RTLActions.SIGNIN) if(session.token) {
).subscribe((action) => {
if (action.type === RTLActions.SIGNIN) {
this.menuNodes.push({id: 200, parentId: 0, name: 'Logout', icon: 'eject'}); this.menuNodes.push({id: 200, parentId: 0, name: 'Logout', icon: 'eject'});
} } else {
if (action.type === RTLActions.SIGNOUT) {
this.menuNodes.pop(); this.menuNodes.pop();
} }
}); });
if (sessionStorage.getItem('token')) {
this.menuNodes.push({id: 200, parentId: 0, name: 'Logout', icon: 'eject'});
}
} }
onClick(node) { onClick(node) {
@ -60,7 +53,7 @@ export class HorizontalNavigationComponent implements OnInit {
width: '70%', data: { type: 'CONFIRM', titleMessage: 'Logout from this device?', noBtnText: 'Cancel', yesBtnText: 'Logout' width: '70%', data: { type: 'CONFIRM', titleMessage: 'Logout from this device?', noBtnText: 'Cancel', yesBtnText: 'Logout'
}})); }}));
this.rtlEffects.closeConfirm this.rtlEffects.closeConfirm
.pipe(takeUntil(this.unSubs[1])) .pipe(takeUntil(this.unSubs[3]))
.subscribe(confirmRes => { .subscribe(confirmRes => {
if (confirmRes) { if (confirmRes) {
this.showLogout = false; this.showLogout = false;
@ -69,4 +62,11 @@ export class HorizontalNavigationComponent implements OnInit {
}); });
} }
} }
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
} }

@ -11,8 +11,9 @@ import { MatTreeNestedDataSource } from '@angular/material/tree';
import { LightningNode, Settings, GetInfoRoot } from '../../../models/RTLconfig'; import { LightningNode, Settings, GetInfoRoot } from '../../../models/RTLconfig';
import { LoggerService } from '../../../services/logger.service'; import { LoggerService } from '../../../services/logger.service';
import { SessionService } from '../../../services/session.service';
import { GetInfoChain } from '../../../models/lndModels'; import { GetInfoChain } from '../../../models/lndModels';
import { MenuChildNode, FlatMenuNode, MENU_DATA } from '../../../models/navMenu'; import { MenuChildNode, MENU_DATA } from '../../../models/navMenu';
import { RTLEffects } from '../../../../store/rtl.effects'; import { RTLEffects } from '../../../../store/rtl.effects';
import * as RTLActions from '../../../../store/rtl.actions'; import * as RTLActions from '../../../../store/rtl.actions';
@ -36,13 +37,13 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
public numPendingChannels = 0; public numPendingChannels = 0;
public smallScreen = false; public smallScreen = false;
public childRootRoute = ''; 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); treeControlLogout = new NestedTreeControl<MenuChildNode>(node => node.children);
treeControlNested = new NestedTreeControl<MenuChildNode>(node => node.children); treeControlNested = new NestedTreeControl<MenuChildNode>(node => node.children);
navMenus = new MatTreeNestedDataSource<MenuChildNode>(); navMenus = new MatTreeNestedDataSource<MenuChildNode>();
navMenusLogout = 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; this.version = environment.VERSION;
if (MENU_DATA.LNDChildren[MENU_DATA.LNDChildren.length - 1].id === 200) { if (MENU_DATA.LNDChildren[MENU_DATA.LNDChildren.length - 1].id === 200) {
MENU_DATA.LNDChildren.pop(); MENU_DATA.LNDChildren.pop();
@ -75,10 +76,6 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
} }
this.flgLoading = (undefined !== this.information.identity_pubkey) ? false : true; 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) { if (window.innerWidth <= 414) {
this.smallScreen = true; this.smallScreen = true;
} }
@ -89,6 +86,12 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
} }
this.logger.info(rtlStore); 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]), this.actions$.pipe(takeUntil(this.unSubs[2]),
filter((action) => action.type === RTLActions.SIGNOUT)) filter((action) => action.type === RTLActions.SIGNOUT))
.subscribe((action: RTLActions.Signout) => { .subscribe((action: RTLActions.Signout) => {
@ -104,7 +107,7 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
width: '70%', data: { type: 'CONFIRM', titleMessage: 'Logout from this device?', noBtnText: 'Cancel', yesBtnText: 'Logout' width: '70%', data: { type: 'CONFIRM', titleMessage: 'Logout from this device?', noBtnText: 'Cancel', yesBtnText: 'Logout'
}})); }}));
this.rtlEffects.closeConfirm this.rtlEffects.closeConfirm
.pipe(takeUntil(this.unSubs[1])) .pipe(takeUntil(this.unSubs[3]))
.subscribe(confirmRes => { .subscribe(confirmRes => {
if (confirmRes) { if (confirmRes) {
this.showLogout = false; this.showLogout = false;

@ -6,6 +6,7 @@ import { Actions } from '@ngrx/effects';
import { GetInfoRoot, LightningNode } from '../../../models/RTLconfig'; import { GetInfoRoot, LightningNode } from '../../../models/RTLconfig';
import { LoggerService } from '../../../services/logger.service'; import { LoggerService } from '../../../services/logger.service';
import { SessionService } from '../../../services/session.service';
import { GetInfoChain } from '../../../models/lndModels'; import { GetInfoChain } from '../../../models/lndModels';
import { environment } from '../../../../../environments/environment'; import { environment } from '../../../../../environments/environment';
@ -25,9 +26,9 @@ export class TopMenuComponent implements OnInit, OnDestroy {
public informationChain: GetInfoChain = {}; public informationChain: GetInfoChain = {};
public flgLoading = true; public flgLoading = true;
public showLogout = false; 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; this.version = environment.VERSION;
} }
@ -53,12 +54,13 @@ export class TopMenuComponent implements OnInit, OnDestroy {
this.informationChain.chain = ''; this.informationChain.chain = '';
this.informationChain.network = ''; this.informationChain.network = '';
} }
this.showLogout = (sessionStorage.getItem('token')) ? true : false;
this.logger.info(rtlStore); 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$ this.actions$
.pipe( .pipe(
@ -74,7 +76,7 @@ export class TopMenuComponent implements OnInit, OnDestroy {
width: '70%', data: { type: 'CONFIRM', titleMessage: 'Logout from this device?', noBtnText: 'Cancel', yesBtnText: 'Logout' width: '70%', data: { type: 'CONFIRM', titleMessage: 'Logout from this device?', noBtnText: 'Cancel', yesBtnText: 'Logout'
}})); }}));
this.rtlEffects.closeConfirm this.rtlEffects.closeConfirm
.pipe(takeUntil(this.unSubs[1])) .pipe(takeUntil(this.unSubs[3]))
.subscribe(confirmRes => { .subscribe(confirmRes => {
if (confirmRes) { if (confirmRes) {
this.showLogout = false; 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 { export interface LocalRemoteBalanceCL {
localBalance?: number; localBalance: number;
remoteBalance?: number; remoteBalance: number;
btc_localBalance?: number; btc_localBalance?: number;
btc_remoteBalance?: number; btc_remoteBalance?: number;
} }

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

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

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

@ -26,7 +26,7 @@ import { TopMenuComponent } from './components/navigation/top-menu/top-menu.comp
import { HorizontalNavigationComponent } from './components/navigation/horizontal-navigation/horizontal-navigation.component'; import { HorizontalNavigationComponent } from './components/navigation/horizontal-navigation/horizontal-navigation.component';
import { SettingsNavComponent } from './components/settings-nav/settings-nav.component'; import { SettingsNavComponent } from './components/settings-nav/settings-nav.component';
import { ServerConfigComponent } from './components/server-config/server-config.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 { ClipboardDirective } from './directive/clipboard.directive';
import { RemoveLeadingZerosPipe } from './pipes/remove-leading-zero.pipe'; import { RemoveLeadingZerosPipe } from './pipes/remove-leading-zero.pipe';
@ -130,7 +130,7 @@ import { RemoveLeadingZerosPipe } from './pipes/remove-leading-zero.pipe';
HelpComponent, HelpComponent,
ServerConfigComponent, ServerConfigComponent,
ClipboardDirective, ClipboardDirective,
SsoFailedComponent, ErrorComponent,
RemoveLeadingZerosPipe RemoveLeadingZerosPipe
], ],
entryComponents: [ entryComponents: [

@ -635,7 +635,7 @@ export class FetchLocalRemoteBalanceCL implements Action {
export class SetLocalRemoteBalanceCL implements Action { export class SetLocalRemoteBalanceCL implements Action {
readonly type = SET_LOCAL_REMOTE_BALANCE_CL; readonly type = SET_LOCAL_REMOTE_BALANCE_CL;
constructor(public payload: {}) {} constructor(public payload: {localBalance: number, remoteBalance: number}) {}
} }
export class GetNewAddressCL implements Action { export class GetNewAddressCL implements Action {

@ -11,6 +11,7 @@ import { MatDialog } from '@angular/material';
import { environment, API_URL } from '../../environments/environment'; import { environment, API_URL } from '../../environments/environment';
import { LoggerService } from '../shared/services/logger.service'; import { LoggerService } from '../shared/services/logger.service';
import { SessionService } from '../shared/services/session.service';
import { Settings, RTLConfiguration, AuthenticateWith } from '../shared/models/RTLconfig'; import { Settings, RTLConfiguration, AuthenticateWith } from '../shared/models/RTLconfig';
import { SpinnerDialogComponent } from '../shared/components/spinner-dialog/spinner-dialog.component'; import { SpinnerDialogComponent } from '../shared/components/spinner-dialog/spinner-dialog.component';
@ -31,6 +32,7 @@ export class RTLEffects implements OnDestroy {
private httpClient: HttpClient, private httpClient: HttpClient,
private store: Store<fromRTLReducer.RTLState>, private store: Store<fromRTLReducer.RTLState>,
private logger: LoggerService, private logger: LoggerService,
private sessionService: SessionService,
public dialog: MatDialog, public dialog: MatDialog,
private router: Router, private router: Router,
private location: Location) { } private location: Location) { }
@ -162,7 +164,7 @@ export class RTLEffects implements OnDestroy {
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('IsAuthorized')); this.store.dispatch(new RTLActions.ClearEffectErrorRoot('IsAuthorized'));
return this.httpClient.post(environment.AUTHENTICATE_API, { return this.httpClient.post(environment.AUTHENTICATE_API, {
authenticateWith: (undefined === action.payload || action.payload == null || action.payload === '') ? AuthenticateWith.TOKEN : AuthenticateWith.PASSWORD, 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( .pipe(
map((postRes: any) => { map((postRes: any) => {
@ -202,18 +204,14 @@ export class RTLEffects implements OnDestroy {
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('Signin')); this.store.dispatch(new RTLActions.ClearEffectErrorRoot('Signin'));
return this.httpClient.post(environment.AUTHENTICATE_API, { return this.httpClient.post(environment.AUTHENTICATE_API, {
authenticateWith: (undefined === action.payload || action.payload == null || action.payload === '') ? AuthenticateWith.TOKEN : AuthenticateWith.PASSWORD, 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( .pipe(
map((postRes: any) => { map((postRes: any) => {
this.logger.info(postRes); this.logger.info(postRes);
this.logger.info('Successfully Authorized!'); this.logger.info('Successfully Authorized!');
this.SetToken(postRes.token); this.SetToken(postRes.token);
if(rootStore.selNode.lnImplementation.toUpperCase() === 'CLT') { this.store.dispatch(new RTLActions.SetSelelectedNode({lnNode: rootStore.selNode, isInitialSetup: true}))
this.router.navigate(['/cl/home']);
} else {
this.router.navigate(['/lnd/home']);
}
}), }),
catchError((err) => { catchError((err) => {
this.store.dispatch(new RTLActions.OpenAlert({ width: '70%', data: {type: 'ERROR', message: JSON.stringify(err.error)}})); 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.error(err.error);
this.logger.info('Redirecting to Signin Error Page'); this.logger.info('Redirecting to Signin Error Page');
if (+rootStore.appConfig.sso.rtlSSO) { if (+rootStore.appConfig.sso.rtlSSO) {
this.router.navigate(['/ssoerror']); this.router.navigate(['/error'], { state: { errorCode: '401', errorMessage: 'Single Sign On Failed!' }});
} else { } else {
this.router.navigate([rootStore.appConfig.sso.logoutRedirectLink]); this.router.navigate([rootStore.appConfig.sso.logoutRedirectLink]);
} }
@ -240,9 +238,9 @@ export class RTLEffects implements OnDestroy {
} else { } else {
this.router.navigate([store.appConfig.sso.logoutRedirectLink]); this.router.navigate([store.appConfig.sso.logoutRedirectLink]);
} }
sessionStorage.removeItem('clUnlocked'); this.sessionService.removeItem('clUnlocked');
sessionStorage.removeItem('lndUnlocked'); this.sessionService.removeItem('lndUnlocked');
sessionStorage.removeItem('token'); this.sessionService.removeItem('token');
this.logger.warn('LOGGED OUT'); this.logger.warn('LOGGED OUT');
return of(); return of();
})); }));
@ -251,44 +249,14 @@ export class RTLEffects implements OnDestroy {
setSelectedNode = this.actions$.pipe( setSelectedNode = this.actions$.pipe(
ofType(RTLActions.SET_SELECTED_NODE), ofType(RTLActions.SET_SELECTED_NODE),
mergeMap((action: RTLActions.SetSelelectedNode) => { mergeMap((action: RTLActions.SetSelelectedNode) => {
sessionStorage.setItem('lndUnlocked', 'true');
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('UpdateSelNode')); this.store.dispatch(new RTLActions.ClearEffectErrorRoot('UpdateSelNode'));
return this.httpClient.post(environment.CONF_API + '/updateSelNode', { selNodeIndex: action.payload.lnNode.index }) return this.httpClient.post(environment.CONF_API + '/updateSelNode', { selNodeIndex: action.payload.lnNode.index })
.pipe( .pipe(
map((postRes: any) => { map((postRes: any) => {
this.logger.info(postRes); this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
let selNode = { channelBackupPath: action.payload.lnNode.settings.channelBackupPath, satsToBTC: action.payload.lnNode.settings.satsToBTC }; this.initializeNode(action.payload);
this.store.dispatch(new RTLActions.ResetRootStore(action.payload.lnNode)); return { type: RTLActions.VOID };
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') {
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 {
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 };
}
}
}), }),
catchError((err: any) => { catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
@ -305,16 +273,30 @@ export class RTLEffects implements OnDestroy {
}) })
); );
} }
)); ));
initializeNode(node) {
let selNode = { channelBackupPath: node.lnNode.settings.channelBackupPath, satsToBTC: node.lnNode.settings.satsToBTC };
this.store.dispatch(new RTLActions.ResetRootStore(node.lnNode));
this.store.dispatch(new RTLActions.ResetLNDStore(selNode));
this.store.dispatch(new RTLActions.ResetCLStore(selNode));
if(node.lnNode.lnImplementation.toUpperCase() === 'CLT') {
this.CHILD_API_URL = API_URL + '/cl';
this.store.dispatch(new RTLActions.FetchInfoCL());
} else {
this.CHILD_API_URL = API_URL + '/lnd';
this.store.dispatch(new RTLActions.FetchInfo());
}
}
SetToken(token: string) { SetToken(token: string) {
if (token) { if (token) {
sessionStorage.setItem('lndUnlocked', 'true'); this.sessionService.setItem('lndUnlocked', 'true');
sessionStorage.setItem('token', token); this.sessionService.setItem('token', token);
this.store.dispatch(new RTLActions.InitAppData()); this.store.dispatch(new RTLActions.InitAppData());
} else { } else {
sessionStorage.removeItem('lndUnlocked'); this.sessionService.removeItem('lndUnlocked');
sessionStorage.removeItem('token'); this.sessionService.removeItem('token');
} }
} }

Loading…
Cancel
Save