Compare commits
10 Commits
eb9a8725da
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a798c1bcd | ||
|
|
f46da9a619 | ||
|
|
c45d304fdd | ||
| 696ca6a393 | |||
| f65ae56f74 | |||
| d1495df5a1 | |||
| 2fa58305cb | |||
| 6fe79a1892 | |||
| 4c37658cd8 | |||
| 002c6a2f3d |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
454
package-lock.json
generated
Normal file
454
package-lock.json
generated
Normal file
@@ -0,0 +1,454 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@auth0/nextjs-auth0": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@auth0/nextjs-auth0/-/nextjs-auth0-1.8.0.tgz",
|
||||
"integrity": "sha512-lOI7i1khyuvVUGD/V9k+YMVxj3LB74KYqrCRQ8Uezai8YEAPHhExGMpIlFUPV7J4iPIuNNhW/4TYa45nkPNr0w==",
|
||||
"requires": {
|
||||
"base64url": "^3.0.1",
|
||||
"cookie": "^0.5.0",
|
||||
"debug": "^4.3.4",
|
||||
"futoin-hkdf": "^1.5.0",
|
||||
"http-errors": "^1.8.1",
|
||||
"joi": "^17.6.0",
|
||||
"jose": "^2.0.5",
|
||||
"on-headers": "^1.0.2",
|
||||
"openid-client": "^4.9.1",
|
||||
"tslib": "^2.3.1",
|
||||
"url-join": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
|
||||
},
|
||||
"@hapi/topo": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
|
||||
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
|
||||
"requires": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"@panva/asn1.js": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz",
|
||||
"integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw=="
|
||||
},
|
||||
"@sideway/address": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
|
||||
"integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
|
||||
"requires": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"@sideway/formula": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
|
||||
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
|
||||
},
|
||||
"@sideway/pinpoint": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
|
||||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
|
||||
},
|
||||
"@sindresorhus/is": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
||||
"integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="
|
||||
},
|
||||
"@szmarczak/http-timer": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||
"integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
|
||||
"requires": {
|
||||
"defer-to-connect": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@types/cacheable-request": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
|
||||
"integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==",
|
||||
"requires": {
|
||||
"@types/http-cache-semantics": "*",
|
||||
"@types/keyv": "*",
|
||||
"@types/node": "*",
|
||||
"@types/responselike": "*"
|
||||
}
|
||||
},
|
||||
"@types/http-cache-semantics": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
|
||||
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
|
||||
},
|
||||
"@types/json-buffer": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz",
|
||||
"integrity": "sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ=="
|
||||
},
|
||||
"@types/keyv": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
|
||||
"integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.32.tgz",
|
||||
"integrity": "sha512-eAIcfAvhf/BkHcf4pkLJ7ECpBAhh9kcxRBpip9cTiO+hf+aJrsxYxBeS6OXvOd9WqNAJmavXVpZvY1rBjNsXmw=="
|
||||
},
|
||||
"@types/responselike": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
|
||||
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"aggregate-error": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
|
||||
"requires": {
|
||||
"clean-stack": "^2.0.0",
|
||||
"indent-string": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"base64url": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
|
||||
"integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A=="
|
||||
},
|
||||
"cacheable-lookup": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
|
||||
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="
|
||||
},
|
||||
"cacheable-request": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
|
||||
"integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==",
|
||||
"requires": {
|
||||
"clone-response": "^1.0.2",
|
||||
"get-stream": "^5.1.0",
|
||||
"http-cache-semantics": "^4.0.0",
|
||||
"keyv": "^4.0.0",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"normalize-url": "^6.0.1",
|
||||
"responselike": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"clean-stack": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
|
||||
},
|
||||
"clone-response": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
|
||||
"integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
|
||||
"requires": {
|
||||
"mimic-response": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"compress-brotli": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.8.tgz",
|
||||
"integrity": "sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==",
|
||||
"requires": {
|
||||
"@types/json-buffer": "~3.0.0",
|
||||
"json-buffer": "~3.0.1"
|
||||
}
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"decompress-response": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"requires": {
|
||||
"mimic-response": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mimic-response": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"defer-to-connect": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
|
||||
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"requires": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"futoin-hkdf": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.5.0.tgz",
|
||||
"integrity": "sha512-4CerDhtTgx4i5PKccQIpEp4T9wqmosPIP9Kep35SdCpYkQeriD3zddUVhrO1Fc4QvGhsAnd2rXyoOr5047mJEg=="
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"got": {
|
||||
"version": "11.8.3",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz",
|
||||
"integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==",
|
||||
"requires": {
|
||||
"@sindresorhus/is": "^4.0.0",
|
||||
"@szmarczak/http-timer": "^4.0.5",
|
||||
"@types/cacheable-request": "^6.0.1",
|
||||
"@types/responselike": "^1.0.0",
|
||||
"cacheable-lookup": "^5.0.3",
|
||||
"cacheable-request": "^7.0.2",
|
||||
"decompress-response": "^6.0.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.2",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"p-cancelable": "^2.0.0",
|
||||
"responselike": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
|
||||
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
|
||||
"requires": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.1"
|
||||
}
|
||||
},
|
||||
"http2-wrapper": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
|
||||
"integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
|
||||
"requires": {
|
||||
"quick-lru": "^5.1.1",
|
||||
"resolve-alpn": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"joi": {
|
||||
"version": "17.6.0",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz",
|
||||
"integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==",
|
||||
"requires": {
|
||||
"@hapi/hoek": "^9.0.0",
|
||||
"@hapi/topo": "^5.0.0",
|
||||
"@sideway/address": "^4.1.3",
|
||||
"@sideway/formula": "^3.0.0",
|
||||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"jose": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz",
|
||||
"integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==",
|
||||
"requires": {
|
||||
"@panva/asn1.js": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
|
||||
},
|
||||
"keyv": {
|
||||
"version": "4.2.7",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.7.tgz",
|
||||
"integrity": "sha512-HeOstD8SXvtWoQhMMBCelcUuZsiV7T7MwsADtOXT0KuwYP9nCxrSoMDeLXNDTLN3VFSuRp38JzoGbbTboq3QQw==",
|
||||
"requires": {
|
||||
"compress-brotli": "^1.3.8",
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
},
|
||||
"lowercase-keys": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"normalize-url": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
|
||||
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="
|
||||
},
|
||||
"object-hash": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="
|
||||
},
|
||||
"oidc-token-hash": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz",
|
||||
"integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ=="
|
||||
},
|
||||
"on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"openid-client": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-4.9.1.tgz",
|
||||
"integrity": "sha512-DYUF07AHjI3QDKqKbn2F7RqozT4hyi4JvmpodLrq0HHoNP7t/AjeG/uqiBK1/N2PZSAQEThVjDLHSmJN4iqu/w==",
|
||||
"requires": {
|
||||
"aggregate-error": "^3.1.0",
|
||||
"got": "^11.8.0",
|
||||
"jose": "^2.0.5",
|
||||
"lru-cache": "^6.0.0",
|
||||
"make-error": "^1.3.6",
|
||||
"object-hash": "^2.0.1",
|
||||
"oidc-token-hash": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"p-cancelable": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
||||
"integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="
|
||||
},
|
||||
"pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"requires": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"quick-lru": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
|
||||
},
|
||||
"resolve-alpn": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
||||
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
|
||||
},
|
||||
"responselike": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
|
||||
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
|
||||
"requires": {
|
||||
"lowercase-keys": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
||||
},
|
||||
"toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||
},
|
||||
"url-join": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
|
||||
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,5 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
# forget me not site
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
Using
|
||||
- nextjs
|
||||
- tailwindcss
|
||||
|
||||
48
web/components/header.js
Normal file
48
web/components/header.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import Link from "next/link";
|
||||
|
||||
function Header({ user, loading }) {
|
||||
return (
|
||||
<header className="container mx-auto flex justify-center flex-col">
|
||||
<h1 className="text-4xl mx-auto">forget me not</h1>
|
||||
|
||||
<nav className="mx-auto mt-2">
|
||||
<ul className="flex justify-center flex-row">
|
||||
<li className="mx-3">
|
||||
<Link href="/">
|
||||
<a>Home</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li className="mx-3">
|
||||
<Link href="/about">
|
||||
<a>About</a>
|
||||
</Link>
|
||||
</li>
|
||||
{!loading &&
|
||||
(user ? (
|
||||
<>
|
||||
<li className="mx-3">
|
||||
<Link href="/profile">
|
||||
<a>Client-rendered profile</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li className="mx-3">
|
||||
<Link href="/advanced/ssr-profile">
|
||||
<a>Server rendered profile (advanced)</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li className="mx-3">
|
||||
<a href="/api/v1/logout">Logout</a>
|
||||
</li>
|
||||
</>
|
||||
) : (
|
||||
<li className="mx-3">
|
||||
<a href="/api/v1/login">Login</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
20
web/components/layout.js
Normal file
20
web/components/layout.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import Head from "next/head";
|
||||
import Header from "./header";
|
||||
|
||||
function Layout({ user, loading = false, children }) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>forget me not</title>
|
||||
</Head>
|
||||
|
||||
<Header user={user} loading={loading} />
|
||||
|
||||
<main>
|
||||
<div className="container mx-auto">{children}</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
11
web/components/wanttobutton.js
Normal file
11
web/components/wanttobutton.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import Link from "next/link";
|
||||
|
||||
function WantToButton({ href, text }) {
|
||||
return (
|
||||
<Link href={href}>
|
||||
<a className="py-1 px-2 mr-1 rounded border text-xl">{text}</a>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default WantToButton;
|
||||
24
web/lib/auth0.js
Normal file
24
web/lib/auth0.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { initAuth0 } from "@auth0/nextjs-auth0";
|
||||
|
||||
export default initAuth0({
|
||||
secret: process.env.SESSION_COOKIE_SECRET,
|
||||
issuerBaseURL: process.env.NEXT_PUBLIC_AUTH0_DOMAIN,
|
||||
baseURL: process.env.NEXT_PUBLIC_BASE_URL,
|
||||
clientID: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID,
|
||||
clientSecret: process.env.AUTH0_CLIENT_SECRET,
|
||||
routes: {
|
||||
callback:
|
||||
process.env.NEXT_PUBLIC_REDIRECT_URI ||
|
||||
"http://localhost:3000/api/v1/callback",
|
||||
postLogoutRedirect:
|
||||
process.env.NEXT_PUBLIC_POST_LOGOUT_REDIRECT_URI ||
|
||||
"http://localhost:3000",
|
||||
},
|
||||
authorizationParams: {
|
||||
response_type: "code",
|
||||
scope: process.env.NEXT_PUBLIC_AUTH0_SCOPE,
|
||||
},
|
||||
session: {
|
||||
absoluteDuration: process.env.SESSION_COOKIE_LIFETIME,
|
||||
},
|
||||
});
|
||||
13
web/lib/db.js
Normal file
13
web/lib/db.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const AWS = require("aws-sdk");
|
||||
|
||||
AWS.config.update({
|
||||
accessKeyId: process.env.FMN_AWS_ACCESS_KEY_ID, // Do NOT HARD-CODE your secret credentials here
|
||||
secretAccessKey: process.env.FMN_AWS_SECRET_ACCESS_KEY, // Do NOT HARD-CODE your secret credentials here
|
||||
region: process.env.FMN_AWS_REGION,
|
||||
});
|
||||
|
||||
const dynamodb = new AWS.DynamoDB.DocumentClient({
|
||||
region: process.env.FMN_AWS_REGION,
|
||||
});
|
||||
|
||||
export default dynamodb;
|
||||
42
web/lib/jsonapi.js
Normal file
42
web/lib/jsonapi.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import auth0 from "./auth0";
|
||||
|
||||
async function jsonApi(handler) {
|
||||
var unauthenticationHandler = async (req, res) => {
|
||||
try {
|
||||
await handler(req, res);
|
||||
} catch (error) {
|
||||
handleError(res, error);
|
||||
}
|
||||
|
||||
res.end();
|
||||
};
|
||||
|
||||
return unauthenticationHandler;
|
||||
}
|
||||
|
||||
function authenticatedJsonApi(handler) {
|
||||
var authenticatedHandler = async (req, res) => {
|
||||
try {
|
||||
const session = await auth0.getSession(req, res);
|
||||
|
||||
if (!session || !session.user) {
|
||||
res.status(401);
|
||||
} else {
|
||||
await handler(req, res, session);
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(res, error);
|
||||
}
|
||||
|
||||
res.end();
|
||||
};
|
||||
|
||||
return authenticatedHandler;
|
||||
}
|
||||
|
||||
function handleError(res, error) {
|
||||
console.error(error);
|
||||
res.status(error.status || 500).end(error.message);
|
||||
}
|
||||
|
||||
export { jsonApi, authenticatedJsonApi };
|
||||
22
web/lib/models/bookmarks.js
Normal file
22
web/lib/models/bookmarks.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import Ajv from "ajv";
|
||||
import addFormats from "ajv-formats";
|
||||
|
||||
const ajv = new Ajv();
|
||||
addFormats(ajv);
|
||||
|
||||
const validateId = ajv.compile({
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
maxLength: 10,
|
||||
});
|
||||
|
||||
const validatePostBody = ajv.compile({
|
||||
type: "object",
|
||||
properties: {
|
||||
title: { type: "string" },
|
||||
url: { type: "string", format: "uri" },
|
||||
},
|
||||
required: ["title", "url"],
|
||||
});
|
||||
|
||||
export { validateId, validatePostBody };
|
||||
3
web/lib/models/index.js
Normal file
3
web/lib/models/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export default {
|
||||
bookmarks: require("./bookmarks"),
|
||||
};
|
||||
73
web/lib/user.js
Normal file
73
web/lib/user.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export async function fetchUser(cookie = "") {
|
||||
if (typeof window !== "undefined" && window.__user) {
|
||||
return window.__user;
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
"/api/v1/me",
|
||||
cookie
|
||||
? {
|
||||
headers: {
|
||||
cookie,
|
||||
},
|
||||
}
|
||||
: {}
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
delete window.__user;
|
||||
return null;
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
if (typeof window !== "undefined") {
|
||||
window.__user = json;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
export function useFetchUser({ required } = {}) {
|
||||
const [loading, setLoading] = useState(
|
||||
() => !(typeof window !== "undefined" && window.__user)
|
||||
);
|
||||
const [user, setUser] = useState(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return window.__user || null;
|
||||
});
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!loading && user) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
let isMounted = true;
|
||||
|
||||
fetchUser().then((user) => {
|
||||
// Only set the user if the component is still mounted
|
||||
if (isMounted) {
|
||||
// When the user is not logged in but login is required
|
||||
if (required && !user) {
|
||||
window.location.href = "/api/v1/login";
|
||||
return;
|
||||
}
|
||||
setUser(user);
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
return { user, loading };
|
||||
}
|
||||
@@ -1,6 +1,23 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: "/api/:path*",
|
||||
headers: [
|
||||
{
|
||||
key: "Content-Type",
|
||||
value: "application/vnd.api+json"
|
||||
},
|
||||
{
|
||||
key: "x-testing",
|
||||
value: "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
5089
web/package-lock.json
generated
5089
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,14 +9,20 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth0/nextjs-auth0": "^1.8.0",
|
||||
"ajv": "^8.11.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"aws-sdk": "^2.1131.0",
|
||||
"next": "12.1.6",
|
||||
"react": "18.1.0",
|
||||
"react-dom": "18.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.8",
|
||||
"eslint": "8.15.0",
|
||||
"eslint-config-next": "12.1.6",
|
||||
"prettier": "^2.6.2"
|
||||
"postcss": "^8.4.16",
|
||||
"prettier": "^2.6.2",
|
||||
"tailwindcss": "^3.1.8"
|
||||
}
|
||||
}
|
||||
|
||||
13
web/pages/_document.js
Normal file
13
web/pages/_document.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Html, Head, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html dir="ltr">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
19
web/pages/about.js
Normal file
19
web/pages/about.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import Layout from "../components/layout";
|
||||
import { useFetchUser } from "../lib/user";
|
||||
|
||||
function About() {
|
||||
const { user, loading } = useFetchUser();
|
||||
|
||||
return (
|
||||
<Layout user={user} loading={loading}>
|
||||
<h1>About</h1>
|
||||
<p>
|
||||
This is the about page, navigating between this page and <i>Home</i> is
|
||||
always pretty fast. However, when you navigate to the <i>Profile</i>{" "}
|
||||
page it takes more time because it uses SSR to fetch the user first;
|
||||
</p>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default About;
|
||||
37
web/pages/advanced/ssr-profile.js
Normal file
37
web/pages/advanced/ssr-profile.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// This import is only included in the server build, because it's only used by getServerSideProps
|
||||
import auth0 from "../../lib/auth0";
|
||||
import Layout from "../../components/layout";
|
||||
|
||||
function Profile({ user }) {
|
||||
return (
|
||||
<Layout user={user}>
|
||||
<h1>Profile</h1>
|
||||
|
||||
<div>
|
||||
<h3>Profile (server rendered)</h3>
|
||||
<img src={user.picture} alt="user picture" />
|
||||
<p>nickname: {user.nickname}</p>
|
||||
<p>name: {user.name}</p>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps({ req, res }) {
|
||||
// Here you can check authentication status directly before rendering the page,
|
||||
// however the page would be a serverless function, which is more expensive and
|
||||
// slower than a static page with client side authentication
|
||||
const session = await auth0.getSession(req, res);
|
||||
|
||||
if (!session || !session.user) {
|
||||
res.writeHead(302, {
|
||||
Location: "/api/v1/login",
|
||||
});
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
return { props: { user: session.user } };
|
||||
}
|
||||
|
||||
export default Profile;
|
||||
@@ -1,19 +0,0 @@
|
||||
const AWS = require("aws-sdk");
|
||||
|
||||
const dynamodb = new AWS.DynamoDB.DocumentClient({
|
||||
region: process.env.AWS_REGION,
|
||||
});
|
||||
|
||||
const handler = async (req, res) => {
|
||||
const params = {
|
||||
TableName: process.env.db,
|
||||
KeyConditionExpression: "hk = :hk",
|
||||
ExpressionAttributeValues: { ":hk": "benramey@fastmail.com" },
|
||||
};
|
||||
|
||||
let user = await dynamodb.query(params).promise();
|
||||
|
||||
res.status(200).json(user);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
53
web/pages/api/v1/[entityName]/[entityId].js
Normal file
53
web/pages/api/v1/[entityName]/[entityId].js
Normal file
@@ -0,0 +1,53 @@
|
||||
import db from "../../../../lib/db";
|
||||
import { authenticatedJsonApi } from "../../../../lib/jsonapi";
|
||||
import models from "../../../../lib/models";
|
||||
|
||||
const handler = authenticatedJsonApi(async (req, res, session) => {
|
||||
const { method } = req;
|
||||
const { user } = session;
|
||||
|
||||
switch (method) {
|
||||
case "GET":
|
||||
await getEntity(req, res, user);
|
||||
break;
|
||||
default:
|
||||
res.setHeader("Allow", ["GET"]);
|
||||
res.status(405).send(`Method ${method} Not Allowed`);
|
||||
}
|
||||
});
|
||||
|
||||
const getEntity = async (req, res, user) => {
|
||||
const { entityName, entityId } = req.query;
|
||||
|
||||
if (!models[entityName]) {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
var model = models[entityName];
|
||||
|
||||
if (!model.validateId(entityId)) {
|
||||
console.warn(model.validateId.errors);
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
TableName: process.env.FMN_DYNAMODB_TABLENAME,
|
||||
KeyConditionExpression: "hk = :hk AND sk = :sk",
|
||||
ExpressionAttributeValues: {
|
||||
":hk": `user#${user.sub}`,
|
||||
":sk": `${entityName}#${entityId}`,
|
||||
},
|
||||
};
|
||||
|
||||
let entities = await db.query(params).promise();
|
||||
|
||||
if (entities.Items.length == 1) {
|
||||
res.status(200).json(entities.Items[0]);
|
||||
} else {
|
||||
res.status(404);
|
||||
}
|
||||
};
|
||||
|
||||
export default handler;
|
||||
73
web/pages/api/v1/[entityName]/index.js
Normal file
73
web/pages/api/v1/[entityName]/index.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import db from "../../../../lib/db";
|
||||
import { authenticatedJsonApi } from "../../../../lib/jsonapi";
|
||||
import models from "../../../../lib/models";
|
||||
|
||||
const handler = authenticatedJsonApi(async (req, res, session) => {
|
||||
const { method } = req;
|
||||
const { user } = session;
|
||||
|
||||
switch (method) {
|
||||
case "GET":
|
||||
await getEntities(req, res, user);
|
||||
break;
|
||||
case "POST":
|
||||
await createEntity(req, res, user);
|
||||
break;
|
||||
default:
|
||||
res.setHeader("Allow", ["GET", "POST"]);
|
||||
res.status(405).json(`Method ${method} Not Allowed`);
|
||||
}
|
||||
});
|
||||
|
||||
const getEntities = async (req, res, user) => {
|
||||
const { entityName } = req.query;
|
||||
|
||||
if (!models[entityName]) {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
TableName: process.env.FMN_DYNAMODB_TABLENAME,
|
||||
KeyConditionExpression: "hk = :hk AND begins_with(sk, :sk)",
|
||||
ExpressionAttributeValues: {
|
||||
":hk": `user#${user.sub}`,
|
||||
":sk": `${entityName}#`,
|
||||
},
|
||||
};
|
||||
|
||||
let entities = await db.query(params).promise();
|
||||
res.status(200).json(entities.Items);
|
||||
};
|
||||
|
||||
const createEntity = async (req, res, user) => {
|
||||
const { entityName } = req.query;
|
||||
|
||||
if (!models[entityName]) {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
var model = models[entityName];
|
||||
|
||||
if (!model.validatePostBody(req.body)) {
|
||||
console.warn(validatePostBody.errors);
|
||||
res.status(400).json(validatePostBody.errors);
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
TableName: process.env.FMN_DYNAMODB_TABLENAME,
|
||||
Item: {
|
||||
hk: `user#${user.sub}`,
|
||||
sk: `${entityName}#${"abc"}`,
|
||||
title: req.body.title,
|
||||
url: req.body.url,
|
||||
},
|
||||
};
|
||||
|
||||
await db.put(params).promise();
|
||||
res.status(200).json(params.Item);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
7
web/pages/api/v1/callback.js
Normal file
7
web/pages/api/v1/callback.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import auth0 from "../../../lib/auth0";
|
||||
|
||||
const callback = async (req, res) => {
|
||||
await auth0.handleCallback(req, res);
|
||||
};
|
||||
|
||||
export default callback;
|
||||
7
web/pages/api/v1/login.js
Normal file
7
web/pages/api/v1/login.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import auth0 from "../../../lib/auth0";
|
||||
|
||||
const login = async (req, res) => {
|
||||
await auth0.handleLogin(req, res);
|
||||
};
|
||||
|
||||
export default login;
|
||||
8
web/pages/api/v1/logout.js
Normal file
8
web/pages/api/v1/logout.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import auth0 from "../../../lib/auth0";
|
||||
import { withErrorHandling } from "../../../lib/apiHelpers";
|
||||
|
||||
const logout = withErrorHandling(async (req, res) => {
|
||||
await auth0.handleLogout(req, res);
|
||||
});
|
||||
|
||||
export default logout;
|
||||
7
web/pages/api/v1/me.js
Normal file
7
web/pages/api/v1/me.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import auth0 from "../../../lib/auth0";
|
||||
|
||||
const me = async (req, res) => {
|
||||
await auth0.handleProfile(req, res);
|
||||
};
|
||||
|
||||
export default me;
|
||||
@@ -1,69 +1,50 @@
|
||||
import Head from 'next/head'
|
||||
import Image from 'next/image'
|
||||
import styles from '../styles/Home.module.css'
|
||||
import Layout from "../components/layout";
|
||||
import WantToButton from "../components/wanttobutton";
|
||||
import Link from "next/link";
|
||||
import { useFetchUser } from "../lib/user";
|
||||
|
||||
function Home() {
|
||||
const { user, loading } = useFetchUser();
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Head>
|
||||
<title>Create Next App: Testing Update 2</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<Layout user={user} loading={loading}>
|
||||
{loading && <p>Loading login info...</p>}
|
||||
|
||||
<main className={styles.main}>
|
||||
<h1 className={styles.title}>
|
||||
Welcome to <a href="https://nextjs.org">Next.js!</a>
|
||||
</h1>
|
||||
{!loading && !user && (
|
||||
<>
|
||||
<div className="flex flex-col justify-center">
|
||||
<p className="text-lg">I want to...</p>
|
||||
<div className="flex flex-row mt-2">
|
||||
<WantToButton href="/api/v1/login" text="login" />
|
||||
<WantToButton
|
||||
href="/api/v1/login"
|
||||
text="stay anonymous"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<p className={styles.description}>
|
||||
Get started by editing{' '}
|
||||
<code className={styles.code}>pages/index.js</code>
|
||||
</p>
|
||||
|
||||
<div className={styles.grid}>
|
||||
<a href="https://nextjs.org/docs" className={styles.card}>
|
||||
<h2>Documentation →</h2>
|
||||
<p>Find in-depth information about Next.js features and API.</p>
|
||||
</a>
|
||||
|
||||
<a href="https://nextjs.org/learn" className={styles.card}>
|
||||
<h2>Learn →</h2>
|
||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://github.com/vercel/next.js/tree/canary/examples"
|
||||
className={styles.card}
|
||||
>
|
||||
<h2>Examples →</h2>
|
||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
>
|
||||
<h2>Deploy →</h2>
|
||||
<p>
|
||||
Instantly deploy your Next.js site to a public URL with Vercel.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer className={styles.footer}>
|
||||
<a
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Powered by{' '}
|
||||
<span className={styles.logo}>
|
||||
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
|
||||
</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
{user && (
|
||||
<>
|
||||
<h4>Rendered user info on the client</h4>
|
||||
<img src={user.picture} alt="user picture" />
|
||||
<p>nickname: {user.nickname}</p>
|
||||
<p>name: {user.name}</p>
|
||||
<p>
|
||||
<form method="POST" action="/api/v1/bookmarks">
|
||||
<label for="title">title: </label>
|
||||
<input type="text" name="title" />
|
||||
<br />
|
||||
<label for="url">url:</label>
|
||||
<input type="text" name="url" />
|
||||
<button type="submit">Add bookmark</button>
|
||||
</form>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
|
||||
31
web/pages/profile.js
Normal file
31
web/pages/profile.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// This import is only needed when checking authentication status directly from getInitialProps
|
||||
// import auth0 from '../lib/auth0'
|
||||
import { useFetchUser } from "../lib/user";
|
||||
import Layout from "../components/layout";
|
||||
|
||||
function ProfileCard({ user }) {
|
||||
return (
|
||||
<>
|
||||
<h1>Profile</h1>
|
||||
|
||||
<div>
|
||||
<h3>Profile (client rendered)</h3>
|
||||
<img src={user.picture} alt="user picture" />
|
||||
<p>nickname: {user.nickname}</p>
|
||||
<p>name: {user.name}</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Profile() {
|
||||
const { user, loading } = useFetchUser({ required: true });
|
||||
|
||||
return (
|
||||
<Layout user={user} loading={loading}>
|
||||
{loading ? <>Loading...</> : <ProfileCard user={user} />}
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default Profile;
|
||||
6
web/postcss.config.js
Normal file
6
web/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,116 +0,0 @@
|
||||
.container {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: 100vh;
|
||||
padding: 4rem 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
padding: 2rem 0;
|
||||
border-top: 1px solid #eaeaea;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.title a {
|
||||
color: #0070f3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.title a:hover,
|
||||
.title a:focus,
|
||||
.title a:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
line-height: 1.15;
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.title,
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 4rem 0;
|
||||
line-height: 1.5;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.code {
|
||||
background: #fafafa;
|
||||
border-radius: 5px;
|
||||
padding: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 1rem;
|
||||
padding: 1.5rem;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 10px;
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card:hover,
|
||||
.card:focus,
|
||||
.card:active {
|
||||
color: #0070f3;
|
||||
border-color: #0070f3;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 1em;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.grid {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
11
web/tailwind.config.js
Normal file
11
web/tailwind.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./pages/**/*.js",
|
||||
"./components/**/*.js"
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
Reference in New Issue
Block a user