@ -0,0 +1,50 @@
|
|||||||
|
.angular/
|
||||||
|
.circleci/
|
||||||
|
.git/
|
||||||
|
.github/
|
||||||
|
.settings/
|
||||||
|
.vscode/
|
||||||
|
frontend/
|
||||||
|
backend/
|
||||||
|
backup/
|
||||||
|
cookies/
|
||||||
|
coverage/
|
||||||
|
dist/
|
||||||
|
docker/
|
||||||
|
dockerfiles/
|
||||||
|
logs/
|
||||||
|
node_modules/
|
||||||
|
node_modules_old/
|
||||||
|
node_modules_prod/
|
||||||
|
node_modules_dev/
|
||||||
|
out-tsc/
|
||||||
|
tmp/
|
||||||
|
typings/
|
||||||
|
.browserlistrc
|
||||||
|
_config.yml
|
||||||
|
.classpath
|
||||||
|
.DS_Store
|
||||||
|
.eslintrc.json
|
||||||
|
.gitattributes
|
||||||
|
.gitignore
|
||||||
|
.idea
|
||||||
|
*.launch
|
||||||
|
.project
|
||||||
|
.sass-cache
|
||||||
|
*.sublime-workspace
|
||||||
|
.vscode/*
|
||||||
|
connect.lock
|
||||||
|
ECLDummyData.log
|
||||||
|
libpeerconnection.log
|
||||||
|
npm-debug.log
|
||||||
|
RTL-Config.json
|
||||||
|
RTL-Config-Old.json
|
||||||
|
RTL-Config-1.json
|
||||||
|
RTL-Multi-Node-Conf.json
|
||||||
|
RTL.conf
|
||||||
|
RTL-1.conf
|
||||||
|
RTL-Multi-Node-Conf-1.json
|
||||||
|
RTL-Config-for-BTC-Testing.json
|
||||||
|
testem.log
|
||||||
|
Thumbs.db
|
||||||
|
yarn-error.log
|
@ -1,4 +1,4 @@
|
|||||||
[Intro](../README.md) -- **Application Features** -- [Road Map](Roadmap.md) -- [Application Configurations](Application_configurations)
|
[Intro](../README.md) -- **Application Features** -- [Road Map](Roadmap.md) -- [Application Configurations](Application_configurations.md)
|
||||||
|
|
||||||
## RTL - Feature List
|
## RTL - Feature List
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
[Intro](../README.md) -- [Application Features](Application_features.md) -- [Road Map](Roadmap.md) -- **LND API Coverage** -- [Application Configurations](Application_configurations)
|
[Intro](../README.md) -- [Application Features](Application_features.md) -- [Road Map](Roadmap.md) -- **LND API Coverage** -- [Application Configurations](Application_configurations.md)
|
||||||
|
|
||||||
- [x] GenSeed
|
- [x] GenSeed
|
||||||
- [x] InitWallet
|
- [x] InitWallet
|
@ -1,4 +1,4 @@
|
|||||||
[Intro](../README.md) -- [Application Features](Application_features.md) -- **Road Map** -- [Application Configurations](Application_configurations)
|
[Intro](../README.md) -- [Application Features](Application_features.md) -- **Road Map** -- [Application Configurations](Application_configurations.md)
|
||||||
|
|
||||||
# Product Roadmap for RTL Application
|
# Product Roadmap for RTL Application
|
||||||
|
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 297 KiB |
Before Width: | Height: | Size: 338 KiB After Width: | Height: | Size: 338 KiB |
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 324 KiB |
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 280 KiB |
Before Width: | Height: | Size: 312 KiB After Width: | Height: | Size: 312 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 222 KiB |
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 146 KiB |
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
@ -0,0 +1,86 @@
|
|||||||
|
name: Lint & Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prepare:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
|
||||||
|
- name: Cache node_modules
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-npm-packages
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
|
- name: Install NPM dependencies
|
||||||
|
if: steps.cache-npm-packages.outputs.cache-hit != 'true'
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: prepare
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
|
||||||
|
- name: Cache node_modules
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-npm-packages
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
|
- name: Install NPM dependencies
|
||||||
|
if: steps.cache-npm-packages.outputs.cache-hit != 'true'
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Lint Scripts
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: prepare
|
||||||
|
env:
|
||||||
|
CI: true
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
|
||||||
|
- name: Cache node_modules
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-npm-packages
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
|
- name: Install NPM dependencies
|
||||||
|
if: steps.cache-npm-packages.outputs.cache-hit != 'true'
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: npm run test
|
@ -0,0 +1,86 @@
|
|||||||
|
name: Artifact
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master, 'Release-*' ]
|
||||||
|
tags: [ 'v*' ]
|
||||||
|
release:
|
||||||
|
types: [released]
|
||||||
|
# Triggers the workflow only when merging pull request to the branches.
|
||||||
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
|
branches: [ master, 'Release-*' ]
|
||||||
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
|
||||||
|
- name: Cache node_modules
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-npm-packages
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
|
- name: Install NPM dependencies
|
||||||
|
if: steps.cache-npm-packages.outputs.cache-hit != 'true'
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Cache build frontend
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-build-frontend
|
||||||
|
with:
|
||||||
|
path: frontend
|
||||||
|
key: ${{ runner.os }}-frontend-${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Run build production application
|
||||||
|
run: npm run buildfrontend
|
||||||
|
|
||||||
|
- name: Cache build backend
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-build-backend
|
||||||
|
with:
|
||||||
|
path: backend
|
||||||
|
key: ${{ runner.os }}-backend-${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Run build backend server
|
||||||
|
run: npm run buildbackend
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Cache build frontend
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-build-frontend
|
||||||
|
with:
|
||||||
|
path: frontend
|
||||||
|
key: ${{ runner.os }}-frontend-${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Cache build backend
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-build-backend
|
||||||
|
with:
|
||||||
|
path: backend
|
||||||
|
key: ${{ runner.os }}-backend-${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Compress files
|
||||||
|
run: tar -czf /tmp/rtlbuild.tar.gz frontend backend rtl.js package.json package-lock.json
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: rtl-build-${{ github.event.release.tag_name }}
|
||||||
|
path: /tmp/rtlbuild.tar.gz
|
@ -1 +0,0 @@
|
|||||||
theme: jekyll-theme-hacker
|
|
@ -1,18 +0,0 @@
|
|||||||
<!DOCTYPE html><html lang="en"><head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>RTL</title>
|
|
||||||
<base href="/rtl/">
|
|
||||||
<meta i18n-content="" name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link i18n-sizes="" i18n-rel="" rel="apple-touch-icon" sizes="180x180" href="assets/images/favicon-light/apple-touch-icon.png">
|
|
||||||
<link i18n-sizes="" i18n-rel="" rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-light/favicon-32x32.png">
|
|
||||||
<link i18n-sizes="" i18n-rel="" rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-light/favicon-16x16.png">
|
|
||||||
<link i18n-rel="" rel="manifest" href="assets/images/favicon-light/site.webmanifest">
|
|
||||||
<link i18n-rel="" rel="mask-icon" href="assets/images/favicon-light/safari-pinned-tab.svg" color="#5bbad5">
|
|
||||||
<meta i18n-content="" name="msapplication-TileColor" content="#da532c">
|
|
||||||
<meta i18n-content="" name="theme-color" content="#ffffff">
|
|
||||||
<style>@font-face{font-family:Roboto;src:url(Roboto-Thin.dbd56bd3357dc3617fe5.woff2) format("woff2"),url(Roboto-Thin.e7f7c82374bd0ebef14b.woff) format("woff");font-weight:100;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-ThinItalic.a8cef84f735ef887abdc.woff2) format("woff2"),url(Roboto-ThinItalic.5dd9349c940073834e9a.woff) format("woff");font-weight:100;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Light.c27d89ac77468ae18f28.woff2) format("woff2"),url(Roboto-Light.d923dfafc0c5183b59aa.woff) format("woff");font-weight:300;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-LightItalic.506274c7228cf81cae4d.woff2) format("woff2"),url(Roboto-LightItalic.d4b8c137518d9d92bb28.woff) format("woff");font-weight:300;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Regular.64cfb66c866ea50cad47.woff2) format("woff2"),url(Roboto-Regular.e02e9d6ff5547f7e9962.woff) format("woff");font-weight:400;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-RegularItalic.4dd2af1e8df532f41db8.woff2) format("woff2"),url(Roboto-RegularItalic.5ea38fff9eebef99c5df.woff) format("woff");font-weight:400;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Medium.1d3bced88509b0838984.woff2) format("woff2"),url(Roboto-Medium.092c6130df8fd2199888.woff) format("woff");font-weight:500;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-MediumItalic.d620b8f53f75966fe42e.woff2) format("woff2"),url(Roboto-MediumItalic.18ff1628c628080166c1.woff) format("woff");font-weight:500;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Bold.92fbd4e93cf0a5dbebaa.woff2) format("woff2"),url(Roboto-Bold.73288d91c325e82a5b92.woff) format("woff");font-weight:700;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-BoldItalic.5f600d98a73d800ae575.woff2) format("woff2"),url(Roboto-BoldItalic.6d89acbd21d7e3fbecb2.woff) format("woff");font-weight:700;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Black.41ed1105a6ebb8ffe34e.woff2) format("woff2"),url(Roboto-Black.937491dfcbe64ca9a9f1.woff) format("woff");font-weight:900;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-BlackItalic.50ca4c51ebc27e7e7d2f.woff2) format("woff2"),url(Roboto-BlackItalic.2e1ee657996854c6f427.woff) format("woff");font-weight:900;font-style:italic;}html{width:100%;height:99%;line-height:1.5;overflow-x:hidden;font-family:Roboto,sans-serif!important;font-size:62.5%;}body{box-sizing:border-box;margin:0;}body{height:100%;overflow:hidden;}*{margin:0;padding:0;}</style><link rel="stylesheet" href="styles.50804d64c130486c55c1.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.50804d64c130486c55c1.css"></noscript></head>
|
|
||||||
<body>
|
|
||||||
<rtl-app></rtl-app>
|
|
||||||
<script src="runtime.d5083173ef6104a11138.js" defer></script><script src="polyfills.a979cbbe16939013cdcf.js" defer></script><script src="main.eecf078bfae29f102c6f.js" defer></script>
|
|
||||||
|
|
||||||
</body></html>
|
|
@ -1 +0,0 @@
|
|||||||
(()=>{"use strict";var e,r,t,o={},a={};function n(e){var r=a[e];if(void 0!==r)return r.exports;var t=a[e]={id:e,loaded:!1,exports:{}};return o[e].call(t.exports,t,t.exports,n),t.loaded=!0,t.exports}n.m=o,e=[],n.O=(r,t,o,a)=>{if(!t){var l=1/0;for(s=0;s<e.length;s++){for(var[t,o,a]=e[s],d=!0,i=0;i<t.length;i++)(!1&a||l>=a)&&Object.keys(n.O).every(e=>n.O[e](t[i]))?t.splice(i--,1):(d=!1,a<l&&(l=a));d&&(e.splice(s--,1),r=o())}return r}a=a||0;for(var s=e.length;s>0&&e[s-1][2]>a;s--)e[s]=e[s-1];e[s]=[t,o,a]},n.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return n.d(r,{a:r}),r},n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+"."+{145:"b1263bd6d4d9c808b95c",432:"95e7cd048ce3f51df4b4",891:"426afa0ec2d9096e8da5",958:"9e0e1f7340063e08f0c5"}[e]+".js",n.miniCssF=e=>"styles.50804d64c130486c55c1.css",n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="rtl:",n.l=(e,o,a,l)=>{if(r[e])r[e].push(o);else{var d,i;if(void 0!==a)for(var s=document.getElementsByTagName("script"),u=0;u<s.length;u++){var c=s[u];if(c.getAttribute("src")==e||c.getAttribute("data-webpack")==t+a){d=c;break}}d||(i=!0,(d=document.createElement("script")).charset="utf-8",d.timeout=120,n.nc&&d.setAttribute("nonce",n.nc),d.setAttribute("data-webpack",t+a),d.src=e),r[e]=[o];var f=(t,o)=>{d.onerror=d.onload=null,clearTimeout(p);var a=r[e];if(delete r[e],d.parentNode&&d.parentNode.removeChild(d),a&&a.forEach(e=>e(o)),t)return t(o)},p=setTimeout(f.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=f.bind(null,d.onerror),d.onload=f.bind(null,d.onload),i&&document.head.appendChild(d)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),n.p="",(()=>{var e={666:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else if(666!=r){var a=new Promise((t,a)=>o=e[r]=[t,a]);t.push(o[2]=a);var l=n.p+n.u(r),d=new Error;n.l(l,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&("load"===t.type?"missing":t.type),l=t&&t.target&&t.target.src;d.message="Loading chunk "+r+" failed.\n("+a+": "+l+")",d.name="ChunkLoadError",d.type=a,d.request=l,o[1](d)}},"chunk-"+r,r)}else e[r]=0},n.O.j=r=>0===e[r];var r=(r,t)=>{var o,a,[l,d,i]=t,s=0;for(o in d)n.o(d,o)&&(n.m[o]=d[o]);if(i)var u=i(n);for(r&&r(t);s<l.length;s++)n.o(e,a=l[s])&&e[a]&&e[a][0](),e[l[s]]=0;return n.O(u)},t=self.webpackChunkrtl=self.webpackChunkrtl||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})()})();
|
|
@ -0,0 +1,31 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getBalance = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Getting Balance..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/getBalance';
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Balance', msg: 'Balance Received', data: body });
|
||||||
|
if (!body.totalBalance) {
|
||||||
|
body.totalBalance = 0;
|
||||||
|
}
|
||||||
|
if (!body.confBalance) {
|
||||||
|
body.confBalance = 0;
|
||||||
|
}
|
||||||
|
if (!body.unconfBalance) {
|
||||||
|
body.unconfBalance = 0;
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,129 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const listChannels = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listChannels';
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'List Channels', data: body });
|
||||||
|
body.map((channel) => {
|
||||||
|
if (!channel.alias || channel.alias === '') {
|
||||||
|
channel.alias = channel.id.substring(0, 20);
|
||||||
|
}
|
||||||
|
const local = (channel.msatoshi_to_us) ? channel.msatoshi_to_us : 0;
|
||||||
|
const remote = (channel.msatoshi_to_them) ? channel.msatoshi_to_them : 0;
|
||||||
|
const total = channel.msatoshi_total ? channel.msatoshi_total : 0;
|
||||||
|
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
|
||||||
|
return channel;
|
||||||
|
});
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channels Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const openChannel = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/openChannel';
|
||||||
|
options.body = req.body;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Options', data: options.body });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const setChannelFee = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Setting Channel Fee..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/setChannelFee';
|
||||||
|
options.body = req.body;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.body });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Fee Set' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const closeChannel = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' });
|
||||||
|
req.setTimeout(60000 * 10); // timeout 10 mins
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
const unilateralTimeoutQuery = req.query.force ? '?unilateralTimeout=1' : '';
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/closeChannel/' + req.params.channelId + unilateralTimeoutQuery;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel', data: options.url });
|
||||||
|
request.delete(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Close Channel Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed' });
|
||||||
|
res.status(204).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getLocalRemoteBalance = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Local & Remote Balances..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/localremotebal';
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Local Remote Balance', data: body });
|
||||||
|
if (!body.localBalance) {
|
||||||
|
body.localBalance = 0;
|
||||||
|
}
|
||||||
|
if (!body.remoteBalance) {
|
||||||
|
body.remoteBalance = 0;
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Local & Remote Balances Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Local Remote Balance Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const listForwards = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channel List Forwards..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listForwards?status=' + req.query.status;
|
||||||
|
request.get(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Forwarding History Response For Status ' + req.query.status, data: body });
|
||||||
|
if (body && body.length > 0) {
|
||||||
|
body = common.sortDescByKey(body, 'received_time');
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Forwarding History Received For Status' + req.query.status, data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel List Forwards Received For Status ' + req.query.status });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Forwarding History Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,25 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getFees = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/getFees';
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Fee Received', data: body });
|
||||||
|
if (!body.feeCollected) {
|
||||||
|
body.feeCollected = 0;
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fees Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,81 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Database } from '../../utils/database.js';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { CLWSClient } from './webSocketClient.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
const clWsClient = CLWSClient;
|
||||||
|
const databaseService = Database;
|
||||||
|
export const getInfo = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting CLightning Node Information..' });
|
||||||
|
common.logEnvVariables(req);
|
||||||
|
common.setOptions(req);
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo';
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Selected Node', data: req.session.selectedNode.ln_node });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Calling Info from C-Lightning server url', data: options.url });
|
||||||
|
if (!options.headers || !options.headers.macaroon) {
|
||||||
|
const errMsg = 'C-Lightning get info failed due to bad or missing macaroon!';
|
||||||
|
const err = common.handleError({ statusCode: 502, message: 'Bad Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Node Information', data: body });
|
||||||
|
const body_str = (!body) ? '' : JSON.stringify(body);
|
||||||
|
const search_idx = (!body) ? -1 : body_str.search('Not Found');
|
||||||
|
if (!body || search_idx > -1 || body.error) {
|
||||||
|
if (body && !body.error) {
|
||||||
|
body.error = 'Error From Server!';
|
||||||
|
}
|
||||||
|
const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
body.lnImplementation = 'C-Lightning';
|
||||||
|
const chainObj = { chain: '', network: '' };
|
||||||
|
if (body.network === 'testnet') {
|
||||||
|
chainObj.chain = 'Bitcoin';
|
||||||
|
chainObj.network = 'Testnet';
|
||||||
|
}
|
||||||
|
else if (body.network === 'bitcoin') {
|
||||||
|
chainObj.chain = 'Bitcoin';
|
||||||
|
chainObj.network = 'Mainnet';
|
||||||
|
}
|
||||||
|
else if (body.network === 'signet') {
|
||||||
|
chainObj.chain = 'Bitcoin';
|
||||||
|
chainObj.network = 'Signet';
|
||||||
|
}
|
||||||
|
else if (body.network === 'litecoin') {
|
||||||
|
chainObj.chain = 'Litecoin';
|
||||||
|
chainObj.network = 'Mainnet';
|
||||||
|
}
|
||||||
|
else if (body.network === 'litecoin-testnet') {
|
||||||
|
chainObj.chain = 'Litecoin';
|
||||||
|
chainObj.network = 'Testnet';
|
||||||
|
}
|
||||||
|
body.chains = [chainObj];
|
||||||
|
body.uris = [];
|
||||||
|
if (body.address && body.address.length > 0) {
|
||||||
|
body.address.forEach((addr) => {
|
||||||
|
body.uris.push(body.id + '@' + addr.address + ':' + addr.port);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
req.session.selectedNode.api_version = body.api_version || '';
|
||||||
|
req.session.selectedNode.ln_version = body.version || '';
|
||||||
|
clWsClient.updateSelectedNode(req.session.selectedNode);
|
||||||
|
databaseService.loadDatabase(req.session.selectedNode);
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'CLightning Node Information Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,62 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const deleteExpiredInvoice = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Deleting Expired Invoices..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
const queryStr = req.query.maxExpiry ? '?maxexpiry=' + req.query.maxExpiry : '';
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/delExpiredInvoice' + queryStr;
|
||||||
|
request.delete(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices Deleted', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Expired Invoices Deleted' });
|
||||||
|
res.status(204).json({ status: 'Invoice Deleted Successfully' });
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoice', 'Delete Invoice Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const listInvoices = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Getting Invoices..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
const labelQuery = req.query.label ? '?label=' + req.query.label : '';
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/listInvoices' + labelQuery;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url });
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body });
|
||||||
|
if (body.invoices && body.invoices.length > 0) {
|
||||||
|
body.invoices = common.sortDescByKey(body.invoices, 'expires_at');
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Invoices Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const addInvoice = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/genInvoice';
|
||||||
|
options.body = req.body;
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Add Invoice Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Invoice Created' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoice', 'Add Invoice Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,39 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const signMessage = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Signing Message..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/utility/signMessage';
|
||||||
|
options.form = { message: req.body.message };
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Messages', msg: 'Message Signed', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Signed' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Message', 'Sign Message Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const verifyMessage = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Verifying Message..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/utility/checkMessage/' + req.body.message + '/' + req.body.signature;
|
||||||
|
request.get(options, (error, response, body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Messages', msg: 'Message Verified', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Message', 'Verify Message Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,69 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getRoute = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Getting Network Routes..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/network/getRoute/' + req.params.destPubkey + '/' + req.params.amount;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'Query Routes Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Routes Received' });
|
||||||
|
res.status(200).json({ routes: body });
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Network', 'Query Routes Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const listNode = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/network/listNode/' + req.params.id;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'Node Lookup', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const listChannel = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Channel Lookup..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/network/listChannel/' + req.params.channelShortId;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'Channel Lookup', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Channel Lookup Finished' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Network', 'Channel Lookup Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const feeRates = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Getting Network Fee Rates..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/network/feeRates/' + req.params.feeRateStyle;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'Network Fee Rates Received for ' + req.params.feeRateStyle, data: body });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Network', 'Fee Rates Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,108 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { Database } from '../../utils/database.js';
|
||||||
|
import { CollectionFieldsEnum, CollectionsEnum } from '../../models/database.model.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
const databaseService = Database;
|
||||||
|
export const listOfferBookmarks = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Bookmarks..' });
|
||||||
|
databaseService.find(req.session.selectedNode, CollectionsEnum.OFFERS).then((offers) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offer Bookmarks Received', data: offers });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmarks Received' });
|
||||||
|
if (offers && offers.length > 0) {
|
||||||
|
offers = common.sortDescByKey(offers, 'lastUpdatedAt');
|
||||||
|
}
|
||||||
|
res.status(200).json(offers);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Offers', 'Offer Bookmarks Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const deleteOfferBookmark = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' });
|
||||||
|
databaseService.destroy(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offer Bookmark Deleted', data: deleteRes });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmark Deleted' });
|
||||||
|
res.status(204).json(req.params.offerStr);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Offers', 'Offer Bookmark Delete Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const listOffers = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offers..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/listoffers';
|
||||||
|
if (req.query.offer_id) {
|
||||||
|
options.url = options.url + '?offer_id=' + req.query.offer_id;
|
||||||
|
}
|
||||||
|
if (req.query.active_only) {
|
||||||
|
options.url = options.url + '?active_only=' + req.query.active_only;
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offers List URL', data: options.url });
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offers List Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offers Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Offers', 'List Offers Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const createOffer = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Creating Offer..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/offer';
|
||||||
|
options.body = req.body;
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offer', msg: 'Add Offer Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Created' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Offer', 'Create Offer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const fetchOfferInvoice = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Getting Offer Invoice..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/fetchInvoice';
|
||||||
|
options.body = req.body;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offer Invoice Body', data: options.body });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offer Invoice Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Invoice Received' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Offers', 'Get Offer Invoice Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const disableOffer = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Disabling Offer..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/offers/disableOffer/' + req.params.offerID;
|
||||||
|
request.delete(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offer Disabled', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Disabled' });
|
||||||
|
res.status(202).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Offers', 'Disable Offer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,57 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getNewAddress = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Generating New Address..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/newaddr?addrType=' + req.query.type;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'OnChain', 'New Address Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const onChainWithdraw = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Withdrawing from On Chain..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/withdraw';
|
||||||
|
options.body = req.body;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'OnChain', msg: 'OnChain Withdraw Options', data: options.body });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'OnChain', msg: 'OnChain Withdraw Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Withdraw Finished' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'OnChain', 'Withdraw Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getUTXOs = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'List Funds..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/listFunds';
|
||||||
|
request(options).then((body) => {
|
||||||
|
if (body.outputs) {
|
||||||
|
body.outputs = common.sortDescByStrKey(body.outputs, 'status');
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'OnChain', msg: 'List Funds Received', data: body });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'OnChain', 'List Funds Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,156 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { Database } from '../../utils/database.js';
|
||||||
|
import { CollectionFieldsEnum, CollectionsEnum } from '../../models/database.model.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
const databaseService = Database;
|
||||||
|
function paymentReducer(accumulator, currentPayment) {
|
||||||
|
const currPayHash = currentPayment.payment_hash;
|
||||||
|
if (!currentPayment.partid) {
|
||||||
|
currentPayment.partid = 0;
|
||||||
|
}
|
||||||
|
if (!accumulator[currPayHash]) {
|
||||||
|
accumulator[currPayHash] = [currentPayment];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
accumulator[currPayHash].push(currentPayment);
|
||||||
|
}
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
function summaryReducer(accumulator, mpp) {
|
||||||
|
if (mpp.status === 'complete') {
|
||||||
|
accumulator.msatoshi = accumulator.msatoshi + mpp.msatoshi;
|
||||||
|
accumulator.msatoshi_sent = accumulator.msatoshi_sent + mpp.msatoshi_sent;
|
||||||
|
accumulator.status = mpp.status;
|
||||||
|
}
|
||||||
|
if (mpp.bolt11) {
|
||||||
|
accumulator.bolt11 = mpp.bolt11;
|
||||||
|
}
|
||||||
|
if (mpp.bolt12) {
|
||||||
|
accumulator.bolt12 = mpp.bolt12;
|
||||||
|
}
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
function groupBy(payments) {
|
||||||
|
const paymentsInGroups = payments.reduce(paymentReducer, {});
|
||||||
|
const paymentsGrpArray = Object.keys(paymentsInGroups).map((key) => ((paymentsInGroups[key].length && paymentsInGroups[key].length > 1) ? common.sortDescByKey(paymentsInGroups[key], 'partid') : paymentsInGroups[key]));
|
||||||
|
return paymentsGrpArray.reduce((acc, curr) => {
|
||||||
|
let temp = {};
|
||||||
|
if (curr.length && curr.length === 1) {
|
||||||
|
temp = JSON.parse(JSON.stringify(curr[0]));
|
||||||
|
temp.is_group = false;
|
||||||
|
temp.is_expanded = false;
|
||||||
|
temp.total_parts = 1;
|
||||||
|
delete temp.partid;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const paySummary = curr.reduce(summaryReducer, { msatoshi: 0, msatoshi_sent: 0, status: (curr[0] && curr[0].status) ? curr[0].status : 'failed' });
|
||||||
|
temp = {
|
||||||
|
is_group: true, is_expanded: false, total_parts: (curr.length ? curr.length : 0), status: paySummary.status, payment_hash: curr[0].payment_hash,
|
||||||
|
destination: curr[0].destination, msatoshi: paySummary.msatoshi, msatoshi_sent: paySummary.msatoshi_sent, created_at: curr[0].created_at,
|
||||||
|
mpps: curr
|
||||||
|
};
|
||||||
|
if (paySummary.bolt11) {
|
||||||
|
temp.bolt11 = paySummary.bolt11;
|
||||||
|
}
|
||||||
|
if (paySummary.bolt12) {
|
||||||
|
temp.bolt12 = paySummary.bolt12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc.concat(temp);
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
export const listPayments = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'List Payments..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments';
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments });
|
||||||
|
if (body && body.payments && body.payments.length > 0) {
|
||||||
|
body.payments = common.sortDescByKey(body.payments, 'created_at');
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'List Payments Received' });
|
||||||
|
res.status(200).json(groupBy(body.payments));
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const decodePayment = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Decoding Payment..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/utility/decode/' + req.params.payReq;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Decode Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const postPayment = (req, res, next) => {
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
if (req.body.paymentType === 'KEYSEND') {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Keysend Payment..' });
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/pay/keysend';
|
||||||
|
options.body = req.body;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (req.body.paymentType === 'OFFER') {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Offer Payment..' });
|
||||||
|
options.body = { invoice: req.body.invoice };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Invoice Payment..' });
|
||||||
|
options.body = req.body;
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/pay';
|
||||||
|
}
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Send Payment Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent' });
|
||||||
|
if (req.body.paymentType === 'OFFER') {
|
||||||
|
if (req.body.saveToDB && req.body.bolt12) {
|
||||||
|
const offerToUpdate = { bolt12: req.body.bolt12, amountmSat: (req.body.zeroAmtOffer ? 0 : req.body.amount), title: req.body.title, lastUpdatedAt: new Date(Date.now()).getTime() };
|
||||||
|
if (req.body.vendor) {
|
||||||
|
offerToUpdate['vendor'] = req.body.vendor;
|
||||||
|
}
|
||||||
|
if (req.body.description) {
|
||||||
|
offerToUpdate['description'] = req.body.description;
|
||||||
|
}
|
||||||
|
return databaseService.update(req.session.selectedNode, CollectionsEnum.OFFERS, offerToUpdate, CollectionFieldsEnum.BOLT12, req.body.bolt12).then((updatedOffer) => {
|
||||||
|
logger.log({ level: 'DEBUG', fileName: 'Offer', msg: 'Offer Updated', data: updatedOffer });
|
||||||
|
return res.status(201).json({ paymentResponse: body, saveToDBResponse: updatedOffer });
|
||||||
|
}).catch((errDB) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Payments', msg: 'Offer DB update error', error: errDB });
|
||||||
|
return res.status(201).json({ paymentResponse: body, saveToDBError: errDB });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return res.status(201).json({ paymentResponse: body, saveToDBResponse: 'NA' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (req.body.paymentType === 'INVOICE') {
|
||||||
|
return res.status(201).json({ paymentResponse: body, saveToDBResponse: 'NA' });
|
||||||
|
}
|
||||||
|
if (req.body.paymentType === 'KEYSEND') {
|
||||||
|
return res.status(201).json(body);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,72 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getPeers = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'List Peers..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers';
|
||||||
|
request(options).then((body) => {
|
||||||
|
body.forEach((peer) => {
|
||||||
|
if (!peer.alias || peer.alias === '') {
|
||||||
|
peer.alias = peer.id.substring(0, 20);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const peers = (body) ? common.sortDescByStrKey(body, 'alias') : [];
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers with Alias', data: peers });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers Received' });
|
||||||
|
res.status(200).json(peers);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const postPeer = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Connecting Peer..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/connect';
|
||||||
|
options.body = req.body;
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Added', data: body });
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers';
|
||||||
|
request(options).then((body) => {
|
||||||
|
let peers = (body) ? common.sortDescByStrKey(body, 'alias') : [];
|
||||||
|
peers = common.newestOnTop(peers, 'id', req.body.id);
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer with Newest On Top', data: peers });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Added Successfully' });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Connected' });
|
||||||
|
res.status(201).json(peers);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const deletePeer = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Disconnecting Peer..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/peer/disconnect/' + req.params.peerId + '?force=' + req.query.force;
|
||||||
|
request.delete(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Detach Peer Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Detached', data: req.params.peerId });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected' });
|
||||||
|
res.status(204).json({});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Detach Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,113 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import WebSocket from 'ws';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { WSServer } from '../../utils/webSocketServer.js';
|
||||||
|
export class CLWebSocketClient {
|
||||||
|
constructor() {
|
||||||
|
this.logger = Logger;
|
||||||
|
this.common = Common;
|
||||||
|
this.wsServer = WSServer;
|
||||||
|
this.webSocketClients = [];
|
||||||
|
this.reconnectTimeOut = null;
|
||||||
|
this.waitTime = 0.5;
|
||||||
|
this.reconnet = (clWsClt) => {
|
||||||
|
if (this.reconnectTimeOut) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.waitTime = (this.waitTime >= 64) ? 64 : (this.waitTime * 2);
|
||||||
|
this.reconnectTimeOut = setTimeout(() => {
|
||||||
|
if (clWsClt.selectedNode) {
|
||||||
|
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Reconnecting to the CLightning\'s Websocket Server..' });
|
||||||
|
this.connect(clWsClt.selectedNode);
|
||||||
|
}
|
||||||
|
this.reconnectTimeOut = null;
|
||||||
|
}, this.waitTime * 1000);
|
||||||
|
};
|
||||||
|
this.connect = (selectedNode) => {
|
||||||
|
try {
|
||||||
|
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
|
||||||
|
if (!clientExists) {
|
||||||
|
if (selectedNode.ln_server_url) {
|
||||||
|
const newWebSocketClient = { selectedNode: selectedNode, reConnect: true, webSocketClient: null };
|
||||||
|
this.connectWithClient(newWebSocketClient);
|
||||||
|
this.webSocketClients.push(newWebSocketClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) {
|
||||||
|
clientExists.reConnect = true;
|
||||||
|
this.connectWithClient(clientExists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.connectWithClient = (clWsClt) => {
|
||||||
|
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connecting to the CLightning\'s Websocket Server..' });
|
||||||
|
const WS_LINK = (clWsClt.selectedNode.ln_server_url).replace(/^http/, 'ws') + '/v1/ws';
|
||||||
|
const mcrnHexEncoded = Buffer.from(fs.readFileSync(join(clWsClt.selectedNode.macaroon_path, 'access.macaroon'))).toString('hex');
|
||||||
|
clWsClt.webSocketClient = new WebSocket(WS_LINK, [mcrnHexEncoded, 'hex'], { rejectUnauthorized: false });
|
||||||
|
clWsClt.webSocketClient.onopen = () => {
|
||||||
|
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connected to the CLightning\'s Websocket Server..' });
|
||||||
|
this.waitTime = 0.5;
|
||||||
|
};
|
||||||
|
clWsClt.webSocketClient.onclose = (e) => {
|
||||||
|
if (clWsClt && clWsClt.selectedNode && clWsClt.selectedNode.ln_implementation === 'CLT') {
|
||||||
|
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Web socket disconnected, will reconnect again...' });
|
||||||
|
clWsClt.webSocketClient.close();
|
||||||
|
if (clWsClt.reConnect) {
|
||||||
|
this.reconnet(clWsClt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
clWsClt.webSocketClient.onmessage = (msg) => {
|
||||||
|
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Received message from the server..', data: msg.data });
|
||||||
|
msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data;
|
||||||
|
msg['source'] = 'CLT';
|
||||||
|
const msgStr = JSON.stringify(msg);
|
||||||
|
this.wsServer.sendEventsToAllLNClients(msgStr, clWsClt.selectedNode);
|
||||||
|
};
|
||||||
|
clWsClt.webSocketClient.onerror = (err) => {
|
||||||
|
if (clWsClt.selectedNode.api_version === '' || !clWsClt.selectedNode.api_version || this.common.isVersionCompatible(clWsClt.selectedNode.api_version, '0.6.0')) {
|
||||||
|
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'ERROR', fileName: 'CLWebSocket', msg: 'Web socket error', error: err });
|
||||||
|
const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }'));
|
||||||
|
this.wsServer.sendErrorToAllLNClients(errStr, clWsClt.selectedNode);
|
||||||
|
clWsClt.webSocketClient.close();
|
||||||
|
if (clWsClt.reConnect) {
|
||||||
|
this.reconnet(clWsClt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
clWsClt.reConnect = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
this.disconnect = (selectedNode) => {
|
||||||
|
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
|
||||||
|
if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) {
|
||||||
|
this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the CLightning\'s Websocket Server..' });
|
||||||
|
clientExists.reConnect = false;
|
||||||
|
clientExists.webSocketClient.close();
|
||||||
|
const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index);
|
||||||
|
this.webSocketClients.splice(clientIdx, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.updateSelectedNode = (newSelectedNode) => {
|
||||||
|
const clientIdx = this.webSocketClients.findIndex((wsc) => +wsc.selectedNode.index === +newSelectedNode.index);
|
||||||
|
const newClient = this.webSocketClients[clientIdx];
|
||||||
|
newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode));
|
||||||
|
this.webSocketClients[clientIdx] = newClient;
|
||||||
|
};
|
||||||
|
this.wsServer.eventEmitterCLT.on('CONNECT', (nodeIndex) => {
|
||||||
|
this.connect(this.common.findNode(+nodeIndex));
|
||||||
|
});
|
||||||
|
this.wsServer.eventEmitterCLT.on('DISCONNECT', (nodeIndex) => {
|
||||||
|
this.disconnect(this.common.findNode(+nodeIndex));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const CLWSClient = new CLWebSocketClient();
|
@ -0,0 +1,156 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const simplifyAllChannels = (lnServerUrl, channels) => {
|
||||||
|
let channelNodeIds = '';
|
||||||
|
const 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 : '',
|
||||||
|
isFunder: channel.data && channel.data.commitments && channel.data.commitments.localParams && channel.data.commitments.localParams.isFunder ? channel.data.commitments.localParams.isFunder : false,
|
||||||
|
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,
|
||||||
|
feeRatePerKw: (channel.data.commitments.localCommit.spec.feeratePerKw) ? channel.data.commitments.localCommit.spec.feeratePerKw : 0,
|
||||||
|
feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0,
|
||||||
|
alias: ''
|
||||||
|
});
|
||||||
|
});
|
||||||
|
channelNodeIds = channelNodeIds.substring(1);
|
||||||
|
options.url = lnServerUrl + '/nodes';
|
||||||
|
options.form = { nodeIds: channelNodeIds };
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Channels', msg: 'Node Ids to find alias', data: channelNodeIds });
|
||||||
|
return request.post(options).then((nodes) => {
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Channels', msg: 'Filtered Nodes', data: nodes });
|
||||||
|
let foundPeer = null;
|
||||||
|
simplifiedChannels.map((channel) => {
|
||||||
|
foundPeer = nodes.find((channelWithAlias) => channel.nodeId === channelWithAlias.nodeId);
|
||||||
|
channel.alias = foundPeer ? foundPeer.alias : channel.nodeId.substring(0, 20);
|
||||||
|
return channel;
|
||||||
|
});
|
||||||
|
return simplifiedChannels;
|
||||||
|
}).catch((err) => simplifiedChannels);
|
||||||
|
};
|
||||||
|
export const getChannels = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'List Channels..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/channels';
|
||||||
|
options.form = {};
|
||||||
|
if (req.query && req.query.nodeId) {
|
||||||
|
options.form = req.query;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels Node Id', data: options.form });
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Options', data: options });
|
||||||
|
if (common.read_dummy_data) {
|
||||||
|
common.getDummyData('Channels', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(data); });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'All Channels', data: body });
|
||||||
|
if (body && body.length) {
|
||||||
|
return simplifyAllChannels(req.session.selectedNode.ln_server_url, body).then((simplifiedChannels) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Simplified Channels with Alias', data: simplifiedChannels });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channels List Received' });
|
||||||
|
res.status(200).json(simplifiedChannels);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Empty Channels List Received' });
|
||||||
|
res.status(200).json({ activeChannels: [], pendingChannels: [], inactiveChannels: [], lightningBalances: { localBalance: 0, remoteBalance: 0 }, channelStatus: { active: { channels: 0, capacity: 0 }, inactive: { channels: 0, capacity: 0 }, pending: { channels: 0, capacity: 0 } } });
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getChannelStats = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channel States..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/channelstats';
|
||||||
|
options.form = {};
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channel Stats Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel States Received' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Get Channel Stats Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const openChannel = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/open';
|
||||||
|
options.form = req.body;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Params', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Open Channel Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const updateChannelRelayFee = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updating Channel Relay Fee..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/updaterelayfee';
|
||||||
|
options.form = req.query;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Relay Fee Params', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Relay Fee Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Relay Fee Updated' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Update Relay Fee Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const closeChannel = (req, res, next) => {
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
if (req.query.force !== 'true') {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' });
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/close';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Force Closing Channel..' });
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/forceclose';
|
||||||
|
}
|
||||||
|
options.form = { channelId: req.query.channelId };
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: '[Close URL, Close Params]', data: [options.url, options.form] });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Close Channel Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Closed' });
|
||||||
|
res.status(204).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Close Channel Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,125 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const arrangeFees = (body, current_time) => {
|
||||||
|
const fees = { daily_fee: 0, daily_txs: 0, weekly_fee: 0, weekly_txs: 0, monthly_fee: 0, monthly_txs: 0 };
|
||||||
|
const week_start_time = current_time - 604800000;
|
||||||
|
const day_start_time = current_time - 86400000;
|
||||||
|
let fee = 0;
|
||||||
|
body.relayed.forEach((relayedEle) => {
|
||||||
|
fee = Math.round((relayedEle.amountIn - relayedEle.amountOut) / 1000);
|
||||||
|
if (relayedEle.timestamp >= day_start_time) {
|
||||||
|
fees.daily_fee = fees.daily_fee + fee;
|
||||||
|
fees.daily_txs = fees.daily_txs + 1;
|
||||||
|
}
|
||||||
|
if (relayedEle.timestamp >= week_start_time) {
|
||||||
|
fees.weekly_fee = fees.weekly_fee + fee;
|
||||||
|
fees.weekly_txs = fees.weekly_txs + 1;
|
||||||
|
}
|
||||||
|
fees.monthly_fee = fees.monthly_fee + fee;
|
||||||
|
fees.monthly_txs = fees.monthly_txs + 1;
|
||||||
|
});
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Fee', data: fees });
|
||||||
|
return fees;
|
||||||
|
};
|
||||||
|
export const arrangePayments = (body) => {
|
||||||
|
const payments = {
|
||||||
|
sent: body && body.sent ? body.sent : [],
|
||||||
|
received: body && body.received ? body.received : [],
|
||||||
|
relayed: body && body.relayed ? body.relayed : []
|
||||||
|
};
|
||||||
|
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.parts.forEach((part) => {
|
||||||
|
if (part.amount) {
|
||||||
|
part.amount = Math.round(part.amount / 1000);
|
||||||
|
}
|
||||||
|
if (part.feesPaid) {
|
||||||
|
part.feesPaid = Math.round(part.feesPaid / 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
payments.received.forEach((receivedEle) => {
|
||||||
|
if (receivedEle.parts && receivedEle.parts.length > 0) {
|
||||||
|
receivedEle.firstPartTimestamp = receivedEle.parts[0].timestamp;
|
||||||
|
}
|
||||||
|
receivedEle.parts.forEach((part) => {
|
||||||
|
if (part.amount) {
|
||||||
|
part.amount = Math.round(part.amount / 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
payments.relayed.forEach((relayedEle) => {
|
||||||
|
if (relayedEle.amountIn) {
|
||||||
|
relayedEle.amountIn = Math.round(relayedEle.amountIn / 1000);
|
||||||
|
}
|
||||||
|
if (relayedEle.amountOut) {
|
||||||
|
relayedEle.amountOut = Math.round(relayedEle.amountOut / 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
payments.sent = common.sortDescByKey(payments.sent, 'firstPartTimestamp');
|
||||||
|
payments.received = common.sortDescByKey(payments.received, 'firstPartTimestamp');
|
||||||
|
payments.relayed = common.sortDescByKey(payments.relayed, 'timestamp');
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Fees', msg: 'Arranged Payments', data: payments });
|
||||||
|
return payments;
|
||||||
|
};
|
||||||
|
export const getFees = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/audit';
|
||||||
|
const today = new Date(Date.now());
|
||||||
|
const tillToday = (Math.round(today.getTime() / 1000)).toString();
|
||||||
|
const fromLastMonth = (Math.round(new Date(today.getFullYear(), today.getMonth() - 1, today.getDate() + 1, 0, 0, 0).getTime() / 1000)).toString();
|
||||||
|
options.form = {
|
||||||
|
from: fromLastMonth,
|
||||||
|
to: tillToday
|
||||||
|
};
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Fee Audit Options', data: options.form });
|
||||||
|
if (common.read_dummy_data) {
|
||||||
|
common.getDummyData('Fees', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangeFees(data, Math.round((new Date().getTime())))); });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Fee Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received' });
|
||||||
|
res.status(200).json(arrangeFees(body, Math.round((new Date().getTime()))));
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Fees', 'Get Fees Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getPayments = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Payments..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/audit';
|
||||||
|
options.form = null;
|
||||||
|
if (common.read_dummy_data) {
|
||||||
|
common.getDummyData('Payments', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangePayments(data)); });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Payments Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Payments Received' });
|
||||||
|
res.status(200).json(arrangePayments(body));
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Fees', 'Get Payments Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,51 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Database } from '../../utils/database.js';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { ECLWSClient } from './webSocketClient.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
const eclWsClient = ECLWSClient;
|
||||||
|
const databaseService = Database;
|
||||||
|
export const getInfo = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting Eclair Node Information..' });
|
||||||
|
common.logEnvVariables(req);
|
||||||
|
common.setOptions(req);
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/getinfo';
|
||||||
|
options.form = {};
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Selected Node', data: req.session.selectedNode.ln_node });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Calling Info from Eclair server url', data: options.url });
|
||||||
|
if (common.read_dummy_data) {
|
||||||
|
common.getDummyData('GetInfo', req.session.selectedNode.ln_implementation).then((data) => {
|
||||||
|
data.lnImplementation = 'Eclair';
|
||||||
|
res.status(200).json(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!options.headers || !options.headers.authorization) {
|
||||||
|
const errMsg = 'Eclair Get info failed due to missing or wrong password!';
|
||||||
|
const err = common.handleError({ statusCode: 502, message: 'Missing or Wrong Password', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Eclair\'s Websocket Server.' });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Get Info Response', data: body });
|
||||||
|
body.lnImplementation = 'Eclair';
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Eclair Node Information Received' });
|
||||||
|
req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
|
||||||
|
eclWsClient.updateSelectedNode(req.session.selectedNode);
|
||||||
|
databaseService.loadDatabase(req.session.selectedNode);
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,129 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
let pendingInvoices = [];
|
||||||
|
export const getReceivedPaymentInfo = (lnServerUrl, invoice) => {
|
||||||
|
let idx = -1;
|
||||||
|
invoice.expiresAt = (!invoice.expiry) ? null : (+invoice.timestamp + +invoice.expiry);
|
||||||
|
if (invoice.amount) {
|
||||||
|
invoice.amount = Math.round(invoice.amount / 1000);
|
||||||
|
}
|
||||||
|
idx = pendingInvoices.findIndex((pendingInvoice) => invoice.serialized === pendingInvoice.serialized);
|
||||||
|
if (idx < 0) {
|
||||||
|
options.url = lnServerUrl + '/getreceivedinfo';
|
||||||
|
options.form = { paymentHash: invoice.paymentHash };
|
||||||
|
return request(options).then((response) => {
|
||||||
|
invoice.status = response.status.type;
|
||||||
|
if (response.status && response.status.type === 'received') {
|
||||||
|
invoice.amountSettled = response.status.amount ? Math.round(response.status.amount / 1000) : 0;
|
||||||
|
invoice.receivedAt = response.status.receivedAt ? Math.round(response.status.receivedAt / 1000) : 0;
|
||||||
|
}
|
||||||
|
return invoice;
|
||||||
|
}).catch((err) => {
|
||||||
|
invoice.status = 'unknown';
|
||||||
|
return invoice;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pendingInvoices.splice(idx, 1);
|
||||||
|
invoice.status = 'unpaid';
|
||||||
|
return invoice;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getInvoice = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Invoice..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/getinvoice';
|
||||||
|
options.form = { paymentHash: req.params.paymentHash };
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoice Found', data: body });
|
||||||
|
const current_time = (Math.round(new Date(Date.now()).getTime() / 1000));
|
||||||
|
body.amount = body.amount ? body.amount / 1000 : 0;
|
||||||
|
body.expiresAt = body.expiresAt ? body.expiresAt : (body.timestamp + body.expiry);
|
||||||
|
body.status = body.status ? body.status : (+body.expiresAt < current_time ? 'expired' : 'unknown');
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoices', 'Get Invoice Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const listInvoices = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Getting List Invoices..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.form = {};
|
||||||
|
const options1 = JSON.parse(JSON.stringify(options));
|
||||||
|
options1.url = req.session.selectedNode.ln_server_url + '/listinvoices';
|
||||||
|
options1.form = {};
|
||||||
|
const options2 = JSON.parse(JSON.stringify(options));
|
||||||
|
options2.url = req.session.selectedNode.ln_server_url + '/listpendinginvoices';
|
||||||
|
options2.form = {};
|
||||||
|
if (common.read_dummy_data) {
|
||||||
|
return common.getDummyData('Invoices', req.session.selectedNode.ln_implementation).then((body) => {
|
||||||
|
const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0];
|
||||||
|
pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1];
|
||||||
|
return Promise.all(invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))).
|
||||||
|
then((values) => {
|
||||||
|
body = common.sortDescByKey(invoices, 'expiresAt');
|
||||||
|
return res.status(200).json(invoices);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Promise.all([request(options1), request(options2)]).
|
||||||
|
then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body });
|
||||||
|
const invoices = (!body[0] || body[0].length <= 0) ? [] : body[0];
|
||||||
|
pendingInvoices = (!body[1] || body[1].length <= 0) ? [] : body[1];
|
||||||
|
if (invoices && invoices.length > 0) {
|
||||||
|
return Promise.all(invoices.map((invoice) => getReceivedPaymentInfo(req.session.selectedNode.ln_server_url, invoice))).
|
||||||
|
then((values) => {
|
||||||
|
body = common.sortDescByKey(invoices, 'expiresAt');
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Final Invoices List', data: invoices });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'List Invoices Received' });
|
||||||
|
return res.status(200).json(invoices);
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Empty List Invoice Received' });
|
||||||
|
return res.status(200).json([]);
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const createInvoice = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/createinvoice';
|
||||||
|
options.form = req.body;
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Create Invoice Response', data: body });
|
||||||
|
if (body.amount) {
|
||||||
|
body.amount = Math.round(body.amount / 1000);
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Invoice Created' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoices', 'Create Invoice Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,23 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getNodes = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/nodes';
|
||||||
|
options.form = { nodeIds: req.params.id };
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'Node Lookup', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Network', 'Node Lookup Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,103 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const arrangeBalances = (body) => {
|
||||||
|
if (!body.confirmed) {
|
||||||
|
body.confirmed = 0;
|
||||||
|
}
|
||||||
|
if (!body.unconfirmed) {
|
||||||
|
body.unconfirmed = 0;
|
||||||
|
}
|
||||||
|
body.total = +body.confirmed + +body.unconfirmed;
|
||||||
|
body.btc_total = +body.btc_confirmed + +body.btc_unconfirmed;
|
||||||
|
return body;
|
||||||
|
};
|
||||||
|
export const getNewAddress = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Generating New Address..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/getnewaddress';
|
||||||
|
options.form = {};
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Onchain', msg: 'New Address Generated', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'OnChain', 'Get New Address Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getBalance = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Balance..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/onchainbalance';
|
||||||
|
options.form = {};
|
||||||
|
if (common.read_dummy_data) {
|
||||||
|
common.getDummyData('OnChainBalance', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(arrangeBalances(data)); });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Onchain', msg: 'Balance Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Balance Received' });
|
||||||
|
res.status(200).json(arrangeBalances(body));
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'OnChain', 'Get Balance Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getTransactions = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Getting On Chain Transactions..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/onchaintransactions';
|
||||||
|
options.form = {
|
||||||
|
count: req.query.count,
|
||||||
|
skip: req.query.skip
|
||||||
|
};
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'OnChain', msg: 'Getting On Chain Transactions Options', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'OnChain', msg: 'Transaction Received', data: body });
|
||||||
|
if (body && body.length > 0) {
|
||||||
|
body = common.sortDescByKey(body, 'timestamp');
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Transaction Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'OnChain', 'Get Transactions Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const sendFunds = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Sending On Chain Funds..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/sendonchain';
|
||||||
|
options.form = {
|
||||||
|
address: req.body.address,
|
||||||
|
amountSatoshis: req.body.amount,
|
||||||
|
confirmationTarget: req.body.blocks
|
||||||
|
};
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Onchain', msg: 'Send Funds Options', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Onchain', msg: 'Send Funds Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'On Chain Fund Sent' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'OnChain', 'Send Funds Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,129 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getSentInfoFromPaymentRequest = (lnServerUrl, payment) => {
|
||||||
|
options.url = lnServerUrl + '/getsentinfo';
|
||||||
|
options.form = { paymentHash: payment };
|
||||||
|
return request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Sent Information Received', data: body });
|
||||||
|
body.forEach((sentPayment) => {
|
||||||
|
if (sentPayment.amount) {
|
||||||
|
sentPayment.amount = Math.round(sentPayment.amount / 1000);
|
||||||
|
}
|
||||||
|
if (sentPayment.recipientAmount) {
|
||||||
|
sentPayment.recipientAmount = Math.round(sentPayment.recipientAmount / 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return body;
|
||||||
|
}).catch((err) => err);
|
||||||
|
};
|
||||||
|
export const getQueryNodes = (lnServerUrl, nodeIds) => {
|
||||||
|
options.url = lnServerUrl + '/nodes';
|
||||||
|
options.form = { nodeIds: nodeIds };
|
||||||
|
return request.post(options).then((nodes) => {
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Payments', msg: 'Query Nodes', data: nodes });
|
||||||
|
return nodes;
|
||||||
|
}).catch((err) => []);
|
||||||
|
};
|
||||||
|
export const decodePayment = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Decoding Payment..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/parseinvoice';
|
||||||
|
options.form = { invoice: req.params.invoice };
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Decode Received', data: body });
|
||||||
|
if (body.amount) {
|
||||||
|
body.amount = Math.round(body.amount / 1000);
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Payments', 'Decode Payment Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const postPayment = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Paying Invoice..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/payinvoice';
|
||||||
|
options.form = req.body;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Send Payment Options', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Send Payment Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Invoice Paid' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const queryPaymentRoute = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Querying Payment Route..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/findroutetonode';
|
||||||
|
options.form = {
|
||||||
|
nodeId: req.query.nodeId,
|
||||||
|
amountMsat: req.query.amountMsat
|
||||||
|
};
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Payment Route Options', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Payment Route Received', data: body });
|
||||||
|
if (body && body.length) {
|
||||||
|
const queryRoutes = [];
|
||||||
|
return getQueryNodes(req.session.selectedNode.ln_server_url, body).then((hopsWithAlias) => {
|
||||||
|
let foundPeer = null;
|
||||||
|
body.map((hop) => {
|
||||||
|
foundPeer = hopsWithAlias.find((hopWithAlias) => hop === hopWithAlias.nodeId);
|
||||||
|
queryRoutes.push({ nodeId: hop, alias: foundPeer ? foundPeer.alias : '' });
|
||||||
|
return hop;
|
||||||
|
});
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Query Routes with Alias', data: queryRoutes });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Route Information Received' });
|
||||||
|
res.status(200).json(queryRoutes);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Empty Payment Route Information Received' });
|
||||||
|
res.status(200).json([]);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Payments', 'Query Route Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getSentPaymentsInformation = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting Sent Payment Information..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
if (req.body.payments) {
|
||||||
|
const paymentsArr = req.body.payments.split(',');
|
||||||
|
return Promise.all(paymentsArr.map((payment) => getSentInfoFromPaymentRequest(req.session.selectedNode.ln_server_url, payment))).
|
||||||
|
then((values) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Sent Informations', data: values });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sent Payment Information Received' });
|
||||||
|
return res.status(200).json(values);
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Payments', 'Sent Payment Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Empty Sent Payment Information Received' });
|
||||||
|
return res.status(200).json([]);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,135 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getFilteredNodes = (lnServerUrl, peersNodeIds) => {
|
||||||
|
options.url = lnServerUrl + '/nodes';
|
||||||
|
options.form = { nodeIds: peersNodeIds };
|
||||||
|
return request.post(options).then((nodes) => {
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Peers', msg: 'Filtered Nodes', data: nodes });
|
||||||
|
return nodes;
|
||||||
|
}).catch((err) => []);
|
||||||
|
};
|
||||||
|
export const getPeers = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Getting Peers..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/peers';
|
||||||
|
options.form = {};
|
||||||
|
if (common.read_dummy_data) {
|
||||||
|
common.getDummyData('Peers', req.session.selectedNode.ln_implementation).then((data) => { res.status(200).json(data); });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers Received', data: body });
|
||||||
|
if (body && body.length) {
|
||||||
|
let peersNodeIds = '';
|
||||||
|
body.forEach((peer) => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; });
|
||||||
|
peersNodeIds = peersNodeIds.substring(1);
|
||||||
|
return getFilteredNodes(req.session.selectedNode.ln_server_url, peersNodeIds).then((peersWithAlias) => {
|
||||||
|
let foundPeer = null;
|
||||||
|
body.map((peer) => {
|
||||||
|
foundPeer = peersWithAlias.find((peerWithAlias) => peer.nodeId === peerWithAlias.nodeId);
|
||||||
|
peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20);
|
||||||
|
return peer;
|
||||||
|
});
|
||||||
|
body = common.sortDescByStrKey(body, 'alias');
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers with Alias', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Empty Peers Received' });
|
||||||
|
res.status(200).json([]);
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const connectPeer = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Conneting Peer..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/connect';
|
||||||
|
options.form = {};
|
||||||
|
if (req.query) {
|
||||||
|
options.form = req.query;
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Connect Peer Params', data: options.form });
|
||||||
|
}
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Add Peer Response', data: body });
|
||||||
|
if (typeof body === 'string' && body.includes('already connected')) {
|
||||||
|
const err = common.handleError({ statusCode: 500, message: 'Connect Peer Error', error: body }, 'Peers', body, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
else if (typeof body === 'string' && body.includes('connection failed')) {
|
||||||
|
const err = common.handleError({ statusCode: 500, message: 'Connect Peer Error', error: body }, 'Peers', body, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/peers';
|
||||||
|
options.form = {};
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers Received', data: body });
|
||||||
|
if (body && body.length) {
|
||||||
|
let peersNodeIds = '';
|
||||||
|
body.forEach((peer) => { peersNodeIds = peersNodeIds + ',' + peer.nodeId; });
|
||||||
|
peersNodeIds = peersNodeIds.substring(1);
|
||||||
|
return getFilteredNodes(req.session.selectedNode.ln_server_url, peersNodeIds).then((peersWithAlias) => {
|
||||||
|
let foundPeer = null;
|
||||||
|
body.map((peer) => {
|
||||||
|
foundPeer = peersWithAlias.find((peerWithAlias) => peer.nodeId === peerWithAlias.nodeId);
|
||||||
|
peer.alias = foundPeer ? foundPeer.alias : peer.nodeId.substring(0, 20);
|
||||||
|
return peer;
|
||||||
|
});
|
||||||
|
let peers = (body) ? common.sortDescByStrKey(body, 'alias') : [];
|
||||||
|
peers = common.newestOnTop(peers, 'nodeId', req.query.nodeId ? req.query.nodeId : req.query.uri ? req.query.uri.substring(0, req.query.uri.indexOf('@')) : '');
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer with Newest On Top', data: peers });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Added Successfully' });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Connected' });
|
||||||
|
res.status(201).json(peers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.status(201).json([]);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const deletePeer = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Disconneting Peer..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/disconnect';
|
||||||
|
options.form = {};
|
||||||
|
if (req.params.nodeId) {
|
||||||
|
options.form = { nodeId: req.params.nodeId };
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Disconnect Peer Params', data: options.form });
|
||||||
|
}
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Disconnect Peer Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Disconnected: ' + req.params.nodeId });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected' });
|
||||||
|
res.status(204).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,112 @@
|
|||||||
|
import WebSocket from 'ws';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { WSServer } from '../../utils/webSocketServer.js';
|
||||||
|
export class ECLWebSocketClient {
|
||||||
|
constructor() {
|
||||||
|
this.logger = Logger;
|
||||||
|
this.common = Common;
|
||||||
|
this.wsServer = WSServer;
|
||||||
|
this.webSocketClients = [];
|
||||||
|
this.reconnectTimeOut = null;
|
||||||
|
this.waitTime = 0.5;
|
||||||
|
this.reconnet = (eclWsClt) => {
|
||||||
|
if (this.reconnectTimeOut) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.waitTime = (this.waitTime >= 64) ? 64 : (this.waitTime * 2);
|
||||||
|
this.reconnectTimeOut = setTimeout(() => {
|
||||||
|
if (eclWsClt.selectedNode) {
|
||||||
|
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Reconnecting to the Eclair\'s Websocket Server..' });
|
||||||
|
this.connect(eclWsClt.selectedNode);
|
||||||
|
}
|
||||||
|
this.reconnectTimeOut = null;
|
||||||
|
}, this.waitTime * 1000);
|
||||||
|
};
|
||||||
|
this.connect = (selectedNode) => {
|
||||||
|
try {
|
||||||
|
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
|
||||||
|
if (!clientExists) {
|
||||||
|
if (selectedNode.ln_server_url) {
|
||||||
|
const newWebSocketClient = { selectedNode: selectedNode, reConnect: true, webSocketClient: null };
|
||||||
|
this.connectWithClient(newWebSocketClient);
|
||||||
|
this.webSocketClients.push(newWebSocketClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) {
|
||||||
|
clientExists.reConnect = true;
|
||||||
|
this.connectWithClient(clientExists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.connectWithClient = (eclWsClt) => {
|
||||||
|
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Connecting to the Eclair\'s Websocket Server..' });
|
||||||
|
const UpdatedLNServerURL = (eclWsClt.selectedNode.ln_server_url).replace(/^http/, 'ws');
|
||||||
|
const firstSubStrIndex = (UpdatedLNServerURL.indexOf('//') + 2);
|
||||||
|
const WS_LINK = UpdatedLNServerURL.slice(0, firstSubStrIndex) + ':' + eclWsClt.selectedNode.ln_api_password + '@' + UpdatedLNServerURL.slice(firstSubStrIndex) + '/ws';
|
||||||
|
eclWsClt.webSocketClient = new WebSocket(WS_LINK);
|
||||||
|
eclWsClt.webSocketClient.onopen = () => {
|
||||||
|
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Connected to the Eclair\'s Websocket Server..' });
|
||||||
|
this.waitTime = 0.5;
|
||||||
|
};
|
||||||
|
eclWsClt.webSocketClient.onclose = (e) => {
|
||||||
|
if (eclWsClt && eclWsClt.selectedNode && eclWsClt.selectedNode.ln_implementation === 'ECL') {
|
||||||
|
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Web socket disconnected, will reconnect again...' });
|
||||||
|
eclWsClt.webSocketClient.close();
|
||||||
|
if (eclWsClt.reConnect) {
|
||||||
|
this.reconnet(eclWsClt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
eclWsClt.webSocketClient.onmessage = (msg) => {
|
||||||
|
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Received message from the server..', data: msg.data });
|
||||||
|
msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data;
|
||||||
|
msg['source'] = 'ECL';
|
||||||
|
const msgStr = JSON.stringify(msg);
|
||||||
|
this.wsServer.sendEventsToAllLNClients(msgStr, eclWsClt.selectedNode);
|
||||||
|
};
|
||||||
|
eclWsClt.webSocketClient.onerror = (err) => {
|
||||||
|
if (eclWsClt.selectedNode.ln_version === '' || !eclWsClt.selectedNode.ln_version || this.common.isVersionCompatible(eclWsClt.selectedNode.ln, '0.5.0')) {
|
||||||
|
this.logger.log({ selectedNode: eclWsClt.selectedNode, level: 'ERROR', fileName: 'ECLWebSocket', msg: 'Web socket error', error: err });
|
||||||
|
const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }'));
|
||||||
|
this.wsServer.sendErrorToAllLNClients(errStr, eclWsClt.selectedNode);
|
||||||
|
eclWsClt.webSocketClient.close();
|
||||||
|
if (eclWsClt.reConnect) {
|
||||||
|
this.reconnet(eclWsClt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
eclWsClt.reConnect = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
this.disconnect = (selectedNode) => {
|
||||||
|
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
|
||||||
|
if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) {
|
||||||
|
this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'ECLWebSocket', msg: 'Disconnecting from the Eclair\'s Websocket Server..' });
|
||||||
|
clientExists.reConnect = false;
|
||||||
|
clientExists.webSocketClient.close();
|
||||||
|
const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index);
|
||||||
|
this.webSocketClients.splice(clientIdx, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.updateSelectedNode = (newSelectedNode) => {
|
||||||
|
const clientIdx = this.webSocketClients.findIndex((wsc) => +wsc.selectedNode.index === +newSelectedNode.index);
|
||||||
|
const newClient = this.webSocketClients[clientIdx];
|
||||||
|
newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode));
|
||||||
|
this.webSocketClients[clientIdx] = newClient;
|
||||||
|
};
|
||||||
|
this.wsServer.eventEmitterECL.on('CONNECT', (nodeIndex) => {
|
||||||
|
this.connect(this.common.findNode(+nodeIndex));
|
||||||
|
});
|
||||||
|
this.wsServer.eventEmitterECL.on('DISCONNECT', (nodeIndex) => {
|
||||||
|
this.disconnect(this.common.findNode(+nodeIndex));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const ECLWSClient = new ECLWebSocketClient();
|
@ -0,0 +1,34 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getBlockchainBalance = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Getting Balance..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/balance/blockchain';
|
||||||
|
options.qs = req.query;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Balance', msg: '[Request params, Request Query, Balance Received]', data: [req.params, req.query, body] });
|
||||||
|
if (body) {
|
||||||
|
if (!body.total_balance) {
|
||||||
|
body.total_balance = 0;
|
||||||
|
}
|
||||||
|
if (!body.confirmed_balance) {
|
||||||
|
body.confirmed_balance = 0;
|
||||||
|
}
|
||||||
|
if (!body.unconfirmed_balance) {
|
||||||
|
body.unconfirmed_balance = 0;
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,262 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getAliasForChannel = (lnServerUrl, channel) => {
|
||||||
|
const pubkey = (channel.remote_pubkey) ? channel.remote_pubkey : (channel.remote_node_pub) ? channel.remote_node_pub : '';
|
||||||
|
options.url = lnServerUrl + '/v1/graph/node/' + pubkey;
|
||||||
|
return request(options).then((aliasBody) => {
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Channels', msg: 'Alias', data: aliasBody.node.alias });
|
||||||
|
channel.remote_alias = aliasBody.node.alias;
|
||||||
|
return aliasBody.node.alias;
|
||||||
|
}).catch((err) => {
|
||||||
|
channel.remote_alias = pubkey.slice(0, 10) + '...' + pubkey.slice(-10);
|
||||||
|
return pubkey;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getAllChannels = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Channels..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels';
|
||||||
|
options.qs = req.query;
|
||||||
|
let local = 0;
|
||||||
|
let remote = 0;
|
||||||
|
let total = 0;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'All Channels Received', data: body });
|
||||||
|
if (body.channels) {
|
||||||
|
return Promise.all(body.channels.map((channel) => {
|
||||||
|
local = (channel.local_balance) ? +channel.local_balance : 0;
|
||||||
|
remote = (channel.remote_balance) ? +channel.remote_balance : 0;
|
||||||
|
total = local + remote;
|
||||||
|
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
|
||||||
|
return getAliasForChannel(req.session.selectedNode.ln_server_url, channel);
|
||||||
|
})).then((values) => {
|
||||||
|
body.channels = common.sortDescByKey(body.channels, 'balancedness');
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'All Channels with Alias', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channels Received' });
|
||||||
|
return res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Get All Channel Aliases Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
body.channels = [];
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Empty Channels Received' });
|
||||||
|
return res.status(200).json(body);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'List Channels Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getPendingChannels = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Pending Channels..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/pending';
|
||||||
|
options.qs = req.query;
|
||||||
|
request(options).then((body) => {
|
||||||
|
if (!body.total_limbo_balance) {
|
||||||
|
body.total_limbo_balance = 0;
|
||||||
|
}
|
||||||
|
const promises = [];
|
||||||
|
if (body.pending_open_channels && body.pending_open_channels.length > 0) {
|
||||||
|
body.pending_open_channels.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode.ln_server_url, channel.channel)));
|
||||||
|
}
|
||||||
|
if (body.pending_closing_channels && body.pending_closing_channels.length > 0) {
|
||||||
|
body.pending_closing_channels.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode.ln_server_url, channel.channel)));
|
||||||
|
}
|
||||||
|
if (body.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) {
|
||||||
|
body.pending_force_closing_channels.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode.ln_server_url, channel.channel)));
|
||||||
|
}
|
||||||
|
if (body.waiting_close_channels && body.waiting_close_channels.length > 0) {
|
||||||
|
body.waiting_close_channels.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode.ln_server_url, channel.channel)));
|
||||||
|
}
|
||||||
|
return Promise.all(promises).then((values) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Pending Channels', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Pending Channels Received' });
|
||||||
|
return res.status(200).json(body);
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Get Pending Channel Aliases Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'List Pending Channels Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getClosedChannels = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting Closed Channels..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/closed';
|
||||||
|
options.qs = req.query;
|
||||||
|
request(options).then((body) => {
|
||||||
|
if (body.channels && body.channels.length > 0) {
|
||||||
|
return Promise.all(body.channels.map((channel) => {
|
||||||
|
channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type;
|
||||||
|
return getAliasForChannel(req.session.selectedNode.ln_server_url, channel);
|
||||||
|
})).then((values) => {
|
||||||
|
body.channels = common.sortDescByKey(body.channels, 'close_height');
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closed Channels', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels Received' });
|
||||||
|
return res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Get Closed Channel Aliases Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
body.channels = [];
|
||||||
|
return res.status(200).json(body);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'List Closed Channels Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const postChannel = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Opening Channel..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels';
|
||||||
|
options.form = {
|
||||||
|
node_pubkey_string: req.body.node_pubkey,
|
||||||
|
local_funding_amount: req.body.local_funding_amount,
|
||||||
|
private: req.body.private,
|
||||||
|
spend_unconfirmed: req.body.spend_unconfirmed
|
||||||
|
};
|
||||||
|
if (req.body.trans_type === '1') {
|
||||||
|
options.form.target_conf = req.body.trans_type_value;
|
||||||
|
}
|
||||||
|
else if (req.body.trans_type === '2') {
|
||||||
|
options.form.sat_per_byte = req.body.trans_type_value;
|
||||||
|
}
|
||||||
|
options.form = JSON.stringify(options.form);
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channel Open Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channels Opened' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Open Channel Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const postTransactions = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sending Payment..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/transactions';
|
||||||
|
options.form = { payment_request: req.body.paymentReq };
|
||||||
|
if (req.body.paymentAmount) {
|
||||||
|
options.form.amt = req.body.paymentAmount;
|
||||||
|
}
|
||||||
|
if (req.body.feeLimit) {
|
||||||
|
options.form.fee_limit = req.body.feeLimit;
|
||||||
|
}
|
||||||
|
if (req.body.outgoingChannel) {
|
||||||
|
options.form.outgoing_chan_id = req.body.outgoingChannel;
|
||||||
|
}
|
||||||
|
if (req.body.allowSelfPayment) {
|
||||||
|
options.form.allow_self_payment = req.body.allowSelfPayment;
|
||||||
|
}
|
||||||
|
if (req.body.lastHopPubkey) {
|
||||||
|
options.form.last_hop_pubkey = Buffer.from(req.body.lastHopPubkey, 'hex').toString('base64');
|
||||||
|
}
|
||||||
|
options.form = JSON.stringify(options.form);
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Send Payment Options', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Send Payment Response', data: body });
|
||||||
|
if (body.payment_error) {
|
||||||
|
const err = common.handleError(body.payment_error, 'Channels', 'Send Payment Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Payment Sent' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Send Payment Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const closeChannel = (req, res, next) => {
|
||||||
|
try {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' });
|
||||||
|
if (!req.session.selectedNode) {
|
||||||
|
const err = common.handleError({ message: 'Session Expired after a day\'s inactivity.', statusCode: 401 }, 'Session Expired', 'Session Expiry Error', null);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
const channelpoint = req.params.channelPoint.replace(':', '/');
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/' + channelpoint + '?force=' + req.query.force;
|
||||||
|
if (req.query.target_conf) {
|
||||||
|
options.url = options.url + '&target_conf=' + req.query.target_conf;
|
||||||
|
}
|
||||||
|
if (req.query.sat_per_byte) {
|
||||||
|
options.url = options.url + '&sat_per_byte=' + req.query.sat_per_byte;
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Closing Channel Options URL', data: options.url });
|
||||||
|
request.delete(options);
|
||||||
|
res.status(202).json({ message: 'Close channel request has been submitted.' });
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Channels', msg: 'Close Channel Error', error: error.message });
|
||||||
|
return res.status(500).json({ message: 'Close Channel Error', error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const postChanPolicy = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Updating Channel Policy..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/chanpolicy';
|
||||||
|
if (req.body.chanPoint === 'all') {
|
||||||
|
options.form = JSON.stringify({
|
||||||
|
global: true,
|
||||||
|
base_fee_msat: req.body.baseFeeMsat,
|
||||||
|
fee_rate: parseFloat((req.body.feeRate / 1000000).toString()),
|
||||||
|
time_lock_delta: parseInt(req.body.timeLockDelta)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const breakPoint = req.body.chanPoint.indexOf(':');
|
||||||
|
const txid_str = req.body.chanPoint.substring(0, breakPoint);
|
||||||
|
const output_idx = req.body.chanPoint.substring(breakPoint + 1, req.body.chanPoint.length);
|
||||||
|
options.form = JSON.stringify({
|
||||||
|
base_fee_msat: req.body.baseFeeMsat,
|
||||||
|
fee_rate: parseFloat((req.body.feeRate / 1000000).toString()),
|
||||||
|
time_lock_delta: parseInt(req.body.timeLockDelta),
|
||||||
|
chan_point: { funding_txid_str: txid_str, output_index: parseInt(output_idx) }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy Options', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Update Channel Policy', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Policy Updated' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Channels', 'Update Channel Policy Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,240 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import { sep } from 'path';
|
||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
function getFilesList(channelBackupPath, callback) {
|
||||||
|
const files_list = [];
|
||||||
|
let all_restore_exists = false;
|
||||||
|
let response = { all_restore_exists: false, files: [] } || { message: '', error: {}, statusCode: 500 };
|
||||||
|
fs.readdir(channelBackupPath + sep + 'restore', (err, files) => {
|
||||||
|
if (err && err.code !== 'ENOENT' && err.errno !== -4058) {
|
||||||
|
response = { message: 'Channels Restore List Failed!', error: err, statusCode: 500 };
|
||||||
|
}
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
files.forEach((file) => {
|
||||||
|
if (!file.includes('.restored')) {
|
||||||
|
if (file.toLowerCase() === 'channel-all.bak' || file.toLowerCase() === 'backup-channel-all.bak') {
|
||||||
|
all_restore_exists = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
files_list.push({ channel_point: file.substring(8, file.length - 4).replace('-', ':') });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
response = { all_restore_exists: all_restore_exists, files: files_list };
|
||||||
|
callback(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export const getBackup = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Getting Channel Backup..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
let channel_backup_file = '';
|
||||||
|
let message = '';
|
||||||
|
if (req.params.channelPoint === 'ALL') {
|
||||||
|
channel_backup_file = req.session.selectedNode.channel_backup_path + sep + 'channel-all.bak';
|
||||||
|
message = 'All Channels Backup Successful.';
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
channel_backup_file = req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak';
|
||||||
|
message = 'Channel Backup Successful.';
|
||||||
|
const channelpoint = req.params.channelPoint.replace(':', '/');
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/' + channelpoint;
|
||||||
|
const exists = fs.existsSync(channel_backup_file);
|
||||||
|
if (exists) {
|
||||||
|
fs.writeFile(channel_backup_file, '', () => { });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
const createStream = fs.createWriteStream(channel_backup_file);
|
||||||
|
createStream.end();
|
||||||
|
}
|
||||||
|
catch (errRes) {
|
||||||
|
const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'ChannelsBackup', msg: 'Channel Backup', data: body });
|
||||||
|
fs.writeFile(channel_backup_file, JSON.stringify(body), (errRes) => {
|
||||||
|
if (errRes) {
|
||||||
|
const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Backup Finished' });
|
||||||
|
res.status(200).json({ message: message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'ChannelsBackup', 'Backup Channels Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const postBackupVerify = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Verifying Channel Backup..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/verify';
|
||||||
|
let channel_verify_file = '';
|
||||||
|
let message = '';
|
||||||
|
let verify_backup = '';
|
||||||
|
if (req.params.channelPoint === 'ALL') {
|
||||||
|
message = 'All Channels Verify Successful.';
|
||||||
|
channel_verify_file = req.session.selectedNode.channel_backup_path + sep + 'channel-all.bak';
|
||||||
|
const exists = fs.existsSync(channel_verify_file);
|
||||||
|
if (exists) {
|
||||||
|
verify_backup = fs.readFileSync(channel_verify_file, 'utf-8');
|
||||||
|
if (verify_backup !== '') {
|
||||||
|
const verify_backup_json = JSON.parse(verify_backup);
|
||||||
|
delete verify_backup_json.single_chan_backups;
|
||||||
|
options.form = JSON.stringify(verify_backup_json);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const errMsg = 'Channel backup to verify does not Exist.';
|
||||||
|
const err = common.handleError({ statusCode: 404, message: 'Verify Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
verify_backup = '';
|
||||||
|
const errMsg = 'Channel backup to verify does not Exist.';
|
||||||
|
const err = common.handleError({ statusCode: 404, message: 'Verify Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message = 'Channel Verify Successful.';
|
||||||
|
channel_verify_file = req.session.selectedNode.channel_backup_path + sep + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak';
|
||||||
|
const exists = fs.existsSync(channel_verify_file);
|
||||||
|
if (exists) {
|
||||||
|
verify_backup = fs.readFileSync(channel_verify_file, 'utf-8');
|
||||||
|
options.form = JSON.stringify({ single_chan_backups: { chan_backups: [JSON.parse(verify_backup)] } });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
verify_backup = '';
|
||||||
|
const errMsg = 'Channel backup to verify does not Exist.';
|
||||||
|
const err = common.handleError({ statusCode: 404, message: 'Verify Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (verify_backup !== '') {
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'ChannelBackup', msg: 'Channel Backup Verify', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Backup Verified' });
|
||||||
|
res.status(201).json({ message: message });
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'ChannelsBackup', 'Verify Channels Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const postRestore = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Restoring Channel Backup..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/channels/backup/restore';
|
||||||
|
let channel_restore_file = '';
|
||||||
|
let message = '';
|
||||||
|
let restore_backup = '';
|
||||||
|
if (req.params.channelPoint === 'ALL') {
|
||||||
|
message = 'All Channels Restore Successful.';
|
||||||
|
channel_restore_file = req.session.selectedNode.channel_backup_path + sep + 'restore' + sep;
|
||||||
|
const exists = fs.existsSync(channel_restore_file + 'channel-all.bak');
|
||||||
|
const downloaded_exists = fs.existsSync(channel_restore_file + 'backup-channel-all.bak');
|
||||||
|
if (exists) {
|
||||||
|
restore_backup = fs.readFileSync(channel_restore_file + 'channel-all.bak', 'utf-8');
|
||||||
|
if (restore_backup !== '') {
|
||||||
|
const restore_backup_json = JSON.parse(restore_backup);
|
||||||
|
options.form = JSON.stringify({ multi_chan_backup: restore_backup_json.multi_chan_backup.multi_chan_backup });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const errMsg = 'Channel backup to restore does not Exist.';
|
||||||
|
const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (downloaded_exists) {
|
||||||
|
restore_backup = fs.readFileSync(channel_restore_file + 'backup-channel-all.bak', 'utf-8');
|
||||||
|
if (restore_backup !== '') {
|
||||||
|
const restore_backup_json = JSON.parse(restore_backup);
|
||||||
|
options.form = JSON.stringify({ multi_chan_backup: restore_backup_json.multi_chan_backup.multi_chan_backup });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const errMsg = 'Channel backup to restore does not Exist.';
|
||||||
|
const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
restore_backup = '';
|
||||||
|
const errMsg = 'Channel backup to restore does not Exist.';
|
||||||
|
const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message = 'Channel Restore Successful.';
|
||||||
|
channel_restore_file = req.session.selectedNode.channel_backup_path + sep + 'restore' + sep + 'channel-' + req.params.channelPoint.replace(':', '-') + '.bak';
|
||||||
|
const exists = fs.existsSync(channel_restore_file);
|
||||||
|
if (exists) {
|
||||||
|
restore_backup = fs.readFileSync(channel_restore_file, 'utf-8');
|
||||||
|
options.form = JSON.stringify({ chan_backups: { chan_backups: [JSON.parse(restore_backup)] } });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
restore_backup = '';
|
||||||
|
const errMsg = 'Channel backup to restore does not Exist.';
|
||||||
|
const err = common.handleError({ statusCode: 404, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (restore_backup !== '') {
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'ChannelBackup', msg: 'Channel Backup Restore', data: body });
|
||||||
|
if (req.params.channelPoint === 'ALL') {
|
||||||
|
channel_restore_file = channel_restore_file + 'channel-all.bak';
|
||||||
|
}
|
||||||
|
fs.rename(channel_restore_file, channel_restore_file + '.restored', () => {
|
||||||
|
getFilesList(req.session.selectedNode.channel_backup_path, (getFilesListRes) => {
|
||||||
|
if (getFilesListRes.error) {
|
||||||
|
const errMsg = getFilesListRes.error;
|
||||||
|
const err = common.handleError({ statusCode: 500, message: 'Restore Channel Error', error: errMsg }, 'ChannelBackup', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.error, list: getFilesListRes });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'ChannelBackup', msg: 'Channel Restored' });
|
||||||
|
return res.status(201).json({ message: message, list: getFilesListRes });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'ChannelsBackup', 'Restore Channel Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getRestoreList = (req, res, next) => {
|
||||||
|
getFilesList(req.session.selectedNode.channel_backup_path, (getFilesListRes) => {
|
||||||
|
if (getFilesListRes.error) {
|
||||||
|
return res.status(getFilesListRes.statusCode).json(getFilesListRes);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return res.status(200).json(getFilesListRes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,48 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { getAllForwardingEvents } from './switch.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getFees = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Getting Fees..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/fees';
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Fee Received', data: body });
|
||||||
|
const today = new Date(Date.now());
|
||||||
|
const start_date = new Date(today.getFullYear(), today.getMonth(), 1, 0, 0, 0);
|
||||||
|
const current_time = (Math.round(today.getTime() / 1000));
|
||||||
|
const month_start_time = (Math.round(start_date.getTime() / 1000));
|
||||||
|
const week_start_time = current_time - 604800;
|
||||||
|
const day_start_time = current_time - 86400;
|
||||||
|
return getAllForwardingEvents(req, month_start_time, current_time, 0, (history) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Forwarding History Received', data: history });
|
||||||
|
const daily_sum = history.forwarding_events.reduce((acc, curr) => ((curr.timestamp >= day_start_time) ? [(acc[0] + 1), (acc[1] + +curr.fee_msat)] : acc), [0, 0]);
|
||||||
|
const weekly_sum = history.forwarding_events.reduce((acc, curr) => ((curr.timestamp >= week_start_time) ? [(acc[0] + 1), (acc[1] + +curr.fee_msat)] : acc), [0, 0]);
|
||||||
|
const monthly_sum = history.forwarding_events.reduce((acc, curr) => [(acc[0] + 1), (acc[1] + +curr.fee_msat)], [0, 0]);
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Daily Sum (Transactions, Fee)', data: daily_sum });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Weekly Sum (Transactions, Fee)', data: weekly_sum });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Fees', msg: 'Monthly Sum (Transactions, Fee)', data: monthly_sum });
|
||||||
|
body.daily_tx_count = daily_sum[0];
|
||||||
|
body.weekly_tx_count = weekly_sum[0];
|
||||||
|
body.monthly_tx_count = monthly_sum[0];
|
||||||
|
body.day_fee_sum = (daily_sum[1] / 1000).toFixed(2);
|
||||||
|
body.week_fee_sum = (weekly_sum[1] / 1000).toFixed(2);
|
||||||
|
body.month_fee_sum = (monthly_sum[1] / 1000).toFixed(2);
|
||||||
|
body.forwarding_events_history = history;
|
||||||
|
if (history.error) {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Fees', msg: 'Fetch Forwarding Events Error', error: history.error });
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fees Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Fees', 'Get Forwarding Events Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,57 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Database } from '../../utils/database.js';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { LNDWSClient } from './webSocketClient.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
const lndWsClient = LNDWSClient;
|
||||||
|
const databaseService = Database;
|
||||||
|
export const getInfo = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Getting LND Node Information..' });
|
||||||
|
common.logEnvVariables(req);
|
||||||
|
common.setOptions(req);
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo';
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Selected Node', data: req.session.selectedNode.ln_node });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Calling Info from LND server url', data: options.url });
|
||||||
|
if (!options.headers || !options.headers['Grpc-Metadata-macaroon']) {
|
||||||
|
const errMsg = 'LND Get info failed due to bad or missing macaroon! Please check RTL-Config.json to verify the setup!';
|
||||||
|
const err = common.handleError({ statusCode: 502, message: 'Bad or Missing Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
common.nodes.map((node) => {
|
||||||
|
if (node.ln_implementation === 'LND') {
|
||||||
|
common.getAllNodeAllChannelBackup(node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Node Information', data: body });
|
||||||
|
const body_str = (!body) ? '' : JSON.stringify(body);
|
||||||
|
const search_idx = (!body) ? -1 : body_str.search('Not Found');
|
||||||
|
if (!body || search_idx > -1 || body.error) {
|
||||||
|
if (body && !body.error) {
|
||||||
|
body.error = 'Error From Server!';
|
||||||
|
}
|
||||||
|
const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'LND Node Information Received' });
|
||||||
|
req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
|
||||||
|
lndWsClient.updateSelectedNode(req.session.selectedNode);
|
||||||
|
databaseService.loadDatabase(req.session.selectedNode);
|
||||||
|
res.status(200).json(body);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,169 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getAliasFromPubkey = (lnServerUrl, pubkey) => {
|
||||||
|
options.url = lnServerUrl + '/v1/graph/node/' + pubkey;
|
||||||
|
return request(options).then((res) => {
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Graph', msg: 'Alias', data: res.node.alias });
|
||||||
|
return res.node.alias;
|
||||||
|
}).
|
||||||
|
catch((err) => pubkey.substring(0, 17) + '...');
|
||||||
|
};
|
||||||
|
export const getDescribeGraph = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Network Graph..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/graph';
|
||||||
|
request.get(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Describe Graph Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Network Graph Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Graph', 'Describe Graph Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getGraphInfo = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Information..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/info';
|
||||||
|
request.get(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Network Information After Rounding and Conversion', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Information Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Graph', 'Graph Information Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getGraphNode = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Node Information..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/node/' + req.params.pubKey;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Node Info Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Node Information Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Graph', 'Get Node Info Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getGraphEdge = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Edge Information..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/edge/' + req.params.chanid;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Edge Info Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Edge Information Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Graph', 'Get Edge Info Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getQueryRoutes = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Graph Routes..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/routes/' + req.params.destPubkey + '/' + req.params.amount;
|
||||||
|
if (req.query.outgoing_chan_id) {
|
||||||
|
options.url = options.url + '?outgoing_chan_id=' + req.query.outgoing_chan_id;
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes URL', data: options.url });
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes Received', data: body });
|
||||||
|
if (body.routes && body.routes.length && body.routes.length > 0 && body.routes[0].hops && body.routes[0].hops.length && body.routes[0].hops.length > 0) {
|
||||||
|
return Promise.all(body.routes[0].hops.map((hop) => getAliasFromPubkey(req.session.selectedNode.ln_server_url, hop.pub_key))).
|
||||||
|
then((values) => {
|
||||||
|
body.routes[0].hops.map((hop, i) => {
|
||||||
|
hop.hop_sequence = i + 1;
|
||||||
|
hop.pubkey_alias = values[i];
|
||||||
|
return hop;
|
||||||
|
});
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Hops with Alias', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes Received' });
|
||||||
|
return res.status(200).json(body);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getRemoteFeePolicy = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Getting Remote Fee Policy..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/graph/edge/' + req.params.chanid;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Edge Info Received', data: body });
|
||||||
|
let remoteNodeFee = {};
|
||||||
|
if (body.node1_pub === req.params.localPubkey) {
|
||||||
|
remoteNodeFee = {
|
||||||
|
time_lock_delta: body.node2_policy.time_lock_delta,
|
||||||
|
fee_base_msat: body.node2_policy.fee_base_msat,
|
||||||
|
fee_rate_milli_msat: body.node2_policy.fee_rate_milli_msat
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (body.node2_pub === req.params.localPubkey) {
|
||||||
|
remoteNodeFee = {
|
||||||
|
time_lock_delta: body.node1_policy.time_lock_delta,
|
||||||
|
fee_base_msat: body.node1_policy.fee_base_msat,
|
||||||
|
fee_rate_milli_msat: body.node1_policy.fee_rate_milli_msat
|
||||||
|
};
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Remote Fee Policy Received' });
|
||||||
|
res.status(200).json(remoteNodeFee);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Graph', 'Remote Fee Policy Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getAliasesForPubkeys = (req, res, next) => {
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
if (req.query.pubkeys) {
|
||||||
|
const pubkeyArr = req.query.pubkeys.split(',');
|
||||||
|
return Promise.all(pubkeyArr.map((pubkey) => getAliasFromPubkey(req.session.selectedNode.ln_server_url, pubkey))).
|
||||||
|
then((values) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Node Alias', data: values });
|
||||||
|
res.status(200).json(values);
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Graph', 'Get Aliases for Pubkeys Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return res.status(200).json([]);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,93 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { LNDWSClient } from './webSocketClient.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
const lndWsClient = LNDWSClient;
|
||||||
|
export const getInvoice = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Getting Invoice Information..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/' + req.params.rHashStr;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoice Info Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Information Received' });
|
||||||
|
body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : '';
|
||||||
|
body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : '';
|
||||||
|
body.description_hash = body.description_hash ? Buffer.from(body.description_hash, 'base64').toString('hex') : null;
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoices', 'Get Invoice Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const listInvoices = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Getting List Invoices..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=' + req.query.num_max_invoices + '&index_offset=' + req.query.index_offset +
|
||||||
|
'&reversed=' + req.query.reversed;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body });
|
||||||
|
if (body.invoices && body.invoices.length > 0) {
|
||||||
|
body.invoices.forEach((invoice) => {
|
||||||
|
invoice.r_preimage = invoice.r_preimage ? Buffer.from(invoice.r_preimage, 'base64').toString('hex') : '';
|
||||||
|
invoice.r_hash = invoice.r_hash ? Buffer.from(invoice.r_hash, 'base64').toString('hex') : '';
|
||||||
|
invoice.description_hash = invoice.description_hash ? Buffer.from(invoice.description_hash, 'base64').toString('hex') : null;
|
||||||
|
});
|
||||||
|
body.invoices = common.sortDescByKey(body.invoices, 'creation_date');
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoices List Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoices', 'List Invoices Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const addInvoice = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Adding Invoice..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/invoices';
|
||||||
|
options.form = {
|
||||||
|
memo: req.body.memo,
|
||||||
|
private: req.body.private,
|
||||||
|
expiry: req.body.expiry
|
||||||
|
};
|
||||||
|
if (req.body.amount > 0 && req.body.amount < 1) {
|
||||||
|
options.form.value_msat = req.body.amount * 1000;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.form.value = req.body.amount;
|
||||||
|
}
|
||||||
|
options.form = JSON.stringify(options.form);
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Add Invoice Responce', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Added' });
|
||||||
|
try {
|
||||||
|
if (body.r_hash) {
|
||||||
|
lndWsClient.subscribeToInvoice(options, req.session.selectedNode, body.r_hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (errRes) {
|
||||||
|
const err = common.handleError(errRes, 'Invoices', 'Subscribe to Newly Added Invoice Error', req.session.selectedNode);
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'ERROR', fileName: 'Invoice', msg: 'Subscribe to Newly Added Invoice Error', error: err });
|
||||||
|
}
|
||||||
|
body.r_preimage = body.r_preimage ? Buffer.from(body.r_preimage, 'base64').toString('hex') : '';
|
||||||
|
body.r_hash = body.r_hash ? Buffer.from(body.r_hash, 'base64').toString('hex') : '';
|
||||||
|
body.description_hash = body.description_hash ? Buffer.from(body.description_hash, 'base64').toString('hex') : null;
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Invoices', 'Add Invoice Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,45 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const signMessage = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Signing Message..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/signmessage';
|
||||||
|
options.form = JSON.stringify({
|
||||||
|
msg: Buffer.from(req.body.message).toString('base64')
|
||||||
|
});
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Messages', msg: 'Message Signed', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Signed' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Messages', 'Sign Message Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const verifyMessage = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Verifying Message..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/verifymessage';
|
||||||
|
options.form = JSON.stringify({
|
||||||
|
msg: Buffer.from(req.body.message).toString('base64'),
|
||||||
|
signature: req.body.signature
|
||||||
|
});
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Messages', msg: 'Message Verified', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Message', msg: 'Message Verified' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Messages', 'Verify Message Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,22 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getNewAddress = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'NewAddress', msg: 'Getting New Address..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/newaddress?type=' + req.query.type;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'NewAddress', msg: 'New Address Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'NewAddress', msg: 'New Address Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'NewAddress', 'New Address Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,89 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const decodePaymentFromPaymentRequest = (lnServerUrl, payment) => {
|
||||||
|
options.url = lnServerUrl + '/v1/payreq/' + payment;
|
||||||
|
return request(options).then((res) => {
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'PayReq', msg: 'Description', data: res.description });
|
||||||
|
return res;
|
||||||
|
}).catch((err) => { });
|
||||||
|
};
|
||||||
|
export const decodePayment = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Decoding Payment..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/payreq/' + req.params.payRequest;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'PayReq', msg: 'Payment Decode Received', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Payment Decoded' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'PayRequest', 'Decode Payment Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const decodePayments = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Decoding Payments List..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
if (req.body.payments) {
|
||||||
|
const paymentsArr = req.body.payments.split(',');
|
||||||
|
return Promise.all(paymentsArr.map((payment) => decodePaymentFromPaymentRequest(req.session.selectedNode.ln_server_url, payment))).
|
||||||
|
then((values) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'PayReq', msg: 'Decoded Payments', data: values });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Payment List Decoded' });
|
||||||
|
res.status(200).json(values);
|
||||||
|
}).
|
||||||
|
catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'PayRequest', 'Decode Payments Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'PayRequest', msg: 'Empty Payment List Decoded' });
|
||||||
|
return res.status(200).json([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getPayments = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting Payments List..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/payments?max_payments=' + req.query.max_payments + '&index_offset=' + req.query.index_offset + '&reversed=' + req.query.reversed;
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body });
|
||||||
|
if (body.payments && body.payments.length > 0) {
|
||||||
|
body.payments = common.sortDescByKey(body.payments, 'creation_date');
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payments After Sort', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payments List Received' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getAllLightningTransactions = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Getting All Lightning Transactions..' });
|
||||||
|
const options1 = JSON.parse(JSON.stringify(common.getOptions(req)));
|
||||||
|
const options2 = JSON.parse(JSON.stringify(common.getOptions(req)));
|
||||||
|
options1.url = req.session.selectedNode.ln_server_url + '/v1/payments?max_payments=100000&index_offset=0&reversed=true';
|
||||||
|
options2.url = req.session.selectedNode.ln_server_url + '/v1/invoices?num_max_invoices=100000&index_offset=0&reversed=true';
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'All Payments Options', data: options1 });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'All Invoices Options', data: options2 });
|
||||||
|
return Promise.all([request(options1), request(options2)]).then((values) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payments & Invoices Received' });
|
||||||
|
res.status(200).json({ listPaymentsAll: values[0], listInvoicesAll: values[1] });
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Payments', 'All Lightning Transactions Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,97 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getAliasForPeers = (lnServerUrl, peer) => {
|
||||||
|
options.url = lnServerUrl + '/v1/graph/node/' + peer.pub_key;
|
||||||
|
return request(options).then((aliasBody) => {
|
||||||
|
logger.log({ selectedNode: null, level: 'DEBUG', fileName: 'Peers', msg: 'Alias', data: aliasBody.node.alias });
|
||||||
|
peer.alias = aliasBody.node.alias;
|
||||||
|
return aliasBody.node.alias;
|
||||||
|
}).catch((err) => {
|
||||||
|
peer.alias = peer.pub_key.slice(0, 10) + '...' + peer.pub_key.slice(-10);
|
||||||
|
return peer.pub_key;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getPeers = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Getting Peers..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/peers';
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers Received', data: body });
|
||||||
|
const peers = !body.peers ? [] : body.peers;
|
||||||
|
return Promise.all(peers.map((peer) => getAliasForPeers(req.session.selectedNode.ln_server_url, peer))).then((values) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers with Alias before Sort', data: body });
|
||||||
|
if (body.peers) {
|
||||||
|
body.peers = common.sortDescByStrKey(body.peers, 'alias');
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers with Alias after Sort', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers Received' });
|
||||||
|
res.status(200).json(body.peers);
|
||||||
|
});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'List Peers Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const postPeer = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Connecting Peer..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/peers';
|
||||||
|
options.form = JSON.stringify({
|
||||||
|
addr: { host: req.body.host, pubkey: req.body.pubkey },
|
||||||
|
perm: req.body.perm
|
||||||
|
});
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Added', data: body });
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/peers';
|
||||||
|
request(options).then((body) => {
|
||||||
|
const peers = (!body.peers) ? [] : body.peers;
|
||||||
|
return Promise.all(peers.map((peer) => getAliasForPeers(req.session.selectedNode.ln_server_url, peer))).then((values) => {
|
||||||
|
if (body.peers) {
|
||||||
|
body.peers = common.sortDescByStrKey(body.peers, 'alias');
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer with Alias', data: body });
|
||||||
|
body.peers = common.newestOnTop(body.peers, 'pub_key', req.body.pubkey);
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer with Newest On Top', data: body });
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Added Successfully' });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Connected' });
|
||||||
|
res.status(201).json(body.peers);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Connect Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const deletePeer = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Disconnecting Peer..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/peers/' + req.params.peerPubKey;
|
||||||
|
request.delete(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Detach Peer Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Detached', data: req.params.peerPubKey });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peer Disconnected' });
|
||||||
|
res.status(204).json({});
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Peers', 'Disconnect Peer Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,61 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
let responseData = { forwarding_events: [], last_offset_index: 0 };
|
||||||
|
const num_max_events = 100;
|
||||||
|
export const forwardingHistory = (req, res, next) => {
|
||||||
|
getAllForwardingEvents(req, req.body.start_time, req.body.end_time, 0, (eventsResponse) => {
|
||||||
|
if (eventsResponse.error) {
|
||||||
|
res.status(eventsResponse.error.statusCode).json(eventsResponse);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.status(201).json(eventsResponse);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getAllForwardingEvents = (req, start, end, offset, callback) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Switch', msg: 'Getting Forwarding Events..' });
|
||||||
|
if (offset === 0) {
|
||||||
|
responseData = { forwarding_events: [], last_offset_index: 0 };
|
||||||
|
}
|
||||||
|
if (!req.session.selectedNode) {
|
||||||
|
const err = common.handleError({ message: 'Session Expired after a day\'s inactivity.', statusCode: 401 }, 'Balance', 'Get Balance Error', req.session.selectedNode);
|
||||||
|
return callback({ message: err.message, error: err.error, statusCode: err.statusCode });
|
||||||
|
}
|
||||||
|
options = common.getOptions(req);
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/switch';
|
||||||
|
options.form = {};
|
||||||
|
if (start) {
|
||||||
|
options.form.start_time = start;
|
||||||
|
}
|
||||||
|
if (end) {
|
||||||
|
options.form.end_time = end;
|
||||||
|
}
|
||||||
|
options.form.num_max_events = num_max_events;
|
||||||
|
options.form.index_offset = offset;
|
||||||
|
options.form = JSON.stringify(options.form);
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Switch', msg: 'Forwarding History Params', data: options.form });
|
||||||
|
return request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Switch', msg: 'Forwarding History', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Switch', msg: 'Forwarding Events Received' });
|
||||||
|
if (body.forwarding_events) {
|
||||||
|
responseData.forwarding_events.push(...body.forwarding_events);
|
||||||
|
}
|
||||||
|
if (!body.last_offset_index || body.last_offset_index < offset + num_max_events) {
|
||||||
|
responseData.last_offset_index = body.last_offset_index ? body.last_offset_index : 0;
|
||||||
|
if (responseData.forwarding_events) {
|
||||||
|
responseData.forwarding_events = common.sortDescByKey(responseData.forwarding_events, 'timestamp');
|
||||||
|
}
|
||||||
|
return callback(responseData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return getAllForwardingEvents(req, start, end, offset + num_max_events, callback);
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Switch', 'Get All Forwarding Events Error', req.session.selectedNode);
|
||||||
|
return callback({ message: err.message, error: err.error, statusCode: err.statusCode });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,51 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const getTransactions = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Getting Transactions..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/transactions';
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Transactions', msg: 'Transaction Received', data: body });
|
||||||
|
if (body.transactions && body.transactions.length > 0) {
|
||||||
|
body.transactions = common.sortDescByKey(body.transactions, 'time_stamp');
|
||||||
|
}
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Transactions Received' });
|
||||||
|
res.status(200).json(body.transactions);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Transactions', 'List Transactions Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const postTransactions = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Sending Transaction..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/transactions';
|
||||||
|
options.form = {
|
||||||
|
amount: req.body.amount,
|
||||||
|
addr: req.body.address,
|
||||||
|
sat_per_byte: req.body.fees,
|
||||||
|
target_conf: req.body.blocks
|
||||||
|
};
|
||||||
|
if (req.body.sendAll) {
|
||||||
|
options.form.send_all = req.body.sendAll;
|
||||||
|
}
|
||||||
|
options.form = JSON.stringify(options.form);
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Transactions', msg: 'Transaction Post Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Transactions', msg: 'Transaction Sent' });
|
||||||
|
res.status(201).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Transactions', 'Send Transaction Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,219 @@
|
|||||||
|
import atob from 'atob';
|
||||||
|
import request from 'request-promise';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
let options = null;
|
||||||
|
const logger = Logger;
|
||||||
|
const common = Common;
|
||||||
|
export const genSeed = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Generating Seed..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
if (req.params.passphrase) {
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/genseed?aezeed_passphrase=' + Buffer.from(atob(req.params.passphrase)).toString('base64');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/genseed';
|
||||||
|
}
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Seed Generated' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Wallet', 'Gen Seed Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const operateWallet = (req, res, next) => {
|
||||||
|
let err_message = '';
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.method = 'POST';
|
||||||
|
if (!req.params.operation || req.params.operation === 'unlockwallet') {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Unlocking Wallet..' });
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/unlockwallet';
|
||||||
|
options.form = JSON.stringify({
|
||||||
|
wallet_password: Buffer.from(atob(req.body.wallet_password)).toString('base64')
|
||||||
|
});
|
||||||
|
err_message = 'Unlocking wallet failed! Verify that lnd is running and the wallet is locked!';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Initializing Wallet..' });
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v1/initwallet';
|
||||||
|
if (req.body.aezeed_passphrase && req.body.aezeed_passphrase !== '') {
|
||||||
|
options.form = JSON.stringify({
|
||||||
|
wallet_password: Buffer.from(atob(req.body.wallet_password)).toString('base64'),
|
||||||
|
cipher_seed_mnemonic: req.body.cipher_seed_mnemonic,
|
||||||
|
aezeed_passphrase: Buffer.from(atob(req.body.aezeed_passphrase)).toString('base64')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.form = JSON.stringify({
|
||||||
|
wallet_password: Buffer.from(atob(req.body.wallet_password)).toString('base64'),
|
||||||
|
cipher_seed_mnemonic: req.body.cipher_seed_mnemonic
|
||||||
|
});
|
||||||
|
}
|
||||||
|
err_message = 'Initializing wallet failed!';
|
||||||
|
}
|
||||||
|
request(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'Wallet Response', data: body });
|
||||||
|
const body_str = (!body) ? '' : JSON.stringify(body);
|
||||||
|
const search_idx = (!body) ? -1 : body_str.search('Not Found');
|
||||||
|
if (!body) {
|
||||||
|
const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: err_message }, 'Wallet', err_message, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.error, error: err.error });
|
||||||
|
}
|
||||||
|
else if (search_idx > -1) {
|
||||||
|
const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: err_message }, 'Wallet', err_message, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.error, error: err.error });
|
||||||
|
}
|
||||||
|
else if (body.error) {
|
||||||
|
if ((body.code === 1 && body.error === 'context canceled') || (body.code === 14 && body.error === 'transport is closing')) {
|
||||||
|
res.status(201).json('Successful');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const errMsg = (body.error && typeof body.error === 'object') ? JSON.stringify(body.error) : (body.error && typeof body.error === 'string') ? body.error : err_message;
|
||||||
|
const err = common.handleError({ statusCode: 500, message: 'Wallet Error', error: errMsg }, 'Wallet', errMsg, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.error, error: err.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Wallet Unlocked/Initialized' });
|
||||||
|
res.status(201).json('Successful');
|
||||||
|
}
|
||||||
|
}).catch((errRes) => {
|
||||||
|
if ((errRes.error.code === 1 && errRes.error.error === 'context canceled') || (errRes.error.code === 14 && errRes.error.error === 'transport is closing')) {
|
||||||
|
res.status(201).json('Successful');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const err = common.handleError(errRes, 'Wallet', err_message, req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const updateSelNodeOptions = (req, res, next) => {
|
||||||
|
const response = common.updateSelectedNodeOptions(req);
|
||||||
|
res.status(response.status).json({ updateMessage: response.message });
|
||||||
|
};
|
||||||
|
export const getUTXOs = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Getting UTXOs..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/utxos';
|
||||||
|
if (common.isVersionCompatible(req.session.selectedNode.ln_version, '0.14.0')) {
|
||||||
|
options.form = JSON.stringify({ max_confs: req.query.max_confs });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.url = options.url + '?max_confs=' + req.query.max_confs;
|
||||||
|
}
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'UTXO List Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXOs Received' });
|
||||||
|
res.status(200).json(body.utxos ? body.utxos : []);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Wallet', 'List UTXOs Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const bumpFee = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Bumping Fee..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/bumpfee';
|
||||||
|
options.form = {};
|
||||||
|
options.form.outpoint = {
|
||||||
|
txid_str: req.body.txid,
|
||||||
|
output_index: req.body.outputIndex
|
||||||
|
};
|
||||||
|
if (req.body.targetConf) {
|
||||||
|
options.form.target_conf = req.body.targetConf;
|
||||||
|
}
|
||||||
|
else if (req.body.satPerByte) {
|
||||||
|
options.form.sat_per_byte = req.body.satPerByte;
|
||||||
|
}
|
||||||
|
options.form = JSON.stringify(options.form);
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'Bump Fee Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Fee Bumped' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Wallet', 'Bump Fee Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const labelTransaction = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Labelling Transaction..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/tx/label';
|
||||||
|
options.form = {};
|
||||||
|
options.form.txid = req.body.txid;
|
||||||
|
options.form.label = req.body.label;
|
||||||
|
options.form.overwrite = req.body.overwrite;
|
||||||
|
options.form = JSON.stringify(options.form);
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'Label Transaction Options', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'Label Transaction Post Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Transaction Labelled' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Wallet', 'Label Transaction Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const leaseUTXO = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Leasing UTXO..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/utxos/lease';
|
||||||
|
options.form = {};
|
||||||
|
options.form.id = req.body.txid;
|
||||||
|
options.form.outpoint = {
|
||||||
|
txid_bytes: req.body.txid,
|
||||||
|
output_index: req.body.outputIndex
|
||||||
|
};
|
||||||
|
options.form = JSON.stringify(options.form);
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'UTXO Lease Options', data: options.form });
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'UTXO Lease Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXO Leased' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Wallet', 'Lease UTXO Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const releaseUTXO = (req, res, next) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'Releasing UTXO..' });
|
||||||
|
options = common.getOptions(req);
|
||||||
|
if (options.error) {
|
||||||
|
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||||
|
}
|
||||||
|
options.url = req.session.selectedNode.ln_server_url + '/v2/wallet/utxos/release';
|
||||||
|
options.form = {};
|
||||||
|
options.form.id = req.body.txid;
|
||||||
|
options.form.outpoint = {
|
||||||
|
txid_bytes: req.body.txid,
|
||||||
|
output_index: req.body.outputIndex
|
||||||
|
};
|
||||||
|
options.form = JSON.stringify(options.form);
|
||||||
|
request.post(options).then((body) => {
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Wallet', msg: 'UTXO Release Response', data: body });
|
||||||
|
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Wallet', msg: 'UTXO Released' });
|
||||||
|
res.status(200).json(body);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = common.handleError(errRes, 'Wallet', 'Release UTXO Error', req.session.selectedNode);
|
||||||
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,103 @@
|
|||||||
|
import request from 'request-promise';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { Logger } from '../../utils/logger.js';
|
||||||
|
import { Common } from '../../utils/common.js';
|
||||||
|
import { WSServer } from '../../utils/webSocketServer.js';
|
||||||
|
export class LNDWebSocketClient {
|
||||||
|
constructor() {
|
||||||
|
this.logger = Logger;
|
||||||
|
this.common = Common;
|
||||||
|
this.wsServer = WSServer;
|
||||||
|
this.webSocketClients = [];
|
||||||
|
this.connect = (selectedNode) => {
|
||||||
|
try {
|
||||||
|
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
|
||||||
|
if (!clientExists && selectedNode.ln_server_url) {
|
||||||
|
const newWebSocketClient = { selectedNode: selectedNode };
|
||||||
|
this.webSocketClients.push(newWebSocketClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.fetchUnpaidInvoices = (selectedNode) => {
|
||||||
|
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Getting Unpaid Invoices..' });
|
||||||
|
const options = this.setOptionsForSelNode(selectedNode);
|
||||||
|
options.url = selectedNode.ln_server_url + '/v1/invoices?pending_only=true';
|
||||||
|
return request(options).then((body) => {
|
||||||
|
this.logger.log({ selectedNode: selectedNode, level: 'DEBUG', fileName: 'WebSocketClient', msg: 'Pending Invoices Received', data: body });
|
||||||
|
if (body.invoices && body.invoices.length > 0) {
|
||||||
|
body.invoices.forEach((invoice) => {
|
||||||
|
if (invoice.state === 'OPEN') {
|
||||||
|
this.subscribeToInvoice(options, selectedNode, invoice.r_hash);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = this.common.handleError(errRes, 'WebSocketClient', 'Pending Invoices Error', selectedNode);
|
||||||
|
return ({ message: err.message, error: err.error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.subscribeToInvoice = (options, selectedNode, rHash) => {
|
||||||
|
rHash = rHash.replace(/\+/g, '-').replace(/[/]/g, '_');
|
||||||
|
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Subscribing to Invoice ' + rHash + ' ..' });
|
||||||
|
options.url = selectedNode.ln_server_url + '/v2/invoices/subscribe/' + rHash;
|
||||||
|
request(options).then((msg) => {
|
||||||
|
this.logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'WebSocketClient', msg: 'Invoice Information Received for ' + rHash });
|
||||||
|
if (typeof msg === 'string') {
|
||||||
|
const results = msg.split('\n');
|
||||||
|
msg = (results.length && results.length > 1) ? JSON.parse(results[1]) : JSON.parse(msg);
|
||||||
|
msg.result.r_preimage = msg.result.r_preimage ? Buffer.from(msg.result.r_preimage, 'base64').toString('hex') : '';
|
||||||
|
msg.result.r_hash = msg.result.r_hash ? Buffer.from(msg.result.r_hash, 'base64').toString('hex') : '';
|
||||||
|
msg.result.description_hash = msg.result.description_hash ? Buffer.from(msg.result.description_hash, 'base64').toString('hex') : null;
|
||||||
|
}
|
||||||
|
msg['type'] = 'invoice';
|
||||||
|
msg['source'] = 'LND';
|
||||||
|
const msgStr = JSON.stringify(msg);
|
||||||
|
this.logger.log({ selectedNode: selectedNode, level: 'DEBUG', fileName: 'WebSocketClient', msg: 'Invoice Info Received', data: msgStr });
|
||||||
|
this.wsServer.sendEventsToAllLNClients(msgStr, selectedNode);
|
||||||
|
}).catch((errRes) => {
|
||||||
|
const err = this.common.handleError(errRes, 'Invoices', 'Subscribe to Invoice Error for ' + rHash, selectedNode);
|
||||||
|
const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message + ' ' + rHash }) : (typeof err === 'object') ? JSON.stringify({ error: err + ' ' + rHash }) : ('{ "error": ' + err + ' ' + rHash + ' }'));
|
||||||
|
this.wsServer.sendErrorToAllLNClients(errStr, selectedNode);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.setOptionsForSelNode = (selectedNode) => {
|
||||||
|
const options = { url: '', rejectUnauthorized: false, json: true, form: null };
|
||||||
|
try {
|
||||||
|
options['headers'] = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(selectedNode.macaroon_path, 'admin.macaroon')).toString('hex') };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'WebSocketClient', msg: 'Set Options Error', error: JSON.stringify(err) });
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
this.disconnect = (selectedNode) => {
|
||||||
|
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
|
||||||
|
if (clientExists) {
|
||||||
|
this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the LND\'s Websocket Server..' });
|
||||||
|
const clientIdx = this.webSocketClients.findIndex((wsc) => wsc.selectedNode.index === selectedNode.index);
|
||||||
|
this.webSocketClients.splice(clientIdx, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.updateSelectedNode = (newSelectedNode) => {
|
||||||
|
const clientIdx = this.webSocketClients.findIndex((wsc) => +wsc.selectedNode.index === +newSelectedNode.index);
|
||||||
|
const newClient = this.webSocketClients[clientIdx];
|
||||||
|
newClient.selectedNode = JSON.parse(JSON.stringify(newSelectedNode));
|
||||||
|
this.webSocketClients[clientIdx] = newClient;
|
||||||
|
if (this.webSocketClients[clientIdx].selectedNode.ln_version === '' || !this.webSocketClients[clientIdx].selectedNode.ln_version || this.common.isVersionCompatible(this.webSocketClients[clientIdx].selectedNode.ln_version, '0.11.0')) {
|
||||||
|
this.fetchUnpaidInvoices(this.webSocketClients[clientIdx].selectedNode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.wsServer.eventEmitterLND.on('CONNECT', (nodeIndex) => {
|
||||||
|
this.connect(this.common.findNode(+nodeIndex));
|
||||||
|
});
|
||||||
|
this.wsServer.eventEmitterLND.on('DISCONNECT', (nodeIndex) => {
|
||||||
|
this.disconnect(this.common.findNode(+nodeIndex));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const LNDWSClient = new LNDWebSocketClient();
|