chore: ws (#394)

This commit is contained in:
Anthony Potdevin 2022-01-30 22:07:05 +01:00 committed by GitHub
parent 71b04da7b2
commit 5f0c140046
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1989 additions and 116 deletions

10
.env
View file

@ -25,6 +25,16 @@
# THEME='dark'
# CURRENCY='sat'
# -----------
# Subscription Configs
# -----------
# DISABLE_ALL_SUBS=true
# DISABLE_INVOICE_SUB=true
# DISABLE_PAYMENT_SUB=true
# DISABLE_FORWARD_SUB=true
# DISABLE_CHANNEL_SUB=true
# DISABLE_BACKUP_SUB=true
# -----------
# Privacy Configs
# -----------

663
package-lock.json generated
View file

@ -14,9 +14,13 @@
"@nestjs/config": "^1.1.6",
"@nestjs/core": "^8.2.6",
"@nestjs/graphql": "^9.1.2",
"@nestjs/jwt": "^8.0.0",
"@nestjs/passport": "^8.1.0",
"@nestjs/platform-express": "^8.2.6",
"@nestjs/platform-socket.io": "^8.2.6",
"@nestjs/throttler": "^2.0.0",
"@nestjs/websockets": "^8.2.6",
"@types/socket.io": "^3.0.2",
"@visx/axis": "^2.6.0",
"@visx/chord": "^2.1.2",
"@visx/event": "^2.6.0",
@ -73,6 +77,7 @@
"rimraf": "^3.0.2",
"rxjs": "^7.5.2",
"secp256k1": "^4.0.3",
"socket.io-client": "^4.4.1",
"socks-proxy-agent": "^6.1.1",
"styled-components": "^5.3.3",
"styled-react-modal": "^2.1.0",
@ -4945,6 +4950,26 @@
}
}
},
"node_modules/@nestjs/jwt": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-8.0.0.tgz",
"integrity": "sha512-fz2LQgYY2zmuD8S+8UE215anwKyXlnB/1FwJQLVR47clNfMeFMK8WCxmn6xdPhF5JKuV1crO6FVabb1qWzDxqQ==",
"dependencies": {
"@types/jsonwebtoken": "8.5.4",
"jsonwebtoken": "8.5.1"
},
"peerDependencies": {
"@nestjs/common": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/@nestjs/jwt/node_modules/@types/jsonwebtoken": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz",
"integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@nestjs/mapped-types": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.0.tgz",
@ -4985,6 +5010,24 @@
"@nestjs/core": "^8.0.0"
}
},
"node_modules/@nestjs/platform-socket.io": {
"version": "8.2.6",
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-8.2.6.tgz",
"integrity": "sha512-Gkay52E6PmhuL1e1EF1GnJuivt4NxXaqY2I3VV4LF4X2jNHmL09EAyJofs1G6ySF8QUtR9HqtW/+ohxr7RipsQ==",
"dependencies": {
"socket.io": "4.4.1",
"tslib": "2.3.1"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/nest"
},
"peerDependencies": {
"@nestjs/common": "^8.0.0",
"@nestjs/websockets": "^8.0.0",
"rxjs": "^7.1.0"
}
},
"node_modules/@nestjs/schematics": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-8.0.5.tgz",
@ -5127,6 +5170,28 @@
"reflect-metadata": "^0.1.13"
}
},
"node_modules/@nestjs/websockets": {
"version": "8.2.6",
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-8.2.6.tgz",
"integrity": "sha512-GUdPd5X+ojNeaYE+/4c4105tb8skvQt3KyR7CCzrhMziHsRakDLQ/8LO932fh8ADrkRxr7jfAbt3UTq5FggQ2w==",
"dependencies": {
"iterare": "1.2.1",
"object-hash": "2.2.0",
"tslib": "2.3.1"
},
"peerDependencies": {
"@nestjs/common": "^8.0.0",
"@nestjs/core": "^8.0.0",
"@nestjs/platform-socket.io": "^8.0.0",
"reflect-metadata": "^0.1.12",
"rxjs": "^7.1.0"
},
"peerDependenciesMeta": {
"@nestjs/platform-socket.io": {
"optional": true
}
}
},
"node_modules/@next/env": {
"version": "12.0.8",
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.0.8.tgz",
@ -5611,6 +5676,19 @@
"@sinonjs/commons": "^1.7.0"
}
},
"node_modules/@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/@socket.io/component-emitter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
},
"node_modules/@szmarczak/http-timer": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
@ -5730,6 +5808,11 @@
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w=="
},
"node_modules/@types/component-emitter": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
"integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ=="
},
"node_modules/@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@ -5741,8 +5824,7 @@
"node_modules/@types/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
"dev": true
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"node_modules/@types/cookiejar": {
"version": "2.1.2",
@ -6158,6 +6240,15 @@
"@types/node": "*"
}
},
"node_modules/@types/socket.io": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.2.tgz",
"integrity": "sha512-pu0sN9m5VjCxBZVK8hW37ZcMe8rjn4HHggBN5CbaRTvFwv5jOmuIRZEuddsBPa9Th0ts0SIo3Niukq+95cMBbQ==",
"deprecated": "This is a stub types definition. socket.io provides its own type definitions, so you do not need this installed.",
"dependencies": {
"socket.io": "*"
}
},
"node_modules/@types/stack-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
@ -8235,6 +8326,14 @@
}
]
},
"node_modules/base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
"engines": {
"node": "^4.5.0 || >= 5.9"
}
},
"node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@ -9370,8 +9469,7 @@
"node_modules/component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
"dev": true
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"node_modules/concat-map": {
"version": "0.0.1",
@ -10780,6 +10878,135 @@
"once": "^1.4.0"
}
},
"node_modules/engine.io": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz",
"integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==",
"dependencies": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.0",
"ws": "~8.2.3"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/engine.io-client": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
"dependencies": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.0",
"has-cors": "1.1.0",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0",
"yeast": "0.1.2"
}
},
"node_modules/engine.io-client/node_modules/debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/engine.io-client/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"dependencies": {
"@socket.io/base64-arraybuffer": "~1.0.2"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/engine.io/node_modules/debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/engine.io/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/engine.io/node_modules/ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/enhanced-resolve": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
@ -13521,6 +13748,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -20709,6 +20941,16 @@
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
"dev": true
},
"node_modules/parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"node_modules/parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@ -22835,6 +23077,131 @@
"tslib": "^2.0.3"
}
},
"node_modules/socket.io": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
"integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==",
"dependencies": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.2",
"engine.io": "~6.1.0",
"socket.io-adapter": "~2.3.3",
"socket.io-parser": "~4.0.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-adapter": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz",
"integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ=="
},
"node_modules/socket.io-client": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
"dependencies": {
"@socket.io/component-emitter": "~3.0.0",
"backo2": "~1.0.2",
"debug": "~4.3.2",
"engine.io-client": "~6.1.1",
"parseuri": "0.0.6",
"socket.io-parser": "~4.1.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-client/node_modules/debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socket.io-client/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/socket.io-client/node_modules/socket.io-parser": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.1.tgz",
"integrity": "sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==",
"dependencies": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
"integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
"dependencies": {
"@types/component-emitter": "^1.2.10",
"component-emitter": "~1.3.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser/node_modules/debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socket.io-parser/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/socket.io/node_modules/debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socket.io/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/socks": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
@ -25718,6 +26085,14 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/xss": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.10.tgz",
@ -25799,6 +26174,11 @@
"node": ">=10"
}
},
"node_modules/yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
@ -29435,6 +29815,25 @@
}
}
},
"@nestjs/jwt": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-8.0.0.tgz",
"integrity": "sha512-fz2LQgYY2zmuD8S+8UE215anwKyXlnB/1FwJQLVR47clNfMeFMK8WCxmn6xdPhF5JKuV1crO6FVabb1qWzDxqQ==",
"requires": {
"@types/jsonwebtoken": "8.5.4",
"jsonwebtoken": "8.5.1"
},
"dependencies": {
"@types/jsonwebtoken": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz",
"integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==",
"requires": {
"@types/node": "*"
}
}
}
},
"@nestjs/mapped-types": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.0.tgz",
@ -29459,6 +29858,15 @@
"tslib": "2.3.1"
}
},
"@nestjs/platform-socket.io": {
"version": "8.2.6",
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-8.2.6.tgz",
"integrity": "sha512-Gkay52E6PmhuL1e1EF1GnJuivt4NxXaqY2I3VV4LF4X2jNHmL09EAyJofs1G6ySF8QUtR9HqtW/+ohxr7RipsQ==",
"requires": {
"socket.io": "4.4.1",
"tslib": "2.3.1"
}
},
"@nestjs/schematics": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-8.0.5.tgz",
@ -29552,6 +29960,16 @@
"md5": "^2.2.1"
}
},
"@nestjs/websockets": {
"version": "8.2.6",
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-8.2.6.tgz",
"integrity": "sha512-GUdPd5X+ojNeaYE+/4c4105tb8skvQt3KyR7CCzrhMziHsRakDLQ/8LO932fh8ADrkRxr7jfAbt3UTq5FggQ2w==",
"requires": {
"iterare": "1.2.1",
"object-hash": "2.2.0",
"tslib": "2.3.1"
}
},
"@next/env": {
"version": "12.0.8",
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.0.8.tgz",
@ -29877,6 +30295,16 @@
"@sinonjs/commons": "^1.7.0"
}
},
"@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ=="
},
"@socket.io/component-emitter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
},
"@szmarczak/http-timer": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
@ -29990,6 +30418,11 @@
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w=="
},
"@types/component-emitter": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
"integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ=="
},
"@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@ -30001,8 +30434,7 @@
"@types/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
"dev": true
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"@types/cookiejar": {
"version": "2.1.2",
@ -30416,6 +30848,14 @@
"@types/node": "*"
}
},
"@types/socket.io": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.2.tgz",
"integrity": "sha512-pu0sN9m5VjCxBZVK8hW37ZcMe8rjn4HHggBN5CbaRTvFwv5jOmuIRZEuddsBPa9Th0ts0SIo3Niukq+95cMBbQ==",
"requires": {
"socket.io": "*"
}
},
"@types/stack-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
@ -32085,6 +32525,11 @@
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@ -32974,8 +33419,7 @@
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
"dev": true
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"concat-map": {
"version": "0.0.1",
@ -34132,6 +34576,89 @@
"once": "^1.4.0"
}
},
"engine.io": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz",
"integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==",
"requires": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.0",
"ws": "~8.2.3"
},
"dependencies": {
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"requires": {}
}
}
},
"engine.io-client": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
"requires": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.0",
"has-cors": "1.1.0",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0",
"yeast": "0.1.2"
},
"dependencies": {
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"requires": {}
}
}
},
"engine.io-parser": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"requires": {
"@socket.io/base64-arraybuffer": "~1.0.2"
}
},
"enhanced-resolve": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
@ -36225,6 +36752,11 @@
"integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
"dev": true
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -41719,6 +42251,16 @@
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
"dev": true
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@ -43376,6 +43918,101 @@
"tslib": "^2.0.3"
}
},
"socket.io": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
"integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==",
"requires": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.2",
"engine.io": "~6.1.0",
"socket.io-adapter": "~2.3.3",
"socket.io-parser": "~4.0.4"
},
"dependencies": {
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"socket.io-adapter": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz",
"integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ=="
},
"socket.io-client": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
"requires": {
"@socket.io/component-emitter": "~3.0.0",
"backo2": "~1.0.2",
"debug": "~4.3.2",
"engine.io-client": "~6.1.1",
"parseuri": "0.0.6",
"socket.io-parser": "~4.1.1"
},
"dependencies": {
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"socket.io-parser": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.1.tgz",
"integrity": "sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==",
"requires": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1"
}
}
}
},
"socket.io-parser": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
"integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
"requires": {
"@types/component-emitter": "^1.2.10",
"component-emitter": "~1.3.0",
"debug": "~4.3.1"
},
"dependencies": {
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"socks": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
@ -45524,6 +46161,11 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
"xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
},
"xss": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.10.tgz",
@ -45586,6 +46228,11 @@
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",

