ready for migration
This commit is contained in:
parent
46819446d5
commit
c050de214a
42 changed files with 3903 additions and 2262 deletions
615
package-lock.json
generated
615
package-lock.json
generated
|
|
@ -28,7 +28,7 @@
|
|||
"grpc-tools": "^1.11.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
"nostr-tools": "^0.24.1",
|
||||
"nostr-tools": "^1.9.0",
|
||||
"pg": "^8.4.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
@ -48,6 +48,7 @@
|
|||
"@types/jsonwebtoken": "^8.5.9",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/node-fetch": "^2.6.3",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-node": "10.7.0",
|
||||
|
|
@ -130,15 +131,24 @@
|
|||
"node-pre-gyp": "bin/node-pre-gyp"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-0.5.9.tgz",
|
||||
"integrity": "sha512-7lN1Qh6d8DUGmfN36XRsbN/WcGIPNtTGhkw26vWId/DlCIGsYJJootTtPGghTLcn/AaXPx2Q0b3cacrwXa7OVw=="
|
||||
"node_modules/@noble/curves": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz",
|
||||
"integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/secp256k1": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.0.tgz",
|
||||
"integrity": "sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw==",
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz",
|
||||
"integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
|
@ -338,6 +348,48 @@
|
|||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
||||
},
|
||||
"node_modules/@scure/base": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@scure/bip32": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.0.tgz",
|
||||
"integrity": "sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@noble/curves": "~1.0.0",
|
||||
"@noble/hashes": "~1.3.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip39": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz",
|
||||
"integrity": "sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@noble/hashes": "~1.3.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sqltools/formatter": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
|
||||
|
|
@ -472,6 +524,30 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz",
|
||||
"integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg=="
|
||||
},
|
||||
"node_modules/@types/node-fetch": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz",
|
||||
"integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node-fetch/node_modules/form-data": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
||||
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/object-hash": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-1.3.4.tgz",
|
||||
|
|
@ -875,6 +951,7 @@
|
|||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buffer-xor": "^1.0.3",
|
||||
"cipher-base": "^1.0.0",
|
||||
|
|
@ -884,27 +961,6 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/browserify-cipher": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
|
||||
"integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
|
||||
"dependencies": {
|
||||
"browserify-aes": "^1.0.4",
|
||||
"browserify-des": "^1.0.0",
|
||||
"evp_bytestokey": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/browserify-des": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
|
||||
"integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"des.js": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
|
|
@ -944,19 +1000,8 @@
|
|||
"node_modules/buffer-xor": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
||||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="
|
||||
},
|
||||
"node_modules/bufferutil": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz",
|
||||
"integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
|
|
@ -1086,6 +1131,7 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
|
|
@ -1246,6 +1292,7 @@
|
|||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"inherits": "^2.0.1",
|
||||
|
|
@ -1274,23 +1321,6 @@
|
|||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||
"dependencies": {
|
||||
"node-fetch": "2.6.7"
|
||||
}
|
||||
},
|
||||
"node_modules/d": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
||||
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
||||
"dependencies": {
|
||||
"es5-ext": "^0.10.50",
|
||||
"type": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/dataloader": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz",
|
||||
|
|
@ -1348,15 +1378,6 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/des.js": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
|
||||
"integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
|
|
@ -1546,44 +1567,11 @@
|
|||
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/es5-ext": {
|
||||
"version": "0.10.62",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
|
||||
"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"next-tick": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
|
||||
"dependencies": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-promise": {
|
||||
"version": "4.2.8",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||
},
|
||||
"node_modules/es6-symbol": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
|
||||
"dependencies": {
|
||||
"d": "^1.0.1",
|
||||
"ext": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
|
|
@ -1609,6 +1597,7 @@
|
|||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
||||
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"md5.js": "^1.3.4",
|
||||
"safe-buffer": "^5.1.1"
|
||||
|
|
@ -1655,19 +1644,6 @@
|
|||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ext": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
||||
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
|
||||
"dependencies": {
|
||||
"type": "^2.7.2"
|
||||
}
|
||||
},
|
||||
"node_modules/ext/node_modules/type": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
|
||||
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
|
|
@ -1962,6 +1938,7 @@
|
|||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
||||
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.6.0",
|
||||
|
|
@ -1975,6 +1952,7 @@
|
|||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
|
|
@ -1988,6 +1966,7 @@
|
|||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
|
|
@ -2268,11 +2247,6 @@
|
|||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
|
|
@ -2476,6 +2450,7 @@
|
|||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
|
|
@ -2511,33 +2486,6 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/micro-base": {
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/micro-base/-/micro-base-0.10.2.tgz",
|
||||
"integrity": "sha512-lqqJrT7lfJtDmmiQ4zRLZuIJBk96t0RAc5pCrrWpL9zDeH5i/SUL85mku9HqzTI/OCZ8EQ3aicbMW+eK5Nyu5w==",
|
||||
"deprecated": "Switch to @scure/base for audited version of the lib & updates"
|
||||
},
|
||||
"node_modules/micro-bip32": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micro-bip32/-/micro-bip32-0.1.0.tgz",
|
||||
"integrity": "sha512-HxwYJzokbObqPHUqQuzRpCEqZ3EE4uHKrGlLX5ylt0ktD6m9LeS3RkWuQ1HApXEgrGMs3XgykN5Bic2YHE0f6Q==",
|
||||
"deprecated": "Switch to @scure/bip32 for audited version of the lib & updates",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^0.5.7",
|
||||
"@noble/secp256k1": "^1.3.4",
|
||||
"micro-base": "^0.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/micro-bip39": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/micro-bip39/-/micro-bip39-0.1.3.tgz",
|
||||
"integrity": "sha512-lEaRG/MKxFQvG19lfJfPkLIG0rgT28nWud3otN+VgAbrozGqXn2PLaZuYPsy9guQjIZWBTvoLw/HDJQxmMXjMA==",
|
||||
"deprecated": "Switch to @scure/bip39 for audited version of the lib & updates",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^0.5.5",
|
||||
"micro-base": "^0.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
|
|
@ -2728,11 +2676,6 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/next-tick": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz",
|
||||
|
|
@ -2967,19 +2910,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/nostr-tools": {
|
||||
"version": "0.24.1",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-0.24.1.tgz",
|
||||
"integrity": "sha512-+aUWblwNTYra8ZsjmfzxStr4XSKAb0gPsehNP3oBiSouLevqD3FWngc++kh8l+zfMYEPPGS6kS0i9iaq/5ZF6A==",
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.9.0.tgz",
|
||||
"integrity": "sha512-ZvFf1uiBqWLWhLBHD2nY0KsdSdNWKb3PrQUmYMWxSzfT4k48cDrDJu2qgULkOhQbFX7oty8IpaKnLvixhqefqA==",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^0.5.7",
|
||||
"@noble/secp256k1": "^1.5.2",
|
||||
"browserify-cipher": ">=1",
|
||||
"buffer": ">=5",
|
||||
"create-hash": "^1.2.0",
|
||||
"cross-fetch": "^3.1.4",
|
||||
"micro-bip32": "^0.1.0",
|
||||
"micro-bip39": "^0.1.3",
|
||||
"websocket-polyfill": "^0.0.3"
|
||||
"@noble/curves": "1.0.0",
|
||||
"@noble/hashes": "1.3.0",
|
||||
"@scure/base": "1.1.1",
|
||||
"@scure/bip32": "1.3.0",
|
||||
"@scure/bip39": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npmlog": {
|
||||
|
|
@ -3428,6 +3367,7 @@
|
|||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1"
|
||||
|
|
@ -4060,16 +4000,6 @@
|
|||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||
},
|
||||
"node_modules/tstl": {
|
||||
"version": "2.5.13",
|
||||
"resolved": "https://registry.npmjs.org/tstl/-/tstl-2.5.13.tgz",
|
||||
"integrity": "sha512-h9wayHHFI5+yqt8iau0vqH96cTNhezhZ/Fk/hrIdpfkiMu3lg9nzyvMfs5bIdX51IVzZO6DudLqhkL/rVXpT6g=="
|
||||
},
|
||||
"node_modules/type": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
||||
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
|
||||
},
|
||||
"node_modules/type-detect": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||
|
|
@ -4090,14 +4020,6 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||
"dependencies": {
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typeorm": {
|
||||
"version": "0.3.10",
|
||||
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.10.tgz",
|
||||
|
|
@ -4317,18 +4239,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/utf-8-validate": {
|
||||
"version": "5.0.10",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
|
||||
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
|
@ -4369,31 +4279,6 @@
|
|||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/websocket": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
|
||||
"integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
|
||||
"dependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"debug": "^2.2.0",
|
||||
"es5-ext": "^0.10.50",
|
||||
"typedarray-to-buffer": "^3.1.5",
|
||||
"utf-8-validate": "^5.0.2",
|
||||
"yaeti": "^0.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/websocket-polyfill": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/websocket-polyfill/-/websocket-polyfill-0.0.3.tgz",
|
||||
"integrity": "sha512-pF3kR8Uaoau78MpUmFfzbIRxXj9PeQrCuPepGE6JIsfsJ/o/iXr07Q2iQNzKSSblQJ0FiGWlS64N4pVSm+O3Dg==",
|
||||
"dependencies": {
|
||||
"tstl": "^2.0.7",
|
||||
"websocket": "^1.0.28"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
|
|
@ -4483,14 +4368,6 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yaeti": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
|
||||
"integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==",
|
||||
"engines": {
|
||||
"node": ">=0.10.32"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
|
@ -4590,15 +4467,18 @@
|
|||
"tar": "^6.1.11"
|
||||
}
|
||||
},
|
||||
"@noble/hashes": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-0.5.9.tgz",
|
||||
"integrity": "sha512-7lN1Qh6d8DUGmfN36XRsbN/WcGIPNtTGhkw26vWId/DlCIGsYJJootTtPGghTLcn/AaXPx2Q0b3cacrwXa7OVw=="
|
||||
"@noble/curves": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz",
|
||||
"integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==",
|
||||
"requires": {
|
||||
"@noble/hashes": "1.3.0"
|
||||
}
|
||||
},
|
||||
"@noble/secp256k1": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.0.tgz",
|
||||
"integrity": "sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw=="
|
||||
"@noble/hashes": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz",
|
||||
"integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg=="
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
|
|
@ -4759,6 +4639,30 @@
|
|||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
||||
},
|
||||
"@scure/base": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="
|
||||
},
|
||||
"@scure/bip32": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.0.tgz",
|
||||
"integrity": "sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==",
|
||||
"requires": {
|
||||
"@noble/curves": "~1.0.0",
|
||||
"@noble/hashes": "~1.3.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"@scure/bip39": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz",
|
||||
"integrity": "sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==",
|
||||
"requires": {
|
||||
"@noble/hashes": "~1.3.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"@sqltools/formatter": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
|
||||
|
|
@ -4890,6 +4794,29 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz",
|
||||
"integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg=="
|
||||
},
|
||||
"@types/node-fetch": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz",
|
||||
"integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"form-data": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
||||
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/object-hash": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-1.3.4.tgz",
|
||||
|
|
@ -5211,6 +5138,7 @@
|
|||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"buffer-xor": "^1.0.3",
|
||||
"cipher-base": "^1.0.0",
|
||||
|
|
@ -5220,27 +5148,6 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"browserify-cipher": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
|
||||
"integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
|
||||
"requires": {
|
||||
"browserify-aes": "^1.0.4",
|
||||
"browserify-des": "^1.0.0",
|
||||
"evp_bytestokey": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"browserify-des": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
|
||||
"integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
|
||||
"requires": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"des.js": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
|
|
@ -5263,15 +5170,8 @@
|
|||
"buffer-xor": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
||||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="
|
||||
},
|
||||
"bufferutil": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz",
|
||||
"integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==",
|
||||
"requires": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
}
|
||||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
|
||||
"optional": true
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.2",
|
||||
|
|
@ -5366,6 +5266,7 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
|
|
@ -5491,6 +5392,7 @@
|
|||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"inherits": "^2.0.1",
|
||||
|
|
@ -5519,23 +5421,6 @@
|
|||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"cross-fetch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||
"requires": {
|
||||
"node-fetch": "2.6.7"
|
||||
}
|
||||
},
|
||||
"d": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
||||
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
||||
"requires": {
|
||||
"es5-ext": "^0.10.50",
|
||||
"type": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"dataloader": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz",
|
||||
|
|
@ -5577,15 +5462,6 @@
|
|||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
|
||||
},
|
||||
"des.js": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
|
||||
"integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
|
|
@ -5740,40 +5616,11 @@
|
|||
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
|
||||
"optional": true
|
||||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.62",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
|
||||
"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
|
||||
"requires": {
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"next-tick": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
|
||||
"requires": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "4.2.8",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||
},
|
||||
"es6-symbol": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
|
||||
"requires": {
|
||||
"d": "^1.0.1",
|
||||
"ext": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
|
|
@ -5793,6 +5640,7 @@
|
|||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
||||
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"md5.js": "^1.3.4",
|
||||
"safe-buffer": "^5.1.1"
|
||||
|
|
@ -5836,21 +5684,6 @@
|
|||
"vary": "~1.1.2"
|
||||
}
|
||||
},
|
||||
"ext": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
||||
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
|
||||
"requires": {
|
||||
"type": "^2.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"type": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
|
||||
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
|
|
@ -6059,6 +5892,7 @@
|
|||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
||||
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.6.0",
|
||||
|
|
@ -6069,6 +5903,7 @@
|
|||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
|
|
@ -6079,6 +5914,7 @@
|
|||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
|
|
@ -6293,11 +6129,6 @@
|
|||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
|
|
@ -6480,6 +6311,7 @@
|
|||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
|
|
@ -6506,30 +6338,6 @@
|
|||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
|
||||
},
|
||||
"micro-base": {
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/micro-base/-/micro-base-0.10.2.tgz",
|
||||
"integrity": "sha512-lqqJrT7lfJtDmmiQ4zRLZuIJBk96t0RAc5pCrrWpL9zDeH5i/SUL85mku9HqzTI/OCZ8EQ3aicbMW+eK5Nyu5w=="
|
||||
},
|
||||
"micro-bip32": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micro-bip32/-/micro-bip32-0.1.0.tgz",
|
||||
"integrity": "sha512-HxwYJzokbObqPHUqQuzRpCEqZ3EE4uHKrGlLX5ylt0ktD6m9LeS3RkWuQ1HApXEgrGMs3XgykN5Bic2YHE0f6Q==",
|
||||
"requires": {
|
||||
"@noble/hashes": "^0.5.7",
|
||||
"@noble/secp256k1": "^1.3.4",
|
||||
"micro-base": "^0.10.1"
|
||||
}
|
||||
},
|
||||
"micro-bip39": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/micro-bip39/-/micro-bip39-0.1.3.tgz",
|
||||
"integrity": "sha512-lEaRG/MKxFQvG19lfJfPkLIG0rgT28nWud3otN+VgAbrozGqXn2PLaZuYPsy9guQjIZWBTvoLw/HDJQxmMXjMA==",
|
||||
"requires": {
|
||||
"@noble/hashes": "^0.5.5",
|
||||
"micro-base": "^0.10.1"
|
||||
}
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
|
|
@ -6670,11 +6478,6 @@
|
|||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
|
||||
},
|
||||
"next-tick": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||
},
|
||||
"node-addon-api": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz",
|
||||
|
|
@ -6851,19 +6654,15 @@
|
|||
"dev": true
|
||||
},
|
||||
"nostr-tools": {
|
||||
"version": "0.24.1",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-0.24.1.tgz",
|
||||
"integrity": "sha512-+aUWblwNTYra8ZsjmfzxStr4XSKAb0gPsehNP3oBiSouLevqD3FWngc++kh8l+zfMYEPPGS6kS0i9iaq/5ZF6A==",
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.9.0.tgz",
|
||||
"integrity": "sha512-ZvFf1uiBqWLWhLBHD2nY0KsdSdNWKb3PrQUmYMWxSzfT4k48cDrDJu2qgULkOhQbFX7oty8IpaKnLvixhqefqA==",
|
||||
"requires": {
|
||||
"@noble/hashes": "^0.5.7",
|
||||
"@noble/secp256k1": "^1.5.2",
|
||||
"browserify-cipher": ">=1",
|
||||
"buffer": ">=5",
|
||||
"create-hash": "^1.2.0",
|
||||
"cross-fetch": "^3.1.4",
|
||||
"micro-bip32": "^0.1.0",
|
||||
"micro-bip39": "^0.1.3",
|
||||
"websocket-polyfill": "^0.0.3"
|
||||
"@noble/curves": "1.0.0",
|
||||
"@noble/hashes": "1.3.0",
|
||||
"@scure/base": "1.1.1",
|
||||
"@scure/bip32": "1.3.0",
|
||||
"@scure/bip39": "1.2.0"
|
||||
}
|
||||
},
|
||||
"npmlog": {
|
||||
|
|
@ -7194,6 +6993,7 @@
|
|||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1"
|
||||
|
|
@ -7672,16 +7472,6 @@
|
|||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||
},
|
||||
"tstl": {
|
||||
"version": "2.5.13",
|
||||
"resolved": "https://registry.npmjs.org/tstl/-/tstl-2.5.13.tgz",
|
||||
"integrity": "sha512-h9wayHHFI5+yqt8iau0vqH96cTNhezhZ/Fk/hrIdpfkiMu3lg9nzyvMfs5bIdX51IVzZO6DudLqhkL/rVXpT6g=="
|
||||
},
|
||||
"type": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
||||
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
|
||||
},
|
||||
"type-detect": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||
|
|
@ -7696,14 +7486,6 @@
|
|||
"mime-types": "~2.1.24"
|
||||
}
|
||||
},
|
||||
"typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||
"requires": {
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"typeorm": {
|
||||
"version": "0.3.10",
|
||||
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.10.tgz",
|
||||
|
|
@ -7812,14 +7594,6 @@
|
|||
"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
|
||||
"integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw=="
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"version": "5.0.10",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
|
||||
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
|
||||
"requires": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
|
@ -7851,28 +7625,6 @@
|
|||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"websocket": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
|
||||
"integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
|
||||
"requires": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"debug": "^2.2.0",
|
||||
"es5-ext": "^0.10.50",
|
||||
"typedarray-to-buffer": "^3.1.5",
|
||||
"utf-8-validate": "^5.0.2",
|
||||
"yaeti": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"websocket-polyfill": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/websocket-polyfill/-/websocket-polyfill-0.0.3.tgz",
|
||||
"integrity": "sha512-pF3kR8Uaoau78MpUmFfzbIRxXj9PeQrCuPepGE6JIsfsJ/o/iXr07Q2iQNzKSSblQJ0FiGWlS64N4pVSm+O3Dg==",
|
||||
"requires": {
|
||||
"tstl": "^2.0.7",
|
||||
"websocket": "^1.0.28"
|
||||
}
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
|
|
@ -7938,11 +7690,6 @@
|
|||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
|
||||
},
|
||||
"yaeti": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
|
||||
"integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
"grpc-tools": "^1.11.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
"nostr-tools": "^0.24.1",
|
||||
"nostr-tools": "^1.9.0",
|
||||
"pg": "^8.4.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
@ -63,9 +63,10 @@
|
|||
"@types/jsonwebtoken": "^8.5.9",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/node-fetch": "^2.6.3",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-node": "10.7.0",
|
||||
"typescript": "4.5.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,12 +7,13 @@ export type Logger = { log: (v: any) => void, error: (v: any) => void }
|
|||
export type ServerOptions = {
|
||||
allowCors?: true
|
||||
staticFiles?: string
|
||||
allowNotImplementedMethods?: true
|
||||
allowNotImplementedMethods?: number
|
||||
logger?: Logger
|
||||
throwErrors?: true
|
||||
GuestAuthGuard: (authorizationHeader?: string) => Promise<Types.GuestContext>
|
||||
UserAuthGuard: (authorizationHeader?: string) => Promise<Types.UserContext>
|
||||
AdminAuthGuard: (authorizationHeader?: string) => Promise<Types.AdminContext>
|
||||
AppAuthGuard: (authorizationHeader?: string) => Promise<Types.AppContext>
|
||||
decryptCallback: (encryptionDeviceId: string, body: any) => Promise<any>
|
||||
encryptCallback: (encryptionDeviceId: string, plain: any) => Promise<any>
|
||||
}
|
||||
|
|
@ -51,19 +52,129 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.LndGetInfo) throw new Error('method: LndGetInfo is not implemented')
|
||||
app.post('/api/lnd/getinfo', async (req, res) => {
|
||||
app.post('/api/admin/lnd/getinfo', async (req, res) => {
|
||||
try {
|
||||
if (!methods.LndGetInfo) throw new Error('method: LndGetInfo is not implemented')
|
||||
const authContext = await opts.AdminAuthGuard(req.headers['authorization'])
|
||||
const encryptionDeviceId = req.headers['x-e2ee-device-id-x']
|
||||
if (typeof encryptionDeviceId !== 'string' || encryptionDeviceId === '') throw new Error('invalid encryption header provided')
|
||||
const request = await opts.decryptCallback(encryptionDeviceId, req.body)
|
||||
const request = req.body
|
||||
const error = Types.LndGetInfoRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.LndGetInfo({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...await opts.encryptCallback(encryptionDeviceId, response)})
|
||||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.AddApp) throw new Error('method: AddApp is not implemented')
|
||||
app.post('/api/admin/app/add', async (req, res) => {
|
||||
try {
|
||||
if (!methods.AddApp) throw new Error('method: AddApp is not implemented')
|
||||
const authContext = await opts.AdminAuthGuard(req.headers['authorization'])
|
||||
const request = req.body
|
||||
const error = Types.AddAppRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddApp({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.AddAppUser) throw new Error('method: AddAppUser is not implemented')
|
||||
app.post('/api/app/user/add', async (req, res) => {
|
||||
try {
|
||||
if (!methods.AddAppUser) throw new Error('method: AddAppUser is not implemented')
|
||||
const authContext = await opts.AppAuthGuard(req.headers['authorization'])
|
||||
const request = req.body
|
||||
const error = Types.AddAppUserRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddAppUser({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.AddAppInvoice) throw new Error('method: AddAppInvoice is not implemented')
|
||||
app.post('/api/app/add/invoice', async (req, res) => {
|
||||
try {
|
||||
if (!methods.AddAppInvoice) throw new Error('method: AddAppInvoice is not implemented')
|
||||
const authContext = await opts.AppAuthGuard(req.headers['authorization'])
|
||||
const request = req.body
|
||||
const error = Types.AddAppInvoiceRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddAppInvoice({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.AddAppUserInvoice) throw new Error('method: AddAppUserInvoice is not implemented')
|
||||
app.post('/api/app/user/add/invoice', async (req, res) => {
|
||||
try {
|
||||
if (!methods.AddAppUserInvoice) throw new Error('method: AddAppUserInvoice is not implemented')
|
||||
const authContext = await opts.AppAuthGuard(req.headers['authorization'])
|
||||
const request = req.body
|
||||
const error = Types.AddAppUserInvoiceRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddAppUserInvoice({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.GetAppUser) throw new Error('method: GetAppUser is not implemented')
|
||||
app.post('/api/app/user/get', async (req, res) => {
|
||||
try {
|
||||
if (!methods.GetAppUser) throw new Error('method: GetAppUser is not implemented')
|
||||
const authContext = await opts.AppAuthGuard(req.headers['authorization'])
|
||||
const request = req.body
|
||||
const error = Types.GetAppUserRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetAppUser({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.PayAppUserInvoice) throw new Error('method: PayAppUserInvoice is not implemented')
|
||||
app.post('/api/app/invoice/pay', async (req, res) => {
|
||||
try {
|
||||
if (!methods.PayAppUserInvoice) throw new Error('method: PayAppUserInvoice is not implemented')
|
||||
const authContext = await opts.AppAuthGuard(req.headers['authorization'])
|
||||
const request = req.body
|
||||
const error = Types.PayAppUserInvoiceRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.PayAppUserInvoice({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.SendAppUserToAppUserPayment) throw new Error('method: SendAppUserToAppUserPayment is not implemented')
|
||||
app.post('/api/app/user/internal/pay', async (req, res) => {
|
||||
try {
|
||||
if (!methods.SendAppUserToAppUserPayment) throw new Error('method: SendAppUserToAppUserPayment is not implemented')
|
||||
const authContext = await opts.AppAuthGuard(req.headers['authorization'])
|
||||
const request = req.body
|
||||
const error = Types.SendAppUserToAppUserPaymentRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.SendAppUserToAppUserPayment({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK'})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.SendAppUserToAppPayment) throw new Error('method: SendAppUserToAppPayment is not implemented')
|
||||
app.post('/api/app/internal/pay', async (req, res) => {
|
||||
try {
|
||||
if (!methods.SendAppUserToAppPayment) throw new Error('method: SendAppUserToAppPayment is not implemented')
|
||||
const authContext = await opts.AppAuthGuard(req.headers['authorization'])
|
||||
const request = req.body
|
||||
const error = Types.SendAppUserToAppPaymentRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.SendAppUserToAppPayment({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK'})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.AddUser) throw new Error('method: AddUser is not implemented')
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export type ClientParams = {
|
|||
retrieveGuestAuth: () => Promise<string | null>
|
||||
retrieveUserAuth: () => Promise<string | null>
|
||||
retrieveAdminAuth: () => Promise<string | null>
|
||||
retrieveAppAuth: () => Promise<string | null>
|
||||
encryptCallback: (plain: any) => Promise<any>
|
||||
decryptCallback: (encrypted: any) => Promise<any>
|
||||
deviceId: string
|
||||
|
|
@ -39,17 +40,123 @@ export default (params: ClientParams) => ({
|
|||
LndGetInfo: async (request: Types.LndGetInfoRequest): Promise<ResultError | ({ status: 'OK' }& Types.LndGetInfoResponse)> => {
|
||||
const auth = await params.retrieveAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveAdminAuth() returned null')
|
||||
let finalRoute = '/api/lnd/getinfo'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, await params.encryptCallback(request), { headers: { 'authorization': auth, 'x-e2ee-device-id-x': params.deviceId } })
|
||||
let finalRoute = '/api/admin/lnd/getinfo'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = await params.decryptCallback(data)
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.LndGetInfoResponseValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddApp: async (request: Types.AddAppRequest): Promise<ResultError | ({ status: 'OK' }& Types.AddAppResponse)> => {
|
||||
const auth = await params.retrieveAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveAdminAuth() returned null')
|
||||
let finalRoute = '/api/admin/app/add'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.AddAppResponseValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddAppUser: async (request: Types.AddAppUserRequest): Promise<ResultError | ({ status: 'OK' }& Types.AppUser)> => {
|
||||
const auth = await params.retrieveAppAuth()
|
||||
if (auth === null) throw new Error('retrieveAppAuth() returned null')
|
||||
let finalRoute = '/api/app/user/add'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.AppUserValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddAppInvoice: async (request: Types.AddAppInvoiceRequest): Promise<ResultError | ({ status: 'OK' }& Types.NewInvoiceResponse)> => {
|
||||
const auth = await params.retrieveAppAuth()
|
||||
if (auth === null) throw new Error('retrieveAppAuth() returned null')
|
||||
let finalRoute = '/api/app/add/invoice'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.NewInvoiceResponseValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddAppUserInvoice: async (request: Types.AddAppUserInvoiceRequest): Promise<ResultError | ({ status: 'OK' }& Types.NewInvoiceResponse)> => {
|
||||
const auth = await params.retrieveAppAuth()
|
||||
if (auth === null) throw new Error('retrieveAppAuth() returned null')
|
||||
let finalRoute = '/api/app/user/add/invoice'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.NewInvoiceResponseValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetAppUser: async (request: Types.GetAppUserRequest): Promise<ResultError | ({ status: 'OK' }& Types.AppUser)> => {
|
||||
const auth = await params.retrieveAppAuth()
|
||||
if (auth === null) throw new Error('retrieveAppAuth() returned null')
|
||||
let finalRoute = '/api/app/user/get'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.AppUserValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
PayAppUserInvoice: async (request: Types.PayAppUserInvoiceRequest): Promise<ResultError | ({ status: 'OK' }& Types.PayAppUserInvoiceResponse)> => {
|
||||
const auth = await params.retrieveAppAuth()
|
||||
if (auth === null) throw new Error('retrieveAppAuth() returned null')
|
||||
let finalRoute = '/api/app/invoice/pay'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.PayAppUserInvoiceResponseValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
SendAppUserToAppUserPayment: async (request: Types.SendAppUserToAppUserPaymentRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveAppAuth()
|
||||
if (auth === null) throw new Error('retrieveAppAuth() returned null')
|
||||
let finalRoute = '/api/app/user/internal/pay'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
SendAppUserToAppPayment: async (request: Types.SendAppUserToAppPaymentRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveAppAuth()
|
||||
if (auth === null) throw new Error('retrieveAppAuth() returned null')
|
||||
let finalRoute = '/api/app/internal/pay'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddUser: async (request: Types.AddUserRequest): Promise<ResultError | ({ status: 'OK' }& Types.AddUserResponse)> => {
|
||||
const auth = await params.retrieveGuestAuth()
|
||||
if (auth === null) throw new Error('retrieveGuestAuth() returned null')
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
BIN
proto/protoc-gen-pub.exe → proto/protoc-gen-pub
Normal file → Executable file
BIN
proto/protoc-gen-pub.exe → proto/protoc-gen-pub
Normal file → Executable file
Binary file not shown.
|
|
@ -25,11 +25,19 @@ option (file_options) = {
|
|||
{
|
||||
id: "admin",
|
||||
name: "Admin",
|
||||
encrypted:true,
|
||||
//encrypted:true,
|
||||
context:{
|
||||
key:"admin_id",
|
||||
value:"string"
|
||||
}
|
||||
},
|
||||
{
|
||||
id:"app",
|
||||
name:"App",
|
||||
context:{
|
||||
key:"app_id",
|
||||
value: "string"
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
|
@ -73,11 +81,65 @@ service LightningPub {
|
|||
option (http_method) = "post";
|
||||
option (http_route) = "/api/encryption/exchange";
|
||||
};
|
||||
|
||||
rpc LndGetInfo(structs.LndGetInfoRequest) returns (structs.LndGetInfoResponse){
|
||||
option (auth_type) = "Admin";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/lnd/getinfo";
|
||||
option (http_route) = "/api/admin/lnd/getinfo";
|
||||
};
|
||||
|
||||
// <App>
|
||||
|
||||
rpc AddApp(structs.AddAppRequest) returns (structs.AddAppResponse) {
|
||||
option (auth_type) = "Admin";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/admin/app/add";
|
||||
};
|
||||
|
||||
rpc AddAppUser(structs.AddAppUserRequest)returns (structs.AppUser) {
|
||||
option (auth_type) = "App";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/app/user/add";
|
||||
};
|
||||
|
||||
rpc AddAppInvoice(structs.AddAppInvoiceRequest) returns (structs.NewInvoiceResponse) {
|
||||
option (auth_type) = "App";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/app/add/invoice";
|
||||
}
|
||||
|
||||
rpc AddAppUserInvoice(structs.AddAppUserInvoiceRequest) returns (structs.NewInvoiceResponse) {
|
||||
option (auth_type) = "App";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/app/user/add/invoice";
|
||||
}
|
||||
|
||||
rpc GetAppUser(structs.GetAppUserRequest) returns (structs.AppUser) {
|
||||
option (auth_type) = "App";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/app/user/get";
|
||||
}
|
||||
|
||||
rpc PayAppUserInvoice(structs.PayAppUserInvoiceRequest) returns (structs.PayAppUserInvoiceResponse) {
|
||||
option (auth_type) = "App";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/app/invoice/pay";
|
||||
}
|
||||
|
||||
rpc SendAppUserToAppUserPayment(structs.SendAppUserToAppUserPaymentRequest) returns (structs.Empty) {
|
||||
option (auth_type) = "App";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/app/user/internal/pay";
|
||||
}
|
||||
|
||||
rpc SendAppUserToAppPayment(structs.SendAppUserToAppPaymentRequest) returns (structs.Empty) {
|
||||
option (auth_type) = "App";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/app/internal/pay";
|
||||
}
|
||||
|
||||
// </App>
|
||||
|
||||
rpc AddUser(structs.AddUserRequest)returns (structs.AddUserResponse){
|
||||
option (auth_type) = "Guest";
|
||||
option (http_method) = "post";
|
||||
|
|
|
|||
|
|
@ -19,6 +19,69 @@ message LndGetInfoRequest {
|
|||
message LndGetInfoResponse {
|
||||
string alias = 1;
|
||||
}
|
||||
|
||||
message AddAppRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message AddAppResponse {
|
||||
string name = 1;
|
||||
string id = 2;
|
||||
string auth_token = 3;
|
||||
}
|
||||
|
||||
message AddAppUserRequest {
|
||||
string identifier = 1;
|
||||
bool fail_if_exists = 2;
|
||||
int64 balance = 3;
|
||||
}
|
||||
|
||||
message AppUser {
|
||||
string identifier = 1;
|
||||
UserInfo info = 2;
|
||||
int64 max_withdrawable = 3;
|
||||
}
|
||||
|
||||
message AddAppInvoiceRequest {
|
||||
string payer_identifier = 1;
|
||||
string http_callback_url = 2;
|
||||
NewInvoiceRequest invoice_req = 3;
|
||||
}
|
||||
|
||||
message AddAppUserInvoiceRequest {
|
||||
string receiver_identifier = 1;
|
||||
string payer_identifier = 2;
|
||||
string http_callback_url = 3;
|
||||
NewInvoiceRequest invoice_req = 4;
|
||||
}
|
||||
|
||||
message GetAppUserRequest {
|
||||
string user_identifier = 1;
|
||||
}
|
||||
|
||||
message PayAppUserInvoiceRequest {
|
||||
string user_identifier = 1;
|
||||
string invoice = 2;
|
||||
int64 amount = 3;
|
||||
}
|
||||
|
||||
message PayAppUserInvoiceResponse {
|
||||
string preimage = 1;
|
||||
int64 amount_paid = 2;
|
||||
}
|
||||
|
||||
|
||||
message SendAppUserToAppUserPaymentRequest {
|
||||
string from_user_identifier = 1;
|
||||
string to_user_identifier = 2;
|
||||
int64 amount = 3;
|
||||
}
|
||||
|
||||
message SendAppUserToAppPaymentRequest {
|
||||
string from_user_identifier = 1;
|
||||
int64 amount = 2;
|
||||
}
|
||||
|
||||
enum AddressType {
|
||||
WITNESS_PUBKEY_HASH = 0;
|
||||
NESTED_PUBKEY_HASH = 1;
|
||||
|
|
@ -61,6 +124,7 @@ message PayInvoiceRequest{
|
|||
|
||||
message PayInvoiceResponse{
|
||||
string preimage = 1;
|
||||
int64 amount_paid = 2;
|
||||
}
|
||||
|
||||
message OpenChannelRequest{
|
||||
|
|
@ -129,13 +193,18 @@ message GetUserOperationsRequest{
|
|||
int64 latestOutgoingInvoice = 2;
|
||||
int64 latestIncomingTx = 3;
|
||||
int64 latestOutgoingTx = 4;
|
||||
int64 latestIncomingUserToUserPayment = 5;
|
||||
int64 latestOutgoingUserToUserPayment = 6;
|
||||
}
|
||||
enum UserOperationType {
|
||||
INCOMING_TX =0;
|
||||
OUTGOING_TX =1;
|
||||
INCOMING_INVOICE =2;
|
||||
OUTGOING_INVOICE=3;
|
||||
OUTGOING_USER_TO_USER=4;
|
||||
INCOMING_USER_TO_USER=5;
|
||||
}
|
||||
|
||||
message UserOperation{
|
||||
int64 paidAtUnix=1;
|
||||
UserOperationType type = 2;
|
||||
|
|
@ -144,14 +213,16 @@ message UserOperation{
|
|||
}
|
||||
message UserOperations {
|
||||
int64 fromIndex=1;
|
||||
int64 toIndex=2;
|
||||
repeated UserOperation operations=3;
|
||||
int64 toIndex=2;
|
||||
repeated UserOperation operations=3;
|
||||
}
|
||||
message GetUserOperationsResponse{
|
||||
UserOperations latestOutgoingInvoiceOperations=1;
|
||||
UserOperations latestIncomingInvoiceOperations=2;
|
||||
UserOperations latestOutgoingTxOperations=3;
|
||||
UserOperations latestIncomingTxOperations=4;
|
||||
UserOperations latestOutgoingUserToUserPayemnts=5;
|
||||
UserOperations latestIncomingUserToUserPayemnts=6 ;
|
||||
}
|
||||
|
||||
message AddProductRequest {
|
||||
|
|
|
|||
27
src/auth.ts
27
src/auth.ts
|
|
@ -1,13 +1,34 @@
|
|||
import { ServerOptions } from "../proto/autogenerated/ts/express_server";
|
||||
import { AdminContext } from "../proto/autogenerated/ts/types";
|
||||
import Main from './services/main'
|
||||
const serverOptions = (mainHandler: Main): ServerOptions => {
|
||||
return {
|
||||
AdminAuthGuard: async (authHeader) => { console.log("admin auth login with header: " + authHeader); return { admin_id: "__Admin__" } },
|
||||
UserAuthGuard: async (authHeader) => { return { user_id: mainHandler.DecodeUserToken(authHeader) } },
|
||||
AdminAuthGuard: adminAuth,
|
||||
AppAuthGuard: async (authHeader) => { return { app_id: mainHandler.applicationManager.DecodeAppToken(authHeader) } },
|
||||
UserAuthGuard: async (authHeader) => { return { user_id: mainHandler.userManager.DecodeUserToken(authHeader) } },
|
||||
GuestAuthGuard: async (_) => ({}),
|
||||
encryptCallback: async (_, b) => b,
|
||||
decryptCallback: async (_, b) => b,
|
||||
throwErrors: true
|
||||
//throwErrors: true
|
||||
}
|
||||
}
|
||||
|
||||
const adminAuth = async (header: string | undefined): Promise<AdminContext> => {
|
||||
const AdminToken = process.env.ADMIN_TOKEN
|
||||
if (!AdminToken) {
|
||||
throw new Error("admin auth disabled")
|
||||
}
|
||||
if (!header) {
|
||||
throw new Error("admin header not found")
|
||||
}
|
||||
let h = header
|
||||
|
||||
if (header.startsWith("Bearer ")) {
|
||||
h = header.substring("Bearer ".length)
|
||||
}
|
||||
if (h !== AdminToken) {
|
||||
throw new Error("admin token invalid")
|
||||
}
|
||||
return { admin_id: "admin1" }
|
||||
}
|
||||
export default serverOptions
|
||||
|
|
@ -18,10 +18,10 @@ import { NostrRequest } from '../proto/autogenerated/ts/nostr_transport.js';
|
|||
const settings = LoadNosrtSettingsFromEnv(true)
|
||||
|
||||
const clientPrivateKey = generatePrivateKey()
|
||||
const clientPublicKey = getPublicKey(Buffer.from(clientPrivateKey, "hex"))
|
||||
const clientPublicKey = getPublicKey(clientPrivateKey)
|
||||
|
||||
const serverPrivateKey = generatePrivateKey()
|
||||
const serverPublicKey = getPublicKey(Buffer.from(serverPrivateKey, "hex"))
|
||||
const serverPublicKey = getPublicKey(serverPrivateKey)
|
||||
|
||||
const testPort = 4000
|
||||
var userAuthHeader = ""
|
||||
|
|
@ -30,6 +30,7 @@ const client = NewClient({
|
|||
retrieveAdminAuth: async () => (""),
|
||||
retrieveGuestAuth: async () => (""),
|
||||
retrieveUserAuth: async () => userAuthHeader,
|
||||
retrieveAppAuth: async () => (""),
|
||||
decryptCallback: async (b) => b,
|
||||
encryptCallback: async (b) => b,
|
||||
deviceId: "device0"
|
||||
|
|
@ -111,7 +112,7 @@ export default async (d: (message: string, failure?: boolean) => void) => {
|
|||
const res = await client.AddUser({ name: "test", callbackUrl: "http://...", secret: "shhhhhht" })
|
||||
if (res.status === 'ERROR') throw new Error(res.reason)
|
||||
console.log(res)
|
||||
const user = await mainHandler.storage.GetUser(res.userId)
|
||||
const user = await mainHandler.storage.userStorage.GetUser(res.userId)
|
||||
console.log(user)
|
||||
userAuthHeader = res.authToken
|
||||
d("create user ok")
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
|||
if (!pub || !nostrSettings.allowedPubs.includes(pub)) {
|
||||
throw new Error("nostr pub invalid or not allowed" + pub)
|
||||
}
|
||||
let nostrUser = await mainHandler.storage.FindNostrUser(pub)
|
||||
let nostrUser = await mainHandler.storage.userStorage.FindNostrUser(pub)
|
||||
if (!nostrUser) { // TODO: add POW
|
||||
nostrUser = await mainHandler.storage.AddNostrUser(pub)
|
||||
nostrUser = await mainHandler.storage.userStorage.AddNostrUser(pub)
|
||||
}
|
||||
return { user_id: nostrUser.user.user_id }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { OpenChannelRequest, Invoice } from "../../../proto/lnd/lightning";
|
||||
|
||||
export const AddInvoiceReq = (value: number, memo = "", privateHints = false, expiry = 60 * 60): Invoice => ({
|
||||
export const AddInvoiceReq = (value: number, memo = "", expiry = 60 * 60, privateHints = false): Invoice => ({
|
||||
expiry: BigInt(expiry),
|
||||
memo: memo,
|
||||
private: privateHints,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export default async (d: (message: string, failure?: boolean) => void) => {
|
|||
console.log(addr)
|
||||
d("new address ok")
|
||||
|
||||
const invoice = await lnd.NewInvoice(1000, "")
|
||||
const invoice = await lnd.NewInvoice(1000, "", 60 * 60)
|
||||
console.log(invoice)
|
||||
d("new invoice ok")
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,16 @@ export default class {
|
|||
const res = await this.lightning.getInfo({}, DeadLineMetadata())
|
||||
return res.response
|
||||
}
|
||||
|
||||
async Health() {
|
||||
if (!this.ready) {
|
||||
throw new Error("not ready")
|
||||
}
|
||||
const info = await this.GetInfo()
|
||||
if (!info.syncedToChain || !info.syncedToGraph) {
|
||||
throw new Error("not ready")
|
||||
}
|
||||
}
|
||||
checkReady() {
|
||||
if (!this.ready) throw new Error("lnd not ready, warmup required before usage")
|
||||
}
|
||||
|
|
@ -141,9 +151,9 @@ export default class {
|
|||
return res.response
|
||||
}
|
||||
|
||||
async NewInvoice(value: number, memo: string): Promise<AddInvoiceResponse> {
|
||||
async NewInvoice(value: number, memo: string, expiry: number): Promise<AddInvoiceResponse> {
|
||||
this.checkReady()
|
||||
const res = await this.lightning.addInvoice(AddInvoiceReq(value, memo), DeadLineMetadata())
|
||||
const res = await this.lightning.addInvoice(AddInvoiceReq(value, memo, expiry), DeadLineMetadata())
|
||||
return res.response
|
||||
}
|
||||
|
||||
|
|
|
|||
103
src/services/main/applicationManager.ts
Normal file
103
src/services/main/applicationManager.ts
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import jwt from 'jsonwebtoken'
|
||||
import Storage from '../storage/index.js'
|
||||
import * as Types from '../../../proto/autogenerated/ts/types.js'
|
||||
import UserManager from './userManager.js'
|
||||
import { MainSettings } from './settings.js'
|
||||
import PaymentManager from './paymentManager.js'
|
||||
import { InboundOptionals, defaultInvoiceExpiry } from '../storage/paymentStorage.js'
|
||||
import { ApplicationUser } from '../storage/entity/ApplicationUser.js'
|
||||
export default class {
|
||||
storage: Storage
|
||||
settings: MainSettings
|
||||
userManager: UserManager
|
||||
paymentManager: PaymentManager
|
||||
constructor(storage: Storage, settings: MainSettings, userManager: UserManager, paymentManager: PaymentManager) {
|
||||
this.storage = storage
|
||||
this.settings = settings
|
||||
this.userManager = userManager
|
||||
this.paymentManager = paymentManager
|
||||
}
|
||||
SignAppToken(appId: string): string {
|
||||
return jwt.sign({ appId }, this.settings.jwtSecret);
|
||||
}
|
||||
DecodeAppToken(token?: string): string {
|
||||
if (!token) throw new Error("empty app token provided")
|
||||
let t = token
|
||||
if (token.startsWith("Bearer ")) {
|
||||
t = token.substring("Bearer ".length)
|
||||
}
|
||||
if (!t) throw new Error("no app token provided")
|
||||
return (jwt.verify(token, this.settings.jwtSecret) as { appId: string }).appId
|
||||
}
|
||||
|
||||
|
||||
async AddApp(req: Types.AddAppRequest): Promise<Types.AddAppResponse> {
|
||||
const app = await this.storage.applicationStorage.AddApplication(req.name)
|
||||
return {
|
||||
id: app.app_id,
|
||||
name: app.name,
|
||||
auth_token: this.SignAppToken(app.app_id)
|
||||
}
|
||||
}
|
||||
|
||||
async AddAppUser(appId: string, req: Types.AddAppUserRequest): Promise<Types.AppUser> {
|
||||
let u: ApplicationUser
|
||||
if (req.fail_if_exists) {
|
||||
u = await this.storage.applicationStorage.AddApplicationUser(appId, req.identifier, req.balance)
|
||||
} else {
|
||||
u = await this.storage.applicationStorage.GetOrCreateApplicationUser(appId, req.identifier, req.balance)
|
||||
}
|
||||
return {
|
||||
identifier: u.identifier,
|
||||
info: {
|
||||
userId: u.user.user_id,
|
||||
balance: u.user.balance_sats
|
||||
},
|
||||
max_withdrawable: this.paymentManager.GetMaxPayableInvoice(u.user.balance_sats)
|
||||
}
|
||||
}
|
||||
|
||||
async AddAppInvoice(appId: string, req: Types.AddAppInvoiceRequest): Promise<Types.NewInvoiceResponse> {
|
||||
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||
const payer = await this.storage.applicationStorage.GetOrCreateApplicationUser(appId, req.payer_identifier, 0)
|
||||
const opts: InboundOptionals = { callbackUrl: req.http_callback_url, expiry: defaultInvoiceExpiry, expectedPayer: payer.user }
|
||||
return this.paymentManager.NewInvoice(app.owner.user_id, req.invoice_req, opts)
|
||||
}
|
||||
|
||||
async AddAppUserInvoice(appId: string, req: Types.AddAppUserInvoiceRequest): Promise<Types.NewInvoiceResponse> {
|
||||
const receiver = await this.storage.applicationStorage.GetApplicationUser(appId, req.receiver_identifier)
|
||||
const payer = await this.storage.applicationStorage.GetOrCreateApplicationUser(appId, req.payer_identifier, 0)
|
||||
const opts: InboundOptionals = { callbackUrl: req.http_callback_url, expiry: defaultInvoiceExpiry, expectedPayer: payer.user }
|
||||
const appUserInvoice = await this.paymentManager.NewInvoice(receiver.user.user_id, req.invoice_req, opts)
|
||||
return {
|
||||
invoice: appUserInvoice.invoice
|
||||
}
|
||||
}
|
||||
|
||||
async GetAppUser(appId: string, req: Types.GetAppUserRequest): Promise<Types.AppUser> {
|
||||
const user = await this.storage.applicationStorage.GetApplicationUser(appId, req.user_identifier)
|
||||
const max = this.paymentManager.GetMaxPayableInvoice(user.user.balance_sats)
|
||||
return {
|
||||
max_withdrawable: max, identifier: req.user_identifier, info: {
|
||||
userId: user.user.user_id, balance: user.user.balance_sats
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async PayAppUserInvoice(appId: string, req: Types.PayAppUserInvoiceRequest): Promise<Types.PayAppUserInvoiceResponse> {
|
||||
const appUser = await this.storage.applicationStorage.GetApplicationUser(appId, req.user_identifier)
|
||||
return this.paymentManager.PayInvoice(appUser.user.user_id, req)
|
||||
}
|
||||
|
||||
async SendAppUserToAppUserPayment(appId: string, req: Types.SendAppUserToAppUserPaymentRequest): Promise<void> {
|
||||
const fromUser = await this.storage.applicationStorage.GetApplicationUser(appId, req.from_user_identifier)
|
||||
const toUser = await this.storage.applicationStorage.GetApplicationUser(appId, req.to_user_identifier)
|
||||
await this.paymentManager.SendUserToUserPayment(fromUser.user.user_id, toUser.user.user_id, req.amount)
|
||||
}
|
||||
|
||||
async SendAppUserToAppPayment(appId: string, req: Types.SendAppUserToAppPaymentRequest): Promise<void> {
|
||||
const fromUser = await this.storage.applicationStorage.GetApplicationUser(appId, req.from_user_identifier)
|
||||
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||
await this.paymentManager.SendUserToUserPayment(fromUser.user.user_id, app.owner.user_id, req.amount)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import jwt from 'jsonwebtoken'
|
||||
import { bech32 } from 'bech32'
|
||||
import Storage, { LoadStorageSettingsFromEnv, StorageSettings } from '../storage/index.js'
|
||||
import fetch from "node-fetch"
|
||||
import Storage, { LoadStorageSettingsFromEnv } from '../storage/index.js'
|
||||
import * as Types from '../../../proto/autogenerated/ts/types.js'
|
||||
import LND, { AddressPaidCb, InvoicePaidCb, LndSettings, LoadLndSettingsFromEnv } from '../lnd/index.js'
|
||||
import LND, { AddressPaidCb, InvoicePaidCb, LoadLndSettingsFromEnv } from '../lnd/index.js'
|
||||
import { EnvMustBeInteger, EnvMustBeNonEmptyString } from '../helpers/envParser.js'
|
||||
import { UserReceivingInvoice } from '../storage/entity/UserReceivingInvoice.js'
|
||||
import ProductManager from './product.js'
|
||||
import ProductManager from './productManager.js'
|
||||
import ApplicationManager from './applicationManager.js'
|
||||
import UserManager from './userManager.js'
|
||||
import PaymentManager from './paymentManager.js'
|
||||
import { MainSettings } from './settings.js'
|
||||
export const LoadMainSettingsFromEnv = (test = false): MainSettings => {
|
||||
return {
|
||||
|
|
@ -16,6 +17,7 @@ export const LoadMainSettingsFromEnv = (test = false): MainSettings => {
|
|||
outgoingTxFee: EnvMustBeInteger("SERVICE_FEE_OUTGOING_TX_PERCENT") / 100,
|
||||
incomingInvoiceFee: EnvMustBeInteger("SERVICE_FEE_INCOMING_INVOICE_PERCENT") / 100,
|
||||
outgoingInvoiceFee: EnvMustBeInteger("SERVICE_FEE_OUTGOING_INVOICE_PERCENT") / 100,
|
||||
userToUserFee: EnvMustBeInteger("SERVICE_FEE_USER_TO_USER_PERCENT") / 100,
|
||||
serviceUrl: EnvMustBeNonEmptyString("SERVICE_URL")
|
||||
}
|
||||
}
|
||||
|
|
@ -27,280 +29,69 @@ type UserOperationsSub = {
|
|||
newIncomingTx: (operation: Types.UserOperation) => void
|
||||
newOutgoingTx: (operation: Types.UserOperation) => void
|
||||
}
|
||||
interface UserOperationInfo {
|
||||
serial_id: number
|
||||
paid_amount: number
|
||||
paid_at_unix: number
|
||||
}
|
||||
const defaultLnurlPayMetadata = '[["text/plain", "lnurl pay to Lightning.pub"]]'
|
||||
|
||||
export default class {
|
||||
storage: Storage
|
||||
lnd: LND
|
||||
settings: MainSettings
|
||||
userOperationsSub: UserOperationsSub | null = null
|
||||
productManager: ProductManager
|
||||
applicationManager: ApplicationManager
|
||||
userManager: UserManager
|
||||
paymentManager: PaymentManager
|
||||
|
||||
constructor(settings: MainSettings) {
|
||||
this.settings = settings
|
||||
this.storage = new Storage(settings.storageSettings)
|
||||
this.lnd = new LND(settings.lndSettings, this.addressPaidCb, this.invoicePaidCb)
|
||||
this.productManager = new ProductManager(this.storage, this.lnd, this.settings)
|
||||
}
|
||||
getServiceFee(action: Types.UserOperationType, amount: number): number {
|
||||
switch (action) {
|
||||
case Types.UserOperationType.INCOMING_TX:
|
||||
return Math.ceil(this.settings.incomingTxFee * amount)
|
||||
case Types.UserOperationType.OUTGOING_TX:
|
||||
return Math.ceil(this.settings.outgoingTxFee * amount)
|
||||
case Types.UserOperationType.INCOMING_INVOICE:
|
||||
return Math.ceil(this.settings.incomingInvoiceFee * amount)
|
||||
case Types.UserOperationType.OUTGOING_INVOICE:
|
||||
return Math.ceil(this.settings.outgoingInvoiceFee * amount)
|
||||
default:
|
||||
throw new Error("Unknown service action type")
|
||||
}
|
||||
|
||||
this.userManager = new UserManager(this.storage, this.settings)
|
||||
this.paymentManager = new PaymentManager(this.storage, this.lnd, this.settings)
|
||||
this.productManager = new ProductManager(this.storage, this.paymentManager, this.settings)
|
||||
this.applicationManager = new ApplicationManager(this.storage, this.settings, this.userManager, this.paymentManager)
|
||||
}
|
||||
|
||||
addressPaidCb: AddressPaidCb = (txOutput, address, amount) => {
|
||||
this.storage.StartTransaction(async tx => {
|
||||
const userAddress = await this.storage.GetAddressOwner(address, tx)
|
||||
const userAddress = await this.storage.paymentStorage.GetAddressOwner(address, tx)
|
||||
if (!userAddress) { return }
|
||||
const fee = this.getServiceFee(Types.UserOperationType.INCOMING_TX, amount)
|
||||
const fee = this.paymentManager.getServiceFee(Types.UserOperationType.INCOMING_TX, amount)
|
||||
try {
|
||||
// This call will fail if the transaction is already registered
|
||||
const addedTx = await this.storage.AddAddressReceivingTransaction(userAddress, txOutput.hash, txOutput.index, amount, fee, tx)
|
||||
await this.storage.IncrementUserBalance(userAddress.user.user_id, addedTx.paid_amount, tx)
|
||||
const addedTx = await this.storage.paymentStorage.AddAddressReceivingTransaction(userAddress, txOutput.hash, txOutput.index, amount, fee, tx)
|
||||
await this.storage.userStorage.IncrementUserBalance(userAddress.user.user_id, addedTx.paid_amount - fee, tx)
|
||||
} catch {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
invoicePaidCb: InvoicePaidCb = (paymentRequest, amount) => {
|
||||
|
||||
invoicePaidCb: InvoicePaidCb = (paymentRequest, amount) => {
|
||||
this.storage.StartTransaction(async tx => {
|
||||
const userInvoice = await this.storage.GetInvoiceOwner(paymentRequest, tx)
|
||||
const userInvoice = await this.storage.paymentStorage.GetInvoiceOwner(paymentRequest, tx)
|
||||
if (!userInvoice || userInvoice.paid_at_unix > 0) { return }
|
||||
const fee = this.getServiceFee(Types.UserOperationType.INCOMING_INVOICE, amount)
|
||||
const fee = this.paymentManager.getServiceFee(Types.UserOperationType.INCOMING_INVOICE, amount)
|
||||
const maybeApp = await this.storage.applicationStorage.IsApplicationUser(userInvoice.user.user_id)
|
||||
try {
|
||||
// This call will fail if the invoice is already registered
|
||||
await this.storage.FlagInvoiceAsPaid(userInvoice, amount, fee, tx)
|
||||
await this.storage.IncrementUserBalance(userInvoice.user.user_id, amount, tx)
|
||||
} catch { }
|
||||
})
|
||||
}
|
||||
SignUserToken(userId: string): string {
|
||||
return jwt.sign({ userId }, this.settings.jwtSecret);
|
||||
}
|
||||
|
||||
DecodeUserToken(token?: string): string {
|
||||
if (!token) throw new Error("empty auth token provided")
|
||||
return (jwt.verify(token, this.settings.jwtSecret) as { userId: string }).userId
|
||||
}
|
||||
|
||||
async AddBasicUser(req: Types.AddUserRequest): Promise<Types.AddUserResponse> {
|
||||
const { user } = await this.storage.AddBasicUser(req.name, req.secret)
|
||||
return {
|
||||
userId: user.user_id,
|
||||
authToken: this.SignUserToken(user.user_id)
|
||||
}
|
||||
}
|
||||
|
||||
async NewAddress(userId: string, req: Types.NewAddressRequest): Promise<Types.NewAddressResponse> {
|
||||
const res = await this.lnd.NewAddress(req.addressType)
|
||||
const userAddress = await this.storage.AddUserAddress(userId, res.address)
|
||||
return {
|
||||
address: userAddress.address
|
||||
}
|
||||
}
|
||||
|
||||
async NewInvoice(userId: string, req: Types.NewInvoiceRequest): Promise<Types.NewInvoiceResponse> {
|
||||
const user = await this.storage.GetUser(userId)
|
||||
const res = await this.lnd.NewInvoice(req.amountSats, req.memo)
|
||||
const userInvoice = await this.storage.AddUserInvoice(user, res.paymentRequest)
|
||||
return {
|
||||
invoice: userInvoice.invoice
|
||||
}
|
||||
}
|
||||
|
||||
async lockUserWithMinBalance(userId: string, minBalance: number) {
|
||||
return this.storage.StartTransaction(async tx => {
|
||||
const user = await this.storage.GetUser(userId, tx)
|
||||
if (user.balance_sats < minBalance) {
|
||||
throw new Error("insufficient balance")
|
||||
await this.storage.paymentStorage.FlagInvoiceAsPaid(userInvoice, amount, fee, tx)
|
||||
await this.storage.userStorage.IncrementUserBalance(userInvoice.user.user_id, amount - fee, tx)
|
||||
await this.triggerPaidCallback(userInvoice.callbackUrl)
|
||||
} catch {
|
||||
//TODO
|
||||
}
|
||||
// this call will fail if the user is already locked
|
||||
await this.storage.LockUser(userId, tx)
|
||||
})
|
||||
}
|
||||
|
||||
GetMaxPayableInvoice(balance: number): number {
|
||||
const maxWithinServiceFee = Math.max(0, Math.floor(balance * (1 - this.settings.outgoingInvoiceFee)))
|
||||
return this.lnd.GetMaxWithinLimit(maxWithinServiceFee)
|
||||
}
|
||||
async DecodeInvoice(req: Types.DecodeInvoiceRequest): Promise<Types.DecodeInvoiceResponse> {
|
||||
const decoded = await this.lnd.DecodeInvoice(req.invoice)
|
||||
return {
|
||||
amount: Number(decoded.numSatoshis)
|
||||
async triggerPaidCallback(url: string) {
|
||||
console.log(url)
|
||||
if (!url) {
|
||||
return
|
||||
}
|
||||
}
|
||||
async PayInvoice(userId: string, req: Types.PayInvoiceRequest): Promise<Types.PayInvoiceResponse> {
|
||||
const decoded = await this.lnd.DecodeInvoice(req.invoice)
|
||||
if (decoded.numSatoshis !== 0n && req.amount !== 0) {
|
||||
throw new Error("invoice has value, do not provide amount the the request")
|
||||
try {
|
||||
await fetch(url + "&ok=true")
|
||||
} catch (err: any) {
|
||||
console.log("error sending cb", err)
|
||||
}
|
||||
if (decoded.numSatoshis === 0n && req.amount === 0) {
|
||||
throw new Error("invoice has no value, an amount must be provided in the request")
|
||||
}
|
||||
const payAmount = req.amount !== 0 ? req.amount : Number(decoded.numSatoshis)
|
||||
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, payAmount)
|
||||
const totalAmountToDecrement = payAmount + serviceFee
|
||||
|
||||
|
||||
const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
||||
await this.lockUserWithMinBalance(userId, totalAmountToDecrement + routingFeeLimit)
|
||||
const payment = await this.lnd.PayInvoice(req.invoice, req.amount, routingFeeLimit)
|
||||
await this.storage.DecrementUserBalance(userId, totalAmountToDecrement + Number(payment.feeSat))
|
||||
await this.storage.UnlockUser(userId)
|
||||
await this.storage.AddUserInvoicePayment(userId, req.invoice, payAmount, Number(payment.feeSat), serviceFee)
|
||||
return {
|
||||
preimage: payment.paymentPreimage
|
||||
}
|
||||
}
|
||||
|
||||
async PayAddress(userId: string, req: Types.PayAddressRequest): Promise<Types.PayAddressResponse> {
|
||||
const estimate = await this.lnd.EstimateChainFees(req.address, req.amoutSats, req.targetConf)
|
||||
const satPerVByte = Number(estimate.satPerVbyte)
|
||||
const chainFees = Number(estimate.feeSat)
|
||||
const total = req.amoutSats + chainFees
|
||||
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, req.amoutSats)
|
||||
await this.lockUserWithMinBalance(userId, total + serviceFee)
|
||||
const payment = await this.lnd.PayAddress(req.address, req.amoutSats, satPerVByte)
|
||||
await this.storage.DecrementUserBalance(userId, total + serviceFee)
|
||||
await this.storage.UnlockUser(userId)
|
||||
await this.storage.AddUserTransactionPayment(userId, req.address, payment.txid, 0, req.amoutSats, chainFees, serviceFee)
|
||||
return {
|
||||
txId: payment.txid
|
||||
}
|
||||
}
|
||||
|
||||
balanceCheckUrl(k1: string): string {
|
||||
return `${this.settings.serviceUrl}/api/guest/lnurl_withdraw/info?k1=${k1}`
|
||||
}
|
||||
|
||||
async GetLnurlChannelLink(userId: string): Promise<Types.LnurlLinkResponse> {
|
||||
const key = await this.storage.AddUserEphemeralKey(userId, 'balanceCheck')
|
||||
return {
|
||||
lnurl: this.encodeLnurl(this.balanceCheckUrl(key.key)),
|
||||
k1: key.key
|
||||
}
|
||||
}
|
||||
|
||||
async GetLnurlWithdrawInfo(balanceCheckK1: string): Promise<Types.LnurlWithdrawInfoResponse> {
|
||||
const key = await this.storage.UseUserEphemeralKey(balanceCheckK1, 'balanceCheck')
|
||||
const maxWithdrawable = this.GetMaxPayableInvoice(key.user.balance_sats)
|
||||
const callbackK1 = await this.storage.AddUserEphemeralKey(key.user.user_id, 'withdraw')
|
||||
const newBalanceCheckK1 = await this.storage.AddUserEphemeralKey(key.user.user_id, 'balanceCheck')
|
||||
const payInfoK1 = await this.storage.AddUserEphemeralKey(key.user.user_id, 'pay')
|
||||
return {
|
||||
tag: "withdrawRequest",
|
||||
callback: `${this.settings.serviceUrl}/api/guest/lnurl_withdraw/handle`,
|
||||
defaultDescription: "lnurl withdraw from lightning.pub",
|
||||
k1: callbackK1.key,
|
||||
maxWithdrawable: maxWithdrawable,
|
||||
minWithdrawable: 0,
|
||||
balanceCheck: this.balanceCheckUrl(newBalanceCheckK1.key),
|
||||
payLink: `${this.settings.serviceUrl}/api/guest/lnurl_pay/info?k1=${payInfoK1.key}`,
|
||||
}
|
||||
}
|
||||
|
||||
async HandleLnurlWithdraw(k1: string, invoice: string): Promise<void> {
|
||||
const key = await this.storage.UseUserEphemeralKey(k1, 'withdraw')
|
||||
this.PayInvoice(key.user.user_id, {
|
||||
invoice: invoice,
|
||||
amount: 0
|
||||
}).catch(err => {
|
||||
console.error("error sending payment for lnurl withdraw to ", key.user.user_id, err)
|
||||
})
|
||||
}
|
||||
|
||||
async GetLnurlPayInfo(payInfoK1: string): Promise<Types.LnurlPayInfoResponse> {
|
||||
const key = await this.storage.UseUserEphemeralKey(payInfoK1, 'payInfo')
|
||||
const payK1 = await this.storage.AddUserEphemeralKey(key.user.user_id, 'pay')
|
||||
return {
|
||||
tag: 'payRequest',
|
||||
callback: `${this.settings.serviceUrl}/api/guest/lnurl_pay/handle?k1=${payK1.key}`,
|
||||
maxSendable: 10000000,
|
||||
minSendable: 0,
|
||||
metadata: defaultLnurlPayMetadata
|
||||
}
|
||||
}
|
||||
|
||||
async HandleLnurlPay(payK1: string, amountMillis: number): Promise<Types.HandleLnurlPayResponse> {
|
||||
const key = await this.storage.UseUserEphemeralKey(payK1, 'pay')
|
||||
const sats = amountMillis / 1000
|
||||
if (!Number.isInteger(sats)) {
|
||||
throw new Error("millisats amount must be integer sats amount")
|
||||
}
|
||||
const invoice = await this.NewInvoice(key.user.user_id, {
|
||||
amountSats: sats,
|
||||
memo: defaultLnurlPayMetadata
|
||||
})
|
||||
return {
|
||||
pr: invoice.invoice,
|
||||
routes: []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async OpenChannel(userId: string, req: Types.OpenChannelRequest): Promise<Types.OpenChannelResponse> { throw new Error("WIP") }
|
||||
|
||||
|
||||
mapOperations(operations: UserOperationInfo[], type: Types.UserOperationType, inbound: boolean): Types.UserOperations {
|
||||
if (operations.length === 0) {
|
||||
return {
|
||||
fromIndex: 0,
|
||||
toIndex: 0,
|
||||
operations: []
|
||||
}
|
||||
}
|
||||
return {
|
||||
toIndex: operations[0].serial_id,
|
||||
fromIndex: operations[operations.length - 1].serial_id,
|
||||
operations: operations.map((o: UserOperationInfo): Types.UserOperation => ({
|
||||
inbound,
|
||||
type,
|
||||
amount: o.paid_amount,
|
||||
paidAtUnix: o.paid_at_unix
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
async GetUserInfo(userId: string): Promise<Types.UserInfo> {
|
||||
const user = await this.storage.GetUser(userId)
|
||||
return {
|
||||
userId: userId,
|
||||
balance: user.balance_sats
|
||||
}
|
||||
}
|
||||
|
||||
async GetUserOperations(userId: string, req: Types.GetUserOperationsRequest): Promise<Types.GetUserOperationsResponse> {
|
||||
const [outgoingInvoices, outgoingTransactions, incomingInvoices, incomingTransactions] = await Promise.all([
|
||||
this.storage.GetUserInvoicePayments(userId, req.latestOutgoingInvoice),
|
||||
this.storage.GetUserTransactionPayments(userId, req.latestOutgoingTx),
|
||||
this.storage.GetUserInvoicesFlaggedAsPaid(userId, req.latestIncomingInvoice),
|
||||
this.storage.GetUserReceivingTransactions(userId, req.latestIncomingTx)
|
||||
])
|
||||
return {
|
||||
latestIncomingInvoiceOperations: this.mapOperations(incomingInvoices, Types.UserOperationType.INCOMING_INVOICE, true),
|
||||
latestIncomingTxOperations: this.mapOperations(incomingTransactions, Types.UserOperationType.INCOMING_TX, true),
|
||||
latestOutgoingInvoiceOperations: this.mapOperations(outgoingInvoices, Types.UserOperationType.OUTGOING_INVOICE, false),
|
||||
latestOutgoingTxOperations: this.mapOperations(outgoingTransactions, Types.UserOperationType.OUTGOING_TX, false)
|
||||
}
|
||||
}
|
||||
|
||||
encodeLnurl(base: string) {
|
||||
if (!base || typeof base !== 'string') {
|
||||
throw new Error("provided string for lnurl encode is not a string or is an empty string")
|
||||
}
|
||||
let words = bech32.toWords(Buffer.from(base, 'utf8'));
|
||||
return bech32.encode('lnurl', words, 1023);
|
||||
}
|
||||
}
|
||||
252
src/services/main/paymentManager.ts
Normal file
252
src/services/main/paymentManager.ts
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
import { bech32 } from 'bech32'
|
||||
import Storage from '../storage/index.js'
|
||||
import * as Types from '../../../proto/autogenerated/ts/types.js'
|
||||
import LND from '../lnd/index.js'
|
||||
import { MainSettings } from './settings.js'
|
||||
import { InboundOptionals, defaultInvoiceExpiry } from '../storage/paymentStorage.js'
|
||||
interface UserOperationInfo {
|
||||
serial_id: number
|
||||
paid_amount: number
|
||||
paid_at_unix: number
|
||||
}
|
||||
const defaultLnurlPayMetadata = '[["text/plain", "lnurl pay to Lightning.pub"]]'
|
||||
|
||||
export default class {
|
||||
storage: Storage
|
||||
settings: MainSettings
|
||||
lnd: LND
|
||||
constructor(storage: Storage, lnd: LND, settings: MainSettings) {
|
||||
this.storage = storage
|
||||
this.settings = settings
|
||||
this.lnd = lnd
|
||||
}
|
||||
getServiceFee(action: Types.UserOperationType, amount: number): number {
|
||||
switch (action) {
|
||||
case Types.UserOperationType.INCOMING_TX:
|
||||
return Math.ceil(this.settings.incomingTxFee * amount)
|
||||
case Types.UserOperationType.OUTGOING_TX:
|
||||
return Math.ceil(this.settings.outgoingTxFee * amount)
|
||||
case Types.UserOperationType.INCOMING_INVOICE:
|
||||
return Math.ceil(this.settings.incomingInvoiceFee * amount)
|
||||
case Types.UserOperationType.OUTGOING_INVOICE:
|
||||
return Math.ceil(this.settings.outgoingInvoiceFee * amount)
|
||||
case Types.UserOperationType.OUTGOING_USER_TO_USER || Types.UserOperationType.INCOMING_USER_TO_USER:
|
||||
return Math.ceil(this.settings.userToUserFee * amount)
|
||||
default:
|
||||
throw new Error("Unknown service action type")
|
||||
}
|
||||
}
|
||||
|
||||
async NewAddress(userId: string, req: Types.NewAddressRequest): Promise<Types.NewAddressResponse> {
|
||||
const res = await this.lnd.NewAddress(req.addressType)
|
||||
const userAddress = await this.storage.paymentStorage.AddUserAddress(userId, res.address)
|
||||
return {
|
||||
address: userAddress.address
|
||||
}
|
||||
}
|
||||
|
||||
async NewInvoice(userId: string, req: Types.NewInvoiceRequest, options: InboundOptionals = { expiry: defaultInvoiceExpiry }): Promise<Types.NewInvoiceResponse> {
|
||||
const user = await this.storage.userStorage.GetUser(userId)
|
||||
const res = await this.lnd.NewInvoice(req.amountSats, req.memo, options.expiry)
|
||||
const userInvoice = await this.storage.paymentStorage.AddUserInvoice(user, res.paymentRequest, options)
|
||||
return {
|
||||
invoice: userInvoice.invoice
|
||||
}
|
||||
}
|
||||
|
||||
async lockUserWithMinBalance(userId: string, minBalance: number) {
|
||||
return this.storage.StartTransaction(async tx => {
|
||||
const user = await this.storage.userStorage.GetUser(userId, tx)
|
||||
if (user.balance_sats < minBalance) {
|
||||
throw new Error("insufficient balance")
|
||||
}
|
||||
// this call will fail if the user is already locked
|
||||
await this.storage.userStorage.LockUser(userId, tx)
|
||||
})
|
||||
}
|
||||
|
||||
GetMaxPayableInvoice(balance: number): number {
|
||||
const maxWithinServiceFee = Math.max(0, Math.floor(balance * (1 - this.settings.outgoingInvoiceFee)))
|
||||
return this.lnd.GetMaxWithinLimit(maxWithinServiceFee)
|
||||
}
|
||||
async DecodeInvoice(req: Types.DecodeInvoiceRequest): Promise<Types.DecodeInvoiceResponse> {
|
||||
const decoded = await this.lnd.DecodeInvoice(req.invoice)
|
||||
return {
|
||||
amount: Number(decoded.numSatoshis)
|
||||
}
|
||||
}
|
||||
async PayInvoice(userId: string, req: Types.PayInvoiceRequest): Promise<Types.PayInvoiceResponse> {
|
||||
const decoded = await this.lnd.DecodeInvoice(req.invoice)
|
||||
if (decoded.numSatoshis !== 0n && req.amount !== 0) {
|
||||
throw new Error("invoice has value, do not provide amount the the request")
|
||||
}
|
||||
if (decoded.numSatoshis === 0n && req.amount === 0) {
|
||||
throw new Error("invoice has no value, an amount must be provided in the request")
|
||||
}
|
||||
const payAmount = req.amount !== 0 ? req.amount : Number(decoded.numSatoshis)
|
||||
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, payAmount)
|
||||
const totalAmountToDecrement = payAmount + serviceFee
|
||||
|
||||
|
||||
const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
||||
await this.lockUserWithMinBalance(userId, totalAmountToDecrement + routingFeeLimit)
|
||||
const payment = await this.lnd.PayInvoice(req.invoice, req.amount, routingFeeLimit)
|
||||
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + Number(payment.feeSat))
|
||||
await this.storage.userStorage.UnlockUser(userId)
|
||||
await this.storage.paymentStorage.AddUserInvoicePayment(userId, req.invoice, payAmount, Number(payment.feeSat), serviceFee)
|
||||
return {
|
||||
preimage: payment.paymentPreimage,
|
||||
amount_paid: Number(payment.valueSat)
|
||||
}
|
||||
}
|
||||
|
||||
async PayAddress(userId: string, req: Types.PayAddressRequest): Promise<Types.PayAddressResponse> {
|
||||
const estimate = await this.lnd.EstimateChainFees(req.address, req.amoutSats, req.targetConf)
|
||||
const satPerVByte = Number(estimate.satPerVbyte)
|
||||
const chainFees = Number(estimate.feeSat)
|
||||
const total = req.amoutSats + chainFees
|
||||
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, req.amoutSats)
|
||||
await this.lockUserWithMinBalance(userId, total + serviceFee)
|
||||
const payment = await this.lnd.PayAddress(req.address, req.amoutSats, satPerVByte)
|
||||
await this.storage.userStorage.DecrementUserBalance(userId, total + serviceFee)
|
||||
await this.storage.userStorage.UnlockUser(userId)
|
||||
await this.storage.paymentStorage.AddUserTransactionPayment(userId, req.address, payment.txid, 0, req.amoutSats, chainFees, serviceFee)
|
||||
return {
|
||||
txId: payment.txid
|
||||
}
|
||||
}
|
||||
|
||||
balanceCheckUrl(k1: string): string {
|
||||
return `${this.settings.serviceUrl}/api/guest/lnurl_withdraw/info?k1=${k1}`
|
||||
}
|
||||
|
||||
async GetLnurlChannelLink(userId: string): Promise<Types.LnurlLinkResponse> {
|
||||
const key = await this.storage.paymentStorage.AddUserEphemeralKey(userId, 'balanceCheck')
|
||||
return {
|
||||
lnurl: this.encodeLnurl(this.balanceCheckUrl(key.key)),
|
||||
k1: key.key
|
||||
}
|
||||
}
|
||||
|
||||
async GetLnurlWithdrawInfo(balanceCheckK1: string): Promise<Types.LnurlWithdrawInfoResponse> {
|
||||
const key = await this.storage.paymentStorage.UseUserEphemeralKey(balanceCheckK1, 'balanceCheck')
|
||||
const maxWithdrawable = this.GetMaxPayableInvoice(key.user.balance_sats)
|
||||
const callbackK1 = await this.storage.paymentStorage.AddUserEphemeralKey(key.user.user_id, 'withdraw')
|
||||
const newBalanceCheckK1 = await this.storage.paymentStorage.AddUserEphemeralKey(key.user.user_id, 'balanceCheck')
|
||||
const payInfoK1 = await this.storage.paymentStorage.AddUserEphemeralKey(key.user.user_id, 'pay')
|
||||
return {
|
||||
tag: "withdrawRequest",
|
||||
callback: `${this.settings.serviceUrl}/api/guest/lnurl_withdraw/handle`,
|
||||
defaultDescription: "lnurl withdraw from lightning.pub",
|
||||
k1: callbackK1.key,
|
||||
maxWithdrawable: maxWithdrawable,
|
||||
minWithdrawable: 0,
|
||||
balanceCheck: this.balanceCheckUrl(newBalanceCheckK1.key),
|
||||
payLink: `${this.settings.serviceUrl}/api/guest/lnurl_pay/info?k1=${payInfoK1.key}`,
|
||||
}
|
||||
}
|
||||
|
||||
async HandleLnurlWithdraw(k1: string, invoice: string): Promise<void> {
|
||||
const key = await this.storage.paymentStorage.UseUserEphemeralKey(k1, 'withdraw')
|
||||
this.PayInvoice(key.user.user_id, {
|
||||
invoice: invoice,
|
||||
amount: 0
|
||||
}).catch(err => {
|
||||
console.error("error sending payment for lnurl withdraw to ", key.user.user_id, err)
|
||||
})
|
||||
}
|
||||
|
||||
async GetLnurlPayInfo(payInfoK1: string): Promise<Types.LnurlPayInfoResponse> {
|
||||
const key = await this.storage.paymentStorage.UseUserEphemeralKey(payInfoK1, 'payInfo')
|
||||
const payK1 = await this.storage.paymentStorage.AddUserEphemeralKey(key.user.user_id, 'pay')
|
||||
return {
|
||||
tag: 'payRequest',
|
||||
callback: `${this.settings.serviceUrl}/api/guest/lnurl_pay/handle?k1=${payK1.key}`,
|
||||
maxSendable: 10000000,
|
||||
minSendable: 0,
|
||||
metadata: defaultLnurlPayMetadata
|
||||
}
|
||||
}
|
||||
|
||||
async HandleLnurlPay(payK1: string, amountMillis: number): Promise<Types.HandleLnurlPayResponse> {
|
||||
const key = await this.storage.paymentStorage.UseUserEphemeralKey(payK1, 'pay')
|
||||
const sats = amountMillis / 1000
|
||||
if (!Number.isInteger(sats)) {
|
||||
throw new Error("millisats amount must be integer sats amount")
|
||||
}
|
||||
const invoice = await this.NewInvoice(key.user.user_id, {
|
||||
amountSats: sats,
|
||||
memo: defaultLnurlPayMetadata
|
||||
})
|
||||
return {
|
||||
pr: invoice.invoice,
|
||||
routes: []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async OpenChannel(userId: string, req: Types.OpenChannelRequest): Promise<Types.OpenChannelResponse> { throw new Error("WIP") }
|
||||
|
||||
|
||||
mapOperations(operations: UserOperationInfo[], type: Types.UserOperationType, inbound: boolean): Types.UserOperations {
|
||||
if (operations.length === 0) {
|
||||
return {
|
||||
fromIndex: 0,
|
||||
toIndex: 0,
|
||||
operations: []
|
||||
}
|
||||
}
|
||||
return {
|
||||
toIndex: operations[0].serial_id,
|
||||
fromIndex: operations[operations.length - 1].serial_id,
|
||||
operations: operations.map((o: UserOperationInfo): Types.UserOperation => ({
|
||||
inbound,
|
||||
type,
|
||||
amount: o.paid_amount,
|
||||
paidAtUnix: o.paid_at_unix
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
async GetUserOperations(userId: string, req: Types.GetUserOperationsRequest): Promise<Types.GetUserOperationsResponse> {
|
||||
const [outgoingInvoices, outgoingTransactions, incomingInvoices, incomingTransactions, incomingUserToUser, outgoingUserToUser] = await Promise.all([
|
||||
this.storage.paymentStorage.GetUserInvoicePayments(userId, req.latestOutgoingInvoice),
|
||||
this.storage.paymentStorage.GetUserTransactionPayments(userId, req.latestOutgoingTx),
|
||||
this.storage.paymentStorage.GetUserInvoicesFlaggedAsPaid(userId, req.latestIncomingInvoice),
|
||||
this.storage.paymentStorage.GetUserReceivingTransactions(userId, req.latestIncomingTx),
|
||||
this.storage.paymentStorage.GetUserToUserReceivedPayments(userId, req.latestIncomingUserToUserPayment),
|
||||
this.storage.paymentStorage.GetUserToUserSentPayments(userId, req.latestOutgoingUserToUserPayment)
|
||||
])
|
||||
return {
|
||||
latestIncomingInvoiceOperations: this.mapOperations(incomingInvoices, Types.UserOperationType.INCOMING_INVOICE, true),
|
||||
latestIncomingTxOperations: this.mapOperations(incomingTransactions, Types.UserOperationType.INCOMING_TX, true),
|
||||
latestOutgoingInvoiceOperations: this.mapOperations(outgoingInvoices, Types.UserOperationType.OUTGOING_INVOICE, false),
|
||||
latestOutgoingTxOperations: this.mapOperations(outgoingTransactions, Types.UserOperationType.OUTGOING_TX, false),
|
||||
latestIncomingUserToUserPayemnts: this.mapOperations(incomingUserToUser, Types.UserOperationType.INCOMING_USER_TO_USER, true),
|
||||
latestOutgoingUserToUserPayemnts: this.mapOperations(outgoingUserToUser, Types.UserOperationType.OUTGOING_USER_TO_USER, false)
|
||||
}
|
||||
}
|
||||
|
||||
async SendUserToUserPayment(fromUserId: string, toUserId: string, amount: number) {
|
||||
await this.storage.StartTransaction(async tx => {
|
||||
const fromUser = await this.storage.userStorage.GetUser(fromUserId, tx)
|
||||
const toUser = await this.storage.userStorage.GetUser(toUserId, tx)
|
||||
if (fromUser.balance_sats < amount) {
|
||||
throw new Error("not enough balance to send user to user payment")
|
||||
}
|
||||
const fee = this.getServiceFee(Types.UserOperationType.OUTGOING_USER_TO_USER, amount)
|
||||
const toIncrement = amount - fee
|
||||
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, amount, tx)
|
||||
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, toIncrement, tx)
|
||||
await this.storage.paymentStorage.AddUserToUserPayment(fromUserId, toUserId, amount, fee)
|
||||
})
|
||||
}
|
||||
|
||||
encodeLnurl(base: string) {
|
||||
if (!base || typeof base !== 'string') {
|
||||
throw new Error("provided string for lnurl encode is not a string or is an empty string")
|
||||
}
|
||||
let words = bech32.toWords(Buffer.from(base, 'utf8'));
|
||||
return bech32.encode('lnurl', words, 1023);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,20 +3,22 @@ import * as Types from '../../../proto/autogenerated/ts/types.js'
|
|||
import LND from '../lnd/index.js'
|
||||
|
||||
import { MainSettings } from './settings.js'
|
||||
import PaymentManager from './paymentManager.js'
|
||||
import { defaultInvoiceExpiry } from '../storage/paymentStorage.js'
|
||||
|
||||
export default class {
|
||||
|
||||
storage: Storage
|
||||
settings: MainSettings
|
||||
lnd: LND
|
||||
constructor(storage: Storage, lnd: LND, settings: MainSettings) {
|
||||
paymentManager: PaymentManager
|
||||
|
||||
constructor(storage: Storage, paymentManager: PaymentManager, settings: MainSettings) {
|
||||
this.storage = storage
|
||||
this.settings = settings
|
||||
this.lnd = lnd
|
||||
this.paymentManager = paymentManager
|
||||
}
|
||||
|
||||
async AddProduct(userId: string, req: Types.AddProductRequest): Promise<Types.Product> {
|
||||
const user = await this.storage.GetUser(userId)
|
||||
const user = await this.storage.userStorage.GetUser(userId)
|
||||
const newProduct = await this.storage.productStorage.AddProduct(req.name, req.price_sats, user)
|
||||
return {
|
||||
id: newProduct.product_id,
|
||||
|
|
@ -27,10 +29,11 @@ export default class {
|
|||
|
||||
async NewProductInvoice(id: string): Promise<Types.NewInvoiceResponse> {
|
||||
const product = await this.storage.productStorage.GetProduct(id)
|
||||
const newInvoice = await this.lnd.NewInvoice(product.price_sats, product.name)
|
||||
await this.storage.AddUserInvoice(product.owner, newInvoice.paymentRequest, product)
|
||||
const productInvoice = await this.paymentManager.NewInvoice(product.owner.user_id, {
|
||||
amountSats: product.price_sats, memo: product.name
|
||||
}, { product, expiry: defaultInvoiceExpiry })
|
||||
return {
|
||||
invoice: newInvoice.paymentRequest
|
||||
invoice: productInvoice.invoice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,5 +8,7 @@ export type MainSettings = {
|
|||
outgoingTxFee: number
|
||||
incomingInvoiceFee: number
|
||||
outgoingInvoiceFee: number
|
||||
userToUserFee: number
|
||||
serviceUrl: string
|
||||
|
||||
}
|
||||
41
src/services/main/userManager.ts
Normal file
41
src/services/main/userManager.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import jwt from 'jsonwebtoken'
|
||||
import Storage from '../storage/index.js'
|
||||
import * as Types from '../../../proto/autogenerated/ts/types.js'
|
||||
|
||||
import { MainSettings } from './settings.js'
|
||||
export default class {
|
||||
storage: Storage
|
||||
settings: MainSettings
|
||||
constructor(storage: Storage, settings: MainSettings) {
|
||||
this.storage = storage
|
||||
this.settings = settings
|
||||
}
|
||||
SignUserToken(userId: string): string {
|
||||
return jwt.sign({ userId }, this.settings.jwtSecret);
|
||||
}
|
||||
|
||||
DecodeUserToken(token?: string): string {
|
||||
if (!token) throw new Error("empty user token provided")
|
||||
let t = token
|
||||
if (token.startsWith("Bearer ")) {
|
||||
t = token.substring("Bearer ".length)
|
||||
}
|
||||
if (!t) throw new Error("no user token provided")
|
||||
return (jwt.verify(token, this.settings.jwtSecret) as { userId: string }).userId
|
||||
}
|
||||
|
||||
async AddBasicUser(req: Types.AddUserRequest): Promise<Types.AddUserResponse> {
|
||||
const { user } = await this.storage.userStorage.AddBasicUser(req.name, req.secret)
|
||||
return {
|
||||
userId: user.user_id,
|
||||
authToken: this.SignUserToken(user.user_id)
|
||||
}
|
||||
}
|
||||
async GetUserInfo(userId: string): Promise<Types.UserInfo> {
|
||||
const user = await this.storage.userStorage.GetUser(userId)
|
||||
return {
|
||||
userId: userId,
|
||||
balance: user.balance_sats
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
import { relayPool, Subscription, Event } from 'nostr-tools'
|
||||
//@ts-ignore
|
||||
import { decrypt, encrypt } from 'nostr-tools/nip04.js'
|
||||
import { SimplePool, Sub, Event, UnsignedEvent, nip04, getEventHash, signEvent } from 'nostr-tools'
|
||||
const handledEvents: string[] = [] // TODO: - big memory leak here, add TTL
|
||||
export type NostrSettings = {
|
||||
privateKey: string
|
||||
|
|
@ -74,52 +72,46 @@ const sendToNostr = (pub: string, message: string) => {
|
|||
send({ type: 'ready' })
|
||||
|
||||
export default class Handler {
|
||||
pool = relayPool()
|
||||
pool = new SimplePool()
|
||||
settings: NostrSettings
|
||||
sub: Subscription
|
||||
sub: Sub
|
||||
constructor(settings: NostrSettings, eventCallback: (event: NostrEvent) => void) {
|
||||
this.settings = settings
|
||||
this.pool.setPrivateKey(settings.privateKey)
|
||||
settings.relays.forEach(relay => {
|
||||
try {
|
||||
this.pool.addRelay(relay, { read: true, write: true })
|
||||
} catch (e) {
|
||||
console.error("cannot add relay:", relay)
|
||||
}
|
||||
});
|
||||
this.sub = this.pool.sub({
|
||||
//@ts-ignore
|
||||
filter: {
|
||||
console.log(settings)
|
||||
this.sub = this.pool.sub(settings.relays, [
|
||||
{
|
||||
since: Math.ceil(Date.now() / 1000),
|
||||
kinds: [4],
|
||||
'#p': [settings.publicKey],
|
||||
},
|
||||
cb: async (e, relay) => {
|
||||
if (e.kind !== 4 || !e.pubkey) {
|
||||
return
|
||||
}
|
||||
//@ts-ignore
|
||||
const eventId = e.id as string
|
||||
if (handledEvents.includes(eventId)) {
|
||||
console.log("event already handled")
|
||||
return
|
||||
}
|
||||
handledEvents.push(eventId)
|
||||
eventCallback({ id: eventId, content: decrypt(this.settings.privateKey, e.pubkey, e.content), pub: e.pubkey })
|
||||
}
|
||||
])
|
||||
this.sub.on("event", async (e) => {
|
||||
if (e.kind !== 4 || !e.pubkey) {
|
||||
return
|
||||
}
|
||||
//@ts-ignore
|
||||
const eventId = e.id as string
|
||||
if (handledEvents.includes(eventId)) {
|
||||
console.log("event already handled")
|
||||
return
|
||||
}
|
||||
handledEvents.push(eventId)
|
||||
eventCallback({ id: eventId, content: await nip04.decrypt(this.settings.privateKey, e.pubkey, e.content), pub: e.pubkey })
|
||||
})
|
||||
}
|
||||
|
||||
Send(nostrPub: string, message: string) {
|
||||
this.pool.publish({
|
||||
content: encrypt(this.settings.privateKey, nostrPub, message),
|
||||
async Send(nostrPub: string, message: string) {
|
||||
const event: UnsignedEvent = {
|
||||
content: await nip04.encrypt(this.settings.privateKey, nostrPub, message),
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 4,
|
||||
pubkey: this.settings.publicKey,
|
||||
//@ts-ignore
|
||||
tags: [['p', nostrPub]]
|
||||
}, (status, url) => {
|
||||
console.log("sent message to", nostrPub) // TODO
|
||||
})
|
||||
tags: [['p', nostrPub]],
|
||||
}
|
||||
const eventId = getEventHash(event)
|
||||
const sign = signEvent(event, this.settings.privateKey)
|
||||
const op = this.pool.publish(this.settings.relays,
|
||||
{ ...event, id: eventId, sig: sign })
|
||||
op.on('failed', (reason: string) => { console.log('failed to send message cuz: ', reason) })
|
||||
}
|
||||
}
|
||||
|
|
@ -8,10 +8,10 @@ export const ignore = true
|
|||
const settings = LoadNosrtSettingsFromEnv(true)
|
||||
|
||||
const clientPrivateKey = generatePrivateKey()
|
||||
const clientPublicKey = getPublicKey(Buffer.from(clientPrivateKey, "hex"))
|
||||
const clientPublicKey = getPublicKey(clientPrivateKey)
|
||||
|
||||
const serverPrivateKey = generatePrivateKey()
|
||||
const serverPublicKey = getPublicKey(Buffer.from(serverPrivateKey, "hex"))
|
||||
const serverPublicKey = getPublicKey(serverPrivateKey)
|
||||
let clientNostr: NostrHandler
|
||||
let serverNostr: NostrHandler
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Main from '../main/index.js'
|
|||
export default (mainHandler: Main): Types.ServerMethods => {
|
||||
return {
|
||||
EncryptionExchange: async (ctx, req) => { },
|
||||
Health: async (ctx) => { },
|
||||
Health: async (ctx) => { await mainHandler.lnd.Health() },
|
||||
LndGetInfo: async (ctx) => {
|
||||
const info = await mainHandler.lnd.GetInfo()
|
||||
return { alias: info.alias }
|
||||
|
|
@ -15,16 +15,16 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
secret_CustomCheck: secret => secret.length >= 8
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.AddBasicUser(req)
|
||||
return mainHandler.userManager.AddBasicUser(req)
|
||||
},
|
||||
AuthUser: async (ctx, req) => {
|
||||
throw new Error("unimplemented")
|
||||
},
|
||||
GetUserInfo: async (ctx) => {
|
||||
return mainHandler.GetUserInfo(ctx.user_id)
|
||||
return mainHandler.userManager.GetUserInfo(ctx.user_id)
|
||||
},
|
||||
GetUserOperations: async (ctx, req) => {
|
||||
return mainHandler.GetUserOperations(ctx.user_id, req)
|
||||
return mainHandler.paymentManager.GetUserOperations(ctx.user_id, req)
|
||||
},
|
||||
OpenChannel: async (ctx, req) => {
|
||||
const err = Types.OpenChannelRequestValidate(req, {
|
||||
|
|
@ -33,10 +33,10 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
destination_CustomCheck: dest => dest !== ""
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.OpenChannel(ctx.user_id, req)
|
||||
return mainHandler.paymentManager.OpenChannel(ctx.user_id, req)
|
||||
},
|
||||
NewAddress: async (ctx, req) => {
|
||||
return mainHandler.NewAddress(ctx.user_id, req)
|
||||
return mainHandler.paymentManager.NewAddress(ctx.user_id, req)
|
||||
},
|
||||
PayAddress: async (ctx, req) => {
|
||||
const err = Types.PayAddressRequestValidate(req, {
|
||||
|
|
@ -45,41 +45,41 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
targetConf_CustomCheck: target => target > 0
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.PayAddress(ctx.user_id, req)
|
||||
return mainHandler.paymentManager.PayAddress(ctx.user_id, req)
|
||||
},
|
||||
NewInvoice: async (ctx, req) => {
|
||||
return mainHandler.NewInvoice(ctx.user_id, req)
|
||||
return mainHandler.paymentManager.NewInvoice(ctx.user_id, req)
|
||||
},
|
||||
DecodeInvoice: async (ctx, req) => {
|
||||
return mainHandler.DecodeInvoice(req)
|
||||
return mainHandler.paymentManager.DecodeInvoice(req)
|
||||
},
|
||||
PayInvoice: async (ctx, req) => {
|
||||
const err = Types.PayInvoiceRequestValidate(req, {
|
||||
invoice_CustomCheck: invoice => invoice !== ''
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.PayInvoice(ctx.user_id, req)
|
||||
return mainHandler.paymentManager.PayInvoice(ctx.user_id, req)
|
||||
},
|
||||
GetLnurlWithdrawLink: async (ctx) => {
|
||||
return mainHandler.GetLnurlChannelLink(ctx.user_id)
|
||||
return mainHandler.paymentManager.GetLnurlChannelLink(ctx.user_id)
|
||||
},
|
||||
GetLnurlWithdrawInfo: async (ctx) => {
|
||||
if (!ctx.k1) {
|
||||
throw new Error("invalid lnurl withdraw to get info")
|
||||
}
|
||||
return mainHandler.GetLnurlWithdrawInfo(ctx.k1)
|
||||
return mainHandler.paymentManager.GetLnurlWithdrawInfo(ctx.k1)
|
||||
},
|
||||
HandleLnurlWithdraw: async (ctx) => {
|
||||
if (!ctx.k1 || !ctx.pr) {
|
||||
throw new Error("invalid lnurl withdraw to handle")
|
||||
}
|
||||
return mainHandler.HandleLnurlWithdraw(ctx.k1, ctx.pr)
|
||||
return mainHandler.paymentManager.HandleLnurlWithdraw(ctx.k1, ctx.pr)
|
||||
},
|
||||
GetLnurlPayInfo: async (ctx) => {
|
||||
if (!ctx.k1) {
|
||||
throw new Error("invalid lnurl pay to get info")
|
||||
}
|
||||
return mainHandler.GetLnurlPayInfo(ctx.k1)
|
||||
return mainHandler.paymentManager.GetLnurlPayInfo(ctx.k1)
|
||||
},
|
||||
HandleLnurlPay: async (ctx) => {
|
||||
if (!ctx.k1 || !ctx.amount) {
|
||||
|
|
@ -88,7 +88,7 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
if (isNaN(+ctx.amount)) {
|
||||
throw new Error("invalid amount in lnurl pay to handle")
|
||||
}
|
||||
return mainHandler.HandleLnurlPay(ctx.k1, +ctx.amount)
|
||||
return mainHandler.paymentManager.HandleLnurlPay(ctx.k1, +ctx.amount)
|
||||
},
|
||||
AddProduct: async (ctx, req) => {
|
||||
return mainHandler.productManager.AddProduct(ctx.user_id, req)
|
||||
|
|
@ -102,5 +102,66 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
GetLNURLChannelLink: async (ctx) => {
|
||||
throw new Error("unimplemented")
|
||||
},
|
||||
AddApp: async (ctx, req) => {
|
||||
const err = Types.AddAppRequestValidate(req, {
|
||||
name_CustomCheck: name => name !== ''
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.applicationManager.AddApp(req)
|
||||
},
|
||||
AddAppUser: async (ctx, req) => {
|
||||
const err = Types.AddAppUserRequestValidate(req, {
|
||||
identifier_CustomCheck: id => id !== ''
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.applicationManager.AddAppUser(ctx.app_id, req)
|
||||
},
|
||||
AddAppInvoice: async (ctx, req) => {
|
||||
const err = Types.AddAppInvoiceRequestValidate(req, {
|
||||
payer_identifier_CustomCheck: id => id !== '',
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
console.log(req)
|
||||
return mainHandler.applicationManager.AddAppInvoice(ctx.app_id, req)
|
||||
},
|
||||
AddAppUserInvoice: async (ctx, req) => {
|
||||
const err = Types.AddAppUserInvoiceRequestValidate(req, {
|
||||
payer_identifier_CustomCheck: id => id !== '',
|
||||
receiver_identifier_CustomCheck: id => id !== '',
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.applicationManager.AddAppUserInvoice(ctx.app_id, req)
|
||||
},
|
||||
GetAppUser: async (ctx, req) => {
|
||||
const err = Types.GetAppUserRequestValidate(req, {
|
||||
user_identifier_CustomCheck: id => id !== '',
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.applicationManager.GetAppUser(ctx.app_id, req)
|
||||
},
|
||||
PayAppUserInvoice: async (ctx, req) => {
|
||||
const err = Types.PayAppUserInvoiceRequestValidate(req, {
|
||||
user_identifier_CustomCheck: id => id !== '',
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.applicationManager.PayAppUserInvoice(ctx.app_id, req)
|
||||
},
|
||||
SendAppUserToAppUserPayment: async (ctx, req) => {
|
||||
const err = Types.SendAppUserToAppUserPaymentRequestValidate(req, {
|
||||
to_user_identifier_CustomCheck: id => id !== '',
|
||||
from_user_identifier_CustomCheck: id => id !== '',
|
||||
amount_CustomCheck: amount => amount > 0
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
mainHandler.applicationManager.SendAppUserToAppUserPayment(ctx.app_id, req)
|
||||
},
|
||||
SendAppUserToAppPayment: async (ctx, req) => {
|
||||
const err = Types.SendAppUserToAppPaymentRequestValidate(req, {
|
||||
from_user_identifier_CustomCheck: id => id !== '',
|
||||
amount_CustomCheck: amount => amount > 0
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
mainHandler.applicationManager.SendAppUserToAppPayment(ctx.app_id, req)
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/services/storage/applicationStorage.ts
Normal file
78
src/services/storage/applicationStorage.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import crypto from 'crypto';
|
||||
import { DataSource, EntityManager } from "typeorm"
|
||||
import { Application } from "./entity/Application.js"
|
||||
import UserStorage from './userStorage.js';
|
||||
import { ApplicationUser } from './entity/ApplicationUser.js';
|
||||
export default class {
|
||||
DB: DataSource | EntityManager
|
||||
userStorage: UserStorage
|
||||
constructor(DB: DataSource | EntityManager, userStorage: UserStorage) {
|
||||
this.DB = DB
|
||||
this.userStorage = userStorage
|
||||
}
|
||||
|
||||
async AddApplication(name: string, entityManager = this.DB): Promise<Application> {
|
||||
const owner = await this.userStorage.AddUser(0, entityManager)
|
||||
const repo = entityManager.getRepository(Application)
|
||||
const newApplication = repo.create({
|
||||
app_id: crypto.randomBytes(32).toString('hex'),
|
||||
name,
|
||||
owner
|
||||
})
|
||||
return repo.save(newApplication)
|
||||
}
|
||||
|
||||
async GetApplication(appId: string, entityManager = this.DB): Promise<Application> {
|
||||
const found = await entityManager.getRepository(Application).findOne({
|
||||
where: {
|
||||
app_id: appId
|
||||
}
|
||||
})
|
||||
if (!found) {
|
||||
throw new Error(`application ${appId} not found`)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
async AddApplicationUser(appId: string, userIdentifier: string, balance: number) {
|
||||
return this.DB.transaction(async tx => {
|
||||
const user = await this.userStorage.AddUser(balance, tx)
|
||||
const repo = tx.getRepository(ApplicationUser)
|
||||
const appUser = repo.create({
|
||||
user: user,
|
||||
application: await this.GetApplication(appId),
|
||||
identifier: userIdentifier,
|
||||
})
|
||||
return repo.save(appUser)
|
||||
})
|
||||
}
|
||||
|
||||
GetApplicationUserIfExists(appId: string, userIdentifier: string, entityManager = this.DB): Promise<ApplicationUser | null> {
|
||||
return entityManager.getRepository(ApplicationUser).findOne({ where: { identifier: userIdentifier, application: { app_id: appId } } })
|
||||
}
|
||||
|
||||
async GetOrCreateApplicationUser(appId: string, userIdentifier: string, balance: number, entityManager = this.DB): Promise<ApplicationUser> {
|
||||
const found = await this.GetApplicationUserIfExists(appId, userIdentifier, entityManager)
|
||||
if (found) {
|
||||
return found
|
||||
}
|
||||
return this.AddApplicationUser(appId, userIdentifier, balance)
|
||||
}
|
||||
|
||||
async GetApplicationUser(appId: string, userIdentifier: string, entityManager = this.DB): Promise<ApplicationUser> {
|
||||
const found = await this.GetApplicationUserIfExists(appId, userIdentifier, entityManager)
|
||||
if (!found) {
|
||||
throw new Error(`application user ${userIdentifier} not found`)
|
||||
}
|
||||
|
||||
if (found.application.app_id !== appId) {
|
||||
console.log(found, appId)
|
||||
throw new Error("requested user does not belong to requestor application")
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
async IsApplicationUser(userId: string, entityManager = this.DB): Promise<ApplicationUser | null> {
|
||||
return await entityManager.getRepository(ApplicationUser).findOne({ where: { user: { user_id: userId } } })
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,9 @@ import { UserNostrAuth } from "./entity/UserNostrAuth.js"
|
|||
import { UserBasicAuth } from "./entity/UserBasicAuth.js"
|
||||
import { UserEphemeralKey } from "./entity/UserEphemeralKey.js"
|
||||
import { Product } from "./entity/Product.js"
|
||||
import { UserToUserPayment } from "./entity/UserToUserPayment.js"
|
||||
import { Application } from "./entity/Application.js"
|
||||
import { ApplicationUser } from "./entity/ApplicationUser.js"
|
||||
export type DbSettings = {
|
||||
databaseFile: string
|
||||
}
|
||||
|
|
@ -22,7 +25,7 @@ export default async (settings: DbSettings) => {
|
|||
type: "sqlite",
|
||||
database: settings.databaseFile,
|
||||
//logging: true,
|
||||
entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment, UserNostrAuth, UserBasicAuth, UserEphemeralKey, Product],
|
||||
synchronize: true
|
||||
entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment, UserNostrAuth, UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment],
|
||||
synchronize: true,
|
||||
}).initialize()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
|
||||
import { UserReceivingAddress } from "./UserReceivingAddress.js"
|
||||
|
||||
|
|
@ -27,4 +27,10 @@ export class AddressReceivingTransaction {
|
|||
|
||||
@Column()
|
||||
paid_at_unix: number
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
26
src/services/storage/entity/Application.ts
Normal file
26
src/services/storage/entity/Application.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, CreateDateColumn, UpdateDateColumn, JoinColumn, ManyToOne } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
export class Application {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
serial_id: number
|
||||
|
||||
@Column()
|
||||
@Index({ unique: true })
|
||||
app_id: string
|
||||
|
||||
@Column({ unique: true })
|
||||
name: string
|
||||
|
||||
@ManyToOne(type => User, { eager: true })
|
||||
@JoinColumn()
|
||||
owner: User
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
28
src/services/storage/entity/ApplicationUser.ts
Normal file
28
src/services/storage/entity/ApplicationUser.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, JoinColumn, OneToOne, OneToMany, ManyToOne, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
import { Application } from "./Application.js"
|
||||
|
||||
@Entity()
|
||||
export class ApplicationUser {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
serial_id: number
|
||||
|
||||
@OneToOne(type => User, { eager: true })
|
||||
@JoinColumn()
|
||||
user: User
|
||||
|
||||
@ManyToOne(type => Application, { eager: true })
|
||||
@JoinColumn()
|
||||
application: Application
|
||||
|
||||
@Column()
|
||||
@Index({ unique: true })
|
||||
identifier: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
|
|
@ -15,4 +15,10 @@ export class Product {
|
|||
|
||||
@Column()
|
||||
price_sats: number
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
serial_id: number
|
||||
|
||||
|
|
@ -15,4 +14,10 @@ export class User {
|
|||
|
||||
@Column({ default: false })
|
||||
locked: boolean
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, JoinColumn, OneToOne, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
|
|
@ -7,7 +7,7 @@ export class UserBasicAuth {
|
|||
@PrimaryGeneratedColumn()
|
||||
serial_id: number
|
||||
|
||||
@ManyToOne(type => User, { eager: true })
|
||||
@OneToOne(type => User, { eager: true })
|
||||
@JoinColumn()
|
||||
user: User
|
||||
|
||||
|
|
@ -17,4 +17,10 @@ export class UserBasicAuth {
|
|||
|
||||
@Column()
|
||||
secret_sha256: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
export type EphemeralKeyType = 'balanceCheck' | 'payInfo' | 'pay' | 'withdraw'
|
||||
@Entity()
|
||||
|
|
@ -17,4 +17,10 @@ export class UserEphemeralKey {
|
|||
|
||||
@Column()
|
||||
type: EphemeralKeyType
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
|
|
@ -26,4 +26,10 @@ export class UserInvoicePayment {
|
|||
|
||||
@Column()
|
||||
paid_at_unix: number
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, JoinColumn, OneToOne, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
|
|
@ -7,11 +7,17 @@ export class UserNostrAuth {
|
|||
@PrimaryGeneratedColumn()
|
||||
serial_id: number
|
||||
|
||||
@ManyToOne(type => User, { eager: true })
|
||||
@OneToOne(type => User, { eager: true })
|
||||
@JoinColumn()
|
||||
user: User
|
||||
|
||||
@Column()
|
||||
@Index({ unique: true })
|
||||
nostr_pub: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
|
|
@ -17,4 +17,10 @@ export class UserReceivingAddress {
|
|||
|
||||
@Column()
|
||||
callbackUrl: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { Product } from "./Product.js"
|
||||
import { User } from "./User.js"
|
||||
|
||||
|
|
@ -16,10 +16,13 @@ export class UserReceivingInvoice {
|
|||
@Index({ unique: true })
|
||||
invoice: string
|
||||
|
||||
@Column()
|
||||
expires_at_unix: number
|
||||
|
||||
@Column({ default: 0 })
|
||||
paid_at_unix: number
|
||||
|
||||
@Column()
|
||||
@Column({ default: "" })
|
||||
callbackUrl: string
|
||||
|
||||
@Column({ default: 0 })
|
||||
|
|
@ -30,4 +33,13 @@ export class UserReceivingInvoice {
|
|||
|
||||
@ManyToOne(type => Product, { eager: true })
|
||||
product: Product | null
|
||||
|
||||
@ManyToOne(type => User, { eager: true })
|
||||
payer: User | null
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
32
src/services/storage/entity/UserToUserPayment.ts
Normal file
32
src/services/storage/entity/UserToUserPayment.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
export class UserToUserPayment {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
serial_id: number
|
||||
|
||||
@ManyToOne(type => User, { eager: true })
|
||||
@JoinColumn()
|
||||
from_user: User
|
||||
|
||||
@ManyToOne(type => User, { eager: true })
|
||||
@JoinColumn()
|
||||
to_user: User
|
||||
|
||||
@Column()
|
||||
paid_amount: number
|
||||
|
||||
@Column()
|
||||
service_fees: number
|
||||
|
||||
@Column()
|
||||
paid_at_unix: number
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn } from "typeorm"
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
|
|
@ -32,4 +32,10 @@ export class UserTransactionPayment {
|
|||
|
||||
@Column()
|
||||
paid_at_unix: number
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ import { UserNostrAuth } from "./entity/UserNostrAuth.js";
|
|||
import { UserBasicAuth } from "./entity/UserBasicAuth.js";
|
||||
import { EphemeralKeyType, UserEphemeralKey } from "./entity/UserEphemeralKey.js";
|
||||
import ProductStorage from './productStorage.js'
|
||||
import ApplicationStorage from './applicationStorage.js'
|
||||
import { Product } from "./entity/Product.js";
|
||||
import UserStorage from "./userStorage.js";
|
||||
import PaymentStorage from "./paymentStorage.js";
|
||||
export type StorageSettings = {
|
||||
dbSettings: DbSettings
|
||||
}
|
||||
|
|
@ -23,255 +26,20 @@ export default class {
|
|||
DB: DataSource | EntityManager
|
||||
settings: StorageSettings
|
||||
productStorage: ProductStorage
|
||||
applicationStorage: ApplicationStorage
|
||||
userStorage: UserStorage
|
||||
paymentStorage: PaymentStorage
|
||||
constructor(settings: StorageSettings) {
|
||||
this.settings = settings
|
||||
}
|
||||
async Connect() {
|
||||
this.DB = await NewDB(this.settings.dbSettings)
|
||||
this.userStorage = new UserStorage(this.DB)
|
||||
this.productStorage = new ProductStorage(this.DB)
|
||||
this.applicationStorage = new ApplicationStorage(this.DB, this.userStorage)
|
||||
this.paymentStorage = new PaymentStorage(this.DB, this.userStorage)
|
||||
}
|
||||
StartTransaction(exec: (entityManager: EntityManager) => Promise<void>) {
|
||||
return this.DB.transaction(exec)
|
||||
}
|
||||
async AddUser(entityManager = this.DB): Promise<User> {
|
||||
|
||||
const newUser = entityManager.getRepository(User).create({
|
||||
user_id: crypto.randomBytes(32).toString('hex')
|
||||
})
|
||||
return entityManager.getRepository(User).save(newUser)
|
||||
}
|
||||
async AddBasicUser(name: string, secret: string): Promise<UserBasicAuth> {
|
||||
return this.DB.transaction(async tx => {
|
||||
const user = await this.AddUser(tx)
|
||||
const newUserAuth = tx.getRepository(UserBasicAuth).create({
|
||||
user: user,
|
||||
name: name,
|
||||
secret_sha256: crypto.createHash('sha256').update(secret).digest('base64')
|
||||
})
|
||||
return tx.getRepository(UserBasicAuth).save(newUserAuth)
|
||||
})
|
||||
|
||||
}
|
||||
FindUser(userId: string, entityManager = this.DB) {
|
||||
return entityManager.getRepository(User).findOne({
|
||||
where: {
|
||||
user_id: userId
|
||||
}
|
||||
})
|
||||
}
|
||||
async GetUser(userId: string, entityManager = this.DB): Promise<User> {
|
||||
const user = await this.FindUser(userId, entityManager)
|
||||
if (!user) {
|
||||
throw new Error(`user ${userId} not found`) // TODO: fix logs doxing
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
async FindNostrUser(nostrPub: string, entityManager = this.DB): Promise<UserNostrAuth | null> {
|
||||
return entityManager.getRepository(UserNostrAuth).findOne({
|
||||
where: { nostr_pub: nostrPub }
|
||||
})
|
||||
|
||||
}
|
||||
async AddNostrUser(nostrPub: string): Promise<UserNostrAuth> {
|
||||
return this.DB.transaction(async tx => {
|
||||
const user = await this.AddUser(tx)
|
||||
const newAuth = tx.getRepository(UserNostrAuth).create({
|
||||
user: user,
|
||||
nostr_pub: nostrPub
|
||||
})
|
||||
return tx.getRepository(UserNostrAuth).save(newAuth)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
async AddAddressReceivingTransaction(address: UserReceivingAddress, txHash: string, outputIndex: number, amount: number, serviceFee: number, entityManager = this.DB) {
|
||||
const newAddressTransaction = entityManager.getRepository(AddressReceivingTransaction).create({
|
||||
user_address: address,
|
||||
tx_hash: txHash,
|
||||
output_index: outputIndex,
|
||||
paid_amount: amount,
|
||||
service_fee: serviceFee,
|
||||
paid_at_unix: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
return entityManager.getRepository(AddressReceivingTransaction).save(newAddressTransaction)
|
||||
}
|
||||
|
||||
GetUserReceivingTransactions(userId: string, fromIndex: number, entityManager = this.DB): Promise<AddressReceivingTransaction[]> {
|
||||
return entityManager.getRepository(AddressReceivingTransaction).find({
|
||||
where: {
|
||||
user_address: { user: { user_id: userId } },
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async AddUserAddress(userId: string, address: string, callbackUrl = "", entityManager = this.DB): Promise<UserReceivingAddress> {
|
||||
const newUserAddress = entityManager.getRepository(UserReceivingAddress).create({
|
||||
address,
|
||||
callbackUrl,
|
||||
user: await this.GetUser(userId, entityManager)
|
||||
})
|
||||
return entityManager.getRepository(UserReceivingAddress).save(newUserAddress)
|
||||
}
|
||||
|
||||
async FlagInvoiceAsPaid(invoice: UserReceivingInvoice, amount: number, serviceFee: number, entityManager = this.DB) {
|
||||
return entityManager.getRepository(UserReceivingInvoice).update(invoice.serial_id, { paid_at_unix: Math.floor(Date.now() / 1000), paid_amount: amount, service_fee: serviceFee })
|
||||
}
|
||||
|
||||
GetUserInvoicesFlaggedAsPaid(userId: string, fromIndex: number, entityManager = this.DB): Promise<UserReceivingInvoice[]> {
|
||||
return entityManager.getRepository(UserReceivingInvoice).find({
|
||||
where: {
|
||||
user: {
|
||||
user_id: userId
|
||||
},
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async AddUserInvoice(user: User, invoice: string, product?: Product, callbackUrl = "", entityManager = this.DB): Promise<UserReceivingInvoice> {
|
||||
const newUserInvoice = entityManager.getRepository(UserReceivingInvoice).create({
|
||||
invoice: invoice,
|
||||
callbackUrl,
|
||||
user: user,
|
||||
product: product
|
||||
})
|
||||
return entityManager.getRepository(UserReceivingInvoice).save(newUserInvoice)
|
||||
}
|
||||
|
||||
async GetAddressOwner(address: string, entityManager = this.DB): Promise<UserReceivingAddress | null> {
|
||||
return entityManager.getRepository(UserReceivingAddress).findOne({
|
||||
where: {
|
||||
address
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async GetInvoiceOwner(paymentRequest: string, entityManager = this.DB): Promise<UserReceivingInvoice | null> {
|
||||
return entityManager.getRepository(UserReceivingInvoice).findOne({
|
||||
where: {
|
||||
invoice: paymentRequest
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async AddUserInvoicePayment(userId: string, invoice: string, amount: number, routingFees: number, serviceFees: number, entityManager = this.DB): Promise<UserInvoicePayment> {
|
||||
const newPayment = entityManager.getRepository(UserInvoicePayment).create({
|
||||
user: await this.GetUser(userId),
|
||||
paid_amount: amount,
|
||||
invoice,
|
||||
routing_fees: routingFees,
|
||||
service_fees: serviceFees,
|
||||
paid_at_unix: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
return entityManager.getRepository(UserInvoicePayment).save(newPayment)
|
||||
}
|
||||
|
||||
GetUserInvoicePayments(userId: string, fromIndex: number, entityManager = this.DB): Promise<UserInvoicePayment[]> {
|
||||
return entityManager.getRepository(UserInvoicePayment).find({
|
||||
where: {
|
||||
user: {
|
||||
user_id: userId
|
||||
},
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async AddUserTransactionPayment(userId: string, address: string, txHash: string, txOutput: number, amount: number, chainFees: number, serviceFees: number, entityManager = this.DB): Promise<UserTransactionPayment> {
|
||||
const newTx = entityManager.getRepository(UserTransactionPayment).create({
|
||||
user: await this.GetUser(userId),
|
||||
address,
|
||||
paid_amount: amount,
|
||||
chain_fees: chainFees,
|
||||
output_index: txOutput,
|
||||
tx_hash: txHash,
|
||||
service_fees: serviceFees,
|
||||
paid_at_unix: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
return entityManager.getRepository(UserTransactionPayment).save(newTx)
|
||||
}
|
||||
|
||||
GetUserTransactionPayments(userId: string, fromIndex: number, entityManager = this.DB): Promise<UserTransactionPayment[]> {
|
||||
return entityManager.getRepository(UserTransactionPayment).find({
|
||||
where: {
|
||||
user: {
|
||||
user_id: userId
|
||||
},
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
async LockUser(userId: string, entityManager = this.DB) {
|
||||
const res = await entityManager.getRepository(User).update({
|
||||
user_id: userId
|
||||
}, { locked: true })
|
||||
if (!res.affected) {
|
||||
throw new Error("unaffected user lock for " + userId) // TODO: fix logs doxing
|
||||
}
|
||||
}
|
||||
async UnlockUser(userId: string, entityManager = this.DB) {
|
||||
const res = await entityManager.getRepository(User).update({
|
||||
user_id: userId
|
||||
}, { locked: false })
|
||||
if (!res.affected) {
|
||||
throw new Error("unaffected user unlock for " + userId) // TODO: fix logs doxing
|
||||
}
|
||||
}
|
||||
async IncrementUserBalance(userId: string, increment: number, entityManager = this.DB) {
|
||||
const res = await entityManager.getRepository(User).increment({
|
||||
user_id: userId,
|
||||
}, "balance_sats", increment)
|
||||
if (!res.affected) {
|
||||
throw new Error("unaffected balance increment for " + userId) // TODO: fix logs doxing
|
||||
}
|
||||
}
|
||||
async DecrementUserBalance(userId: string, decrement: number, entityManager = this.DB) {
|
||||
const res = await entityManager.getRepository(User).decrement({
|
||||
user_id: userId,
|
||||
}, "balance_sats", decrement)
|
||||
if (!res.affected) {
|
||||
throw new Error("unaffected balance decrement for " + userId) // TODO: fix logs doxing
|
||||
}
|
||||
}
|
||||
|
||||
async AddUserEphemeralKey(userId: string, keyType: EphemeralKeyType, entityManager = this.DB): Promise<UserEphemeralKey> {
|
||||
const newKey = entityManager.getRepository(UserEphemeralKey).create({
|
||||
user: await this.GetUser(userId, entityManager),
|
||||
key: crypto.randomBytes(31).toString('hex'),
|
||||
type: keyType
|
||||
})
|
||||
return entityManager.getRepository(UserEphemeralKey).save(newKey)
|
||||
}
|
||||
|
||||
async UseUserEphemeralKey(key: string, keyType: EphemeralKeyType, entityManager = this.DB): Promise<UserEphemeralKey> {
|
||||
const found = await entityManager.getRepository(UserEphemeralKey).findOne({
|
||||
where: {
|
||||
key: key,
|
||||
type: keyType
|
||||
}
|
||||
})
|
||||
if (!found) {
|
||||
throw new Error("the provided ephemeral key is invalid")
|
||||
}
|
||||
await entityManager.getRepository(UserEphemeralKey).delete(found.serial_id)
|
||||
return found
|
||||
}
|
||||
}
|
||||
228
src/services/storage/paymentStorage.ts
Normal file
228
src/services/storage/paymentStorage.ts
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
import crypto from 'crypto';
|
||||
import { DataSource, EntityManager, MoreThan, MoreThanOrEqual } from "typeorm"
|
||||
import { User } from './entity/User.js';
|
||||
import { UserBasicAuth } from './entity/UserBasicAuth.js';
|
||||
import { UserNostrAuth } from './entity/UserNostrAuth.js';
|
||||
import { UserTransactionPayment } from './entity/UserTransactionPayment.js';
|
||||
import { EphemeralKeyType, UserEphemeralKey } from './entity/UserEphemeralKey.js';
|
||||
import { UserReceivingInvoice } from './entity/UserReceivingInvoice.js';
|
||||
import { UserReceivingAddress } from './entity/UserReceivingAddress.js';
|
||||
import { Product } from './entity/Product.js';
|
||||
import UserStorage from './userStorage.js';
|
||||
import { AddressReceivingTransaction } from './entity/AddressReceivingTransaction.js';
|
||||
import { UserInvoicePayment } from './entity/UserInvoicePayment.js';
|
||||
import { UserToUserPayment } from './entity/UserToUserPayment.js';
|
||||
export type InboundOptionals = { product?: Product, callbackUrl?: string, expiry: number, expectedPayer?: User }
|
||||
export const defaultInvoiceExpiry = 60 * 60
|
||||
export default class {
|
||||
DB: DataSource | EntityManager
|
||||
userStorage: UserStorage
|
||||
constructor(DB: DataSource | EntityManager, userStorage: UserStorage) {
|
||||
this.DB = DB
|
||||
this.userStorage = userStorage
|
||||
}
|
||||
async AddAddressReceivingTransaction(address: UserReceivingAddress, txHash: string, outputIndex: number, amount: number, serviceFee: number, entityManager = this.DB) {
|
||||
const newAddressTransaction = entityManager.getRepository(AddressReceivingTransaction).create({
|
||||
user_address: address,
|
||||
tx_hash: txHash,
|
||||
output_index: outputIndex,
|
||||
paid_amount: amount,
|
||||
service_fee: serviceFee,
|
||||
paid_at_unix: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
return entityManager.getRepository(AddressReceivingTransaction).save(newAddressTransaction)
|
||||
}
|
||||
|
||||
GetUserReceivingTransactions(userId: string, fromIndex: number, entityManager = this.DB): Promise<AddressReceivingTransaction[]> {
|
||||
return entityManager.getRepository(AddressReceivingTransaction).find({
|
||||
where: {
|
||||
user_address: { user: { user_id: userId } },
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
async AddUserAddress(userId: string, address: string, callbackUrl = "", entityManager = this.DB): Promise<UserReceivingAddress> {
|
||||
const newUserAddress = entityManager.getRepository(UserReceivingAddress).create({
|
||||
address,
|
||||
callbackUrl,
|
||||
user: await this.userStorage.GetUser(userId, entityManager)
|
||||
})
|
||||
return entityManager.getRepository(UserReceivingAddress).save(newUserAddress)
|
||||
}
|
||||
|
||||
async FlagInvoiceAsPaid(invoice: UserReceivingInvoice, amount: number, serviceFee: number, entityManager = this.DB) {
|
||||
return entityManager.getRepository(UserReceivingInvoice).update(invoice.serial_id, { paid_at_unix: Math.floor(Date.now() / 1000), paid_amount: amount, service_fee: serviceFee })
|
||||
}
|
||||
|
||||
GetUserInvoicesFlaggedAsPaid(userId: string, fromIndex: number, entityManager = this.DB): Promise<UserReceivingInvoice[]> {
|
||||
return entityManager.getRepository(UserReceivingInvoice).find({
|
||||
where: {
|
||||
user: {
|
||||
user_id: userId
|
||||
},
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async AddUserInvoice(user: User, invoice: string, options: InboundOptionals = { expiry: defaultInvoiceExpiry }, entityManager = this.DB): Promise<UserReceivingInvoice> {
|
||||
const newUserInvoice = entityManager.getRepository(UserReceivingInvoice).create({
|
||||
invoice: invoice,
|
||||
callbackUrl: options.callbackUrl,
|
||||
user: user,
|
||||
product: options.product,
|
||||
expires_at_unix: Math.floor(Date.now() / 1000) + options.expiry,
|
||||
payer: options.expectedPayer
|
||||
})
|
||||
return entityManager.getRepository(UserReceivingInvoice).save(newUserInvoice)
|
||||
}
|
||||
|
||||
async GetAddressOwner(address: string, entityManager = this.DB): Promise<UserReceivingAddress | null> {
|
||||
return entityManager.getRepository(UserReceivingAddress).findOne({
|
||||
where: {
|
||||
address
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async GetInvoiceOwner(paymentRequest: string, entityManager = this.DB): Promise<UserReceivingInvoice | null> {
|
||||
return entityManager.getRepository(UserReceivingInvoice).findOne({
|
||||
where: {
|
||||
invoice: paymentRequest
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async AddUserInvoicePayment(userId: string, invoice: string, amount: number, routingFees: number, serviceFees: number, entityManager = this.DB): Promise<UserInvoicePayment> {
|
||||
const newPayment = entityManager.getRepository(UserInvoicePayment).create({
|
||||
user: await this.userStorage.GetUser(userId),
|
||||
paid_amount: amount,
|
||||
invoice,
|
||||
routing_fees: routingFees,
|
||||
service_fees: serviceFees,
|
||||
paid_at_unix: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
return entityManager.getRepository(UserInvoicePayment).save(newPayment)
|
||||
}
|
||||
|
||||
GetUserInvoicePayments(userId: string, fromIndex: number, entityManager = this.DB): Promise<UserInvoicePayment[]> {
|
||||
return entityManager.getRepository(UserInvoicePayment).find({
|
||||
where: {
|
||||
user: {
|
||||
user_id: userId
|
||||
},
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async AddUserTransactionPayment(userId: string, address: string, txHash: string, txOutput: number, amount: number, chainFees: number, serviceFees: number, entityManager = this.DB): Promise<UserTransactionPayment> {
|
||||
const newTx = entityManager.getRepository(UserTransactionPayment).create({
|
||||
user: await this.userStorage.GetUser(userId),
|
||||
address,
|
||||
paid_amount: amount,
|
||||
chain_fees: chainFees,
|
||||
output_index: txOutput,
|
||||
tx_hash: txHash,
|
||||
service_fees: serviceFees,
|
||||
paid_at_unix: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
return entityManager.getRepository(UserTransactionPayment).save(newTx)
|
||||
}
|
||||
|
||||
GetUserTransactionPayments(userId: string, fromIndex: number, entityManager = this.DB): Promise<UserTransactionPayment[]> {
|
||||
return entityManager.getRepository(UserTransactionPayment).find({
|
||||
where: {
|
||||
user: {
|
||||
user_id: userId
|
||||
},
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
async AddUserEphemeralKey(userId: string, keyType: EphemeralKeyType, entityManager = this.DB): Promise<UserEphemeralKey> {
|
||||
const newKey = entityManager.getRepository(UserEphemeralKey).create({
|
||||
user: await this.userStorage.GetUser(userId, entityManager),
|
||||
key: crypto.randomBytes(31).toString('hex'),
|
||||
type: keyType
|
||||
})
|
||||
return entityManager.getRepository(UserEphemeralKey).save(newKey)
|
||||
}
|
||||
|
||||
async UseUserEphemeralKey(key: string, keyType: EphemeralKeyType, entityManager = this.DB): Promise<UserEphemeralKey> {
|
||||
const found = await entityManager.getRepository(UserEphemeralKey).findOne({
|
||||
where: {
|
||||
key: key,
|
||||
type: keyType
|
||||
}
|
||||
})
|
||||
if (!found) {
|
||||
throw new Error("the provided ephemeral key is invalid")
|
||||
}
|
||||
await entityManager.getRepository(UserEphemeralKey).delete(found.serial_id)
|
||||
return found
|
||||
}
|
||||
|
||||
async AddUserToUserPayment(fromUserId: string, toUserId: string, amount: number, fee: number, entityManager = this.DB) {
|
||||
const newKey = entityManager.getRepository(UserToUserPayment).create({
|
||||
from_user: await this.userStorage.GetUser(fromUserId, entityManager),
|
||||
to_user: await this.userStorage.GetUser(toUserId, entityManager),
|
||||
paid_at_unix: Math.floor(Date.now() / 1000),
|
||||
paid_amount: amount,
|
||||
service_fees: fee
|
||||
})
|
||||
return entityManager.getRepository(UserToUserPayment).save(newKey)
|
||||
}
|
||||
|
||||
GetUserToUserReceivedPayments(userId: string, fromIndex: number, entityManager = this.DB) {
|
||||
return entityManager.getRepository(UserToUserPayment).find({
|
||||
where: {
|
||||
to_user: {
|
||||
user_id: userId
|
||||
},
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
GetUserToUserSentPayments(userId: string, fromIndex: number, entityManager = this.DB) {
|
||||
return entityManager.getRepository(UserToUserPayment).find({
|
||||
where: {
|
||||
from_user: {
|
||||
user_id: userId
|
||||
},
|
||||
serial_id: MoreThanOrEqual(fromIndex),
|
||||
paid_at_unix: MoreThan(0),
|
||||
},
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
105
src/services/storage/userStorage.ts
Normal file
105
src/services/storage/userStorage.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import crypto from 'crypto';
|
||||
import { DataSource, EntityManager } from "typeorm"
|
||||
import { User } from './entity/User.js';
|
||||
import { UserBasicAuth } from './entity/UserBasicAuth.js';
|
||||
import { UserNostrAuth } from './entity/UserNostrAuth.js';
|
||||
export default class {
|
||||
DB: DataSource | EntityManager
|
||||
constructor(DB: DataSource | EntityManager) {
|
||||
this.DB = DB
|
||||
}
|
||||
async AddUser(balance: number, entityManager = this.DB): Promise<User> {
|
||||
if (balance && process.env.ALLOW_BALANCE_MIGRATION !== 'true') {
|
||||
throw new Error("balance migration is not allowed")
|
||||
}
|
||||
console.log("Adding user with balance", balance)
|
||||
const newUser = entityManager.getRepository(User).create({
|
||||
user_id: crypto.randomBytes(32).toString('hex'),
|
||||
balance_sats: balance
|
||||
})
|
||||
return entityManager.getRepository(User).save(newUser)
|
||||
}
|
||||
|
||||
|
||||
async AddBasicUser(name: string, secret: string): Promise<UserBasicAuth> {
|
||||
return this.DB.transaction(async tx => {
|
||||
const user = await this.AddUser(0, tx)
|
||||
const newUserAuth = tx.getRepository(UserBasicAuth).create({
|
||||
user: user,
|
||||
name: name,
|
||||
secret_sha256: crypto.createHash('sha256').update(secret).digest('base64')
|
||||
})
|
||||
return tx.getRepository(UserBasicAuth).save(newUserAuth)
|
||||
})
|
||||
|
||||
}
|
||||
FindUser(userId: string, entityManager = this.DB) {
|
||||
return entityManager.getRepository(User).findOne({
|
||||
where: {
|
||||
user_id: userId
|
||||
}
|
||||
})
|
||||
}
|
||||
async GetUser(userId: string, entityManager = this.DB): Promise<User> {
|
||||
const user = await this.FindUser(userId, entityManager)
|
||||
if (!user) {
|
||||
throw new Error(`user ${userId} not found`) // TODO: fix logs doxing
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
async FindNostrUser(nostrPub: string, entityManager = this.DB): Promise<UserNostrAuth | null> {
|
||||
return entityManager.getRepository(UserNostrAuth).findOne({
|
||||
where: { nostr_pub: nostrPub }
|
||||
})
|
||||
|
||||
}
|
||||
async AddNostrUser(nostrPub: string): Promise<UserNostrAuth> {
|
||||
return this.DB.transaction(async tx => {
|
||||
const user = await this.AddUser(0, tx)
|
||||
const newAuth = tx.getRepository(UserNostrAuth).create({
|
||||
user: user,
|
||||
nostr_pub: nostrPub
|
||||
})
|
||||
return tx.getRepository(UserNostrAuth).save(newAuth)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
async LockUser(userId: string, entityManager = this.DB) {
|
||||
const res = await entityManager.getRepository(User).update({
|
||||
user_id: userId
|
||||
}, { locked: true })
|
||||
if (!res.affected) {
|
||||
throw new Error("unaffected user lock for " + userId) // TODO: fix logs doxing
|
||||
}
|
||||
}
|
||||
async UnlockUser(userId: string, entityManager = this.DB) {
|
||||
const res = await entityManager.getRepository(User).update({
|
||||
user_id: userId
|
||||
}, { locked: false })
|
||||
if (!res.affected) {
|
||||
throw new Error("unaffected user unlock for " + userId) // TODO: fix logs doxing
|
||||
}
|
||||
}
|
||||
async IncrementUserBalance(userId: string, increment: number, entityManager = this.DB) {
|
||||
const res = await entityManager.getRepository(User).increment({
|
||||
user_id: userId,
|
||||
}, "balance_sats", increment)
|
||||
if (!res.affected) {
|
||||
throw new Error("unaffected balance increment for " + userId) // TODO: fix logs doxing
|
||||
}
|
||||
}
|
||||
async DecrementUserBalance(userId: string, decrement: number, entityManager = this.DB) {
|
||||
const user = await this.GetUser(userId, entityManager)
|
||||
if (!user || user.balance_sats < decrement) {
|
||||
throw new Error("not enough balance to decrement")
|
||||
}
|
||||
const res = await entityManager.getRepository(User).decrement({
|
||||
user_id: userId,
|
||||
}, "balance_sats", decrement)
|
||||
if (!res.affected) {
|
||||
throw new Error("unaffected balance decrement for " + userId) // TODO: fix logs doxing
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue