mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-05 08:42:02 +00:00
Nostr login flow
This commit is contained in:
parent
2de8b23d07
commit
1f9d76d783
@ -21,6 +21,8 @@ services:
|
|||||||
- .env
|
- .env
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
links:
|
links:
|
||||||
- db
|
- db
|
||||||
|
|
||||||
|
292
package-lock.json
generated
292
package-lock.json
generated
@ -8,9 +8,12 @@
|
|||||||
"name": "plebdevs-new",
|
"name": "plebdevs-new",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@prisma/client": "^5.9.1",
|
||||||
"@reduxjs/toolkit": "^2.1.0",
|
"@reduxjs/toolkit": "^2.1.0",
|
||||||
|
"axios": "^1.6.7",
|
||||||
"next": "14.0.4",
|
"next": "14.0.4",
|
||||||
"next-auth": "^4.24.5",
|
"next-auth": "^4.24.5",
|
||||||
|
"nostr-tools": "^2.1.5",
|
||||||
"primeicons": "^6.0.1",
|
"primeicons": "^6.0.1",
|
||||||
"primereact": "^10.2.1",
|
"primereact": "^10.2.1",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
@ -23,6 +26,7 @@
|
|||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.0.4",
|
"eslint-config-next": "14.0.4",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
|
"prisma": "^5.9.1",
|
||||||
"tailwindcss": "^3.3.0"
|
"tailwindcss": "^3.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -388,6 +392,47 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@noble/ciphers": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-6YBxJDAapHSdd3bLDv6x2wRPwq4QFMUaB3HvljNBUTThDd12eSm7/3F+2lnfzx2jvM+S6Nsy0jEt9QbPqSwqRw==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@noble/curves": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.3.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@noble/curves/node_modules/@noble/hashes": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@noble/hashes": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -441,6 +486,68 @@
|
|||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@prisma/client": {
|
||||||
|
"version": "5.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.9.1.tgz",
|
||||||
|
"integrity": "sha512-caSOnG4kxcSkhqC/2ShV7rEoWwd3XrftokxJqOCMVvia4NYV/TPtJlS9C2os3Igxw/Qyxumj9GBQzcStzECvtQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.13"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"prisma": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"prisma": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/debug": {
|
||||||
|
"version": "5.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.9.1.tgz",
|
||||||
|
"integrity": "sha512-yAHFSFCg8KVoL0oRUno3m60GAjsUKYUDkQ+9BA2X2JfVR3kRVSJFc/GpQ2fSORi4pSHZR9orfM4UC9OVXIFFTA==",
|
||||||
|
"devOptional": true
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines": {
|
||||||
|
"version": "5.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.9.1.tgz",
|
||||||
|
"integrity": "sha512-gkdXmjxQ5jktxWNdDA5aZZ6R8rH74JkoKq6LD5mACSvxd2vbqWeWIOV0Py5wFC8vofOYShbt6XUeCIUmrOzOnQ==",
|
||||||
|
"devOptional": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.9.1",
|
||||||
|
"@prisma/engines-version": "5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64",
|
||||||
|
"@prisma/fetch-engine": "5.9.1",
|
||||||
|
"@prisma/get-platform": "5.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines-version": {
|
||||||
|
"version": "5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64.tgz",
|
||||||
|
"integrity": "sha512-HFl7275yF0FWbdcNvcSRbbu9JCBSLMcurYwvWc8WGDnpu7APxQo2ONtZrUggU3WxLxUJ2uBX+0GOFIcJeVeOOQ==",
|
||||||
|
"devOptional": true
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/fetch-engine": {
|
||||||
|
"version": "5.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.9.1.tgz",
|
||||||
|
"integrity": "sha512-l0goQOMcNVOJs1kAcwqpKq3ylvkD9F04Ioe1oJoCqmz05mw22bNAKKGWuDd3zTUoUZr97va0c/UfLNru+PDmNA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.9.1",
|
||||||
|
"@prisma/engines-version": "5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64",
|
||||||
|
"@prisma/get-platform": "5.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/get-platform": {
|
||||||
|
"version": "5.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.9.1.tgz",
|
||||||
|
"integrity": "sha512-6OQsNxTyhvG+T2Ksr8FPFpuPeL4r9u0JF0OZHUBI/Uy9SS43sPyAIutt4ZEAyqWQt104ERh70EZedkHZKsnNbg==",
|
||||||
|
"devOptional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@reduxjs/toolkit": {
|
"node_modules/@reduxjs/toolkit": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.1.0.tgz",
|
||||||
@ -470,6 +577,53 @@
|
|||||||
"integrity": "sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw==",
|
"integrity": "sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@scure/base": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@scure/bip32": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/curves": "~1.1.0",
|
||||||
|
"@noble/hashes": "~1.3.1",
|
||||||
|
"@scure/base": "~1.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@scure/bip32/node_modules/@noble/curves": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.3.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@scure/bip39": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "~1.3.0",
|
||||||
|
"@scure/base": "~1.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@swc/helpers": {
|
"node_modules/@swc/helpers": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||||
@ -871,6 +1025,11 @@
|
|||||||
"has-symbols": "^1.0.3"
|
"has-symbols": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.16",
|
"version": "10.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz",
|
||||||
@ -929,6 +1088,16 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
|
||||||
|
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.4",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/axobject-query": {
|
"node_modules/axobject-query": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
|
||||||
@ -1147,6 +1316,17 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/commander": {
|
"node_modules/commander": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
@ -1261,6 +1441,14 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dequal": {
|
"node_modules/dequal": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||||
@ -2007,6 +2195,25 @@
|
|||||||
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
|
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||||
|
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||||
@ -2032,6 +2239,19 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fraction.js": {
|
"node_modules/fraction.js": {
|
||||||
"version": "4.3.7",
|
"version": "4.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||||
@ -2985,6 +3205,25 @@
|
|||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
@ -3179,6 +3418,36 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nostr-tools": {
|
||||||
|
"version": "2.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.1.5.tgz",
|
||||||
|
"integrity": "sha512-Gug/j54YGQ0ewB09dZW3mS9qfXWFlcOQMlyb1MmqQsuNO/95mfNOQSBi+jZ61O++Y+jG99SzAUPFLopUsKf0MA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/ciphers": "0.2.0",
|
||||||
|
"@noble/curves": "1.2.0",
|
||||||
|
"@noble/hashes": "1.3.1",
|
||||||
|
"@scure/base": "1.1.1",
|
||||||
|
"@scure/bip32": "1.3.1",
|
||||||
|
"@scure/bip39": "1.2.1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"nostr-wasm": "v0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nostr-wasm": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/oauth": {
|
"node_modules/oauth": {
|
||||||
"version": "0.9.15",
|
"version": "0.9.15",
|
||||||
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||||
@ -3717,6 +3986,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prisma": {
|
||||||
|
"version": "5.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.9.1.tgz",
|
||||||
|
"integrity": "sha512-Hy/8KJZz0ELtkw4FnG9MS9rNWlXcJhf98Z2QMqi0QiVMoS8PzsBkpla0/Y5hTlob8F3HeECYphBjqmBxrluUrQ==",
|
||||||
|
"devOptional": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/engines": "5.9.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"prisma": "build/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prop-types": {
|
"node_modules/prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
@ -3727,6 +4012,11 @@
|
|||||||
"react-is": "^16.13.1"
|
"react-is": "^16.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
@ -4648,7 +4938,7 @@
|
|||||||
"version": "5.3.3",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
|
@ -9,9 +9,12 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@prisma/client": "^5.9.1",
|
||||||
"@reduxjs/toolkit": "^2.1.0",
|
"@reduxjs/toolkit": "^2.1.0",
|
||||||
|
"axios": "^1.6.7",
|
||||||
"next": "14.0.4",
|
"next": "14.0.4",
|
||||||
"next-auth": "^4.24.5",
|
"next-auth": "^4.24.5",
|
||||||
|
"nostr-tools": "^2.1.5",
|
||||||
"primeicons": "^6.0.1",
|
"primeicons": "^6.0.1",
|
||||||
"primereact": "^10.2.1",
|
"primereact": "^10.2.1",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
@ -24,6 +27,7 @@
|
|||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.0.4",
|
"eslint-config-next": "14.0.4",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
|
"prisma": "^5.9.1",
|
||||||
"tailwindcss": "^3.3.0"
|
"tailwindcss": "^3.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
87
prisma/migrations/20240211045317_init/migration.sql
Normal file
87
prisma/migrations/20240211045317_init/migration.sql
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "User" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"pubkey" TEXT NOT NULL,
|
||||||
|
"username" TEXT,
|
||||||
|
"roleId" INTEGER,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Role" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"subscribed" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
|
CONSTRAINT "Role_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Purchase" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"userId" INTEGER NOT NULL,
|
||||||
|
"courseId" INTEGER,
|
||||||
|
"resourceId" INTEGER,
|
||||||
|
"amountPaid" INTEGER NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Purchase_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Course" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"description" TEXT NOT NULL,
|
||||||
|
"image" TEXT NOT NULL,
|
||||||
|
"isFree" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"noteId" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Course_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Resource" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"image" TEXT,
|
||||||
|
"courseId" INTEGER,
|
||||||
|
"noteId" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Resource_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "User_pubkey_key" ON "User"("pubkey");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Course_noteId_key" ON "Course"("noteId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Resource_noteId_key" ON "Resource"("noteId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "User" ADD CONSTRAINT "User_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Purchase" ADD CONSTRAINT "Purchase_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Purchase" ADD CONSTRAINT "Purchase_courseId_fkey" FOREIGN KEY ("courseId") REFERENCES "Course"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Purchase" ADD CONSTRAINT "Purchase_resourceId_fkey" FOREIGN KEY ("resourceId") REFERENCES "Resource"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Resource" ADD CONSTRAINT "Resource_courseId_fkey" FOREIGN KEY ("courseId") REFERENCES "Course"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (i.e. Git)
|
||||||
|
provider = "postgresql"
|
@ -3,9 +3,13 @@ datasource db {
|
|||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
npub String @unique
|
pubkey String @unique
|
||||||
username String? @unique
|
username String? @unique
|
||||||
purchased Purchase[]
|
purchased Purchase[]
|
||||||
role Role? @relation(fields: [roleId], references: [id])
|
role Role? @relation(fields: [roleId], references: [id])
|
||||||
|
@ -1,19 +1,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { Menubar } from 'primereact/menubar';
|
import { Menubar } from 'primereact/menubar';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import 'primereact/resources/primereact.min.css';
|
import 'primereact/resources/primereact.min.css';
|
||||||
import 'primeicons/primeicons.css';
|
import 'primeicons/primeicons.css';
|
||||||
import styles from './navbar.module.css';
|
import styles from './navbar.module.css';
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const user = useSelector((state) => state.user);
|
||||||
|
|
||||||
const end = (
|
const end = (
|
||||||
|
(user && user?.username || user.pubkey) ?
|
||||||
|
<h1>{user.username || user.pubkey}</h1>
|
||||||
|
:
|
||||||
<Button
|
<Button
|
||||||
label={"Login"}
|
label={"Login"}
|
||||||
icon="pi pi-user"
|
icon="pi pi-user"
|
||||||
className="text-[#f8f8ff]"
|
className="text-[#f8f8ff]"
|
||||||
rounded
|
rounded
|
||||||
|
onClick={() => router.push('/login')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -29,6 +29,21 @@ export const getUserById = async (id) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getUserByPubkey = async (pubkey) => {
|
||||||
|
return await prisma.user.findUnique({
|
||||||
|
where: { pubkey },
|
||||||
|
include: {
|
||||||
|
role: true, // Include related role
|
||||||
|
purchased: {
|
||||||
|
include: {
|
||||||
|
course: true, // Include course details in purchases
|
||||||
|
resource: true, // Include resource details in purchases
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export const createUser = async (data) => {
|
export const createUser = async (data) => {
|
||||||
return await prisma.user.create({
|
return await prisma.user.create({
|
||||||
data,
|
data,
|
||||||
|
106
src/hooks/useNostr.js
Normal file
106
src/hooks/useNostr.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { useState, useEffect, useRef } from "react";
|
||||||
|
import { SimplePool, relayInit, nip19 } from "nostr-tools";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { initialRelays } from "@/redux/reducers/userReducer";
|
||||||
|
|
||||||
|
export const useNostr = () => {
|
||||||
|
const [relays, setRelays] = useState(initialRelays);
|
||||||
|
const [relayStatuses, setRelayStatuses] = useState({});
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const pool = useRef(new SimplePool({ seenOnEnabled: true }));
|
||||||
|
const subscriptions = useRef([]);
|
||||||
|
|
||||||
|
const getRelayStatuses = () => {
|
||||||
|
if (pool.current && pool.current._conn) {
|
||||||
|
const statuses = {};
|
||||||
|
|
||||||
|
for (const url in pool.current._conn) {
|
||||||
|
const relay = pool.current._conn[url];
|
||||||
|
statuses[url] = relay.status; // Assuming 'status' is an accessible field in Relay object
|
||||||
|
}
|
||||||
|
|
||||||
|
setRelayStatuses(statuses);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateRelays = async (newRelays) => {
|
||||||
|
// Set new relays
|
||||||
|
setRelays(newRelays);
|
||||||
|
|
||||||
|
// Ensure the relays are connected before using them
|
||||||
|
await Promise.all(newRelays.map(relay => pool.current.ensureRelay(relay)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchKind0 = async (criteria, params) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const events = [];
|
||||||
|
const timeoutDuration = 1000;
|
||||||
|
|
||||||
|
const sub = pool.current.subscribeMany(relays, criteria, {
|
||||||
|
...params,
|
||||||
|
onevent: (event) => {
|
||||||
|
events.push(event);
|
||||||
|
},
|
||||||
|
onerror: (error) => {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set a timeout to sort and resolve with the most recent event
|
||||||
|
setTimeout(() => {
|
||||||
|
if (events.length === 0) {
|
||||||
|
resolve(null); // or reject based on your needs
|
||||||
|
} else {
|
||||||
|
events.sort((a, b) => b.created_at - a.created_at); // Sort in descending order
|
||||||
|
const mostRecentEventContent = JSON.parse(events[0].content);
|
||||||
|
resolve(mostRecentEventContent);
|
||||||
|
}
|
||||||
|
}, timeoutDuration);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchSingleEvent = async (id) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const sub = pool.current.subscribeMany(relays, [{ ids: [id] }]);
|
||||||
|
|
||||||
|
sub.on("event", (event) => {
|
||||||
|
resolve(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.on("error", (error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const publishEvent = async (event) => {
|
||||||
|
try {
|
||||||
|
const publishPromises = pool.current.publish(relays, event);
|
||||||
|
await Promise.all(publishPromises);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error publishing event:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getRelayStatuses(); // Get initial statuses on mount
|
||||||
|
|
||||||
|
// Copy current subscriptions to a local variable inside the effect
|
||||||
|
const currentSubscriptions = subscriptions.current;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// Use the local variable in the cleanup function
|
||||||
|
currentSubscriptions.forEach((sub) => sub.unsub());
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateRelays,
|
||||||
|
fetchSingleEvent,
|
||||||
|
publishEvent,
|
||||||
|
fetchKind0,
|
||||||
|
getRelayStatuses,
|
||||||
|
};
|
||||||
|
};
|
21
src/hooks/useToast.js
Normal file
21
src/hooks/useToast.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React, { createContext, useContext, useRef } from 'react';
|
||||||
|
import { Toast } from 'primereact/toast';
|
||||||
|
|
||||||
|
const ToastContext = createContext();
|
||||||
|
|
||||||
|
export const useToast = () => useContext(ToastContext);
|
||||||
|
|
||||||
|
export const ToastProvider = ({ children }) => {
|
||||||
|
const toast = useRef(null);
|
||||||
|
|
||||||
|
const showToast = (severity, summary, detail) => {
|
||||||
|
toast.current.show({ severity, summary, detail });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToastContext.Provider value={{ showToast }}>
|
||||||
|
<Toast ref={toast} />
|
||||||
|
{children}
|
||||||
|
</ToastContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
@ -2,6 +2,7 @@ import { PrimeReactProvider } from 'primereact/api';
|
|||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { store } from "@/redux/store";
|
import { store } from "@/redux/store";
|
||||||
import Navbar from '@/components/navbar/Navbar';
|
import Navbar from '@/components/navbar/Navbar';
|
||||||
|
import { ToastProvider } from '@/hooks/useToast';
|
||||||
import '@/styles/globals.css'
|
import '@/styles/globals.css'
|
||||||
import 'primereact/resources/themes/lara-dark-indigo/theme.css';
|
import 'primereact/resources/themes/lara-dark-indigo/theme.css';
|
||||||
|
|
||||||
@ -11,8 +12,10 @@ export default function MyApp({
|
|||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<PrimeReactProvider>
|
<PrimeReactProvider>
|
||||||
<Navbar />
|
<ToastProvider>
|
||||||
<Component {...pageProps} />
|
<Navbar />
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</ToastProvider>
|
||||||
</PrimeReactProvider>
|
</PrimeReactProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
@ -1,36 +1,59 @@
|
|||||||
import { getUserById, updateUser, deleteUser } from "@/db/models/userModels";
|
import { getUserById, getUserByPubkey, updateUser, deleteUser } from "@/db/models/userModels";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const { slug } = req.query;
|
const { slug } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
// Determine if slug is a pubkey or an ID
|
||||||
try {
|
const isPubkey = /^[0-9a-fA-F]{64}$/.test(slug);
|
||||||
const user = await getUserById(parseInt(slug));
|
|
||||||
if (user) {
|
try {
|
||||||
res.status(200).json(user);
|
let user;
|
||||||
} else {
|
if (isPubkey) {
|
||||||
res.status(404).json({ error: 'User not found' });
|
console.log('is pub', slug);
|
||||||
|
// If slug is a pubkey
|
||||||
|
user = await getUserByPubkey(slug);
|
||||||
|
} else {
|
||||||
|
// Assume slug is an ID
|
||||||
|
const id = parseInt(slug);
|
||||||
|
if (isNaN(id)) {
|
||||||
|
return res.status(400).json({ error: "Invalid identifier" });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
user = await getUserById(id);
|
||||||
res.status(500).json({ error: error.message });
|
|
||||||
}
|
}
|
||||||
} else if (req.method === 'PUT') {
|
|
||||||
try {
|
if (!user) {
|
||||||
const user = await updateUser(parseInt(slug), req.body);
|
return res.status(204).end();
|
||||||
res.status(200).json(user);
|
|
||||||
} catch (error) {
|
|
||||||
res.status(400).json({ error: error.message });
|
|
||||||
}
|
}
|
||||||
} else if (req.method === 'DELETE') {
|
|
||||||
try {
|
switch (req.method) {
|
||||||
await deleteUser(parseInt(slug));
|
case 'GET':
|
||||||
res.status(204).end();
|
return res.status(200).json(user);
|
||||||
} catch (error) {
|
|
||||||
res.status(500).json({ error: error.message });
|
case 'PUT':
|
||||||
|
if (!isPubkey) {
|
||||||
|
// Update operation should be done with an ID, not a pubkey
|
||||||
|
const updatedUser = await updateUser(parseInt(slug), req.body);
|
||||||
|
return res.status(200).json(updatedUser);
|
||||||
|
} else {
|
||||||
|
// Handle attempt to update user with pubkey
|
||||||
|
return res.status(400).json({ error: "Cannot update user with pubkey. Use ID instead." });
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'DELETE':
|
||||||
|
if (!isPubkey) {
|
||||||
|
// Delete operation should be done with an ID, not a pubkey
|
||||||
|
await deleteUser(parseInt(slug));
|
||||||
|
return res.status(204).end();
|
||||||
|
} else {
|
||||||
|
// Handle attempt to delete user with pubkey
|
||||||
|
return res.status(400).json({ error: "Cannot delete user with pubkey. Use ID instead." });
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
|
||||||
|
return res.status(405).end(`Method ${req.method} Not Allowed`);
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
// Handle any other HTTP method
|
return res.status(500).json({ error: error.message });
|
||||||
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
|
|
||||||
res.status(405).end(`Method ${req.method} Not Allowed`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
83
src/pages/login.js
Normal file
83
src/pages/login.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import React from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { Button } from 'primereact/button';
|
||||||
|
import { useToast } from "@/hooks/useToast";
|
||||||
|
import { useNostr } from "@/hooks/useNostr";
|
||||||
|
import { findKind0Username } from "@/utils/nostr";
|
||||||
|
import { setPubkey, setUsername } from "@/redux/reducers/userReducer";
|
||||||
|
|
||||||
|
const Login = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
const { showToast } = useToast();
|
||||||
|
|
||||||
|
const nostrLogin = async () => {
|
||||||
|
try {
|
||||||
|
if (!window || !window.nostr) {
|
||||||
|
throw new Error('Nostr is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicKey = await window.nostr.getPublicKey();
|
||||||
|
if (!publicKey) {
|
||||||
|
throw new Error('Failed to obtain public key');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`/api/users/${publicKey}`);
|
||||||
|
if (response.status === 200 && response.data) {
|
||||||
|
dispatch(setPubkey(publicKey));
|
||||||
|
if (response.data.username) {
|
||||||
|
dispatch(setUsername(response.data.username));
|
||||||
|
}
|
||||||
|
router.push('/');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response?.status !== 204) {
|
||||||
|
throw error; // Rethrow error if it's not the expected 204 status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle user creation if status is 204 (No Content)
|
||||||
|
const kind0 = await fetchKind0([{ authors: [publicKey], kinds: [0] }], {});
|
||||||
|
const username = kind0 ? await findKind0Username(kind0) : undefined;
|
||||||
|
const payload = { pubkey: publicKey, ...(username && { username }) };
|
||||||
|
|
||||||
|
const createUserResponse = await axios.post(`/api/users`, payload);
|
||||||
|
if (createUserResponse.status === 201) {
|
||||||
|
dispatch(setPubkey(publicKey));
|
||||||
|
if (username) {
|
||||||
|
dispatch(setUsername(username));
|
||||||
|
}
|
||||||
|
router.push('/');
|
||||||
|
} else {
|
||||||
|
showToast('error', 'Error', 'User not created');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showToast('error', 'Error', error.message || 'An unexpected error occurred');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-fit mx-auto mt-24 flex flex-col justify-center">
|
||||||
|
<h1 className="text-center mb-8">Login</h1>
|
||||||
|
<Button
|
||||||
|
label={"login with nostr"}
|
||||||
|
icon="pi pi-user"
|
||||||
|
className="text-[#f8f8ff] w-[250px] my-4"
|
||||||
|
rounded
|
||||||
|
onClick={nostrLogin}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
label={"login anonymously"}
|
||||||
|
icon="pi pi-user"
|
||||||
|
className="text-[#f8f8ff] w-[250px] my-4"
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Login;
|
@ -1,6 +1,6 @@
|
|||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
const initialRelays = [
|
export const initialRelays = [
|
||||||
"wss://nos.lol/",
|
"wss://nos.lol/",
|
||||||
"wss://relay.damus.io/",
|
"wss://relay.damus.io/",
|
||||||
"wss://relay.snort.social/",
|
"wss://relay.snort.social/",
|
||||||
@ -14,6 +14,7 @@ export const userSlice = createSlice({
|
|||||||
name: "user",
|
name: "user",
|
||||||
initialState: {
|
initialState: {
|
||||||
pubkey: '',
|
pubkey: '',
|
||||||
|
username: '',
|
||||||
relays: initialRelays,
|
relays: initialRelays,
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
@ -22,10 +23,13 @@ export const userSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setPubkey: (state, action) => {
|
setPubkey: (state, action) => {
|
||||||
state.pubkey = action.payload;
|
state.pubkey = action.payload;
|
||||||
}
|
},
|
||||||
|
setUsername: (state, action) => {
|
||||||
|
state.username = action.payload;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setRelays, setPubkey } = userSlice.actions;
|
export const { setRelays, setPubkey, setUsername } = userSlice.actions;
|
||||||
|
|
||||||
export default userSlice.reducer;
|
export default userSlice.reducer;
|
16
src/utils/nostr.js
Normal file
16
src/utils/nostr.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const findKind0Username = async (kind0) => {
|
||||||
|
const usernameProperties = ['name', 'displayName', 'display_name', 'username', 'handle', 'alias'];
|
||||||
|
|
||||||
|
const findTruthyPropertyValue = (object, properties) => {
|
||||||
|
for (const property of properties) {
|
||||||
|
if (object[property]) {
|
||||||
|
return object[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const username = findTruthyPropertyValue(kind0, usernameProperties);
|
||||||
|
|
||||||
|
return username;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user