View file

@ -43,9 +43,13 @@
"@nestjs/config": "^1.1.6",
"@nestjs/core": "^8.2.6",
"@nestjs/graphql": "^9.1.2",
"@nestjs/jwt": "^8.0.0",
"@nestjs/passport": "^8.1.0",
"@nestjs/platform-express": "^8.2.6",
"@nestjs/platform-socket.io": "^8.2.6",
"@nestjs/throttler": "^2.0.0",
"@nestjs/websockets": "^8.2.6",
"@types/socket.io": "^3.0.2",
"@visx/axis": "^2.6.0",
"@visx/chord": "^2.1.2",
"@visx/event": "^2.6.0",
@ -102,6 +106,7 @@
"rimraf": "^3.0.2",
"rxjs": "^7.5.2",
"secp256k1": "^4.0.3",
"socket.io-client": "^4.4.1",
"socks-proxy-agent": "^6.1.1",
"styled-components": "^5.3.3",
"styled-react-modal": "^2.1.0",

View file

@ -1,8 +1,6 @@
import { useEffect } from 'react';
import styled, { ThemeProvider } from 'styled-components';
import { ModalProvider, BaseModalBackground } from 'styled-react-modal';
import { useRouter } from 'next/router';
import Head from 'next/head';
import { StyledToastContainer } from '../src/components/toastContainer/ToastContainer';
import { AppProps } from 'next/app';
import { ApolloProvider } from '@apollo/client';
import { useApollo } from '../config/client';
@ -13,13 +11,17 @@ import { GlobalStyles } from '../src/styles/GlobalStyle';
import { Header } from '../src/layouts/header/Header';
import { Footer } from '../src/layouts/footer/Footer';
import { PageWrapper, HeaderBodyWrapper } from '../src/layouts/Layout.styled';
import { ToastContainer } from 'react-toastify';
import { useListener } from '../src/hooks/UseListener';
import { SocketProvider } from '../src/context/SocketContext';
import { useRouter } from 'next/router';
import getConfig from 'next/config';
import Head from 'next/head';
import 'react-toastify/dist/ReactToastify.min.css';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import 'react-circular-progressbar/dist/styles.css';
import { useEffect } from 'react';
const { publicRuntimeConfig } = getConfig();
const { logoutUrl } = publicRuntimeConfig;
@ -57,6 +59,8 @@ const Wrapper: React.FC<{ authenticated: boolean }> = ({
const isRoot = pathname === '/login' || pathname === '/sso';
useListener(isRoot);
return (
<ThemeProvider theme={{ mode: isRoot ? 'light' : theme }}>
<ModalProvider backgroundComponent={BaseModalBackground}>
@ -67,6 +71,7 @@ const Wrapper: React.FC<{ authenticated: boolean }> = ({
{authenticated ? children : <NotAuthenticated />}
</HeaderBodyWrapper>
<Footer />
<ToastContainer theme={theme === 'light' ? 'light' : 'dark'} />
</PageWrapper>
</ModalProvider>
</ThemeProvider>
@ -91,14 +96,15 @@ export default function App({ Component, pageProps }: AppProps) {
</Head>
<ConfigProvider initialConfig={initialConfig}>
<BaseProvider initialHasToken={hasToken}>
<ContextProvider>
<Wrapper authenticated={authenticated}>
<Component {...pageProps} />
</Wrapper>
</ContextProvider>
<SocketProvider authToken={authToken}>
<ContextProvider>
<Wrapper authenticated={authenticated}>
<Component {...pageProps} />
</Wrapper>
</ContextProvider>
</SocketProvider>
</BaseProvider>
</ConfigProvider>
<StyledToastContainer />
</ApolloProvider>
);
}

View file

@ -208,7 +208,7 @@ export const getStatusDot = (status: boolean, type: string) => {
};
export const renderLine = (
title: string,
title: string | number,
content: number | string | JSX.Element | undefined | null,
key?: string | number,
deleteCallback?: () => void

View file

@ -5,7 +5,6 @@ import { BitcoinPrice } from '../../../src/components/bitcoinInfo/BitcoinPrice';
import { mediaWidths } from '../../styles/Themes';
import { Section } from '../section/Section';
import { Navigation } from '../../layouts/navigation/Navigation';
import { StatusCheck } from '../statusCheck/StatusCheck';
type GridProps = {
noNavigation?: boolean;
@ -41,7 +40,6 @@ export const GridWrapper: React.FC<GridProps & { centerContent?: boolean }> = ({
<Container noNavigation={noNavigation}>
<BitcoinPrice />
<BitcoinFees />
<StatusCheck />
{!noNavigation && <Navigation />}
<ContentStyle>
{centerContent ? (
@ -58,7 +56,6 @@ export const SimpleWrapper: React.FC<GridProps> = ({ children }) => (
<Section padding={'16px'}>
<BitcoinPrice />
<BitcoinFees />
<StatusCheck />
{children}
</Section>
);

View file

@ -1,24 +0,0 @@
import { useEffect } from 'react';
import { toast } from 'react-toastify';
import getConfig from 'next/config';
import { useGetNodeInfoQuery } from '../../../src/graphql/queries/__generated__/getNodeInfo.generated';
const { publicRuntimeConfig } = getConfig();
const { logoutUrl, basePath } = publicRuntimeConfig;
export const StatusCheck: React.FC = () => {
const { error, stopPolling } = useGetNodeInfoQuery({
fetchPolicy: 'network-only',
pollInterval: 10000,
});
useEffect(() => {
if (error) {
toast.error(`Unable to connect to node`);
stopPolling();
window.location.href = logoutUrl || `${basePath}/login`;
}
}, [error, stopPolling]);
return null;
};

View file

@ -1,17 +0,0 @@
import styled from 'styled-components';
import { ToastContainer } from 'react-toastify';
export const StyledToastContainer = styled(ToastContainer)`
.Toastify__toast {
border-radius: 4px;
}
.Toastify__toast--error {
border-radius: 4px;
}
.Toastify__toast--warning {
border-radius: 4px;
}
.Toastify__toast--success {
border-radius: 4px;
}
`;

View file

@ -0,0 +1,115 @@
import React, { FC, useCallback, useRef, useState } from 'react';
import io from 'socket.io-client';
import { Socket } from 'socket.io-client';
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
const { basePath } = publicRuntimeConfig;
type Connection = {
socket: Socket | undefined;
cleanup: () => void;
};
type CreateConnection = () => Connection;
type Status = 'connecting' | 'connected' | 'disconnected';
type Context = {
createConnection: CreateConnection;
getConnection: () => Socket | undefined;
getLastMessage: (forEvent: string) => any;
setLastMessage: (forEvent: string, message: any) => void;
registerSharedListener: (forEvent: string) => void;
getError: () => any;
setError: (error: any) => void;
getStatus: () => Status;
};
const SocketContext = React.createContext<Context | undefined>(undefined);
const SocketProvider: FC<{ authToken?: string }> = ({
children,
authToken,
}) => {
const sockets = useRef<Socket | undefined>(undefined);
const [status, setStatus] = useState<Status>('disconnected');
const [error, setError] = useState<any>(undefined);
const [lastMessages, setLastMessages] = useState<Record<string, any>>({});
const createConnection = useCallback(() => {
const cleanup = () => {
sockets.current?.disconnect();
};
// Early return if the user has no authToken cookie
if (!authToken) {
return { socket: undefined, cleanup };
}
if (sockets.current) {
sockets.current.connect();
return { socket: sockets.current, cleanup };
}
const handleConnect = () => setStatus('connected');
const handleDisconnect = () => setStatus('disconnected');
const socket = io({
...(basePath ? { path: `${basePath}/socket.io` } : {}),
reconnectionAttempts: 5,
});
sockets.current = socket;
socket.on('error', (error: any) => setError(error));
socket.on('connect', handleConnect);
socket.on('disconnect', handleDisconnect);
return { socket, cleanup };
}, []);
const getLastMessage = (forEvent = '') => lastMessages[forEvent];
const setLastMessage = (forEvent: string, message: any) =>
setLastMessages(state => ({
...state,
[forEvent]: message,
}));
const getConnection = () => sockets.current;
const getStatus = () => status;
const getError = () => error;
const registerSharedListener = (forEvent = '') => {
if (!sockets.current) return;
if (sockets.current.hasListeners(forEvent)) return;
sockets.current.on(forEvent, (message: any) =>
setLastMessages(state => ({
...state,
[forEvent]: message,
}))
);
};
return (
<SocketContext.Provider
value={{
createConnection,
getConnection,
getLastMessage,
setLastMessage,
getError,
setError,
getStatus,
registerSharedListener,
}}
>
{children}
</SocketContext.Provider>
);
};
export { SocketProvider, SocketContext };

View file

@ -0,0 +1,321 @@
import { useApolloClient } from '@apollo/client';
import { useCallback, useEffect, useRef } from 'react';
import { toast } from 'react-toastify';
import {
getNodeLink,
getTransactionLink,
renderLine,
} from '../components/generic/helpers';
import { Separation } from '../components/generic/Styled';
import { formatSats } from '../utils/helpers';
import { useSocket, useSocketEvent } from './UseSocket';
const refetchTimeMs = 1000 * 1;
const options: { autoClose: false } = {
autoClose: false,
};
const renderToast = (
title: string,
content: JSX.Element | null | string | number
) => {
return (
<>
{title}
<Separation lineColor={'transparent'} withMargin="4px 0" />
{content}
</>
);
};
export const useListener = (disabled?: boolean) => {
const refetchQueryTimeout: { current: ReturnType<typeof setTimeout> | null } =
useRef(null);
const client = useApolloClient();
const { socket } = useSocket(disabled);
const invoice = useSocketEvent(socket, 'invoice_updated');
const payment = useSocketEvent(socket, 'payment');
const forward = useSocketEvent(socket, 'forward');
// const change = useSocketEvent(socket, 'channel_active_changed');
const closed = useSocketEvent(socket, 'channel_closed');
const opened = useSocketEvent(socket, 'channel_opened');
const opening = useSocketEvent(socket, 'channel_opening');
const handleRefetchQueries = useCallback(
(extra: string[] = []) => {
if (!!refetchQueryTimeout.current) {
clearTimeout(refetchQueryTimeout.current);
}
refetchQueryTimeout.current = setTimeout(async () => {
client.refetchQueries({
include: ['GetNodeBalances', 'GetNodeInfo', ...extra],
});
}, refetchTimeMs);
},
[client]
);
useEffect(() => {
return () => {
if (!!refetchQueryTimeout.current) {
clearTimeout(refetchQueryTimeout.current);
}
};
}, []);
useEffect(() => {
if (!invoice.lastMessage) return;
const { tokens, is_confirmed, description, description_hash, received } =
invoice.lastMessage;
if (is_confirmed) {
toast.success(
renderToast(
'Invoice Paid',
<>
{renderLine('Description', description)}
{renderLine('Description Hash', description_hash)}
{renderLine('Amount', formatSats(received))}
</>
),
options
);
} else {
toast.info(
renderToast(
'New Invoice Created',
<>
{renderLine('Description', description)}
{renderLine('Description Hash', description_hash)}
{renderLine('Amount', formatSats(tokens))}
</>
),
options
);
}
handleRefetchQueries(['GetInvoices']);
}, [invoice.lastMessage, handleRefetchQueries]);
useEffect(() => {
if (!payment.lastMessage) return;
const { hops, fee, destination, tokens } = payment.lastMessage;
const hopLines = hops.map((h: any, index: number) =>
renderLine(`Hop ${index + 1}`, h.channel)
);
toast.success(
renderToast(
'New Payment',
<>
{renderLine('Destination', getNodeLink(destination))}
{renderLine('Amount', formatSats(tokens))}
{renderLine('Fee', fee ? formatSats(fee) : null)}
{hopLines}
</>
),
options
);
handleRefetchQueries(['GetPayments']);
}, [payment.lastMessage, handleRefetchQueries]);
useEffect(() => {
if (!forward.lastMessage) return;
const {
is_confirmed,
// is_failed,
is_receive,
is_send,
out_channel,
fee,
in_channel,
tokens,
} = forward.lastMessage;
// if (is_send && is_confirmed) {
// toast.success(
// renderToast(
// 'New Payment',
// <>
// {renderLine('Out Channel', out_channel)}
// {renderLine('Fee', fee ? formatSats(fee) : null)}
// </>
// )
// );
// }
if (is_send || is_receive) return;
if (!is_confirmed) {
toast.warn(
renderToast(
'Forward Attempt',
<>
{renderLine('In Channel', in_channel)}
{renderLine('Out Channel', out_channel)}
{renderLine('Tokens', formatSats(tokens))}
{renderLine('Fee', fee ? formatSats(fee) : null)}
</>
),
options
);
}
// if (is_failed) {
// toast.error(
// renderToast(
// 'Forward Failed',
// <>
// {renderLine('In Channel', in_channel)}
// {renderLine('Out Channel', out_channel)}
// {renderLine('Tokens', formatSats(tokens))}
// {renderLine('Fee', fee ? formatSats(fee) : null)}
// </>
// )
// );
// }
if (is_confirmed) {
toast.success(
renderToast(
'Successful Forward',
<>
{renderLine('In Channel', in_channel)}
{renderLine('Out Channel', out_channel)}
{renderLine('Fee', fee ? formatSats(fee) : null)}
</>
),
options
);
handleRefetchQueries(['GetForwards']);
}
}, [forward.lastMessage, handleRefetchQueries]);
// useEffect(() => {
// if (!change.lastMessage) return;
// toast.info('Channel Active Change');
// handleRefetchQueries();
// }, [change.lastMessage, handleRefetchQueries]);
useEffect(() => {
if (!closed.lastMessage) return;
const {
capacity,
close_transaction_id,
id,
is_breach_close,
is_cooperative_close,
is_funding_cancel,
is_local_force_close,
is_remote_force_close,
partner_public_key,
transaction_id,
} = closed.lastMessage;
const getCloseType = (): string => {
const types: string[] = [];
if (is_breach_close) {
types.push('Breach');
}
if (is_cooperative_close) {
types.push('Cooperative');
}
if (is_funding_cancel) {
types.push('Funding Cancel');
}
if (is_local_force_close) {
types.push('Local Force Close');
}
if (is_remote_force_close) {
types.push('Remote Force Close');
}
return types.join(', ');
};
toast.info(
renderToast(
'Channel Closed',
<>
{renderLine('Reason', getCloseType())}
{renderLine('Capacity', formatSats(capacity))}
{renderLine('Id', id)}
{renderLine('Peer', getNodeLink(partner_public_key))}
{renderLine(
'Tx',
transaction_id ? getTransactionLink(transaction_id) : null
)}
{renderLine(
'Closing Tx',
close_transaction_id
? getTransactionLink(close_transaction_id)
: null
)}
</>
),
options
);
handleRefetchQueries([
'GetChannels',
'GetPendingChannels',
'GetClosedChannels',
]);
}, [closed.lastMessage, handleRefetchQueries]);
useEffect(() => {
if (!opened.lastMessage) return;
const {
id,
partner_public_key,
remote_balance,
local_balance,
capacity,
is_partner_initiated,
is_private,
} = opened.lastMessage;
toast.info(
renderToast(
'Channel Opened',
<>
{renderLine(
'Initiated By',
is_partner_initiated ? 'Your Peer' : 'You'
)}
{renderLine('Id', id)}
{renderLine('Peer', getNodeLink(partner_public_key))}
{renderLine('Private', is_private ? 'Yes' : 'No')}
{renderLine('Capacity', formatSats(capacity))}
{renderLine('Local', formatSats(local_balance))}
{renderLine('Remote', formatSats(remote_balance))}
</>
),
options
);
handleRefetchQueries(['GetChannels', 'GetPendingChannels']);
}, [opened.lastMessage, handleRefetchQueries]);
useEffect(() => {
if (!opening.lastMessage) return;
toast.info(
renderToast(
'Channel Opening',
renderLine(
'Transaction',
getTransactionLink(opening.lastMessage.transaction_id)
)
),
options
);
handleRefetchQueries(['GetChannels', 'GetPendingChannels']);
}, [opening.lastMessage, handleRefetchQueries]);
};

View file

@ -11,9 +11,7 @@ const initialState = {
};
export const useNodeBalances = () => {
const { data, loading, error } = useGetNodeBalancesQuery({
pollInterval: 10000,
});
const { data, loading, error } = useGetNodeBalancesQuery();
if (!data?.getNodeBalances || loading || error) {
return initialState;

View file

@ -0,0 +1,51 @@
import { useContext, useEffect, useRef } from 'react';
import { Socket } from 'socket.io-client';
import { SocketContext } from '../context/SocketContext';
export const useSocket = (disabled?: boolean) => {
const socket = useRef<Socket | undefined>(undefined);
const context = useContext(SocketContext);
if (context === undefined) {
throw new Error('useSocket must be used within a SocketProvider');
}
const { getStatus, createConnection, getError } = context;
const status = getStatus();
const error = getError();
useEffect(() => {
if (disabled) return;
const { socket: _socket, cleanup } = createConnection();
socket.current = _socket;
return () => {
cleanup();
};
}, [createConnection, disabled]);
return {
socket: socket.current,
status,
error,
};
};
export const useSocketEvent = (socket: Socket | undefined, event: string) => {
const context = useContext(SocketContext);
if (context === undefined) {
throw new Error('useSocketEvent must be used within a SocketProvider');
}
const { registerSharedListener, getLastMessage } = context;
const lastMessage = getLastMessage(event);
const sendMessage = (message: any) => socket?.emit(event, message);
useEffect(() => {
registerSharedListener(event);
}, [event, registerSharedListener]);
return { lastMessage, sendMessage };
};

View file

@ -28,6 +28,8 @@ import {
RebalanceSubTitle,
} from './Balance.styled';
import { PeerSelection } from './PeerSelection';
import { LoadingCard } from '../../components/loading/LoadingCard';
import { RebalanceLogs } from './Logs';
export type RebalanceIdType = {
alias: string;
@ -145,6 +147,7 @@ const SettingLine: React.FC<{ title: string }> = ({ children, title }) => (
);
export const AdvancedBalance = () => {
const [messages, setMessages] = useState<any[]>([]);
const [openType, openTypeSet] = useState<string>('none');
const [isTarget, setIsTarget] = useState<boolean>(false);
@ -174,7 +177,7 @@ export const AdvancedBalance = () => {
}
);
const [rebalance, { data: _data, loading }] = useBosRebalanceMutation({
const [rebalance, { data: _data, loading, error }] = useBosRebalanceMutation({
onError: error => toast.error(getErrorContent(error)),
onCompleted: () => {
dispatch({ type: 'clearFilters' });
@ -311,13 +314,15 @@ export const AdvancedBalance = () => {
</ColorButton>
</SettingLine>
<Separation />
<Text>{'5. Set a timeout for the rebalance attempt. (Optional)'}</Text>
<Text>
{'5. Set a timeout for the rebalance attempt. (Default: 5 minutes)'}
</Text>
<InputWithDeco
inputType={'number'}
title={'Max time'}
title={'Max minutes'}
value={state.timeout_minutes || ''}
override={'m'}
placeholder={'timeout'}
placeholder={'timeout minutes'}
customAmount={
state.timeout_minutes ? <span>{state.timeout_minutes}m</span> : ''
}
@ -329,9 +334,16 @@ export const AdvancedBalance = () => {
</>
);
return (
<>
{data && data.bosRebalance ? (
const renderRebalance = () => {
if (loading) {
return (
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
<LoadingCard noCard={true} />
</Card>
);
}
if (data?.bosRebalance) {
return (
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
<AdvancedResult rebalanceResult={data.bosRebalance} />
<ColorButton
@ -343,46 +355,61 @@ export const AdvancedBalance = () => {
Balance Again
</ColorButton>
</Card>
) : (
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
{renderDetails()}
<SingleLine>
<ColorButton
color={chartColors.orange2}
withMargin={'16px 8px 0 0'}
fullWidth={true}
onClick={() => {
dispatch({ type: 'clearFilters' });
}}
>
Reset
</ColorButton>
<ColorButton
withMargin={'16px 0 0'}
loading={loading}
disabled={isDisabled}
fullWidth={true}
onClick={() => {
rebalance({
variables: {
...(isTarget
? { out_inbound: state.out_inbound }
: { max_rebalance: state.max_rebalance }),
max_fee_rate: state.max_fee_rate,
max_fee: state.max_fee,
timeout_minutes: state.timeout_minutes,
avoid: state.avoid.map(a => a.id),
in_through: state.in_through.id,
out_through: state.out_through.id,
},
});
}}
>
Rebalance
</ColorButton>
</SingleLine>
</Card>
)}
);
}
};
const renderContent = () => {
if ((loading || data?.bosRebalance) && !error) {
return <>{renderRebalance()}</>;
}
return (
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
{renderDetails()}
<SingleLine>
<ColorButton
color={chartColors.orange2}
withMargin={'16px 8px 0 0'}
fullWidth={true}
onClick={() => {
dispatch({ type: 'clearFilters' });
}}
>
Reset
</ColorButton>
<ColorButton
withMargin={'16px 0 0'}
loading={loading}
disabled={isDisabled}
fullWidth={true}
onClick={() => {
setMessages([]);
rebalance({
variables: {
...(isTarget
? { out_inbound: state.out_inbound }
: { max_rebalance: state.max_rebalance }),
max_fee_rate: state.max_fee_rate,
max_fee: state.max_fee,
timeout_minutes: state.timeout_minutes,
avoid: state.avoid.map(a => a.id),
in_through: state.in_through.id,
out_through: state.out_through.id,
},
});
}}
>
Rebalance
</ColorButton>
</SingleLine>
</Card>
);
};
return (
<>
{renderContent()}
<RebalanceLogs messages={messages} setMessages={setMessages} />
<Modal
isOpen={openType !== 'none'}
closeCallback={() => {

View file

@ -0,0 +1,83 @@
import { FC, useEffect } from 'react';
import { Card } from '../../components/generic/CardGeneric';
import { renderLine } from '../../components/generic/helpers';
import {
CardWithTitle,
SubCard,
SubTitle,
} from '../../components/generic/Styled';
import { useSocket, useSocketEvent } from '../../hooks/UseSocket';
import { btcToSat, formatSats } from '../../utils/helpers';
type RebalanceProps = {
messages: any[];
setMessages: (state: (state: any[]) => any[]) => void;
};
export const RebalanceLogs: FC<RebalanceProps> = ({
messages,
setMessages,
}) => {
const { socket } = useSocket();
const { lastMessage } = useSocketEvent(socket, 'rebalance');
useEffect(() => {
if (!lastMessage) return;
setMessages(state => [lastMessage, ...state]);
}, [lastMessage, setMessages]);
const renderContent = () => {
return messages.map(m => {
if ('rebalance_target_amount' in m) {
return (
<SubCard>
{renderLine(
'Amount',
formatSats(btcToSat(m.rebalance_target_amount))
)}
{renderLine('Incoming Peer', m.incoming_peer_to_decrease_inbound)}
{renderLine('Outgoing Peer', m.outgoing_peer_to_increase_inbound)}
</SubCard>
);
}
if ('circular_rebalance_for' in m) {
return (
<SubCard>
{renderLine('Rebalancing peer', m.circular_rebalance_for)}
</SubCard>
);
}
if ('evaluating' in m) {
return (
<SubCard>
{m.evaluating.map((s: string, index: number) =>
renderLine(index + 1, s)
)}
</SubCard>
);
}
if ('evaluating_amount' in m) {
return (
<SubCard>
{renderLine('Evaluating amount', formatSats(m.evaluating_amount))}
</SubCard>
);
}
if ('failure' in m) {
return <SubCard>{renderLine('Failure', m.failure)}</SubCard>;
}
return null;
});
};
if (!messages.length) {
return null;
}
return (
<CardWithTitle>
<SubTitle>Logs</SubTitle>
<Card>{renderContent()}</Card>
</CardWithTitle>
);
};

View file

@ -15,6 +15,8 @@ import { transports, format } from 'winston';
import configuration from './config/configuration';
import jwt from 'jsonwebtoken';
import cookie from 'cookie';
import { WsModule } from './modules/ws/ws.module';
import { SubModule } from './modules/sub/sub.module';
const { combine, timestamp, prettyPrint, json } = format;
@ -36,6 +38,9 @@ export type JwtObjectType = {
@Module({
imports: [
AuthenticationModule,
SubModule,
WsModule,
ApiModule,
ViewModule,
NodeModule,

View file

@ -36,6 +36,15 @@ export type YamlEnvs = {
YML_ENV_4: string;
};
type SubscriptionsConfig = {
disableAll: boolean;
disableInvoices: boolean;
disablePayments: boolean;
disableForwards: boolean;
disableChannels: boolean;
disableBackups: boolean;
};
type ConfigType = {
basePath: string;
isProduction: boolean;
@ -53,6 +62,7 @@ type ConfigType = {
masterPasswordOverride: string;
disable2FA: boolean;
headers: Headers;
subscriptions: SubscriptionsConfig;
};
export default (): ConfigType => {
@ -106,6 +116,15 @@ export default (): ConfigType => {
YML_ENV_4: process.env.YML_ENV_4 || '',
};
const subscriptions = {
disableAll: process.env.DISABLE_ALL_SUBS === 'true',
disableInvoices: process.env.DISABLE_INVOICE_SUB === 'true',
disablePayments: process.env.DISABLE_PAYMENT_SUB === 'true',
disableForwards: process.env.DISABLE_FORWARD_SUB === 'true',
disableChannels: process.env.DISABLE_CHANNEL_SUB === 'true',
disableBackups: process.env.DISABLE_BACKUP_SUB === 'true',
};
const config: ConfigType = {
logJson: process.env.LOG_JSON === 'true',
masterPasswordOverride: process.env.MASTER_PASSWORD_OVERRIDE || '',
@ -123,6 +142,7 @@ export default (): ConfigType => {
urls,
jwtSecret,
yamlEnvs,
subscriptions,
};
if (!isProduction) {

View file

@ -1,9 +1,10 @@
import { Module } from '@nestjs/common';
import { AccountsModule } from '../../accounts/accounts.module';
import { WsModule } from '../../ws/ws.module';
import { BosResolver } from './bos.resolver';
@Module({
imports: [AccountsModule],
imports: [WsModule, AccountsModule],
providers: [BosResolver],
})
export class BosModule {}

View file

@ -11,10 +11,13 @@ import { Inject } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { to } from 'src/server/utils/async';
import { BosRebalanceResult, RebalanceResponseType } from './bos.types';
import { WsService } from '../../ws/ws.service';
import { stripAnsi } from 'src/server/utils/string';
@Resolver()
export class BosResolver {
constructor(
private wsService: WsService,
private accountsService: AccountsService,
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
) {}
@ -79,7 +82,7 @@ export class BosResolver {
...(in_through && { in_through }),
...(max_fee && max_fee > 0 && { max_fee }),
...(max_fee_rate && max_fee_rate > 0 && { max_fee_rate }),
...(timeout_minutes && timeout_minutes > 0 && { timeout_minutes }),
...(timeout_minutes ? { timeout_minutes } : { timeout_minutes: 5 }),
...(max_rebalance && max_rebalance > 0
? { max_rebalance: `${max_rebalance}` }
: {}),
@ -92,10 +95,33 @@ export class BosResolver {
this.logger.info('Rebalance Params', { filteredParams });
const logger = {
info: (message, ...args) => {
let payload = message;
if (payload?.evaluating?.length) {
payload = {
evaluating: payload.evaluating.map((m: string) => stripAnsi(m)),
};
}
this.wsService.emit(user.id, 'rebalance', payload);
this.logger.info(message, args);
},
warn: (message, ...args) => {
this.wsService.emit(user.id, 'rebalance', message);
this.logger.warn(message, args);
},
error: (message, ...args) => {
this.wsService.emit(user.id, 'rebalance', message);
this.logger.error(message, args);
},
};
const response = await to<RebalanceResponseType>(
rebalance({
lnd: account.lnd,
logger: this.logger,
logger,
fs: { getFile: fs.readFile },
...filteredParams,
})

View file

@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { AccountsModule } from '../accounts/accounts.module';
import { AuthenticationService } from './auth.service';
@Module({
imports: [
AccountsModule,
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
secret: config.get('jwtSecret'),
}),
}),
],
providers: [AuthenticationService],
exports: [AuthenticationService],
})
export class AuthenticationModule {}

View file

@ -0,0 +1,23 @@
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { AccountsService } from '../accounts/accounts.service';
@Injectable()
export class AuthenticationService {
constructor(
private readonly jwtService: JwtService,
private accountsService: AccountsService
) {}
public async getUserFromAuthToken(token: string) {
const payload = this.jwtService.verify(token);
if (payload.sub) {
const account = this.accountsService.getAccount(payload.sub);
if (account) {
return payload.sub;
}
}
}
}

View file

@ -24,6 +24,10 @@ export const getErrorMsg = (error: any[] | string): string => {
return errorString;
}
if (error[1] && typeof error[1] === 'string') {
return error[1];
}
console.log('Unknown Error:', error);
return 'Unknown Error';
};

View file

@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { AccountsModule } from '../accounts/accounts.module';
import { WsModule } from '../ws/ws.module';
import { SubService } from './sub.service';
@Module({
imports: [AccountsModule, WsModule],
providers: [SubService],
})
export class SubModule {}

View file

@ -0,0 +1,433 @@
import { Inject, Injectable, OnApplicationBootstrap } from '@nestjs/common';
import {
subscribeToForwards,
getWalletInfo,
subscribeToChannels,
subscribeToInvoices,
subscribeToBackups,
subscribeToPastPayments,
} from 'ln-service';
import asyncAuto from 'async/auto';
import asyncEach from 'async/each';
import asyncMap from 'async/map';
import asyncForever from 'async/forever';
import { Logger } from 'winston';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { AccountsService } from '../accounts/accounts.service';
import { WsService } from '../ws/ws.service';
import { ConfigService } from '@nestjs/config';
const restartSubscriptionTimeMs = 1000 * 30;
type NodeType = {
id: string;
name: string;
pubkey: string;
lnd: any;
};
@Injectable()
export class SubService implements OnApplicationBootstrap {
subscriptions = [];
constructor(
private accountsService: AccountsService,
private wsService: WsService,
private configService: ConfigService,
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
) {}
async onApplicationBootstrap(): Promise<void> {
const disabled = this.configService.get('subscriptions.disableAll');
if (disabled) {
this.logger.info('All subscriptions are disabled');
return;
}
this.startSubscription();
}
startSubscription() {
return asyncForever(
next => {
return asyncAuto(
{
// Get Authenticated LND objects for each node
getNodes: callback => {
const accounts = this.accountsService.getAllAccounts();
const validAccounts = [];
for (const key in accounts) {
if (accounts.hasOwnProperty(key)) {
const account = accounts[key];
if (!account.encrypted) {
validAccounts.push({ id: account.hash, lnd: account.lnd });
}
}
}
callback(null, validAccounts);
},
// Try to connect to nodes
checkNodes: [
'getNodes',
async ({ getNodes }) => {
return asyncMap(getNodes, async ({ lnd, id }) => {
try {
const info = await getWalletInfo({ lnd });
const sliced = info.public_key.slice(0, 10);
const name = `${info.alias}(${sliced})`;
return {
id,
name,
pubkey: info.public_key,
lnd,
};
} catch (err) {
this.logger.error('Error connecting to node', {
id,
err,
});
}
});
},
],
// Check which nodes are available and remove duplicates
checkAvailable: [
'checkNodes',
async ({ checkNodes }: { checkNodes: NodeType[] }) => {
const unique = checkNodes.filter(Boolean);
if (!unique.length) {
this.logger.error(`Unable to connect to any node.`);
throw new Error('UnableToConnectToAnyNode');
}
const names = unique.map(a => a.name);
this.logger.info(`Connected to ${names.join(', ')}`);
return unique;
},
],
// Subscribe to node invoices
invoices: [
'checkAvailable',
async ({ checkAvailable }, callback) => {
const disabled = this.configService.get(
'subscriptions.disableInvoices'
);
if (disabled) {
this.logger.info('Invoice subscriptions are disabled');
return;
}
const names = checkAvailable.map(a => a.name);
this.logger.info('Invoice subscription', {
connections: names.join(', '),
});
return asyncEach(
checkAvailable,
(node, cbk) => {
const sub = subscribeToInvoices({ lnd: node.lnd });
this.subscriptions.push(sub);
sub.on('invoice_updated', data => {
this.logger.info('invoice_updated', { node: node.name });
this.wsService.emit(node.id, 'invoice_updated', data);
return;
});
sub.on('error', async err => {
sub.removeAllListeners();
this.logger.error(
`ErrorInInvoiceSubscribe: ${node.name}`,
{ err }
);
cbk([
'ErrorInInvoiceSubscribe',
{ node: node.name, err },
]);
});
},
callback
);
},
],
// Subscribe to node payments
payments: [
'checkAvailable',
async ({ checkAvailable }, callback) => {
const disabled = this.configService.get(
'subscriptions.disablePayments'
);
if (disabled) {
this.logger.info('Payment subscriptions are disabled');
return;
}
const names = checkAvailable.map(a => a.name);
this.logger.info('Payment subscription', {
connections: names.join(', '),
});
return asyncEach(
checkAvailable,
(node, cbk) => {
const sub = subscribeToPastPayments({ lnd: node.lnd });
this.subscriptions.push(sub);
sub.on('payment', data => {
this.logger.info('payment', { node: node.name });
this.wsService.emit(node.id, 'payment', data);
return;
});
sub.on('error', async err => {
sub.removeAllListeners();
this.logger.error(
`ErrorInPaymentSubscribe: ${node.name}`,
{ err }
);
cbk([
'ErrorInPaymentSubscribe',
{ node: node.name, err },
]);
});
},
callback
);
},
],
// Subscribe to node forwards
forwards: [
'checkAvailable',
async ({ checkAvailable }, callback) => {
const disabled = this.configService.get(
'subscriptions.disableForwards'
);
if (disabled) {
this.logger.info('Forward subscriptions are disabled');
return;
}
const names = checkAvailable.map(a => a.name);
this.logger.info('Forward subscription', {
connections: names.join(', '),
});
return asyncEach(
checkAvailable,
(node, cbk) => {
const sub = subscribeToForwards({ lnd: node.lnd });
this.subscriptions.push(sub);
sub.on('forward', data => {
this.logger.info('forward', { node: node.name });
this.wsService.emit(node.id, 'forward', data);
return;
});
sub.on('error', async err => {
sub.removeAllListeners();
this.logger.error(
`ErrorInForwardSubscribe: ${node.name}`,
{ err }
);
cbk([
'ErrorInForwardSubscribe',
{ node: node.name, err },
]);
});
},
callback
);
},
],
// Subscribe to node channels
channels: [
'checkAvailable',
async ({ checkAvailable }, callback) => {
const disabled = this.configService.get(
'subscriptions.disableChannels'
);
if (disabled) {
this.logger.info('Channel subscriptions are disabled');
return;
}
const names = checkAvailable.map(a => a.name);
this.logger.info('Channels subscription', {
connections: names.join(', '),
});
return asyncEach(
checkAvailable,
(node, cbk) => {
const sub = subscribeToChannels({ lnd: node.lnd });
this.subscriptions.push(sub);
sub.on('channel_active_changed', data => {
this.logger.info('channel_active_changed', {
node: node.name,
});
this.wsService.emit(
node.id,
'channel_active_changed',
data
);
return;
});
sub.on('channel_closed', data => {
this.logger.info('channel_closed', { node: node.name });
this.wsService.emit(node.id, 'channel_closed', data);
return;
});
sub.on('channel_opened', data => {
this.logger.info('channel_opened', { node: node.name });
this.wsService.emit(node.id, 'channel_opened', data);
return;
});
sub.on('channel_opening', data => {
this.logger.info('channel_opening', { node: node.name });
this.wsService.emit(node.id, 'channel_opening', data);
return;
});
sub.on('error', async err => {
sub.removeAllListeners();
this.logger.error(
`ErrorInChannelSubscribe: ${node.name}`,
{ err }
);
cbk([
'ErrorInChannelSubscribe',
{ node: node.name, err },
]);
});
},
callback
);
},
],
// // Subscribe to node backups
backups: [
'checkAvailable',
({ checkAvailable }, callback) => {
const disabled = this.configService.get(
'subscriptions.disableBackups'
);
if (disabled) {
this.logger.info('Backup subscriptions are disabled');
return;
}
const names = checkAvailable.map(a => a.name);
this.logger.info('Backup subscription', {
connections: names.join(', '),
});
return asyncEach(
checkAvailable,
(node, cbk) => {
let postBackupTimeoutHandle;
const sub = subscribeToBackups({ lnd: node.lnd });
this.subscriptions.push(sub);
sub.on('backup', ({ backup }) => {
if (!!postBackupTimeoutHandle) {
clearTimeout(postBackupTimeoutHandle);
}
postBackupTimeoutHandle = setTimeout(async () => {
const time = Math.round(new Date().getTime() / 1000);
this.logger.info('channel_backup', { node: node.name });
this.wsService.emit(node.id, 'channel_backup', {
file: `${time}-${node.name}-backup.txt`,
backup,
});
}, restartSubscriptionTimeMs);
return;
});
sub.on('error', async err => {
sub.removeAllListeners();
this.logger.error(`ErrorInBackupSubscribe: ${node.name}`);
cbk(['ErrorInBackupSubscribe', { node: node.name, err }]);
});
},
callback
);
},
],
},
async (err, results) => {
this.subscriptions.forEach(sub => sub.removeAllListeners());
this.subscriptions = [];
this.logger.error(err?.message || '.....');
if (err?.message === 'UnableToConnectToAnyNode') {
next('UnableToConnectToAnyNode');
return;
}
if (err) {
this.logger.error('AsyncAuto error:', err);
} else {
this.logger.error('AsyncAuto results:', results);
}
const message = `Restarting subscription after ${restartSubscriptionTimeMs} ms`;
this.logger.warn(message);
setTimeout(async () => {
this.logger.warn('Restarting...');
next(null, 'retry');
}, restartSubscriptionTimeMs);
}
);
},
async err => {
this.logger.error('Initiating subscriptions failed: ', err);
}
);
}
}

View file

@ -0,0 +1,68 @@
import {
WebSocketGateway,
OnGatewayInit,
OnGatewayConnection,
OnGatewayDisconnect,
WebSocketServer,
} from '@nestjs/websockets';
import { Inject } from '@nestjs/common';
import { Server, Socket } from 'socket.io';
import { WsService } from './ws.service';
import { parse } from 'cookie';
import { appConstants } from 'src/server/utils/appConstants';
import { AuthenticationService } from '../auth/auth.service';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
import { ConfigModule } from '@nestjs/config';
ConfigModule.forRoot({
envFilePath: ['.env.local', '.env'],
});
@WebSocketGateway({ path: `${process.env.BASE_PATH || ''}/socket.io` })
export class WsGateway
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
constructor(
private socketService: WsService,
private authService: AuthenticationService,
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
) {}
@WebSocketServer()
public server: Server;
async getUserFromSocket(socket: Socket) {
const cookie = parse(socket.handshake.headers.cookie);
const authToken = cookie[appConstants.cookieName] || '';
if (!authToken) return null;
const user = await this.authService.getUserFromAuthToken(authToken);
if (!user) return null;
return user;
}
afterInit(server: Server) {
this.logger.info('WS server created');
this.socketService.init(server);
}
async handleDisconnect(client: Socket) {
const user = await this.getUserFromSocket(client);
client.leave(user);
this.logger.info(`Client disconnected: ${client.id}`);
}
async handleConnection(client: Socket) {
const user = await this.getUserFromSocket(client);
if (!user) {
client.disconnect();
} else {
client.join(user);
this.logger.info(`Client connected: ${client.id}`);
}
}
}

View file

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { AuthenticationModule } from '../auth/auth.module';
import { WsGateway } from './ws.gateway';
import { WsService } from './ws.service';
@Module({
imports: [AuthenticationModule],
providers: [WsGateway, WsService],
exports: [WsService],
})
export class WsModule {}

View file

@ -0,0 +1,16 @@
import { Injectable } from '@nestjs/common';
import { Server } from 'socket.io';
@Injectable()
export class WsService {
private socket: Server = null;
init(socket: Server) {
this.socket = socket;
}
emit(account: string, event: string, payload: any) {
if (!this.socket || !account || !event || !payload) return;
this.socket.in(account).emit(event, payload);
}
}

View file

@ -9,3 +9,20 @@ export const shorten = (text: string): string => {
export const reversedBytes = hex =>
Buffer.from(hex, 'hex').reverse().toString('hex');
const ansiRegex = ({ onlyFirst = false } = {}) => {
const pattern = [
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))',
].join('|');
return new RegExp(pattern, onlyFirst ? undefined : 'g');
};
export const stripAnsi = string => {
if (typeof string !== 'string') {
throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
}
return string.replace(ansiRegex(), '');
};