App settings (#604)

Settings updates
UTXO lease confirmation box
Config settings without add new node
Login and Settings page
pull/605/head
ShahanaFarooqui 3 years ago committed by GitHub
parent a2ffd6ea05
commit 835c834b82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

File diff suppressed because one or more lines are too long

@ -12,8 +12,8 @@
<link rel="mask-icon" href="assets/images/favicon-light/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="styles.464550ab9821baed01a0.css"></head>
<link rel="stylesheet" href="styles.e2497f72fe03c85f38b7.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.352b083a0907b5a06343.js" defer></script><script src="polyfills.ea991b800cfaf577eb9d.js" defer></script><script src="main.04aadc93837da252d0a3.js" defer></script></body>
<script src="runtime.836c6a42497327d3805d.js" defer></script><script src="polyfills.ea991b800cfaf577eb9d.js" defer></script><script src="main.feef70834d7529dfaaf8.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],f=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=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,a=1;a<t.length;a++)0!==o[t[a]]&&(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,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"."+{1:"7b681204396e758e5e1b",5:"0b6506ded802705e1cf8",6:"2ec1dce4f108299e1066",7:"b47272f856369667bec5"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.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:a})},12e4);a.onerror=a.onload=u,document.head.appendChild(a)}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 a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var f=0;f<a.length;f++)r(a[f]);var l=c;t()}([]);

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -12,7 +12,7 @@ common.rtl_sso = 0;
common.port = 3000;
common.host = null;
common.rtl_cookie_path = '';
common.logout_redirect_link = '/login';
common.logout_redirect_link = '';
common.cookie = '';
common.secret_key = crypto.randomBytes(64).toString('hex');
common.nodes = [];
@ -152,7 +152,8 @@ common.findNode = (selNodeIndex) => {
}
common.replaceNode = (selNodeIndex, newNode) => {
common.nodes.splice(common.nodes.findIndex((node) => {node.index == selNodeIndex}), 1, newNode);
let foundIndex = common.nodes.findIndex((node) => node.index == selNodeIndex);
common.nodes.splice(foundIndex, 1, newNode);
common.selectedNode = common.findNode(selNodeIndex);
}

@ -253,22 +253,22 @@ connect.setSSOParams = (config) => {
} else if (config.SSO && config.SSO.rtlSSO) {
common.rtl_sso = config.SSO.rtlSSO;
}
if (process.env.RTL_COOKIE_PATH) {
common.rtl_cookie_path = process.env.RTL_COOKIE_PATH;
} else if (config.SSO && config.SSO.rtlCookiePath) {
common.rtl_cookie_path = config.SSO.rtlCookiePath;
} else {
common.rtl_cookie_path = '';
}
if (+common.rtl_sso) {
if (process.env.LOGOUT_REDIRECT_LINK) {
common.logout_redirect_link = process.env.LOGOUT_REDIRECT_LINK;
} else if (config.SSO && config.SSO.logoutRedirectLink) {
common.logout_redirect_link = config.SSO.logoutRedirectLink;
}
if (process.env.RTL_COOKIE_PATH) {
common.rtl_cookie_path = process.env.RTL_COOKIE_PATH;
} else if (config.SSO && config.SSO.rtlCookiePath) {
common.rtl_cookie_path = config.SSO.rtlCookiePath;
} else {
common.rtl_cookie_path = common.rtl_conf_file_path + '/cookies/auth.cookie';
}
if (process.env.LOGOUT_REDIRECT_LINK) {
common.logout_redirect_link = process.env.LOGOUT_REDIRECT_LINK;
} else if (config.SSO && config.SSO.logoutRedirectLink) {
common.logout_redirect_link = config.SSO.logoutRedirectLink;
}
if (+common.rtl_sso) {
if (!common.rtl_cookie_path || common.rtl_cookie_path.trim() === '') {
errMsg = 'Please set rtlCookiePath value for single sign on option!';
} else {

@ -37,11 +37,9 @@ exports.getRTLConfig = (req, res, next) => {
if (common.nodes && common.nodes.length > 0) {
common.nodes.forEach((node, i) => {
const authentication = {};
if(node.config_path) {
authentication.configPath = node.config_path;
} else {
authentication.configPath = '';
}
authentication.configPath = (node.config_path) ? node.config_path : '';
authentication.swapMacaroonPath = (node.swap_macaroon_path) ? node.swap_macaroon_path : '';
authentication.boltzMacaroonPath = (node.boltz_macaroon_path) ? node.boltz_macaroon_path : '';
const settings = {};
settings.userPersona = node.user_persona ? node.user_persona : 'MERCHANT';
settings.themeMode = (node.theme_mode) ? node.theme_mode : 'DAY';
@ -96,14 +94,14 @@ exports.updateUISettings = (req, res, next) => {
});
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
logger.info({fileName: 'RTLConf', msg: 'Updating Application Node Settings Succesful!'});
res.status(201).json({message: 'Application Node Settings Updated Successfully'});
logger.info({fileName: 'RTLConf', msg: 'Updating Node Settings Succesful!'});
res.status(201).json({message: 'Node Settings Updated Successfully'});
}
catch (err) {
logger.error({fileName: 'Conf', lineNum: 101, msg: 'Updating Application Node Settings Failed!'});
logger.error({fileName: 'Conf', lineNum: 101, msg: 'Updating Node Settings Failed!'});
res.status(500).json({
message: "Updating Application Node Settings Failed!",
error: 'Updating Application Node Settings Failed!'
message: "Updating Node Settings Failed!",
error: 'Updating Node Settings Failed!'
});
}
};
@ -178,8 +176,6 @@ exports.getConfig = (req, res, next) => {
jsonConfig = JSON.parse(data);
} else {
jsonConfig = ini.parse(data);
console.warn();
console.warn(jsonConfig);
switch (common.selectedNode.ln_implementation) {
case 'ECL':
if (jsonConfig['eclair.api.password']) {
@ -264,4 +260,78 @@ exports.getCurrencyRates = (req, res, next) => {
error: err.error
});
});
};
};
exports.updateSSO = (req, res, next) => {
RTLConfFile = common.rtl_conf_file_path + common.path_separator + 'RTL-Config.json';
var config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
delete config.SSO;
config.SSO = req.body.SSO;
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
logger.info({fileName: 'RTLConf', msg: 'Updating SSO Succesful!'});
res.status(201).json({message: 'SSO Updated Successfully'});
}
catch (err) {
logger.error({fileName: 'RTLConf', lineNum: 279, msg: 'Updating SSO Failed!'});
res.status(500).json({
message: "Updating SSO Failed!",
error: 'Updating SSO Failed!'
});
}
};
exports.updateServiceSettings = (req, res, next) => {
var RTLConfFile = common.rtl_conf_file_path + common.path_separator + 'RTL-Config.json';
var config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
const selectedNode = common.findNode(common.selectedNode.index);
config.nodes.find(node => {
if(node.index == common.selectedNode.index) {
switch (req.body.service) {
case 'LOOP':
if (req.body.settings.enable) {
node.Settings.swapServerUrl = req.body.settings.serverUrl;
node.Authentication.swapMacaroonPath = req.body.settings.macaroonPath;
selectedNode.swap_server_url = req.body.settings.serverUrl;
selectedNode.swap_macaroon_path = req.body.settings.macaroonPath;
} else {
delete node.Settings.swapServerUrl;
delete node.Authentication.swapMacaroonPath;
delete selectedNode.swap_server_url;
delete selectedNode.swap_macaroon_path;
}
break;
case 'BOLTZ':
if (req.body.settings.enable) {
node.Settings.boltzServerUrl = req.body.settings.serverUrl;
node.Authentication.boltzMacaroonPath = req.body.settings.macaroonPath;
selectedNode.boltz_server_url = req.body.settings.serverUrl;
selectedNode.boltz_macaroon_path = req.body.settings.macaroonPath;
} else {
delete node.Settings.boltzServerUrl;
delete node.Authentication.boltzMacaroonPath;
delete selectedNode.boltz_server_url;
delete selectedNode.boltz_macaroon_path;
}
break;
default:
break;
}
common.replaceNode(common.selectedNode.index, selectedNode);
}
});
try {
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
logger.info({fileName: 'RTLConf', msg: 'Updating Service Settings Succesful!'});
res.status(201).json({message: 'Service Settings Updated Successfully'});
}
catch (err) {
logger.error({fileName: 'RTLConf', lineNum: 333, msg: 'Updating Service Settings Failed!'});
res.status(500).json({
message: "Updating Service Settings Failed!",
error: 'Updating Service Settings Failed!'
});
}
};

@ -34,7 +34,7 @@ handleError = (failed, currentTime, errMsg) => {
} else {
return {
message: "Authentication Failed!",
error: errMsg + " Application will be locked after " + (ALLOWED_LOGIN_ATTEMPTS - failed.count) + " more unsuccessful attempts!"
error: errMsg + "\nApplication will be locked after " + (ALLOWED_LOGIN_ATTEMPTS - failed.count) + " more unsuccessful attempts!"
};
}
}

@ -10,5 +10,7 @@ router.get("/config/:nodeType", authCheck, RTLConfController.getConfig);
router.get("/file", authCheck, RTLConfController.getFile);
router.post("/updateSelNode", RTLConfController.updateSelectedNode);
router.post("/updateDefaultNode", RTLConfController.updateDefaultNode);
router.post("/updateServiceSettings", RTLConfController.updateServiceSettings);
router.post("/updateSSO", RTLConfController.updateSSO);
router.get("/rates", RTLConfController.getCurrencyRates);
module.exports = router;

@ -1,10 +1,10 @@
<div fxLayout="column" id="rtl-container" class="rtl-container medium" [ngClass]="[settings.themeColor | lowercase, settings.themeMode | lowercase]">
<mat-toolbar fxLayout="row" fxLayoutAlign="space-between center" class="padding-gap-x bg-primary rtl-top-toolbar">
<div>
<button class="top-toolbar-icon mr-1" mat-icon-button (click)="sideNavToggle()" [matTooltip]="flgSideNavOpened ? 'Hide Navigation Menu' : 'Show Navigation Menu'" [matTooltipDisabled]="smallScreen" matTooltipPosition="right">
<button *ngIf="flgLoggedIn" class="top-toolbar-icon mr-1" mat-icon-button (click)="sideNavToggle()" [matTooltip]="flgSideNavOpened ? 'Hide Navigation Menu' : 'Show Navigation Menu'" [matTooltipDisabled]="smallScreen" matTooltipPosition="right">
<mat-icon>menu</mat-icon>
</button>
<button *ngIf="!smallScreen" mat-icon-button (click)="flgSidenavPinned = !flgSidenavPinned" [matTooltip]="flgSidenavPinned ? 'Unpin Navigation Menu' : 'Pin Navigation Menu'" matTooltipPosition="right">
<button *ngIf="!smallScreen && flgLoggedIn" mat-icon-button (click)="flgSidenavPinned = !flgSidenavPinned" [matTooltip]="flgSidenavPinned ? 'Unpin Navigation Menu' : 'Pin Navigation Menu'" matTooltipPosition="right">
<svg class="top-toolbar-icon icon-pinned" viewBox="0 0 32 32">
<path fill="currentColor" *ngIf="!flgSidenavPinned" d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z" />
<path fill="currentColor" *ngIf="flgSidenavPinned" d="M2,5.27L3.28,4L20,20.72L18.73,22L12.8,16.07V22H11.2V16H6V14L8,12V11.27L2,5.27M16,12L18,14V16H17.82L8,6.18V4H7V2H17V4H16V12Z" />
@ -20,12 +20,12 @@
</div>
</mat-toolbar>
<mat-sidenav-container>
<mat-sidenav [perfectScrollbar] [opened]="flgSideNavOpened" [mode]="(flgSidenavPinned && !smallScreen) ? 'side' : 'over'" #sideNavigation class="sidenav mat-elevation-z6">
<mat-sidenav [perfectScrollbar] [opened]="flgSideNavOpened && flgLoggedIn" [mode]="(flgSidenavPinned && !smallScreen) ? 'side' : 'over'" #sideNavigation class="sidenav mat-elevation-z6">
<rtl-side-navigation (ChildNavClicked)="onNavigationClicked($event)" fxFlex="100"></rtl-side-navigation>
</mat-sidenav>
<mat-sidenav-content [perfectScrollbar] #sideNavContent>
<div [ngClass]="{'inner-sidenav-content': true}">
<router-outlet></router-outlet>
<div [@routeAnimation]="outlet && outlet.activatedRouteData" class="inner-sidenav-content" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<router-outlet #outlet="outlet"></router-outlet>
</div>
</mat-sidenav-content>>
</mat-sidenav-container>

@ -14,6 +14,7 @@ import { CommonService } from './shared/services/common.service';
import { SessionService } from './shared/services/session.service';
import { AlertTypeEnum, ScreenSizeEnum } from './shared/services/consts-enums-functions';
import { RTLConfiguration, Settings, ConfigSettingsNode, GetInfoRoot } from './shared/models/RTLconfig';
import { routeAnimation } from './shared/animation/route-animation';
import * as RTLActions from './store/rtl.actions';
import * as fromRTLReducer from './store/rtl.reducers';
@ -21,7 +22,8 @@ import * as fromRTLReducer from './store/rtl.reducers';
@Component({
selector: 'rtl-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
styleUrls: ['./app.component.scss'],
animations: [routeAnimation]
})
export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('sideNavigation', { static: false }) sideNavigation: any;
@ -37,6 +39,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
public xSmallScreen = false;
public smallScreen = false;
public flgSidenavPinned = true;
public flgLoggedIn = false;
unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions,
@ -79,26 +82,33 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
this.flgLoading[0] = ( this.information.identity_pubkey) ? false : true;
this.logger.info(this.settings);
if (!this.sessionService.getItem('token')) {
this.flgLoggedIn = false;
this.flgLoading[0] = false;
} else {
this.flgLoggedIn = true;
}
});
this.actions$.pipe(takeUntil(this.unSubs[1]),
filter((action) => action.type === RTLActions.SET_RTL_CONFIG))
.subscribe((action: (RTLActions.SetRTLConfig)) => {
filter((action) => action.type === RTLActions.SET_RTL_CONFIG || action.type === RTLActions.LOGOUT))
.subscribe((action: (RTLActions.SetRTLConfig | RTLActions.Logout)) => {
if (action.type === RTLActions.SET_RTL_CONFIG) {
if (!this.sessionService.getItem('token')) {
if (+action.payload.sso.rtlSSO) {
this.store.dispatch(new RTLActions.Login({password: sha256(this.accessKey), defaultPassword: false}));
} else {
this.router.navigate([this.appConfig.sso.logoutRedirectLink]);
this.router.navigate(['./login']);
}
}
}
if (action.type === RTLActions.LOGOUT) {
this.flgLoggedIn = false;
}
});
this.userIdle.startWatching();
this.userIdle.onTimerStart().pipe(takeUntil(this.unSubs[2])).subscribe(count => {});
this.userIdle.onTimeout().pipe(takeUntil(this.unSubs[3])).subscribe(() => {
if (this.sessionService.getItem('token')) {
this.flgLoggedIn = false;
this.logger.warn('Time limit exceeded for session inactivity.');
this.store.dispatch(new RTLActions.CloseAllDialogs());
this.store.dispatch(new RTLActions.OpenAlert({ data: {
@ -123,7 +133,9 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
this.commonService.setContainerSize(this.sideNavContent.elementRef.nativeElement.clientWidth, this.sideNavContent.elementRef.nativeElement.clientHeight);
} else {
setTimeout(() => {
this.renderer.setStyle(this.sideNavContent.elementRef.nativeElement, 'marginLeft', '22rem'); //$regular-sidenav-width
if (this.flgLoggedIn) {
this.renderer.setStyle(this.sideNavContent.elementRef.nativeElement, 'marginLeft', '22rem'); //$regular-sidenav-width
}
this.commonService.setContainerSize(this.sideNavContent.elementRef.nativeElement.clientWidth, this.sideNavContent.elementRef.nativeElement.clientHeight);
}, 100);
}

@ -4,7 +4,13 @@ import { ModuleWithProviders } from '@angular/core';
import { SettingsComponent } from './shared/components/settings/settings.component';
import { AppSettingsComponent } from './shared/components/settings/app-settings/app-settings.component';
import { AuthSettingsComponent } from './shared/components/settings/auth-settings/auth-settings.component';
import { ServerConfigComponent } from './shared/components/settings/server-config/server-config.component';
import { BitcoinConfigComponent } from './shared/components/settings/bitcoin-config/bitcoin-config.component';
import { NodeConfigComponent } from './shared/components/node-config/node-config.component';
import { LNPConfigComponent } from './shared/components/node-config/lnp-config/lnp-config.component';
import { NodeSettingsComponent } from './shared/components/node-config/node-settings/node-settings.component';
import { ServicesSettingsComponent } from './shared/components/node-config/services-settings/services-settings.component';
import { LoopServiceSettingsComponent } from './shared/components/node-config/services-settings/loop-service-settings/loop-service-settings.component';
import { BoltzServiceSettingsComponent } from './shared/components/node-config/services-settings/boltz-service-settings/boltz-service-settings.component';
import { ServicesComponent } from './shared/components/services/services.component';
import { LoopComponent } from './shared/components/services/loop/loop.component';
import { BoltzRootComponent } from './shared/components/services/boltz/boltz-root.component';
@ -15,16 +21,25 @@ import { ErrorComponent } from './shared/components/error/error.component';
import { AuthGuard } from './shared/services/auth.guard';
export const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'lnd' },
{ path: '', pathMatch: 'full', redirectTo: 'login' },
{ 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: 'ecl', loadChildren: () => import('./eclair/ecl.module').then(childModule => childModule.ECLModule), canActivate: [AuthGuard] },
{ path: 'settings', component: SettingsComponent, canActivate: [AuthGuard], children: [
{ path: '', pathMatch: 'full', redirectTo: 'layout' },
{ path: 'layout', component: AppSettingsComponent, canActivate: [AuthGuard] },
{ path: '', pathMatch: 'full', redirectTo: 'app' },
{ path: 'app', component: AppSettingsComponent, canActivate: [AuthGuard] },
{ path: 'auth', component: AuthSettingsComponent, canActivate: [AuthGuard] },
{ path: 'lnconfig', component: ServerConfigComponent, canActivate: [AuthGuard] },
{ path: 'bconfig', component: ServerConfigComponent, canActivate: [AuthGuard] }
{ path: 'bconfig', component: BitcoinConfigComponent, canActivate: [AuthGuard] }
]},
{ path: 'config', component: NodeConfigComponent, canActivate: [AuthGuard], children: [
{ path: '', pathMatch: 'full', redirectTo: 'node' },
{ path: 'node', component: NodeSettingsComponent, canActivate: [AuthGuard] },
{ path: 'services', component: ServicesSettingsComponent, canActivate: [AuthGuard], children: [
{ path: '', pathMatch: 'full', redirectTo: 'loop' },
{ path: 'loop', component: LoopServiceSettingsComponent, canActivate: [AuthGuard] },
{ path: 'boltz', component: BoltzServiceSettingsComponent, canActivate: [AuthGuard] },
]},
{ path: 'lnconfig', component: LNPConfigComponent, canActivate: [AuthGuard] }
]},
{ path: 'services', component: ServicesComponent, canActivate: [AuthGuard], children: [
{ path: '', pathMatch: 'full', redirectTo: 'loop' },

@ -1,2 +1,4 @@
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<router-outlet *ngIf="!loading"></router-outlet>
<div [@routeAnimation]="outlet && outlet.activatedRouteData" class="inner-sidenav-content" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<router-outlet #outlet="outlet"></router-outlet>
</div>

@ -1,10 +1,12 @@
import { Component } from '@angular/core';
import { Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
import { routeAnimation } from '../shared/animation/route-animation';
@Component({
selector: 'rtl-cl-root',
templateUrl: './cl-root.component.html',
styleUrls: ['./cl-root.component.scss']
styleUrls: ['./cl-root.component.scss'],
animations: [routeAnimation]
})
export class CLRootComponent {
loading = false;

@ -116,7 +116,6 @@ export class CLOnChainSendModalComponent implements OnInit, OnDestroy {
this.commonService.isVersionCompatible(this.information.version, '0.9.0')
&& this.commonService.isVersionCompatible(this.information.api_version, '0.4.0');
this.utxos = this.commonService.sortAscByKey(rtlStore.utxos.filter(utxo => utxo.status === 'confirmed'), 'value');
console.warn(this.utxos);
this.logger.info(rootStore);
this.logger.info(rtlStore);
});

@ -1,2 +1,4 @@
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<router-outlet *ngIf="!loading"></router-outlet>
<div [@routeAnimation]="outlet && outlet.activatedRouteData" class="inner-sidenav-content" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<router-outlet #outlet="outlet"></router-outlet>
</div>

@ -1,10 +1,12 @@
import { Component } from '@angular/core';
import { Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
import { routeAnimation } from '../shared/animation/route-animation';
@Component({
selector: 'rtl-ecl-root',
templateUrl: './ecl-root.component.html',
styleUrls: ['./ecl-root.component.scss']
styleUrls: ['./ecl-root.component.scss'],
animations: [routeAnimation]
})
export class ECLRootComponent {
loading = false;

@ -5,9 +5,6 @@ import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faUsers, faChartPie } from '@fortawesome/free-solid-svg-icons';
import { GetInfo, Peer } from '../../shared/models/eclModels';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as fromRTLReducer from '../../store/rtl.reducers';
@Component({

@ -2,12 +2,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ResolveEnd } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faMapSigns } from '@fortawesome/free-solid-svg-icons';
import { LoggerService } from '../../shared/services/logger.service';
import * as fromRTLReducer from '../../store/rtl.reducers';
@Component({
selector: 'rtl-ecl-routing',

@ -1,2 +1,4 @@
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<router-outlet *ngIf="!loading"></router-outlet>
<div [@routeAnimation]="outlet && outlet.activatedRouteData" class="inner-sidenav-content" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<router-outlet #outlet="outlet"></router-outlet>
</div>

@ -1,10 +1,12 @@
import { Component } from '@angular/core';
import { Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
import { routeAnimation } from '../shared/animation/route-animation';
@Component({
selector: 'rtl-lnd-root',
templateUrl: './lnd-root.component.html',
styleUrls: ['./lnd-root.component.scss']
styleUrls: ['./lnd-root.component.scss'],
animations: [routeAnimation]
})
export class LNDRootComponent {
loading = false;

@ -64,7 +64,6 @@ export class OnChainTransactionHistoryComponent implements OnChanges {
}
onTransactionClick(selTransaction: Transaction) {
console.warn(selTransaction);
const reorderedTransactions = [
[{key: 'block_hash', value: selTransaction.block_hash, title: 'Block Hash', width: 100}],
[{key: 'tx_hash', value: selTransaction.tx_hash, title: 'Transaction Hash', width: 100}],

@ -1,5 +1,7 @@
import { Component, ViewChild, Input, OnChanges, OnDestroy } from '@angular/core';
import { DecimalPipe } from '@angular/common';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faMoneyBillWave } from '@fortawesome/free-solid-svg-icons';
@ -15,6 +17,7 @@ import { OnChainLabelModalComponent } from '../../on-chain-label-modal/on-chain-
import * as RTLActions from '../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
import { RTLEffects } from '../../../../store/rtl.effects';
@Component({
selector: 'rtl-on-chain-utxos',
@ -42,7 +45,7 @@ export class OnChainUTXOsComponent implements OnChanges, OnDestroy {
public screenSizeEnum = ScreenSizeEnum;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private dataService: DataService, private store: Store<fromRTLReducer.RTLState>) {
constructor(private logger: LoggerService, private commonService: CommonService, private dataService: DataService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private decimalPipe: DecimalPipe) {
this.screenSize = this.commonService.getScreenSize();
if(this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
@ -116,7 +119,28 @@ export class OnChainUTXOsComponent implements OnChanges, OnDestroy {
}
onLeaseUTXO(utxo: UTXO) {
this.dataService.leaseUTXO(utxo.outpoint.txid_bytes, utxo.outpoint.output_index);
const utxoDetails = [
[{key: 'txid_str', value: utxo.outpoint.txid_str, title: 'Transaction ID', width: 100}],
[{key: 'amount_sat', value: this.decimalPipe.transform(utxo.amount_sat), title: 'Amount (Sats)', width: 100}]
];
if (utxo.label) {
utxoDetails.splice(1, 0, [{key: 'label', value: utxo.label, title: 'Label', width: 100}]);
}
this.store.dispatch(new RTLActions.OpenConfirmation({ data: {
type: AlertTypeEnum.CONFIRM,
alertTitle: 'Lease UTXO',
informationMessage: 'The UTXO will be leased for 10 minutes.',
message: utxoDetails,
noBtnText: 'Cancel',
yesBtnText: 'Lease UTXO'
}}));
this.rtlEffects.closeConfirm
.pipe(takeUntil(this.unSubs[0]))
.subscribe(confirmRes => {
if (confirmRes) {
this.dataService.leaseUTXO(utxo.outpoint.txid_bytes, utxo.outpoint.output_index);
}
});
}
onDownloadCSV() {

@ -20,7 +20,6 @@ import { RTLEffects } from '../../../store/rtl.effects';
import * as LNDActions from '../../store/lnd.actions';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
import { isNumber } from 'util';
@Component({
selector: 'rtl-peers',

@ -0,0 +1,17 @@
import { transition, trigger, query, style, animate, group } from '@angular/animations';
export const routeAnimation = trigger('routeAnimation', [
transition('* => *', [
query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
group([
query(':enter', [
style({ transform: 'translateX(100%)' }),
animate('1000ms ease-in-out', style({ transform: 'translateX(0%)' }))
], { optional: true }),
query(':leave', [
style({ transform: 'translateX(0%)' }),
animate('1000ms ease-in-out', style({ transform: 'translateX(-100%)' }))
], { optional: true }),
])
])
]);

@ -8,6 +8,14 @@
</mat-card-header>
<mat-card-content class="mt-5px">
<form fxLayout="column">
<div *ngIf="warningMessage && warningMessage != ''" fxFlex="100" class="alert alert-warn">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span>{{warningMessage}}</span>
</div>
<div *ngIf="informationMessage && informationMessage != ''" fxFlex="100" class="alert alert-info">
<fa-icon [icon]="faInfoCircle" class="mr-1 alert-icon"></fa-icon>
<span>{{informationMessage}}</span>
</div>
<p *ngIf="data.titleMessage && !flgShowInput" fxLayoutAlign="start center" class="pb-1">{{data.titleMessage}}</p>
<div *ngIf="messageObjs?.length>0">
<div *ngFor="let objs of messageObjs; index as i;">

@ -1,6 +1,7 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { faInfoCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { LoggerService } from '../../../services/logger.service';
import { InputData, ConfirmationData } from '../../../models/alertData';
@ -15,6 +16,10 @@ import * as fromRTLReducer from '../../../../store/rtl.reducers';
styleUrls: ['./confirmation-message.component.scss']
})
export class ConfirmationMessageComponent implements OnInit {
public faInfoCircle = faInfoCircle;
public faExclamationTriangle = faExclamationTriangle;
public informationMessage = '';
public warningMessage = '';
public noBtnText = 'No';
public yesBtnText = 'Yes';
public messageObjs = [];
@ -27,6 +32,8 @@ export class ConfirmationMessageComponent implements OnInit {
private store: Store<fromRTLReducer.RTLState>) { }
ngOnInit() {
this.informationMessage = this.data.informationMessage;
this.warningMessage = this.data.warningMessage;
this.flgShowInput = this.data.flgShowInput;
this.getInputs = this.data.getInputs;
this.noBtnText = (this.data.noBtnText) ? this.data.noBtnText : 'No';

@ -1,23 +1,35 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon [icon]="faUnlockAlt" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Login to RTL</span>
</div>
<div fxLayout="column">
<div class="padding-gap">
<mat-card>
<mat-card-content class="card-content-gap">
<form (ngSubmit)="onLogin()" #loginForm="ngForm" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign="start" fxLayoutAlign.gt-sm="space-between">
<mat-form-field fxFlex="100" fxLayoutAlign="start">
<input autoFocus matInput placeholder="Password" type="password" id="password" name="password" [(ngModel)]="password" tabindex="1" required>
<mat-error *ngIf="!password">Password is required.</mat-error>
</mat-form-field>
<p *ngIf="loginErrorMessage !== ''" fxFlex="100" class="color-warn" fxLayoutAlign="start center"><mat-icon class="mr-1 icon-small">close</mat-icon>{{loginErrorMessage}}</p>
<div fxLayout="row" class="mt-2">
<button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear</button>
<button mat-flat-button color="primary" tabindex="3" type="submit">Login</button>
</div>
</form>
</mat-card-content>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="center stretch" class="login-container">
<div fxLayout="row" fxFlex="50" fxLayoutAlign="center stretch">
<mat-card fxLayout="row" fxFlex="50" fxLayoutAlign="center stretch">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="stretch stretch">
<div fxFlex="35" fxLayoutAlign="center center" class="bg-primary">
<img src="assets/images/RTL-Horse-BY.svg" alt="RTL Logo" class="rtl-logo-svg">
</div>
<div fxFlex="65" fxLayout="column" fxLayoutAlign="center stretch" class="padding-gap-large pl-3">
<mat-card-header fxLayout="row" fxLayoutAlign="center center" class="page-title-container p-0">
<mat-card-title class="font-size-300 font-bold-500">
<span class="page-title">Welcome</span>
</mat-card-title>
</mat-card-header>
<mat-card-content class="mt-5px mb-0 pr-2">
<form #loginForm="ngForm" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign="start" fxLayoutAlign.gt-sm="space-between">
<mat-form-field fxFlex="100" fxLayoutAlign="start">
<input autoFocus matInput placeholder="Password" [type]="flgShow ? 'text' : 'password'" id="password" name="password" [(ngModel)]="password" tabindex="1" required>
<mat-icon tabindex="2" matSuffix (click)="flgShow = !flgShow">{{flgShow ? 'visibility_off' : 'visibility'}}</mat-icon>
<mat-error *ngIf="!password">Password is required.</mat-error>
</mat-form-field>
<p *ngIf="loginErrorMessage !== ''" fxFlex="100" class="color-warn pre-wrap" fxLayoutAlign="start start">
<mat-icon class="mr-1 icon-small">close</mat-icon>
{{loginErrorMessage}}
</p>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="end center" class="mt-2">
<button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear</button>
<button mat-flat-button color="primary" tabindex="3" type="submit" (click)="onLogin()">Login</button>
</div>
</form>
</mat-card-content>
</div>
</div>
</mat-card>
</div>
</div>

@ -0,0 +1,17 @@
.login-container {
height: 90vh;
& .mat-card {
height: 30rem;
}
& .rtl-logo-svg {
width: 100%;
}
& .material-icons.mat-icon {
font-size: 120%;
cursor: pointer;
}
}

@ -27,6 +27,7 @@ export class LoginComponent implements OnInit, OnDestroy {
public rtlCookiePath = '';
public accessKey = '';
public loginErrorMessage = '';
public flgShow = false;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects) { }
@ -69,6 +70,7 @@ export class LoginComponent implements OnInit, OnDestroy {
resetData() {
this.password = '';
this.loginErrorMessage = '';
this.flgShow = false;
}
ngOnDestroy() {

@ -3,7 +3,11 @@
<fa-icon [icon]="faCodeBranch" class="fa-icon-small mr-1"></fa-icon>
<span>Version: {{version}}</span>
</p>
<a mat-menu-item routerLink="/help">
<a *ngIf="showLogout" mat-menu-item routerLink="/settings">
<fa-icon [icon]="faUserCog" class="fa-icon-small mr-1"></fa-icon>
<span routerLink="/settings">Settings</span>
</a>
<a *ngIf="showLogout" mat-menu-item routerLink="/help">
<fa-icon [icon]="faLifeRing" class="fa-icon-small mr-1"></fa-icon>
<span routerLink="/help">Help</span>
</a>

@ -1,26 +1,12 @@
.mat-menu-panel.top-menu{
.mat-toolbar, .mat-toolbar-row{
height: 100px !important;
padding: 0 16px !important;
}
.info-block{
width: 230px;
p{
font-size: 16px;
line-height: 22px;
text-align: center;
}
}
.mat-menu-item{
height: 36px;
line-height: 36px;
}
.mat-menu-content {
p{
.mat-menu-content {
cursor: default;
p.mat-menu-item {
cursor: default;
fa-icon, span, div {
cursor: default;
mat-icon, span, div {
cursor: default;
}
}
}
p.mat-menu-item:hover {
cursor: default !important;
}
}

@ -1,28 +1,30 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faCodeBranch, faCog, faLifeRing, faEject, faUserCog } from '@fortawesome/free-solid-svg-icons';
import { GetInfoRoot, ConfigSettingsNode } from '../../../models/RTLconfig';
import { LoggerService } from '../../../services/logger.service';
import { SessionService } from '../../../services/session.service';
import { GetInfoChain } from '../../../models/lndModels';
import { environment } from '../../../../../environments/environment';
import { AlertTypeEnum } from '../../../services/consts-enums-functions';
import { RTLEffects } from '../../../../store/rtl.effects';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
import * as RTLActions from '../../../../store/rtl.actions';
import { faCodeBranch, faCog, faLifeRing, faEject } from '@fortawesome/free-solid-svg-icons';
import { AlertTypeEnum } from '../../../services/consts-enums-functions';
@Component({
selector: 'rtl-top-menu',
templateUrl: './top-menu.component.html',
styleUrls: ['./top-menu.component.scss']
styleUrls: ['./top-menu.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class TopMenuComponent implements OnInit, OnDestroy {
public selNode: ConfigSettingsNode;
public faUserCog = faUserCog;
public faCodeBranch = faCodeBranch;
public faCog = faCog;
public faLifeRing = faLifeRing;

@ -10,11 +10,11 @@ import * as RTLActions from '../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
@Component({
selector: 'rtl-server-config',
templateUrl: './server-config.component.html',
styleUrls: ['./server-config.component.scss']
selector: 'rtl-lnp-config',
templateUrl: './lnp-config.component.html',
styleUrls: ['./lnp-config.component.scss']
})
export class ServerConfigComponent implements OnInit, OnDestroy {
export class LNPConfigComponent implements OnInit, OnDestroy {
public selectedNodeType = '';
public configData = '';
public fileFormat = 'INI';

@ -0,0 +1,19 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon [icon]="faTools" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Node Config</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<mat-card>
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar>
<div role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[0].link" (click)="activeLink = links[0].link" routerLink="{{links[0].link}}">{{links[0].name}}</div>
<div role="tab" mat-tab-link *ngIf="selNode.lnImplementation.toUpperCase() === 'LND'" class="mat-tab-label" [active]="activeLink === links[1].link" (click)="activeLink = links[1].link" routerLink="{{links[1].link}}" [state]="{ initial: false }">{{links[1].name}}</div>
<div role="tab" mat-tab-link *ngIf="showLnConfig" class="mat-tab-label" [active]="activeLink === links[2].link" (click)="activeLink = links[2].link" routerLink="{{links[2].link}}">{{links[2].name}}</div>
</nav>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">
<router-outlet></router-outlet>
</div>
</mat-card-content>
</mat-card>
</div>

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

@ -0,0 +1,67 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ResolveEnd } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faTools } from '@fortawesome/free-solid-svg-icons';
import { ConfigSettingsNode, RTLConfiguration } from '../../models/RTLconfig';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@Component({
selector: 'rtl-node-config',
templateUrl: './node-config.component.html',
styleUrls: ['./node-config.component.scss']
})
export class NodeConfigComponent implements OnInit, OnDestroy{
public faTools = faTools;
public showLnConfig = false;
public selNode: ConfigSettingsNode;
public appConfig: RTLConfiguration;
public lnImplementationStr = '';
public links = [{link: 'node', name: 'Node'}, {link: 'services', name: 'Services'}, {link: 'lnconfig', name: this.lnImplementationStr}];
public activeLink = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(private store: Store<fromRTLReducer.RTLState>, private router: Router) {}
ngOnInit() {
let linkFound = this.links.find(link => this.router.url.includes(link.link));
this.activeLink = linkFound ? linkFound.link : this.links[0].link;
this.router.events.pipe(takeUntil(this.unSubs[0]), filter(e => e instanceof ResolveEnd))
.subscribe((value: ResolveEnd) => {
let linkFound = this.links.find(link => value.urlAfterRedirects.includes(link.link));
this.activeLink = linkFound ? linkFound.link : this.links[0].link;
});
this.store.select('root').pipe(takeUntil(this.unSubs[1]))
.subscribe((rtlStore) => {
this.showLnConfig = false;
this.appConfig = rtlStore.appConfig;
this.selNode = rtlStore.selNode;
switch (this.selNode.lnImplementation.toUpperCase()) {
case 'CLT':
this.lnImplementationStr = 'C-Lightning Config';
break;
case 'ECL':
this.lnImplementationStr = 'Eclair Config';
break;
default:
this.lnImplementationStr = 'LND Config';
break;
}
if (this.selNode.authentication && this.selNode.authentication.configPath && this.selNode.authentication.configPath.trim() !== '') {
this.links[2].name = this.lnImplementationStr;
this.showLnConfig = true;
}
});
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -0,0 +1,75 @@
<div [perfectScrollbar] fxLayout="column" fxFlex="100">
<form fxLayout="column" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
<div fxLayout="row">
<fa-icon [icon]="faMoneyBillAlt" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Balance Display</span>
</div>
<div fxLayout="column" fxLayoutAlign="start stretch" class="mt-1 bordered-box padding-gap-large">
<div fxFlex="100" class="alert alert-warn">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span>Fiat conversion calls <strong><a href="https://www.blockchain.com/api/exchange_rates_api" target="blank">Blockchain.com</a></strong> API to get conversion rates.</span>
</div>
<div fxLayout="row wrap" fxLayoutAlign="start center">
<mat-slide-toggle tabindex="2" color="primary" [(ngModel)]="selNode.settings.fiatConversion" (change)="selNode.settings.currencyUnit = !$event.checked ? null : selNode.settings.currencyUnit" name="fiatConversion">Enable Fiat Conversion</mat-slide-toggle>
<mat-form-field>
<mat-select autoFocus [(ngModel)]="selNode.settings.currencyUnit" (selectionChange)="onCurrencyChange($event)" placeholder="Fiat Currency" [disabled]="!selNode.settings.fiatConversion" tabindex="3" [required]="selNode.settings.fiatConversion" name="currencyUnit" #currencyUnit="ngModel">
<mat-option *ngFor="let currencyUnit of currencyUnits" [value]="currencyUnit.id">
{{currencyUnit.id}}
</mat-option>
</mat-select>
<mat-error *ngIf="selNode.settings.fiatConversion && !selNode.settings.currencyUnit">Currency unit is required.</mat-error>
</mat-form-field>
</div>
</div>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<div fxLayout="row wrap" fxLayoutAlign="start start" fxLayout.gt-sm="column" fxFlex="100" fxLayoutAlign.gt-sm="space-between stretch" class="settings-container page-sub-title-container mt-1">
<div class="mt-1">
<fa-icon [icon]="faPaintBrush" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Customization</span>
</div>
<div fxLayout="column" fxLayoutAlign="start stretch" class="mt-1 bordered-box padding-gap-large">
<div fxLayout="column" fxLayoutAlign="start stretch" fxFlex="100">
<div fxLayout="row" fxFlex="100" class="alert alert-info mb-0">
<fa-icon [icon]="faInfoCircle" class="mr-1 alert-icon"></fa-icon>
<span>Dashboard layout will be tailored based on the role selected to better serve its needs.</span>
</div>
<div fxLayout="column" fxLayoutAlign="start start" fxFlex="100">
<h4>Dashboard Layout</h4>
<mat-radio-group color="primary" [(ngModel)]="selNode.settings.userPersona" tabindex="1" name="userPersona">
<mat-radio-button *ngFor="let userPersona of userPersonas" [value]="userPersona" [checked]="selNode.settings.userPersona === userPersona" class="mr-4">
{{userPersona | titlecase}}
</mat-radio-button>
</mat-radio-group>
</div>
</div>
<mat-divider [inset]="true" class="mt-1"></mat-divider>
<div fxLayout="column" fxLayout.gt-xs="row" fxFlex="100" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-xs="start stretch">
<div fxFlex.gt-xs="20" fxFlex.gt-md="15" fxLayout="column" fxLayoutAlign="space-between stretch">
<h4>Mode</h4>
<mat-radio-group color="primary" [(ngModel)]="selectedThemeMode" (change)="chooseThemeMode()" name="themeMode">
<mat-radio-button tabindex="5" *ngFor="let themeMode of themeModes" [value]="themeMode" [ngClass]="{'mr-4': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">{{themeMode.name}}
</mat-radio-button>
</mat-radio-group>
</div>
</div>
<mat-divider [inset]="true" class="mt-1"></mat-divider>
<div fxLayout="column" fxLayout.gt-xs="row" fxFlex="100" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-xs="start stretch">
<div fxLayout="column" fxFlex.gt-xs="50" fxFlex.gt-md="40" fxLayoutAlign="space-between stretch">
<h4>Themes</h4>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start">
<span *ngFor="let themeColor of themeColors" fxLayout="row">
<div tabindex="9" [class]="themeColor.id | lowercase" [ngClass]="{'skin': true, 'selected-color': selectedThemeColor === themeColor.id}" (click)="changeThemeColor(themeColor.id)"></div>
{{themeColor.name}}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
<div fxLayout="row" class="mt-1">
<button class="mr-1" mat-stroked-button color="primary" (click)="onResetSettings()" tabindex="10">Reset</button>
<button mat-flat-button color="primary" (click)="onUpdateSettings()" tabindex="11">Update</button>
</div>
</div>

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

@ -0,0 +1,112 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faMoneyBillAlt, faPaintBrush, faInfoCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { CURRENCY_UNITS, UserPersonaEnum, ScreenSizeEnum, FIAT_CURRENCY_UNITS, NODE_SETTINGS } from '../../../services/consts-enums-functions';
import { ConfigSettingsNode, Settings, RTLConfiguration, GetInfoRoot } from '../../../models/RTLconfig';
import { LoggerService } from '../../../services/logger.service';
import { CommonService } from '../../../services/common.service';
import * as ECLActions from '../../../../eclair/store/ecl.actions';
import * as CLActions from '../../../../clightning/store/cl.actions';
import * as LNDActions from '../../../../lnd/store/lnd.actions';
import * as RTLActions from '../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
@Component({
selector: 'rtl-node-settings',
templateUrl: './node-settings.component.html',
styleUrls: ['./node-settings.component.scss']
})
export class NodeSettingsComponent implements OnInit, OnDestroy {
public faExclamationTriangle = faExclamationTriangle;
public faMoneyBillAlt = faMoneyBillAlt;
public faPaintBrush = faPaintBrush;
public faInfoCircle = faInfoCircle;
public selNode: ConfigSettingsNode;
public information: GetInfoRoot = {};
public userPersonas = [UserPersonaEnum.OPERATOR, UserPersonaEnum.MERCHANT];
public currencyUnits = FIAT_CURRENCY_UNITS;
public themeModes = NODE_SETTINGS.modes;
public themeColors = NODE_SETTINGS.themes;
public selectedThemeMode = NODE_SETTINGS.modes[0];
public selectedThemeColor = NODE_SETTINGS.themes[0].id;
public currencyUnit = 'BTC';
public smallerCurrencyUnit = 'Sats';
public showSettingOption = true;
public appConfig: RTLConfiguration;
public previousSettings: Settings;
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<fromRTLReducer.RTLState>) {
this.screenSize = this.commonService.getScreenSize();
}
ngOnInit() {
this.store.select('root')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
this.appConfig = rtlStore.appConfig;
this.selNode = rtlStore.selNode;
this.selectedThemeMode = this.themeModes.find(themeMode => this.selNode.settings.themeMode === themeMode.id);
this.selectedThemeColor = this.selNode.settings.themeColor;
this.information = rtlStore.nodeData;
this.smallerCurrencyUnit = ( this.information && this.information.smaller_currency_unit) ? this.information.smaller_currency_unit : 'Sats';
this.currencyUnit = ( this.information && this.information.currency_unit) ? this.information.currency_unit : 'BTC';
if(!this.selNode.settings.fiatConversion) {
this.selNode.settings.currencyUnit = null;
}
this.previousSettings = JSON.parse(JSON.stringify(this.selNode.settings));
this.logger.info(rtlStore);
});
}
onCurrencyChange(event: any) {
this.selNode.settings.currencyUnits = [...CURRENCY_UNITS, event.value];
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
}
toggleSettings(toggleField: string, event?: any) {
this.selNode.settings[toggleField] = !this.selNode.settings[toggleField];
}
changeThemeColor(newThemeColor: string) {
this.selectedThemeColor = newThemeColor;
this.selNode.settings.themeColor = newThemeColor;
}
chooseThemeMode() {
this.selNode.settings.themeMode = this.selectedThemeMode.id;
}
onUpdateSettings():boolean|void {
if(this.selNode.settings.fiatConversion && !this.selNode.settings.currencyUnit) { return true; }
this.logger.info(this.selNode.settings);
this.store.dispatch(new RTLActions.OpenSpinner('Updating Node Settings...'));
this.store.dispatch(new RTLActions.SaveSettings({settings: this.selNode.settings}));
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
}
onResetSettings() {
this.selNode.settings = this.previousSettings;
this.selectedThemeMode = this.themeModes.find(themeMode => themeMode.id === this.previousSettings.themeMode);
this.selectedThemeColor = this.previousSettings.themeColor;
this.store.dispatch(new RTLActions.SetSelelectedNode({ lnNode: this.selNode, isInitialSetup: true }));
}
ngOnDestroy() {
this.unSubs.forEach(unsub => {
unsub.next();
unsub.complete();
});
}
}

@ -0,0 +1,21 @@
<div [perfectScrollbar] fxLayout="column" fxFlex="100">
<form fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
<div fxLayout="column" fxFlex="50" fxLayoutAlign="start stretch">
<mat-slide-toggle autoFocus class="mb-1" tabindex="1" color="primary" [(ngModel)]="enableBoltz" (change)="onEnableServiceChanged($event)" name="boltz">Enable Boltz Service</mat-slide-toggle>
<mat-form-field class="mb-1">
<input matInput placeholder="Boltz Server URL" type="text" id="boltzServerUrl" name="boltzServerUrl" [(ngModel)]="selNode.settings.boltzServerUrl" tabindex="2" [required]="enableBoltz" [disabled]="!enableBoltz">
<mat-hint>Service url for boltz server REST APIs, eg. https://localhost:9003</mat-hint>
<mat-error *ngIf="!selNode.settings.boltzServerUrl && enableBoltz">Boltz server URL is required.</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Boltz Macaroon Path" type="text" id="boltzMacaroonPath" name="boltzMacaroonPath" [(ngModel)]="selNode.authentication.boltzMacaroonPath" tabindex="3" [required]="enableBoltz" [disabled]="!enableBoltz">
<mat-hint>Path for the folder containing boltz 'admin.macaroon', eg. D:\\xyz\\AppData\\Boltz\\testnet</mat-hint>
<mat-error *ngIf="!selNode.authentication.boltzMacaroonPath && enableBoltz">Boltz macaroon path is required.</mat-error>
</mat-form-field>
</div>
</form>
<div fxLayout="row" class="mt-2">
<button class="mr-1" mat-stroked-button color="primary" type="reset" (click)="onReset()" tabindex="4">Reset</button>
<button mat-flat-button color="primary" type="submit" (click)="onUpdateService()" tabindex="5">Update</button>
</div>
</div>

@ -0,0 +1,72 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { ServicesEnum } from '../../../../services/consts-enums-functions';
import { ConfigSettingsNode, RTLConfiguration } from '../../../../models/RTLconfig';
import { LoggerService } from '../../../../services/logger.service';
import * as ECLActions from '../../../../../eclair/store/ecl.actions';
import * as CLActions from '../../../../../clightning/store/cl.actions';
import * as LNDActions from '../../../../../lnd/store/lnd.actions';
import * as RTLActions from '../../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../../store/rtl.reducers';
@Component({
selector: 'rtl-boltz-service-settings',
templateUrl: './boltz-service-settings.component.html',
styleUrls: ['./boltz-service-settings.component.scss']
})
export class BoltzServiceSettingsComponent implements OnInit, OnDestroy {
public appConfig: RTLConfiguration;
public selNode: ConfigSettingsNode;
public previousSelNode: ConfigSettingsNode;
public enableBoltz = false;
unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>) {}
ngOnInit() {
this.store.select('root')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
this.appConfig = rtlStore.appConfig;
this.selNode = rtlStore.selNode;
this.enableBoltz = rtlStore.selNode.settings.boltzServerUrl && rtlStore.selNode.settings.boltzServerUrl.trim() !== '';
this.previousSelNode = JSON.parse(JSON.stringify(this.selNode));
this.logger.info(rtlStore);
});
}
onEnableServiceChanged(event) {
this.enableBoltz = event.checked;
if (!this.enableBoltz) {
this.selNode.authentication.boltzMacaroonPath = '';
this.selNode.settings.boltzServerUrl = '';
}
}
onUpdateService():boolean|void {
if(this.enableBoltz && (!this.selNode.settings.boltzServerUrl || this.selNode.settings.boltzServerUrl.trim() === '' || !this.selNode.authentication.boltzMacaroonPath || this.selNode.authentication.boltzMacaroonPath.trim() === '')) { return true; }
this.logger.info(this.selNode);
this.store.dispatch(new RTLActions.OpenSpinner('Updating Boltz Service Settings...'));
this.store.dispatch(new RTLActions.UpdateServiceSettings({service: ServicesEnum.BOLTZ, settings: { enable: this.enableBoltz, serverUrl: this.selNode.settings.boltzServerUrl, macaroonPath: this.selNode.authentication.boltzMacaroonPath }}));
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
}
onReset() {
this.selNode = JSON.parse(JSON.stringify(this.previousSelNode));
this.enableBoltz = this.selNode.settings.boltzServerUrl && this.selNode.settings.boltzServerUrl.trim() !== '';
}
ngOnDestroy() {
this.unSubs.forEach(unsub => {
unsub.next();
unsub.complete();
});
}
}

@ -0,0 +1,21 @@
<div [perfectScrollbar] fxLayout="column" fxFlex="100">
<form fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
<div fxLayout="column" fxFlex="50" fxLayoutAlign="start stretch">
<mat-slide-toggle autoFocus class="mb-1" tabindex="1" color="primary" [(ngModel)]="enableLoop" (change)="onEnableServiceChanged($event)" name="loop">Enable Loop Service</mat-slide-toggle>
<mat-form-field class="mb-1">
<input matInput placeholder="Loop Server URL" type="text" id="swapServerUrl" name="swapServerUrl" [(ngModel)]="selNode.settings.swapServerUrl" tabindex="2" [required]="enableLoop" [disabled]="!enableLoop">
<mat-hint>Service url for loop server REST APIs, eg. https://localhost:8081</mat-hint>
<mat-error *ngIf="!selNode.settings.swapServerUrl && enableLoop">Loop server URL is required.</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Loop Macaroon Path" type="text" id="swapMacaroonPath" name="swapMacaroonPath" [(ngModel)]="selNode.authentication.swapMacaroonPath" tabindex="3" [required]="enableLoop" [disabled]="!enableLoop">
<mat-hint>Path for the folder containing service 'loop.macaroon', eg. D:\\xyz\\AppData\\Local\\Loop\\testnet</mat-hint>
<mat-error *ngIf="!selNode.authentication.swapMacaroonPath && enableLoop">Loop macaroon path is required.</mat-error>
</mat-form-field>
</div>
</form>
<div fxLayout="row" class="mt-2">
<button class="mr-1" mat-stroked-button color="primary" type="reset" (click)="onReset()" tabindex="4">Reset</button>
<button mat-flat-button color="primary" type="submit" (click)="onUpdateService()" tabindex="5">Update</button>
</div>
</div>

@ -0,0 +1,72 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { ServicesEnum } from '../../../../services/consts-enums-functions';
import { ConfigSettingsNode, RTLConfiguration } from '../../../../models/RTLconfig';
import { LoggerService } from '../../../../services/logger.service';
import * as ECLActions from '../../../../../eclair/store/ecl.actions';
import * as CLActions from '../../../../../clightning/store/cl.actions';
import * as LNDActions from '../../../../../lnd/store/lnd.actions';
import * as RTLActions from '../../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../../store/rtl.reducers';
@Component({
selector: 'rtl-loop-service-settings',
templateUrl: './loop-service-settings.component.html',
styleUrls: ['./loop-service-settings.component.scss']
})
export class LoopServiceSettingsComponent implements OnInit, OnDestroy {
public appConfig: RTLConfiguration;
public selNode: ConfigSettingsNode;
public previousSelNode: ConfigSettingsNode;
public enableLoop = false;
unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>) {}
ngOnInit() {
this.store.select('root')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
this.appConfig = rtlStore.appConfig;
this.selNode = rtlStore.selNode;
this.enableLoop = rtlStore.selNode.settings.swapServerUrl && rtlStore.selNode.settings.swapServerUrl.trim() !== '';
this.previousSelNode = JSON.parse(JSON.stringify(this.selNode));
this.logger.info(rtlStore);
});
}
onEnableServiceChanged(event) {
this.enableLoop = event.checked;
if (!this.enableLoop) {
this.selNode.authentication.swapMacaroonPath = '';
this.selNode.settings.swapServerUrl = '';
}
}
onUpdateService():boolean|void {
if(this.enableLoop && (!this.selNode.settings.swapServerUrl || this.selNode.settings.swapServerUrl.trim() === '' || !this.selNode.authentication.swapMacaroonPath || this.selNode.authentication.swapMacaroonPath.trim() === '')) { return true; }
this.logger.info(this.selNode);
this.store.dispatch(new RTLActions.OpenSpinner('Updating Loop Service Settings...'));
this.store.dispatch(new RTLActions.UpdateServiceSettings({service: ServicesEnum.LOOP, settings: { enable: this.enableLoop, serverUrl: this.selNode.settings.swapServerUrl, macaroonPath: this.selNode.authentication.swapMacaroonPath }}));
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
}
onReset() {
this.selNode = JSON.parse(JSON.stringify(this.previousSelNode));
this.enableLoop = this.selNode.settings.swapServerUrl && this.selNode.settings.swapServerUrl.trim() !== '';
}
ngOnDestroy() {
this.unSubs.forEach(unsub => {
unsub.next();
unsub.complete();
});
}
}

@ -0,0 +1,17 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container mt-1">
<fa-icon [icon]="faLayerGroup" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Services</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<mat-card>
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar>
<div role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[0].link" (click)="activeLink = links[0].link" routerLink="{{links[0].link}}">{{links[0].name}}</div>
<div role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[1].link" (click)="activeLink = links[1].link" routerLink="{{links[1].link}}" [state]="{ initial: false }">{{links[1].name}}</div>
</nav>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">
<router-outlet></router-outlet>
</div>
</mat-card-content>
</mat-card>
</div>

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

@ -0,0 +1,49 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ResolveEnd } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faLayerGroup } from '@fortawesome/free-solid-svg-icons';
import { ConfigSettingsNode, RTLConfiguration } from '../../../models/RTLconfig';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
@Component({
selector: 'rtl-services-settings',
templateUrl: './services-settings.component.html',
styleUrls: ['./services-settings.component.scss']
})
export class ServicesSettingsComponent implements OnInit, OnDestroy {
public faLayerGroup = faLayerGroup;
public selNode: ConfigSettingsNode;
public appConfig: RTLConfiguration;
public links = [{link: 'loop', name: 'Loop'}, {link: 'boltz', name: 'Boltz'}];
public activeLink = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(private store: Store<fromRTLReducer.RTLState>, private router: Router) {}
ngOnInit() {
let linkFound = this.links.find(link => this.router.url.includes(link.link));
this.activeLink = linkFound ? linkFound.link : this.links[0].link;
this.router.events.pipe(takeUntil(this.unSubs[0]), filter(e => e instanceof ResolveEnd))
.subscribe((value: ResolveEnd) => {
let linkFound = this.links.find(link => value.urlAfterRedirects.includes(link.link));
this.activeLink = linkFound ? linkFound.link : this.links[0].link;
});
this.store.select('root').pipe(takeUntil(this.unSubs[1]))
.subscribe((rtlStore) => {
this.appConfig = rtlStore.appConfig;
this.selNode = rtlStore.selNode;
});
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -53,7 +53,6 @@ export class LoopComponent implements OnInit {
this.storedSwaps = swaps;
this.filteredSwaps = this.storedSwaps.filter(swap => swap.type === this.selectedSwapType);
}, (err) => {
console.warn('HERE');
this.flgLoading[0] = 'error';
this.emptyTableMessage = err.message ? err.message : 'No loop ' + ((this.selectedSwapType === LoopTypeEnum.LOOP_IN) ? 'in' : 'out') + ' available.';
});

@ -1,14 +1 @@
<!-- <div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon [icon]="faLayerGroup" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Services</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<mat-card>
<mat-card-content fxLayout="column">
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">
<router-outlet></router-outlet>
</div>
</mat-card-content>
</mat-card>
</div> -->
<router-outlet></router-outlet>

@ -1,84 +1,37 @@
<div [perfectScrollbar] fxLayout="column" fxFlex="100">
<form fxLayout="column" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
<div [perfectScrollbar] fxLayout="column" fxFlex="100" class="padding-gap-x-large">
<form *ngIf="appConfig.nodes && appConfig.nodes.length && appConfig.nodes.length > 1" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
<div fxLayout="row">
<fa-icon [icon]="faTools" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Balance Display</span>
<fa-icon [icon]="faWindowRestore" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Default Node</span>
</div>
<div fxLayout="column" fxLayoutAlign="start stretch" class="mt-1 bordered-box padding-gap-large">
<div fxFlex="100" class="alert alert-warn">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span>Fiat conversion calls <strong><a href="https://www.blockchain.com/api/exchange_rates_api" target="blank">Blockchain.com</a></strong> API to get conversion rates.</span>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="my-2">
<div fxLayout="row" fxFlex="100" class="alert alert-info">
<fa-icon [icon]="faInfoCircle" class="mr-1 alert-icon"></fa-icon>
<span>The setting will apply after RTL server restart.</span>
</div>
<div fxLayout="row wrap" fxLayoutAlign="start center">
<mat-slide-toggle tabindex="2" color="primary" [(ngModel)]="selNode.settings.fiatConversion" (change)="selNode.settings.currencyUnit = !$event.checked ? null : selNode.settings.currencyUnit" name="fiatConversion">Include Fiat Conversion</mat-slide-toggle>
<mat-form-field>
<mat-select autoFocus [(ngModel)]="selNode.settings.currencyUnit" (selectionChange)="onCurrencyChange($event)" placeholder="Fiat Currency" [disabled]="!selNode.settings.fiatConversion" tabindex="3" [required]="selNode.settings.fiatConversion" name="currencyUnit" #currencyUnit="ngModel">
<mat-option *ngFor="let currencyUnit of currencyUnits" [value]="currencyUnit.id">
{{currencyUnit.id}}
<div fxLayout="row" fxLayoutAlign="start start">
<mat-form-field fxLayout="row" fxLayoutAlign="start start">
<mat-select autoFocus [(ngModel)]="appConfig.defaultNodeIndex" tabindex="1" name="defaultNode">
<mat-option *ngFor="let node of appConfig.nodes" [value]="node.index">
{{node.lnNode}} ({{node.lnImplementation}})
</mat-option>
</mat-select>
<mat-error *ngIf="selNode.settings.fiatConversion && !selNode.settings.currencyUnit">Currency unit is required.</mat-error>
</mat-form-field>
</div>
</div>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<div fxLayout="row wrap" fxLayoutAlign="start start" fxLayout.gt-sm="column" fxFlex="100" fxLayoutAlign.gt-sm="space-between stretch" class="settings-container page-sub-title-container mt-1">
<div class="mt-1">
<fa-icon [icon]="faPaintBrush" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Customization</span>
</div>
<div fxLayout="column" fxLayoutAlign="start stretch" class="mt-1 bordered-box padding-gap-large">
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start start" *ngIf="appConfig.nodes.length && appConfig.nodes.length > 1">
<h4 class="my-0">Default Node</h4>
<mat-form-field>
<mat-select [(ngModel)]="appConfig.defaultNodeIndex" tabindex="4" name="defaultNode">
<mat-option *ngFor="let node of appConfig.nodes" [value]="node.index">
{{node.lnNode}} ({{node.lnImplementation}})
</mat-option>
</mat-select>
</mat-form-field>
</div>
<mat-divider [inset]="true" class="mb-1" *ngIf="appConfig.nodes.length && appConfig.nodes.length > 1"></mat-divider>
<div fxLayout="column" fxLayoutAlign="start stretch" fxFlex="100">
<div fxLayout="row" fxFlex="100" class="alert alert-info mb-0">
<fa-icon [icon]="faInfoCircle" class="mr-1 alert-icon"></fa-icon>
<span>Application layout will be tailored based on the role selected to better serve its needs.</span>
</div>
<div fxLayout="column" fxLayoutAlign="start start" fxFlex="100">
<h4>Application Layout</h4>
<mat-radio-group color="primary" [(ngModel)]="selNode.settings.userPersona" tabindex="1" name="userPersona">
<mat-radio-button *ngFor="let userPersona of userPersonas" [value]="userPersona" [checked]="selNode.settings.userPersona === userPersona" class="mr-4">
{{userPersona | titlecase}}
</mat-radio-button>
</mat-radio-group>
</div>
</div>
<mat-divider [inset]="true" class="mt-1"></mat-divider>
<div fxLayout="column" fxLayout.gt-xs="row" fxFlex="100" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-xs="start stretch">
<div fxFlex.gt-xs="20" fxFlex.gt-md="15" fxLayout="column" fxLayoutAlign="space-between stretch">
<h4>Mode</h4>
<mat-radio-group color="primary" [(ngModel)]="selectedThemeMode" (change)="chooseThemeMode()" name="themeMode">
<mat-radio-button tabindex="5" *ngFor="let themeMode of themeModes" [value]="themeMode" [ngClass]="{'mr-4': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">{{themeMode.name}}
</mat-radio-button>
</mat-radio-group>
</div>
<div fxLayout="column" fxFlex.gt-xs="20" fxFlex.gt-md="15" fxLayoutAlign="space-between stretch"></div>
<div fxLayout="column" fxFlex.gt-xs="50" fxFlex.gt-md="40" fxLayoutAlign="space-between stretch">
<h4>Themes</h4>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start">
<span *ngFor="let themeColor of themeColors" fxLayout="row">
<div tabindex="9" [class]="themeColor.id | lowercase" [ngClass]="{'skin': true, 'selected-color': selectedThemeColor === themeColor.id}" (click)="changeThemeColor(themeColor.id)"></div>
{{themeColor.name}}
</span>
</div>
</div>
</div>
<div fxLayout="row" fxLayoutAlign="start start" class="mt-1">
<div fxLayout="row" fxLayoutAlign="start start">
<button class="mr-1" mat-stroked-button color="primary" (click)="onResetSettings()" tabindex="2">Reset</button>
<button mat-flat-button color="primary" (click)="onUpdateSettings()" tabindex="3">Update</button>
</div>
</div>
</div>
<!-- <mat-divider [inset]="true" class="my-2"></mat-divider> -->
</form>
<div fxLayout="row" class="mt-1">
<button class="mr-1" mat-stroked-button color="primary" (click)="onResetSettings()" tabindex="10">Reset</button>
<button mat-flat-button color="primary" (click)="onUpdateSettings()" tabindex="11">Update</button>
<!-- <div fxLayout="row">
<fa-icon [icon]="faPlus" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Add New Node</span>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start start">
<button mat-flat-button color="primary" (click)="onAddNewNode()" tabindex="4" class="my-2">Add Node</button>
</div> -->
</div>

@ -2,16 +2,11 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faTools, faPaintBrush, faInfoCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { faWindowRestore, faPlus, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { CURRENCY_UNITS, UserPersonaEnum, ScreenSizeEnum, FIAT_CURRENCY_UNITS, NODE_SETTINGS } from '../../../services/consts-enums-functions';
import { ConfigSettingsNode, Settings, RTLConfiguration, GetInfoRoot } from '../../../models/RTLconfig';
import { ConfigSettingsNode, RTLConfiguration } from '../../../models/RTLconfig';
import { LoggerService } from '../../../services/logger.service';
import { CommonService } from '../../../services/common.service';
import * as ECLActions from '../../../../eclair/store/ecl.actions';
import * as CLActions from '../../../../clightning/store/cl.actions';
import * as LNDActions from '../../../../lnd/store/lnd.actions';
import * as RTLActions from '../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
@ -21,88 +16,39 @@ import * as fromRTLReducer from '../../../../store/rtl.reducers';
styleUrls: ['./app-settings.component.scss']
})
export class AppSettingsComponent implements OnInit, OnDestroy {
public faExclamationTriangle = faExclamationTriangle;
public faTools = faTools;
public faPaintBrush = faPaintBrush;
public faInfoCircle = faInfoCircle;
public faWindowRestore = faWindowRestore;
public faPlus = faPlus;
public selNode: ConfigSettingsNode;
public information: GetInfoRoot = {};
public userPersonas = [UserPersonaEnum.OPERATOR, UserPersonaEnum.MERCHANT];
public currencyUnits = FIAT_CURRENCY_UNITS;
public themeModes = NODE_SETTINGS.modes;
public themeColors = NODE_SETTINGS.themes;
public selectedThemeMode = NODE_SETTINGS.modes[0];
public selectedThemeColor = NODE_SETTINGS.themes[0].id;
public currencyUnit = 'BTC';
public smallerCurrencyUnit = 'Sats';
public showSettingOption = true;
public appConfig: RTLConfiguration;
public previousSettings: Settings;
public previousDefaultNode = 0;
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<fromRTLReducer.RTLState>) {
this.screenSize = this.commonService.getScreenSize();
}
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>) {}
ngOnInit() {
this.store.select('root')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
this.appConfig = rtlStore.appConfig;
this.selNode = rtlStore.selNode;
this.selectedThemeMode = this.themeModes.find(themeMode => this.selNode.settings.themeMode === themeMode.id);
this.selectedThemeColor = this.selNode.settings.themeColor;
this.information = rtlStore.nodeData;
this.smallerCurrencyUnit = ( this.information && this.information.smaller_currency_unit) ? this.information.smaller_currency_unit : 'Sats';
this.currencyUnit = ( this.information && this.information.currency_unit) ? this.information.currency_unit : 'BTC';
if(!this.selNode.settings.fiatConversion) {
this.selNode.settings.currencyUnit = null;
}
this.previousSettings = JSON.parse(JSON.stringify(this.selNode.settings));
this.previousDefaultNode = this.appConfig.defaultNodeIndex;
this.selNode = rtlStore.selNode;
this.logger.info(rtlStore);
});
}
onCurrencyChange(event: any) {
this.selNode.settings.currencyUnits = [...CURRENCY_UNITS, event.value];
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
}
toggleSettings(toggleField: string, event?: any) {
this.selNode.settings[toggleField] = !this.selNode.settings[toggleField];
}
changeThemeColor(newThemeColor: string) {
this.selectedThemeColor = newThemeColor;
this.selNode.settings.themeColor = newThemeColor;
}
chooseThemeMode() {
this.selNode.settings.themeMode = this.selectedThemeMode.id;
onAddNewNode() {
console.warn('ADD NEW NODE');
}
onUpdateSettings():boolean|void {
if(this.selNode.settings.fiatConversion && !this.selNode.settings.currencyUnit) { return true; }
let defaultNodeIndex = (this.previousDefaultNode !== this.appConfig.defaultNodeIndex) ? this.appConfig.defaultNodeIndex : null;
this.logger.info(this.selNode.settings);
this.store.dispatch(new RTLActions.OpenSpinner('Updating Settings...'));
this.store.dispatch(new RTLActions.SaveSettings({settings: this.selNode.settings, defaultNodeIndex: defaultNodeIndex}));
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
let defaultNodeIndex = (this.appConfig.defaultNodeIndex) ? this.appConfig.defaultNodeIndex : +this.appConfig.nodes[0].index;
this.store.dispatch(new RTLActions.OpenSpinner('Updating Defaule Node Settings...'));
this.store.dispatch(new RTLActions.SaveSettings({defaultNodeIndex: defaultNodeIndex}));
}
onResetSettings() {
this.selNode.settings = this.previousSettings;
this.selectedThemeMode = this.themeModes.find(themeMode => themeMode.id === this.previousSettings.themeMode);
this.selectedThemeColor = this.previousSettings.themeColor;
this.store.dispatch(new RTLActions.SetSelelectedNode({ lnNode: this.selNode, isInitialSetup: true }));
this.appConfig.defaultNodeIndex = this.previousDefaultNode;
}
ngOnDestroy() {

@ -1,23 +1,24 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="overflow-x-hidden">
<form fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="page-sub-title-container mt-1" (ngSubmit)="onChangePassword()" #authForm="ngForm">
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="padding-gap-x-large">
<form fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="page-sub-title-container mt-1" #authForm="ngForm">
<div fxLayout="row" fxLayoutAlign="start start" class="mb-2">
<fa-icon [icon]="faUserLock" class="page-title-img mr-1"></fa-icon>
<fa-icon [icon]="faLock" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Password</span>
</div>
<mat-form-field>
<input autoFocus matInput placeholder="Current Password" type="password" id="currpassword" name="currpassword" [(ngModel)]="currPassword" tabindex="4" required>
<input matInput placeholder="Current Password" type="password" id="currpassword" name="currpassword" [(ngModel)]="currPassword" tabindex="6" required>
<mat-error *ngIf="!currPassword">Current password is required.</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="New Password" type="password" id="newpassword" name="newpassword" [(ngModel)]="newPassword" tabindex="5" required>
<input matInput placeholder="New Password" type="password" id="newpassword" name="newpassword" [(ngModel)]="newPassword" tabindex="7" required>
<mat-error *ngIf="matchOldAndNewPasswords()">{{errorMsg}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Confirm New Password" type="password" id="confirmpassword" name="confirmpassword" [(ngModel)]="confirmPassword" tabindex="6" required>
<input matInput placeholder="Confirm New Password" type="password" id="confirmpassword" name="confirmpassword" [(ngModel)]="confirmPassword" tabindex="8" required>
<mat-error *ngIf="matchNewPasswords()">{{errorConfirmMsg}}</mat-error>
</mat-form-field>
<div fxLayout="row" fxLayoutAlign="start start" class="mt-1">
<button mat-flat-button color="primary" tabindex="8" type="submit">Change Password</button>
<button class="mr-1" mat-stroked-button color="primary" type="reset" (click)="onResetPassword()" tabindex="9">Reset</button>
<button mat-flat-button color="primary" tabindex="10" type="button" (click)="onChangePassword()">Change Password</button>
</div>
</form>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="end stretch" class="my-2">
@ -33,7 +34,7 @@
<span>Protect your account from unauthorized access by requiring a second authentication method in addition to your password.</span>
</div>
<div class="mt-1">
<button mat-flat-button color="primary" tabindex="3" (click)="on2FAuth()">{{appConfig.enable2FA ? 'Disable 2FA' : 'Enable 2FA'}}</button>
<button mat-flat-button color="primary" tabindex="11" (click)="on2FAuth()" class="mb-2">{{appConfig.enable2FA ? 'Disable 2FA' : 'Enable 2FA'}}</button>
</div>
</div>
</div>

@ -4,7 +4,7 @@ import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faUserLock, faUserClock, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { faUserLock, faUserClock, faInfoCircle, faLock } from '@fortawesome/free-solid-svg-icons';
import * as sha256 from 'sha256';
import { TwoFactorAuthComponent } from '../../data-modal/two-factor-auth/two-factor-auth.component';
@ -24,6 +24,7 @@ export class AuthSettingsComponent implements OnInit, OnDestroy {
public faInfoCircle = faInfoCircle;
public faUserLock = faUserLock;
public faUserClock = faUserClock;
public faLock = faLock;
public currPassword = '';
public newPassword = '';
public confirmPassword = '';
@ -119,6 +120,10 @@ export class AuthSettingsComponent implements OnInit, OnDestroy {
}}));
}
onResetPassword() {
this.form.resetForm();
}
ngOnDestroy() {
if(this.initializeNodeData) {
this.store.dispatch(new RTLActions.SetSelelectedNode({lnNode: this.selNode, isInitialSetup: true}));

@ -0,0 +1,21 @@
<div fxLayout="column" fxFlex="100">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start start">
<div *ngIf="configData !== '' && fileFormat === 'JSON'" fxFlex="100" class="mb-6">
<pre class="pre-wrap">{{configData | json}}</pre>
<mat-divider *ngIf="configData !== ''" class="my-1"></mat-divider>
</div>
<div *ngIf="configData !== '' && (fileFormat === 'INI' || fileFormat === 'HOCON')" fxFlex="100">
<mat-list>
<mat-list-item *ngFor="let conf of configData; index as i;">
<mat-card-subtitle class="my-1">
<h2 *ngIf="conf.indexOf('[') >= 0">{{conf}}</h2>
</mat-card-subtitle>
<mat-card-subtitle class="m-0">
<h4 *ngIf="conf.indexOf('[') < 0" class="ml-4">{{conf}}</h4>
</mat-card-subtitle>
<mat-divider [inset]="true" *ngIf="conf.indexOf('[') < 0"></mat-divider>
</mat-list-item>
</mat-list>
</div>
</div>
</div>

@ -0,0 +1,55 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ResolveEnd } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faCog } from '@fortawesome/free-solid-svg-icons';
import { RTLEffects } from '../../../../store/rtl.effects';
import * as RTLActions from '../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
@Component({
selector: 'rtl-bitcoin-config',
templateUrl: './bitcoin-config.component.html',
styleUrls: ['./bitcoin-config.component.scss']
})
export class BitcoinConfigComponent implements OnInit, OnDestroy {
public selectedNodeType = '';
public configData = '';
public fileFormat = 'INI';
public faCog = faCog;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private router: Router) {}
ngOnInit() {
this.selectedNodeType = (this.router.url.includes('bconfig')) ? 'bitcoind' : 'ln';
this.router.events.pipe(takeUntil(this.unSubs[0]), filter(e => e instanceof ResolveEnd))
.subscribe((value: ResolveEnd) => {
this.selectedNodeType = (value.urlAfterRedirects.includes('bconfig')) ? 'bitcoind' : 'ln';
});
this.store.dispatch(new RTLActions.OpenSpinner('Opening Config File...'));
this.store.dispatch(new RTLActions.FetchConfig(this.selectedNodeType));
this.rtlEffects.showLnConfig
.pipe(takeUntil(this.unSubs[1]))
.subscribe((config: any) => {
const configFile = config.data;
this.fileFormat = config.format;
if (configFile !== '' && configFile && (this.fileFormat === 'INI' || this.fileFormat === 'HOCON')) {
this.configData = configFile.split('\n');
} else if (configFile !== '' && configFile && this.fileFormat === 'JSON') {
this.configData = configFile;
} else {
this.configData = '';
}
});
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -1,5 +1,5 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon [icon]="faTools" class="page-title-img mr-1"></fa-icon>
<fa-icon [icon]="faUserCog" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Settings</span>
</div>
<div fxLayout="column" class="padding-gap-x">
@ -7,9 +7,8 @@
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar>
<div role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[0].link" (click)="activeLink = links[0].link" routerLink="{{links[0].link}}">{{links[0].name}}</div>
<div role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[1].link" (click)="activeLink = links[1].link" routerLink="{{links[1].link}}" [state]="{ initial: false }">{{links[1].name}}</div>
<div role="tab" mat-tab-link *ngIf="showLnConfig" class="mat-tab-label" [active]="activeLink === links[2].link" (click)="activeLink = links[2].link" routerLink="{{links[2].link}}">{{links[2].name}}</div>
<div role="tab" mat-tab-link *ngIf="showBitcoind" class="mat-tab-label" [active]="activeLink === links[3].link" (click)="activeLink = links[3].link" routerLink="{{links[3].link}}">{{links[3].name}}</div>
<div role="tab" mat-tab-link *ngIf="!appConfig.sso.rtlSSO" class="mat-tab-label" [active]="activeLink === links[1].link" (click)="activeLink = links[1].link" routerLink="{{links[1].link}}" [state]="{ initial: false }">{{links[1].name}}</div>
<div role="tab" mat-tab-link *ngIf="showBitcoind" class="mat-tab-label" [active]="activeLink === links[2].link" (click)="activeLink = links[2].link" routerLink="{{links[2].link}}">{{links[2].name}}</div>
</nav>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">
<router-outlet></router-outlet>

@ -3,7 +3,7 @@ import { Router, ResolveEnd } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faTools } from '@fortawesome/free-solid-svg-icons';
import { faUserCog } from '@fortawesome/free-solid-svg-icons';
import { ConfigSettingsNode, RTLConfiguration } from '../../models/RTLconfig';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@ -14,13 +14,11 @@ import * as fromRTLReducer from '../../../store/rtl.reducers';
styleUrls: ['./settings.component.scss']
})
export class SettingsComponent implements OnInit, OnDestroy{
public faTools = faTools;
public showLnConfig = false;
public faUserCog = faUserCog;
public showBitcoind = false;
public selNode: ConfigSettingsNode;
public appConfig: RTLConfiguration;
public lnImplementationStr = '';
public links = [{link: 'layout', name: 'Layout'}, {link: 'auth', name: 'Authentication'}, {link: 'lnconfig', name: this.lnImplementationStr}, {link: 'bconfig', name: 'BitcoinD Config'}];
public links = [{link: 'app', name: 'Application'}, {link: 'auth', name: 'Authentication'}, {link: 'bconfig', name: 'BitcoinD Config'}];
public activeLink = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
@ -36,27 +34,9 @@ export class SettingsComponent implements OnInit, OnDestroy{
});
this.store.select('root').pipe(takeUntil(this.unSubs[1]))
.subscribe((rtlStore) => {
this.showLnConfig = false;
this.showBitcoind = false;
this.appConfig = rtlStore.appConfig;
this.selNode = rtlStore.selNode;
switch (this.selNode.lnImplementation.toUpperCase()) {
case 'CLT':
this.lnImplementationStr = 'C-Lightning Config';
break;
case 'ECL':
this.lnImplementationStr = 'Eclair Config';
break;
default:
this.lnImplementationStr = 'LND Config';
break;
}
if (this.selNode.authentication && this.selNode.authentication.configPath && this.selNode.authentication.configPath.trim() !== '') {
this.links[2].name = this.lnImplementationStr;
this.showLnConfig = true;
}
if (this.selNode.settings && this.selNode.settings.bitcoindConfigPath && this.selNode.settings.bitcoindConfigPath.trim() !== '') {
this.showBitcoind = true;
}

@ -26,6 +26,8 @@ export class Settings {
export class Authentication {
constructor(
public swapMacaroonPath: string,
public boltzMacaroonPath: string,
public configPath?: string
) { }
}

@ -163,6 +163,8 @@ export interface AlertData {
export interface ConfirmationData {
type: string; // INFORMATION/WARNING/SUCCESS/ERROR
alertTitle?: string;
warningMessage?: string;
informationMessage?: string;
titleMessage?: string;
message?: any;
scrollable?: boolean;

@ -20,7 +20,7 @@ export const MENU_DATA: MenuRootNode = {
{id: 41, parentId: 4, name: 'Loop', iconType: 'FA', icon: faInfinity, link: '/services/loop', userPersona: UserPersonaEnum.ALL},
{id: 42, parentId: 4, name: 'Boltz', iconType: 'SVG', icon: 'boltzIconBlock', link: '/services/boltz', userPersona: UserPersonaEnum.ALL}
]},
{id: 5, parentId: 0, name: 'Settings', iconType: 'FA', icon: faTools, link: '/settings', userPersona: UserPersonaEnum.ALL},
{id: 5, parentId: 0, name: 'Node Config', iconType: 'FA', icon: faTools, link: '/config', userPersona: UserPersonaEnum.ALL},
{id: 6, parentId: 0, name: 'Help', iconType: 'FA', icon: faQuestion, link: '/help', userPersona: UserPersonaEnum.ALL}
],
CLChildren: [
@ -36,8 +36,8 @@ export const MENU_DATA: MenuRootNode = {
{id: 37, parentId: 3, name: 'Fee Rates', iconType: 'FA', icon: faPercentage, link: '/cl/rates', userPersona: UserPersonaEnum.OPERATOR},
{id: 38, parentId: 3, name: 'Node/Fee Rates', iconType: 'FA', icon: faServer, link: '/cl/rates', userPersona: UserPersonaEnum.MERCHANT}
]},
{id: 7, parentId: 0, name: 'Settings', iconType: 'FA', icon: faTools, link: '/settings', userPersona: UserPersonaEnum.ALL},
{id: 8, parentId: 0, name: 'Help', iconType: 'FA', icon: faQuestion, link: '/help', userPersona: UserPersonaEnum.ALL}
{id: 4, parentId: 0, name: 'Node Config', iconType: 'FA', icon: faTools, link: '/config', userPersona: UserPersonaEnum.ALL},
{id: 5, parentId: 0, name: 'Help', iconType: 'FA', icon: faQuestion, link: '/help', userPersona: UserPersonaEnum.ALL}
],
ECLChildren: [
{id: 1, parentId: 0, name: 'Dashboard', iconType: 'FA', icon: faTachometerAlt, link: '/ecl/home', userPersona: UserPersonaEnum.ALL},
@ -49,8 +49,8 @@ export const MENU_DATA: MenuRootNode = {
{id: 34, parentId: 3, name: 'Reports', iconType: 'FA', icon: faChartBar, link: '/ecl/reports', userPersona: UserPersonaEnum.ALL},
{id: 35, parentId: 3, name: 'Graph Lookup', iconType: 'FA', icon: faSearch, link: '/ecl/lookups', userPersona: UserPersonaEnum.ALL}
]},
{id: 7, parentId: 0, name: 'Settings', iconType: 'FA', icon: faTools, link: '/settings', userPersona: UserPersonaEnum.ALL},
{id: 8, parentId: 0, name: 'Help', iconType: 'FA', icon: faQuestion, link: '/help', userPersona: UserPersonaEnum.ALL}
{id: 4, parentId: 0, name: 'Node Config', iconType: 'FA', icon: faTools, link: '/config', userPersona: UserPersonaEnum.ALL},
{id: 5, parentId: 0, name: 'Help', iconType: 'FA', icon: faQuestion, link: '/help', userPersona: UserPersonaEnum.ALL}
]
};

@ -180,3 +180,8 @@ export const MONTHS = [
];
export const SCROLL_RANGES = ['MONTHLY','YEARLY'];
export enum ServicesEnum {
LOOP = 'LOOP',
BOLTZ = 'BOLTZ'
}

@ -51,18 +51,24 @@ import { OverlayContainer } from '@angular/cdk/overlay';
import { LoggerService, ConsoleLoggerService } from './services/logger.service';
import { MONTHS } from './services/consts-enums-functions';
import { AppSettingsComponent } from './components/settings/app-settings/app-settings.component';
import { NotFoundComponent } from './components/not-found/not-found.component';
import { LoginComponent } from './components/login/login.component';
import { HelpComponent } from './components/help/help.component';
import { SideNavigationComponent } from './components/navigation/side-navigation/side-navigation.component';
import { TopMenuComponent } from './components/navigation/top-menu/top-menu.component';
import { SettingsComponent } from './components/settings/settings.component';
import { ServerConfigComponent } from './components/settings/server-config/server-config.component';
import { BitcoinConfigComponent } from './components/settings/bitcoin-config/bitcoin-config.component';
import { AuthSettingsComponent } from './components/settings/auth-settings/auth-settings.component';
import { AppSettingsComponent } from './components/settings/app-settings/app-settings.component';
import { NodeConfigComponent } from './components/node-config/node-config.component';
import { LNPConfigComponent } from './components/node-config/lnp-config/lnp-config.component';
import { NodeSettingsComponent } from './components/node-config/node-settings/node-settings.component';
import { ServicesSettingsComponent } from './components/node-config/services-settings/services-settings.component';
import { LoopServiceSettingsComponent } from './components/node-config/services-settings/loop-service-settings/loop-service-settings.component';
import { BoltzServiceSettingsComponent } from './components/node-config/services-settings/boltz-service-settings/boltz-service-settings.component';
import { ErrorComponent } from './components/error/error.component';
import { CurrencyUnitConverterComponent } from './components/currency-unit-converter/currency-unit-converter.component';
import { HorizontalScrollerComponent } from './components/horizontal-scroller/horizontal-scroller.component';
import { AuthSettingsComponent } from './components/settings/auth-settings/auth-settings.component';
import { TransactionsReportTableComponent } from './components/transactions-report-table/transactions-report-table.component';
import { ShowPubkeyComponent } from './components/data-modal/show-pubkey/show-pubkey.component';
import { OnChainGeneratedAddressComponent } from './components/data-modal/on-chain-generated-address/on-chain-generated-address.component';
@ -223,7 +229,16 @@ export const DEFAULT_DATE_FORMAT: MatDateFormats = {
TopMenuComponent,
LoginComponent,
HelpComponent,
ServerConfigComponent,
SettingsComponent,
BitcoinConfigComponent,
AuthSettingsComponent,
AppSettingsComponent,
NodeConfigComponent,
LNPConfigComponent,
NodeSettingsComponent,
ServicesSettingsComponent,
LoopServiceSettingsComponent,
BoltzServiceSettingsComponent,
CurrencyUnitConverterComponent,
HorizontalScrollerComponent,
TransactionsReportTableComponent,
@ -251,7 +266,16 @@ export const DEFAULT_DATE_FORMAT: MatDateFormats = {
TopMenuComponent,
LoginComponent,
HelpComponent,
ServerConfigComponent,
SettingsComponent,
BitcoinConfigComponent,
AuthSettingsComponent,
AppSettingsComponent,
NodeConfigComponent,
LNPConfigComponent,
NodeSettingsComponent,
ServicesSettingsComponent,
LoopServiceSettingsComponent,
BoltzServiceSettingsComponent,
CurrencyUnitConverterComponent,
HorizontalScrollerComponent,
ErrorComponent,

@ -1294,6 +1294,10 @@ table {
font-size: 120% !important;
}
.font-size-300 {
font-size: 300% !important;
}
.font-weight-900 {
font-weight: 900 !important;
}

@ -97,10 +97,6 @@
color: $primary-color;
}
.page-title-container, .page-sub-title-container {
color: $foreground-text;
}
.material-icons.primary { color: $primary-color; }
.material-icons.accent { color: $accent-color; }

@ -2,7 +2,8 @@ import { Action } from '@ngrx/store';
import { ErrorPayload } from '../shared/models/errorPayload';
import { DialogConfig } from '../shared/models/alertData';
import { RTLConfiguration, Settings, ConfigSettingsNode, GetInfoRoot } from '../shared/models/RTLconfig';
import { RTLConfiguration, Settings, ConfigSettingsNode, GetInfoRoot, SSO } from '../shared/models/RTLconfig';
import { ServicesEnum } from '../shared/services/consts-enums-functions';
export const VOID = 'VOID';
export const UPDATE_SELECTED_NODE_OPTIONS = 'UPDATE_SELECTED_NODE_OPTIONS';
@ -24,9 +25,11 @@ export const FETCH_STORE = 'FETCH_STORE';
export const SET_STORE = 'SET_STORE';
export const FETCH_RTL_CONFIG = 'FETCH_RTL_CONFIG';
export const SET_RTL_CONFIG = 'SET_RTL_CONFIG';
export const SAVE_SSO = 'SAVE_SSO';
export const SAVE_SETTINGS = 'SAVE_SETTINGS';
export const TWO_FA_SAVE_SETTINGS = 'TWO_FA_SAVE_SETTINGS';
export const SET_SELECTED_NODE = 'SET_SELECTED_NODE';
export const UPDATE_SERVICE_SETTINGS = 'UPDATE_SERVICE_SETTINGS';
export const SET_NODE_DATA = 'SET_NODE_DATA';
export const IS_AUTHORIZED = 'IS_AUTHORIZED';
export const IS_AUTHORIZED_RES = 'IS_AUTHORIZED_RES';
@ -125,7 +128,7 @@ export class SetRTLConfig implements Action {
export class SaveSettings implements Action {
readonly type = SAVE_SETTINGS;
constructor(public payload: {settings: Settings, defaultNodeIndex?: number}) {}
constructor(public payload: {settings?: Settings, defaultNodeIndex?: number}) {}
}
export class TwoFASaveSettings implements Action {
@ -138,11 +141,21 @@ export class SetSelelectedNode implements Action {
constructor(public payload: { lnNode: ConfigSettingsNode, isInitialSetup: boolean }) {}
}
export class UpdateServiceSettings implements Action {
readonly type = UPDATE_SERVICE_SETTINGS;
constructor(public payload: { service: ServicesEnum, settings: any }) {}
}
export class SetNodeData implements Action {
readonly type = SET_NODE_DATA;
constructor(public payload: GetInfoRoot) {}
}
export class SaveSSO implements Action {
readonly type = SAVE_SSO;
constructor(public payload: SSO) {}
}
export class Logout implements Action {
readonly type = LOGOUT;
constructor() {}
@ -193,5 +206,5 @@ export type RTLActions = ClearEffectErrorRoot | EffectErrorRoot |
VoidAction | CloseAllDialogs | OpenSnackBar | OpenSpinner | CloseSpinner | FetchRTLConfig | SetRTLConfig | SaveSettings |
OpenAlert | CloseAlert | OpenConfirmation | CloseConfirmation | ShowPubkey | FetchConfig | ShowConfig |
UpdateSelectedNodeOptions | ResetRootStore |
SetSelelectedNode | SetNodeData |
SetSelelectedNode | SetNodeData | SaveSSO | UpdateServiceSettings |
Logout | ResetPassword | ResetPasswordRes | FetchFile | ShowFile;

@ -223,6 +223,50 @@ export class RTLEffects implements OnDestroy {
})
));
@Effect()
updateServicesettings = this.actions$.pipe(
ofType(RTLActions.UPDATE_SERVICE_SETTINGS),
mergeMap((action: RTLActions.UpdateServiceSettings) => {
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('UpdateServiceSettings'));
return this.httpClient.post(environment.CONF_API + '/updateServiceSettings', action.payload);
}),
map((updateStatus: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.info(updateStatus);
return {
type: RTLActions.OPEN_SNACK_BAR,
payload: updateStatus.message + '.'
};
},
catchError((err) => {
this.store.dispatch(new RTLActions.EffectErrorRoot({ action: 'UpdateServiceSettings', code: err.status, message: err.error.error }));
this.handleErrorWithAlert('ERROR', 'Update Service Settings Failed!', environment.CONF_API, err);
return of({type: RTLActions.VOID});
})
));
@Effect()
ssoSave = this.actions$.pipe(
ofType(RTLActions.SAVE_SSO),
mergeMap((action: RTLActions.SaveSSO) => {
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('UpdateSSO'));
return this.httpClient.post(environment.CONF_API + '/updateSSO', { SSO: action.payload });
}),
map((updateStatus: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.info(updateStatus);
return {
type: RTLActions.OPEN_SNACK_BAR,
payload:updateStatus.message + '.'
};
},
catchError((err) => {
this.store.dispatch(new RTLActions.EffectErrorRoot({ action: 'UpdateSSO', code: err.status, message: err.error.error }));
this.handleErrorWithAlert('ERROR', 'Update SSO Failed!', environment.CONF_API, err);
return of({type: RTLActions.VOID});
})
));
@Effect()
twoFASettingSave = this.actions$.pipe(
ofType(RTLActions.TWO_FA_SAVE_SETTINGS),
@ -236,8 +280,8 @@ export class RTLEffects implements OnDestroy {
return { type: RTLActions.VOID };
},
catchError((err) => {
this.store.dispatch(new RTLActions.EffectErrorRoot({ action: 'Update2FASettings', code: (!err.length) ? err.status : err[0].status, message: (!err.length) ? err.error.error : err[0].error.error }));
this.handleErrorWithAlert('ERROR', 'Update 2FA Settings Failed!', environment.CONF_API, (!err.length) ? err : err[0]);
this.store.dispatch(new RTLActions.EffectErrorRoot({ action: 'Update2FASettings', code: err.status, message: err.error.error }));
this.handleErrorWithAlert('ERROR', 'Update 2FA Settings Failed!', environment.CONF_API, err);
return of({type: RTLActions.VOID});
})
));
@ -349,7 +393,7 @@ export class RTLEffects implements OnDestroy {
if (+rootStore.appConfig.sso.rtlSSO) {
this.router.navigate(['/error'], { state: { errorCode: '406', errorMessage: 'Single Sign On Failed!' }});
} else {
this.router.navigate([rootStore.appConfig.sso.logoutRedirectLink]);
this.router.navigate(['./login']);
}
return of({type: RTLActions.VOID});
})
@ -385,12 +429,13 @@ export class RTLEffects implements OnDestroy {
if (+store.appConfig.sso.rtlSSO) {
window.location.href = store.appConfig.sso.logoutRedirectLink;
} else {
this.router.navigate(['/login']);
this.router.navigate(['./login']);
}
this.sessionService.removeItem('eclUnlocked');
this.sessionService.removeItem('clUnlocked');
this.sessionService.removeItem('lndUnlocked');
this.sessionService.removeItem('token');
this.store.dispatch(new RTLActions.SetNodeData({}));
this.logger.warn('LOGGED OUT');
return of();
}));

@ -14,8 +14,8 @@ export interface RootState {
nodeData: GetInfoRoot;
}
const initNodeSettings = { userPersona: 'OPERATOR', themeMode: 'DAY', themeColor: 'PURPLE', channelBackupPath: '', selCurrencyUnit: 'USD', fiatConversion: false, currencyUnits: ['Sats', 'BTC', 'USD'] };
const initNodeAuthentication = { configPath: '', bitcoindConfigPath: '' };
const initNodeSettings = { userPersona: 'OPERATOR', themeMode: 'DAY', themeColor: 'PURPLE', channelBackupPath: '', selCurrencyUnit: 'USD', fiatConversion: false, currencyUnits: ['Sats', 'BTC', 'USD'], bitcoindConfigPath: '' };
const initNodeAuthentication = { configPath: '', swapMacaroonPath: '', boltzMacaroonPath: '', };
const initRootState: RootState = {
effectErrorsRoot: [],
@ -23,7 +23,7 @@ const initRootState: RootState = {
appConfig: {
defaultNodeIndex: -1,
selectedNodeIndex: -1,
sso: { rtlSSO: 0, logoutRedirectLink: '/login' },
sso: { rtlSSO: 0, logoutRedirectLink: '' },
enable2FA: false,
nodes: [{ settings: initNodeSettings, authentication: initNodeAuthentication}]
},
@ -53,7 +53,7 @@ export function RootReducer(state = initRootState, action: RTLActions.RTLActions
return {
...initRootState,
appConfig: state.appConfig,
selNode: action.payload,
selNode: action.payload
};
case RTLActions.SET_SELECTED_NODE:
return {

Loading…
Cancel
Save