better error handling
This commit is contained in:
parent
70e009ddf7
commit
67c5252983
16 changed files with 931 additions and 125 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "nsecbunkerd",
|
||||
"version": "0.9.0",
|
||||
"version": "0.10.0",
|
||||
"description": "nsecbunker daemon",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
|
|
@ -43,6 +43,8 @@
|
|||
"@prisma/client": "^5.4.1",
|
||||
"@scure/base": "^1.1.1",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"axios": "^1.6.2",
|
||||
"bcrypt": "^5.1.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"debug": "^4.3.4",
|
||||
"dotenv": "^16.3.1",
|
||||
|
|
@ -54,6 +56,8 @@
|
|||
"gravatar": "^1.8.2",
|
||||
"handlebars": "^4.7.8",
|
||||
"isomorphic-ws": "^5.0.0",
|
||||
"lnbits": "^1.1.5",
|
||||
"lnbits-ts": "^0.0.2",
|
||||
"nostr-tools": "^1.17.0",
|
||||
"websocket-polyfill": "^0.0.3",
|
||||
"ws": "^8.13.0",
|
||||
|
|
|
|||
322
pnpm-lock.yaml
generated
322
pnpm-lock.yaml
generated
|
|
@ -29,6 +29,12 @@ dependencies:
|
|||
'@types/yargs':
|
||||
specifier: ^17.0.24
|
||||
version: 17.0.24
|
||||
axios:
|
||||
specifier: ^1.6.2
|
||||
version: 1.6.2(debug@4.3.4)
|
||||
bcrypt:
|
||||
specifier: ^5.1.1
|
||||
version: 5.1.1
|
||||
crypto-js:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
|
|
@ -62,6 +68,12 @@ dependencies:
|
|||
isomorphic-ws:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0(ws@8.13.0)
|
||||
lnbits:
|
||||
specifier: ^1.1.5
|
||||
version: 1.1.5(debug@4.3.4)
|
||||
lnbits-ts:
|
||||
specifier: ^0.0.2
|
||||
version: 0.0.2(debug@4.3.4)
|
||||
nostr-tools:
|
||||
specifier: ^1.17.0
|
||||
version: 1.17.0(typescript@5.1.3)
|
||||
|
|
@ -562,6 +574,24 @@ packages:
|
|||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
dev: true
|
||||
|
||||
/@mapbox/node-pre-gyp@1.0.11:
|
||||
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
detect-libc: 2.0.2
|
||||
https-proxy-agent: 5.0.1
|
||||
make-dir: 3.1.0
|
||||
node-fetch: 2.7.0
|
||||
nopt: 5.0.0
|
||||
npmlog: 5.0.1
|
||||
rimraf: 3.0.2
|
||||
semver: 7.5.4
|
||||
tar: 6.2.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@noble/ciphers@0.2.0:
|
||||
resolution: {integrity: sha512-6YBxJDAapHSdd3bLDv6x2wRPwq4QFMUaB3HvljNBUTThDd12eSm7/3F+2lnfzx2jvM+S6Nsy0jEt9QbPqSwqRw==}
|
||||
dev: false
|
||||
|
|
@ -717,6 +747,10 @@ packages:
|
|||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||
dev: false
|
||||
|
||||
/abbrev@1.1.1:
|
||||
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||
dev: false
|
||||
|
||||
/abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
engines: {node: '>=6.5'}
|
||||
|
|
@ -761,6 +795,15 @@ packages:
|
|||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/agent-base@6.0.2:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/ajv-formats@2.1.1(ajv@8.12.0):
|
||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||
peerDependencies:
|
||||
|
|
@ -821,10 +864,22 @@ packages:
|
|||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/aproba@2.0.0:
|
||||
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
|
||||
dev: false
|
||||
|
||||
/archy@1.0.0:
|
||||
resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
|
||||
dev: false
|
||||
|
||||
/are-we-there-yet@2.0.0:
|
||||
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
delegates: 1.0.0
|
||||
readable-stream: 3.6.2
|
||||
dev: false
|
||||
|
||||
/arg@4.1.3:
|
||||
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
|
||||
dev: true
|
||||
|
|
@ -880,6 +935,10 @@ packages:
|
|||
es-shim-unscopables: 1.0.0
|
||||
dev: false
|
||||
|
||||
/asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
dev: false
|
||||
|
||||
/atomic-sleep@1.0.0:
|
||||
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
|
@ -900,6 +959,24 @@ packages:
|
|||
- supports-color
|
||||
dev: false
|
||||
|
||||
/axios@0.21.4(debug@4.3.4):
|
||||
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.3(debug@4.3.4)
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/axios@1.6.2(debug@4.3.4):
|
||||
resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.3(debug@4.3.4)
|
||||
form-data: 4.0.0
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
|
|
@ -907,6 +984,18 @@ packages:
|
|||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
dev: false
|
||||
|
||||
/bcrypt@5.1.1:
|
||||
resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@mapbox/node-pre-gyp': 1.0.11
|
||||
node-addon-api: 5.1.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -1028,6 +1117,11 @@ packages:
|
|||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/chownr@2.0.0:
|
||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/cli-spinners@2.9.0:
|
||||
resolution: {integrity: sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -1066,6 +1160,18 @@ packages:
|
|||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
dev: false
|
||||
|
||||
/color-support@1.1.3:
|
||||
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
dev: false
|
||||
|
||||
/commander@4.1.1:
|
||||
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
|
@ -1074,6 +1180,10 @@ packages:
|
|||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
/console-control-strings@1.1.0:
|
||||
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
||||
dev: false
|
||||
|
||||
/content-disposition@0.5.4:
|
||||
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
|
@ -1173,6 +1283,15 @@ packages:
|
|||
object-keys: 1.1.1
|
||||
dev: false
|
||||
|
||||
/delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
dev: false
|
||||
|
||||
/delegates@1.0.0:
|
||||
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
|
||||
dev: false
|
||||
|
||||
/depd@2.0.0:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
|
@ -1183,6 +1302,11 @@ packages:
|
|||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
dev: false
|
||||
|
||||
/detect-libc@2.0.2:
|
||||
resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/diff@4.0.2:
|
||||
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
|
@ -1802,12 +1926,33 @@ packages:
|
|||
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
|
||||
dev: false
|
||||
|
||||
/follow-redirects@1.15.3(debug@4.3.4):
|
||||
resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
dev: false
|
||||
|
||||
/for-each@0.3.3:
|
||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||
dependencies:
|
||||
is-callable: 1.2.7
|
||||
dev: false
|
||||
|
||||
/form-data@4.0.0:
|
||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/formdata-polyfill@4.0.10:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
|
|
@ -1825,6 +1970,13 @@ packages:
|
|||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/fs-minipass@2.1.0:
|
||||
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
dev: false
|
||||
|
||||
/fs.realpath@1.0.0:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||
|
||||
|
|
@ -1854,6 +2006,21 @@ packages:
|
|||
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
|
||||
dev: false
|
||||
|
||||
/gauge@3.0.2:
|
||||
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
aproba: 2.0.0
|
||||
color-support: 1.1.3
|
||||
console-control-strings: 1.1.0
|
||||
has-unicode: 2.0.1
|
||||
object-assign: 4.1.1
|
||||
signal-exit: 3.0.7
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wide-align: 1.1.5
|
||||
dev: false
|
||||
|
||||
/get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
|
|
@ -2009,6 +2176,10 @@ packages:
|
|||
has-symbols: 1.0.3
|
||||
dev: false
|
||||
|
||||
/has-unicode@2.0.1:
|
||||
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
|
||||
dev: false
|
||||
|
||||
/has@1.0.3:
|
||||
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
|
@ -2031,6 +2202,16 @@ packages:
|
|||
toidentifier: 1.0.1
|
||||
dev: false
|
||||
|
||||
/https-proxy-agent@5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/human-signals@2.1.0:
|
||||
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
|
||||
engines: {node: '>=10.17.0'}
|
||||
|
|
@ -2320,6 +2501,23 @@ packages:
|
|||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
dev: true
|
||||
|
||||
/lnbits-ts@0.0.2(debug@4.3.4):
|
||||
resolution: {integrity: sha512-3OnkL/IILpPQ0SUkN1kBdAO1dBmV+QqQNIh1GSz3koBqwjnFM8+RoZDKMsiq8WWHRaVFH49OA+a2ODgrC9sH/Q==}
|
||||
dependencies:
|
||||
axios: 1.6.2(debug@4.3.4)
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/lnbits@1.1.5(debug@4.3.4):
|
||||
resolution: {integrity: sha512-RPCBNsKKxlyQTHPKdU66iiXFBz6SuISVVkxJoSZY3Z+CBEzOu6xpgzZtQcZTbc1BCLqQc6HeK4qtfByKWjBTmg==}
|
||||
dependencies:
|
||||
axios: 0.21.4(debug@4.3.4)
|
||||
typescript: 4.9.5
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/load-tsconfig@0.2.5:
|
||||
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
|
@ -2354,6 +2552,13 @@ packages:
|
|||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/make-dir@3.1.0:
|
||||
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
semver: 6.3.0
|
||||
dev: false
|
||||
|
||||
/make-error@1.3.6:
|
||||
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
|
||||
dev: true
|
||||
|
|
@ -2421,6 +2626,32 @@ packages:
|
|||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
dev: false
|
||||
|
||||
/minipass@3.3.6:
|
||||
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/minipass@5.0.0:
|
||||
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/minizlib@2.1.2:
|
||||
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/mkdirp@1.0.4:
|
||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/ms@2.0.0:
|
||||
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
||||
dev: false
|
||||
|
|
@ -2462,11 +2693,27 @@ packages:
|
|||
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
|
||||
dev: false
|
||||
|
||||
/node-addon-api@5.1.0:
|
||||
resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==}
|
||||
dev: false
|
||||
|
||||
/node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
dev: false
|
||||
|
||||
/node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
dev: false
|
||||
|
||||
/node-fetch@3.3.2:
|
||||
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
|
@ -2481,6 +2728,14 @@ packages:
|
|||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/nopt@5.0.0:
|
||||
resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
abbrev: 1.1.1
|
||||
dev: false
|
||||
|
||||
/normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -2510,10 +2765,18 @@ packages:
|
|||
path-key: 3.1.1
|
||||
dev: true
|
||||
|
||||
/npmlog@5.0.1:
|
||||
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
|
||||
dependencies:
|
||||
are-we-there-yet: 2.0.0
|
||||
console-control-strings: 1.1.0
|
||||
gauge: 3.0.2
|
||||
set-blocking: 2.0.0
|
||||
dev: false
|
||||
|
||||
/object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/object-inspect@1.12.3:
|
||||
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
|
||||
|
|
@ -2740,6 +3003,10 @@ packages:
|
|||
ipaddr.js: 1.9.1
|
||||
dev: false
|
||||
|
||||
/proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
dev: false
|
||||
|
||||
/punycode@2.3.0:
|
||||
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -2785,6 +3052,15 @@ packages:
|
|||
unpipe: 1.0.0
|
||||
dev: false
|
||||
|
||||
/readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: false
|
||||
|
||||
/readable-stream@4.4.2:
|
||||
resolution: {integrity: sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
|
@ -2997,7 +3273,6 @@ packages:
|
|||
|
||||
/signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
dev: true
|
||||
|
||||
/slash@3.0.0:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
|
|
@ -3120,6 +3395,18 @@ packages:
|
|||
engines: {node: '>= 0.4'}
|
||||
dev: false
|
||||
|
||||
/tar@6.2.0:
|
||||
resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
chownr: 2.0.0
|
||||
fs-minipass: 2.1.0
|
||||
minipass: 5.0.0
|
||||
minizlib: 2.1.2
|
||||
mkdirp: 1.0.4
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/text-table@0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||
dev: false
|
||||
|
|
@ -3167,6 +3454,10 @@ packages:
|
|||
engines: {node: '>=0.6'}
|
||||
dev: false
|
||||
|
||||
/tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
dev: false
|
||||
|
||||
/tr46@1.0.1:
|
||||
resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
|
||||
dependencies:
|
||||
|
|
@ -3317,6 +3608,12 @@ packages:
|
|||
resolution: {integrity: sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA==}
|
||||
dev: false
|
||||
|
||||
/typescript@4.9.5:
|
||||
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
|
||||
engines: {node: '>=4.2.0'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/typescript@5.1.3:
|
||||
resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==}
|
||||
engines: {node: '>=14.17'}
|
||||
|
|
@ -3363,6 +3660,10 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
dev: false
|
||||
|
||||
/utils-merge@1.0.1:
|
||||
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
|
@ -3382,6 +3683,10 @@ packages:
|
|||
engines: {node: '>= 8'}
|
||||
dev: false
|
||||
|
||||
/webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
dev: false
|
||||
|
||||
/webidl-conversions@4.0.2:
|
||||
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
|
||||
dev: true
|
||||
|
|
@ -3409,6 +3714,13 @@ packages:
|
|||
- supports-color
|
||||
dev: false
|
||||
|
||||
/whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
dev: false
|
||||
|
||||
/whatwg-url@7.1.0:
|
||||
resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
|
||||
dependencies:
|
||||
|
|
@ -3450,6 +3762,12 @@ packages:
|
|||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
/wide-align@1.1.5:
|
||||
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
dev: false
|
||||
|
||||
/wordwrap@1.0.0:
|
||||
resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
|
||||
dev: false
|
||||
|
|
|
|||
|
|
@ -1,13 +1,25 @@
|
|||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
|
||||
import { NDKPrivateKeySigner, NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { IAdminOpts } from '../daemon/admin';
|
||||
|
||||
import { version } from '../../package.json';
|
||||
|
||||
const generatedKey = NDKPrivateKeySigner.generate();
|
||||
|
||||
export type LNBitsWalletConfig = {
|
||||
url: string,
|
||||
key: string,
|
||||
nostdressUrl: string,
|
||||
}
|
||||
|
||||
export interface IWalletConfig {
|
||||
lnbits?: LNBitsWalletConfig;
|
||||
}
|
||||
|
||||
export interface DomainConfig {
|
||||
nip05: string;
|
||||
wallet?: IWalletConfig;
|
||||
defaultProfile?: Record<string, string>;
|
||||
};
|
||||
|
||||
export interface IConfig {
|
||||
|
|
|
|||
89
src/daemon/admin/commands/account/wallet.ts
Normal file
89
src/daemon/admin/commands/account/wallet.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import axios from "axios";
|
||||
import createDebug from "debug";
|
||||
import { IWalletConfig, LNBitsWalletConfig } from "../../../../config";
|
||||
|
||||
const debug = createDebug("nsecbunker:wallet");
|
||||
|
||||
export async function generateWallet(
|
||||
walletConfig: IWalletConfig,
|
||||
username: string,
|
||||
domain: string,
|
||||
npub: string
|
||||
) {
|
||||
debug("generateWallet", walletConfig, username, domain, npub);
|
||||
if (walletConfig.lnbits) {
|
||||
return generateLNBitsWallet(walletConfig.lnbits, username, domain, npub);
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateLNBitsWallet(
|
||||
lnbitsConfig: LNBitsWalletConfig,
|
||||
username: string,
|
||||
domain: string,
|
||||
npub: string
|
||||
) {
|
||||
debug("generateLNBitsWallet", lnbitsConfig, username, domain, npub);
|
||||
|
||||
const url = new URL(lnbitsConfig.url);
|
||||
url.pathname = '/usermanager/api/v1/users';
|
||||
|
||||
const res = await axios.post(url.toString(), {
|
||||
user_name: username,
|
||||
wallet_name: `${username}@${domain}`,
|
||||
}, {
|
||||
headers: {
|
||||
"X-Api-Key": lnbitsConfig.key,
|
||||
},
|
||||
});
|
||||
|
||||
const user = res.data;
|
||||
const wallet = user.wallets[0];
|
||||
|
||||
debug("lnbits response: ", {status: res.status, data: res.data});
|
||||
|
||||
return await generateLNAddress(
|
||||
username,
|
||||
domain,
|
||||
wallet.inkey,
|
||||
npub,
|
||||
'lnbits',
|
||||
lnbitsConfig.url,
|
||||
lnbitsConfig.nostdressUrl,
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateLNAddress(
|
||||
username: string,
|
||||
domain: string,
|
||||
userInvoiceKey: string,
|
||||
userNpub: string,
|
||||
kind: string,
|
||||
host: string,
|
||||
nostdressUrl: string
|
||||
) {
|
||||
debug("generateLNAddress", username, domain, userInvoiceKey, userNpub, kind, host, nostdressUrl);
|
||||
const formData = new URLSearchParams();
|
||||
formData.append('name', username);
|
||||
formData.append('domain', domain);
|
||||
formData.append('kind', kind);
|
||||
formData.append('host', host);
|
||||
formData.append('key', userInvoiceKey);
|
||||
formData.append('pin', ' ');
|
||||
formData.append('npub', userNpub);
|
||||
formData.append('currentName', ' ');
|
||||
|
||||
const url = new URL(nostdressUrl);
|
||||
url.pathname = '/api/easy/';
|
||||
|
||||
debug("nostdress urL: ", url.toString());
|
||||
|
||||
const res = await axios.post(url.toString(), formData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
});
|
||||
|
||||
debug("nostdress response: ", res.data);
|
||||
|
||||
return `${username}@${domain}`;
|
||||
}
|
||||
|
|
@ -6,7 +6,11 @@ import { IConfig, getCurrentConfig, saveCurrentConfig } from "../../../config";
|
|||
import { readFileSync, writeFileSync } from "fs";
|
||||
import { allowAllRequestsFromKey } from "../../lib/acl";
|
||||
import { requestAuthorization } from "../../authorize";
|
||||
import { generateWallet } from "./account/wallet";
|
||||
import prisma from "../../../db";
|
||||
import createDebug from "debug";
|
||||
|
||||
const debug = createDebug("nsecbunker:createAccount");
|
||||
|
||||
export async function validate(currentConfig, username: string, domain: string, email?: string) {
|
||||
if (!username) {
|
||||
|
|
@ -26,10 +30,19 @@ export async function validate(currentConfig, username: string, domain: string,
|
|||
}
|
||||
}
|
||||
|
||||
const emptyNip05File = {
|
||||
names: {},
|
||||
relays: {},
|
||||
}
|
||||
|
||||
async function getCurrentNip05File(currentConfig: any, domain: string) {
|
||||
try {
|
||||
const nip05File = currentConfig.domains[domain].nip05;
|
||||
const file = readFileSync(nip05File, 'utf8');
|
||||
return JSON.parse(file);
|
||||
} catch (e: any) {
|
||||
return emptyNip05File;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -47,12 +60,26 @@ async function addNip05(currentConfig: IConfig, username: string, domain: string
|
|||
writeFileSync(nip05File, JSON.stringify(currentNip05s, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserved usernames that cannot be used since someone might
|
||||
* confuse them with some type of authority of this domain
|
||||
* and scammers are scoundrels
|
||||
*/
|
||||
const RESERVED_USERNAMES = [
|
||||
"admin", "root", "_", "administrator", "__"
|
||||
];
|
||||
|
||||
async function validateUsername(username: string | undefined, domain: string, admin: AdminInterface, req: NDKRpcRequest) {
|
||||
if (!username || username.length === 0) {
|
||||
// create a random username of 10 characters
|
||||
username = Math.random().toString(36).substring(2, 15);
|
||||
}
|
||||
|
||||
// check if the username is available
|
||||
if (RESERVED_USERNAMES.includes(username)) {
|
||||
throw new Error('username not available');
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
|
|
@ -87,8 +114,7 @@ export default async function createAccount(admin: AdminInterface, req: NDKRpcRe
|
|||
const payload: string[] = [ username, domain ];
|
||||
if (email) payload.push(email);
|
||||
|
||||
console.log('requesting authorization', payload);
|
||||
|
||||
debug(`Requesting authorization for ${nip05}`);
|
||||
const authorizationWithPayload = await requestAuthorization(
|
||||
admin,
|
||||
nip05,
|
||||
|
|
@ -97,7 +123,7 @@ export default async function createAccount(admin: AdminInterface, req: NDKRpcRe
|
|||
req.method,
|
||||
JSON.stringify(payload)
|
||||
);
|
||||
console.log('authorizationWithPayload', authorizationWithPayload);
|
||||
debug(`Authorization for ${nip05} ${authorizationWithPayload ? 'granted' : 'denied'}`);
|
||||
|
||||
if (authorizationWithPayload) {
|
||||
const payload = JSON.parse(authorizationWithPayload);
|
||||
|
|
@ -108,6 +134,9 @@ export default async function createAccount(admin: AdminInterface, req: NDKRpcRe
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is where the real work of creating the private key, wallet, nip-05, granting access, etc happen
|
||||
*/
|
||||
export async function createAccountReal(
|
||||
admin: AdminInterface,
|
||||
req: NDKRpcRequest,
|
||||
|
|
@ -115,13 +144,15 @@ export async function createAccountReal(
|
|||
domain: string,
|
||||
email?: string
|
||||
) {
|
||||
// Fetch record since the authorization backend might have changed it
|
||||
|
||||
|
||||
console.log('creating account');
|
||||
try {
|
||||
const currentConfig = await getCurrentConfig(admin.configFile);
|
||||
|
||||
if (!currentConfig.domains) {
|
||||
throw new Error('no domains configured');
|
||||
}
|
||||
|
||||
const domainConfig = currentConfig.domains[domain];
|
||||
|
||||
await validate(currentConfig, username, domain, email);
|
||||
|
||||
const nip05 = `${username}@${domain}`;
|
||||
|
|
@ -129,14 +160,39 @@ export async function createAccountReal(
|
|||
const profile: NDKUserProfile = {
|
||||
display_name: username,
|
||||
name: username,
|
||||
nip05
|
||||
nip05,
|
||||
...(domainConfig.defaultProfile || {})
|
||||
};
|
||||
|
||||
setupSkeletonProfile(key, profile, email);
|
||||
const generatedUser = await key.user();
|
||||
|
||||
debug(`Created user ${generatedUser.npub} for ${nip05}`);
|
||||
|
||||
// Add NIP-05
|
||||
await addNip05(currentConfig, username, domain, generatedUser.pubkey);
|
||||
|
||||
debug(`Added NIP-05 for ${nip05}`);
|
||||
|
||||
// Create wallet
|
||||
if (domainConfig.wallet) {
|
||||
generateWallet(
|
||||
domainConfig.wallet,
|
||||
username, domain, generatedUser.npub
|
||||
).then((lnaddress) => {
|
||||
debug(`wallet for ${nip05}`, {lnaddress});
|
||||
if (lnaddress) profile.lud16 = lnaddress;
|
||||
}).catch((e) => {
|
||||
debug(`error generating wallet for ${nip05}`, e);
|
||||
}).finally(() => {
|
||||
debug(`saving profile for ${nip05}`, profile);
|
||||
setupSkeletonProfile(key, profile, email);
|
||||
})
|
||||
} else {
|
||||
debug(`no wallet configuration for ${domain}`);
|
||||
// Create user profile
|
||||
setupSkeletonProfile(key, profile, email);
|
||||
}
|
||||
|
||||
const keyName = nip05;
|
||||
const nsec = nip19.nsecEncode(key.privateKey!);
|
||||
currentConfig.keys[keyName] = { key: key.privateKey };
|
||||
|
|
@ -148,6 +204,8 @@ export async function createAccountReal(
|
|||
await prisma.key.create({ data: { keyName, pubkey: generatedUser.pubkey } });
|
||||
|
||||
// Immediately grant access to the creator key
|
||||
// This means that the client creating this account can immediately
|
||||
// access it without having to go through an approval flow
|
||||
await grantPermissions(req, keyName);
|
||||
|
||||
return admin.rpc.sendResponse(req.id, req.pubkey, generatedUser.pubkey, NDKKind.NostrConnectAdmin);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import "websocket-polyfill";
|
||||
import NDK, { NDKEvent, NDKKind, NDKPrivateKeySigner, NDKRpcRequest, NDKRpcResponse, NDKUser, NostrEvent } from '@nostr-dev-kit/ndk';
|
||||
import NDK, { NDKKind, NDKPrivateKeySigner, NDKRpcRequest, NDKRpcResponse, NDKUser } from '@nostr-dev-kit/ndk';
|
||||
import { NDKNostrRpc } from '@nostr-dev-kit/ndk';
|
||||
import { debug } from 'debug';
|
||||
import createDebug from 'debug';
|
||||
import { Key, KeyUser } from '../run';
|
||||
import { allowAllRequestsFromKey } from '../lib/acl/index.js';
|
||||
import prisma from '../../db';
|
||||
|
|
@ -18,6 +18,8 @@ import { validateRequestFromAdmin } from './validations/request-from-admin';
|
|||
import { dmUser } from '../../utils/dm-user';
|
||||
import { IConfig, getCurrentConfig } from "../../config";
|
||||
|
||||
const debug = createDebug("nsecbunker:admin");
|
||||
|
||||
export type IAdminOpts = {
|
||||
npubs: string[];
|
||||
adminRelays: string[];
|
||||
|
|
@ -74,7 +76,7 @@ class AdminInterface {
|
|||
});
|
||||
});
|
||||
|
||||
this.rpc = new NDKNostrRpc(this.ndk, this.ndk.signer!, debug("ndk:rpc"));
|
||||
this.rpc = new NDKNostrRpc(this.ndk, this.ndk.signer!, debug);
|
||||
}
|
||||
|
||||
public async config(): Promise<IConfig> {
|
||||
|
|
@ -149,7 +151,7 @@ class AdminInterface {
|
|||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(`Error handling request ${req.method}: ${err?.message??err}`, req.params);
|
||||
debug(`Error handling request ${req.method}: ${err?.message??err}`, req.params);
|
||||
return this.rpc.sendResponse(req.id, req.pubkey, "error", NDKKind.NostrConnectAdmin, err?.message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ export async function requestAuthorization(
|
|||
if (baseUrl) {
|
||||
// If we have a URL, request authorization through web
|
||||
urlAuthFlow(baseUrl, admin, remotePubkey, requestId, request, resolve, reject);
|
||||
}
|
||||
} else {
|
||||
adminAuthFlow(admin, keyName, remotePubkey, method, param, resolve, reject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -41,8 +42,10 @@ async function adminAuthFlow(adminInterface, keyName, remotePubkey, method, para
|
|||
const requestedPerm = await adminInterface.requestPermission(keyName, remotePubkey, method, param);
|
||||
|
||||
if (requestedPerm) {
|
||||
console.log('resolve adminAuthFlow', !!requestedPerm);
|
||||
resolve();
|
||||
} else {
|
||||
console.log('reject adminAuthFlow', !!requestedPerm);
|
||||
reject();
|
||||
}
|
||||
}
|
||||
|
|
@ -54,8 +57,6 @@ async function createRecord(
|
|||
method: string,
|
||||
param?: string | NDKEvent,
|
||||
) {
|
||||
console.trace('createRecord', { keyName, requestId, remotePubkey, method, param});
|
||||
|
||||
let params: string | undefined;
|
||||
|
||||
if (param?.rawEvent) {
|
||||
|
|
@ -114,6 +115,7 @@ export function urlAuthFlow(
|
|||
if (record.allowed === false) {
|
||||
reject(record.payload);
|
||||
}
|
||||
console.log('resolve urlAuthFlow', !!record.params);
|
||||
resolve(record.params);
|
||||
}
|
||||
}, 100);
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
import NDK, { NDKEvent, NDKPrivateKeySigner, NostrEvent, type NDKUserProfile } from "@nostr-dev-kit/ndk";
|
||||
import * as CryptoJS from 'crypto-js';
|
||||
import createDebug from "debug";
|
||||
|
||||
const debug = createDebug("nsecbunker:profile");
|
||||
|
||||
const explicitRelayUrls = [
|
||||
'wss://purplepag.es',
|
||||
'wss://relay.damus.io',
|
||||
'wss://relay.nostr.band',
|
||||
'wss://nos.lol',
|
||||
"wss://nostr.mutinywallet.com"
|
||||
];
|
||||
|
||||
/**
|
||||
* Setup a skeleton profile for a new key since
|
||||
* the experience of a completely empty profile
|
||||
* is pretty bad when logging in with Coracle
|
||||
* is pretty bad when logging in with Coracle.
|
||||
*
|
||||
* @param email - if provided, will fetch the gravatar
|
||||
*/
|
||||
export async function setupSkeletonProfile(key: NDKPrivateKeySigner, profile?: NDKUserProfile, email?: string) {
|
||||
const rand = Math.random().toString(36).substring(7);
|
||||
|
|
@ -27,9 +33,9 @@ export async function setupSkeletonProfile(key: NDKPrivateKeySigner, profile?: N
|
|||
const hash = CryptoJS.MD5(trimmedEmail);
|
||||
const shash = hash.toString(CryptoJS.enc.Hex);
|
||||
profile.image = `https://robohash.org/${shash}?gravatar=hashed&set=set5`;
|
||||
console.log('fetching gravatar', profile.image);
|
||||
debug('fetching gravatar', profile.image);
|
||||
} catch (e) {
|
||||
console.log('error fetching gravatar', e);
|
||||
debug('error fetching gravatar', e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,17 +56,18 @@ export async function setupSkeletonProfile(key: NDKPrivateKeySigner, profile?: N
|
|||
await event.sign(key);
|
||||
|
||||
const t = await event.publish();
|
||||
console.log(t);
|
||||
debug(`Published to ${t.size} relays`);
|
||||
|
||||
event = new NDKEvent(ndk, {
|
||||
kind: 3,
|
||||
tags: [
|
||||
['p', 'fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52'],
|
||||
['p', user.pubkey],
|
||||
],
|
||||
pubkey: user.pubkey,
|
||||
} as NostrEvent);
|
||||
await event.sign(key);
|
||||
console.log(`trying to publish profile`, event.rawEvent());
|
||||
debug(`follow list event`, event.rawEvent());
|
||||
await event.publish();
|
||||
|
||||
const relays = new NDKEvent(ndk, {
|
||||
|
|
@ -69,7 +76,9 @@ export async function setupSkeletonProfile(key: NDKPrivateKeySigner, profile?: N
|
|||
['r', 'wss://purplepag.es'],
|
||||
['r', 'wss://relay.f7z.io'],
|
||||
['r', 'wss://relay.damus.io'],
|
||||
['r', 'wss://nos.lol'],
|
||||
['r', 'wss://relayable.org'],
|
||||
['r', 'wss://relay.nostr.band'],
|
||||
['r', 'wss://relay.primal.net'],
|
||||
],
|
||||
pubkey: user.pubkey,
|
||||
} as NostrEvent);
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ class Daemon {
|
|||
async startKeys() {
|
||||
console.log('🔑 Starting keys', Object.keys(this.config.keys));
|
||||
for (const [name, nsec] of Object.entries(this.config.keys)) {
|
||||
console.log(`🔑 Starting ${name}...`);
|
||||
await this.startKey(name, nsec);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,107 @@
|
|||
import prisma from "../../db";
|
||||
import type { Request } from "@prisma/client";
|
||||
import bcrypt from "bcrypt";
|
||||
import { IAllowScope, allowAllRequestsFromKey } from "../lib/acl";
|
||||
import createDebug from "debug";
|
||||
import { validateRegistration } from "./registration-validations";
|
||||
|
||||
export async function authorizeRequestWebHandler(request, reply) {
|
||||
const debug = createDebug("nsecbunker:authorize");
|
||||
|
||||
/**
|
||||
* TODO: This is still nto being used as no JWT is ever created
|
||||
*/
|
||||
async function validateAuthCookie(request) {
|
||||
const cookies = request.cookies || {};
|
||||
const jwt = cookies.jwt;
|
||||
|
||||
if (!jwt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { pubkey: jwt }
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getAndValidateStateOfRequest(request) {
|
||||
const record = await prisma.request.findUnique({
|
||||
where: { id: request.params.id }
|
||||
});
|
||||
const reqCookies = request.cookies;
|
||||
|
||||
if (!record || record.allowed !== null) {
|
||||
throw new Error("Request not found or already processed");
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the view to authorize a request
|
||||
*/
|
||||
export async function authorizeRequestWebHandler(request, reply) {
|
||||
try {
|
||||
const record = await getAndValidateStateOfRequest(request);
|
||||
const url = new URL(request.url, `http://${request.headers.host}`);
|
||||
const callbackUrl = url.searchParams.get("callbackUrl");
|
||||
|
||||
const method = record.method;
|
||||
let email: string | undefined;
|
||||
let username: string | undefined;
|
||||
let domain: string | undefined;
|
||||
let nip05: string | undefined;
|
||||
|
||||
if (method === "create_account") {
|
||||
const payload = JSON.parse(record.params);
|
||||
const [ username, domain, email ] = payload;
|
||||
const [ username, domain, email ] = JSON.parse(record.params!);
|
||||
nip05 = `${username}@${domain}`;
|
||||
|
||||
return reply.view("/templates/createAccount.handlebar", { record, email, username, domain, nip05, callbackUrl });
|
||||
} else {
|
||||
return reply.view("/templates/authorizeRequest.handlebar", { record, email, username, domain, nip05, callbackUrl });
|
||||
const authorized = validateAuthCookie(request);
|
||||
return reply.view("/templates/authorizeRequest.handlebar", { record, callbackUrl, authorized });
|
||||
}
|
||||
} catch (error: any) {
|
||||
debug(`Error processing request`, error, request);
|
||||
return reply.view("/templates/error.handlebar", { error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates + authenticates a request POSTed to the authorize endpoint
|
||||
*/
|
||||
export async function validateRequest(request, record) {
|
||||
if (await validateAuthCookie(request)) {
|
||||
debug("Already authenticated");
|
||||
return true;
|
||||
}
|
||||
|
||||
const keyName = record.keyName;
|
||||
const [username, domain] = keyName.split("@");
|
||||
|
||||
if (!username || !domain) {
|
||||
throw new Error("Invalid keyName");
|
||||
}
|
||||
|
||||
const password = request.body.password;
|
||||
|
||||
const userRecord = await prisma.user.findUnique({
|
||||
where: { username, domain }
|
||||
});
|
||||
|
||||
if (!userRecord) {
|
||||
debug("No user record found");
|
||||
throw new Error("No user record found");
|
||||
}
|
||||
|
||||
const hashedPassword = userRecord.password;
|
||||
const match = await bcrypt.compare(password, hashedPassword);
|
||||
|
||||
if (!match) {
|
||||
debug("Provided password didn't match")
|
||||
throw new Error("Invalid password");
|
||||
}
|
||||
// return record;
|
||||
}
|
||||
|
||||
export async function processRequestWebHandler(request, reply) {
|
||||
|
|
@ -34,26 +109,25 @@ export async function processRequestWebHandler(request, reply) {
|
|||
where: { id: request.params.id }
|
||||
});
|
||||
|
||||
if (!record) {
|
||||
if (!record || !record.keyName) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await validateRequest(request, record);
|
||||
} catch (e: any) {
|
||||
reply.status(401);
|
||||
reply.type("application/json");
|
||||
return reply.send({ ok: false, error: e.message });
|
||||
}
|
||||
|
||||
await prisma.request.update({
|
||||
where: { id: request.params.id },
|
||||
data: { allowed: true }
|
||||
});
|
||||
|
||||
let allowScope: IAllowScope | undefined;
|
||||
|
||||
const body = request.body;
|
||||
|
||||
console.log({body});
|
||||
|
||||
// if (body.permissions === 'all') {
|
||||
allowScope = {kind: 'all'};
|
||||
// }
|
||||
|
||||
console.log({allowScope});
|
||||
|
||||
await allowAllRequestsFromKey(
|
||||
record.remotePubkey,
|
||||
|
|
@ -64,19 +138,26 @@ export async function processRequestWebHandler(request, reply) {
|
|||
allowScope
|
||||
);
|
||||
|
||||
if (record.method === "connect") {
|
||||
debug("connect, adding sign_event capability");
|
||||
await allowAllRequestsFromKey(
|
||||
record.remotePubkey,
|
||||
record.keyName,
|
||||
"sign_event",
|
||||
undefined,
|
||||
undefined,
|
||||
allowScope
|
||||
);
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export async function processRegistrationWebHandler(request, reply) {
|
||||
const record = await prisma.request.findUnique({
|
||||
where: { id: request.params.id }
|
||||
});
|
||||
try {
|
||||
const record = await getAndValidateStateOfRequest(request);
|
||||
const body = request.body;
|
||||
|
||||
if (!record || record.allowed) {
|
||||
return { ok: false, error: "Request not found or already processed" };
|
||||
}
|
||||
|
||||
// we serialize the payload again and store it
|
||||
// along with the allowed flag
|
||||
// so that the original caller can get the current state
|
||||
|
|
@ -84,10 +165,16 @@ export async function processRegistrationWebHandler(request, reply) {
|
|||
const payload: string[] = [];
|
||||
payload.push(body.username);
|
||||
payload.push(body.domain);
|
||||
payload.push(body.email);
|
||||
payload.push(body.password);
|
||||
|
||||
// TODO: validations here
|
||||
try {
|
||||
await validateRegistration(request, record);
|
||||
} catch (e: any) {
|
||||
const [ username, domain, email ] = JSON.parse(record.params!);
|
||||
const nip05 = `${username}@${domain}`;
|
||||
|
||||
return reply.view("/templates/createAccount.handlebar", { record, email, username, domain, nip05, error: e.message});
|
||||
}
|
||||
|
||||
await prisma.request.update({
|
||||
where: { id: request.params.id },
|
||||
|
|
@ -100,6 +187,9 @@ export async function processRegistrationWebHandler(request, reply) {
|
|||
createdPubkey = await new Promise((resolve) => {
|
||||
const interval = setInterval(async () => {
|
||||
const keyName = record.keyName;
|
||||
|
||||
if (!keyName) throw new Error("Invalid keyName on generated account");
|
||||
|
||||
const keyRecord = await prisma.key.findUnique({ where: { keyName } });
|
||||
|
||||
if (keyRecord) {
|
||||
|
|
@ -110,6 +200,16 @@ export async function processRegistrationWebHandler(request, reply) {
|
|||
}, 100);
|
||||
});
|
||||
|
||||
if (!createdPubkey) throw new Error("No pubkey found for keyName");
|
||||
|
||||
await createUserRecord(
|
||||
body.username,
|
||||
body.domain,
|
||||
createdPubkey,
|
||||
body.email,
|
||||
body.password,
|
||||
)
|
||||
|
||||
const callbackUrlString = body.callbackUrl;
|
||||
let callbackUrl: string | undefined;
|
||||
|
||||
|
|
@ -122,10 +222,6 @@ export async function processRegistrationWebHandler(request, reply) {
|
|||
}
|
||||
}
|
||||
|
||||
// const url = new URL(callbackUrl);
|
||||
|
||||
// add to url a query param with the user's pubkey
|
||||
|
||||
await allowAllRequestsFromKey(
|
||||
record.remotePubkey,
|
||||
record.keyName,
|
||||
|
|
@ -134,7 +230,7 @@ export async function processRegistrationWebHandler(request, reply) {
|
|||
undefined,
|
||||
);
|
||||
|
||||
// redirect to login page
|
||||
// redirect to callbackUrl
|
||||
if (callbackUrl) {
|
||||
return reply
|
||||
.view("/templates/redirect.handlebar", { callbackUrl })
|
||||
|
|
@ -142,4 +238,33 @@ export async function processRegistrationWebHandler(request, reply) {
|
|||
}
|
||||
|
||||
return reply.view("/templates/redirect.handlebar", { callbackUrl });
|
||||
} catch (error: any) {
|
||||
debug(`Error processing registration request`, error, request);
|
||||
return reply.view("/templates/error.handlebar", { error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async function createUserRecord(
|
||||
username: string,
|
||||
domain: string,
|
||||
pubkey: string,
|
||||
email: string,
|
||||
password: string,
|
||||
) {
|
||||
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
debug(`Creating user record for ${username}@${domain}`, {hashedPassword})
|
||||
|
||||
const userRecord = await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
domain,
|
||||
pubkey,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
}
|
||||
});
|
||||
|
||||
return userRecord;
|
||||
}
|
||||
25
src/daemon/web/registration-validations.ts
Normal file
25
src/daemon/web/registration-validations.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import prisma from "../../db";
|
||||
|
||||
export async function validateRegistration(request, record) {
|
||||
// validate username uniqueness
|
||||
const body = request.body;
|
||||
const { username, domain, email, password } = body;
|
||||
|
||||
const userRecord = await prisma.user.findUnique({
|
||||
where: { username, domain }
|
||||
});
|
||||
|
||||
if (userRecord) throw new Error("Username already exists. If this is your account, please login instead.");
|
||||
|
||||
// validate password length
|
||||
if (password.length < 8) throw new Error("Password is too short");
|
||||
|
||||
// validate email (if present)
|
||||
if (email) {
|
||||
if (!email.includes("@")) throw new Error("Invalid email address");
|
||||
|
||||
// validate email uniqueness (if one was provided)
|
||||
const emailRecord = await prisma.user.findUnique({ where: { email } });
|
||||
if (emailRecord) throw new Error("Email already exists");
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,10 @@
|
|||
<script>
|
||||
function sendPostRequest(permissions) {
|
||||
const url = '/requests/{{record.id}}';
|
||||
const password = document.getElementById('password').value;
|
||||
const data = {
|
||||
permissions
|
||||
permissions,
|
||||
password
|
||||
};
|
||||
|
||||
fetch(url, {
|
||||
|
|
@ -26,7 +28,16 @@
|
|||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
console.log('Success:', data);
|
||||
window.close();
|
||||
|
||||
if (data.error) {
|
||||
document.getElementById('error').innerText = data.error;
|
||||
document.getElementById('error').classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
// hide main content and show close message
|
||||
document.getElementById('main').classList.add('hidden');
|
||||
document.getElementById('closeit').classList.remove('hidden');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
|
|
@ -56,14 +67,40 @@
|
|||
</head>
|
||||
<body class="flex flex-col items-center justify-center min-h-screen min-w-screen px-10">
|
||||
<div class="flex justify-center mb-6">
|
||||
<h1 class="text-4xl font-black text-center text-primary w-full">
|
||||
<svg id="_8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 927.56 361.95" class="w-full px-20"><defs><style>.cls-1{fill:none;}.cls-2{fill:#662482;}</style></defs><path class="cls-1" d="m72.63,0h782.3c40.09,0,72.63,32.54,72.63,72.63v216.69c0,40.09-32.54,72.63-72.63,72.63H72.63c-40.09,0-72.63-32.54-72.63-72.63V72.63C0,32.54,32.54,0,72.63,0Z"/><path class="cls-2" d="m857.69,133.61c-16.21.9-29.75,5.48-39.96,13.54-3.97,3.13-9.76.57-9.76-4.49v-.88c0-3.27-2.65-5.92-5.92-5.92h-33.11c-3.27,0-5.92,2.65-5.92,5.92v150.46c0,3.27,2.65,5.92,5.92,5.92h35.22c3.27,0,5.92-2.65,5.92-5.92v-70.7c0-31.07,17.2-45.25,43.14-45.25,1.51,0,2.91.05,4.32.13,3.39.2,6.24-2.51,6.24-5.91v-30.98c0-3.34-2.76-6.11-6.09-5.92Z"/><path class="cls-2" d="m728.78,263.61c-1.24-3.25-4.94-4.53-8.13-3.14-4.09,1.79-8.71,2.72-13.34,2.72-12.67,0-20.21-7.54-20.21-21.42v-96.41c0-3.24,2.62-5.86,5.86-5.86h28.7c3.24,0,5.86-2.62,5.86-5.86v-24.48c0-3.24-2.62-5.86-5.86-5.86h-28.7c-3.24,0-5.86-2.62-5.86-5.86v-27.8c0-3.24-2.62-5.86-5.86-5.86h-35.34c-3.24,0-5.86,2.62-5.86,5.86v27.8c0,3.24-2.62,5.86-5.86,5.86h-13.31c-3.24,0-5.86,2.62-5.86,5.86v24.48c0,3.24,2.62,5.86,5.86,5.86h13.31c3.24,0,5.86,2.62,5.86,5.86v97.01c0,38.62,22.33,58.23,60.64,58.23,12.38,0,24.54-2.42,33.84-7.47,2.62-1.42,3.8-4.54,2.74-7.32l-8.48-22.21Z"/><path class="cls-2" d="m504.8,184.13c0-8.45,9.05-15.09,30.17-15.09,13.14,0,27.23,2.39,41.43,9.27,2.95,1.43,6.49.32,7.88-2.65l10.73-22.9c1.4-2.98.11-6.5-2.86-7.91-15.41-7.32-37.26-11.4-57.19-11.4-47.37,0-75.12,21.72-75.12,52.49,0,64.87,98.05,37.71,98.05,64.26,0,9.05-8.14,14.79-29.87,14.79-17.58,0-36.81-4.94-51.05-12.54-2.99-1.6-6.68-.35-8.1,2.73l-10.7,23.04c-1.33,2.86-.22,6.24,2.55,7.74,15.73,8.5,40.73,14.63,65.48,14.63,48.57,0,76.63-21.42,76.63-51.59,0-63.96-98.05-37.11-98.05-64.86Z"/><path class="cls-2" d="m351.26,133.45c-51.59,0-89.3,34.69-89.3,83.57s37.71,83.57,89.3,83.57,89-34.69,89-83.57-37.41-83.57-89-83.57Zm42.94,109.73c-6.48,24.4-31.8,38.86-56.55,32.29-24.75-6.57-39.57-31.68-33.09-56.08,6.48-24.4,39.62-68.32,64.37-61.74,24.75,6.57,31.75,61.14,25.27,85.54Z"/><path class="cls-2" d="m162.43,133.45c-17.1,0-32.38,4.55-44.29,13.07-3.95,2.83-9.41.12-9.41-4.74,0-3.27-2.65-5.92-5.92-5.92h-33.11c-3.27,0-5.92,2.65-5.92,5.92v150.46c0,3.27,2.65,5.92,5.92,5.92h35.22c3.23,0,5.84-2.6,5.9-5.82v-8.76c0-65.62-34.67-114.78-.38-121.3,31.37-5.98,66.98-3.09,67.4,21.06.04,2.09.32,8.34,8.99,11.75,5.18,2.04,13.16,2.75,23.65,2.48,0,0,9.51-.76,9.51,8.94,0,12.06-21.29,11.21-21.29,11.21-7.05.33-23.65-1.61-33.11,1.23-5.01,1.51-9.36,4.35-12.01,9.55-4.41,8.65-6.51,27.67-6.84,47.54v16.2c0,3.27,2.65,5.92,5.92,5.92h71.12c3.27,0,5.92-2.65,5.92-5.92v-87c0-49.47-28.96-71.8-67.28-71.8Z"/><path class="cls-2" d="m166.12,187.59c0-7-5.67-12.67-12.67-12.67s-12.67,5.68-12.67,12.67,5.67,12.68,12.67,12.68,12.67-5.68,12.67-12.68Z"/><path class="cls-2" d="m229.71,205.25v87c0,3.27-2.65,5.92-5.92,5.92h-71.12c-3.27,0-5.92-2.65-5.92-5.92v-16.2c.33-19.87,2.43-38.89,6.84-47.54,2.65-5.2,7-8.04,12.01-9.55,9.46-2.84,26.06-.9,33.11-1.23,0,0,21.29.85,21.29-11.21,0-9.7-9.51-8.94-9.51-8.94-10.49.27-18.47-.44-23.65-2.48-8.67-3.41-8.95-9.66-8.99-11.75-.42-24.15-36.03-27.04-67.4-21.06-34.29,6.53.38,55.68.38,121.3v8.76c-.06,3.22-2.67,5.82-5.9,5.82h-35.22c-3.27,0-5.92-2.65-5.92-5.92v-150.46c0-3.27,2.65-5.92,5.92-5.92h33.11c3.27,0,5.92,2.65,5.92,5.92,0,4.86,5.46,7.57,9.41,4.74,11.91-8.53,27.19-13.07,44.29-13.07,38.31,0,67.28,22.33,67.28,71.8Zm-63.59-17.66c0-7-5.67-12.67-12.67-12.67s-12.67,5.68-12.67,12.67,5.67,12.68,12.67,12.68,12.67-5.68,12.67-12.68Z"/></svg>
|
||||
</h1>
|
||||
</div>
|
||||
<div id="main">
|
||||
<h1 class="text-center text-2xl font-semibold">Do you want to allow this client to use account
|
||||
<br/>
|
||||
<span class="font-black">{{record.keyName}}</span>?</h1>
|
||||
|
||||
<div id="error" class="flex flex-col gap-4 bg-red-200 rounded-lg p-4 w-full hidden">
|
||||
</div>
|
||||
|
||||
{{#unless authenticated}}
|
||||
<div class="flex flex-col gap-4 mt-8">
|
||||
<div class="flex flex-col gap-2 bg-neutral-200 rounded-lg p-4 w-full">
|
||||
<span>Enter your password to authenticate this request</span>
|
||||
<input type="password" id="password" name="password" class="border border-gray-300 rounded-lg px-4 py-2" placeholder="Password" />
|
||||
</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<div class="flex flex-row items-center justify-center gap-8 mt-8">
|
||||
<button onclick="sendPostRequest()" class="py-3 bg-black hover:bg-neutral-700 transition-all duration-300 text-lg rounded-lg px-10 justify-center items-center gap-2 inline-flex text-white font-semibold">Yes</button>
|
||||
<button onclick="window.close()" class="text-lg px-10 bg-neutral-200 hover:!bg-neutral-300 transition-all duration-200 rounded-lg py-3">No</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden" id="closeit">
|
||||
<div class="flex justify-center mb-6">
|
||||
<p class="text-center text-gray-600">
|
||||
You can close this window now.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- List all cookies -->
|
||||
<script>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,41 @@
|
|||
<link rel="preconnect" href="https://rsms.me/" />
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://unpkg.com/nostr-tools/lib/nostr.bundle.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const form = document.querySelector('form');
|
||||
const email = document.querySelector('input[name="email"]');
|
||||
const password = document.querySelector('input[name="password"]');
|
||||
const confirmPassword = document.querySelector('input[name="confirm_password"]');
|
||||
|
||||
form.addEventListener('submit', function(event) {
|
||||
let valid = true;
|
||||
|
||||
// Check if passwords match and are at least 8 characters long
|
||||
if (password.value !== confirmPassword.value) {
|
||||
alert("Passwords do not match!");
|
||||
valid = false;
|
||||
} else if (password.value.length < 8) {
|
||||
alert("Password must be at least 8 characters long!");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
// Check if email is valid or empty
|
||||
if (email.value) {
|
||||
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
|
||||
if (!emailRegex.test(email.value)) {
|
||||
alert("Please enter a valid email address or leave it empty!");
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html {
|
||||
|
|
@ -39,9 +74,15 @@
|
|||
</div>
|
||||
|
||||
<form action="/register/{{record.id}}" method="POST" class="flex flex-col gap-2">
|
||||
{{#if error}}
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
||||
<span class="block sm:inline">{{error}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<label>
|
||||
<span>Recovery Email</span>
|
||||
<input type="email" name="email" value="{{email}}" required class="w-full px-4 py-3 bg-white rounded-lg shadow border border-neutral-200 justify-start items-center gap-2 inline-flex" />
|
||||
<input type="email" name="email" value="{{email}}" class="w-full px-4 py-3 bg-white rounded-lg shadow border border-neutral-200 justify-start items-center gap-2 inline-flex" />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
|
|
|
|||
33
templates/error.handlebar
Normal file
33
templates/error.handlebar
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - Nostr</title>
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
html {
|
||||
font-family: Inter, sans-serif;
|
||||
}
|
||||
|
||||
label > span {
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="p-2 flex flex-col justify-center items-center min-h-screen bg-gray-100">
|
||||
<div class="max-w-lg w-full">
|
||||
<div class="flex justify-center mb-6">
|
||||
<h1 class="text-4xl font-black text-center text-primary w-full">
|
||||
<svg id="_8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 927.56 361.95" class="w-full px-20"><defs><style>.cls-1{fill:none;}.cls-2{fill:#662482;}</style></defs><path class="cls-1" d="m72.63,0h782.3c40.09,0,72.63,32.54,72.63,72.63v216.69c0,40.09-32.54,72.63-72.63,72.63H72.63c-40.09,0-72.63-32.54-72.63-72.63V72.63C0,32.54,32.54,0,72.63,0Z"/><path class="cls-2" d="m857.69,133.61c-16.21.9-29.75,5.48-39.96,13.54-3.97,3.13-9.76.57-9.76-4.49v-.88c0-3.27-2.65-5.92-5.92-5.92h-33.11c-3.27,0-5.92,2.65-5.92,5.92v150.46c0,3.27,2.65,5.92,5.92,5.92h35.22c3.27,0,5.92-2.65,5.92-5.92v-70.7c0-31.07,17.2-45.25,43.14-45.25,1.51,0,2.91.05,4.32.13,3.39.2,6.24-2.51,6.24-5.91v-30.98c0-3.34-2.76-6.11-6.09-5.92Z"/><path class="cls-2" d="m728.78,263.61c-1.24-3.25-4.94-4.53-8.13-3.14-4.09,1.79-8.71,2.72-13.34,2.72-12.67,0-20.21-7.54-20.21-21.42v-96.41c0-3.24,2.62-5.86,5.86-5.86h28.7c3.24,0,5.86-2.62,5.86-5.86v-24.48c0-3.24-2.62-5.86-5.86-5.86h-28.7c-3.24,0-5.86-2.62-5.86-5.86v-27.8c0-3.24-2.62-5.86-5.86-5.86h-35.34c-3.24,0-5.86,2.62-5.86,5.86v27.8c0,3.24-2.62,5.86-5.86,5.86h-13.31c-3.24,0-5.86,2.62-5.86,5.86v24.48c0,3.24,2.62,5.86,5.86,5.86h13.31c3.24,0,5.86,2.62,5.86,5.86v97.01c0,38.62,22.33,58.23,60.64,58.23,12.38,0,24.54-2.42,33.84-7.47,2.62-1.42,3.8-4.54,2.74-7.32l-8.48-22.21Z"/><path class="cls-2" d="m504.8,184.13c0-8.45,9.05-15.09,30.17-15.09,13.14,0,27.23,2.39,41.43,9.27,2.95,1.43,6.49.32,7.88-2.65l10.73-22.9c1.4-2.98.11-6.5-2.86-7.91-15.41-7.32-37.26-11.4-57.19-11.4-47.37,0-75.12,21.72-75.12,52.49,0,64.87,98.05,37.71,98.05,64.26,0,9.05-8.14,14.79-29.87,14.79-17.58,0-36.81-4.94-51.05-12.54-2.99-1.6-6.68-.35-8.1,2.73l-10.7,23.04c-1.33,2.86-.22,6.24,2.55,7.74,15.73,8.5,40.73,14.63,65.48,14.63,48.57,0,76.63-21.42,76.63-51.59,0-63.96-98.05-37.11-98.05-64.86Z"/><path class="cls-2" d="m351.26,133.45c-51.59,0-89.3,34.69-89.3,83.57s37.71,83.57,89.3,83.57,89-34.69,89-83.57-37.41-83.57-89-83.57Zm42.94,109.73c-6.48,24.4-31.8,38.86-56.55,32.29-24.75-6.57-39.57-31.68-33.09-56.08,6.48-24.4,39.62-68.32,64.37-61.74,24.75,6.57,31.75,61.14,25.27,85.54Z"/><path class="cls-2" d="m162.43,133.45c-17.1,0-32.38,4.55-44.29,13.07-3.95,2.83-9.41.12-9.41-4.74,0-3.27-2.65-5.92-5.92-5.92h-33.11c-3.27,0-5.92,2.65-5.92,5.92v150.46c0,3.27,2.65,5.92,5.92,5.92h35.22c3.23,0,5.84-2.6,5.9-5.82v-8.76c0-65.62-34.67-114.78-.38-121.3,31.37-5.98,66.98-3.09,67.4,21.06.04,2.09.32,8.34,8.99,11.75,5.18,2.04,13.16,2.75,23.65,2.48,0,0,9.51-.76,9.51,8.94,0,12.06-21.29,11.21-21.29,11.21-7.05.33-23.65-1.61-33.11,1.23-5.01,1.51-9.36,4.35-12.01,9.55-4.41,8.65-6.51,27.67-6.84,47.54v16.2c0,3.27,2.65,5.92,5.92,5.92h71.12c3.27,0,5.92-2.65,5.92-5.92v-87c0-49.47-28.96-71.8-67.28-71.8Z"/><path class="cls-2" d="m166.12,187.59c0-7-5.67-12.67-12.67-12.67s-12.67,5.68-12.67,12.67,5.67,12.68,12.67,12.68,12.67-5.68,12.67-12.68Z"/><path class="cls-2" d="m229.71,205.25v87c0,3.27-2.65,5.92-5.92,5.92h-71.12c-3.27,0-5.92-2.65-5.92-5.92v-16.2c.33-19.87,2.43-38.89,6.84-47.54,2.65-5.2,7-8.04,12.01-9.55,9.46-2.84,26.06-.9,33.11-1.23,0,0,21.29.85,21.29-11.21,0-9.7-9.51-8.94-9.51-8.94-10.49.27-18.47-.44-23.65-2.48-8.67-3.41-8.95-9.66-8.99-11.75-.42-24.15-36.03-27.04-67.4-21.06-34.29,6.53.38,55.68.38,121.3v8.76c-.06,3.22-2.67,5.82-5.9,5.82h-35.22c-3.27,0-5.92-2.65-5.92-5.92v-150.46c0-3.27,2.65-5.92,5.92-5.92h33.11c3.27,0,5.92,2.65,5.92,5.92,0,4.86,5.46,7.57,9.41,4.74,11.91-8.53,27.19-13.07,44.29-13.07,38.31,0,67.28,22.33,67.28,71.8Zm-63.59-17.66c0-7-5.67-12.67-12.67-12.67s-12.67,5.68-12.67,12.67,5.67,12.68,12.67,12.68,12.67-5.68,12.67-12.68Z"/></svg>
|
||||
Login to Nostr
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div id="error" class="flex flex-col gap-4 bg-red-200 rounded-lg p-4 w-full">
|
||||
{{error}}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
49
templates/login.handlebar
Normal file
49
templates/login.handlebar
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - Nostr</title>
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
html {
|
||||
font-family: Inter, sans-serif;
|
||||
}
|
||||
|
||||
label > span {
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="p-2 flex flex-col justify-center items-center min-h-screen bg-gray-100">
|
||||
<div class="max-w-lg w-full">
|
||||
<div class="flex justify-center mb-6">
|
||||
<h1 class="text-4xl font-black text-center text-primary w-full">
|
||||
<svg id="_8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 927.56 361.95" class="w-full px-20"><defs><style>.cls-1{fill:none;}.cls-2{fill:#662482;}</style></defs><path class="cls-1" d="m72.63,0h782.3c40.09,0,72.63,32.54,72.63,72.63v216.69c0,40.09-32.54,72.63-72.63,72.63H72.63c-40.09,0-72.63-32.54-72.63-72.63V72.63C0,32.54,32.54,0,72.63,0Z"/><path class="cls-2" d="m857.69,133.61c-16.21.9-29.75,5.48-39.96,13.54-3.97,3.13-9.76.57-9.76-4.49v-.88c0-3.27-2.65-5.92-5.92-5.92h-33.11c-3.27,0-5.92,2.65-5.92,5.92v150.46c0,3.27,2.65,5.92,5.92,5.92h35.22c3.27,0,5.92-2.65,5.92-5.92v-70.7c0-31.07,17.2-45.25,43.14-45.25,1.51,0,2.91.05,4.32.13,3.39.2,6.24-2.51,6.24-5.91v-30.98c0-3.34-2.76-6.11-6.09-5.92Z"/><path class="cls-2" d="m728.78,263.61c-1.24-3.25-4.94-4.53-8.13-3.14-4.09,1.79-8.71,2.72-13.34,2.72-12.67,0-20.21-7.54-20.21-21.42v-96.41c0-3.24,2.62-5.86,5.86-5.86h28.7c3.24,0,5.86-2.62,5.86-5.86v-24.48c0-3.24-2.62-5.86-5.86-5.86h-28.7c-3.24,0-5.86-2.62-5.86-5.86v-27.8c0-3.24-2.62-5.86-5.86-5.86h-35.34c-3.24,0-5.86,2.62-5.86,5.86v27.8c0,3.24-2.62,5.86-5.86,5.86h-13.31c-3.24,0-5.86,2.62-5.86,5.86v24.48c0,3.24,2.62,5.86,5.86,5.86h13.31c3.24,0,5.86,2.62,5.86,5.86v97.01c0,38.62,22.33,58.23,60.64,58.23,12.38,0,24.54-2.42,33.84-7.47,2.62-1.42,3.8-4.54,2.74-7.32l-8.48-22.21Z"/><path class="cls-2" d="m504.8,184.13c0-8.45,9.05-15.09,30.17-15.09,13.14,0,27.23,2.39,41.43,9.27,2.95,1.43,6.49.32,7.88-2.65l10.73-22.9c1.4-2.98.11-6.5-2.86-7.91-15.41-7.32-37.26-11.4-57.19-11.4-47.37,0-75.12,21.72-75.12,52.49,0,64.87,98.05,37.71,98.05,64.26,0,9.05-8.14,14.79-29.87,14.79-17.58,0-36.81-4.94-51.05-12.54-2.99-1.6-6.68-.35-8.1,2.73l-10.7,23.04c-1.33,2.86-.22,6.24,2.55,7.74,15.73,8.5,40.73,14.63,65.48,14.63,48.57,0,76.63-21.42,76.63-51.59,0-63.96-98.05-37.11-98.05-64.86Z"/><path class="cls-2" d="m351.26,133.45c-51.59,0-89.3,34.69-89.3,83.57s37.71,83.57,89.3,83.57,89-34.69,89-83.57-37.41-83.57-89-83.57Zm42.94,109.73c-6.48,24.4-31.8,38.86-56.55,32.29-24.75-6.57-39.57-31.68-33.09-56.08,6.48-24.4,39.62-68.32,64.37-61.74,24.75,6.57,31.75,61.14,25.27,85.54Z"/><path class="cls-2" d="m162.43,133.45c-17.1,0-32.38,4.55-44.29,13.07-3.95,2.83-9.41.12-9.41-4.74,0-3.27-2.65-5.92-5.92-5.92h-33.11c-3.27,0-5.92,2.65-5.92,5.92v150.46c0,3.27,2.65,5.92,5.92,5.92h35.22c3.23,0,5.84-2.6,5.9-5.82v-8.76c0-65.62-34.67-114.78-.38-121.3,31.37-5.98,66.98-3.09,67.4,21.06.04,2.09.32,8.34,8.99,11.75,5.18,2.04,13.16,2.75,23.65,2.48,0,0,9.51-.76,9.51,8.94,0,12.06-21.29,11.21-21.29,11.21-7.05.33-23.65-1.61-33.11,1.23-5.01,1.51-9.36,4.35-12.01,9.55-4.41,8.65-6.51,27.67-6.84,47.54v16.2c0,3.27,2.65,5.92,5.92,5.92h71.12c3.27,0,5.92-2.65,5.92-5.92v-87c0-49.47-28.96-71.8-67.28-71.8Z"/><path class="cls-2" d="m166.12,187.59c0-7-5.67-12.67-12.67-12.67s-12.67,5.68-12.67,12.67,5.67,12.68,12.67,12.68,12.67-5.68,12.67-12.68Z"/><path class="cls-2" d="m229.71,205.25v87c0,3.27-2.65,5.92-5.92,5.92h-71.12c-3.27,0-5.92-2.65-5.92-5.92v-16.2c.33-19.87,2.43-38.89,6.84-47.54,2.65-5.2,7-8.04,12.01-9.55,9.46-2.84,26.06-.9,33.11-1.23,0,0,21.29.85,21.29-11.21,0-9.7-9.51-8.94-9.51-8.94-10.49.27-18.47-.44-23.65-2.48-8.67-3.41-8.95-9.66-8.99-11.75-.42-24.15-36.03-27.04-67.4-21.06-34.29,6.53.38,55.68.38,121.3v8.76c-.06,3.22-2.67,5.82-5.9,5.82h-35.22c-3.27,0-5.92-2.65-5.92-5.92v-150.46c0-3.27,2.65-5.92,5.92-5.92h33.11c3.27,0,5.92,2.65,5.92,5.92,0,4.86,5.46,7.57,9.41,4.74,11.91-8.53,27.19-13.07,44.29-13.07,38.31,0,67.28,22.33,67.28,71.8Zm-63.59-17.66c0-7-5.67-12.67-12.67-12.67s-12.67,5.68-12.67,12.67,5.67,12.68,12.67,12.68,12.67-5.68,12.67-12.68Z"/></svg>
|
||||
Login to Nostr
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<form action="/login" method="POST" class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2">
|
||||
<span>Username</span>
|
||||
<input type="text" name="username" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required value={{nip05}}>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2">
|
||||
<span>Password</span>
|
||||
<input type="password" name="password" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" required>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<button type="submit" class="bg-black text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
|
||||
Log In
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -68,6 +68,7 @@
|
|||
<script>
|
||||
// If callbackUrl is not provided, close the window
|
||||
if (!"{{callbackUrl}}") {
|
||||
window.close();
|
||||
} else {
|
||||
// If callbackUrl is provided, redirect to it
|
||||
window.location.href = "{{callbackUrl}}";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue