Channel restore

Channel restore
This commit is contained in:
Shahana Farooqui 2019-10-07 19:10:21 -04:00
parent 5c0c1fdc7e
commit aac9d42bb4
24 changed files with 289 additions and 49 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -9,5 +9,5 @@
<link rel="stylesheet" href="styles.41a6644c686ce68dce93.css"></head> <link rel="stylesheet" href="styles.41a6644c686ce68dce93.css"></head>
<body> <body>
<rtl-app></rtl-app> <rtl-app></rtl-app>
<script src="runtime.66d0b5931cf64a2c9467.js"></script><script src="polyfills-es5.763f4f23e8aee5ec234d.js" nomodule></script><script src="polyfills.e59b6f9dc696bd89cf7f.js"></script><script src="main.271bb5b2dae0b00c178c.js"></script></body> <script src="runtime.881b21e05dd0455c6919.js"></script><script src="polyfills-es5.763f4f23e8aee5ec234d.js" nomodule></script><script src="polyfills.e59b6f9dc696bd89cf7f.js"></script><script src="main.c17160159e9da3b43373.js"></script></body>
</html> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

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

View file

@ -107,7 +107,7 @@ exports.postRestore = (req, res, next) => {
let restore_backup = ''; let restore_backup = '';
if (req.params.channelPoint === 'ALL') { if (req.params.channelPoint === 'ALL') {
message = 'All Channels Restore Successful!'; message = 'All Channels Restore Successful!';
channel_restore_file = common.selectedNode.channel_backup_path + common.path_separator + 'channel-all.bak'; channel_restore_file = common.selectedNode.channel_backup_path + common.path_separator + 'restore' + common.path_separator + 'channel-all.bak';
let exists = fs.existsSync(channel_restore_file); let exists = fs.existsSync(channel_restore_file);
if (exists) { if (exists) {
restore_backup = fs.readFileSync(channel_restore_file, 'utf-8'); restore_backup = fs.readFileSync(channel_restore_file, 'utf-8');
@ -124,7 +124,7 @@ exports.postRestore = (req, res, next) => {
} }
} else { } else {
message = 'Channel ' + req.params.channelPoint + ' Restore Successful!'; message = 'Channel ' + req.params.channelPoint + ' Restore Successful!';
channel_restore_file = common.selectedNode.channel_backup_path + common.path_separator + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak'; channel_restore_file = common.selectedNode.channel_backup_path + common.path_separator + 'restore' + common.path_separator + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak';
let exists = fs.existsSync(channel_restore_file); let exists = fs.existsSync(channel_restore_file);
if (exists) { if (exists) {
restore_backup = fs.readFileSync(channel_restore_file, 'utf-8'); restore_backup = fs.readFileSync(channel_restore_file, 'utf-8');
@ -143,8 +143,30 @@ exports.postRestore = (req, res, next) => {
logger.error({fileName: 'Channels Backup Restore', lineNum: 143, msg: 'Channel Backup Restore: ' + JSON.stringify(err)}); logger.error({fileName: 'Channels Backup Restore', lineNum: 143, msg: 'Channel Backup Restore: ' + JSON.stringify(err)});
return res.status(404).json({ return res.status(404).json({
message: 'Channel restore failed!', message: 'Channel restore failed!',
error: err.error error: err.error.error
}); });
}); });
} }
}; };
exports.getRestoreList = (req, res, next) => {
let files_list = [];
let all_restore_exists = false;
fs.readdir(common.selectedNode.channel_backup_path + common.path_separator + 'restore', function (err, files) {
if (err) {
if (err.code === 'ENOENT' && err.errno === -4058) {
return res.status(200).json({all_restore_exists: false, files: []});
} else {
return res.status(500).json({ message: 'Channels Restore List Failed!', error: err });
}
}
files.forEach(file => {
if (file === 'channel-all.bak') {
all_restore_exists = true;
} else {
files_list.push({channel_point: file.substring(8, file.length - 4).replace('-', ':')});
}
});
return res.status(200).json({all_restore_exists: all_restore_exists, files: files_list});
});
};

View file

@ -4,6 +4,7 @@ const router = express.Router();
const authCheck = require("../authCheck"); const authCheck = require("../authCheck");
router.get("/:channelPoint", authCheck, ChannelsBackupController.getBackup); router.get("/:channelPoint", authCheck, ChannelsBackupController.getBackup);
router.get("/restore/list", authCheck, ChannelsBackupController.getRestoreList);
router.post("/verify/:channelPoint", authCheck, ChannelsBackupController.postBackupVerify); router.post("/verify/:channelPoint", authCheck, ChannelsBackupController.postBackupVerify);
router.post("/restore/:channelPoint", authCheck, ChannelsBackupController.postRestore); router.post("/restore/:channelPoint", authCheck, ChannelsBackupController.postRestore);

View file

@ -1,10 +1,9 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Router, NavigationStart, ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subject, Observable } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil, filter, map, subscribeOn } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatTableDataSource, MatSort } from '@angular/material'; import { MatTableDataSource, MatSort } from '@angular/material';
import { ChannelCL, PeerCL, GetInfoCL, ChannelEdgeCL } from '../../shared/models/clModels'; import { ChannelCL, PeerCL, GetInfoCL, ChannelEdgeCL } from '../../shared/models/clModels';

View file

@ -3,15 +3,14 @@
<mat-card> <mat-card>
<mat-card-header> <mat-card-header>
<mat-card-subtitle> <mat-card-subtitle>
<h2>All Channels Backup</h2> <h2>Channels Backup</h2>
</mat-card-subtitle> </mat-card-subtitle>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxFlex="100" fxLayoutAlign="space-between start"> <div fxLayout="column" fxLayout.gt-sm="row wrap" fxFlex="100" fxLayoutAlign="space-between start">
<h4 fxFlex="100">Backup folder location: {{selNode.channelBackupPath}}</h4> <h4 fxFlex="100">Backup folder location: {{selNode.channelBackupPath}}</h4>
<button fxFlex="33" fxLayoutAlign="center center" mat-raised-button color="primary" tabindex="1" (click)="onBackupChannels({})">Backup</button> <button fxFlex="49" fxLayoutAlign="center center" mat-raised-button color="primary" tabindex="1" (click)="onBackupChannels({})">All Channels Backup</button>
<button fxFlex="33" fxLayoutAlign="center center" mat-raised-button color="primary" tabindex="2" (click)="onVerifyChannels({})">Verify Backup</button> <button fxFlex="49" fxLayoutAlign="center center" mat-raised-button color="primary" tabindex="2" (click)="onVerifyChannels({})">All Channels Verify Backup</button>
<button fxFlex="33" fxLayoutAlign="center center" mat-raised-button color="accent" tabindex="3" (click)="onRestoreChannels({})">Restore</button>
</div> </div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
@ -27,9 +26,9 @@
<div perfectScrollbar class="table-container mat-elevation-z8"> <div perfectScrollbar class="table-container mat-elevation-z8">
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar> <mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar>
<table mat-table #table [dataSource]="channels" matSort [ngClass]="{'mat-elevation-z8 overflow-x-auto error-border': flgLoading[0]==='error','mat-elevation-z8 overflow-x-auto': true}"> <table mat-table #table [dataSource]="channels" matSort [ngClass]="{'mat-elevation-z8 overflow-x-auto error-border': flgLoading[0]==='error','mat-elevation-z8 overflow-x-auto': true}">
<ng-container matColumnDef="chan_id"> <ng-container matColumnDef="chan_point">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Channel ID </th> <th mat-header-cell *matHeaderCellDef mat-sort-header> Channel Point </th>
<td mat-cell *matCellDef="let channel">{{channel?.chan_id}}</td> <td mat-cell *matCellDef="let channel">{{channel?.channel_point}}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="backup"> <ng-container matColumnDef="backup">
<th mat-header-cell *matHeaderCellDef>Backup</th> <th mat-header-cell *matHeaderCellDef>Backup</th>
@ -39,10 +38,6 @@
<th mat-header-cell *matHeaderCellDef>Verify</th> <th mat-header-cell *matHeaderCellDef>Verify</th>
<td mat-cell *matCellDef="let channel"><mat-icon color="primary" (click)="onVerifyChannels(channel)">verified_user</mat-icon></td> <td mat-cell *matCellDef="let channel"><mat-icon color="primary" (click)="onVerifyChannels(channel)">verified_user</mat-icon></td>
</ng-container> </ng-container>
<ng-container matColumnDef="restore">
<th mat-header-cell *matHeaderCellDef>Restore</th>
<td mat-cell *matCellDef="let channel"><mat-icon color="accent" (click)="onRestoreChannels(channel)">restore</mat-icon></td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="onChannelClick(row, $event)"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="onChannelClick(row, $event)"></tr>
</table> </table>

View file

@ -1,6 +1,10 @@
.mat-column-chan_id { .mat-column-chan_point {
flex: 0 0 25%; flex: 1 1 25%;
min-width: 100px; min-width: 100px;
max-width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
table { table {

View file

@ -23,7 +23,7 @@ import * as fromRTLReducer from '../../../store/rtl.reducers';
export class ChannelBackupComponent implements OnInit, OnDestroy { export class ChannelBackupComponent implements OnInit, OnDestroy {
@ViewChild(MatSort, { static: true }) sort: MatSort; @ViewChild(MatSort, { static: true }) sort: MatSort;
public selNode: SelNodeChild = {}; public selNode: SelNodeChild = {};
public displayedColumns = ['chan_id', 'backup', 'verify', 'restore']; public displayedColumns = ['chan_point', 'backup', 'verify'];
public selChannel: Channel; public selChannel: Channel;
public channels: any; public channels: any;
public flgLoading: Array<Boolean | 'error'> = [true]; // 0: channels public flgLoading: Array<Boolean | 'error'> = [true]; // 0: channels
@ -82,11 +82,6 @@ export class ChannelBackupComponent implements OnInit, OnDestroy {
this.store.dispatch(new RTLActions.VerifyChannels({channelPoint: (selChannel.channel_point) ? selChannel.channel_point : 'ALL'})); this.store.dispatch(new RTLActions.VerifyChannels({channelPoint: (selChannel.channel_point) ? selChannel.channel_point : 'ALL'}));
} }
onRestoreChannels(selChannel: Channel) {
this.store.dispatch(new RTLActions.OpenSpinner('Restoring Channels...'));
this.store.dispatch(new RTLActions.RestoreChannels({channelPoint: (selChannel.channel_point) ? selChannel.channel_point : 'ALL'}));
}
onChannelClick(selRow: Channel, event: any) { onChannelClick(selRow: Channel, event: any) {
const flgButtonsClicked = event.target.className.includes('mat-icon') const flgButtonsClicked = event.target.className.includes('mat-icon')
|| event.target.className.includes('mat-column-backup') || event.target.className.includes('mat-column-backup')

View file

@ -0,0 +1,51 @@
<div fxLayout="column">
<div class="padding-gap" *ngIf="allRestoreExists">
<mat-card>
<mat-card-header>
<mat-card-subtitle>
<h2>Channels Restore</h2>
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxFlex="100" fxLayoutAlign="space-between start">
<h4 fxFlex="100">Restore folder location: {{selNode.channelBackupPath}}/restore</h4>
<button fxFlex="49" fxLayoutAlign="center center" mat-raised-button color="primary" tabindex="1" (click)="onRestoreChannels({})">All Channels Restore</button>
</div>
</mat-card-content>
</mat-card>
</div>
<div class="padding-gap">
<mat-card>
<mat-card-header *ngIf="!allRestoreExists">
<mat-card-subtitle>
<h2>Channels Restore</h2>
<h4 fxLayout="row" fxLayoutAlign="start start" class="pt-2">Restore folder location: {{selNode.channelBackupPath}}/restore</h4>
</mat-card-subtitle>
</mat-card-header>
<mat-card-content class="table-card-content" *ngIf="!channels || channels.data.length<=0">Backup file/s not found!<br>To perform channel restoration, channel backup file/s must be placed at the above location.</mat-card-content>
<mat-card-content class="table-card-content" *ngIf="channels && channels.data.length>0">
<div fxLayout="row" fxLayoutAlign="start start">
<mat-form-field fxFlex="30">
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
</div>
<div perfectScrollbar class="table-container mat-elevation-z8">
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar>
<table mat-table #table [dataSource]="channels" matSort [ngClass]="{'mat-elevation-z8 overflow-x-auto error-border': flgLoading[0]==='error','mat-elevation-z8 overflow-x-auto': true}">
<ng-container matColumnDef="chan_point">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Channel Point </th>
<td mat-cell *matCellDef="let channel">{{channel?.channel_point}}</td>
</ng-container>
<ng-container matColumnDef="restore">
<th mat-header-cell *matHeaderCellDef>Restore</th>
<td mat-cell *matCellDef="let channel"><mat-icon color="primary" (click)="onRestoreChannels(channel)">restore</mat-icon></td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</mat-card-content>
</mat-card>
</div>
</div>

View file

@ -0,0 +1,12 @@
.mat-column-chan_point {
flex: 1 1 25%;
min-width: 100px;
max-width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
table {
width:100%;
}

View file

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

View file

@ -0,0 +1,80 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatTableDataSource, MatSort } from '@angular/material';
import { SelNodeChild } from '../../../shared/models/RTLconfig';
import { Channel } from '../../../shared/models/lndModels';
import { LoggerService } from '../../../shared/services/logger.service';
import { LNDEffects } from '../../../lnd/store/lnd.effects';
import { RTLEffects } from '../../../store/rtl.effects';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@Component({
selector: 'rtl-channel-restore',
templateUrl: './channel-restore.component.html',
styleUrls: ['./channel-restore.component.scss']
})
export class ChannelRestoreComponent implements OnInit {
@ViewChild(MatSort, { static: true }) sort: MatSort;
public selNode: SelNodeChild = {};
public displayedColumns = ['chan_point', 'restore'];
public selChannel: Channel;
public channels: any;
public allRestoreExists = false;
public flgLoading: Array<Boolean | 'error'> = [true]; // 0: channels
public flgSticky = false;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private lndEffects: LNDEffects, private actions$: Actions) {}
ngOnInit() {
this.store.dispatch(new RTLActions.RestoreChannelsList());
this.store.select('lnd')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
this.selNode = rtlStore.nodeSettings;
// rtlStore.effectErrorsLnd.forEach(effectsErr => {
// if (effectsErr.action === 'RestoreChannelsList') {
// this.flgLoading[0] = 'error';
// }
// });
this.logger.info(rtlStore);
});
this.lndEffects.setRestoreChannelList
.pipe(takeUntil(this.unSubs[0]))
.subscribe((resRCList) => {
this.allRestoreExists = resRCList.all_restore_exists;
this.channels = new MatTableDataSource([...resRCList.files]);
this.channels.data = resRCList.files;
this.channels.sort = this.sort;
if (this.flgLoading[0] !== 'error' || (resRCList && resRCList.files)) {
this.flgLoading[0] = false;
}
this.logger.info(resRCList);
});
}
onRestoreChannels(selChannel: Channel) {
this.store.dispatch(new RTLActions.OpenSpinner('Restoring Channels...'));
this.store.dispatch(new RTLActions.RestoreChannels({channelPoint: (selChannel.channel_point) ? selChannel.channel_point : 'ALL'}));
}
applyFilter(selFilter: string) {
this.channels.filter = selFilter;
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

View file

@ -21,6 +21,7 @@ import { RoutingPeersComponent } from './routing-peers/routing-peers.component';
import { ChannelLookupComponent } from './lookups/channel-lookup/channel-lookup.component'; import { ChannelLookupComponent } from './lookups/channel-lookup/channel-lookup.component';
import { NodeLookupComponent } from './lookups/node-lookup/node-lookup.component'; import { NodeLookupComponent } from './lookups/node-lookup/node-lookup.component';
import { ChannelBackupComponent } from './channels/channel-backup/channel-backup.component'; import { ChannelBackupComponent } from './channels/channel-backup/channel-backup.component';
import { ChannelRestoreComponent } from './channels/channel-restore/channel-restore.component';
import { QueryRoutesComponent } from './payments/query-routes/query-routes.component'; import { QueryRoutesComponent } from './payments/query-routes/query-routes.component';
import { CommonService } from '../shared/services/common.service'; import { CommonService } from '../shared/services/common.service';
@ -51,7 +52,8 @@ import { LNDUnlockedGuard } from '../shared/services/auth.guard';
ChannelLookupComponent, ChannelLookupComponent,
NodeLookupComponent, NodeLookupComponent,
ChannelBackupComponent, ChannelBackupComponent,
QueryRoutesComponent QueryRoutesComponent,
ChannelRestoreComponent
], ],
providers: [ providers: [
{ provide: LoggerService, useClass: ConsoleLoggerService }, { provide: LoggerService, useClass: ConsoleLoggerService },

View file

@ -17,6 +17,7 @@ import { LookupsComponent } from './lookups/lookups.component';
import { ForwardingHistoryComponent } from './switch/forwarding-history.component'; import { ForwardingHistoryComponent } from './switch/forwarding-history.component';
import { RoutingPeersComponent } from './routing-peers/routing-peers.component'; import { RoutingPeersComponent } from './routing-peers/routing-peers.component';
import { ChannelBackupComponent } from './channels/channel-backup/channel-backup.component'; import { ChannelBackupComponent } from './channels/channel-backup/channel-backup.component';
import { ChannelRestoreComponent } from './channels/channel-restore/channel-restore.component';
import { LNDUnlockedGuard } from '../shared/services/auth.guard'; import { LNDUnlockedGuard } from '../shared/services/auth.guard';
import { NotFoundComponent } from '../shared/components/not-found/not-found.component'; import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
@ -31,6 +32,7 @@ export const LndRoutes: Routes = [
{ path: 'chnlmanage', component: ChannelManageComponent, canActivate: [LNDUnlockedGuard] }, { path: 'chnlmanage', component: ChannelManageComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'chnlpending', component: ChannelPendingComponent, canActivate: [LNDUnlockedGuard] }, { path: 'chnlpending', component: ChannelPendingComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'chnlbackup', component: ChannelBackupComponent, canActivate: [LNDUnlockedGuard] }, { path: 'chnlbackup', component: ChannelBackupComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'chnlrestore', component: ChannelRestoreComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'transsendreceive', component: SendReceiveTransComponent, canActivate: [LNDUnlockedGuard] }, { path: 'transsendreceive', component: SendReceiveTransComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'translist', component: ListTransactionsComponent, canActivate: [LNDUnlockedGuard] }, { path: 'translist', component: ListTransactionsComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'paymentsend', component: PaymentsComponent, canActivate: [LNDUnlockedGuard] }, { path: 'paymentsend', component: PaymentsComponent, canActivate: [LNDUnlockedGuard] },

View file

@ -448,7 +448,7 @@ export class LNDEffects implements OnDestroy {
payload: { payload: {
width: '70%', data: { width: '70%', data: {
type: 'ERROR', titleMessage: 'Unable to Restore Channel. Try again later.', type: 'ERROR', titleMessage: 'Unable to Restore Channel. Try again later.',
message: JSON.stringify({ code: err.status, Message: err.error.message }) message: JSON.stringify({ code: err.status, Message: err.error.error })
} }
} }
} }
@ -1166,6 +1166,51 @@ export class LNDEffects implements OnDestroy {
}) })
); );
@Effect()
getRestoreChannelList = this.actions$.pipe(
ofType(RTLActions.RESTORE_CHANNELS_LIST),
mergeMap((action: RTLActions.RestoreChannelsList) => {
this.store.dispatch(new RTLActions.ClearEffectErrorLnd('RestoreChannelsList'));
return this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/restore/list')
.pipe(
map((resRestoreList) => {
this.logger.info(resRestoreList);
this.store.dispatch(new RTLActions.CloseSpinner());
return {
type: RTLActions.SET_RESTORE_CHANNELS_LIST,
payload: (resRestoreList) ? resRestoreList : {all_restore_exists: false, files: []}
};
}),
catchError((err: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: 'RestoreChannelsList', code: err.status, message: err.error.message }));
this.logger.error(err);
return of(
{
type: RTLActions.OPEN_ALERT,
payload: {
width: '70%', data: {
type: 'ERROR', titleMessage: 'Restore Channels List Failed',
message: JSON.stringify({ Code: err.status, Message: err.error, URL: this.CHILD_API_URL + environment.CHANNELS_BACKUP_API })
}
}
}
);
})
);
})
);
@Effect({ dispatch: false })
setRestoreChannelList = this.actions$.pipe(
ofType(RTLActions.SET_RESTORE_CHANNELS_LIST),
map((action: RTLActions.SetRestoreChannelsList) => {
this.logger.info(action.payload);
return action.payload;
})
);
ngOnDestroy() { ngOnDestroy() {
this.unSubs.forEach(completeSub => { this.unSubs.forEach(completeSub => {
completeSub.next(); completeSub.next();

View file

@ -24,8 +24,8 @@
</div> </div>
</div> </div>
</mat-tree-node> </mat-tree-node>
<mat-nested-tree-node fxLayout="column" *matTreeNodeDef="let node; when: hasChild" matTreeNodeToggle routerLink="{{node.link}}"> <mat-nested-tree-node fxLayout="column" *matTreeNodeDef="let node; when: hasChild" matTreeNodeToggle>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start center" class="mat-nested-tree-node-parent" (click)="onParentNodeClicked()"> <div fxLayout="row" fxFlex="100" fxLayoutAlign="start center" class="mat-nested-tree-node-parent">
<div fxFlex="89" fxLayoutAlign="start center"> <div fxFlex="89" fxLayoutAlign="start center">
<mat-icon class="mr-1" matTooltip="{{node.name}}" matTooltipPosition="right" [matTooltipDisabled]="settings.menuType !== 'Mini'">{{node.icon}}</mat-icon><span *ngIf="settings.menuType !== 'Mini'">{{node.name}}</span> <mat-icon class="mr-1" matTooltip="{{node.name}}" matTooltipPosition="right" [matTooltipDisabled]="settings.menuType !== 'Mini'">{{node.icon}}</mat-icon><span *ngIf="settings.menuType !== 'Mini'">{{node.name}}</span>
</div> </div>
@ -46,12 +46,12 @@
</mat-tree> </mat-tree>
<mat-tree [dataSource]="navMenus" [treeControl]="treeControlNested" class="example-tree" *ngIf="settings.menuType === 'Compact'"> <mat-tree [dataSource]="navMenus" [treeControl]="treeControlNested" class="example-tree" *ngIf="settings.menuType === 'Compact'">
<mat-tree-node fxLayout="column" fxLayoutAlign="center center" *matTreeNodeDef="let node" matTreeNodeToggle (click)="onChildNavClicked(node)" routerLinkActive="active-link" routerLink="{{node.link}}"> <mat-tree-node fxLayout="column" matTreeNodeToggle fxLayoutAlign="center center" *matTreeNodeDef="let node" (click)="onChildNavClicked(node)" routerLinkActive="active-link" routerLink="{{node.link}}">
<mat-icon class="mat-icon-36">{{node.icon}}</mat-icon> <mat-icon class="mat-icon-36">{{node.icon}}</mat-icon>
<span>{{node.name}}<span *ngIf="node.name === 'Pending'" [matBadgeHidden]="numPendingChannels<1" matBadge="{{numPendingChannels}}" matBadgeOverlap="false" matBadgeColor="accent"></span></span> <span>{{node.name}}<span *ngIf="node.name === 'Pending'" [matBadgeHidden]="numPendingChannels<1" matBadge="{{numPendingChannels}}" matBadgeOverlap="false" matBadgeColor="accent"></span></span>
</mat-tree-node> </mat-tree-node>
<mat-nested-tree-node fxLayout="column" *matTreeNodeDef="let node; when: hasChild" matTreeNodeToggle routerLink="{{node.link}}"> <mat-nested-tree-node fxLayout="column" *matTreeNodeDef="let node; when: hasChild" matTreeNodeToggle>
<div class="ml-8" fxLayout="column" fxLayoutAlign="center center" class="mat-nested-tree-node-parent"> <div class="ml-8" fxLayout="column" fxLayoutAlign="center center" class="mat-nested-tree-node-parent">
<mat-icon class="mat-icon-36">{{node.icon}}</mat-icon> <mat-icon class="mat-icon-36">{{node.icon}}</mat-icon>
<span>{{node.name}}</span> <span>{{node.name}}</span>

View file

@ -113,16 +113,10 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
}); });
} }
this.ChildNavClicked.emit(node); this.ChildNavClicked.emit(node);
this.treeControlNested.collapseAll();
} }
onChildNavClicked(node) { onChildNavClicked(node) {
this.ChildNavClicked.emit(node); this.ChildNavClicked.emit(node);
this.treeControlNested.collapseAll();
}
onParentNodeClicked() {
this.treeControlNested.collapseAll();
} }
ngOnDestroy() { ngOnDestroy() {

View file

@ -10,7 +10,8 @@ export const MENU_DATA: MenuRootNode = {
{id: 41, parentId: 4, name: 'Management', icon: 'subtitles', link: '/lnd/chnlmanage'}, {id: 41, parentId: 4, name: 'Management', icon: 'subtitles', link: '/lnd/chnlmanage'},
{id: 42, parentId: 4, name: 'Pending', icon: 'watch', link: '/lnd/chnlpending'}, {id: 42, parentId: 4, name: 'Pending', icon: 'watch', link: '/lnd/chnlpending'},
{id: 43, parentId: 4, name: 'Closed', icon: 'watch_later', link: '/lnd/chnlclosed'}, {id: 43, parentId: 4, name: 'Closed', icon: 'watch_later', link: '/lnd/chnlclosed'},
{id: 44, parentId: 4, name: 'Backup', icon: 'cloud_circle', link: '/lnd/chnlbackup'} {id: 44, parentId: 4, name: 'Backup', icon: 'cloud_circle', link: '/lnd/chnlbackup'},
{id: 45, parentId: 4, name: 'Restore', icon: 'restore', link: '/lnd/chnlrestore'}
]}, ]},
{id: 5, parentId: 0, name: 'Payments', icon: 'payment', link: '/lnd/paymentsend', children: [ {id: 5, parentId: 0, name: 'Payments', icon: 'payment', link: '/lnd/paymentsend', children: [
{id: 51, parentId: 5, name: 'Send', icon: 'send', link: '/lnd/paymentsend'}, {id: 51, parentId: 5, name: 'Send', icon: 'send', link: '/lnd/paymentsend'},

View file

@ -60,6 +60,8 @@ export const BACKUP_CHANNELS = 'BACKUP_CHANNELS';
export const VERIFY_CHANNELS = 'VERIFY_CHANNELS'; export const VERIFY_CHANNELS = 'VERIFY_CHANNELS';
export const BACKUP_CHANNELS_RES = 'BACKUP_CHANNELS_RES'; export const BACKUP_CHANNELS_RES = 'BACKUP_CHANNELS_RES';
export const VERIFY_CHANNELS_RES = 'VERIFY_CHANNELS_RES'; export const VERIFY_CHANNELS_RES = 'VERIFY_CHANNELS_RES';
export const RESTORE_CHANNELS_LIST = 'RESTORE_CHANNELS_LIST';
export const SET_RESTORE_CHANNELS_LIST = 'SET_RESTORE_CHANNELS_LIST';
export const RESTORE_CHANNELS = 'RESTORE_CHANNELS'; export const RESTORE_CHANNELS = 'RESTORE_CHANNELS';
export const RESTORE_CHANNELS_RES = 'RESTORE_CHANNELS_RES'; export const RESTORE_CHANNELS_RES = 'RESTORE_CHANNELS_RES';
export const FETCH_INVOICES = 'FETCH_INVOICES'; export const FETCH_INVOICES = 'FETCH_INVOICES';
@ -394,6 +396,15 @@ export class VerifyChannelsRes implements Action {
constructor(public payload: string) {} constructor(public payload: string) {}
} }
export class RestoreChannelsList implements Action {
readonly type = RESTORE_CHANNELS_LIST;
}
export class SetRestoreChannelsList implements Action {
readonly type = SET_RESTORE_CHANNELS_LIST;
constructor(public payload: {all_restore_exists: boolean, files: []}) {}
}
export class RestoreChannels implements Action { export class RestoreChannels implements Action {
readonly type = RESTORE_CHANNELS; readonly type = RESTORE_CHANNELS;
constructor(public payload: {channelPoint: string}) {} constructor(public payload: {channelPoint: string}) {}
@ -807,7 +818,8 @@ export type RTLActions =
FetchNetwork | SetNetwork | FetchNetwork | SetNetwork |
FetchChannels | SetChannels | SetPendingChannels | SetClosedChannels | UpdateChannels | FetchChannels | SetChannels | SetPendingChannels | SetClosedChannels | UpdateChannels |
SaveNewChannel | CloseChannel | RemoveChannel | SaveNewChannel | CloseChannel | RemoveChannel |
BackupChannels | VerifyChannels | BackupChannelsRes | VerifyChannelsRes | RestoreChannels | RestoreChannelsRes | BackupChannels | VerifyChannels | BackupChannelsRes | VerifyChannelsRes |
RestoreChannels | RestoreChannelsRes | RestoreChannelsList | SetRestoreChannelsList |
FetchTransactions | SetTransactions | FetchTransactions | SetTransactions |
FetchInvoices | SetInvoices | SetTotalInvoices | FetchInvoices | SetInvoices | SetTotalInvoices |
FetchPayments | SetPayments | SendPayment | FetchPayments | SetPayments | SendPayment |

View file

@ -254,11 +254,11 @@ export class RTLEffects implements OnDestroy {
map((postRes: any) => { map((postRes: any) => {
this.logger.info(postRes); this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.CloseSpinner());
let selNode = { channelBackupPath: action.payload.lnNode.settings.channelBackupPath, satsToBTC: action.payload.lnNode.settings.satsToBTC };
this.store.dispatch(new RTLActions.ResetRootStore(action.payload.lnNode));
this.store.dispatch(new RTLActions.ResetLNDStore(selNode));
this.store.dispatch(new RTLActions.ResetCLStore(selNode));
if (sessionStorage.getItem('token')) { if (sessionStorage.getItem('token')) {
let selNode = { channelBackupPath: action.payload.lnNode.settings.channelBackupPath, satsToBTC: action.payload.lnNode.settings.satsToBTC };
this.store.dispatch(new RTLActions.ResetRootStore(action.payload.lnNode));
this.store.dispatch(new RTLActions.ResetLNDStore(selNode));
this.store.dispatch(new RTLActions.ResetCLStore(selNode));
let newRoute = this.location.path(); let newRoute = this.location.path();
if(action.payload.lnNode.lnImplementation.toUpperCase() === 'CLT') { if(action.payload.lnNode.lnImplementation.toUpperCase() === 'CLT') {
if(newRoute.includes('/lnd/')) { if(newRoute.includes('/lnd/')) {