mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-21 22:11:37 +01:00
chore: ws (#394)
This commit is contained in:
parent
71b04da7b2
commit
5f0c140046
27 changed files with 1989 additions and 116 deletions
10
.env
10
.env
|
@ -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
663
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
`;
|
115
src/client/src/context/SocketContext.tsx
Normal file
115
src/client/src/context/SocketContext.tsx
Normal 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 };
|
321
src/client/src/hooks/UseListener.tsx
Normal file
321
src/client/src/hooks/UseListener.tsx
Normal 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]);
|
||||
};
|
|
@ -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;
|
||||
|
|
51
src/client/src/hooks/UseSocket.tsx
Normal file
51
src/client/src/hooks/UseSocket.tsx
Normal 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 };
|
||||
};
|
|
@ -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={() => {
|
||||
|
|
83
src/client/src/views/balance/Logs.tsx
Normal file
83
src/client/src/views/balance/Logs.tsx
Normal 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>
|
||||
);
|
||||
};
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
20
src/server/modules/auth/auth.module.ts
Normal file
20
src/server/modules/auth/auth.module.ts
Normal 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 {}
|
23
src/server/modules/auth/auth.service.ts
Normal file
23
src/server/modules/auth/auth.service.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
};
|
||||
|
|
10
src/server/modules/sub/sub.module.ts
Normal file
10
src/server/modules/sub/sub.module.ts
Normal 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 {}
|
433
src/server/modules/sub/sub.service.ts
Normal file
433
src/server/modules/sub/sub.service.ts
Normal 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);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
68
src/server/modules/ws/ws.gateway.ts
Normal file
68
src/server/modules/ws/ws.gateway.ts
Normal 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}`);
|
||||
}
|
||||
}
|
||||
}
|
11
src/server/modules/ws/ws.module.ts
Normal file
11
src/server/modules/ws/ws.module.ts
Normal 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 {}
|
16
src/server/modules/ws/ws.service.ts
Normal file
16
src/server/modules/ws/ws.service.ts
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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(), '');
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue