fixies
This commit is contained in:
parent
7784beabad
commit
bfa23681e6
35 changed files with 4374 additions and 2666 deletions
362
package-lock.json
generated
362
package-lock.json
generated
|
|
@ -13,6 +13,7 @@
|
|||
"@protobuf-ts/grpc-transport": "^2.5.0",
|
||||
"@protobuf-ts/plugin": "^2.5.0",
|
||||
"@protobuf-ts/runtime": "^2.5.0",
|
||||
"@stablelib/xchacha20": "^1.0.1",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/node": "^17.0.31",
|
||||
"@types/secp256k1": "^4.0.3",
|
||||
|
|
@ -39,7 +40,9 @@
|
|||
"ts-proto": "^1.112.1",
|
||||
"typeorm": "0.3.15",
|
||||
"typescript": "^4.6.4",
|
||||
"uuid": "^8.3.2"
|
||||
"uuid": "^8.3.2",
|
||||
"websocket": "^1.0.34",
|
||||
"websocket-polyfill": "^0.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.4",
|
||||
|
|
@ -50,6 +53,7 @@
|
|||
"@types/node": "^16.11.10",
|
||||
"@types/node-fetch": "^2.6.3",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/websocket": "^1.0.6",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-node": "10.7.0",
|
||||
"typescript": "4.5.2"
|
||||
|
|
@ -395,6 +399,43 @@
|
|||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
|
||||
"integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw=="
|
||||
},
|
||||
"node_modules/@stablelib/binary": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz",
|
||||
"integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==",
|
||||
"dependencies": {
|
||||
"@stablelib/int": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@stablelib/chacha": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/chacha/-/chacha-1.0.1.tgz",
|
||||
"integrity": "sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==",
|
||||
"dependencies": {
|
||||
"@stablelib/binary": "^1.0.1",
|
||||
"@stablelib/wipe": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@stablelib/int": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz",
|
||||
"integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w=="
|
||||
},
|
||||
"node_modules/@stablelib/wipe": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz",
|
||||
"integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg=="
|
||||
},
|
||||
"node_modules/@stablelib/xchacha20": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/xchacha20/-/xchacha20-1.0.1.tgz",
|
||||
"integrity": "sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw==",
|
||||
"dependencies": {
|
||||
"@stablelib/binary": "^1.0.1",
|
||||
"@stablelib/chacha": "^1.0.1",
|
||||
"@stablelib/wipe": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
|
|
@ -586,6 +627,15 @@
|
|||
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/websocket": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.6.tgz",
|
||||
"integrity": "sha512-JXkliwz93B2cMWOI1ukElQBPN88vMg3CruvW4KVSKpflt3NyNCJImnhIuB/f97rG7kakqRJGFiwkA895Kn02Dg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
|
|
@ -998,6 +1048,18 @@
|
|||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
|
||||
"optional": true
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
|
@ -1316,6 +1378,15 @@
|
|||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"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",
|
||||
|
|
@ -1550,11 +1621,44 @@
|
|||
"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",
|
||||
|
|
@ -1627,6 +1731,19 @@
|
|||
"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",
|
||||
|
|
@ -2230,6 +2347,11 @@
|
|||
"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",
|
||||
|
|
@ -2599,6 +2721,11 @@
|
|||
"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",
|
||||
|
|
@ -3918,6 +4045,16 @@
|
|||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||
},
|
||||
"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",
|
||||
|
|
@ -3938,6 +4075,14 @@
|
|||
"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.15",
|
||||
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.15.tgz",
|
||||
|
|
@ -4213,6 +4358,18 @@
|
|||
"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",
|
||||
|
|
@ -4253,6 +4410,31 @@
|
|||
"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",
|
||||
|
|
@ -4322,6 +4504,14 @@
|
|||
"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",
|
||||
|
|
@ -4622,6 +4812,43 @@
|
|||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
|
||||
"integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw=="
|
||||
},
|
||||
"@stablelib/binary": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz",
|
||||
"integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==",
|
||||
"requires": {
|
||||
"@stablelib/int": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@stablelib/chacha": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/chacha/-/chacha-1.0.1.tgz",
|
||||
"integrity": "sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==",
|
||||
"requires": {
|
||||
"@stablelib/binary": "^1.0.1",
|
||||
"@stablelib/wipe": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@stablelib/int": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz",
|
||||
"integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w=="
|
||||
},
|
||||
"@stablelib/wipe": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz",
|
||||
"integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg=="
|
||||
},
|
||||
"@stablelib/xchacha20": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@stablelib/xchacha20/-/xchacha20-1.0.1.tgz",
|
||||
"integrity": "sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw==",
|
||||
"requires": {
|
||||
"@stablelib/binary": "^1.0.1",
|
||||
"@stablelib/chacha": "^1.0.1",
|
||||
"@stablelib/wipe": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
|
|
@ -4809,6 +5036,15 @@
|
|||
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/websocket": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.6.tgz",
|
||||
"integrity": "sha512-JXkliwz93B2cMWOI1ukElQBPN88vMg3CruvW4KVSKpflt3NyNCJImnhIuB/f97rG7kakqRJGFiwkA895Kn02Dg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
|
|
@ -5122,6 +5358,14 @@
|
|||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
|
||||
"optional": true
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
|
@ -5370,6 +5614,15 @@
|
|||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"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",
|
||||
|
|
@ -5560,11 +5813,40 @@
|
|||
"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",
|
||||
|
|
@ -5628,6 +5910,21 @@
|
|||
"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",
|
||||
|
|
@ -6073,6 +6370,11 @@
|
|||
"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",
|
||||
|
|
@ -6368,6 +6670,11 @@
|
|||
"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",
|
||||
|
|
@ -7357,6 +7664,16 @@
|
|||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||
},
|
||||
"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",
|
||||
|
|
@ -7371,6 +7688,14 @@
|
|||
"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.15",
|
||||
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.15.tgz",
|
||||
|
|
@ -7514,6 +7839,14 @@
|
|||
"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",
|
||||
|
|
@ -7545,6 +7878,28 @@
|
|||
"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",
|
||||
|
|
@ -7596,6 +7951,11 @@
|
|||
"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",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
"@protobuf-ts/grpc-transport": "^2.5.0",
|
||||
"@protobuf-ts/plugin": "^2.5.0",
|
||||
"@protobuf-ts/runtime": "^2.5.0",
|
||||
"@stablelib/xchacha20": "^1.0.1",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/node": "^17.0.31",
|
||||
"@types/secp256k1": "^4.0.3",
|
||||
|
|
@ -54,7 +55,9 @@
|
|||
"ts-proto": "^1.112.1",
|
||||
"typeorm": "0.3.15",
|
||||
"typescript": "^4.6.4",
|
||||
"uuid": "^8.3.2"
|
||||
"uuid": "^8.3.2",
|
||||
"websocket": "^1.0.34",
|
||||
"websocket-polyfill": "^0.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.4",
|
||||
|
|
@ -65,6 +68,7 @@
|
|||
"@types/node": "^16.11.10",
|
||||
"@types/node-fetch": "^2.6.3",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/websocket": "^1.0.6",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-node": "10.7.0",
|
||||
"typescript": "4.5.2"
|
||||
|
|
|
|||
|
|
@ -88,10 +88,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __user_id__: _string_
|
||||
|
||||
- __Admin__:
|
||||
- this auth type is __encrypted__
|
||||
- expected context content
|
||||
- __admin_id__: _string_
|
||||
|
||||
- __App__:
|
||||
- expected context content
|
||||
- __app_id__: _string_
|
||||
|
||||
## HTTP Methods
|
||||
### These are the http methods the client implements to communicate with the API
|
||||
|
||||
|
|
@ -111,12 +114,109 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
|
||||
- LndGetInfo
|
||||
- auth type: __Admin__
|
||||
- this method is encrypted
|
||||
- http method: __post__
|
||||
- http route: __/api/lnd/getinfo__
|
||||
- http route: __/api/admin/lnd/getinfo__
|
||||
- input: [LndGetInfoRequest](#LndGetInfoRequest)
|
||||
- output: [LndGetInfoResponse](#LndGetInfoResponse)
|
||||
|
||||
- SetMockInvoiceAsPaid
|
||||
- auth type: __Guest__
|
||||
- http method: __post__
|
||||
- http route: __/api/lnd/mock/invoice/paid__
|
||||
- input: [SetMockInvoiceAsPaidRequest](#SetMockInvoiceAsPaidRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- AddApp
|
||||
- auth type: __Admin__
|
||||
- http method: __post__
|
||||
- http route: __/api/admin/app/add__
|
||||
- input: [AddAppRequest](#AddAppRequest)
|
||||
- output: [AuthApp](#AuthApp)
|
||||
|
||||
- AuthApp
|
||||
- auth type: __Admin__
|
||||
- http method: __post__
|
||||
- http route: __/api/admin/app/auth__
|
||||
- input: [AuthAppRequest](#AuthAppRequest)
|
||||
- output: [AuthApp](#AuthApp)
|
||||
|
||||
- GetApp
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/get__
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [Application](#Application)
|
||||
|
||||
- AddAppUser
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/user/add__
|
||||
- input: [AddAppUserRequest](#AddAppUserRequest)
|
||||
- output: [AppUser](#AppUser)
|
||||
|
||||
- AddAppInvoice
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/add/invoice__
|
||||
- input: [AddAppInvoiceRequest](#AddAppInvoiceRequest)
|
||||
- output: [NewInvoiceResponse](#NewInvoiceResponse)
|
||||
|
||||
- AddAppUserInvoice
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/user/add/invoice__
|
||||
- input: [AddAppUserInvoiceRequest](#AddAppUserInvoiceRequest)
|
||||
- output: [NewInvoiceResponse](#NewInvoiceResponse)
|
||||
|
||||
- GetAppUser
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/user/get__
|
||||
- input: [GetAppUserRequest](#GetAppUserRequest)
|
||||
- output: [AppUser](#AppUser)
|
||||
|
||||
- PayAppUserInvoice
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/invoice/pay__
|
||||
- input: [PayAppUserInvoiceRequest](#PayAppUserInvoiceRequest)
|
||||
- output: [PayAppUserInvoiceResponse](#PayAppUserInvoiceResponse)
|
||||
|
||||
- SendAppUserToAppUserPayment
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/user/internal/pay__
|
||||
- input: [SendAppUserToAppUserPaymentRequest](#SendAppUserToAppUserPaymentRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- SendAppUserToAppPayment
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/internal/pay__
|
||||
- input: [SendAppUserToAppPaymentRequest](#SendAppUserToAppPaymentRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- GetAppUserLNURLInfo
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/user/lnurl/pay/info__
|
||||
- input: [GetAppUserLNURLInfoRequest](#GetAppUserLNURLInfoRequest)
|
||||
- output: [LnurlPayInfoResponse](#LnurlPayInfoResponse)
|
||||
|
||||
- SetMockAppUserBalance
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/mock/user/blance/set__
|
||||
- input: [SetMockAppUserBalanceRequest](#SetMockAppUserBalanceRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- SetMockAppBalance
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
- http route: __/api/app/mock/blance/set__
|
||||
- input: [SetMockAppBalanceRequest](#SetMockAppBalanceRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- AddUser
|
||||
- auth type: __Guest__
|
||||
- http method: __post__
|
||||
|
|
@ -260,56 +360,96 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
## Messages
|
||||
### The content of requests and response from the methods
|
||||
|
||||
### OpenChannelResponse
|
||||
- __channelId__: _string_
|
||||
### LnurlPayInfoResponse
|
||||
- __tag__: _string_
|
||||
- __callback__: _string_
|
||||
- __maxSendable__: _number_
|
||||
- __minSendable__: _number_
|
||||
- __metadata__: _string_
|
||||
|
||||
### UserInfo
|
||||
- __userId__: _string_
|
||||
- __balance__: _number_
|
||||
### AddUserRequest
|
||||
- __callbackUrl__: _string_
|
||||
- __name__: _string_
|
||||
- __secret__: _string_
|
||||
|
||||
### GetUserOperationsRequest
|
||||
- __latestIncomingInvoice__: _number_
|
||||
- __latestOutgoingInvoice__: _number_
|
||||
- __latestIncomingTx__: _number_
|
||||
- __latestOutgoingTx__: _number_
|
||||
- __latestIncomingUserToUserPayment__: _number_
|
||||
- __latestOutgoingUserToUserPayment__: _number_
|
||||
|
||||
### Empty
|
||||
|
||||
### AddAppUserRequest
|
||||
- __identifier__: _string_
|
||||
- __fail_if_exists__: _boolean_
|
||||
- __balance__: _number_
|
||||
|
||||
### SetMockAppBalanceRequest
|
||||
- __amount__: _number_
|
||||
|
||||
### NewAddressRequest
|
||||
- __addressType__: _[AddressType](#AddressType)_
|
||||
|
||||
### NewInvoiceRequest
|
||||
- __amountSats__: _number_
|
||||
- __memo__: _string_
|
||||
|
||||
### GetUserOperationsResponse
|
||||
- __latestOutgoingInvoiceOperations__: _[UserOperations](#UserOperations)_
|
||||
- __latestIncomingInvoiceOperations__: _[UserOperations](#UserOperations)_
|
||||
- __latestOutgoingTxOperations__: _[UserOperations](#UserOperations)_
|
||||
- __latestIncomingTxOperations__: _[UserOperations](#UserOperations)_
|
||||
- __latestOutgoingUserToUserPayemnts__: _[UserOperations](#UserOperations)_
|
||||
- __latestIncomingUserToUserPayemnts__: _[UserOperations](#UserOperations)_
|
||||
|
||||
### Application
|
||||
- __name__: _string_
|
||||
- __id__: _string_
|
||||
- __balance__: _number_
|
||||
- __npub__: _string_
|
||||
|
||||
### AddAppInvoiceRequest
|
||||
- __payer_identifier__: _string_
|
||||
- __http_callback_url__: _string_
|
||||
- __invoice_req__: _[NewInvoiceRequest](#NewInvoiceRequest)_
|
||||
|
||||
### GetAppUserLNURLInfoRequest
|
||||
- __user_identifier__: _string_
|
||||
- __base_url_override__: _string_
|
||||
|
||||
### AuthUserResponse
|
||||
- __userId__: _string_
|
||||
- __authToken__: _string_
|
||||
|
||||
### AddProductRequest
|
||||
- __name__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### Product
|
||||
- __id__: _string_
|
||||
- __name__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### EncryptionExchangeRequest
|
||||
- __publicKey__: _string_
|
||||
- __deviceId__: _string_
|
||||
|
||||
### NewInvoiceRequest
|
||||
- __amountSats__: _number_
|
||||
- __memo__: _string_
|
||||
### AppUser
|
||||
- __identifier__: _string_
|
||||
- __info__: _[UserInfo](#UserInfo)_
|
||||
- __max_withdrawable__: _number_
|
||||
|
||||
### PayInvoiceResponse
|
||||
- __preimage__: _string_
|
||||
### DecodeInvoiceResponse
|
||||
- __amount__: _number_
|
||||
|
||||
### HandleLnurlPayResponse
|
||||
- __pr__: _string_
|
||||
- __routes__: ARRAY of: _[Empty](#Empty)_
|
||||
### PayInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### AuthUserRequest
|
||||
- __name__: _string_
|
||||
- __secret__: _string_
|
||||
|
||||
### UserOperations
|
||||
- __fromIndex__: _number_
|
||||
- __toIndex__: _number_
|
||||
- __operations__: ARRAY of: _[UserOperation](#UserOperation)_
|
||||
|
||||
### PayAddressRequest
|
||||
- __address__: _string_
|
||||
- __amoutSats__: _number_
|
||||
- __targetConf__: _number_
|
||||
### PayAddressResponse
|
||||
- __txId__: _string_
|
||||
|
||||
### OpenChannelRequest
|
||||
- __destination__: _string_
|
||||
|
|
@ -321,33 +461,56 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __lnurl__: _string_
|
||||
- __k1__: _string_
|
||||
|
||||
### Empty
|
||||
|
||||
### NewInvoiceResponse
|
||||
### SetMockInvoiceAsPaidRequest
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### DecodeInvoiceRequest
|
||||
### AddAppUserInvoiceRequest
|
||||
- __receiver_identifier__: _string_
|
||||
- __payer_identifier__: _string_
|
||||
- __http_callback_url__: _string_
|
||||
- __invoice_req__: _[NewInvoiceRequest](#NewInvoiceRequest)_
|
||||
|
||||
### PayAppUserInvoiceRequest
|
||||
- __user_identifier__: _string_
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### AuthUserResponse
|
||||
### SendAppUserToAppPaymentRequest
|
||||
- __from_user_identifier__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### PayAddressRequest
|
||||
- __address__: _string_
|
||||
- __amoutSats__: _number_
|
||||
- __targetConf__: _number_
|
||||
|
||||
### UserOperation
|
||||
- __paidAtUnix__: _number_
|
||||
- __type__: _[UserOperationType](#UserOperationType)_
|
||||
- __inbound__: _boolean_
|
||||
- __amount__: _number_
|
||||
|
||||
### UserInfo
|
||||
- __userId__: _string_
|
||||
- __authToken__: _string_
|
||||
- __balance__: _number_
|
||||
|
||||
### GetUserOperationsResponse
|
||||
- __latestOutgoingInvoiceOperations__: _[UserOperations](#UserOperations)_
|
||||
- __latestIncomingInvoiceOperations__: _[UserOperations](#UserOperations)_
|
||||
- __latestOutgoingTxOperations__: _[UserOperations](#UserOperations)_
|
||||
- __latestIncomingTxOperations__: _[UserOperations](#UserOperations)_
|
||||
### Product
|
||||
- __id__: _string_
|
||||
- __name__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### LndGetInfoRequest
|
||||
- __nodeId__: _number_
|
||||
### SendAppUserToAppUserPaymentRequest
|
||||
- __from_user_identifier__: _string_
|
||||
- __to_user_identifier__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### LndGetInfoResponse
|
||||
- __alias__: _string_
|
||||
### SetMockAppUserBalanceRequest
|
||||
- __user_identifier__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### AddUserResponse
|
||||
- __userId__: _string_
|
||||
- __authToken__: _string_
|
||||
### NewAddressResponse
|
||||
- __address__: _string_
|
||||
|
||||
### LnurlWithdrawInfoResponse
|
||||
- __tag__: _string_
|
||||
|
|
@ -359,42 +522,59 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __balanceCheck__: _string_
|
||||
- __payLink__: _string_
|
||||
|
||||
### HandleLnurlPayResponse
|
||||
- __pr__: _string_
|
||||
- __routes__: ARRAY of: _[Empty](#Empty)_
|
||||
|
||||
### AddUserResponse
|
||||
- __userId__: _string_
|
||||
- __authToken__: _string_
|
||||
|
||||
### UserOperations
|
||||
- __fromIndex__: _number_
|
||||
- __toIndex__: _number_
|
||||
- __operations__: ARRAY of: _[UserOperation](#UserOperation)_
|
||||
|
||||
### AuthAppRequest
|
||||
- __name__: _string_
|
||||
- __allow_user_creation__: _boolean_ *this field is optional
|
||||
|
||||
### GetAppUserRequest
|
||||
- __user_identifier__: _string_
|
||||
|
||||
### NewInvoiceResponse
|
||||
- __invoice__: _string_
|
||||
|
||||
### DecodeInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
|
||||
### OpenChannelResponse
|
||||
- __channelId__: _string_
|
||||
|
||||
### AddAppRequest
|
||||
- __name__: _string_
|
||||
- __allow_user_creation__: _boolean_
|
||||
|
||||
### PayAppUserInvoiceResponse
|
||||
- __preimage__: _string_
|
||||
- __amount_paid__: _number_
|
||||
|
||||
### PayInvoiceResponse
|
||||
- __preimage__: _string_
|
||||
- __amount_paid__: _number_
|
||||
|
||||
### GetProductBuyLinkResponse
|
||||
- __link__: _string_
|
||||
|
||||
### AddUserRequest
|
||||
- __callbackUrl__: _string_
|
||||
- __name__: _string_
|
||||
- __secret__: _string_
|
||||
### LndGetInfoRequest
|
||||
- __nodeId__: _number_
|
||||
|
||||
### UserOperation
|
||||
- __paidAtUnix__: _number_
|
||||
- __type__: _[UserOperationType](#UserOperationType)_
|
||||
- __inbound__: _boolean_
|
||||
- __amount__: _number_
|
||||
### LndGetInfoResponse
|
||||
- __alias__: _string_
|
||||
|
||||
### NewAddressRequest
|
||||
- __addressType__: _[AddressType](#AddressType)_
|
||||
|
||||
### NewAddressResponse
|
||||
- __address__: _string_
|
||||
|
||||
### PayInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### PayAddressResponse
|
||||
- __txId__: _string_
|
||||
|
||||
### DecodeInvoiceResponse
|
||||
- __amount__: _number_
|
||||
|
||||
### LnurlPayInfoResponse
|
||||
- __tag__: _string_
|
||||
- __callback__: _string_
|
||||
- __maxSendable__: _number_
|
||||
- __minSendable__: _number_
|
||||
- __metadata__: _string_
|
||||
### AuthApp
|
||||
- __app__: _[Application](#Application)_
|
||||
- __auth_token__: _string_
|
||||
## Enums
|
||||
### The enumerators used in the messages
|
||||
|
||||
|
|
@ -408,3 +588,5 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __OUTGOING_TX__
|
||||
- __INCOMING_INVOICE__
|
||||
- __OUTGOING_INVOICE__
|
||||
- __OUTGOING_USER_TO_USER__
|
||||
- __INCOMING_USER_TO_USER__
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,9 +7,12 @@ export type Logger = { log: (v: any) => void, error: (v: any) => void }
|
|||
export type ServerOptions = {
|
||||
allowCors?: true
|
||||
staticFiles?: string
|
||||
allowNotImplementedMethods?: number
|
||||
allowNotImplementedMethods?: true
|
||||
logger?: Logger
|
||||
throwErrors?: true
|
||||
overrides?: MethodsOverride
|
||||
logMethod?: true
|
||||
logBody?: true
|
||||
GuestAuthGuard: (authorizationHeader?: string) => Promise<Types.GuestContext>
|
||||
UserAuthGuard: (authorizationHeader?: string) => Promise<Types.UserContext>
|
||||
AdminAuthGuard: (authorizationHeader?: string) => Promise<Types.AdminContext>
|
||||
|
|
@ -26,6 +29,8 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
}
|
||||
app.use(json())
|
||||
app.use(urlencoded({ extended: true }))
|
||||
if (opts.logMethod) app.use((req, _, next) => { console.log(req.method, req.path); if (opts.logBody) console.log(req.body); next() })
|
||||
const overrides = opts.overrides || {} as MethodsOverride
|
||||
if (!opts.allowNotImplementedMethods && !methods.Health) throw new Error('method: Health is not implemented')
|
||||
app.get('/api/health', async (req, res) => {
|
||||
try {
|
||||
|
|
@ -34,7 +39,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.Health({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK'})
|
||||
if (overrides.Health_Override) await overrides.Health_Override(res); else 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.EncryptionExchange) throw new Error('method: EncryptionExchange is not implemented')
|
||||
|
|
@ -48,7 +53,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.EncryptionExchange({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK'})
|
||||
if (overrides.EncryptionExchange_Override) await overrides.EncryptionExchange_Override(res); else 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.LndGetInfo) throw new Error('method: LndGetInfo is not implemented')
|
||||
|
|
@ -62,7 +67,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.LndGetInfo({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.LndGetInfo_Override) await overrides.LndGetInfo_Override(res, response); else 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.SetMockInvoiceAsPaid) throw new Error('method: SetMockInvoiceAsPaid is not implemented')
|
||||
|
|
@ -76,7 +81,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.SetMockInvoiceAsPaid({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK'})
|
||||
if (overrides.SetMockInvoiceAsPaid_Override) await overrides.SetMockInvoiceAsPaid_Override(res); else 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.AddApp) throw new Error('method: AddApp is not implemented')
|
||||
|
|
@ -85,12 +90,12 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
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.AuthAppRequestValidate(request)
|
||||
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})
|
||||
if (overrides.AddApp_Override) await overrides.AddApp_Override(res, response); else 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.AuthApp) throw new Error('method: AuthApp is not implemented')
|
||||
|
|
@ -104,7 +109,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AuthApp({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.AuthApp_Override) await overrides.AuthApp_Override(res, response); else 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.GetApp) throw new Error('method: GetApp is not implemented')
|
||||
|
|
@ -115,7 +120,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetApp({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.GetApp_Override) await overrides.GetApp_Override(res, response); else 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')
|
||||
|
|
@ -129,7 +134,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddAppUser({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.AddAppUser_Override) await overrides.AddAppUser_Override(res, response); else 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')
|
||||
|
|
@ -143,7 +148,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddAppInvoice({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.AddAppInvoice_Override) await overrides.AddAppInvoice_Override(res, response); else 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')
|
||||
|
|
@ -157,7 +162,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddAppUserInvoice({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.AddAppUserInvoice_Override) await overrides.AddAppUserInvoice_Override(res, response); else 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')
|
||||
|
|
@ -171,7 +176,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetAppUser({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.GetAppUser_Override) await overrides.GetAppUser_Override(res, response); else 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')
|
||||
|
|
@ -185,7 +190,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.PayAppUserInvoice({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.PayAppUserInvoice_Override) await overrides.PayAppUserInvoice_Override(res, response); else 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')
|
||||
|
|
@ -199,7 +204,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.SendAppUserToAppUserPayment({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK'})
|
||||
if (overrides.SendAppUserToAppUserPayment_Override) await overrides.SendAppUserToAppUserPayment_Override(res); else 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')
|
||||
|
|
@ -213,7 +218,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.SendAppUserToAppPayment({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK'})
|
||||
if (overrides.SendAppUserToAppPayment_Override) await overrides.SendAppUserToAppPayment_Override(res); else 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.GetAppUserLNURLInfo) throw new Error('method: GetAppUserLNURLInfo is not implemented')
|
||||
|
|
@ -227,7 +232,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetAppUserLNURLInfo({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.GetAppUserLNURLInfo_Override) await overrides.GetAppUserLNURLInfo_Override(res, response); else 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.SetMockAppUserBalance) throw new Error('method: SetMockAppUserBalance is not implemented')
|
||||
|
|
@ -241,7 +246,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.SetMockAppUserBalance({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK'})
|
||||
if (overrides.SetMockAppUserBalance_Override) await overrides.SetMockAppUserBalance_Override(res); else 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.SetMockAppBalance) throw new Error('method: SetMockAppBalance is not implemented')
|
||||
|
|
@ -255,7 +260,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.SetMockAppBalance({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK'})
|
||||
if (overrides.SetMockAppBalance_Override) await overrides.SetMockAppBalance_Override(res); else 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')
|
||||
|
|
@ -269,7 +274,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddUser({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.AddUser_Override) await overrides.AddUser_Override(res, response); else 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.AuthUser) throw new Error('method: AuthUser is not implemented')
|
||||
|
|
@ -283,7 +288,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AuthUser({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.AuthUser_Override) await overrides.AuthUser_Override(res, response); else 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.GetUserInfo) throw new Error('method: GetUserInfo is not implemented')
|
||||
|
|
@ -294,7 +299,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetUserInfo({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.GetUserInfo_Override) await overrides.GetUserInfo_Override(res, response); else 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.AddProduct) throw new Error('method: AddProduct is not implemented')
|
||||
|
|
@ -308,7 +313,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddProduct({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.AddProduct_Override) await overrides.AddProduct_Override(res, response); else 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.NewProductInvoice) throw new Error('method: NewProductInvoice is not implemented')
|
||||
|
|
@ -319,7 +324,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.NewProductInvoice({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.NewProductInvoice_Override) await overrides.NewProductInvoice_Override(res, response); else 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.GetUserOperations) throw new Error('method: GetUserOperations is not implemented')
|
||||
|
|
@ -333,7 +338,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetUserOperations({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.GetUserOperations_Override) await overrides.GetUserOperations_Override(res, response); else 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.NewAddress) throw new Error('method: NewAddress is not implemented')
|
||||
|
|
@ -347,7 +352,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.NewAddress({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.NewAddress_Override) await overrides.NewAddress_Override(res, response); else 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.PayAddress) throw new Error('method: PayAddress is not implemented')
|
||||
|
|
@ -361,7 +366,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.PayAddress({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.PayAddress_Override) await overrides.PayAddress_Override(res, response); else 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.NewInvoice) throw new Error('method: NewInvoice is not implemented')
|
||||
|
|
@ -375,7 +380,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.NewInvoice({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.NewInvoice_Override) await overrides.NewInvoice_Override(res, response); else 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.DecodeInvoice) throw new Error('method: DecodeInvoice is not implemented')
|
||||
|
|
@ -389,7 +394,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.DecodeInvoice({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.DecodeInvoice_Override) await overrides.DecodeInvoice_Override(res, response); else 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.PayInvoice) throw new Error('method: PayInvoice is not implemented')
|
||||
|
|
@ -403,7 +408,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.PayInvoice({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.PayInvoice_Override) await overrides.PayInvoice_Override(res, response); else 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.OpenChannel) throw new Error('method: OpenChannel is not implemented')
|
||||
|
|
@ -417,7 +422,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.OpenChannel({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.OpenChannel_Override) await overrides.OpenChannel_Override(res, response); else 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.GetLnurlWithdrawLink) throw new Error('method: GetLnurlWithdrawLink is not implemented')
|
||||
|
|
@ -428,7 +433,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetLnurlWithdrawLink({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.GetLnurlWithdrawLink_Override) await overrides.GetLnurlWithdrawLink_Override(res, response); else 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.GetLnurlWithdrawInfo) throw new Error('method: GetLnurlWithdrawInfo is not implemented')
|
||||
|
|
@ -439,7 +444,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetLnurlWithdrawInfo({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.GetLnurlWithdrawInfo_Override) await overrides.GetLnurlWithdrawInfo_Override(res, response); else 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.HandleLnurlWithdraw) throw new Error('method: HandleLnurlWithdraw is not implemented')
|
||||
|
|
@ -450,7 +455,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.HandleLnurlWithdraw({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK'})
|
||||
if (overrides.HandleLnurlWithdraw_Override) await overrides.HandleLnurlWithdraw_Override(res); else 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.GetLnurlPayInfo) throw new Error('method: GetLnurlPayInfo is not implemented')
|
||||
|
|
@ -461,7 +466,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetLnurlPayInfo({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.GetLnurlPayInfo_Override) await overrides.GetLnurlPayInfo_Override(res, response); else 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.HandleLnurlPay) throw new Error('method: HandleLnurlPay is not implemented')
|
||||
|
|
@ -472,7 +477,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.HandleLnurlPay({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.HandleLnurlPay_Override) await overrides.HandleLnurlPay_Override(res, response); else 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.GetLNURLChannelLink) throw new Error('method: GetLNURLChannelLink is not implemented')
|
||||
|
|
@ -483,11 +488,12 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetLNURLChannelLink({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK', ...response})
|
||||
if (overrides.GetLNURLChannelLink_Override) await overrides.GetLNURLChannelLink_Override(res, response); else 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.staticFiles) {
|
||||
app.use(express.static(opts.staticFiles))
|
||||
app.get('*', function (_, res) { res.sendFile('index.html', { root: opts.staticFiles })})
|
||||
}
|
||||
var server: { close: () => void } | undefined
|
||||
return {
|
||||
|
|
@ -495,3 +501,40 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
Listen: (port: number) => { server = app.listen(port, () => logger.log('Example app listening on port ' + port)) }
|
||||
}
|
||||
}
|
||||
export type MethodsOverride = {
|
||||
Health_Override?: (httpRes:Response) => Promise<void>
|
||||
EncryptionExchange_Override?: (httpRes:Response) => Promise<void>
|
||||
LndGetInfo_Override?: (httpRes:Response, handlerRes:Types.LndGetInfoResponse) => Promise<void>
|
||||
SetMockInvoiceAsPaid_Override?: (httpRes:Response) => Promise<void>
|
||||
AddApp_Override?: (httpRes:Response, handlerRes:Types.AuthApp) => Promise<void>
|
||||
AuthApp_Override?: (httpRes:Response, handlerRes:Types.AuthApp) => Promise<void>
|
||||
GetApp_Override?: (httpRes:Response, handlerRes:Types.Application) => Promise<void>
|
||||
AddAppUser_Override?: (httpRes:Response, handlerRes:Types.AppUser) => Promise<void>
|
||||
AddAppInvoice_Override?: (httpRes:Response, handlerRes:Types.NewInvoiceResponse) => Promise<void>
|
||||
AddAppUserInvoice_Override?: (httpRes:Response, handlerRes:Types.NewInvoiceResponse) => Promise<void>
|
||||
GetAppUser_Override?: (httpRes:Response, handlerRes:Types.AppUser) => Promise<void>
|
||||
PayAppUserInvoice_Override?: (httpRes:Response, handlerRes:Types.PayAppUserInvoiceResponse) => Promise<void>
|
||||
SendAppUserToAppUserPayment_Override?: (httpRes:Response) => Promise<void>
|
||||
SendAppUserToAppPayment_Override?: (httpRes:Response) => Promise<void>
|
||||
GetAppUserLNURLInfo_Override?: (httpRes:Response, handlerRes:Types.LnurlPayInfoResponse) => Promise<void>
|
||||
SetMockAppUserBalance_Override?: (httpRes:Response) => Promise<void>
|
||||
SetMockAppBalance_Override?: (httpRes:Response) => Promise<void>
|
||||
AddUser_Override?: (httpRes:Response, handlerRes:Types.AddUserResponse) => Promise<void>
|
||||
AuthUser_Override?: (httpRes:Response, handlerRes:Types.AuthUserResponse) => Promise<void>
|
||||
GetUserInfo_Override?: (httpRes:Response, handlerRes:Types.UserInfo) => Promise<void>
|
||||
AddProduct_Override?: (httpRes:Response, handlerRes:Types.Product) => Promise<void>
|
||||
NewProductInvoice_Override?: (httpRes:Response, handlerRes:Types.NewInvoiceResponse) => Promise<void>
|
||||
GetUserOperations_Override?: (httpRes:Response, handlerRes:Types.GetUserOperationsResponse) => Promise<void>
|
||||
NewAddress_Override?: (httpRes:Response, handlerRes:Types.NewAddressResponse) => Promise<void>
|
||||
PayAddress_Override?: (httpRes:Response, handlerRes:Types.PayAddressResponse) => Promise<void>
|
||||
NewInvoice_Override?: (httpRes:Response, handlerRes:Types.NewInvoiceResponse) => Promise<void>
|
||||
DecodeInvoice_Override?: (httpRes:Response, handlerRes:Types.DecodeInvoiceResponse) => Promise<void>
|
||||
PayInvoice_Override?: (httpRes:Response, handlerRes:Types.PayInvoiceResponse) => Promise<void>
|
||||
OpenChannel_Override?: (httpRes:Response, handlerRes:Types.OpenChannelResponse) => Promise<void>
|
||||
GetLnurlWithdrawLink_Override?: (httpRes:Response, handlerRes:Types.LnurlLinkResponse) => Promise<void>
|
||||
GetLnurlWithdrawInfo_Override?: (httpRes:Response, handlerRes:Types.LnurlWithdrawInfoResponse) => Promise<void>
|
||||
HandleLnurlWithdraw_Override?: (httpRes:Response) => Promise<void>
|
||||
GetLnurlPayInfo_Override?: (httpRes:Response, handlerRes:Types.LnurlPayInfoResponse) => Promise<void>
|
||||
HandleLnurlPay_Override?: (httpRes:Response, handlerRes:Types.HandleLnurlPayResponse) => Promise<void>
|
||||
GetLNURLChannelLink_Override?: (httpRes:Response, handlerRes:Types.LnurlLinkResponse) => Promise<void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddApp: async (request: Types.AuthAppRequest): Promise<ResultError | ({ status: 'OK' }& Types.AuthApp)> => {
|
||||
AddApp: async (request: Types.AddAppRequest): Promise<ResultError | ({ status: 'OK' }& Types.AuthApp)> => {
|
||||
const auth = await params.retrieveAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveAdminAuth() returned null')
|
||||
let finalRoute = '/api/admin/app/add'
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
|
@ -96,7 +96,7 @@ service LightningPub {
|
|||
|
||||
// <App>
|
||||
|
||||
rpc AddApp(structs.AuthAppRequest) returns (structs.AuthApp) {
|
||||
rpc AddApp(structs.AddAppRequest) returns (structs.AuthApp) {
|
||||
option (auth_type) = "Admin";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/admin/app/add";
|
||||
|
|
@ -184,13 +184,13 @@ service LightningPub {
|
|||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/auth";
|
||||
}
|
||||
// USER
|
||||
rpc GetUserInfo(structs.Empty)returns(structs.UserInfo){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/info";
|
||||
option (nostr) = true;
|
||||
}
|
||||
// USER
|
||||
rpc AddProduct(structs.AddProductRequest) returns (structs.Product){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
|
|
|
|||
|
|
@ -25,15 +25,23 @@ message LndGetInfoResponse {
|
|||
string alias = 1;
|
||||
}
|
||||
|
||||
message AddAppRequest {
|
||||
string name = 1;
|
||||
bool allow_user_creation = 2;
|
||||
}
|
||||
|
||||
message AuthAppRequest {
|
||||
string name = 1;
|
||||
optional bool allow_user_creation = 2;
|
||||
}
|
||||
|
||||
message Application {
|
||||
string name = 1;
|
||||
string id = 2;
|
||||
int64 balance = 3;
|
||||
string npub = 4;
|
||||
}
|
||||
|
||||
message AuthApp {
|
||||
Application app = 1;
|
||||
string auth_token = 2;
|
||||
|
|
@ -212,6 +220,7 @@ message UserInfo{
|
|||
string userId = 1;
|
||||
int64 balance = 2;
|
||||
}
|
||||
|
||||
message GetUserOperationsRequest{
|
||||
int64 latestIncomingInvoice = 1;
|
||||
int64 latestOutgoingInvoice = 2;
|
||||
|
|
|
|||
|
|
@ -1,140 +0,0 @@
|
|||
import 'dotenv/config' // TODO - test env
|
||||
import crypto from 'crypto';
|
||||
import { generatePrivateKey, getPublicKey } from 'nostr-tools';
|
||||
import NewServer from '../proto/autogenerated/ts/express_server.js'
|
||||
import NewClient from '../proto/autogenerated/ts/http_client.js'
|
||||
import serverOptions from './auth.js';
|
||||
import GetServerMethods from './services/serverMethods/index.js'
|
||||
import Main, { LoadMainSettingsFromEnv } from './services/main/index.js'
|
||||
import * as Types from '../proto/autogenerated/ts/types.js';
|
||||
import nostrMiddleware from './nostrMiddleware.js'
|
||||
import { LoadNosrtSettingsFromEnv } from './services/nostr/index.js';
|
||||
import { expect } from 'chai';
|
||||
import NostrHandler from './services/nostr/index.js'
|
||||
import NewNostrClient from '../proto/autogenerated/ts/nostr_client.js'
|
||||
import { NostrRequest } from '../proto/autogenerated/ts/nostr_transport.js';
|
||||
|
||||
|
||||
const settings = LoadNosrtSettingsFromEnv(true)
|
||||
|
||||
const clientPrivateKey = generatePrivateKey()
|
||||
const clientPublicKey = getPublicKey(clientPrivateKey)
|
||||
|
||||
const serverPrivateKey = generatePrivateKey()
|
||||
const serverPublicKey = getPublicKey(serverPrivateKey)
|
||||
|
||||
const testPort = 4000
|
||||
var userAuthHeader = ""
|
||||
const client = NewClient({
|
||||
baseUrl: `http://localhost:${testPort}`,
|
||||
retrieveAdminAuth: async () => (""),
|
||||
retrieveGuestAuth: async () => (""),
|
||||
retrieveUserAuth: async () => userAuthHeader,
|
||||
retrieveAppAuth: async () => (""),
|
||||
decryptCallback: async (b) => b,
|
||||
encryptCallback: async (b) => b,
|
||||
deviceId: "device0"
|
||||
})
|
||||
const clientCbs: Record<string, (res: any) => void> = {}
|
||||
const clientNostrHandler = new NostrHandler({
|
||||
allowedPubs: [],
|
||||
privateKey: clientPrivateKey,
|
||||
publicKey: clientPublicKey,
|
||||
relays: settings.relays
|
||||
}, (e) => {
|
||||
const res = JSON.parse(e.content) as { requestId: string }
|
||||
if (clientCbs[res.requestId]) {
|
||||
const cb = clientCbs[res.requestId]
|
||||
cb(res)
|
||||
delete clientCbs[res.requestId]
|
||||
}
|
||||
})
|
||||
const clientSend = (to: string, message: NostrRequest): Promise<any> => {
|
||||
console.log("sending", message)
|
||||
if (!message.requestId) {
|
||||
message.requestId = crypto.randomBytes(20).toString('hex')
|
||||
}
|
||||
const reqId = message.requestId
|
||||
if (clientCbs[reqId]) {
|
||||
throw new Error("request was already sent")
|
||||
}
|
||||
clientNostrHandler.Send(to, JSON.stringify(message))
|
||||
return new Promise(res => {
|
||||
clientCbs[reqId] = (response: any) => {
|
||||
res(response)
|
||||
}
|
||||
})
|
||||
}
|
||||
const clientNostr = NewNostrClient({
|
||||
pubDestination: serverPublicKey,
|
||||
retrieveNostrUserAuth: async () => { return clientPublicKey }
|
||||
}, clientSend)
|
||||
/* new NostrHandler({
|
||||
allowedPubs: [],
|
||||
privateKey: clientPrivateKey,
|
||||
publicKey: clientPublicKey,
|
||||
relays: settings.relays
|
||||
}, (event) => {
|
||||
console.log(event.content)
|
||||
})*/
|
||||
const mainSettings = LoadMainSettingsFromEnv(true)
|
||||
const mainHandler = new Main(mainSettings) // TODO - test env file
|
||||
const serverMethods = GetServerMethods(mainHandler)
|
||||
const serverNostr = nostrMiddleware(serverMethods, mainHandler, {
|
||||
allowedPubs: [clientPublicKey],
|
||||
privateKey: serverPrivateKey,
|
||||
publicKey: serverPublicKey,
|
||||
relays: settings.relays
|
||||
})
|
||||
const server = NewServer(serverMethods, { ...serverOptions(mainHandler), throwErrors: true })
|
||||
export const ignore = false
|
||||
|
||||
export const setup = async () => {
|
||||
await mainHandler.storage.Connect()
|
||||
await mainHandler.lnd.Warmup()
|
||||
server.Listen(testPort)
|
||||
}
|
||||
export const teardown = async () => {
|
||||
clientNostrHandler.Stop()
|
||||
serverNostr.Stop()
|
||||
mainHandler.lnd.Stop()
|
||||
server.Close()
|
||||
}
|
||||
|
||||
|
||||
export default async (d: (message: string, failure?: boolean) => void) => {
|
||||
await client.Health()
|
||||
d("health ok")
|
||||
|
||||
console.log(await client.LndGetInfo({ nodeId: 0 }))
|
||||
d("lnd info ok")
|
||||
|
||||
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.userStorage.GetUser(res.userId)
|
||||
console.log(user)
|
||||
userAuthHeader = res.authToken
|
||||
d("create user ok")
|
||||
|
||||
console.log(await client.NewAddress({ addressType: Types.AddressType.WITNESS_PUBKEY_HASH }))
|
||||
d("new address ok")
|
||||
await new Promise(res => setTimeout(res, 2000))
|
||||
//clientNostr.Send(serverPublicKey, JSON.stringify({ requestId: "a", method: '/api/user/chain/new', body: { address_type: 'WITNESS_PUBKEY_HASH' } }))
|
||||
const nostrRes = await clientNostr.NewAddress({ addressType: Types.AddressType.WITNESS_PUBKEY_HASH })
|
||||
console.log(nostrRes)
|
||||
d("nostr ok")
|
||||
const lnurlWithdrawLink = await client.GetLnurlWithdrawLink()
|
||||
if (lnurlWithdrawLink.status === 'ERROR') throw new Error(lnurlWithdrawLink.reason)
|
||||
const lnurlWithdrawInfo = await client.GetLnurlWithdrawInfo({ k1: lnurlWithdrawLink.k1 })
|
||||
const expectedInfo: Partial<Types.LnurlWithdrawInfoResponse> & { status: 'OK' } = {
|
||||
status: 'OK',
|
||||
tag: "withdrawRequest",
|
||||
defaultDescription: "lnurl withdraw from lightning.pub",
|
||||
maxWithdrawable: 0,
|
||||
minWithdrawable: 0,
|
||||
}
|
||||
expect(lnurlWithdrawInfo).to.deep.include(expectedInfo)
|
||||
d("lnurl info ok")
|
||||
|
||||
}
|
||||
12
src/index.ts
12
src/index.ts
|
|
@ -13,7 +13,17 @@ const start = async () => {
|
|||
await mainHandler.lnd.Warmup()
|
||||
const serverMethods = GetServerMethods(mainHandler)
|
||||
const nostrSettings = LoadNosrtSettingsFromEnv()
|
||||
nostrMiddleware(serverMethods, mainHandler, nostrSettings)
|
||||
const appsData = await mainHandler.storage.applicationStorage.GetApplications()
|
||||
|
||||
const apps = await Promise.all(appsData.map(app => {
|
||||
if (!app.nostr_private_key) { // TMP --
|
||||
return mainHandler.storage.applicationStorage.GenerateApplicationKeys(app);
|
||||
} // --
|
||||
else {
|
||||
return { privateKey: app.nostr_private_key, publicKey: app.nostr_public_key, appId: app.app_id, name: app.name }
|
||||
}
|
||||
}))
|
||||
nostrMiddleware(serverMethods, mainHandler, { ...nostrSettings, apps })
|
||||
const Server = NewServer(serverMethods, serverOptions(mainHandler))
|
||||
if (process.argv[2] === 'unlock') {
|
||||
const u = process.argv[3]
|
||||
|
|
|
|||
|
|
@ -7,14 +7,10 @@ const handledRequests: string[] = [] // TODO: - big memory leak here, add TTL
|
|||
|
||||
export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSettings: NostrSettings) => {
|
||||
const nostrTransport = NewNostrTransport(serverMethods, {
|
||||
NostrUserAuthGuard: async pub => {
|
||||
if (!pub || !nostrSettings.allowedPubs.includes(pub)) {
|
||||
throw new Error("nostr pub invalid or not allowed" + pub)
|
||||
}
|
||||
let nostrUser = await mainHandler.storage.userStorage.FindNostrUser(pub)
|
||||
if (!nostrUser) { // TODO: add POW
|
||||
nostrUser = await mainHandler.storage.userStorage.AddNostrUser(pub)
|
||||
}
|
||||
NostrUserAuthGuard: async (appId, pub) => {
|
||||
console.log({ appId })
|
||||
const app = await mainHandler.storage.applicationStorage.GetApplication(appId || "")
|
||||
let nostrUser = await mainHandler.storage.applicationStorage.GetOrCreateNostrAppUser(app, pub || "")
|
||||
return { user_id: nostrUser.user.user_id }
|
||||
}
|
||||
})
|
||||
|
|
@ -27,8 +23,8 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
|||
console.error("invalid json event received", event.content)
|
||||
return
|
||||
}
|
||||
nostrTransport(j, res => {
|
||||
nostr.Send(event.pub, JSON.stringify({ ...res, requestId: j.requestId }))
|
||||
nostrTransport({ ...j, appId: event.appId }, res => {
|
||||
nostr.Send(event.appId, event.pub, JSON.stringify({ ...res, requestId: j.requestId }))
|
||||
})
|
||||
})
|
||||
return { Stop: nostr.Stop }
|
||||
|
|
|
|||
|
|
@ -47,15 +47,16 @@ export default class {
|
|||
}
|
||||
|
||||
|
||||
async AddApp(req: Types.AuthAppRequest): Promise<Types.AuthApp> {
|
||||
const app = await this.storage.applicationStorage.AddApplication(req.name)
|
||||
async AddApp(req: Types.AddAppRequest): Promise<Types.AuthApp> {
|
||||
const app = await this.storage.applicationStorage.AddApplication(req.name, req.allow_user_creation)
|
||||
getLogger({ appName: app.name })("app created")
|
||||
|
||||
return {
|
||||
app: {
|
||||
id: app.app_id,
|
||||
name: app.name,
|
||||
balance: app.owner.balance_sats
|
||||
balance: app.owner.balance_sats,
|
||||
npub: app.nostr_public_key
|
||||
},
|
||||
auth_token: this.SignAppToken(app.app_id)
|
||||
}
|
||||
|
|
@ -63,11 +64,15 @@ export default class {
|
|||
|
||||
async AuthApp(req: Types.AuthAppRequest): Promise<Types.AuthApp> {
|
||||
const app = await this.storage.applicationStorage.GetApplicationByName(req.name)
|
||||
if (typeof req.allow_user_creation === 'boolean') {
|
||||
await this.storage.applicationStorage.UpdateApplication(app, { allow_user_creation: req.allow_user_creation })
|
||||
}
|
||||
return {
|
||||
app: {
|
||||
id: app.app_id,
|
||||
name: app.name,
|
||||
balance: app.owner.balance_sats
|
||||
balance: app.owner.balance_sats,
|
||||
npub: app.nostr_public_key
|
||||
},
|
||||
auth_token: this.SignAppToken(app.app_id)
|
||||
}
|
||||
|
|
@ -78,7 +83,8 @@ export default class {
|
|||
return {
|
||||
name: app.name,
|
||||
id: app.app_id,
|
||||
balance: app.owner.balance_sats
|
||||
balance: app.owner.balance_sats,
|
||||
npub: app.nostr_public_key
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@ export default class {
|
|||
}
|
||||
|
||||
DecodeUserToken(token?: string): string {
|
||||
throw new Error("users methods temporarely disabled")
|
||||
/*if (!token) throw new Error("empty user token provided")
|
||||
if (!token) throw new Error("empty user token provided")
|
||||
let t = token
|
||||
if (token.startsWith("Bearer ")) {
|
||||
t = token.substring("Bearer ".length)
|
||||
|
|
@ -26,7 +25,7 @@ export default class {
|
|||
if (!decoded.userId) {
|
||||
throw new Error("the provided token is not an app token")
|
||||
}
|
||||
return decoded.userId*/
|
||||
return decoded.userId
|
||||
}
|
||||
|
||||
async AddBasicUser(req: Types.AddUserRequest): Promise<Types.AddUserResponse> {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import { SimplePool, Sub, Event, UnsignedEvent, nip04, getEventHash, signEvent } from 'nostr-tools'
|
||||
//import { SimplePool, Sub, Event, UnsignedEvent, getEventHash, signEvent } from 'nostr-tools'
|
||||
import { SimplePool, Sub, Event, UnsignedEvent, getEventHash, finishEvent, relayInit } from './tools/index.js'
|
||||
import { encryptData, decryptData, getSharedSecret, decodePayload, encodePayload } from './nip44.js'
|
||||
const handledEvents: string[] = [] // TODO: - big memory leak here, add TTL
|
||||
type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string }
|
||||
export type NostrSettings = {
|
||||
privateKey: string
|
||||
publicKey: string
|
||||
apps: AppInfo[]
|
||||
relays: string[]
|
||||
allowedPubs: string[]
|
||||
}
|
||||
export type NostrEvent = {
|
||||
id: string
|
||||
pub: string
|
||||
content: string
|
||||
appId: string
|
||||
}
|
||||
type SettingsRequest = {
|
||||
type: 'settings'
|
||||
|
|
@ -18,6 +20,7 @@ type SettingsRequest = {
|
|||
|
||||
type SendRequest = {
|
||||
type: 'send'
|
||||
appId: string
|
||||
pub: string
|
||||
message: string
|
||||
}
|
||||
|
|
@ -43,7 +46,7 @@ process.on("message", (message: ChildProcessRequest) => {
|
|||
initSubprocessHandler(message.settings)
|
||||
break
|
||||
case 'send':
|
||||
sendToNostr(message.pub, message.message)
|
||||
sendToNostr(message.appId, message.pub, message.message)
|
||||
break
|
||||
default:
|
||||
console.error("unknown nostr request", message)
|
||||
|
|
@ -62,30 +65,47 @@ const initSubprocessHandler = (settings: NostrSettings) => {
|
|||
})
|
||||
})
|
||||
}
|
||||
const sendToNostr = (pub: string, message: string) => {
|
||||
const sendToNostr = (appId: string, pub: string, message: string) => {
|
||||
if (!subProcessHandler) {
|
||||
console.error("nostr was not initialized")
|
||||
return
|
||||
}
|
||||
subProcessHandler.Send(pub, message)
|
||||
subProcessHandler.Send(appId, pub, message)
|
||||
}
|
||||
send({ type: 'ready' })
|
||||
|
||||
export default class Handler {
|
||||
pool = new SimplePool()
|
||||
settings: NostrSettings
|
||||
sub: Sub
|
||||
subs: Sub[] = []
|
||||
constructor(settings: NostrSettings, eventCallback: (event: NostrEvent) => void) {
|
||||
this.settings = settings
|
||||
console.log(settings)
|
||||
this.sub = this.pool.sub(settings.relays, [
|
||||
this.settings.apps.forEach(app => {
|
||||
this.SubForApp(app, eventCallback)
|
||||
})
|
||||
}
|
||||
|
||||
async SubForApp(appInfo: AppInfo, eventCallback: (event: NostrEvent) => void) {
|
||||
const relay = relayInit(this.settings.relays[0])
|
||||
relay.on('connect', () => {
|
||||
console.log(`connected to ${relay.url}`)
|
||||
})
|
||||
relay.on('error', () => {
|
||||
console.log(`failed to connect to ${relay.url}`)
|
||||
})
|
||||
|
||||
await relay.connect()
|
||||
console.log("relay connected")
|
||||
const sub = relay.sub([
|
||||
{
|
||||
since: Math.ceil(Date.now() / 1000),
|
||||
kinds: [4],
|
||||
'#p': [settings.publicKey],
|
||||
'#p': [appInfo.publicKey],
|
||||
}
|
||||
])
|
||||
this.sub.on("event", async (e) => {
|
||||
sub.on("event", async (e) => {
|
||||
console.log({ nostrEvent: e })
|
||||
if (e.kind !== 4 || !e.pubkey) {
|
||||
return
|
||||
}
|
||||
|
|
@ -96,22 +116,47 @@ export default class Handler {
|
|||
return
|
||||
}
|
||||
handledEvents.push(eventId)
|
||||
eventCallback({ id: eventId, content: await nip04.decrypt(this.settings.privateKey, e.pubkey, e.content), pub: e.pubkey })
|
||||
const decoded = decodePayload(e.content)
|
||||
const content = await decryptData(decoded, getSharedSecret(appInfo.privateKey, e.pubkey))
|
||||
eventCallback({ id: eventId, content, pub: e.pubkey, appId: appInfo.appId })
|
||||
//eventCallback({ id: eventId, content: await nip04.decrypt(appInfo.privateKey, e.pubkey, e.content), pub: e.pubkey, appId: appInfo.appId })
|
||||
})
|
||||
this.subs.push(sub)
|
||||
}
|
||||
|
||||
async Send(appId: string, pubKey: string, message: string) {
|
||||
const appInfo = this.GetAppKeys({ appId })
|
||||
const decoded = await encryptData(message, getSharedSecret(appInfo.privateKey, pubKey))
|
||||
const content = encodePayload(decoded)
|
||||
const event: UnsignedEvent = {
|
||||
content,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 4,
|
||||
pubkey: appInfo.publicKey,
|
||||
tags: [['p', pubKey]],
|
||||
}
|
||||
const signed = finishEvent(event, appInfo.privateKey)
|
||||
this.pool.publish(this.settings.relays, signed).forEach(p => {
|
||||
p.then(() => console.log("sent ok"))
|
||||
p.catch(() => console.log("failed to send"))
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
tags: [['p', nostrPub]],
|
||||
GetAppKeys(appInfo: Partial<AppInfo>) {
|
||||
let check: (info: AppInfo) => boolean
|
||||
if (appInfo.appId) {
|
||||
check = (info: AppInfo) => info.appId === appInfo.appId
|
||||
} else if (appInfo.privateKey) {
|
||||
check = (info: AppInfo) => info.privateKey === appInfo.privateKey
|
||||
} else if (appInfo.publicKey) {
|
||||
check = (info: AppInfo) => info.publicKey === appInfo.publicKey
|
||||
} else {
|
||||
throw new Error("app info is empty")
|
||||
}
|
||||
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) })
|
||||
const found = this.settings.apps.find(check)
|
||||
if (!found) {
|
||||
throw new Error("unkown app")
|
||||
}
|
||||
return found
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
import 'dotenv/config' // TODO - test env
|
||||
import { Buffer } from 'buffer'
|
||||
import { generatePrivateKey, getPublicKey } from 'nostr-tools'
|
||||
|
||||
import NostrHandler, { LoadNosrtSettingsFromEnv } from './index.js'
|
||||
import { expect } from 'chai'
|
||||
export const ignore = true
|
||||
const settings = LoadNosrtSettingsFromEnv(true)
|
||||
|
||||
const clientPrivateKey = generatePrivateKey()
|
||||
const clientPublicKey = getPublicKey(clientPrivateKey)
|
||||
|
||||
const serverPrivateKey = generatePrivateKey()
|
||||
const serverPublicKey = getPublicKey(serverPrivateKey)
|
||||
let clientNostr: NostrHandler
|
||||
let serverNostr: NostrHandler
|
||||
|
||||
let receivedServerEvents = 0
|
||||
let latestReceivedServerEvent = ""
|
||||
export const setup = () => {
|
||||
clientNostr = new NostrHandler({
|
||||
allowedPubs: [],
|
||||
privateKey: clientPrivateKey,
|
||||
publicKey: clientPublicKey,
|
||||
relays: settings.relays
|
||||
}, (event) => {
|
||||
|
||||
})
|
||||
serverNostr = new NostrHandler({
|
||||
allowedPubs: [clientPublicKey],
|
||||
privateKey: serverPrivateKey,
|
||||
publicKey: serverPublicKey,
|
||||
relays: settings.relays
|
||||
}, (event) => {
|
||||
receivedServerEvents++
|
||||
latestReceivedServerEvent = event.content
|
||||
})
|
||||
}
|
||||
export const teardown = () => {
|
||||
clientNostr.Stop()
|
||||
serverNostr.Stop()
|
||||
}
|
||||
|
||||
export default async (d: (message: string, failure?: boolean) => void) => {
|
||||
await new Promise(res => setTimeout(res, 2000))
|
||||
clientNostr.Send(serverPublicKey, "test")
|
||||
await new Promise(res => setTimeout(res, 1000))
|
||||
console.log(receivedServerEvents, latestReceivedServerEvent)
|
||||
expect(receivedServerEvents).to.equal(1)
|
||||
expect(latestReceivedServerEvent).to.equal("test")
|
||||
d("nostr ok")
|
||||
}
|
||||
|
|
@ -4,9 +4,6 @@ import { NostrSettings, NostrEvent, ChildProcessRequest, ChildProcessResponse }
|
|||
type EventCallback = (event: NostrEvent) => void
|
||||
export const LoadNosrtSettingsFromEnv = (test = false) => {
|
||||
return {
|
||||
allowedPubs: EnvMustBeNonEmptyString("NOSTR_ALLOWED_PUBS").split(' '),
|
||||
privateKey: EnvMustBeNonEmptyString("NOSTR_PRIVATE_KEY"),
|
||||
publicKey: EnvMustBeNonEmptyString("NOSTR_PUBLIC_KEY"),
|
||||
relays: EnvMustBeNonEmptyString("NOSTR_RELAYS").split(' ')
|
||||
}
|
||||
}
|
||||
|
|
@ -34,8 +31,8 @@ export default class NostrSubprocess {
|
|||
this.childProcess.send(message)
|
||||
}
|
||||
|
||||
Send(pub: string, message: string) {
|
||||
this.sendToChildProcess({ type: 'send', pub: pub, message: message })
|
||||
Send(appId: string, pub: string, message: string) {
|
||||
this.sendToChildProcess({ type: 'send', pub, message, appId })
|
||||
}
|
||||
Stop() {
|
||||
this.childProcess.kill()
|
||||
|
|
|
|||
55
src/services/nostr/nip44.ts
Normal file
55
src/services/nostr/nip44.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { base64 } from "@scure/base";
|
||||
import { randomBytes } from "@noble/hashes/utils";
|
||||
import { streamXOR as xchacha20 } from "@stablelib/xchacha20";
|
||||
import { secp256k1 } from "@noble/curves/secp256k1";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
type EncryptedData = {
|
||||
ciphertext: Uint8Array;
|
||||
nonce: Uint8Array;
|
||||
}
|
||||
export const getSharedSecret = (privateKey: string, publicKey: string) => {
|
||||
const key = secp256k1.getSharedSecret(privateKey, "02" + publicKey);
|
||||
return sha256(key.slice(1, 33));
|
||||
}
|
||||
|
||||
export const encryptData = (content: string, sharedSecret: Uint8Array) => {
|
||||
const nonce = randomBytes(24);
|
||||
const plaintext = new TextEncoder().encode(content);
|
||||
const ciphertext = xchacha20(sharedSecret, nonce, plaintext, plaintext);
|
||||
return {
|
||||
ciphertext: Uint8Array.from(ciphertext),
|
||||
nonce: nonce,
|
||||
} as EncryptedData;
|
||||
}
|
||||
|
||||
export const decryptData = (payload: EncryptedData, sharedSecret: Uint8Array) => {
|
||||
const dst = xchacha20(sharedSecret, payload.nonce, payload.ciphertext, payload.ciphertext);
|
||||
const decoded = new TextDecoder().decode(dst);
|
||||
return decoded;
|
||||
}
|
||||
const xchacha20EncryptionVersion = 1
|
||||
export const decodePayload = (p: string) => {
|
||||
if (p.startsWith("{") && p.endsWith("}")) {
|
||||
const pj = JSON.parse(p) as { v: number; nonce: string; ciphertext: string };
|
||||
if (pj.v !== xchacha20EncryptionVersion) {
|
||||
throw new Error("Encryption version unsupported")
|
||||
}
|
||||
return {
|
||||
nonce: base64.decode(pj.nonce),
|
||||
ciphertext: base64.decode(pj.ciphertext),
|
||||
} as EncryptedData;
|
||||
} else {
|
||||
const buf = base64.decode(p);
|
||||
if (buf[0] !== xchacha20EncryptionVersion) {
|
||||
throw new Error("Encryption version unsupported")
|
||||
}
|
||||
return {
|
||||
nonce: buf.subarray(1, 25),
|
||||
ciphertext: buf.subarray(25),
|
||||
} as EncryptedData;
|
||||
}
|
||||
}
|
||||
|
||||
export const encodePayload = (p: EncryptedData) => {
|
||||
return base64.encode(new Uint8Array([xchacha20EncryptionVersion, ...p.nonce, ...p.ciphertext]));
|
||||
}
|
||||
144
src/services/nostr/tools/event.ts
Normal file
144
src/services/nostr/tools/event.ts
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import { schnorr } from '@noble/curves/secp256k1'
|
||||
import { sha256 } from '@noble/hashes/sha256'
|
||||
import { bytesToHex } from '@noble/hashes/utils'
|
||||
|
||||
import { getPublicKey } from './keys.js'
|
||||
import { utf8Encoder } from './utils.js'
|
||||
|
||||
/** Designates a verified event signature. */
|
||||
export const verifiedSymbol = Symbol('verified')
|
||||
|
||||
/** @deprecated Use numbers instead. */
|
||||
/* eslint-disable no-unused-vars */
|
||||
export enum Kind {
|
||||
Metadata = 0,
|
||||
Text = 1,
|
||||
RecommendRelay = 2,
|
||||
Contacts = 3,
|
||||
EncryptedDirectMessage = 4,
|
||||
EventDeletion = 5,
|
||||
Repost = 6,
|
||||
Reaction = 7,
|
||||
BadgeAward = 8,
|
||||
ChannelCreation = 40,
|
||||
ChannelMetadata = 41,
|
||||
ChannelMessage = 42,
|
||||
ChannelHideMessage = 43,
|
||||
ChannelMuteUser = 44,
|
||||
Blank = 255,
|
||||
Report = 1984,
|
||||
ZapRequest = 9734,
|
||||
Zap = 9735,
|
||||
RelayList = 10002,
|
||||
ClientAuth = 22242,
|
||||
HttpAuth = 27235,
|
||||
ProfileBadge = 30008,
|
||||
BadgeDefinition = 30009,
|
||||
Article = 30023,
|
||||
FileMetadata = 1063,
|
||||
}
|
||||
|
||||
export interface Event<K extends number = number> {
|
||||
kind: K
|
||||
tags: string[][]
|
||||
content: string
|
||||
created_at: number
|
||||
pubkey: string
|
||||
id: string
|
||||
sig: string
|
||||
[verifiedSymbol]?: boolean
|
||||
}
|
||||
|
||||
export type EventTemplate<K extends number = number> = Pick<Event<K>, 'kind' | 'tags' | 'content' | 'created_at'>
|
||||
export type UnsignedEvent<K extends number = number> = Pick<
|
||||
Event<K>,
|
||||
'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'
|
||||
>
|
||||
|
||||
/** An event whose signature has been verified. */
|
||||
export interface VerifiedEvent<K extends number = number> extends Event<K> {
|
||||
[verifiedSymbol]: true
|
||||
}
|
||||
|
||||
export function getBlankEvent(): EventTemplate<Kind.Blank>
|
||||
export function getBlankEvent<K extends number>(kind: K): EventTemplate<K>
|
||||
export function getBlankEvent<K>(kind: K | Kind.Blank = Kind.Blank) {
|
||||
return {
|
||||
kind,
|
||||
content: '',
|
||||
tags: [],
|
||||
created_at: 0,
|
||||
}
|
||||
}
|
||||
|
||||
export function finishEvent<K extends number = number>(t: EventTemplate<K>, privateKey: string): VerifiedEvent<K> {
|
||||
const event = t as VerifiedEvent<K>
|
||||
event.pubkey = getPublicKey(privateKey)
|
||||
event.id = getEventHash(event)
|
||||
event.sig = getSignature(event, privateKey)
|
||||
event[verifiedSymbol] = true
|
||||
return event
|
||||
}
|
||||
|
||||
export function serializeEvent(evt: UnsignedEvent<number>): string {
|
||||
if (!validateEvent(evt)) throw new Error("can't serialize event with wrong or missing properties")
|
||||
|
||||
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])
|
||||
}
|
||||
|
||||
export function getEventHash(event: UnsignedEvent<number>): string {
|
||||
let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))
|
||||
return bytesToHex(eventHash)
|
||||
}
|
||||
|
||||
const isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object
|
||||
|
||||
export function validateEvent<T>(event: T): event is T & UnsignedEvent<number> {
|
||||
if (!isRecord(event)) return false
|
||||
if (typeof event.kind !== 'number') return false
|
||||
if (typeof event.content !== 'string') return false
|
||||
if (typeof event.created_at !== 'number') return false
|
||||
if (typeof event.pubkey !== 'string') return false
|
||||
if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false
|
||||
|
||||
if (!Array.isArray(event.tags)) return false
|
||||
for (let i = 0; i < event.tags.length; i++) {
|
||||
let tag = event.tags[i]
|
||||
if (!Array.isArray(tag)) return false
|
||||
for (let j = 0; j < tag.length; j++) {
|
||||
if (typeof tag[j] === 'object') return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/** Verify the event's signature. This function mutates the event with a `verified` symbol, making it idempotent. */
|
||||
export function verifySignature<K extends number>(event: Event<K>): event is VerifiedEvent<K> {
|
||||
//@ts-ignore
|
||||
if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]
|
||||
|
||||
const hash = getEventHash(event)
|
||||
if (hash !== event.id) {
|
||||
return (event[verifiedSymbol] = false)
|
||||
}
|
||||
|
||||
try {
|
||||
return (event[verifiedSymbol] = schnorr.verify(event.sig, hash, event.pubkey))
|
||||
} catch (err) {
|
||||
return (event[verifiedSymbol] = false)
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Use `getSignature` instead. */
|
||||
export function signEvent(event: UnsignedEvent<number>, key: string): string {
|
||||
console.warn(
|
||||
'nostr-tools: `signEvent` is deprecated and will be removed or changed in the future. Please use `getSignature` instead.',
|
||||
)
|
||||
return getSignature(event, key)
|
||||
}
|
||||
|
||||
/** Calculate the signature for an event. */
|
||||
export function getSignature(event: UnsignedEvent<number>, key: string): string {
|
||||
return bytesToHex(schnorr.sign(getEventHash(event), key))
|
||||
}
|
||||
41
src/services/nostr/tools/fakejson.ts
Normal file
41
src/services/nostr/tools/fakejson.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
export function getHex64(json: string, field: string): string {
|
||||
let len = field.length + 3
|
||||
let idx = json.indexOf(`"${field}":`) + len
|
||||
let s = json.slice(idx).indexOf(`"`) + idx + 1
|
||||
return json.slice(s, s + 64)
|
||||
}
|
||||
|
||||
export function getInt(json: string, field: string): number {
|
||||
let len = field.length
|
||||
let idx = json.indexOf(`"${field}":`) + len + 3
|
||||
let sliced = json.slice(idx)
|
||||
let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))
|
||||
return parseInt(sliced.slice(0, end), 10)
|
||||
}
|
||||
|
||||
export function getSubscriptionId(json: string): string | null {
|
||||
let idx = json.slice(0, 22).indexOf(`"EVENT"`)
|
||||
if (idx === -1) return null
|
||||
|
||||
let pstart = json.slice(idx + 7 + 1).indexOf(`"`)
|
||||
if (pstart === -1) return null
|
||||
let start = idx + 7 + 1 + pstart
|
||||
|
||||
let pend = json.slice(start + 1, 80).indexOf(`"`)
|
||||
if (pend === -1) return null
|
||||
let end = start + 1 + pend
|
||||
|
||||
return json.slice(start + 1, end)
|
||||
}
|
||||
|
||||
export function matchEventId(json: string, id: string): boolean {
|
||||
return id === getHex64(json, 'id')
|
||||
}
|
||||
|
||||
export function matchEventPubkey(json: string, pubkey: string): boolean {
|
||||
return pubkey === getHex64(json, 'pubkey')
|
||||
}
|
||||
|
||||
export function matchEventKind(json: string, kind: number): boolean {
|
||||
return kind === getInt(json, 'kind')
|
||||
}
|
||||
72
src/services/nostr/tools/filter.ts
Normal file
72
src/services/nostr/tools/filter.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { Event } from './event.js'
|
||||
|
||||
export type Filter<K extends number = number> = {
|
||||
ids?: string[]
|
||||
kinds?: K[]
|
||||
authors?: string[]
|
||||
since?: number
|
||||
until?: number
|
||||
limit?: number
|
||||
search?: string
|
||||
[key: `#${string}`]: string[] | undefined
|
||||
}
|
||||
|
||||
export function matchFilter(filter: Filter<number>, event: Event<number>): boolean {
|
||||
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
|
||||
if (!filter.ids.some(prefix => event.id.startsWith(prefix))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false
|
||||
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
|
||||
if (!filter.authors.some(prefix => event.pubkey.startsWith(prefix))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for (let f in filter) {
|
||||
if (f[0] === '#') {
|
||||
let tagName = f.slice(1)
|
||||
let values = filter[`#${tagName}`]
|
||||
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.since && event.created_at < filter.since) return false
|
||||
if (filter.until && event.created_at > filter.until) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export function matchFilters(filters: Filter<number>[], event: Event<number>): boolean {
|
||||
for (let i = 0; i < filters.length; i++) {
|
||||
if (matchFilter(filters[i], event)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function mergeFilters(...filters: Filter<number>[]): Filter<number> {
|
||||
let result: Filter<number> = {}
|
||||
for (let i = 0; i < filters.length; i++) {
|
||||
let filter = filters[i]
|
||||
Object.entries(filter).forEach(([property, values]) => {
|
||||
if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {
|
||||
// @ts-ignore
|
||||
result[property] = result[property] || []
|
||||
// @ts-ignore
|
||||
for (let v = 0; v < values.length; v++) {
|
||||
// @ts-ignore
|
||||
let value = values[v]
|
||||
// @ts-ignore
|
||||
if (!result[property].includes(value)) result[property].push(value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit
|
||||
if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until
|
||||
if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
7
src/services/nostr/tools/index.ts
Normal file
7
src/services/nostr/tools/index.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export * from './event.js'
|
||||
export * from './fakejson.js'
|
||||
export * from './filter.js'
|
||||
export * from './keys.js'
|
||||
export * from './pool.js'
|
||||
export * from './relay.js'
|
||||
export * from './utils.js'
|
||||
10
src/services/nostr/tools/keys.ts
Normal file
10
src/services/nostr/tools/keys.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { schnorr } from '@noble/curves/secp256k1'
|
||||
import { bytesToHex } from '@noble/hashes/utils'
|
||||
|
||||
export function generatePrivateKey(): string {
|
||||
return bytesToHex(schnorr.utils.randomPrivateKey())
|
||||
}
|
||||
|
||||
export function getPublicKey(privateKey: string): string {
|
||||
return bytesToHex(schnorr.getPublicKey(privateKey))
|
||||
}
|
||||
249
src/services/nostr/tools/pool.ts
Normal file
249
src/services/nostr/tools/pool.ts
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
import { relayInit, eventsGenerator, type Relay, type Sub, type SubscriptionOptions } from './relay.js'
|
||||
import { normalizeURL } from './utils.js'
|
||||
|
||||
import type { Event } from './event.js'
|
||||
import { matchFilters, type Filter } from './filter.js'
|
||||
|
||||
type BatchedRequest = {
|
||||
filters: Filter<any>[]
|
||||
relays: string[]
|
||||
resolve: (events: Event<any>[]) => void
|
||||
events: Event<any>[]
|
||||
}
|
||||
|
||||
export class SimplePool {
|
||||
private _conn: { [url: string]: Relay }
|
||||
private _seenOn: { [id: string]: Set<string> } = {} // a map of all events we've seen in each relay
|
||||
private batchedByKey: { [batchKey: string]: BatchedRequest[] } = {}
|
||||
|
||||
private eoseSubTimeout: number
|
||||
private getTimeout: number
|
||||
private seenOnEnabled: boolean = true
|
||||
private batchInterval: number = 100
|
||||
|
||||
constructor(
|
||||
options: {
|
||||
eoseSubTimeout?: number
|
||||
getTimeout?: number
|
||||
seenOnEnabled?: boolean
|
||||
batchInterval?: number
|
||||
} = {},
|
||||
) {
|
||||
this._conn = {}
|
||||
this.eoseSubTimeout = options.eoseSubTimeout || 3400
|
||||
this.getTimeout = options.getTimeout || 3400
|
||||
this.seenOnEnabled = options.seenOnEnabled !== false
|
||||
this.batchInterval = options.batchInterval || 100
|
||||
}
|
||||
|
||||
close(relays: string[]): void {
|
||||
relays.forEach(url => {
|
||||
let relay = this._conn[normalizeURL(url)]
|
||||
if (relay) relay.close()
|
||||
})
|
||||
}
|
||||
|
||||
async ensureRelay(url: string): Promise<Relay> {
|
||||
const nm = normalizeURL(url)
|
||||
|
||||
if (!this._conn[nm]) {
|
||||
this._conn[nm] = relayInit(nm, {
|
||||
getTimeout: this.getTimeout * 0.9,
|
||||
listTimeout: this.getTimeout * 0.9,
|
||||
})
|
||||
}
|
||||
|
||||
const relay = this._conn[nm]
|
||||
await relay.connect()
|
||||
return relay
|
||||
}
|
||||
|
||||
sub<K extends number = number>(relays: string[], filters: Filter<K>[], opts?: SubscriptionOptions): Sub<K> {
|
||||
let _knownIds: Set<string> = new Set()
|
||||
let modifiedOpts = { ...(opts || {}) }
|
||||
modifiedOpts.alreadyHaveEvent = (id, url) => {
|
||||
if (opts?.alreadyHaveEvent?.(id, url)) {
|
||||
return true
|
||||
}
|
||||
if (this.seenOnEnabled) {
|
||||
let set = this._seenOn[id] || new Set()
|
||||
set.add(url)
|
||||
this._seenOn[id] = set
|
||||
}
|
||||
return _knownIds.has(id)
|
||||
}
|
||||
|
||||
let subs: Sub[] = []
|
||||
let eventListeners: Set<any> = new Set()
|
||||
let eoseListeners: Set<() => void> = new Set()
|
||||
let eosesMissing = relays.length
|
||||
|
||||
let eoseSent = false
|
||||
let eoseTimeout = setTimeout(
|
||||
() => {
|
||||
eoseSent = true
|
||||
for (let cb of eoseListeners.values()) cb()
|
||||
},
|
||||
opts?.eoseSubTimeout || this.eoseSubTimeout,
|
||||
)
|
||||
|
||||
relays
|
||||
.filter((r, i, a) => a.indexOf(r) === i)
|
||||
.forEach(async relay => {
|
||||
let r
|
||||
try {
|
||||
r = await this.ensureRelay(relay)
|
||||
} catch (err) {
|
||||
handleEose()
|
||||
return
|
||||
}
|
||||
if (!r) return
|
||||
let s = r.sub(filters, modifiedOpts)
|
||||
s.on('event', event => {
|
||||
_knownIds.add(event.id as string)
|
||||
for (let cb of eventListeners.values()) cb(event)
|
||||
})
|
||||
s.on('eose', () => {
|
||||
if (eoseSent) return
|
||||
handleEose()
|
||||
})
|
||||
subs.push(s)
|
||||
|
||||
function handleEose() {
|
||||
eosesMissing--
|
||||
if (eosesMissing === 0) {
|
||||
clearTimeout(eoseTimeout)
|
||||
for (let cb of eoseListeners.values()) cb()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let greaterSub: Sub<K> = {
|
||||
sub(filters, opts) {
|
||||
subs.forEach(sub => sub.sub(filters, opts))
|
||||
return greaterSub as any
|
||||
},
|
||||
unsub() {
|
||||
subs.forEach(sub => sub.unsub())
|
||||
},
|
||||
on(type, cb) {
|
||||
if (type === 'event') {
|
||||
eventListeners.add(cb)
|
||||
} else if (type === 'eose') {
|
||||
eoseListeners.add(cb as () => void | Promise<void>)
|
||||
}
|
||||
},
|
||||
off(type, cb) {
|
||||
if (type === 'event') {
|
||||
eventListeners.delete(cb)
|
||||
} else if (type === 'eose') eoseListeners.delete(cb as () => void | Promise<void>)
|
||||
},
|
||||
get events() {
|
||||
return eventsGenerator(greaterSub)
|
||||
},
|
||||
}
|
||||
|
||||
return greaterSub
|
||||
}
|
||||
|
||||
get<K extends number = number>(
|
||||
relays: string[],
|
||||
filter: Filter<K>,
|
||||
opts?: SubscriptionOptions,
|
||||
): Promise<Event<K> | null> {
|
||||
return new Promise(resolve => {
|
||||
let sub = this.sub(relays, [filter], opts)
|
||||
let timeout = setTimeout(() => {
|
||||
sub.unsub()
|
||||
resolve(null)
|
||||
}, this.getTimeout)
|
||||
sub.on('event', event => {
|
||||
resolve(event)
|
||||
clearTimeout(timeout)
|
||||
sub.unsub()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
list<K extends number = number>(
|
||||
relays: string[],
|
||||
filters: Filter<K>[],
|
||||
opts?: SubscriptionOptions,
|
||||
): Promise<Event<K>[]> {
|
||||
return new Promise(resolve => {
|
||||
let events: Event<K>[] = []
|
||||
let sub = this.sub(relays, filters, opts)
|
||||
|
||||
sub.on('event', event => {
|
||||
events.push(event)
|
||||
})
|
||||
|
||||
// we can rely on an eose being emitted here because pool.sub() will fake one
|
||||
sub.on('eose', () => {
|
||||
sub.unsub()
|
||||
resolve(events)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
batchedList<K extends number = number>(
|
||||
batchKey: string,
|
||||
relays: string[],
|
||||
filters: Filter<K>[],
|
||||
): Promise<Event<K>[]> {
|
||||
return new Promise(resolve => {
|
||||
if (!this.batchedByKey[batchKey]) {
|
||||
this.batchedByKey[batchKey] = [
|
||||
{
|
||||
filters,
|
||||
relays,
|
||||
resolve,
|
||||
events: [],
|
||||
},
|
||||
]
|
||||
|
||||
setTimeout(() => {
|
||||
Object.keys(this.batchedByKey).forEach(async batchKey => {
|
||||
const batchedRequests = this.batchedByKey[batchKey]
|
||||
|
||||
const filters = [] as Filter[]
|
||||
const relays = [] as string[]
|
||||
batchedRequests.forEach(br => {
|
||||
filters.push(...br.filters)
|
||||
relays.push(...br.relays)
|
||||
})
|
||||
|
||||
const sub = this.sub(relays, filters)
|
||||
sub.on('event', event => {
|
||||
batchedRequests.forEach(br => matchFilters(br.filters, event) && br.events.push(event))
|
||||
})
|
||||
sub.on('eose', () => {
|
||||
sub.unsub()
|
||||
batchedRequests.forEach(br => br.resolve(br.events))
|
||||
})
|
||||
|
||||
delete this.batchedByKey[batchKey]
|
||||
})
|
||||
}, this.batchInterval)
|
||||
} else {
|
||||
this.batchedByKey[batchKey].push({
|
||||
filters,
|
||||
relays,
|
||||
resolve,
|
||||
events: [],
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
publish(relays: string[], event: Event<number>): Promise<void>[] {
|
||||
return relays.map(async relay => {
|
||||
let r = await this.ensureRelay(relay)
|
||||
return r.publish(event)
|
||||
})
|
||||
}
|
||||
|
||||
seenOn(id: string): string[] {
|
||||
return Array.from(this._seenOn[id]?.values?.() || [])
|
||||
}
|
||||
}
|
||||
402
src/services/nostr/tools/relay.ts
Normal file
402
src/services/nostr/tools/relay.ts
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
/* global WebSocket */
|
||||
import "websocket-polyfill"
|
||||
import { verifySignature, validateEvent, type Event } from './event.js'
|
||||
import { matchFilters, type Filter } from './filter.js'
|
||||
import { getHex64, getSubscriptionId } from './fakejson.js'
|
||||
import { MessageQueue } from './utils.js'
|
||||
|
||||
type RelayEvent = {
|
||||
connect: () => void | Promise<void>
|
||||
disconnect: () => void | Promise<void>
|
||||
error: () => void | Promise<void>
|
||||
notice: (msg: string) => void | Promise<void>
|
||||
auth: (challenge: string) => void | Promise<void>
|
||||
}
|
||||
export type CountPayload = {
|
||||
count: number
|
||||
}
|
||||
export type SubEvent<K extends number> = {
|
||||
event: (event: Event<K>) => void | Promise<void>
|
||||
count: (payload: CountPayload) => void | Promise<void>
|
||||
eose: () => void | Promise<void>
|
||||
}
|
||||
export type Relay = {
|
||||
url: string
|
||||
status: number
|
||||
connect: () => Promise<void>
|
||||
close: () => void
|
||||
sub: <K extends number = number>(filters: Filter<K>[], opts?: SubscriptionOptions) => Sub<K>
|
||||
list: <K extends number = number>(filters: Filter<K>[], opts?: SubscriptionOptions) => Promise<Event<K>[]>
|
||||
get: <K extends number = number>(filter: Filter<K>, opts?: SubscriptionOptions) => Promise<Event<K> | null>
|
||||
count: (filters: Filter[], opts?: SubscriptionOptions) => Promise<CountPayload | null>
|
||||
publish: (event: Event<number>) => Promise<void>
|
||||
auth: (event: Event<number>) => Promise<void>
|
||||
off: <T extends keyof RelayEvent, U extends RelayEvent[T]>(event: T, listener: U) => void
|
||||
on: <T extends keyof RelayEvent, U extends RelayEvent[T]>(event: T, listener: U) => void
|
||||
}
|
||||
export type Sub<K extends number = number> = {
|
||||
sub: <K extends number = number>(filters: Filter<K>[], opts: SubscriptionOptions) => Sub<K>
|
||||
unsub: () => void
|
||||
on: <T extends keyof SubEvent<K>, U extends SubEvent<K>[T]>(event: T, listener: U) => void
|
||||
off: <T extends keyof SubEvent<K>, U extends SubEvent<K>[T]>(event: T, listener: U) => void
|
||||
events: AsyncGenerator<Event<K>, void, unknown>
|
||||
}
|
||||
|
||||
export type SubscriptionOptions = {
|
||||
id?: string
|
||||
verb?: 'REQ' | 'COUNT'
|
||||
skipVerification?: boolean
|
||||
alreadyHaveEvent?: null | ((id: string, relay: string) => boolean)
|
||||
eoseSubTimeout?: number
|
||||
}
|
||||
|
||||
const newListeners = (): { [TK in keyof RelayEvent]: RelayEvent[TK][] } => ({
|
||||
connect: [],
|
||||
disconnect: [],
|
||||
error: [],
|
||||
notice: [],
|
||||
auth: [],
|
||||
})
|
||||
|
||||
export function relayInit(
|
||||
url: string,
|
||||
options: {
|
||||
getTimeout?: number
|
||||
listTimeout?: number
|
||||
countTimeout?: number
|
||||
} = {},
|
||||
): Relay {
|
||||
let { listTimeout = 3000, getTimeout = 3000, countTimeout = 3000 } = options
|
||||
|
||||
var ws: WebSocket
|
||||
var openSubs: { [id: string]: { filters: Filter[] } & SubscriptionOptions } = {}
|
||||
var listeners = newListeners()
|
||||
var subListeners: {
|
||||
[subid: string]: { [TK in keyof SubEvent<any>]: SubEvent<any>[TK][] }
|
||||
} = {}
|
||||
var pubListeners: {
|
||||
[eventid: string]: {
|
||||
resolve: (_: unknown) => void
|
||||
reject: (err: Error) => void
|
||||
}
|
||||
} = {}
|
||||
|
||||
var connectionPromise: Promise<void> | undefined
|
||||
async function connectRelay(): Promise<void> {
|
||||
if (connectionPromise) return connectionPromise
|
||||
connectionPromise = new Promise((resolve, reject) => {
|
||||
try {
|
||||
ws = new WebSocket(url)
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
ws.onopen = () => {
|
||||
listeners.connect.forEach(cb => cb())
|
||||
resolve()
|
||||
}
|
||||
ws.onerror = () => {
|
||||
connectionPromise = undefined
|
||||
listeners.error.forEach(cb => cb())
|
||||
reject()
|
||||
}
|
||||
ws.onclose = async () => {
|
||||
connectionPromise = undefined
|
||||
listeners.disconnect.forEach(cb => cb())
|
||||
}
|
||||
|
||||
let incomingMessageQueue: MessageQueue = new MessageQueue()
|
||||
let handleNextInterval: any
|
||||
|
||||
ws.onmessage = e => {
|
||||
incomingMessageQueue.enqueue(e.data)
|
||||
if (!handleNextInterval) {
|
||||
handleNextInterval = setInterval(handleNext, 0)
|
||||
}
|
||||
}
|
||||
|
||||
function handleNext() {
|
||||
if (incomingMessageQueue.size === 0) {
|
||||
clearInterval(handleNextInterval)
|
||||
handleNextInterval = null
|
||||
return
|
||||
}
|
||||
|
||||
var json = incomingMessageQueue.dequeue()
|
||||
if (!json) return
|
||||
|
||||
let subid = getSubscriptionId(json)
|
||||
if (subid) {
|
||||
let so = openSubs[subid]
|
||||
if (so && so.alreadyHaveEvent && so.alreadyHaveEvent(getHex64(json, 'id'), url)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let data = JSON.parse(json)
|
||||
|
||||
// we won't do any checks against the data since all failures (i.e. invalid messages from relays)
|
||||
// will naturally be caught by the encompassing try..catch block
|
||||
|
||||
switch (data[0]) {
|
||||
case 'EVENT': {
|
||||
let id = data[1]
|
||||
let event = data[2]
|
||||
if (
|
||||
validateEvent(event) &&
|
||||
openSubs[id] &&
|
||||
(openSubs[id].skipVerification || verifySignature(event)) &&
|
||||
matchFilters(openSubs[id].filters, event)
|
||||
) {
|
||||
openSubs[id]
|
||||
; (subListeners[id]?.event || []).forEach(cb => cb(event))
|
||||
}
|
||||
return
|
||||
}
|
||||
case 'COUNT':
|
||||
let id = data[1]
|
||||
let payload = data[2]
|
||||
if (openSubs[id]) {
|
||||
; (subListeners[id]?.count || []).forEach(cb => cb(payload))
|
||||
}
|
||||
return
|
||||
case 'EOSE': {
|
||||
let id = data[1]
|
||||
if (id in subListeners) {
|
||||
subListeners[id].eose.forEach(cb => cb())
|
||||
subListeners[id].eose = [] // 'eose' only happens once per sub, so stop listeners here
|
||||
}
|
||||
return
|
||||
}
|
||||
case 'OK': {
|
||||
let id: string = data[1]
|
||||
let ok: boolean = data[2]
|
||||
let reason: string = data[3] || ''
|
||||
if (id in pubListeners) {
|
||||
let { resolve, reject } = pubListeners[id]
|
||||
if (ok) resolve(null)
|
||||
else reject(new Error(reason))
|
||||
}
|
||||
return
|
||||
}
|
||||
case 'NOTICE':
|
||||
let notice = data[1]
|
||||
listeners.notice.forEach(cb => cb(notice))
|
||||
return
|
||||
case 'AUTH': {
|
||||
let challenge = data[1]
|
||||
listeners.auth?.forEach(cb => cb(challenge))
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return connectionPromise
|
||||
}
|
||||
|
||||
function connected() {
|
||||
return ws?.readyState === 1
|
||||
}
|
||||
|
||||
async function connect(): Promise<void> {
|
||||
if (connected()) return // ws already open
|
||||
await connectRelay()
|
||||
}
|
||||
|
||||
async function trySend(params: [string, ...any]) {
|
||||
let msg = JSON.stringify(params)
|
||||
if (!connected()) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
if (!connected()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
try {
|
||||
ws.send(msg)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
const sub = <K extends number = number>(
|
||||
filters: Filter<K>[],
|
||||
{
|
||||
verb = 'REQ',
|
||||
skipVerification = false,
|
||||
alreadyHaveEvent = null,
|
||||
id = Math.random().toString().slice(2),
|
||||
}: SubscriptionOptions = {},
|
||||
): Sub<K> => {
|
||||
let subid = id
|
||||
|
||||
openSubs[subid] = {
|
||||
id: subid,
|
||||
filters,
|
||||
skipVerification,
|
||||
alreadyHaveEvent,
|
||||
}
|
||||
trySend([verb, subid, ...filters])
|
||||
|
||||
let subscription: Sub<K> = {
|
||||
sub: (newFilters, newOpts = {}) =>
|
||||
sub(newFilters || filters, {
|
||||
skipVerification: newOpts.skipVerification || skipVerification,
|
||||
alreadyHaveEvent: newOpts.alreadyHaveEvent || alreadyHaveEvent,
|
||||
id: subid,
|
||||
}),
|
||||
unsub: () => {
|
||||
delete openSubs[subid]
|
||||
delete subListeners[subid]
|
||||
trySend(['CLOSE', subid])
|
||||
},
|
||||
on: (type, cb) => {
|
||||
subListeners[subid] = subListeners[subid] || {
|
||||
event: [],
|
||||
count: [],
|
||||
eose: [],
|
||||
}
|
||||
//@ts-ignore
|
||||
subListeners[subid][type].push(cb)
|
||||
},
|
||||
off: (type, cb): void => {
|
||||
let listeners = subListeners[subid]
|
||||
//@ts-ignore
|
||||
let idx = listeners[type].indexOf(cb)
|
||||
if (idx >= 0) listeners[type].splice(idx, 1)
|
||||
},
|
||||
get events() {
|
||||
return eventsGenerator(subscription)
|
||||
},
|
||||
}
|
||||
|
||||
return subscription
|
||||
}
|
||||
|
||||
function _publishEvent(event: Event<number>, type: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!event.id) {
|
||||
reject(new Error(`event ${event} has no id`))
|
||||
return
|
||||
}
|
||||
|
||||
let id = event.id
|
||||
trySend([type, event])
|
||||
pubListeners[id] = { resolve, reject }
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
url,
|
||||
sub,
|
||||
on: <T extends keyof RelayEvent, U extends RelayEvent[T]>(type: T, cb: U): void => {
|
||||
//@ts-ignore
|
||||
listeners[type].push(cb)
|
||||
if (type === 'connect' && ws?.readyState === 1) {
|
||||
// i would love to know why we need this
|
||||
; (cb as () => void)()
|
||||
}
|
||||
},
|
||||
off: <T extends keyof RelayEvent, U extends RelayEvent[T]>(type: T, cb: U): void => {
|
||||
//@ts-ignore
|
||||
let index = listeners[type].indexOf(cb)
|
||||
if (index !== -1) listeners[type].splice(index, 1)
|
||||
},
|
||||
list: (filters, opts?: SubscriptionOptions) =>
|
||||
new Promise(resolve => {
|
||||
let s = sub(filters, opts)
|
||||
let events: Event<any>[] = []
|
||||
let timeout = setTimeout(() => {
|
||||
s.unsub()
|
||||
resolve(events)
|
||||
}, listTimeout)
|
||||
s.on('eose', () => {
|
||||
s.unsub()
|
||||
clearTimeout(timeout)
|
||||
resolve(events)
|
||||
})
|
||||
s.on('event', event => {
|
||||
events.push(event)
|
||||
})
|
||||
}),
|
||||
get: (filter, opts?: SubscriptionOptions) =>
|
||||
new Promise(resolve => {
|
||||
let s = sub([filter], opts)
|
||||
let timeout = setTimeout(() => {
|
||||
s.unsub()
|
||||
resolve(null)
|
||||
}, getTimeout)
|
||||
s.on('event', event => {
|
||||
s.unsub()
|
||||
clearTimeout(timeout)
|
||||
resolve(event)
|
||||
})
|
||||
}),
|
||||
count: (filters: Filter[]): Promise<CountPayload | null> =>
|
||||
new Promise(resolve => {
|
||||
let s = sub(filters, { ...sub, verb: 'COUNT' })
|
||||
let timeout = setTimeout(() => {
|
||||
s.unsub()
|
||||
resolve(null)
|
||||
}, countTimeout)
|
||||
s.on('count', (event: CountPayload) => {
|
||||
s.unsub()
|
||||
clearTimeout(timeout)
|
||||
resolve(event)
|
||||
})
|
||||
}),
|
||||
async publish(event): Promise<void> {
|
||||
await _publishEvent(event, 'EVENT')
|
||||
},
|
||||
async auth(event): Promise<void> {
|
||||
await _publishEvent(event, 'AUTH')
|
||||
},
|
||||
connect,
|
||||
close(): void {
|
||||
listeners = newListeners()
|
||||
subListeners = {}
|
||||
pubListeners = {}
|
||||
if (ws?.readyState === WebSocket.OPEN) {
|
||||
ws.close()
|
||||
}
|
||||
},
|
||||
get status() {
|
||||
return ws?.readyState ?? 3
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function* eventsGenerator<K extends number>(sub: Sub<K>): AsyncGenerator<Event<K>, void, unknown> {
|
||||
let nextResolve: ((event: Event<K>) => void) | undefined
|
||||
const eventQueue: Event<K>[] = []
|
||||
|
||||
const pushToQueue = (event: Event<K>) => {
|
||||
if (nextResolve) {
|
||||
nextResolve(event)
|
||||
nextResolve = undefined
|
||||
} else {
|
||||
eventQueue.push(event)
|
||||
}
|
||||
}
|
||||
|
||||
sub.on('event', pushToQueue)
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
if (eventQueue.length > 0) {
|
||||
yield eventQueue.shift()!
|
||||
} else {
|
||||
const event = await new Promise<Event<K>>(resolve => {
|
||||
nextResolve = resolve
|
||||
})
|
||||
yield event
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
sub.off('event', pushToQueue)
|
||||
}
|
||||
}
|
||||
169
src/services/nostr/tools/utils.ts
Normal file
169
src/services/nostr/tools/utils.ts
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import type { Event } from './event.js'
|
||||
|
||||
export const utf8Decoder = new TextDecoder('utf-8')
|
||||
export const utf8Encoder = new TextEncoder()
|
||||
|
||||
export function normalizeURL(url: string): string {
|
||||
let p = new URL(url)
|
||||
p.pathname = p.pathname.replace(/\/+/g, '/')
|
||||
if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)
|
||||
if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''
|
||||
p.searchParams.sort()
|
||||
p.hash = ''
|
||||
return p.toString()
|
||||
}
|
||||
|
||||
//
|
||||
// fast insert-into-sorted-array functions adapted from https://github.com/terrymorse58/fast-sorted-array
|
||||
//
|
||||
export function insertEventIntoDescendingList(sortedArray: Event<number>[], event: Event<number>) {
|
||||
let start = 0
|
||||
let end = sortedArray.length - 1
|
||||
let midPoint
|
||||
let position = start
|
||||
|
||||
if (end < 0) {
|
||||
position = 0
|
||||
} else if (event.created_at < sortedArray[end].created_at) {
|
||||
position = end + 1
|
||||
} else if (event.created_at >= sortedArray[start].created_at) {
|
||||
position = start
|
||||
} else
|
||||
while (true) {
|
||||
if (end <= start + 1) {
|
||||
position = end
|
||||
break
|
||||
}
|
||||
midPoint = Math.floor(start + (end - start) / 2)
|
||||
if (sortedArray[midPoint].created_at > event.created_at) {
|
||||
start = midPoint
|
||||
} else if (sortedArray[midPoint].created_at < event.created_at) {
|
||||
end = midPoint
|
||||
} else {
|
||||
// aMidPoint === num
|
||||
position = midPoint
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// insert when num is NOT already in (no duplicates)
|
||||
if (sortedArray[position]?.id !== event.id) {
|
||||
return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)]
|
||||
}
|
||||
|
||||
return sortedArray
|
||||
}
|
||||
|
||||
export function insertEventIntoAscendingList(sortedArray: Event<number>[], event: Event<number>) {
|
||||
let start = 0
|
||||
let end = sortedArray.length - 1
|
||||
let midPoint
|
||||
let position = start
|
||||
|
||||
if (end < 0) {
|
||||
position = 0
|
||||
} else if (event.created_at > sortedArray[end].created_at) {
|
||||
position = end + 1
|
||||
} else if (event.created_at <= sortedArray[start].created_at) {
|
||||
position = start
|
||||
} else
|
||||
while (true) {
|
||||
if (end <= start + 1) {
|
||||
position = end
|
||||
break
|
||||
}
|
||||
midPoint = Math.floor(start + (end - start) / 2)
|
||||
if (sortedArray[midPoint].created_at < event.created_at) {
|
||||
start = midPoint
|
||||
} else if (sortedArray[midPoint].created_at > event.created_at) {
|
||||
end = midPoint
|
||||
} else {
|
||||
// aMidPoint === num
|
||||
position = midPoint
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// insert when num is NOT already in (no duplicates)
|
||||
if (sortedArray[position]?.id !== event.id) {
|
||||
return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)]
|
||||
}
|
||||
|
||||
return sortedArray
|
||||
}
|
||||
|
||||
export class MessageNode {
|
||||
private _value: string
|
||||
private _next: MessageNode | null
|
||||
|
||||
public get value(): string {
|
||||
return this._value
|
||||
}
|
||||
public set value(message: string) {
|
||||
this._value = message
|
||||
}
|
||||
public get next(): MessageNode | null {
|
||||
return this._next
|
||||
}
|
||||
public set next(node: MessageNode | null) {
|
||||
this._next = node
|
||||
}
|
||||
|
||||
constructor(message: string) {
|
||||
this._value = message
|
||||
this._next = null
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageQueue {
|
||||
private _first: MessageNode | null
|
||||
private _last: MessageNode | null
|
||||
|
||||
public get first(): MessageNode | null {
|
||||
return this._first
|
||||
}
|
||||
public set first(messageNode: MessageNode | null) {
|
||||
this._first = messageNode
|
||||
}
|
||||
public get last(): MessageNode | null {
|
||||
return this._last
|
||||
}
|
||||
public set last(messageNode: MessageNode | null) {
|
||||
this._last = messageNode
|
||||
}
|
||||
private _size: number
|
||||
public get size(): number {
|
||||
return this._size
|
||||
}
|
||||
public set size(v: number) {
|
||||
this._size = v
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._first = null
|
||||
this._last = null
|
||||
this._size = 0
|
||||
}
|
||||
enqueue(message: string): boolean {
|
||||
const newNode = new MessageNode(message)
|
||||
if (this._size === 0 || !this._last) {
|
||||
this._first = newNode
|
||||
this._last = newNode
|
||||
} else {
|
||||
this._last.next = newNode
|
||||
this._last = newNode
|
||||
}
|
||||
this._size++
|
||||
return true
|
||||
}
|
||||
dequeue(): string | null {
|
||||
if (this._size === 0 || !this._first) return null
|
||||
|
||||
let prev = this._first
|
||||
this._first = prev.next
|
||||
prev.next = null
|
||||
|
||||
this._size--
|
||||
return prev.value
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import crypto from 'crypto';
|
||||
import { DataSource, EntityManager } from "typeorm"
|
||||
import { generatePrivateKey, getPublicKey } from 'nostr-tools';
|
||||
import { Application } from "./entity/Application.js"
|
||||
import UserStorage from './userStorage.js';
|
||||
import { ApplicationUser } from './entity/ApplicationUser.js';
|
||||
|
|
@ -12,13 +13,14 @@ export default class {
|
|||
this.userStorage = userStorage
|
||||
}
|
||||
|
||||
async AddApplication(name: string, entityManager = this.DB): Promise<Application> {
|
||||
async AddApplication(name: string, allowUserCreation: boolean, 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
|
||||
owner,
|
||||
allow_user_creation: allowUserCreation
|
||||
})
|
||||
return repo.save(newApplication)
|
||||
}
|
||||
|
|
@ -35,7 +37,13 @@ export default class {
|
|||
return found
|
||||
}
|
||||
|
||||
async GetApplications(entityManager = this.DB): Promise<Application[]> {
|
||||
return entityManager.getRepository(Application).find()
|
||||
}
|
||||
async GetApplication(appId: string, entityManager = this.DB): Promise<Application> {
|
||||
if (!appId) {
|
||||
throw new Error("invalid app id provided")
|
||||
}
|
||||
const found = await entityManager.getRepository(Application).findOne({
|
||||
where: {
|
||||
app_id: appId
|
||||
|
|
@ -47,7 +55,18 @@ export default class {
|
|||
return found
|
||||
}
|
||||
|
||||
async AddApplicationUser(application: Application, userIdentifier: string, balance: number) {
|
||||
async UpdateApplication(app: Application, update: Partial<Application>, entityManager = this.DB) {
|
||||
await entityManager.getRepository(Application).update(app.serial_id, update)
|
||||
}
|
||||
|
||||
async GenerateApplicationKeys(app: Application) {
|
||||
const priv = generatePrivateKey()
|
||||
const pub = getPublicKey(priv)
|
||||
await this.UpdateApplication(app, { nostr_private_key: priv, nostr_public_key: pub })
|
||||
return { privateKey: priv, publicKey: pub, appId: app.app_id, name: app.name }
|
||||
}
|
||||
|
||||
async AddApplicationUser(application: Application, userIdentifier: string, balance: number, nostrPub?: string) {
|
||||
return this.DB.transaction(async tx => {
|
||||
const user = await this.userStorage.AddUser(balance, tx)
|
||||
const repo = tx.getRepository(ApplicationUser)
|
||||
|
|
@ -55,6 +74,7 @@ export default class {
|
|||
user: user,
|
||||
application,
|
||||
identifier: userIdentifier,
|
||||
nostr_public_key: nostrPub
|
||||
})
|
||||
return repo.save(appUser)
|
||||
})
|
||||
|
|
@ -64,6 +84,20 @@ export default class {
|
|||
return entityManager.getRepository(ApplicationUser).findOne({ where: { identifier: userIdentifier, application: { serial_id: application.serial_id } } })
|
||||
}
|
||||
|
||||
async GetOrCreateNostrAppUser(application: Application, nostrPub: string, entityManager = this.DB): Promise<ApplicationUser> {
|
||||
if (!nostrPub) {
|
||||
throw new Error("no nostrPub provided")
|
||||
}
|
||||
const user = await entityManager.getRepository(ApplicationUser).findOne({ where: { application: { serial_id: application.serial_id }, nostr_public_key: nostrPub } })
|
||||
if (user) {
|
||||
return user
|
||||
}
|
||||
if (!application.allow_user_creation) {
|
||||
throw new Error("user creation by client is not allowed in this app")
|
||||
}
|
||||
return this.AddApplicationUser(application, crypto.randomBytes(32).toString('hex'), 0, nostrPub)
|
||||
}
|
||||
|
||||
async GetOrCreateApplicationUser(application: Application, userIdentifier: string, balance: number, entityManager = this.DB): Promise<{ user: ApplicationUser, created: boolean }> {
|
||||
const user = await this.GetApplicationUserIfExists(application, userIdentifier, entityManager)
|
||||
if (user) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { UserReceivingInvoice } from "./entity/UserReceivingInvoice.js"
|
|||
import { UserInvoicePayment } from "./entity/UserInvoicePayment.js"
|
||||
import { EnvMustBeNonEmptyString } from "../helpers/envParser.js"
|
||||
import { UserTransactionPayment } from "./entity/UserTransactionPayment.js"
|
||||
import { UserNostrAuth } from "./entity/UserNostrAuth.js"
|
||||
import { UserBasicAuth } from "./entity/UserBasicAuth.js"
|
||||
import { UserEphemeralKey } from "./entity/UserEphemeralKey.js"
|
||||
import { Product } from "./entity/Product.js"
|
||||
|
|
@ -25,7 +24,7 @@ export default async (settings: DbSettings) => {
|
|||
type: "sqlite",
|
||||
database: settings.databaseFile,
|
||||
//logging: true,
|
||||
entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment, UserNostrAuth, UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment],
|
||||
entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment, UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment],
|
||||
synchronize: true,
|
||||
}).initialize()
|
||||
}
|
||||
|
|
@ -18,6 +18,15 @@ export class Application {
|
|||
@JoinColumn()
|
||||
owner: User
|
||||
|
||||
@Column({ default: false })
|
||||
allow_user_creation: boolean
|
||||
|
||||
@Column({ nullable: true, unique: true })
|
||||
nostr_private_key: string
|
||||
|
||||
@Column({ nullable: true, unique: true })
|
||||
nostr_public_key: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ export class ApplicationUser {
|
|||
@Index({ unique: true })
|
||||
identifier: string
|
||||
|
||||
@Column({ nullable: true, unique: true })
|
||||
nostr_public_key: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, JoinColumn, OneToOne, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
export class UserNostrAuth {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
serial_id: number
|
||||
|
||||
@OneToOne(type => User, { eager: true })
|
||||
@JoinColumn()
|
||||
user: User
|
||||
|
||||
@Column()
|
||||
@Index({ unique: true })
|
||||
nostr_pub: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
@ -1,18 +1,7 @@
|
|||
import { DataSource, EntityManager, MoreThan, MoreThanOrEqual, TransactionAlreadyStartedError } from "typeorm"
|
||||
import crypto from 'crypto';
|
||||
import { DataSource, EntityManager } from "typeorm"
|
||||
import NewDB, { DbSettings, LoadDbSettingsFromEnv } from "./db.js"
|
||||
import { User } from "./entity/User.js"
|
||||
import { UserReceivingAddress } from "./entity/UserReceivingAddress.js";
|
||||
import { UserReceivingInvoice } from "./entity/UserReceivingInvoice.js";
|
||||
import { AddressReceivingTransaction } from "./entity/AddressReceivingTransaction.js";
|
||||
import { UserInvoicePayment } from "./entity/UserInvoicePayment.js";
|
||||
import { UserTransactionPayment } from "./entity/UserTransactionPayment.js";
|
||||
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 = {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
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';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ 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';
|
||||
import { getLogger } from '../helpers/logger.js';
|
||||
export default class {
|
||||
DB: DataSource | EntityManager
|
||||
|
|
@ -21,7 +20,6 @@ export default class {
|
|||
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)
|
||||
|
|
@ -49,24 +47,6 @@ export default class {
|
|||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue