Eclair Implementation (#371)

Eclair Implementation, Closing issue #224
pull/372/head
ShahanaFarooqui 4 years ago committed by GitHub
parent f83fa33a34
commit 1b86a8bc84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

1
.gitignore vendored

@ -6,6 +6,7 @@
# dependencies
/node_modules
/node_modules_old
# IDEs and editors
/.idea

@ -5,7 +5,7 @@
[![license](https://img.shields.io/github/license/DAVFoundation/captain-n3m0.svg?style=flat-square)](https://github.com/DAVFoundation/captain-n3m0/blob/master/LICENSE)
### Stable Release: v0.7.1
**Intro** -- [Application Features](docs/Application_features.md) -- [Road Map](docs/Roadmap.md) -- [LND API Coverage](docs/LNDAPICoverage.md) -- [Application Configurations](docs/Application_configurations) -- [C-Lightning](docs/C-Lightning-setup.md)
**Intro** -- [Application Features](docs/Application_features.md) -- [Road Map](docs/Roadmap.md) -- [LND API Coverage](docs/LNDAPICoverage.md) -- [Application Configurations](docs/Application_configurations) -- [C-Lightning](docs/C-Lightning-setup.md) -- [Eclair](docs/Eclair-setup.md)
* [Introduction](#intro)
* [Architecture](#arch)
@ -18,9 +18,9 @@
### <a name="intro"></a>Introduction
RTL is a full function, device agnostic, web user interface to help manage lightning node operations.
RTL is available on LND and C-Lightning implementations.
RTL is available on LND, C-Lightning and Eclair implementations.
This page covers instructions for LND. For C-Lightning, refer to [this](docs/C-Lightning-setup.md) page.
This page covers instructions for LND. For C-Lightning, refer to [this](docs/C-Lightning-setup.md) page. For Eclair, refer to [this](docs/Eclair-setup.md) page.
Lightning Network Daemon(LND) is an implementation of Lightning Network BOLT protocol by [Lightning Labs](https://lightning.engineering/).

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -30,7 +30,7 @@ MIT
MIT
The MIT License
Copyright (c) 2019 Google LLC.
Copyright (c) 2020 Google LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -61,7 +61,7 @@ MIT
MIT
The MIT License
Copyright (c) 2019 Google LLC.
Copyright (c) 2020 Google LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -89,7 +89,7 @@ MIT
MIT
The MIT License
Copyright (c) 2019 Google LLC.
Copyright (c) 2020 Google LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -110,64 +110,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@angular/material/autocomplete
@angular/material/button
@angular/material/card
@angular/material/checkbox
@angular/material/core
@angular/material/datepicker
@angular/material/dialog
@angular/material/divider
@angular/material/expansion
@angular/material/form-field
@angular/material/grid-list
@angular/material/icon
@angular/material/list
@angular/material/menu
@angular/material/paginator
@angular/material/progress-bar
@angular/material/progress-spinner
@angular/material/radio
@angular/material/select
@angular/material/sidenav
@angular/material/slide-toggle
@angular/material/snack-bar
@angular/material/sort
@angular/material/stepper
@angular/material/table
@angular/material/tabs
@angular/material/toolbar
@angular/material/tooltip
@angular/material/tree
@angular/platform-browser
MIT
@ -297,12 +239,65 @@ MIT
@ngrx/store
MIT
@ngrx/store-devtools
MIT
angular-user-idle
MIT
angularx-qrcode
MIT
base64-js
MIT
The MIT License (MIT)
Copyright (c) 2014 Jameson Little
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
buffer
MIT
The MIT License (MIT)
Copyright (c) Feross Aboukhadijeh, and other contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
convert-hex
convert-string
@ -330,6 +325,30 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
css-loader
MIT
Copyright JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
dijkstrajs
MIT
```
@ -378,6 +397,21 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
ieee754
BSD-3-Clause
Copyright 2008 Fair Oaks Labs, Inc.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
isarray
MIT
MIT License
@ -455,7 +489,7 @@ SOFTWARE.
perfect-scrollbar
MIT
The MIT License (MIT) Copyright (c) 2012-2017 Hyunje Jun and other contributors
The MIT License (MIT) Copyright (c) 2012-2019 Hyunje Jun, MDBootstrap.com and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@ -749,69 +783,25 @@ Apache-2.0
sha256
tslib
Apache-2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
0BSD
Copyright (c) Microsoft Corporation.
END OF TERMS AND CONDITIONS
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
zone.js
MIT
The MIT License
Copyright (c) 2016-2018 Google, Inc.
Copyright (c) 2010-2020 Google LLC. http://angular.io/license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

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.da5d02955b06fbd16881.css"></head>
<link rel="stylesheet" href="styles.6830931f3c22f4338345.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.9c469fe6b4e77e4e8274.js" defer></script><script src="polyfills-es5.2ae7ace69949ec0a3f00.js" nomodule defer></script><script src="polyfills.3302e98effc5e50a54c2.js" defer></script><script src="main.6f088743ff52235d1682.js" defer></script></body>
<script src="runtime.d3d799d41e64323a3724.js" defer></script><script src="polyfills-es5.c5e45b6cc146403bcd74.js" nomodule defer></script><script src="polyfills.afc43f94c88c0955cb04.js" defer></script><script src="main.fb298156caeda6c0e64a.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

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

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

@ -0,0 +1 @@
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],f=r[2],p=0,d=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&d.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);d.length;)d.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:"bd61e6826dbafb306978",6:"04daddbdac8cd62e9aea",7:"4a4cbdfbbc02f4356464",8:"65561c0d66d24e36d541"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -9,6 +9,7 @@ const baseHref = "/rtl/";
const apiRoot = baseHref + "api/";
const apiLNDRoot = baseHref + "api/lnd/";
const apiCLRoot = baseHref + "api/cl/";
const apiECLRoot = baseHref + "api/ecl/";
const authenticateRoutes = require("./routes/authenticate");
const RTLConfRoutes = require("./routes/RTLConf");
@ -40,6 +41,15 @@ const peersCLRoutes = require("./routes/c-lightning/peers");
const networkCLRoutes = require("./routes/c-lightning/network");
const messageCLRoutes = require("./routes/c-lightning/message");
const infoECLRoutes = require("./routes/eclair/getInfo");
const feesECLRoutes = require("./routes/eclair/fees");
const channelsECLRoutes = require("./routes/eclair/channels");
const onChainECLRoutes = require("./routes/eclair/onchain");
const peersECLRoutes = require("./routes/eclair/peers");
const invoicesECLRoutes = require("./routes/eclair/invoices");
const paymentsECLRoutes = require("./routes/eclair/payments");
const networkECLRoutes = require("./routes/eclair/network");
app.use(cookieParser(common.secret_key));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
@ -90,6 +100,15 @@ app.use(apiCLRoot + "peers", peersCLRoutes);
app.use(apiCLRoot + "network", networkCLRoutes);
app.use(apiCLRoot + "message", messageCLRoutes);
app.use(apiECLRoot + "getinfo", infoECLRoutes);
app.use(apiECLRoot + "fees", feesECLRoutes);
app.use(apiECLRoot + "channels", channelsECLRoutes);
app.use(apiECLRoot + "onchain", onChainECLRoutes);
app.use(apiECLRoot + "peers", peersECLRoutes);
app.use(apiECLRoot + "invoices", invoicesECLRoutes);
app.use(apiECLRoot + "payments", paymentsECLRoutes);
app.use(apiECLRoot + "network", networkECLRoutes);
app.use((req, res, next) => {
res.sendFile(path.join(__dirname, "angular", "index.html"));
});

@ -1,6 +1,7 @@
var fs = require('fs');
var crypto = require('crypto');
var path = require('path');
var ini = require('ini');
var common = {};
common.rtl_conf_file_path = '';
@ -25,7 +26,7 @@ common.getSelLNServerUrl = () => {
};
common.getOptions = () => {
common.selectedNode.options.method = 'GET';
common.selectedNode.options.method = common.selectedNode.ln_implementation.toUpperCase() !== 'ECL' ? 'GET' : 'POST';
common.selectedNode.options.qs = {};
return common.selectedNode.options;
};
@ -41,10 +42,21 @@ common.updateSelectedNodeOptions = () => {
form: ''
};
try {
if (common.selectedNode && common.selectedNode.ln_implementation && common.selectedNode.ln_implementation.toUpperCase() === 'CLT') {
common.selectedNode.options.headers = { 'macaroon': Buffer.from(fs.readFileSync(path.join(common.selectedNode.macaroon_path, 'access.macaroon'))).toString("base64") };
} else {
common.selectedNode.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(path.join(common.selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') };
if (common.selectedNode && common.selectedNode.ln_implementation) {
switch (common.selectedNode.ln_implementation.toUpperCase()) {
case 'CLT':
common.selectedNode.options.headers = { 'macaroon': Buffer.from(fs.readFileSync(path.join(common.selectedNode.macaroon_path, 'access.macaroon'))).toString("base64") };
break;
case 'ECL':
var eclPwd = ini.parse(fs.readFileSync(common.selectedNode.config_path, 'utf-8'))['eclair.api.password'];
common.selectedNode.options.headers = { 'authorization': 'Basic ' + Buffer.from(':' + eclPwd).toString('base64') };
break;
default:
common.selectedNode.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(path.join(common.selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') };
break;
}
}
return { status: 200, message: 'Updated Successfully!' };
} catch(err) {
@ -70,10 +82,21 @@ common.setOptions = () => {
form: ''
};
try {
if (node.ln_implementation && node.ln_implementation.toUpperCase() === 'CLT') {
node.options.headers = { 'macaroon': Buffer.from(fs.readFileSync(path.join(node.macaroon_path, 'access.macaroon'))).toString("base64") };
} else {
node.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(path.join(node.macaroon_path, 'admin.macaroon')).toString('hex') };
if (node.ln_implementation) {
switch (node.ln_implementation.toUpperCase()) {
case 'CLT':
node.options.headers = { 'macaroon': Buffer.from(fs.readFileSync(path.join(node.macaroon_path, 'access.macaroon'))).toString("base64") };
break;
case 'ECL':
var eclPwd = ini.parse(fs.readFileSync(node.config_path, 'utf-8'))['eclair.api.password'];
node.options.headers = { 'authorization': 'Basic ' + Buffer.from(':' + eclPwd).toString('base64') };
break;
default:
node.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(path.join(node.macaroon_path, 'admin.macaroon')).toString('hex') };
break;
}
}
} catch (err) {
console.error('Common Set Options Error:' + JSON.stringify(err));

@ -115,14 +115,34 @@ connect.validateNodeConfig = (config) => {
if (config.nodes && config.nodes.length > 0) {
config.nodes.forEach((node, idx) => {
common.nodes[idx] = {};
if (process.env.MACAROON_PATH && process.env.MACAROON_PATH.trim() !== '') {
common.nodes[idx].index = node.index;
common.nodes[idx].ln_node = node.lnNode;
common.nodes[idx].ln_implementation = (process.env.LN_IMPLEMENTATION) ? process.env.LN_IMPLEMENTATION : node.lnImplementation ? node.lnImplementation : 'LND';
if (common.nodes[idx].ln_implementation !== 'ECL' && process.env.MACAROON_PATH && process.env.MACAROON_PATH.trim() !== '') {
common.nodes[idx].macaroon_path = process.env.MACAROON_PATH;
} else if(node.Authentication && node.Authentication.macaroonPath && node.Authentication.macaroonPath.trim() !== '') {
} else if(common.nodes[idx].ln_implementation !== 'ECL' && node.Authentication && node.Authentication.macaroonPath && node.Authentication.macaroonPath.trim() !== '') {
common.nodes[idx].macaroon_path = node.Authentication.macaroonPath;
} else {
} else if (common.nodes[idx].ln_implementation !== 'ECL') {
errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Config.json!';
}
if (process.env.CONFIG_PATH) {
common.nodes[idx].config_path = process.env.CONFIG_PATH;
} else if (process.env.LND_CONFIG_PATH) {
common.nodes[idx].config_path = process.env.LND_CONFIG_PATH;
} else if (node.Authentication && node.Authentication.lndConfigPath) {
common.nodes[idx].config_path = node.Authentication.lndConfigPath;
} else if (node.Authentication && node.Authentication.configPath) {
common.nodes[idx].config_path = node.Authentication.configPath;
} else {
common.nodes[idx].config_path = '';
}
if (common.nodes[idx].ln_implementation === 'ECL' && common.nodes[idx].config_path === '') {
errMsg = errMsg + '\nPlease set config path for node index ' + node.index + ' in RTL-Config.json! Eclair authentications with `eclair.api.password` value from the file!';
}
if(process.env.LN_SERVER_URL && process.env.LN_SERVER_URL.trim() !== '') {
common.nodes[idx].ln_server_url = process.env.LN_SERVER_URL;
} else if(process.env.LND_SERVER_URL && process.env.LND_SERVER_URL.trim() !== '') {
@ -134,10 +154,6 @@ connect.validateNodeConfig = (config) => {
} else {
errMsg = errMsg + '\nPlease set LN Server URL for node index ' + node.index + ' in RTL-Config.json!';
}
common.nodes[idx].index = node.index;
common.nodes[idx].ln_node = node.lnNode;
common.nodes[idx].ln_implementation = (process.env.LN_IMPLEMENTATION) ? process.env.LN_IMPLEMENTATION : node.lnImplementation ? node.lnImplementation : 'LND';
common.nodes[idx].user_persona = node.Settings.userPersona ? node.Settings.userPersona : 'MERCHANT';
common.nodes[idx].theme_mode = node.Settings.themeMode ? node.Settings.themeMode : 'DAY';
common.nodes[idx].theme_color = node.Settings.themeColor ? node.Settings.themeColor : 'PURPLE';
@ -145,17 +161,6 @@ connect.validateNodeConfig = (config) => {
if(common.nodes[idx].fiat_conversion) {
common.nodes[idx].currency_unit = node.Settings.currencyUnit ? node.Settings.currencyUnit : 'USD';
}
if (process.env.CONFIG_PATH) {
common.nodes[idx].config_path = process.env.CONFIG_PATH;
} else if (process.env.LND_CONFIG_PATH) {
common.nodes[idx].config_path = process.env.LND_CONFIG_PATH;
} else if (node.Authentication && node.Authentication.lndConfigPath) {
common.nodes[idx].config_path = node.Authentication.lndConfigPath;
} else if (node.Authentication && node.Authentication.configPath) {
common.nodes[idx].config_path = node.Authentication.configPath;
} else {
common.nodes[idx].config_path = '';
}
common.nodes[idx].swap_server_url = process.env.SWAP_SERVER_URL ? process.env.SWAP_SERVER_URL : (node.Settings.swapServerUrl) ? node.Settings.swapServerUrl.trim() : '';
common.nodes[idx].bitcoind_config_path = process.env.BITCOIND_CONFIG_PATH ? process.env.BITCOIND_CONFIG_PATH : (node.Settings.bitcoindConfigPath) ? node.Settings.bitcoindConfigPath : '';
common.nodes[idx].enable_logging = (node.Settings.enableLogging) ? !!node.Settings.enableLogging : false;

@ -184,6 +184,12 @@ exports.getConfig = (req, res, next) => {
if (jsonConfig['rpcpassword']) {
jsonConfig['rpcpassword'] = jsonConfig['rpcpassword'].replace(/./g, '*');
}
if (jsonConfig['eclair.api.password']) {
jsonConfig['eclair.api.password'] = jsonConfig['eclair.api.password'].replace(/./g, '*');
}
if (jsonConfig['eclair.bitcoind.rpcpassword']) {
jsonConfig['eclair.bitcoind.rpcpassword'] = jsonConfig['eclair.bitcoind.rpcpassword'].replace(/./g, '*');
}
if (jsonConfig.multiPass) {
jsonConfig.multiPass = jsonConfig.multiPass.replace(/./g, '*');
}
@ -193,6 +199,23 @@ exports.getConfig = (req, res, next) => {
});
};
exports.getFile = (req, res, next) => {
let file = req.query.path ? req.query.path : (common.selectedNode.channel_backup_path + common.path_separator + 'channel-' + req.query.channel.replace(':', '-') + '.bak');
logger.info({fileName: 'Conf', msg: 'Channel Point: ' + req.query.channel + ', File Path: ' + file});
fs.readFile(file, 'utf8', function(err, data) {
if (err) {
logger.error({fileName: 'Conf', lineNum: 207, msg: 'Reading File Failed!'});
res.status(500).json({
message: "Reading File Failed!",
error: err
});
} else {
logger.info({fileName: 'Conf', msg: 'File Data: ' + data});
res.status(200).json(data);
}
});
};
exports.getCurrencyRates = (req, res, next) => {
options.url = 'https://blockchain.info/ticker';
request(options).then((body) => {

@ -0,0 +1,222 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
const { resolve } = require('path');
var options = {};
simplifyAllChannels = (channels) => {
let channelNodeIds = '';
let simplifiedChannels = [];
channels.forEach(channel => {
channelNodeIds = channelNodeIds + ',' + channel.nodeId;
simplifiedChannels.push({
nodeId: channel.nodeId ? channel.nodeId : '',
channelId: channel.channelId ? channel.channelId : '',
state: channel.state ? channel.state : '',
channelFlags: channel.data && channel.data.commitments && channel.data.commitments.channelFlags ? channel.data.commitments.channelFlags : 0,
toLocal: (channel.data.commitments.localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.localCommit.spec.toLocal/1000) : 0,
toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote/1000) : 0,
shortChannelId: channel.data && channel.data.shortChannelId ? channel.data.shortChannelId : '',
buried: channel.data && channel.data.buried ? channel.data.buried : false,
feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0,
feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0,
alias: ''
});
});
channelNodeIds = channelNodeIds.substring(1);
return new Promise(function(resolve, reject) {
options.url = common.getSelLNServerUrl() + '/nodes';
options.form = { nodeIds: channelNodeIds };
request.post(options).then(function(nodes) {
logger.info({fileName: 'Channels', msg: 'Filtered Nodes: ' + JSON.stringify(nodes)});
simplifiedChannels.map(channel => {
channel.alias = nodes.find(channelWithAlias => channel.nodeId === channelWithAlias.nodeId).alias;
});
resolve(simplifiedChannels);
}).catch(err => {
resolve(simplifiedChannels);
});
});
};
exports.getChannels = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/channels';
options.form = {};
if (req.query && req.query.nodeId) {
options.form = req.query;
logger.info({fileName: 'Channels', msg: 'Channels Node Id: ' + JSON.stringify(options.form)});
}
logger.info({fileName: 'Channels', msg: 'Options: ' + JSON.stringify(options)});
request.post(options).then(function (body) {
logger.info({fileName: 'Channels', msg: 'All Channels: ' + JSON.stringify(body)});
let channelTotal = 0;
let totalLocalBalance = 0;
let totalRemoteBalance = 0;
let lightningBalances = { localBalance: 0, remoteBalance: 0 };
let channelStatus = {active: { channels: 0, capacity: 0 }, inactive: { channels: 0, capacity: 0 }, pending: { channels: 0, capacity: 0 }};
let activeChannels = [];
let pendingChannels = [];
let inactiveChannels = [];
if(body && body.length) {
simplifyAllChannels(body).then(function(simplifiedChannels) {
logger.info({fileName: 'Channels', msg: 'Simplified Channels with Alias: ' + JSON.stringify(simplifiedChannels)});
simplifiedChannels.forEach((channel, i) => {
if (channel.state === 'NORMAL') {
channelTotal = channel.toLocal + channel.toRemote;
totalLocalBalance = totalLocalBalance + channel.toLocal;
totalRemoteBalance = totalRemoteBalance + channel.toRemote;
channel.balancedness = (channelTotal == 0) ? 1 : (1 - Math.abs((channel.toLocal-channel.toRemote)/channelTotal)).toFixed(3);
activeChannels.push(channel);
channelStatus.active.channels = channelStatus.active.channels + 1;
channelStatus.active.capacity = channelStatus.active.capacity + channel.toLocal;
} else if (channel.state.includes('WAIT') || channel.state.includes('CLOSING') || channel.state.includes('SYNCING')) {
channel.state = channel.state.replace(/_/g, ' ');
pendingChannels.push(channel);
channelStatus.pending.channels = channelStatus.pending.channels + 1;
channelStatus.pending.capacity = channelStatus.pending.capacity + channel.toLocal;
} else {
channel.state = channel.state.replace(/_/g, ' ');
inactiveChannels.push(channel);
channelStatus.inactive.channels = channelStatus.inactive.channels + 1;
channelStatus.inactive.capacity = channelStatus.inactive.capacity + channel.toLocal;
}
});
lightningBalances = { localBalance: totalLocalBalance, remoteBalance: totalRemoteBalance };
activeChannels = common.sortDescByKey(activeChannels, 'balancedness');
logger.info({fileName: 'Channels', msg: 'Lightning Balances: ' + JSON.stringify(lightningBalances)});
logger.info({fileName: 'Channels', msg: 'Active Channels: ' + JSON.stringify(activeChannels)});
logger.info({fileName: 'Channels', msg: 'Pending Channels: ' + JSON.stringify(pendingChannels)});
logger.info({fileName: 'Channels', msg: 'Inactive Channels: ' + JSON.stringify(inactiveChannels)});
res.status(200).json({activeChannels: activeChannels, pendingChannels: pendingChannels, inactiveChannels: inactiveChannels, lightningBalances: lightningBalances, channelStatus: channelStatus});
});
} else {
res.status(200).json({activeChannels: activeChannels, pendingChannels: pendingChannels, inactiveChannels: inactiveChannels, lightningBalances: lightningBalances, channelStatus: channelStatus});
}
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Channels', lineNum: 35, msg: 'Get Channels Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: 'Fetching Channels Failed!',
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};
exports.getChannelStats = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/channelstats';
options.form = {};
request.post(options).then((body) => {
logger.info({fileName: 'ChannelStats', msg: 'Channel Stats Response: ' + JSON.stringify(body)});
res.status(201).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'ChannelStats', lineNum: 54, msg: 'Get Channel Stats Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Channel Stats Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
}
exports.openChannel = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/open';
options.form = req.body;
logger.info({fileName: 'Channels', msg: 'Open Channel Params: ' + JSON.stringify(options.form)});
request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Open Channel Response: ' + JSON.stringify(body)});
if(!body || body.error) {
logger.error({fileName: 'Channels', lineNum: 140, msg: 'Open Channel Error: ' + ((!body || !body.error) ? 'Error From Server!' : JSON.stringify(body.error))});
res.status(500).json({
message: 'Open Channel Failed!',
error: (!body) ? 'Error From Server!' : body.error
});
} else {
res.status(201).json(body);
}
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Channels', lineNum: 58, msg: 'Open Channel Failed: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Open Channel Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
}
exports.updateChannelRelayFee = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/updaterelayfee';
options.form = req.query;
logger.info({fileName: 'Channels', msg: 'Update Relay Fee Params: ' + JSON.stringify(options.form)});
request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Update Relay Fee Response: ' + JSON.stringify(body)});
res.status(201).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Channels', lineNum: 186, msg: 'Update Relay Fee Failed: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Update Relay Fee Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
}
exports.closeChannel = (req, res, next) => {
options = common.getOptions();
if (!req.query.force) {
options.url = common.getSelLNServerUrl() + '/close';
} else {
options.url = common.getSelLNServerUrl() + '/forceclose';
}
options.form = { channelId: req.query.channelId };
logger.info({fileName: 'Channels', msg: 'Close Channel Params: ' + JSON.stringify(options.form)});
request.post(options).then((body) => {
logger.info({fileName: 'Channels', msg: 'Close Channel Response: ' + JSON.stringify(body)});
res.status(204).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Channels', lineNum: 217, msg: 'Close Channel Failed: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Close Channel Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
}

@ -0,0 +1,91 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
var options = {};
exports.getFees = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/audit';
tillToday = Math.floor(Date.now() / 1000);
fromLastMonth = tillToday - (86400 * 30);
options.form = {
from: fromLastMonth,
to: tillToday
};
request.post(options).then((body) => {
logger.info({fileName: 'Fees', msg: 'Audit Response: ' + JSON.stringify(body)});
let resBody = {
fees: {daily_fee: 0, daily_txs: 0, weekly_fee: 0, weekly_txs: 0, monthly_fee: 0, monthly_txs: 0 },
payments: {
sent: body && body.sent ? body.sent : [],
received: body && body.received ? body.received : [],
relayed: body && body.relayed ? body.relayed : []
}
};
let current_time = Math.round((new Date().getTime()) / 1000);
let month_start_time = current_time - 2629743;
let week_start_time = current_time - 604800;
let day_start_time = current_time - 86400;
let fee = 0;
resBody.payments.sent.forEach(sentEle => {
if (sentEle.recipientAmount) { sentEle.recipientAmount = Math.round(sentEle.recipientAmount/1000); }
if (sentEle.parts && sentEle.parts.length > 0) {
sentEle.firstPartTimestamp = sentEle.parts[0].timestamp;
sentEle.firstPartTimestampStr = (!sentEle.firstPartTimestamp) ? '' : common.convertTimestampToDate(Math.round(sentEle.firstPartTimestamp / 1000));
}
sentEle.parts.forEach(part => {
part.timestampStr = (!part.timestamp) ? '' : common.convertTimestampToDate(Math.round(part.timestamp / 1000));
if (part.amount) { part.amount = Math.round(part.amount/1000); }
if (part.feesPaid) { part.feesPaid = Math.round(part.feesPaid/1000); }
});
});
resBody.payments.received.forEach(receivedEle => {
if (receivedEle.parts && receivedEle.parts.length > 0) {
receivedEle.firstPartTimestamp = receivedEle.parts[0].timestamp;
receivedEle.firstPartTimestampStr = (!receivedEle.firstPartTimestamp) ? '' : common.convertTimestampToDate(Math.round(receivedEle.firstPartTimestamp / 1000));
}
receivedEle.parts.forEach(part => {
part.timestampStr = (!part.timestamp) ? '' : common.convertTimestampToDate(Math.round(part.timestamp / 1000));
if (part.amount) { part.amount = Math.round(part.amount/1000); }
});
});
resBody.payments.relayed.forEach(relayedEle => {
logger.info({fileName: 'Fees', msg: 'Relayed Transaction: ' + JSON.stringify(relayedEle)});
relayedEle.timestampStr = (!relayedEle.timestamp) ? '' : common.convertTimestampToDate(Math.round(relayedEle.timestamp / 1000));
if (relayedEle.amountIn) { relayedEle.amountIn = Math.round(relayedEle.amountIn/1000); }
if (relayedEle.amountOut) { relayedEle.amountOut = Math.round(relayedEle.amountOut/1000); }
fee = relayedEle.amountIn - relayedEle.amountOut;
if (relayedEle.timestamp >= day_start_time) {
resBody.fees.daily_fee = resBody.fees.daily_fee + fee;
resBody.fees.daily_txs = resBody.fees.daily_txs + 1;
}
if (relayedEle.timestamp >= week_start_time) {
resBody.fees.weekly_fee = resBody.fees.weekly_fee + fee;
resBody.fees.weekly_txs = resBody.fees.weekly_txs + 1;
}
if (relayedEle.timestamp >= month_start_time) {
resBody.fees.monthly_fee = resBody.fees.monthly_fee + fee;
resBody.fees.monthly_txs = resBody.fees.monthly_txs + 1;
}
});
resBody.payments.sent = common.sortDescByKey(resBody.payments.sent, 'firstPartTimestamp');
resBody.payments.received = common.sortDescByKey(resBody.payments.received, 'firstPartTimestamp');
resBody.payments.relayed = common.sortDescByKey(resBody.payments.relayed, 'timestamp');
logger.info({fileName: 'Fees', msg: JSON.stringify(resBody)});
res.status(200).json(resBody);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Fees', lineNum: 57, msg: 'Get Fees Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Fetching Fees failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};

@ -0,0 +1,44 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
var options = {};
exports.getInfo = (req, res, next) => {
common.setOptions();
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getinfo';
options.form = {};
logger.info({fileName:'GetInfo', msg: 'Selected Node: ' + JSON.stringify(common.selectedNode.ln_node)});
logger.info({fileName: 'GetInfo', msg: 'Calling Info from Eclair server url: ' + options.url});
if (!options.headers || !options.headers.authorization) {
logger.error({fileName: 'GetInfo', lineNum: 13, msg: 'Eclair Get info failed due to missing or wrong password!'});
res.status(502).json({
message: "Fetching Info Failed!",
error: "Missing Or Wrong Password"
});
} else {
request.post(options).then((body) => {
logger.info({fileName: 'GetInfo', msg: JSON.stringify(body)});
const body_str = (!body) ? '' : JSON.stringify(body);
const search_idx = (!body) ? -1 : body_str.search('Not Found');
body.currency_unit = 'BTC';
body.smaller_currency_unit = 'Sats';
body.lnImplementation = 'Eclair';
res.status(200).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'GetInfo', lineNum: 57, msg: 'Get Info Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Fetching Info failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
}
};

@ -0,0 +1,116 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
var options = {};
var pendingInvoices = [];
getReceivedPaymentInfo = (invoice) => {
let idx = -1;
return new Promise(function(resolve, reject) {
invoice.timestampStr = (!invoice.timestamp) ? '' : common.convertTimestampToDate(invoice.timestamp);
invoice.expiresAt = (!invoice.expiry) ? null : (+invoice.timestamp + +invoice.expiry);
invoice.expiresAtStr = (!invoice.expiresAt) ? '' : common.convertTimestampToDate(invoice.expiresAt);
if (invoice.amount) { invoice.amount = Math.round(invoice.amount/1000); }
idx = pendingInvoices.findIndex(pendingInvoice => invoice.serialized === pendingInvoice.serialized);
if (idx < 0) {
options.url = common.getSelLNServerUrl() + '/getreceivedinfo';
options.form = { paymentHash: invoice.paymentHash };
request(options).then(response => {
invoice.status = response.status.type;
invoice.amount = Math.round(response.status.amount/1000);
if (response.status.receivedAt) {
invoice.receivedAt = Math.round(response.status.receivedAt / 1000);
invoice.receivedAtStr = common.convertTimestampToDate(invoice.receivedAt);
}
resolve(invoice);
}).catch(err => {
invoice.status = 'unknown';
resolve(invoice);
});
} else {
pendingInvoices.splice(idx, 1);
invoice.status = 'unpaid';
resolve(invoice);
}
});
}
exports.listInvoices = (req, res, next) => {
options = common.getOptions();
options.form = {};
options1 = JSON.parse(JSON.stringify(options));
options1.url = common.getSelLNServerUrl() + '/listinvoices';
options1.form = {};
options2 = JSON.parse(JSON.stringify(options));
options2.url = common.getSelLNServerUrl() + '/listpendinginvoices';
options2.form = {};
Promise.all([request(options1), request(options2)])
.then(body => {
logger.info({fileName: 'Invoice', msg: 'Invoices List Received: ' + JSON.stringify(body)});
let invoices = (!body[0] || body[0].length <= 0) ? [] : body[0];
pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1];
if (invoices && invoices.length > 0) {
Promise.all(invoices.map(invoice => getReceivedPaymentInfo(invoice)))
.then(values => {
body = common.sortDescByKey(invoices, 'expiresAt');
logger.info({fileName: 'Invoice', msg: 'Final Invoices List: ' + JSON.stringify(invoices)});
res.status(200).json(invoices);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Invoice', lineNum: 66, msg: 'List Invoices Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Fetching Invoices failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
} else {
res.status(200).json([]);
}
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Invoice', lineNum: 84, msg: 'List Invoices Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Fetching Invoices failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};
exports.createInvoice = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/createinvoice';
options.form = req.body;
request.post(options).then((body) => {
logger.info({fileName: 'Invoice', msg: 'Create Invoice Response: ' + JSON.stringify(body)});
if (body.amount) { body.amount = Math.round(body.amount/1000); }
res.status(201).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Invoice', lineNum: 108, msg: 'Create Invoice Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Create Invoice Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};

@ -0,0 +1,31 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
var options = {};
exports.getNodes = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/nodes';
options.form = { nodeIds: req.params.id };
request.post(options).then(function (body) {
logger.info({fileName: 'Network', msg: 'Node Lookup: ' + JSON.stringify(body)});
body.forEach(node => {
node.timestampStr = (node.timestamp) ? common.convertTimestampToDate(node.timestamp) : '';
});
res.status(200).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Network', lineNum: 49, msg: 'Node Lookup Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: 'Node Lookup Failed!',
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};

@ -0,0 +1,127 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
var options = {};
exports.getNewAddress = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/getnewaddress';
options.form = {};
request.post(options).then((body) => {
logger.info({fileName: 'Onchain', msg: JSON.stringify(body)});
res.status(200).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Onchain', lineNum: 21, msg: 'Get New Address Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Getting New Address failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};
exports.getBalance = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/onchainbalance';
options.form = {};
request.post(options).then((body) => {
logger.info({fileName: 'Onchain', msg: 'Balance Received: ' + JSON.stringify(body)});
if(!body.confirmed) {
body.confirmed = 0;
body.btc_confirmed = 0;
} else {
body.btc_confirmed = common.convertToBTC(body.confirmed);
}
if(!body.unconfirmed) {
body.unconfirmed = 0;
body.btc_unconfirmed = 0;
} else {
body.btc_unconfirmed = common.convertToBTC(body.unconfirmed);
}
body.total = +body.confirmed + +body.unconfirmed;
body.btc_total = +body.btc_confirmed + +body.btc_unconfirmed;
res.status(200).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Onchain', lineNum: 58, msg: 'Fetch Balance Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Fetching balance failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};
exports.getTransactions = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/onchaintransactions';
options.form = {
count: req.query.count,
skip: req.query.skip
};
request.post(options).then((body) => {
logger.info({fileName: 'Transactions', msg: 'Transaction Received: ' + JSON.stringify(body)});
if (body && body.length > 0) {
body.forEach(transaction => {
transaction.timestampStr = (!transaction.timestamp) ? '' : common.convertTimestampToDate(transaction.timestamp);
});
body = common.sortDescByKey(body, 'timestamp');
}
res.status(200).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Onchain', lineNum: 101, msg: 'Get Transactions Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Getting transactions failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};
exports.sendFunds = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/sendonchain';
options.form = {
address: req.body.address,
amountSatoshis: req.body.amount,
confirmationTarget: req.body.blocks
};
request.post(options).then((body) => {
logger.info({fileName: 'Onchain', msg: 'Send Funds Response: ' + JSON.stringify(body)});
res.status(201).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Onchain', lineNum: 129, msg: 'Send Funds Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Send funds failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};

@ -0,0 +1,107 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
var options = {};
getQueryNodes = (nodeIds) => {
return new Promise(function(resolve, reject) {
options.url = common.getSelLNServerUrl() + '/nodes';
options.form = { nodeIds: nodeIds };
request.post(options).then(function(nodes) {
logger.info({fileName: 'Payments', msg: 'Query Nodes: ' + JSON.stringify(nodes)});
resolve(nodes);
}).catch(err => {
resolve([]);
});
});
}
exports.decodePayment = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/parseinvoice';
options.form = { invoice: req.params.invoice };
request.post(options).then((body) => {
logger.info({fileName: 'Payments', msg: 'Payment Decode Received: ' + JSON.stringify(body)});
body.timestampStr = (!body.timestamp) ? '' : common.convertTimestampToDate(body.timestamp);
if (body.amount) { body.amount = Math.round(body.amount/1000); }
res.status(200).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Payments', lineNum: 22, msg: 'Payment Decode Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Payment Decode Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};
exports.postPayment = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/payinvoice';
options.form = req.body;
request.post(options).then((body) => {
logger.info({fileName: 'Payments', msg: 'Send Payment Response: ' + JSON.stringify(body)});
res.status(201).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Payments', lineNum: 46, msg: 'Send Payment Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Send Payment Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};
exports.queryPaymentRoute = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/findroutetonode';
options.form = {
nodeId: req.query.nodeId,
amountMsat: req.query.amountMsat
};
request.post(options).then((body) => {
logger.info({fileName: 'Payments', msg: 'Query Payment Route Received: ' + JSON.stringify(body)});
if (body && body.length) {
let queryRoutes = [];
getQueryNodes(body).then(function(hopsWithAlias) {
let foundPeer = {};
body.map(hop => {
foundPeer = hopsWithAlias.find(hopWithAlias => hop === hopWithAlias.nodeId);
queryRoutes.push({nodeId: hop, alias: foundPeer ? foundPeer.alias : ''});
});
logger.info({fileName: 'Payments', msg: 'Query Routes with Alias: ' + JSON.stringify(queryRoutes)});
res.status(200).json(queryRoutes);
});
} else {
res.status(200).json([]);
}
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Payments', lineNum: 109, msg: 'Query Payment Route Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Query Payment Route Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};

@ -0,0 +1,153 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
var options = {};
getFilteredNodes = (peersNodeIds) => {
return new Promise(function(resolve, reject) {
options.url = common.getSelLNServerUrl() + '/nodes';
options.form = { nodeIds: peersNodeIds };
request.post(options).then(function(nodes) {
logger.info({fileName: 'Peers', msg: 'Filtered Nodes: ' + JSON.stringify(nodes)});
resolve(nodes);
}).catch(err => {
resolve([]);
});
});
}
exports.getPeers = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/peers';
options.form = {};
request.post(options).then(function (body) {
logger.info({fileName: 'Peers', msg: 'Peers Received: ' + JSON.stringify(body)});
if (body && body.length) {
let peersNodeIds = '';
body.forEach(peer => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; });
peersNodeIds = peersNodeIds.substring(1);
getFilteredNodes(peersNodeIds).then(function(peersWithAlias) {
let foundPeer = {};
body.map(peer => {
foundPeer = peersWithAlias.find(peerWithAlias => peer.nodeId === peerWithAlias.nodeId);
peer.alias = foundPeer ? foundPeer.alias : '';
});
body = common.sortDescByStrKey(body, 'alias');
logger.info({fileName: 'Peers', msg: 'Peers with Alias: ' + JSON.stringify(body)});
res.status(200).json(body);
});
} else {
res.status(200).json([]);
}
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Peers', lineNum: 49, msg: 'Get Peers Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: 'Fetching Peers Failed!',
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};
exports.connectPeer = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/connect';
options.form = {};
if (req.query) {
options.form = req.query;
logger.info({fileName: 'Peers', msg: 'Connect Peer Params: ' + JSON.stringify(options.form)});
}
request.post(options, (error, response, body) => {
logger.info({fileName: 'Peers', msg: 'Add Peer Response: ' + JSON.stringify(body)});
if (body === 'already connected') {
return res.status(500).json({
message: "Connect Peer Failed!",
error: "already connected"
});
}
options.url = common.getSelLNServerUrl() + '/peers';
options.form = {};
request.post(options).then(function (body) {
logger.info({fileName: 'Peers', msg: 'Peers Received: ' + JSON.stringify(body)});
if (body && body.length) {
let peersNodeIds = '';
body.forEach(peer => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; });
peersNodeIds = peersNodeIds.substring(1);
getFilteredNodes(peersNodeIds).then(function(peersWithAlias) {
body.map(peer => {
peer.alias = peersWithAlias.find(peerWithAlias => peer.nodeId === peerWithAlias.nodeId).alias;
});
let peers = (body) ? common.sortDescByStrKey(body, 'alias') : [];
peers = common.newestOnTop(peers, 'nodeId', req.query.uri ? req.query.uri.substring(0, req.query.uri.indexOf('@')) : req.query.nodeId ? req.query.nodeId : '');
logger.info({fileName: 'Peers', msg: 'Peer with Newest On Top: ' + JSON.stringify(peers)});
logger.info({fileName: 'Peers', msg: 'Peer Added Successfully'});
res.status(201).json(peers);
});
} else {
res.status(201).json([]);
}
}).catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Peers', lineNum: 89, msg: 'Connect Peer Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Connect Peer Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
}).catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Peers', lineNum: 103, msg: 'Connect Peer Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Connect Peer Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};
exports.deletePeer = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/disconnect';
options.form = {};
if (req.params.nodeId) {
options.form = { nodeId: req.params.nodeId };
logger.info({fileName: 'Peers', msg: 'Disconnect Peer Params: ' + JSON.stringify(options.form)});
}
request.post(options, (error, response, body) => {
logger.info({fileName: 'Peers', msg: 'Disconnect Peer Response: ' + JSON.stringify(body)});
logger.info({fileName: 'Peers', msg: 'Peer Disconnected: ' + req.params.nodeId});
res.status(204).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers.authorization) {
delete err.options.headers.authorization;
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
delete err.response.request.headers.authorization;
}
logger.error({fileName: 'Peers', lineNum: 132, msg: 'Disconnect Peer Error: ' + JSON.stringify(err)});
return res.status(err.statusCode ? err.statusCode : 500).json({
message: "Disconnect Peer Failed!",
error: err.error && err.error.error ? err.error.error : err.error ? err.error : "Unknown Server Error"
});
});
};

@ -17,10 +17,10 @@ parameters have `default` values for initial setup and can be updated after RTL
{
"index": <Incremental node indices starting from 1, Required>,
"lnNode": "<Node name to uniquely identify the node in the UI, Default 'Node 1', Required>",
"lnImplementation": "<LNP implementation, Allowed values LND/CLT. Default 'LND', Required>",
"lnImplementation": "<LNP implementation, Allowed values LND/CLT/ECL. Default 'LND', Required>",
"Authentication": {
"macaroonPath": "<Path for the folder containing 'admin.macaroon' file, Required>",
"configPath": "<Optional:Full path of the lnd.conf file including the file name, if present locally or empty, Optional>"
"macaroonPath": "<Path for the folder containing 'admin.macaroon' (LND)/'access.macaroon' (CLT) file, Required for LND & CLT>",
"configPath": "<Full path of the lnd.conf/c-lightning config/eclair.conf file including the file name, if present locally, Optional for LND & CLT, Mandatory for ECL>"
},
"Settings": {
"userPersona": "<User persona to tailor the data on UI. Allowed values MERCHANT, OPERATOR. Default MERCHANT, Required>",
@ -31,7 +31,7 @@ parameters have `default` values for initial setup and can be updated after RTL
"enableLogging": <Parameter to turn RTL logging off/on. Allowed values - true, false, default false, Required>,
"fiatConversion": <parameter to turn fiat conversion off/on. Allowed values - true, false, default false, Required>,
"currencyUnit": "<Optional: Fiat current Unit for currency conversion, default 'USD' If fiatConversion is true, Required if fiatConversion is true>",
"lnServerUrl": "<Service url for LND/CLightning REST APIs for the node, e.g. https://192.168.0.1:8080/v1 OR https://192.168.0.1:3001/v1. Default 'https://localhost:8080/v1', Required",
"lnServerUrl": "<Service url for LND/CLightning REST APIs for the node, e.g. https://192.168.0.1:8080/v1 OR https://192.168.0.1:3001/v1 OR http://192.168.0.1:8080. Default 'https://localhost:8080/v1', Required",
"swapServerUrl": "<Service url for swap server REST APIs for the node, e.g. http://localhost:8081/v1, Optional>",
}
}
@ -43,11 +43,11 @@ parameters have `default` values for initial setup and can be updated after RTL
;If the environment variables are set, it will take precedence over the parameters in the RTL-Config.json file.
PORT (port number for the rtl node server, default 3000, Required)
HOST (host for the rtl node server, default localhost, Optional)
LN_IMPLEMENTATION (LND, CLT. Default 'LND', Required)
LN_SERVER_URL (LND server URL for REST APIs, default https://localhost:8080/v1) OR LN_SERVER_URL (LN server URL for LNP REST APIs) (Required)
LN_IMPLEMENTATION (LND/CLT/ECL. Default 'LND', Required)
LN_SERVER_URL (LN server URL for LNP REST APIs, default https://localhost:8080/v1) (Required)
SWAP_SERVER_URL (Swap server URL for REST APIs, default http://localhost:8081/v1) (Optional)
CONFIG_PATH (Full path of the lnd.conf file including the file name) OR CONFIG_PATH (Full path of the LNP .conf file including the file name) (Optional)
MACAROON_PATH (Path for the folder containing 'admin.macaroon' file, Required)
CONFIG_PATH (Full path of the LNP .conf file including the file name) (Optional for LND & CLT, Mandatory for ECL)
MACAROON_PATH (Path for the folder containing 'admin.macaroon' (LND)/'access.macaroon' (CLT) file, Required for LND & CLT)
RTL_SSO (1 - single sign on via an external cookie, 0 - stand alone RTL authentication, Optional)
RTL_COOKIE_PATH (Full path of the cookie file including the file name, Required if RTL_SSO=1 else Optional)
LOGOUT_REDIRECT_LINK (URL to re-direct to after logout/timeout from RTL, Required if RTL_SSO=1 else Optional)

@ -0,0 +1,98 @@
![](../screenshots/RTL-ECL-Dashboard.png)
## RTL Eclair setup
* [Introduction](#intro)
* [Pre-requisite](#prereq)
* [Architecture](#arch)
* [Installation](#install)
* [Prep for execution](#prep)
* [Start the server and access the app](#start)
### <a name="intro"></a>Introduction
RTL is now enabled to manage an Eclair node.
Follow the below steps to install and setup RTL to run on Eclair.
### <a name="prereq"></a>Pre-requisites:
1. Functioning Eclair node
2. NodeJS - Can be downloaded [here](https://nodejs.org/en/download)
### <a name="arch"></a>Architecture
![](../screenshots/RTL-ECL-Arch-2.png)
### <a name="install"></a>Installation:
#### First time setup
* Fetch sources from RTL git repository, by executing the below on the command prompt:
`$ git clone https://github.com/Ride-The-Lightning/RTL.git`
* Change directory to RTL folder:
`$ cd RTL`
* Fetch the production dependencies by running:
`$ npm install --only=prod`
#### Or: Update existing build
```
$ cd RTL
$ git reset --hard HEAD
$ git clean -f -d
$ git pull
$ npm install --only=prod
```
### <a name="prep"></a>Prep for Execution
RTL requires its own config file `RTL-Config.json`, to start the server and provide user authentication on the app.
* Rename `sample-RTL-Config.json` file to `RTL-Config.json`.
* Locate the complete path of the readable `eclair.conf` for your node.
* Modify the RTL conf file per the example file below
Ensure that the follow values are correct per your config:
* `lnImplementation` - This should be `ECL`, indicating that RTL is connecting to an Eclair node.
* `lnServerUrl` - complete url with ip address and port of the eclair server.
* `multiPass` - Specify the password (in plain text) to access RTL. This password will be hashed and not stored as plain text.
* `configPath` (Mandatory) - Full path of the folder containing `eclair.conf` including the file name for the basic password authentication through `eclair.api.password`.
```
{
"port": "3000",
"SSO": {
"rtlSSO": 0,
"rtlCookiePath": "",
"logoutRedirectLink": ""
},
"nodes": [
{
"index": 1,
"lnNode": "Eclair Testnet # 1",
"lnImplementation": "ECL",
"Authentication": {
"configPath": "<Mandatory - Config file path, including .conf file, for authentication>"
},
"Settings": {
"userPersona": "OPERATOR",
"themeMode": "DAY",
"themeColor": "PURPLE",
"bitcoindConfigPath": "",
"enableLogging": true,
"fiatConversion": false,
"lnServerUrl": "http://<eclair api server ip address>:port"
}
}
],
"multiPass": <password required for accessing RTL>
}
```
### <a name="start"></a>Start the server and access the app
Run the following command:
`$ node rtl`
If the server started successfully, you should get the below output on the console:
`$ Server is up and running, please open the UI at http://localhost:3000`
Open your browser at the following address: http://localhost:3000 to access the RTL app.
### Detailed config and instructions
For detailed config and access options and other information, view the main readme page.

14760
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "rtl",
"version": "0.7.1-beta",
"version": "0.8.0-beta",
"license": "MIT",
"scripts": {
"ng": "ng",
@ -11,30 +11,31 @@
"server": "nodemon ./rtl.js",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
"e2e": "ng e2e",
"postinstall": "ngcc"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.2.14",
"@angular/cdk": "~8.2.3",
"@angular/common": "~8.2.14",
"@angular/compiler": "~8.2.14",
"@angular/core": "~8.2.14",
"@angular/flex-layout": "^8.0.0-beta.27",
"@angular/forms": "~8.2.14",
"@angular/animations": "~9.1.9",
"@angular/cdk": "~9.2.4",
"@angular/common": "~9.1.9",
"@angular/compiler": "~9.1.9",
"@angular/core": "~9.1.9",
"@angular/flex-layout": "^9.0.0-beta.31",
"@angular/forms": "~9.1.9",
"@angular/http": "^7.2.16",
"@angular/material": "^8.2.3",
"@angular/platform-browser": "~8.2.14",
"@angular/platform-browser-dynamic": "~8.2.14",
"@angular/router": "~8.2.14",
"@angular/material": "^9.2.4",
"@angular/platform-browser": "~9.1.9",
"@angular/platform-browser-dynamic": "~9.1.9",
"@angular/router": "~9.1.9",
"@fortawesome/angular-fontawesome": "^0.5.0",
"@fortawesome/fontawesome-svg-core": "^1.2.27",
"@fortawesome/free-regular-svg-icons": "^5.12.1",
"@fortawesome/free-solid-svg-icons": "^5.12.1",
"@ngrx/effects": "^8.6.0",
"@ngrx/store": "^8.6.0",
"@ngrx/effects": "^9.2.0",
"@ngrx/store": "^9.2.0",
"angular-user-idle": "^2.2.1",
"angularx-qrcode": "^2.1.0",
"angularx-qrcode": "^2.3.4",
"atob": "^2.1.2",
"cookie-parser": "^1.4.4",
"express": "^4.17.1",
@ -47,35 +48,35 @@
"request": "^2.88.0",
"request-promise": "^4.2.5",
"roboto-fontface": "^0.10.0",
"rxjs": "^6.4.0",
"rxjs": "^6.5.5",
"sha256": "^0.2.0",
"tslib": "^1.10.0",
"upper-case": "^1.1.3",
"zone.js": "~0.9.1"
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.803.25",
"@angular/cli": "^8.3.25",
"@angular/compiler-cli": "~8.2.14",
"@angular/language-service": "~8.2.14",
"@ngrx/store-devtools": "^8.6.0",
"@angular-devkit/build-angular": "~0.901.7",
"@angular/cli": "^9.1.7",
"@angular/compiler-cli": "~9.1.9",
"@angular/language-service": "~9.1.9",
"@ngrx/store-devtools": "^9.2.0",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "^5.0.0",
"@types/node": "^12.11.1",
"codelyzer": "^5.1.2",
"dotenv": "^8.2.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "^4.4.1",
"karma": "^5.0.9",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.5.2",
"node-sass": "^4.14.1",
"nodemon": "^2.0.2",
"protractor": "^5.4.3",
"protractor": "^7.0.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.5.3"
"typescript": "~3.8.3"
}
}

@ -7,6 +7,7 @@ router.get("/rtlconf", RTLConfController.getRTLConfig);
router.post("/", authCheck, RTLConfController.updateUISettings);
router.post("/update2FA", authCheck, RTLConfController.update2FASettings);
router.get("/config/:nodeType", authCheck, RTLConfController.getConfig);
router.get("/file", authCheck, RTLConfController.getFile);
router.post("/updateSelNode", RTLConfController.updateSelectedNode);
router.post("/updateDefaultNode", RTLConfController.updateDefaultNode);
router.get("/rates", RTLConfController.getCurrencyRates);

@ -0,0 +1,12 @@
const ChannelsController = require("../../controllers/eclair/channels");
const express = require("express");
const router = express.Router();
const authCheck = require("../authCheck");
router.get("/", authCheck, ChannelsController.getChannels);
router.get("/stats", authCheck, ChannelsController.getChannelStats);
router.post("/", authCheck, ChannelsController.openChannel);
router.post("/updateRelayFee", authCheck, ChannelsController.updateChannelRelayFee);
router.delete("/", authCheck, ChannelsController.closeChannel);
module.exports = router;

@ -0,0 +1,8 @@
const FeesController = require("../../controllers/eclair/fees");
const express = require("express");
const router = express.Router();
const authCheck = require("../authCheck");
router.get("/", authCheck, FeesController.getFees);
module.exports = router;

@ -0,0 +1,8 @@
const infoController = require("../../controllers/eclair/getInfo");
const express = require("express");
const router = express.Router();
const authCheck = require("../authCheck");
router.get("/", authCheck, infoController.getInfo);
module.exports = router;

@ -0,0 +1,9 @@
const invoicesController = require("../../controllers/eclair/invoices");
const express = require("express");
const router = express.Router();
const authCheck = require("../authCheck");
router.get("/", authCheck, invoicesController.listInvoices);
router.post("/", authCheck, invoicesController.createInvoice);
module.exports = router;

@ -0,0 +1,8 @@
const NetworkController = require("../../controllers/eclair/network");
const express = require("express");
const router = express.Router();
const authCheck = require("../authCheck");
router.get("/nodes/:id", authCheck, NetworkController.getNodes);
module.exports = router;

@ -0,0 +1,11 @@
const OnChainController = require("../../controllers/eclair/onchain");
const express = require("express");
const router = express.Router();
const authCheck = require("../authCheck");
router.get("/", authCheck, OnChainController.getNewAddress);
router.get("/balance/", authCheck, OnChainController.getBalance);
router.get("/transactions/", authCheck, OnChainController.getTransactions);
router.post("/", authCheck, OnChainController.sendFunds);
module.exports = router;

@ -0,0 +1,10 @@
const PaymentsController = require("../../controllers/eclair/payments");
const express = require("express");
const router = express.Router();
const authCheck = require("../authCheck");
router.get("/route/", authCheck, PaymentsController.queryPaymentRoute);
router.get("/:invoice", authCheck, PaymentsController.decodePayment);
router.post("/", authCheck, PaymentsController.postPayment);
module.exports = router;

@ -0,0 +1,10 @@
const PeersController = require("../../controllers/eclair/peers");
const express = require("express");
const router = express.Router();
const authCheck = require("../authCheck");
router.get("/", authCheck, PeersController.getPeers);
router.post("/", authCheck, PeersController.connectPeer);
router.delete("/:nodeId", authCheck, PeersController.deletePeer);
module.exports = router;

@ -1,4 +1,4 @@
import { BrowserModule } from '@angular/platform-browser';
import { BrowserModule, HammerModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
@ -7,7 +7,7 @@ import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { MatDialogModule, MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material';
import { MatDialogModule, MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material/dialog';
import { UserIdleModule } from 'angular-user-idle';
import { OverlayContainer } from '@angular/cdk/overlay';
import { routing } from './app.routing';
@ -28,6 +28,7 @@ import { RTLReducer } from './store/rtl.reducers';
import { RTLEffects } from './store/rtl.effects';
import { LNDEffects } from './lnd/store/lnd.effects';
import { CLEffects } from './clightning/store/cl.effects';
import { ECLEffects } from './eclair/store/ecl.effects';
import { LayoutModule } from '@angular/cdk/layout';
import { CLOpenChannelComponent } from './clightning/peers-channels/channels/open-channel-modal/open-channel.component';
import { CLChannelInformationComponent } from './clightning/peers-channels/channels/channel-information-modal/channel-information.component';
@ -54,6 +55,14 @@ import { ErrorMessageComponent } from './shared/components/data-modal/error-mess
import { LoopModalComponent } from './lnd/loop/loop-modal/loop-modal.component';
import { TwoFactorAuthComponent } from './shared/components/data-modal/two-factor-auth/two-factor-auth.component';
import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-token/login-2fa-token.component';
import { ECLInvoiceInformationComponent } from './eclair/transactions/invoice-information-modal/invoice-information.component';
import { ECLPaymentInformationComponent } from './eclair/transactions/payment-information-modal/payment-information.component';
import { ECLOpenChannelComponent } from './eclair/peers-channels/channels/open-channel-modal/open-channel.component';
import { ECLConnectPeerComponent } from './eclair/peers-channels/connect-peer/connect-peer.component';
import { ECLLightningSendPaymentsComponent } from './eclair/transactions/send-payment-modal/send-payment.component';
import { ECLCreateInvoiceComponent } from './eclair/transactions/create-invoice-modal/create-invoice.component';
import { ECLOnChainSendComponent } from './eclair/on-chain/on-chain-send-modal/on-chain-send.component';
import { ECLChannelInformationComponent } from './eclair/peers-channels/channels/channel-information-modal/channel-information.component';
@NgModule({
imports: [
@ -62,24 +71,23 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
SharedModule,
routing,
UserIdleModule.forRoot({idle: 60 * 60, timeout: 1, ping: null}),
StoreModule.forRoot(RTLReducer),
EffectsModule.forRoot([RTLEffects, LNDEffects, CLEffects]),
StoreModule.forRoot(RTLReducer, {
runtimeChecks: {
strictStateImmutability: false,
strictActionImmutability: false
}
}),
EffectsModule.forRoot([RTLEffects, LNDEffects, CLEffects, ECLEffects]),
!environment.production ? StoreDevtoolsModule.instrument() : [],
LayoutModule,
MatDialogModule
MatDialogModule,
HammerModule
],
declarations: [
AppComponent,
CLInvoiceInformationComponent,
InvoiceInformationComponent,
ChannelRebalanceComponent,
OnChainGeneratedAddressComponent,
CLOpenChannelComponent,
CLConnectPeerComponent,
CLLightningSendPaymentsComponent,
CLCreateInvoiceComponent,
CLOnChainSendComponent,
CLChannelInformationComponent,
OpenChannelComponent,
ChannelInformationComponent,
LightningSendPaymentsComponent,
@ -94,34 +102,57 @@ import { LoginTokenComponent } from './shared/components/data-modal/login-2fa-to
TwoFactorAuthComponent,
LoginTokenComponent,
CreateInvoiceComponent,
OnChainSendComponent
],
entryComponents: [
OnChainSendComponent,
CLInvoiceInformationComponent,
InvoiceInformationComponent,
ChannelRebalanceComponent,
OnChainGeneratedAddressComponent,
CLOpenChannelComponent,
CLConnectPeerComponent,
CLLightningSendPaymentsComponent,
CLCreateInvoiceComponent,
CLOnChainSendComponent,
CLChannelInformationComponent,
OpenChannelComponent,
ChannelInformationComponent,
LightningSendPaymentsComponent,
ConnectPeerComponent,
ShowPubkeyComponent,
ECLInvoiceInformationComponent,
ECLPaymentInformationComponent,
ECLOpenChannelComponent,
ECLConnectPeerComponent,
ECLLightningSendPaymentsComponent,
ECLCreateInvoiceComponent,
ECLOnChainSendComponent,
ECLChannelInformationComponent
],
entryComponents: [
SpinnerDialogComponent,
AlertMessageComponent,
ConfirmationMessageComponent,
ErrorMessageComponent,
CloseChannelComponent,
LoopModalComponent,
ShowPubkeyComponent,
TwoFactorAuthComponent,
LoginTokenComponent,
OnChainGeneratedAddressComponent,
CloseChannelComponent,
LoopModalComponent,
InvoiceInformationComponent,
ChannelRebalanceComponent,
OpenChannelComponent,
ConnectPeerComponent,
LightningSendPaymentsComponent,
CreateInvoiceComponent,
OnChainSendComponent
OnChainSendComponent,
ChannelInformationComponent,
CLInvoiceInformationComponent,
CLOpenChannelComponent,
CLConnectPeerComponent,
CLLightningSendPaymentsComponent,
CLCreateInvoiceComponent,
CLOnChainSendComponent,
CLChannelInformationComponent,
ECLInvoiceInformationComponent,
ECLPaymentInformationComponent,
ECLOpenChannelComponent,
ECLConnectPeerComponent,
ECLLightningSendPaymentsComponent,
ECLCreateInvoiceComponent,
ECLOnChainSendComponent,
ECLChannelInformationComponent
],
providers: [
{ provide: LoggerService, useClass: ConsoleLoggerService },

@ -11,6 +11,7 @@ import { AuthGuard } from './shared/services/auth.guard';
export const routes: Routes = [
{ 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] },
{ path: 'help', component: HelpComponent },
{ path: 'login', component: LoginComponent },

@ -5,7 +5,7 @@
<mat-hint fxFlex="40" fxLayoutAlign="start center" class="font-size-90"><strong class="font-weight-900 mr-5px">Local:</strong>{{channelBalances.localBalance || 0 | number:'1.0-0'}} Sats</mat-hint>
<mat-hint fxFlex="20" fxLayoutAlign="center center" class="font-size-90">
<fa-icon [icon]="faBalanceScale" class="mr-3px" matTooltip="Balance Score"></fa-icon>
({{channelBalances.balancedness || 0 | number}})
({{channelBalances?.balancedness || 0 | number}})
</mat-hint>
<mat-hint fxFlex="40" fxLayoutAlign="end center" class="font-size-90"><strong class="font-weight-900 mr-5px">Remote:</strong>{{channelBalances.remoteBalance || 0 | number:'1.0-0'}} Sats</mat-hint>
</div>

@ -2,7 +2,7 @@ import { Component, Input } from '@angular/core';
import { Router } from '@angular/router';
import { faBalanceScale, faDumbbell } from '@fortawesome/free-solid-svg-icons';
import { ChannelCL } from '../../../shared/models/clModels';
import { Channel } from '../../../shared/models/clModels';
@Component({
selector: 'rtl-cl-channel-capacity-info',
@ -12,14 +12,14 @@ import { ChannelCL } from '../../../shared/models/clModels';
export class CLChannelCapacityInfoComponent {
public faBalanceScale = faBalanceScale;
public faDumbbell = faDumbbell;
@Input() channelBalances: {localBalance: number, remoteBalance: number, balancedness: string};
@Input() allChannels: ChannelCL[];
@Input() channelBalances: {localBalance: number, remoteBalance: number, balancedness: number};
@Input() allChannels: Channel[];
@Input() sortBy: string = 'Balance Score';
constructor(private router: Router) {}
goToChannels() {
this.router.navigateByUrl('/lnd/peerschannels');
this.router.navigateByUrl('/cl/peerschannels');
}
}

@ -1,7 +1,7 @@
import { Component, Input } from '@angular/core';
import { Router } from '@angular/router';
import { ChannelCL } from '../../../shared/models/clModels';
import { Channel } from '../../../shared/models/clModels';
@Component({
selector: 'rtl-cl-channel-liquidity-info',
@ -11,12 +11,12 @@ import { ChannelCL } from '../../../shared/models/clModels';
export class CLChannelLiquidityInfoComponent {
@Input() direction: string;
@Input() totalLiquidity: number;
@Input() allChannels: ChannelCL[];
@Input() allChannels: Channel[];
constructor(private router: Router) {}
goToChannels() {
this.router.navigateByUrl('/lnd/peerschannels');
this.router.navigateByUrl('/cl/peerschannels');
}
}

@ -1,5 +1,5 @@
import { Component, OnChanges, Input } from '@angular/core';
import { ChannelsStatusCL } from '../../../shared/models/clModels';
import { ChannelsStatus } from '../../../shared/models/clModels';
@Component({
selector: 'rtl-cl-channel-status-info',
@ -7,7 +7,7 @@ import { ChannelsStatusCL } from '../../../shared/models/clModels';
styleUrls: ['./channel-status-info.component.scss']
})
export class CLChannelStatusInfoComponent implements OnChanges {
@Input() channelsStatus: ChannelsStatusCL = {};
@Input() channelsStatus: ChannelsStatus = {};
constructor() {}

@ -1,5 +1,5 @@
import { Component, Input } from '@angular/core';
import { FeesCL } from '../../../shared/models/clModels';
import { Fees } from '../../../shared/models/clModels';
@Component({
selector: 'rtl-cl-fee-info',
@ -7,7 +7,7 @@ import { FeesCL } from '../../../shared/models/clModels';
styleUrls: ['./fee-info.component.scss']
})
export class CLFeeInfoComponent {
@Input() fees: FeesCL;
@Input() fees: Fees;
totalFees = [{'name': 'Total', 'value': 0}];
}

@ -10,10 +10,11 @@ import { faAngleDoubleDown, faAngleDoubleUp, faChartPie, faBolt, faServer, faNet
import { LoggerService } from '../../shared/services/logger.service';
import { CommonService } from '../../shared/services/common.service';
import { UserPersonaEnum, ScreenSizeEnum } from '../../shared/services/consts-enums-functions';
import { ChannelsStatusCL, GetInfoCL, FeesCL, ChannelCL, BalanceCL, FeeRatesCL } from '../../shared/models/clModels';
import { ChannelsStatus, GetInfo, Fees, Channel, Balance, FeeRates } from '../../shared/models/clModels';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as fromRTLReducer from '../../store/rtl.reducers';
import * as CLActions from '../store/cl.actions';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
@Component({
selector: 'rtl-cl-home',
@ -31,21 +32,21 @@ export class CLHomeComponent implements OnInit, OnDestroy {
public faNetworkWired = faNetworkWired;
public flgChildInfoUpdated = false;
public userPersonaEnum = UserPersonaEnum;
public channelBalances = {localBalance: 0, remoteBalance: 0, balancedness: '0'};
public channelBalances = {localBalance: 0, remoteBalance: 0, balancedness: 0};
public selNode: SelNodeChild = {};
public fees: FeesCL;
public information: GetInfoCL = {};
public totalBalance: BalanceCL = {};
public fees: Fees;
public information: GetInfo = {};
public totalBalance: Balance = {};
public balances = { onchain: -1, lightning: -1, total: 0 };
public allChannels: ChannelCL[] = [];
public channelsStatus: ChannelsStatusCL = {};
public allChannelsCapacity: ChannelCL[] = [];
public allInboundChannels: ChannelCL[] = [];
public allOutboundChannels: ChannelCL[] = [];
public allChannels: Channel[] = [];
public channelsStatus: ChannelsStatus = {};
public allChannelsCapacity: Channel[] = [];
public allInboundChannels: Channel[] = [];
public allOutboundChannels: Channel[] = [];
public totalInboundLiquidity = 0;
public totalOutboundLiquidity = 0;
public feeRatesPerKB: FeeRatesCL = {};
public feeRatesPerKW: FeeRatesCL = {};
public feeRatesPerKB: FeeRates = {};
public feeRatesPerKW: FeeRates = {};
public operatorCards = [];
public merchantCards = [];
public screenSize = '';
@ -109,23 +110,23 @@ export class CLHomeComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.unSubs[1]))
.subscribe((rtlStore) => {
this.flgLoading = [true, true, true, true, true, true, true, true];
rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'FetchInfoCL') {
rtlStore.effectErrors.forEach(effectsErr => {
if (effectsErr.action === 'FetchInfo') {
this.flgLoading[0] = 'error';
}
if (effectsErr.action === 'FetchFeesCL') {
if (effectsErr.action === 'FetchFees') {
this.flgLoading[1] = 'error';
}
if (effectsErr.action === 'FetchBalanceCL') {
if (effectsErr.action === 'FetchBalance') {
this.flgLoading[2] = 'error';
}
if (effectsErr.action === 'FetchLocalRemoteBalanceCL') {
if (effectsErr.action === 'FetchLocalRemoteBalance') {
this.flgLoading[3] = 'error';
}
if (effectsErr.action === 'FetchFeeRatesCL') {
if (effectsErr.action === 'FetchFeeRates') {
this.flgLoading[4] = 'error';
}
if (effectsErr.action === 'FetchChannelsCL') {
if (effectsErr.action === 'FetchChannels') {
this.flgLoading[5] = 'error';
}
});
@ -138,7 +139,7 @@ export class CLHomeComponent implements OnInit, OnDestroy {
this.fees = rtlStore.fees;
this.fees.totalTxCount = 0;
if (rtlStore.forwardingHistory && rtlStore.forwardingHistory.forwarding_events && rtlStore.forwardingHistory.forwarding_events.length) {
this.fees.totalTxCount = rtlStore.forwardingHistory.forwarding_events.filter(event => event.status === 'settled').length
this.fees.totalTxCount = rtlStore.forwardingHistory.forwarding_events.filter(event => event.status === 'settled').length;
}
if (this.flgLoading[1] !== 'error') {
this.flgLoading[1] = ( this.fees.feeCollected) ? false : true;
@ -156,7 +157,7 @@ export class CLHomeComponent implements OnInit, OnDestroy {
let local = (rtlStore.localRemoteBalance.localBalance) ? +rtlStore.localRemoteBalance.localBalance : 0;
let remote = (rtlStore.localRemoteBalance.remoteBalance) ? +rtlStore.localRemoteBalance.remoteBalance : 0;
let total = local + remote;
this.channelBalances = { localBalance: local, remoteBalance: remote, balancedness: (1 - Math.abs((local-remote)/total)).toFixed(3) };
this.channelBalances = { localBalance: local, remoteBalance: remote, balancedness: +(1 - Math.abs((local-remote)/total)).toFixed(3) };
if (this.flgLoading[3] !== 'error') {
this.flgLoading[3] = (rtlStore.localRemoteBalance.localBalance) ? false : true;
}
@ -193,12 +194,12 @@ export class CLHomeComponent implements OnInit, OnDestroy {
this.logger.info(rtlStore);
});
this.actions$.pipe(takeUntil(this.unSubs[2]),
filter((action) => action.type === RTLActions.FETCH_FEES_CL || action.type === RTLActions.SET_FEES_CL))
filter((action) => action.type === CLActions.FETCH_FEES_CL || action.type === CLActions.SET_FEES_CL))
.subscribe(action => {
if(action.type === RTLActions.FETCH_FEES_CL) {
if(action.type === CLActions.FETCH_FEES_CL) {
this.flgChildInfoUpdated = false;
}
if(action.type === RTLActions.SET_FEES_CL) {
if(action.type === CLActions.SET_FEES_CL) {
this.flgChildInfoUpdated = true;
}
});

@ -1,5 +1,5 @@
import { Component, OnChanges, Input } from '@angular/core';
import { GetInfoCL } from '../../../shared/models/clModels';
import { GetInfo } from '../../../shared/models/clModels';
import { CommonService } from '../../../shared/services/common.service';
@Component({
@ -8,7 +8,7 @@ import { CommonService } from '../../../shared/services/common.service';
styleUrls: ['./node-info.component.scss']
})
export class CLNodeInfoComponent implements OnChanges {
@Input() information: GetInfoCL;
@Input() information: GetInfo;
@Input() showColorFieldSeparately: false;
public chains: Array<string> = [''];

@ -3,7 +3,7 @@ import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { ChannelEdgeCL } from '../../../shared/models/clModels';
import { ChannelEdge } from '../../../shared/models/clModels';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@Component({
@ -12,7 +12,7 @@ import * as fromRTLReducer from '../../../store/rtl.reducers';
styleUrls: ['./channel-lookup.component.scss']
})
export class CLChannelLookupComponent implements OnInit {
@Input() lookupResult: ChannelEdgeCL[];
@Input() lookupResult: ChannelEdge[];
public node1_match = false;
public node2_match = false;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];

@ -7,6 +7,7 @@ import { faSearch } from '@fortawesome/free-solid-svg-icons';
import { LoggerService } from '../../shared/services/logger.service';
import * as CLActions from '../store/cl.actions';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
import { ScreenSizeEnum } from '../../shared/services/consts-enums-functions';
@ -44,9 +45,9 @@ export class CLLookupsComponent implements OnInit, OnDestroy {
this.actions$
.pipe(
takeUntil(this.unSubs[0]),
filter((action) => (action.type === RTLActions.SET_LOOKUP_CL || action.type === RTLActions.EFFECT_ERROR_CL))
).subscribe((resLookup: RTLActions.SetLookupCL | RTLActions.EffectErrorCl) => {
if(resLookup.type === RTLActions.SET_LOOKUP_CL) {
filter((action) => (action.type === CLActions.SET_LOOKUP_CL || action.type === CLActions.EFFECT_ERROR_CL))
).subscribe((resLookup: CLActions.SetLookup | CLActions.EffectError) => {
if(resLookup.type === CLActions.SET_LOOKUP_CL) {
this.flgLoading[0] = true;
switch (this.selectedFieldId) {
case 0:
@ -62,7 +63,7 @@ export class CLLookupsComponent implements OnInit, OnDestroy {
this.logger.info(this.nodeLookupValue);
this.logger.info(this.channelLookupValue);
}
if (resLookup.type === RTLActions.EFFECT_ERROR_CL && resLookup.payload.action === 'LookupCL') {
if (resLookup.type === CLActions.EFFECT_ERROR_CL && resLookup.payload.action === 'Lookup') {
this.flgLoading[0] = 'error';
}
});
@ -76,10 +77,10 @@ export class CLLookupsComponent implements OnInit, OnDestroy {
this.store.dispatch(new RTLActions.OpenSpinner('Searching ' + this.lookupFields[this.selectedFieldId].name + '...'));
switch (this.selectedFieldId) {
case 0:
this.store.dispatch(new RTLActions.PeerLookupCL(this.lookupKey.trim()));
this.store.dispatch(new CLActions.PeerLookup(this.lookupKey.trim()));
break;
case 1:
this.store.dispatch(new RTLActions.ChannelLookupCL({shortChannelID: this.lookupKey.trim(), showError: false}));
this.store.dispatch(new CLActions.ChannelLookup({shortChannelID: this.lookupKey.trim(), showError: false}));
break;
default:
break;

@ -1,7 +1,9 @@
import { Component, OnInit, Input, ViewChild } from '@angular/core';
import { MatTableDataSource, MatSort, MatSnackBar } from '@angular/material';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { LookupNodeCL } from '../../../shared/models/clModels';
import { LookupNode } from '../../../shared/models/clModels';
import { LoggerService } from '../../../shared/services/logger.service';
@Component({
@ -11,7 +13,7 @@ import { LoggerService } from '../../../shared/services/logger.service';
})
export class CLNodeLookupComponent implements OnInit {
@ViewChild(MatSort, { static: true }) sort: MatSort;
@Input() lookupResult: LookupNodeCL;
@Input() lookupResult: LookupNode;
public addresses: any;
public displayedColumns = ['type', 'address', 'port', 'actions'];

@ -1,6 +1,6 @@
import { Component, AfterContentChecked, Input } from '@angular/core';
import { FeeRatesCL, FeeRatePerObj, feeRateStyle } from '../../../shared/models/clModels';
import { FeeRates, FeeRatePerObj, feeRateStyle } from '../../../shared/models/clModels';
@Component({
selector: 'rtl-cl-fee-rates',
@ -9,7 +9,7 @@ import { FeeRatesCL, FeeRatePerObj, feeRateStyle } from '../../../shared/models/
})
export class CLFeeRatesComponent implements AfterContentChecked {
@Input() feeRateStyle: string;
@Input() feeRates: FeeRatesCL;
@Input() feeRates: FeeRates;
@Input() flgLoading: Boolean | 'error';
perkbw: FeeRatePerObj = {};

@ -5,7 +5,7 @@ import { Store } from '@ngrx/store';
import { faBolt, faServer, faNetworkWired } from '@fortawesome/free-solid-svg-icons';
import { LoggerService } from '../../shared/services/logger.service';
import { GetInfoCL, FeesCL, ChannelsStatusCL, FeeRatesCL } from '../../shared/models/clModels';
import { GetInfo, Fees, ChannelsStatus, FeeRates } from '../../shared/models/clModels';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as fromRTLReducer from '../../store/rtl.reducers';
@ -22,11 +22,11 @@ export class CLNetworkInfoComponent implements OnInit, OnDestroy {
public faServer = faServer;
public faNetworkWired = faNetworkWired;
public selNode: SelNodeChild = {};
public information: GetInfoCL = {};
public fees: FeesCL;
public channelsStatus: ChannelsStatusCL = {};
feeRatesPerKB: FeeRatesCL = {};
feeRatesPerKW: FeeRatesCL = {};
public information: GetInfo = {};
public fees: Fees;
public channelsStatus: ChannelsStatus = {};
feeRatesPerKB: FeeRates = {};
feeRatesPerKW: FeeRates = {};
public nodeCardsOperator = [];
public nodeCardsMerchant = [];
public screenSize = '';
@ -68,14 +68,14 @@ export class CLNetworkInfoComponent implements OnInit, OnDestroy {
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'FetchInfoCL') {
rtlStore.effectErrors.forEach(effectsErr => {
if (effectsErr.action === 'FetchInfo') {
this.flgLoading[0] = 'error';
}
if (effectsErr.action === 'FetchFeesCL') {
if (effectsErr.action === 'FetchFees') {
this.flgLoading[1] = 'error';
}
if (effectsErr.action === 'FetchFeeRatesCL') {
if (effectsErr.action === 'FetchFeeRates') {
this.flgLoading[2] = 'error';
}
});

@ -6,6 +6,7 @@ import { ADDRESS_TYPES } from '../../../shared/services/consts-enums-functions';
import { OnChainGeneratedAddressComponent } from '../../../shared/components/data-modal/on-chain-generated-address/on-chain-generated-address.component';
import { CLEffects } from '../../store/cl.effects';
import * as CLActions from '../../store/cl.actions';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@ -19,14 +20,14 @@ export class CLOnChainReceiveComponent implements OnInit {
public selectedAddressType = ADDRESS_TYPES[0];
public newAddress = '';
constructor(private store: Store<fromRTLReducer.RTLState>, private lndEffects: CLEffects) {}
constructor(private store: Store<fromRTLReducer.RTLState>, private clEffects: CLEffects) {}
ngOnInit() {}
onGenerateAddress() {
this.store.dispatch(new RTLActions.OpenSpinner('Getting New Address...'));
this.store.dispatch(new RTLActions.GetNewAddressCL(this.selectedAddressType));
this.lndEffects.setNewAddressCL
this.store.dispatch(new CLActions.GetNewAddress(this.selectedAddressType));
this.clEffects.setNewAddressCL
.pipe(take(1))
.subscribe(newAddress => {
this.newAddress = newAddress;

@ -4,16 +4,17 @@ import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatDialogRef } from '@angular/material';
import { MatDialogRef } from '@angular/material/dialog';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { SelNodeChild, GetInfoRoot } from '../../../shared/models/RTLconfig';
import { GetInfoCL, BalanceCL, OnChainCL } from '../../../shared/models/clModels';
import { CURRENCY_UNITS, CurrencyUnitEnum, CURRENCY_UNIT_FORMATS, AlertTypeEnum, ADDRESS_TYPES, FEE_RATE_TYPES } from '../../../shared/services/consts-enums-functions';
import { GetInfo, Balance, OnChain } from '../../../shared/models/clModels';
import { CURRENCY_UNITS, CurrencyUnitEnum, CURRENCY_UNIT_FORMATS, ADDRESS_TYPES, FEE_RATE_TYPES } from '../../../shared/services/consts-enums-functions';
import { RTLConfiguration } from '../../../shared/models/RTLconfig';
import { CommonService } from '../../../shared/services/common.service';
import { LoggerService } from '../../../shared/services/logger.service';
import * as CLActions from '../../store/cl.actions';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@ -31,10 +32,10 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
public addressTypes = [];
public flgLoadingWallet: Boolean | 'error' = true;
public selectedAddress = ADDRESS_TYPES[1];
public blockchainBalance: BalanceCL = {};
public information: GetInfoCL = {};
public blockchainBalance: Balance = {};
public information: GetInfo = {};
public newAddress = '';
public transaction: OnChainCL = {};
public transaction: OnChain = {};
public feeRateTypes = FEE_RATE_TYPES;
public flgMinConf = false;
public sendFundError = '';
@ -59,13 +60,13 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
this.logger.info(rootStore);
});
this.actions$.pipe(takeUntil(this.unSubs[1]),
filter(action => action.type === RTLActions.EFFECT_ERROR_CL || action.type === RTLActions.SET_CHANNEL_TRANSACTION_RES_CL))
.subscribe((action: RTLActions.EffectErrorCl | RTLActions.SetChannelTransactionResCL) => {
if (action.type === RTLActions.SET_CHANNEL_TRANSACTION_RES_CL) {
filter(action => action.type === CLActions.EFFECT_ERROR_CL || action.type === CLActions.SET_CHANNEL_TRANSACTION_RES_CL))
.subscribe((action: CLActions.EffectError | CLActions.SetChannelTransactionRes) => {
if (action.type === CLActions.SET_CHANNEL_TRANSACTION_RES_CL) {
this.store.dispatch(new RTLActions.OpenSnackBar('Fund Sent Successfully!'));
this.dialogRef.close();
}
if (action.type === RTLActions.EFFECT_ERROR_CL && action.payload.action === 'SetChannelTransactionCL') {
if (action.type === CLActions.EFFECT_ERROR_CL && action.payload.action === 'SetChannelTransaction') {
this.sendFundError = action.payload.message;
}
});
@ -82,10 +83,10 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
.subscribe(data => {
this.transaction.satoshis = parseInt(data[CurrencyUnitEnum.SATS]);
this.selAmountUnit = CurrencyUnitEnum.SATS;
this.store.dispatch(new RTLActions.SetChannelTransactionCL(this.transaction));
this.store.dispatch(new CLActions.SetChannelTransaction(this.transaction));
});
} else {
this.store.dispatch(new RTLActions.SetChannelTransactionCL(this.transaction));
this.store.dispatch(new CLActions.SetChannelTransaction(this.transaction));
}
}

@ -1,12 +1,12 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { faReceipt } from '@fortawesome/free-solid-svg-icons';
import { MatSnackBar } from '@angular/material/snack-bar';
import { LoggerService } from '../../../../shared/services/logger.service';
import { CommonService } from '../../../../shared/services/common.service';
import { CLChannelInformation } from '../../../../shared/models/alertData';
import { ChannelCL } from '../../../../shared/models/clModels';
import { Channel } from '../../../../shared/models/clModels';
import { ScreenSizeEnum } from '../../../../shared/services/consts-enums-functions';
@Component({
@ -19,7 +19,7 @@ export class CLChannelInformationComponent implements OnInit {
public showAdvanced = false;
public showCopy = true;
public showCopyField = null;
public channel: ChannelCL;
public channel: Channel;
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;

@ -3,8 +3,10 @@ import { Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { MatTableDataSource, MatSort, MatPaginator, MatPaginatorIntl } from '@angular/material';
import { ChannelCL, GetInfoCL, ChannelEdgeCL } from '../../../../../shared/models/clModels';
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Channel, GetInfo, ChannelEdge } from '../../../../../shared/models/clModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTypeEnum, ScreenSizeEnum, FEE_RATE_TYPES } from '../../../../../shared/services/consts-enums-functions';
import { LoggerService } from '../../../../../shared/services/logger.service';
import { CommonService } from '../../../../../shared/services/common.service';
@ -12,6 +14,7 @@ import { CommonService } from '../../../../../shared/services/common.service';
import { CLChannelInformationComponent } from '../../channel-information-modal/channel-information.component';
import { CLEffects } from '../../../../store/cl.effects';
import { RTLEffects } from '../../../../../store/rtl.effects';
import * as CLActions from '../../../../store/cl.actions';
import * as RTLActions from '../../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../../store/rtl.reducers';
@ -30,7 +33,7 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
public displayedColumns = [];
public channels: any;
public myChanPolicy: any = {};
public information: GetInfoCL = {};
public information: GetInfo = {};
public numPeers = -1;
public feeRateTypes = FEE_RATE_TYPES;
public flgLoading: Array<Boolean | 'error'> = [true];
@ -64,8 +67,8 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'FetchChannelsCL') {
rtlStore.effectErrors.forEach(effectsErr => {
if (effectsErr.action === 'FetchChannels') {
this.flgLoading[0] = 'error';
}
});
@ -82,13 +85,13 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
});
}
onViewRemotePolicy(selChannel: ChannelCL) {
this.store.dispatch(new RTLActions.ChannelLookupCL({shortChannelID: selChannel.short_channel_id, showError: true}));
onViewRemotePolicy(selChannel: Channel) {
this.store.dispatch(new CLActions.ChannelLookup({shortChannelID: selChannel.short_channel_id, showError: true}));
this.clEffects.setLookupCL
.pipe(take(1))
.subscribe((resLookup: ChannelEdgeCL[]) => {
.subscribe((resLookup: ChannelEdge[]) => {
if(resLookup.length === 0) { return false; }
let remoteNode: ChannelEdgeCL = {};
let remoteNode: ChannelEdge = {};
if(resLookup[0].source !== this.information.id) {
remoteNode = resLookup[0];
} else {
@ -134,16 +137,16 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
const base_fee = confirmRes[0].inputValue;
const fee_rate = confirmRes[1].inputValue;
this.store.dispatch(new RTLActions.OpenSpinner('Updating Channel Policy...'));
this.store.dispatch(new RTLActions.UpdateChannelsCL({baseFeeMsat: base_fee, feeRate: fee_rate, channelId: 'all'}));
this.store.dispatch(new CLActions.UpdateChannels({baseFeeMsat: base_fee, feeRate: fee_rate, channelId: 'all'}));
}
});
} else {
this.myChanPolicy = {fee_base_msat: 0, fee_rate_milli_msat: 0};
this.store.dispatch(new RTLActions.OpenSpinner('Fetching Channel Policy...'));
this.store.dispatch(new RTLActions.ChannelLookupCL(channelToUpdate.short_channel_id));
this.store.dispatch(new CLActions.ChannelLookup(channelToUpdate.short_channel_id));
this.clEffects.setLookupCL
.pipe(take(1))
.subscribe((resLookup: ChannelEdgeCL[]) => {
.subscribe((resLookup: ChannelEdge[]) => {
if (resLookup.length > 0 && resLookup[0].destination === this.information.id) {
this.myChanPolicy = {fee_base_msat: resLookup[0].base_fee_millisatoshi, fee_rate_milli_msat: resLookup[0].fee_per_millionth};
} else if (resLookup.length > 1 && resLookup[1].destination === this.information.id) {
@ -176,14 +179,14 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
const base_fee = confirmRes[0].inputValue;
const fee_rate = confirmRes[1].inputValue;
this.store.dispatch(new RTLActions.OpenSpinner('Updating Channel Policy...'));
this.store.dispatch(new RTLActions.UpdateChannelsCL({baseFeeMsat: base_fee, feeRate: fee_rate, channelId: channelToUpdate.channel_id}));
this.store.dispatch(new CLActions.UpdateChannels({baseFeeMsat: base_fee, feeRate: fee_rate, channelId: channelToUpdate.channel_id}));
}
});
}
this.applyFilter();
}
onChannelClose(channelToClose: ChannelCL) {
onChannelClose(channelToClose: Channel) {
this.store.dispatch(new RTLActions.OpenConfirmation({ data: {
type: AlertTypeEnum.CONFIRM,
alertTitle: 'Close Channel',
@ -196,7 +199,7 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
.subscribe(confirmRes => {
if (confirmRes) {
this.store.dispatch(new RTLActions.OpenSpinner('Closing Channel...'));
this.store.dispatch(new RTLActions.CloseChannelCL({channelId: channelToClose.channel_id}));
this.store.dispatch(new CLActions.CloseChannel({channelId: channelToClose.channel_id}));
}
});
}
@ -206,7 +209,7 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
this.channels.filter = this.selFilter;
}
onChannelClick(selChannel: ChannelCL, event: any) {
onChannelClick(selChannel: Channel, event: any) {
this.store.dispatch(new RTLActions.OpenAlert({ data: {
channel: selChannel,
showCopy: true,
@ -218,8 +221,8 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
mychannels.sort(function(a, b) {
return (a.active === b.active) ? 0 : ((b.active) ? 1 : -1);
});
this.channels = new MatTableDataSource<ChannelCL>([...mychannels]);
this.channels.filterPredicate = (channel: ChannelCL, fltr: string) => {
this.channels = new MatTableDataSource<Channel>([...mychannels]);
this.channels.filterPredicate = (channel: Channel, fltr: string) => {
const newChannel = ((channel.connected) ? 'connected' : 'disconnected') + (channel.channel_id ? channel.channel_id : '') +
(channel.short_channel_id ? channel.short_channel_id : '') + (channel.id ? channel.id : '') + (channel.alias ? channel.alias : '') +
(channel.private ? 'private' : 'public') + (channel.state ? channel.state.toLowerCase() : '') +
@ -235,7 +238,7 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
onDownloadCSV() {
if(this.channels.data && this.channels.data.length > 0) {
this.commonService.downloadCSV(this.channels.data, 'Open-channels');
this.commonService.downloadFile(this.channels.data, 'Open-channels');
}
}

@ -1,11 +1,13 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { MatTableDataSource, MatSort, MatPaginator, MatPaginatorIntl } from '@angular/material';
import { ChannelCL, GetInfoCL } from '../../../../../shared/models/clModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTypeEnum, ScreenSizeEnum, FEE_RATE_TYPES } from '../../../../../shared/services/consts-enums-functions';
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { GetInfo, Channel } from '../../../../../shared/models/clModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, ScreenSizeEnum, FEE_RATE_TYPES } from '../../../../../shared/services/consts-enums-functions';
import { LoggerService } from '../../../../../shared/services/logger.service';
import { CommonService } from '../../../../../shared/services/common.service';
@ -30,7 +32,7 @@ export class CLChannelPendingTableComponent implements OnInit, OnDestroy {
public displayedColumns = [];
public channels: any;
public myChanPolicy: any = {};
public information: GetInfoCL = {};
public information: GetInfo = {};
public numPeers = -1;
public feeRateTypes = FEE_RATE_TYPES;
public flgLoading: Array<Boolean | 'error'> = [true];
@ -64,8 +66,8 @@ export class CLChannelPendingTableComponent implements OnInit, OnDestroy {
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'FetchChannelsCL') {
rtlStore.effectErrors.forEach(effectsErr => {
if (effectsErr.action === 'FetchChannels') {
this.flgLoading[0] = 'error';
}
});
@ -87,7 +89,7 @@ export class CLChannelPendingTableComponent implements OnInit, OnDestroy {
this.channels.filter = this.selFilter;
}
onChannelClick(selChannel: ChannelCL, event: any) {
onChannelClick(selChannel: Channel, event: any) {
this.store.dispatch(new RTLActions.OpenAlert({ data: {
channel: selChannel,
showCopy: true,
@ -99,8 +101,8 @@ export class CLChannelPendingTableComponent implements OnInit, OnDestroy {
mychannels.sort(function(a, b) {
return (a.active === b.active) ? 0 : ((b.active) ? 1 : -1);
});
this.channels = new MatTableDataSource<ChannelCL>([...mychannels]);
this.channels.filterPredicate = (channel: ChannelCL, fltr: string) => {
this.channels = new MatTableDataSource<Channel>([...mychannels]);
this.channels.filterPredicate = (channel: Channel, fltr: string) => {
const newChannel = ((channel.connected) ? 'connected' : 'disconnected') + (channel.channel_id ? channel.channel_id : '') +
(channel.short_channel_id ? channel.short_channel_id : '') + (channel.id ? channel.id : '') + (channel.alias ? channel.alias : '') +
(channel.private ? 'private' : 'public') + (channel.state ? channel.state.toLowerCase() : '') +
@ -116,7 +118,7 @@ export class CLChannelPendingTableComponent implements OnInit, OnDestroy {
onDownloadCSV() {
if(this.channels.data && this.channels.data.length > 0) {
this.commonService.downloadCSV(this.channels.data, 'Pending-inactive-channels');
this.commonService.downloadFile(this.channels.data, 'Pending-inactive-channels');
}
}

@ -1,16 +1,17 @@
import { Component, OnInit, Inject, OnDestroy, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { Subject, Observable, of } from 'rxjs';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Subject, Observable } from 'rxjs';
import { takeUntil, filter, startWith, map } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { PeerCL, GetInfoCL } from '../../../../shared/models/clModels';
import { Peer, GetInfo } from '../../../../shared/models/clModels';
import { CLOpenChannelAlert } from '../../../../shared/models/alertData';
import { FEE_RATE_TYPES } from '../../../../shared/services/consts-enums-functions';
import * as CLActions from '../../../store/cl.actions';
import * as RTLActions from '../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
@ -24,13 +25,13 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
public selectedPeer = new FormControl();
public faExclamationTriangle = faExclamationTriangle;
public alertTitle: string;
public peer: PeerCL;
public peers: PeerCL[];
public sortedPeers: PeerCL[];
public filteredPeers: Observable<PeerCL[]>;
public peer: Peer;
public peers: Peer[];
public sortedPeers: Peer[];
public filteredPeers: Observable<Peer[]>;
public channelConnectionError = '';
public advancedTitle = 'Advanced Options';
public information: GetInfoCL;
public information: GetInfo;
public totalBalance = 0;
public fundingAmount: number;
public selectedPubkey = '';
@ -50,12 +51,12 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
this.peer = this.data.message.peer ? this.data.message.peer : null;
this.peers = this.data.message.peers && this.data.message.peers.length ? this.data.message.peers : [];
this.actions$.pipe(takeUntil(this.unSubs[0]),
filter(action => action.type === RTLActions.EFFECT_ERROR_CL || action.type === RTLActions.FETCH_CHANNELS_CL))
.subscribe((action: RTLActions.EffectErrorCl | RTLActions.FetchChannelsCL) => {
if (action.type === RTLActions.EFFECT_ERROR_CL && action.payload.action === 'SaveNewChannelCL') {
filter(action => action.type === CLActions.EFFECT_ERROR_CL || action.type === CLActions.FETCH_CHANNELS_CL))
.subscribe((action: CLActions.EffectError | CLActions.FetchChannels) => {
if (action.type === CLActions.EFFECT_ERROR_CL && action.payload.action === 'SaveNewChannel') {
this.channelConnectionError = action.payload.message;
}
if (action.type === RTLActions.FETCH_CHANNELS_CL) {
if (action.type === CLActions.FETCH_CHANNELS_CL) {
this.dialogRef.close();
}
});
@ -71,11 +72,11 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
);
}
private filterPeers(newlySelectedPeer: string): PeerCL[] {
private filterPeers(newlySelectedPeer: string): Peer[] {
return this.sortedPeers.filter(peer => peer.alias.toLowerCase().indexOf(newlySelectedPeer ? newlySelectedPeer.toLowerCase() : '') === 0);
}
displayFn(peer: PeerCL): string {
displayFn(peer: Peer): string {
return (peer && peer.alias) ? peer.alias : (peer && peer.id) ? peer.id : '';
}
@ -120,7 +121,7 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
onOpenChannel() {
if ((!this.peer && !this.selectedPubkey) || (!this.fundingAmount || ((this.totalBalance - this.fundingAmount) < 0) || (this.flgMinConf && !this.minConfValue))) { return true; }
this.store.dispatch(new RTLActions.OpenSpinner('Opening Channel...'));
this.store.dispatch(new RTLActions.SaveNewChannelCL({
this.store.dispatch(new CLActions.SaveNewChannel({
peerId: ((!this.peer || !this.peer.id) ? this.selectedPubkey : this.peer.id), satoshis: this.fundingAmount, announce: !this.isPrivate, feeRate: this.selFeeRate, minconf: this.flgMinConf ? this.minConfValue : null
}));
}

@ -4,15 +4,17 @@ import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatDialogRef, MAT_DIALOG_DATA, MatVerticalStepper } from '@angular/material';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatVerticalStepper } from '@angular/material/stepper';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { LoggerService } from '../../../shared/services/logger.service';
import { PeerCL } from '../../../shared/models/clModels';
import { Peer } from '../../../shared/models/clModels';
import { CLOpenChannelAlert } from '../../../shared/models/alertData';
import { FEE_RATE_TYPES } from '../../../shared/services/consts-enums-functions';
import { CLEffects } from '../../store/cl.effects';
import * as CLActions from '../../store/cl.actions';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@ -30,7 +32,7 @@ export class CLConnectPeerComponent implements OnInit, OnDestroy {
public feeRateTypes = FEE_RATE_TYPES;
public flgChannelOpened = false;
public channelOpenStatus = null;
public newlyAddedPeer: PeerCL = null;
public newlyAddedPeer: Peer = null;
public flgEditable = true;
public peerConnectionError = '';
public channelConnectionError = '';
@ -71,22 +73,22 @@ export class CLConnectPeerComponent implements OnInit, OnDestroy {
}
});
this.actions$.pipe(takeUntil(this.unSubs[1]),
filter((action) => action.type === RTLActions.NEWLY_ADDED_PEER_CL || action.type === RTLActions.FETCH_CHANNELS_CL || action.type === RTLActions.EFFECT_ERROR_CL))
.subscribe((action: (RTLActions.NewlyAddedPeerCL | RTLActions.FetchChannelsCL | RTLActions.EffectErrorCl)) => {
if (action.type === RTLActions.NEWLY_ADDED_PEER_CL) {
filter((action) => action.type === CLActions.NEWLY_ADDED_PEER_CL || action.type === CLActions.FETCH_CHANNELS_CL || action.type === CLActions.EFFECT_ERROR_CL))
.subscribe((action: (CLActions.NewlyAddedPeer | CLActions.FetchChannels | CLActions.EffectError)) => {
if (action.type === CLActions.NEWLY_ADDED_PEER_CL) {
this.logger.info(action.payload);
this.flgEditable = false;
this.newlyAddedPeer = action.payload.peer;
this.peerFormGroup.controls.hiddenAddress.setValue(this.peerFormGroup.controls.peerAddress.value);
this.stepper.next();
}
if (action.type === RTLActions.FETCH_CHANNELS_CL) {
if (action.type === CLActions.FETCH_CHANNELS_CL) {
this.dialogRef.close();
}
if (action.type === RTLActions.EFFECT_ERROR_CL) {
if (action.payload.action === 'SaveNewPeerCL') {
if (action.type === CLActions.EFFECT_ERROR_CL) {
if (action.payload.action === 'SaveNewPeer') {
this.peerConnectionError = action.payload.message;
} else if (action.payload.action === 'SaveNewChannelCL') {
} else if (action.payload.action === 'SaveNewChannel') {
this.channelConnectionError = action.payload.message;
}
}
@ -97,14 +99,14 @@ export class CLConnectPeerComponent implements OnInit, OnDestroy {
if(!this.peerFormGroup.controls.peerAddress.value) { return true; }
this.peerConnectionError = '';
this.store.dispatch(new RTLActions.OpenSpinner('Adding Peer...'));
this.store.dispatch(new RTLActions.SaveNewPeerCL({id: this.peerFormGroup.controls.peerAddress.value}));
this.store.dispatch(new CLActions.SaveNewPeer({id: this.peerFormGroup.controls.peerAddress.value}));
}
onOpenChannel() {
if (!this.channelFormGroup.controls.fundingAmount.value || ((this.totalBalance - this.channelFormGroup.controls.fundingAmount.value) < 0) || (this.channelFormGroup.controls.flgMinConf.value && !this.channelFormGroup.controls.minConfValue.value)) { return true; }
this.channelConnectionError = '';
this.store.dispatch(new RTLActions.OpenSpinner('Opening Channel...'));
this.store.dispatch(new RTLActions.SaveNewChannelCL({
this.store.dispatch(new CLActions.SaveNewChannel({
peerId: this.newlyAddedPeer.id, satoshis: this.channelFormGroup.controls.fundingAmount.value, announce: !this.channelFormGroup.controls.isPrivate.value, feeRate: this.channelFormGroup.controls.selFeeRate.value, minconf: this.channelFormGroup.controls.flgMinConf.value ? this.channelFormGroup.controls.minConfValue.value : null
}));
}

@ -5,7 +5,7 @@ import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faUsers, faChartPie } from '@fortawesome/free-solid-svg-icons';
import { GetInfoCL, PeerCL } from '../../shared/models/clModels';
import { GetInfo, Peer } from '../../shared/models/clModels';
import { CLOpenChannelComponent } from './channels/open-channel-modal/open-channel.component';
import { SelNodeChild } from '../../shared/models/RTLconfig';
@ -19,8 +19,8 @@ import * as fromRTLReducer from '../../store/rtl.reducers';
})
export class CLPeersChannelsComponent implements OnInit, OnDestroy {
public selNode: SelNodeChild = {};
public information: GetInfoCL = {};
public peers: PeerCL[] = [];
public information: GetInfo = {};
public peers: Peer[] = [];
public totalBalance = 0;
public activePeers = 0;
public activeChannels = 0;

@ -6,15 +6,19 @@ import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faUsers } from '@fortawesome/free-solid-svg-icons';
import { MatTableDataSource, MatSort, MatPaginator, MatPaginatorIntl } from '@angular/material';
import { PeerCL, GetInfoCL } from '../../../shared/models/clModels';
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Peer, GetInfo } from '../../../shared/models/clModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, ScreenSizeEnum } from '../../../shared/services/consts-enums-functions';
import { LoggerService } from '../../../shared/services/logger.service';
import { CommonService } from '../../../shared/services/common.service';
import { CLOpenChannelComponent } from '../channels/open-channel-modal/open-channel.component';
import { newlyAddedRowAnimation } from '../../../shared/animation/row-animation';
import { CLEffects } from '../../store/cl.effects';
import { RTLEffects } from '../../../store/rtl.effects';
import * as CLActions from '../../store/cl.actions';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
import { CLConnectPeerComponent } from '../connect-peer/connect-peer.component';
@ -37,7 +41,7 @@ export class CLPeersComponent implements OnInit, OnDestroy {
public displayedColumns = [];
public peerAddress = '';
public peers: any;
public information: GetInfoCL = {};
public information: GetInfo = {};
public availableBalance = 0;
public flgLoading: Array<Boolean | 'error'> = [true]; // 0: peers
public flgSticky = false;
@ -68,8 +72,8 @@ export class CLPeersComponent implements OnInit, OnDestroy {
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'FetchPeersCL') {
rtlStore.effectErrors.forEach(effectsErr => {
if (effectsErr.action === 'FetchPeers') {
this.flgLoading[0] = 'error';
}
});
@ -78,7 +82,7 @@ export class CLPeersComponent implements OnInit, OnDestroy {
this.peers = new MatTableDataSource([]);
this.peers.data = [];
if ( rtlStore.peers) {
this.peers = new MatTableDataSource<PeerCL>([...rtlStore.peers]);
this.peers = new MatTableDataSource<Peer>([...rtlStore.peers]);
this.peers.data = rtlStore.peers;
setTimeout(() => { this.flgAnimate = false; }, 3000);
}
@ -92,14 +96,14 @@ export class CLPeersComponent implements OnInit, OnDestroy {
this.actions$
.pipe(
takeUntil(this.unSubs[1]),
filter((action) => action.type === RTLActions.SET_PEERS_CL)
).subscribe((setPeers: RTLActions.SetPeersCL) => {
filter((action) => action.type === CLActions.SET_PEERS_CL)
).subscribe((setPeers: CLActions.SetPeers) => {
this.peerAddress = undefined;
this.flgAnimate = true;
});
}
onPeerClick(selPeer: PeerCL, event: any) {
onPeerClick(selPeer: Peer, event: any) {
const reorderedPeer = [
[{key: 'id', value: selPeer.id, title: 'Public Key', width: 100}],
[{key: 'netaddr', value: selPeer.netaddr, title: 'Address', width: 100}],
@ -124,7 +128,7 @@ export class CLPeersComponent implements OnInit, OnDestroy {
}}));
}
onOpenChannel(peerToAddChannel: PeerCL) {
onOpenChannel(peerToAddChannel: Peer) {
const peerToAddChannelMessage = {
peer: peerToAddChannel,
information: this.information,
@ -138,7 +142,7 @@ export class CLPeersComponent implements OnInit, OnDestroy {
}}));
}
onPeerDetach(peerToDetach: PeerCL) {
onPeerDetach(peerToDetach: Peer) {
const msg = 'Disconnect peer: ' + ((peerToDetach.alias) ? peerToDetach.alias : peerToDetach.id);
this.store.dispatch(new RTLActions.OpenConfirmation({ data: {
type: AlertTypeEnum.CONFIRM,
@ -152,7 +156,7 @@ export class CLPeersComponent implements OnInit, OnDestroy {
.subscribe(confirmRes => {
if (confirmRes) {
this.store.dispatch(new RTLActions.OpenSpinner('Disconnecting Peer...'));
this.store.dispatch(new RTLActions.DetachPeerCL({id: peerToDetach.id, force: false}));
this.store.dispatch(new CLActions.DetachPeer({id: peerToDetach.id, force: false}));
}
});
}
@ -163,7 +167,7 @@ export class CLPeersComponent implements OnInit, OnDestroy {
onDownloadCSV() {
if(this.peers.data && this.peers.data.length > 0) {
this.commonService.downloadCSV(this.peers.data, 'Peers');
this.commonService.downloadFile(this.peers.data, 'Peers');
}
}

@ -1,8 +1,10 @@
import { Component, OnInit, OnChanges, ViewChild, Input } from '@angular/core';
import { Store } from '@ngrx/store';
import { MatTableDataSource, MatSort, MatPaginator, MatPaginatorIntl } from '@angular/material';
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { ForwardingEventCL } from '../../../shared/models/clModels';
import { ForwardingEvent } from '../../../shared/models/clModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTypeEnum, ScreenSizeEnum } from '../../../shared/services/consts-enums-functions';
import { LoggerService } from '../../../shared/services/logger.service';
import { CommonService } from '../../../shared/services/common.service';
@ -50,7 +52,7 @@ export class CLFailedTransactionsComponent implements OnInit, OnChanges {
this.loadForwardingEventsTable(this.failedEvents);
}
onForwardingEventClick(selFEvent: ForwardingEventCL, event: any) {
onForwardingEventClick(selFEvent: ForwardingEvent, event: any) {
const reorderedFHEvent = [
[{key: 'payment_hash', value: selFEvent.payment_hash, title: 'Payment Hash', width: 100, type: DataTypeEnum.STRING}],
[{key: 'received_time_str', value: selFEvent.received_time_str, title: 'Received Time', width: 50, type: DataTypeEnum.DATE_TIME},
@ -69,11 +71,11 @@ export class CLFailedTransactionsComponent implements OnInit, OnChanges {
}}));
}
loadForwardingEventsTable(forwardingEvents: ForwardingEventCL[]) {
this.forwardingHistoryEvents = new MatTableDataSource<ForwardingEventCL>([...forwardingEvents]);
loadForwardingEventsTable(forwardingEvents: ForwardingEvent[]) {
this.forwardingHistoryEvents = new MatTableDataSource<ForwardingEvent>([...forwardingEvents]);
this.forwardingHistoryEvents.sort = this.sort;
this.forwardingHistoryEvents.paginator = this.paginator;
this.forwardingHistoryEvents.filterPredicate = (event: ForwardingEventCL, fltr: string) => {
this.forwardingHistoryEvents.filterPredicate = (event: ForwardingEvent, fltr: string) => {
const newEvent = event.status + event.received_time_str + event.resolved_time_str + event.in_channel + event.out_channel + (event.in_msatoshi/1000) + (event.out_msatoshi/1000) + event.fee;
return newEvent.includes(fltr.toLowerCase());
};
@ -82,7 +84,7 @@ export class CLFailedTransactionsComponent implements OnInit, OnChanges {
onDownloadCSV() {
if(this.forwardingHistoryEvents.data && this.forwardingHistoryEvents.data.length > 0) {
this.commonService.downloadCSV(this.forwardingHistoryEvents.data, 'Failed-transactions');
this.commonService.downloadFile(this.forwardingHistoryEvents.data, 'Failed-transactions');
}
}

@ -1,8 +1,10 @@
import { Component, OnInit, OnChanges, ViewChild, Input } from '@angular/core';
import { Store } from '@ngrx/store';
import { MatTableDataSource, MatSort, MatPaginator, MatPaginatorIntl } from '@angular/material';
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { ForwardingEventCL } from '../../../shared/models/clModels';
import { ForwardingEvent } from '../../../shared/models/clModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTypeEnum, ScreenSizeEnum } from '../../../shared/services/consts-enums-functions';
import { LoggerService } from '../../../shared/services/logger.service';
import { CommonService } from '../../../shared/services/common.service';
@ -50,7 +52,7 @@ export class CLForwardingHistoryComponent implements OnInit, OnChanges {
this.loadForwardingEventsTable(this.successfulEvents);
}
onForwardingEventClick(selFEvent: ForwardingEventCL, event: any) {
onForwardingEventClick(selFEvent: ForwardingEvent, event: any) {
const reorderedFHEvent = [
[{key: 'payment_hash', value: selFEvent.payment_hash, title: 'Payment Hash', width: 100, type: DataTypeEnum.STRING}],
[{key: 'received_time_str', value: selFEvent.received_time_str, title: 'Received Time', width: 50, type: DataTypeEnum.DATE_TIME},
@ -69,11 +71,11 @@ export class CLForwardingHistoryComponent implements OnInit, OnChanges {
}}));
}
loadForwardingEventsTable(forwardingEvents: ForwardingEventCL[]) {
this.forwardingHistoryEvents = new MatTableDataSource<ForwardingEventCL>([...forwardingEvents]);
loadForwardingEventsTable(forwardingEvents: ForwardingEvent[]) {
this.forwardingHistoryEvents = new MatTableDataSource<ForwardingEvent>([...forwardingEvents]);
this.forwardingHistoryEvents.sort = this.sort;
this.forwardingHistoryEvents.paginator = this.paginator;
this.forwardingHistoryEvents.filterPredicate = (event: ForwardingEventCL, fltr: string) => {
this.forwardingHistoryEvents.filterPredicate = (event: ForwardingEvent, fltr: string) => {
const newEvent = event.received_time_str + event.resolved_time_str + event.in_channel + event.out_channel + (event.in_msatoshi/1000) + (event.out_msatoshi/1000) + event.fee;
return newEvent.includes(fltr.toLowerCase());
};
@ -82,7 +84,7 @@ export class CLForwardingHistoryComponent implements OnInit, OnChanges {
onDownloadCSV() {
if(this.forwardingHistoryEvents.data && this.forwardingHistoryEvents.data.length > 0) {
this.commonService.downloadCSV(this.forwardingHistoryEvents.data, 'Forwarding-history');
this.commonService.downloadFile(this.forwardingHistoryEvents.data, 'Forwarding-history');
}
}

@ -7,6 +7,7 @@ import { faMapSigns } from '@fortawesome/free-solid-svg-icons';
import { LoggerService } from '../../shared/services/logger.service';
import * as CLActions from '../store/cl.actions';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
@ -38,7 +39,7 @@ export class CLRoutingComponent implements OnInit, OnDestroy {
ngOnInit() {
this.onEventsFetch();
// this.actions$.pipe(takeUntil(this.unSubs[1]), filter((action) => action.type === RTLActions.RESET_CL_STORE))
// this.actions$.pipe(takeUntil(this.unSubs[1]), filter((action) => action.type === RTLActions.RESET_STORE))
// .subscribe((resetClStore: RTLActions.ResetCLStore) => {
// this.onEventsFetch();
// });
@ -47,8 +48,8 @@ export class CLRoutingComponent implements OnInit, OnDestroy {
.subscribe((rtlStore) => {
this.filteredData = false;
this.errorMessage = '';
rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'GetForwardingHistoryCL') {
rtlStore.effectErrors.forEach(effectsErr => {
if (effectsErr.action === 'GetForwardingHistory') {
this.flgLoading[0] = 'error';
this.errorMessage = (typeof(effectsErr.message) === 'object') ? JSON.stringify(effectsErr.message) : effectsErr.message;
}
@ -83,7 +84,7 @@ export class CLRoutingComponent implements OnInit, OnDestroy {
if (!this.startDate) {
this.startDate = new Date(this.endDate.getFullYear(), this.endDate.getMonth(), this.endDate.getDate() - 30);
}
this.store.dispatch(new RTLActions.GetForwardingHistoryCL(
this.store.dispatch(new CLActions.GetForwardingHistory(
// {
// end_time: Math.round(this.endDate.getTime() / 1000).toString(),
// start_time: Math.round(this.startDate.getTime() / 1000).toString()
@ -101,7 +102,7 @@ export class CLRoutingComponent implements OnInit, OnDestroy {
ngOnDestroy() {
this.resetData();
this.store.dispatch(new RTLActions.SetForwardingHistoryCL({}));
this.store.dispatch(new CLActions.SetForwardingHistory({}));
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save