diff --git a/.gitignore b/.gitignore index e35094d..232fd4d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +**/**/.env diff --git a/api/package-lock.json b/api/package-lock.json index 3d1b60b..6b3b710 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -15,6 +15,7 @@ "express": "^4.21.0", "mongodb": "^6.9.0", "mongoose": "^8.4.1", + "sendgrid": "^5.2.3", "ts-node-dev": "^2.0.0", "uuid": "^10.0.0" }, @@ -24,7 +25,9 @@ "@types/node": "^22.8.1", "@types/uuid": "^10.0.0", "body-parser": "^1.20.3", + "nodemon": "^3.1.7", "prettier": "^3.3.3", + "ts-node": "^10.9.2", "typescript": "^5.6.2" } }, @@ -165,9 +168,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dev": true, "dependencies": { "@types/node": "*", @@ -189,17 +192,17 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.8.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.1.tgz", - "integrity": "sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==", + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", "dev": true }, "node_modules/@types/range-parser": { @@ -271,9 +274,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "bin": { "acorn": "bin/acorn" }, @@ -292,6 +295,11 @@ "node": ">=0.4.0" } }, + "node_modules/addressparser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", + "integrity": "sha512-aQX7AISOMM7HFE0iZ3+YnD07oIeJqWGVnJ+ZIKaBZAk03ftmVYVqsGas/rbXKR21n4D/hKCSHypvcyOkds/xzg==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -314,15 +322,88 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/async.ensureasync": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.ensureasync/-/async.ensureasync-0.5.2.tgz", + "integrity": "sha512-eez/kJFHbnnHUBWpYlmWcjZpho/Oh7SHwnnnuscM8nOZcIJ41t+ACMSYTfNtdrES+XfbGVQtyw+6YK2evQgV5w==", + "dependencies": { + "async.util.ensureasync": "0.5.2" + } + }, + "node_modules/async.queue": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.queue/-/async.queue-0.5.2.tgz", + "integrity": "sha512-SX5gCWh47bIRLqHAt/zfJmRl2xpCF4OM4kkT3HehVJLmgQfLzYjL6QhLHe+SS4e7FOYU6NgMy2kXPB2wVchncg==", + "dependencies": { + "async.util.queue": "0.5.2" + } + }, + "node_modules/async.util.arrayeach": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.arrayeach/-/async.util.arrayeach-0.5.2.tgz", + "integrity": "sha512-PIb4rVYjwzLqb93XX2wj0+mA9YTgSWtxQRKxtuLqxXvGj1xZMB6qJUfr1NhS+FSaMPJIE1tF40Gl/o/vlfIz/A==" + }, + "node_modules/async.util.ensureasync": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.ensureasync/-/async.util.ensureasync-0.5.2.tgz", + "integrity": "sha512-sQufNz4JYLHO/7ZUklaTGJtiX36gCp28McNNcMZfVYPm/wqi1pKk4xRCPr2DOZDM6KfA54zjG36jQjBU2mLzXg==", + "dependencies": { + "async.util.restparam": "0.5.2", + "async.util.setimmediate": "0.5.2" + } + }, + "node_modules/async.util.isarray": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.isarray/-/async.util.isarray-0.5.2.tgz", + "integrity": "sha512-wbUzlrwON8RUgi+v/rhF0U99Ce8Osjcn+JP/mFNg6ymvShcobAOvE6cvLajSY5dPqKCOE1xfdhefgBif11zZgw==" + }, + "node_modules/async.util.map": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.map/-/async.util.map-0.5.2.tgz", + "integrity": "sha512-uXZhyNIH9Jo25jn35lUoEwFLAdZWC2ZQKjLO5PLq8VAisfW6qvSfgDLH4H57/WQSKZSo6OOmuqGhtdvIHDTi1Q==" + }, + "node_modules/async.util.noop": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.noop/-/async.util.noop-0.5.2.tgz", + "integrity": "sha512-AdwShXwE0KoskgqVJAck8zcM32nIHj3AC8ZN62ZaR5srhrY235Nw18vOJZWxcOfhxdVM0hRVKM8kMx7lcl7cCQ==" + }, + "node_modules/async.util.onlyonce": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.onlyonce/-/async.util.onlyonce-0.5.2.tgz", + "integrity": "sha512-UgQvkU9JZ+I0Cm1f56XyGXcII+J3d/5XWUuHpcevlItuA3WFSJcqZrsyAUck2FkRSD8BwYQX1zUTDp3SJMVESg==" + }, + "node_modules/async.util.queue": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.queue/-/async.util.queue-0.5.2.tgz", + "integrity": "sha512-DlKOFnhCzERL9D3bLKlNdgXwSckckcj+XkCvNuX4KMs4brBc2lHvPg8MK4NoPIhwAvUBGvE4NECdNRY0I5UOEQ==", + "dependencies": { + "async.util.arrayeach": "0.5.2", + "async.util.isarray": "0.5.2", + "async.util.map": "0.5.2", + "async.util.noop": "0.5.2", + "async.util.onlyonce": "0.5.2", + "async.util.setimmediate": "0.5.2" + } + }, + "node_modules/async.util.restparam": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.restparam/-/async.util.restparam-0.5.2.tgz", + "integrity": "sha512-Q9Z+zgmtMxFX5i7CnBvNOkgrL5hptztCqwarQluyNudUUk4iCmyjmsQl8MuQEjNh3gGqP5ayvDaextL1VXXgIg==" + }, + "node_modules/async.util.setimmediate": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.setimmediate/-/async.util.setimmediate-0.5.2.tgz", + "integrity": "sha512-aCYF85ZFCQ9Xn0106GcOVx+LvFguIIzfbfRTOlQoie3G4KeSjURfA6f7CfpFAF09FNP2A1MtdjeFdvYeTGDebw==" + }, "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/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", + "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -349,7 +430,6 @@ "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -369,6 +449,11 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/bottleneck": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-1.16.0.tgz", + "integrity": "sha512-HinT6aCt+ZLPVvMtOy8Vd1vQtR2R/Qi06fKRCa4oGiIuFF+CQwNYR/KZTjrEynkER7y9WaY8fhwWKqZp+/aXeg==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -390,9 +475,9 @@ } }, "node_modules/bson": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", - "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.0.tgz", + "integrity": "sha512-ROchNosXMJD2cbQGm84KoP7vOGPO6/bOAW0veMMbzhXLqoZptcaYRVLitwvuhwhjjpU1qP4YZRWLhgETdgqUQw==", "engines": { "node": ">=16.20.1" } @@ -499,6 +584,16 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -613,6 +708,25 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -637,6 +751,14 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -686,6 +808,11 @@ "node": ">= 0.10.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -848,6 +975,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -918,6 +1054,12 @@ "node": ">=0.10.0" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1001,6 +1143,23 @@ "node": ">=12.0.0" } }, + "node_modules/lodash.chunk": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", + "integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==" + }, + "node_modules/mailparser": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-0.6.2.tgz", + "integrity": "sha512-5Z5nv7TYojrdrKHjhCOKKFaYs0Lo7PCja4RnLZ2kFE1Fskqg+rRVwFpG9Oqx3YjosGM2QEItjHo7+Jc9qvxGqw==", + "deprecated": "Mailparser versions older than v2.3.0 are deprecated", + "dependencies": { + "encoding": "^0.1.12", + "mime": "^1.3.4", + "mimelib": "^0.3.0", + "uue": "^3.1.0" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -1065,6 +1224,16 @@ "node": ">= 0.6" } }, + "node_modules/mimelib": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/mimelib/-/mimelib-0.3.1.tgz", + "integrity": "sha512-ipzNp6TBsNfD3hButGlPVlGmuCgybIM9SBf8YwIG+SYmBgtU0u8wjf+BSrJX0mvqtv59SLmwphw/XiCbkLWv7w==", + "deprecated": "This project is unmaintained", + "dependencies": { + "addressparser": "~1.0.1", + "encoding": "~0.1.12" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1096,12 +1265,12 @@ } }, "node_modules/mongodb": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.9.0.tgz", - "integrity": "sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.11.0.tgz", + "integrity": "sha512-yVbPw0qT268YKhG241vAMLaDQAPbRyTgo++odSgGc9kXnzOujQI60Iyj23B9sQQFPSvmNPvMZ3dsFz0aN55KgA==", "dependencies": { - "@mongodb-js/saslprep": "^1.1.5", - "bson": "^6.7.0", + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.0", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -1150,13 +1319,13 @@ } }, "node_modules/mongoose": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.1.tgz", - "integrity": "sha512-odQ2WEWGL3hb0Qex+QMN4eH6D34WdMEw7F1If2MGABApSDmG9cMmqv/G1H6WsXmuaH9mkuuadW/WbLE5+tHJwA==", + "version": "8.8.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.3.tgz", + "integrity": "sha512-/I4n/DcXqXyIiLRfAmUIiTjj3vXfeISke8dt4U4Y8Wfm074Wa6sXnQrXN49NFOFf2mM1kUdOXryoBvkuCnr+Qw==", "dependencies": { "bson": "^6.7.0", "kareem": "2.6.3", - "mongodb": "6.6.2", + "mongodb": "~6.10.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -1171,9 +1340,9 @@ } }, "node_modules/mongoose/node_modules/mongodb": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", - "integrity": "sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz", + "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==", "dependencies": { "@mongodb-js/saslprep": "^1.1.5", "bson": "^6.7.0", @@ -1273,6 +1442,57 @@ "node": ">= 0.6" } }, + "node_modules/nodemon": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", + "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1290,9 +1510,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "engines": { "node": ">= 0.4" }, @@ -1357,11 +1577,10 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", "dev": true, - "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -1389,6 +1608,12 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1496,6 +1721,18 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", @@ -1532,6 +1769,44 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/sendgrid": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/sendgrid/-/sendgrid-5.2.3.tgz", + "integrity": "sha512-FD7oR9TbJFUew1p0Vw9JX0wBetDyq634LzylSXz4n9+hwaf+6a9dNloZl8CcjpsX4NuEc3HJanTN4GjDwNyi4A==", + "deprecated": "Please see v6.X+ at https://www.npmjs.com/org/sendgrid", + "dependencies": { + "async.ensureasync": "^0.5.2", + "async.queue": "^0.5.2", + "bottleneck": "^1.12.0", + "debug": "^2.2.0", + "lodash.chunk": "^4.2.0", + "mailparser": "^0.6.1", + "sendgrid-rest": "^2.3.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/sendgrid-rest": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/sendgrid-rest/-/sendgrid-rest-2.6.1.tgz", + "integrity": "sha512-W7gPROAPkEiD3+XQh+jeiU5mTNWqfgn45y1Xi8WiFA3CYKGOvHn+U0d62pTWjjynNqsmQIq9AJ0+93Bq67gbSQ==", + "dependencies": { + "core-js": "^3.5.0", + "dotenv": "^4.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/sendgrid-rest/node_modules/dotenv": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "integrity": "sha512-XcaMACOr3JMVcEv0Y/iUM2XaOsATRZ3U1In41/1jjK6vJZ2PZbQ1bzCG8uvaByfaBpl9gqc9QWJovpUGBXLLYQ==", + "engines": { + "node": ">=4.6.0" + } + }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -1589,6 +1864,18 @@ "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1638,6 +1925,18 @@ "node": ">=0.10.0" } }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -1668,6 +1967,15 @@ "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/tr46": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", @@ -1786,9 +2094,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1797,10 +2105,16 @@ "node": ">=14.17" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" }, "node_modules/unpipe": { "version": "1.0.0", @@ -1818,6 +2132,15 @@ "node": ">= 0.4.0" } }, + "node_modules/uue": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/uue/-/uue-3.1.2.tgz", + "integrity": "sha512-axKLXVqwtdI/czrjG0X8hyV1KLgeWx8F4KvSbvVCnS+RUvsQMGRjx0kfuZDXXqj0LYvVJmx3B9kWlKtEdRrJLg==", + "dependencies": { + "escape-string-regexp": "~1.0.5", + "extend": "~3.0.0" + } + }, "node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", @@ -2008,9 +2331,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dev": true, "requires": { "@types/node": "*", @@ -2032,17 +2355,17 @@ "dev": true }, "@types/node": { - "version": "22.8.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.1.tgz", - "integrity": "sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==", + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", "requires": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", "dev": true }, "@types/range-parser": { @@ -2111,9 +2434,9 @@ } }, "acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==" + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==" }, "acorn-walk": { "version": "8.3.4", @@ -2123,6 +2446,11 @@ "acorn": "^8.11.0" } }, + "addressparser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", + "integrity": "sha512-aQX7AISOMM7HFE0iZ3+YnD07oIeJqWGVnJ+ZIKaBZAk03ftmVYVqsGas/rbXKR21n4D/hKCSHypvcyOkds/xzg==" + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -2142,15 +2470,88 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "async.ensureasync": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.ensureasync/-/async.ensureasync-0.5.2.tgz", + "integrity": "sha512-eez/kJFHbnnHUBWpYlmWcjZpho/Oh7SHwnnnuscM8nOZcIJ41t+ACMSYTfNtdrES+XfbGVQtyw+6YK2evQgV5w==", + "requires": { + "async.util.ensureasync": "0.5.2" + } + }, + "async.queue": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.queue/-/async.queue-0.5.2.tgz", + "integrity": "sha512-SX5gCWh47bIRLqHAt/zfJmRl2xpCF4OM4kkT3HehVJLmgQfLzYjL6QhLHe+SS4e7FOYU6NgMy2kXPB2wVchncg==", + "requires": { + "async.util.queue": "0.5.2" + } + }, + "async.util.arrayeach": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.arrayeach/-/async.util.arrayeach-0.5.2.tgz", + "integrity": "sha512-PIb4rVYjwzLqb93XX2wj0+mA9YTgSWtxQRKxtuLqxXvGj1xZMB6qJUfr1NhS+FSaMPJIE1tF40Gl/o/vlfIz/A==" + }, + "async.util.ensureasync": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.ensureasync/-/async.util.ensureasync-0.5.2.tgz", + "integrity": "sha512-sQufNz4JYLHO/7ZUklaTGJtiX36gCp28McNNcMZfVYPm/wqi1pKk4xRCPr2DOZDM6KfA54zjG36jQjBU2mLzXg==", + "requires": { + "async.util.restparam": "0.5.2", + "async.util.setimmediate": "0.5.2" + } + }, + "async.util.isarray": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.isarray/-/async.util.isarray-0.5.2.tgz", + "integrity": "sha512-wbUzlrwON8RUgi+v/rhF0U99Ce8Osjcn+JP/mFNg6ymvShcobAOvE6cvLajSY5dPqKCOE1xfdhefgBif11zZgw==" + }, + "async.util.map": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.map/-/async.util.map-0.5.2.tgz", + "integrity": "sha512-uXZhyNIH9Jo25jn35lUoEwFLAdZWC2ZQKjLO5PLq8VAisfW6qvSfgDLH4H57/WQSKZSo6OOmuqGhtdvIHDTi1Q==" + }, + "async.util.noop": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.noop/-/async.util.noop-0.5.2.tgz", + "integrity": "sha512-AdwShXwE0KoskgqVJAck8zcM32nIHj3AC8ZN62ZaR5srhrY235Nw18vOJZWxcOfhxdVM0hRVKM8kMx7lcl7cCQ==" + }, + "async.util.onlyonce": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.onlyonce/-/async.util.onlyonce-0.5.2.tgz", + "integrity": "sha512-UgQvkU9JZ+I0Cm1f56XyGXcII+J3d/5XWUuHpcevlItuA3WFSJcqZrsyAUck2FkRSD8BwYQX1zUTDp3SJMVESg==" + }, + "async.util.queue": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.queue/-/async.util.queue-0.5.2.tgz", + "integrity": "sha512-DlKOFnhCzERL9D3bLKlNdgXwSckckcj+XkCvNuX4KMs4brBc2lHvPg8MK4NoPIhwAvUBGvE4NECdNRY0I5UOEQ==", + "requires": { + "async.util.arrayeach": "0.5.2", + "async.util.isarray": "0.5.2", + "async.util.map": "0.5.2", + "async.util.noop": "0.5.2", + "async.util.onlyonce": "0.5.2", + "async.util.setimmediate": "0.5.2" + } + }, + "async.util.restparam": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.restparam/-/async.util.restparam-0.5.2.tgz", + "integrity": "sha512-Q9Z+zgmtMxFX5i7CnBvNOkgrL5hptztCqwarQluyNudUUk4iCmyjmsQl8MuQEjNh3gGqP5ayvDaextL1VXXgIg==" + }, + "async.util.setimmediate": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/async.util.setimmediate/-/async.util.setimmediate-0.5.2.tgz", + "integrity": "sha512-aCYF85ZFCQ9Xn0106GcOVx+LvFguIIzfbfRTOlQoie3G4KeSjURfA6f7CfpFAF09FNP2A1MtdjeFdvYeTGDebw==" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", + "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", "requires": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -2186,6 +2587,11 @@ "unpipe": "1.0.0" } }, + "bottleneck": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-1.16.0.tgz", + "integrity": "sha512-HinT6aCt+ZLPVvMtOy8Vd1vQtR2R/Qi06fKRCa4oGiIuFF+CQwNYR/KZTjrEynkER7y9WaY8fhwWKqZp+/aXeg==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2204,9 +2610,9 @@ } }, "bson": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", - "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==" + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.0.tgz", + "integrity": "sha512-ROchNosXMJD2cbQGm84KoP7vOGPO6/bOAW0veMMbzhXLqoZptcaYRVLitwvuhwhjjpU1qP4YZRWLhgETdgqUQw==" }, "buffer-from": { "version": "1.1.2", @@ -2281,6 +2687,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==" + }, "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -2361,6 +2772,24 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, "es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -2379,6 +2808,11 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2422,6 +2856,11 @@ "vary": "~1.1.2" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2526,6 +2965,12 @@ "get-intrinsic": "^1.1.3" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, "has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -2572,6 +3017,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2630,6 +3081,22 @@ "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==" }, + "lodash.chunk": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", + "integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==" + }, + "mailparser": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-0.6.2.tgz", + "integrity": "sha512-5Z5nv7TYojrdrKHjhCOKKFaYs0Lo7PCja4RnLZ2kFE1Fskqg+rRVwFpG9Oqx3YjosGM2QEItjHo7+Jc9qvxGqw==", + "requires": { + "encoding": "^0.1.12", + "mime": "^1.3.4", + "mimelib": "^0.3.0", + "uue": "^3.1.0" + } + }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -2673,6 +3140,15 @@ "mime-db": "1.52.0" } }, + "mimelib": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/mimelib/-/mimelib-0.3.1.tgz", + "integrity": "sha512-ipzNp6TBsNfD3hButGlPVlGmuCgybIM9SBf8YwIG+SYmBgtU0u8wjf+BSrJX0mvqtv59SLmwphw/XiCbkLWv7w==", + "requires": { + "addressparser": "~1.0.1", + "encoding": "~0.1.12" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2692,12 +3168,12 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "mongodb": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.9.0.tgz", - "integrity": "sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.11.0.tgz", + "integrity": "sha512-yVbPw0qT268YKhG241vAMLaDQAPbRyTgo++odSgGc9kXnzOujQI60Iyj23B9sQQFPSvmNPvMZ3dsFz0aN55KgA==", "requires": { - "@mongodb-js/saslprep": "^1.1.5", - "bson": "^6.7.0", + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.0", "mongodb-connection-string-url": "^3.0.0" } }, @@ -2711,13 +3187,13 @@ } }, "mongoose": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.1.tgz", - "integrity": "sha512-odQ2WEWGL3hb0Qex+QMN4eH6D34WdMEw7F1If2MGABApSDmG9cMmqv/G1H6WsXmuaH9mkuuadW/WbLE5+tHJwA==", + "version": "8.8.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.3.tgz", + "integrity": "sha512-/I4n/DcXqXyIiLRfAmUIiTjj3vXfeISke8dt4U4Y8Wfm074Wa6sXnQrXN49NFOFf2mM1kUdOXryoBvkuCnr+Qw==", "requires": { "bson": "^6.7.0", "kareem": "2.6.3", - "mongodb": "6.6.2", + "mongodb": "~6.10.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -2725,9 +3201,9 @@ }, "dependencies": { "mongodb": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", - "integrity": "sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz", + "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==", "requires": { "@mongodb-js/saslprep": "^1.1.5", "bson": "^6.7.0", @@ -2779,6 +3255,41 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "nodemon": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", + "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2790,9 +3301,9 @@ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==" + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==" }, "on-finished": { "version": "2.4.1", @@ -2836,9 +3347,9 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", "dev": true }, "proxy-addr": { @@ -2855,6 +3366,12 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2920,6 +3437,12 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true + }, "send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", @@ -2952,6 +3475,36 @@ } } }, + "sendgrid": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/sendgrid/-/sendgrid-5.2.3.tgz", + "integrity": "sha512-FD7oR9TbJFUew1p0Vw9JX0wBetDyq634LzylSXz4n9+hwaf+6a9dNloZl8CcjpsX4NuEc3HJanTN4GjDwNyi4A==", + "requires": { + "async.ensureasync": "^0.5.2", + "async.queue": "^0.5.2", + "bottleneck": "^1.12.0", + "debug": "^2.2.0", + "lodash.chunk": "^4.2.0", + "mailparser": "^0.6.1", + "sendgrid-rest": "^2.3.0" + } + }, + "sendgrid-rest": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/sendgrid-rest/-/sendgrid-rest-2.6.1.tgz", + "integrity": "sha512-W7gPROAPkEiD3+XQh+jeiU5mTNWqfgn45y1Xi8WiFA3CYKGOvHn+U0d62pTWjjynNqsmQIq9AJ0+93Bq67gbSQ==", + "requires": { + "core-js": "^3.5.0", + "dotenv": "^4.0.0" + }, + "dependencies": { + "dotenv": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "integrity": "sha512-XcaMACOr3JMVcEv0Y/iUM2XaOsATRZ3U1In41/1jjK6vJZ2PZbQ1bzCG8uvaByfaBpl9gqc9QWJovpUGBXLLYQ==" + } + } + }, "serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -2997,6 +3550,15 @@ "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" }, + "simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3034,6 +3596,15 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -3052,6 +3623,12 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true + }, "tr46": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", @@ -3123,14 +3700,20 @@ } }, "typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==" + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==" + }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true }, "undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" }, "unpipe": { "version": "1.0.0", @@ -3142,6 +3725,15 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, + "uue": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/uue/-/uue-3.1.2.tgz", + "integrity": "sha512-axKLXVqwtdI/czrjG0X8hyV1KLgeWx8F4KvSbvVCnS+RUvsQMGRjx0kfuZDXXqj0LYvVJmx3B9kWlKtEdRrJLg==", + "requires": { + "escape-string-regexp": "~1.0.5", + "extend": "~3.0.0" + } + }, "uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", diff --git a/api/package.json b/api/package.json index de25796..07fe8b2 100644 --- a/api/package.json +++ b/api/package.json @@ -5,10 +5,10 @@ "main": "src/server.ts", "scripts": { "build": "tsc", - "start": "nodemon ./dist/server.js", - "dev": "nodemon ./src/server.ts", + "start": "npx ts-node src/server.ts", + "dev": "nodemon --watch 'src/**/*.ts' --exec 'npx ts-node src/server.ts'", "test": "echo \"Error: no test specified\" && exit 1", - "prettier": "prettier --single-quote --write 'src/**/*.{js,ts}'", + "prettier": "npx prettier --write .", "format": "prettier --check ." }, "keywords": [], @@ -21,6 +21,7 @@ "express": "^4.21.0", "mongodb": "^6.9.0", "mongoose": "^8.4.1", + "sendgrid": "^5.2.3", "ts-node-dev": "^2.0.0", "uuid": "^10.0.0" }, @@ -30,7 +31,9 @@ "@types/node": "^22.8.1", "@types/uuid": "^10.0.0", "body-parser": "^1.20.3", + "nodemon": "^3.1.7", "prettier": "^3.3.3", + "ts-node": "^10.9.2", "typescript": "^5.6.2" } } diff --git a/api/src/controllers/auth0-errors.ts b/api/src/controllers/auth0-errors.ts new file mode 100644 index 0000000..23dcee5 --- /dev/null +++ b/api/src/controllers/auth0-errors.ts @@ -0,0 +1,35 @@ +import { Request, Response, NextFunction } from "express"; +import { + InvalidTokenError, + UnauthorizedError, +} from "express-oauth2-jwt-bearer"; + +export const errorHandler = ( + error: any, + request: Request, + response: Response, + next: NextFunction, +) => { + console.log("Auth Error"); + + if (error instanceof InvalidTokenError) { + const message = "Bad credentials"; + + response.status(error.status).json({ message }); + + return; + } + + if (error instanceof UnauthorizedError) { + const message = "Requires authentication"; + + response.status(error.status).json({ message }); + + return; + } + + const status = 500; + const message = "Internal Server Error"; + + response.status(status).json({ message }); +}; diff --git a/api/src/controllers/auth0-middleware.ts b/api/src/controllers/auth0-middleware.ts new file mode 100644 index 0000000..ed88db0 --- /dev/null +++ b/api/src/controllers/auth0-middleware.ts @@ -0,0 +1,21 @@ +import { auth } from "express-oauth2-jwt-bearer"; +import dotenv from "dotenv"; +import path from "path"; + +dotenv.config({ path: path.resolve(__dirname, "../.env") }); + +const auth0Domain = process.env.AUTH0_DOMAIN; +const auth0Audience = process.env.AUTH0_AUDIENCE; + +try { + if (!auth0Domain || !auth0Audience) { + throw new Error("AUTH0_DOMAIN or AUTH0_AUDIENCE is not set"); + } +} catch (error) { + console.error(error); +} + +export const validateAccessToken = auth({ + issuerBaseURL: `https://${auth0Domain}`, + audience: auth0Audience, +}); diff --git a/api/src/controllers/auth0-notFound.ts b/api/src/controllers/auth0-notFound.ts new file mode 100644 index 0000000..752e809 --- /dev/null +++ b/api/src/controllers/auth0-notFound.ts @@ -0,0 +1,11 @@ +import { Request, Response, NextFunction } from "express"; + +export const notFoundHandler = ( + request: Request, + response: Response, + next: NextFunction, +) => { + const message = "Not Found"; + + response.status(404).json({ message }); +}; diff --git a/api/src/routes/user.ts b/api/src/routes/user.ts index ecfa60e..4395f00 100644 --- a/api/src/routes/user.ts +++ b/api/src/routes/user.ts @@ -2,9 +2,13 @@ import express from "express"; import mongoose from "mongoose"; import dbConnect from "../config/db"; import sgMail from "@sendgrid/mail"; +// import { validateAccessToken } from "../controllers/auth0-middleware"; const router = express.Router(); +// TODO: Add auth0 middleware +// router.use(validateAccessToken); + // Call the dbConnect function to connect to MongoDB dbConnect(); @@ -19,6 +23,13 @@ const userSchema = new mongoose.Schema({ menteeInfo: [String], // For mentors only meetingSchedule: [String], // For mentees only mentorData: String, // For mentees only + meetings: [ + { + name: String, // title ?? + notes: String, + createdAt: { type: Date, default: Date.now }, + }, + ], }); const User = mongoose.model("User", userSchema); @@ -35,6 +46,7 @@ router.post("/create-user", async (req: any, res: any) => { menteeInfo, meetingSchedule, mentorData, + meetings, } = req.body; if (!firstName || !lastName || !username || !email || !role) { @@ -52,6 +64,7 @@ router.post("/create-user", async (req: any, res: any) => { menteeInfo: role === "mentor" ? menteeInfo : undefined, meetingSchedule: role === "mentee" ? meetingSchedule : undefined, mentorData: role === "mentee" ? mentorData : undefined, + meetings: meetings || [], }); try { @@ -67,9 +80,15 @@ router.post("/create-user", async (req: any, res: any) => { // Test route to check if the API is working router.post("/test", async (req: any, res: any) => { console.log("Received group data:"); - const { name } = req.body; - return res.status(200).json({ name }); + let name; + if (req.body.name === undefined) { + name = "empty"; + } else { + ({ name } = req.body); + } + + return res.status(200).json(`Your name is ${name}`); }); router.post("/send-email", async (req: any, res: any) => { @@ -81,14 +100,19 @@ router.post("/send-email", async (req: any, res: any) => { throw new Error("SendGrid API key or test email is missing"); } - const { email, name } = req.body; + const { email, name, role } = req.body; sgMail.setApiKey(SENDGRID_API_KEY); + const templateId = + role.toLowerCase().trim() === "mentor" + ? "d-1694192e437348e2a0517103acae3f00" + : "d-7e26b82cf8624bafa4077b6ed73b52bf"; + await sgMail.send({ to: email, from: SEND_GRID_TEST_EMAIL, - templateId: "d-7e26b82cf8624bafa4077b6ed73b52bf", + templateId: templateId, dynamicTemplateData: { name: name, }, @@ -100,4 +124,39 @@ router.post("/send-email", async (req: any, res: any) => { } }); +// Route to add a meeting +router.post("/add-meeting", async (req, res) => { + const { username, meeting, notes } = req.body; + + // Validate required fields + if (!username || !meeting || !notes) { + return res.status(400).json({ message: "Missing required fields" }); + } + + try { + // Find the user by username + console.log("Searching for user with username:", username); + const user = await User.findOne({ username }); + + if (!user) { + console.error(`User not found for username: ${username}`); + return res.status(404).json({ message: "User not found" }); + } + + // Add the meeting to the user's meetings array + user.meetings.push({ name: meeting, notes }); + + // Save the updated user document + await user.save(); + + console.log("Meeting added successfully for username:", username); + return res + .status(200) + .json({ message: "Meeting added successfully", user }); + } catch (error) { + console.error("Error adding meeting:", error); + return res.status(500).json({ message: "Error adding meeting", error }); + } +}); + export default router; diff --git a/api/src/routes/workshop.ts b/api/src/routes/workshop.ts index 9fd6946..32094f1 100644 --- a/api/src/routes/workshop.ts +++ b/api/src/routes/workshop.ts @@ -10,39 +10,42 @@ import { const router = express.Router(); +// TODO: Add auth0 middleware +// router.use(validateAccessToken); + // Call the dbConnect function to connect to MongoDB dbConnect(); -// Workshop schema definition (name and S3 bucket ID) -const workshopIDSchema = new mongoose.Schema({ - name: String, - s3ID: String, +// Workshop schema definition (name (required by user), description (required by user), and S3 bucket ID (not required as user input)) +const workshopSchema = new mongoose.Schema({ + name: { type: String, required: true }, + description: { type: String, required: true }, + s3id: { type: String, required: false }, }); -const Workshop = mongoose.model("WorkshopID", workshopIDSchema); +const Workshop = mongoose.model("Workshop", workshopSchema); // Route to create a new workshop router.post("/create-workshop", async (req: any, res: any) => { - const { name, s3id } = req.body; + const { name, description, s3id } = req.body; - if (!name || !s3id) { + if (!name || !description) { return res.status(400).json({ message: "Missing required fields" }); } - // Create a new workshop - const newWorkshop = new Workshop({ - name, - s3id, - }); - try { + // Create a new workshop with + const newWorkshop = new Workshop({ name, description, s3id }); const savedWorkshop = await newWorkshop.save(); + + // Success: res.status(201).json({ message: "Workshop created successfully", - WorkshopID: savedWorkshop, + workshop: savedWorkshop, }); } catch (error) { - res.status(401).json({ message: "Failed to create workshop", error }); + console.error("Error saving workshop:", error); + res.status(500).json({ message: "Failed to create workshop", error }); } }); @@ -76,4 +79,3 @@ router.get( // export default router; export default router; -// NO POPULATE VERSION diff --git a/api/src/server.ts b/api/src/server.ts index c3f6ace..1557625 100644 --- a/api/src/server.ts +++ b/api/src/server.ts @@ -1,24 +1,23 @@ import dotenv from "dotenv"; +import path from "path"; + dotenv.config({ path: path.resolve(__dirname, "../.env") }); import express from "express"; import bodyParser from "body-parser"; import connectDB from "./config/db"; -import * as routes from "./routes/index"; -import path from "path"; var cors = require("cors"); const app = express(); -app.use(cors()); +app.use(cors({ origin: "http://localhost:3000" })); // Connect to the frontend PORT 3000 app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); -app.use("/user", routes.user); -app.use("/workshop", routes.workshop); - connectDB(); -app.listen(process.env.PORT || 8000, () => - console.log(`Server running on port ${process.env.PORT || 8000}`), -); +import * as routes from "./routes/index"; +app.use("/user", routes.user); +app.use("/api", routes.workshop); // New workshop route + +app.listen(process.env.PORT || 8000, () => console.log("Server running...")); diff --git a/app/.env b/app/.env deleted file mode 100644 index 61fb955..0000000 --- a/app/.env +++ /dev/null @@ -1 +0,0 @@ -REACT_APP_API_URL = http://127.0.0.1:8000 \ No newline at end of file diff --git a/app/package-lock.json b/app/package-lock.json index 4cb3bea..57f4d2c 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -8,6 +8,7 @@ "name": "my-app", "version": "0.1.0", "dependencies": { + "@auth0/auth0-react": "^2.2.4", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -27,6 +28,9 @@ "typescript": "^4.9.5", "web-vitals": "^2.1.4", "yup": "^1.4.0" + }, + "devDependencies": { + "prettier": "^3.3.3" } }, "node_modules/@adobe/css-tools": { @@ -57,6 +61,23 @@ "node": ">=6.0.0" } }, + "node_modules/@auth0/auth0-react": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.2.4.tgz", + "integrity": "sha512-l29PQC0WdgkCoOc6WeMAY26gsy/yXJICW0jHfj0nz8rZZphYKrLNqTRWFFCMJY+sagza9tSgB1kG/UvQYgGh9A==", + "dependencies": { + "@auth0/auth0-spa-js": "^2.1.3" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17 || ^18", + "react-dom": "^16.11.0 || ^17 || ^18" + } + }, + "node_modules/@auth0/auth0-spa-js": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.1.3.tgz", + "integrity": "sha512-NMTBNuuG4g3rame1aCnNS5qFYIzsTUV5qTFPRfTyYFS1feS6jsCBR+eTq9YkxCp1yuoM2UIcjunPaoPl77U9xQ==" + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -14467,6 +14488,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -18406,6 +18442,19 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, + "@auth0/auth0-react": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.2.4.tgz", + "integrity": "sha512-l29PQC0WdgkCoOc6WeMAY26gsy/yXJICW0jHfj0nz8rZZphYKrLNqTRWFFCMJY+sagza9tSgB1kG/UvQYgGh9A==", + "requires": { + "@auth0/auth0-spa-js": "^2.1.3" + } + }, + "@auth0/auth0-spa-js": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.1.3.tgz", + "integrity": "sha512-NMTBNuuG4g3rame1aCnNS5qFYIzsTUV5qTFPRfTyYFS1feS6jsCBR+eTq9YkxCp1yuoM2UIcjunPaoPl77U9xQ==" + }, "@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -28412,6 +28461,12 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, + "prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/app/package.json b/app/package.json index 1460a5e..73ea78c 100644 --- a/app/package.json +++ b/app/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@auth0/auth0-react": "^2.2.4", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -27,7 +28,9 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "format": "npx prettier --write \"src/**/*.{js,jsx,ts,tsx}\"", + "format-check": "npx prettier --check \"src/**/*.{js,jsx,ts,tsx}\"" }, "eslintConfig": { "extends": [ @@ -46,5 +49,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "prettier": "^3.3.3" } } diff --git a/app/src/App.tsx b/app/src/App.tsx index acc3ce2..f98fdb6 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1,21 +1,24 @@ import React, { type ReactElement } from "react"; -import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import { Routes, Route } from "react-router-dom"; import Home from "./pages/Home"; import MentorDashboard from "./pages/MentorDashboard"; +import MenteeDashboard from "./pages/MenteeDashboard"; import CreateWorkshop from "./pages/CreateWorkshop"; import CreateMeeting from "./pages/CreateMeeting"; +import AuthCallback from "./pages/auth-callback"; + function App(): ReactElement { return (
- - - } /> - } /> - } /> - } /> - } /> - - + + } /> + } /> + } /> + }/> + } /> + } /> + } /> +
); } diff --git a/app/src/assets/home.png b/app/src/assets/home.png new file mode 100644 index 0000000..f3b2179 Binary files /dev/null and b/app/src/assets/home.png differ diff --git a/app/src/components/Navbar.tsx b/app/src/components/Navbar.tsx index f0f8ef6..c8e5af0 100644 --- a/app/src/components/Navbar.tsx +++ b/app/src/components/Navbar.tsx @@ -1,8 +1,14 @@ import react, { type ReactElement } from "react"; import { useNavigate } from "react-router-dom"; +import { LoginButton } from "./mock-login-button"; +import { SignupButton } from "./mock-sign-up"; +import { LogoutButton } from "./mock-logout-button"; +import { useAuth0 } from "@auth0/auth0-react"; const Navbar = (): ReactElement => { const navigate = useNavigate(); + const { isAuthenticated } = useAuth0(); + return ( <>
@@ -15,13 +21,11 @@ const Navbar = (): ReactElement => { >
{ navigate("/home"); }} - > - Home -
+ className="Navbar-body-home" + >
{ @@ -30,6 +34,14 @@ const Navbar = (): ReactElement => { > Mentor
+
{ + navigate("/mentee"); + }} + > + Mentee +
{ @@ -47,6 +59,15 @@ const Navbar = (): ReactElement => { Create Meeting
+
+ {!isAuthenticated && ( + <> + + + + )} + {isAuthenticated && } +
diff --git a/app/src/components/mock-login-button.tsx b/app/src/components/mock-login-button.tsx new file mode 100644 index 0000000..9be1df9 --- /dev/null +++ b/app/src/components/mock-login-button.tsx @@ -0,0 +1,34 @@ +import { useAuth0 } from "@auth0/auth0-react"; +import React from "react"; + +export const LoginButton = () => { + const { loginWithRedirect } = useAuth0(); + + const handleLogin = async () => { + await loginWithRedirect({ + appState: { + returnTo: "/home", + }, + }); + }; + + return ( + + ); +}; diff --git a/app/src/components/mock-logout-button.tsx b/app/src/components/mock-logout-button.tsx new file mode 100644 index 0000000..37a5400 --- /dev/null +++ b/app/src/components/mock-logout-button.tsx @@ -0,0 +1,34 @@ +import { useAuth0 } from "@auth0/auth0-react"; +import React from "react"; + +export const LogoutButton = () => { + const { logout } = useAuth0(); + + const handleLogout = () => { + logout({ + logoutParams: { + returnTo: window.location.origin, + }, + }); + }; + + return ( + + ); +}; diff --git a/app/src/components/mock-sign-up.tsx b/app/src/components/mock-sign-up.tsx new file mode 100644 index 0000000..4fdd26e --- /dev/null +++ b/app/src/components/mock-sign-up.tsx @@ -0,0 +1,37 @@ +import { useAuth0 } from "@auth0/auth0-react"; +import React from "react"; + +export const SignupButton = () => { + const { loginWithRedirect } = useAuth0(); + + const handleSignUp = async () => { + await loginWithRedirect({ + appState: { + returnTo: "/home", + }, + authorizationParams: { + screen_hint: "signup", + }, + }); + }; + + return ( + + ); +}; diff --git a/app/src/index.tsx b/app/src/index.tsx index cf4e546..906ae28 100644 --- a/app/src/index.tsx +++ b/app/src/index.tsx @@ -3,14 +3,21 @@ import ReactDOM from "react-dom/client"; import "./styles/main.scss"; import App from "./App"; import reportWebVitals from "./reportWebVitals"; +import { Auth0ProviderWithNavigate } from "./utils/auth0-provider"; +import { BrowserRouter } from "react-router-dom"; +document.title = "PWW"; const root = ReactDOM.createRoot( - document.getElementById("root") as HTMLElement, + document.getElementById("root") as HTMLElement ); root.render( - - , + + + + + + ); // If you want to start measuring performance in your app, pass a function diff --git a/app/src/pages/CreateMeeting.tsx b/app/src/pages/CreateMeeting.tsx index 5cdf493..4ef8724 100644 --- a/app/src/pages/CreateMeeting.tsx +++ b/app/src/pages/CreateMeeting.tsx @@ -1,20 +1,48 @@ -import React, { useState } from "react"; -import { Formik, Form, Field, FieldArray } from "formik"; +import React from "react"; +import { Formik, Form, Field } from "formik"; import Navbar from "../components/Navbar"; import * as Yup from "yup"; +import { api } from "../api"; // Ensure this points to your configured API instance + +const initialValues = { + meeting: "", + notes: "", +}; + +// Validation schema using Yup +const validationSchema = Yup.object().shape({ + meeting: Yup.string().required("Meeting name is required"), + notes: Yup.string().required("Meeting notes are required"), +}); const CreateMeeting = () => { - const initialValues = { - meeting: "", - notes: "", - }; - const validationSchema = Yup.object().shape({ - meeting: Yup.string().required("Please enter a meeting name"), - notes: Yup.string().required("Please enter meeting notes"), - }); - const handleSubmit = (values: any) => { - console.log(values); + const handleSubmit = async ( + values: any, + { setSubmitting, resetForm }: any, + ) => { + setSubmitting(true); + try { + const payload = { + username: "sample-username", // TODO: Replace with the logged-in user's username + meeting: values.meeting, + notes: values.notes, + }; + + console.log("Submitting payload:", payload); // Debugging log + + await api.post("/user/add-meeting", payload); + + alert("Meeting added successfully!"); + + resetForm(); // Clear the form after successful submission + } catch (error) { + console.error("Error adding meeting:", error); + alert("Failed to add meeting. Please try again."); + } finally { + setSubmitting(false); + } }; + return ( <> @@ -24,10 +52,10 @@ const CreateMeeting = () => { validationSchema={validationSchema} onSubmit={handleSubmit} > - {({ values, errors, touched, isSubmitting }) => ( + {({ errors, touched, isSubmitting }) => (
- + {
{errors.meeting}
)}
+
{
{errors.notes}
)}
+
)} diff --git a/app/src/pages/CreateWorkshop.tsx b/app/src/pages/CreateWorkshop.tsx index fa1e61d..00e0cc7 100644 --- a/app/src/pages/CreateWorkshop.tsx +++ b/app/src/pages/CreateWorkshop.tsx @@ -2,23 +2,41 @@ import React from "react"; import { Formik, Form, Field } from "formik"; import * as Yup from "yup"; import Navbar from "../components/Navbar"; +import { api } from "../api"; -const CreateWorkshop = () => { - // Initial form values - const initialValues = { - name: "", - description: "", - }; +const initialValues = { + name: "", + description: "", +}; - // Validation schema using Yup - const validationSchema = Yup.object().shape({ - name: Yup.string().required("Name is required"), - description: Yup.string().required("Description is required"), - }); +// Validation schema using Yup +const validationSchema = Yup.object().shape({ + name: Yup.string().required("Name is required"), + description: Yup.string().required("Description is required"), +}); +const CreateWorkshop = () => { // Handle form submission - const handleSubmit = (values: any) => { - console.log(values); + const handleSubmit = async ( + values: any, + { setSubmitting, resetForm }: any, + ) => { + setSubmitting(true); + try { + const payload = { + name: values.name, + description: values.description, + s3id: "example-s3-id", // TODO: Placeholder for S3 ID until set up + }; + + await api.post("/api/create-workshop", payload); + // api.ts deals with error responses ! + } catch (error) { + console.error("Error creating workshop:", error); + alert("Failed to create workshop. Please try again."); + } finally { + setSubmitting(false); + } }; return ( diff --git a/app/src/pages/MenteeDashboard.tsx b/app/src/pages/MenteeDashboard.tsx new file mode 100644 index 0000000..6c2e40f --- /dev/null +++ b/app/src/pages/MenteeDashboard.tsx @@ -0,0 +1,119 @@ +import React, { useState } from "react"; +import Navbar from "../components/Navbar"; + +const MenteeDashboard = () => { + return ( + <> + +
+
+
+ My courses +
+ +
+
+
+
+

+ Resume Workshop +

+

+ Workshop content +

+
+
+ +
+
+
+

+ Resume Workshop +

+

+ Workshop content +

+
+
+ +
+
+
+

+ Resume Workshop +

+

+ Workshop content +

+
+
+
+
+ + +
+
+ Upcoming Events! +
+
+ June +
+ +
+
+
+
wed
+
25
+
+
+
+ Mock Interview Session +
+
+ Practice your interview skills with an industry professional +
+
+
+
+ +
+
+
+
wed
+
25
+
+
+
+ Resume Workshop +
+
+ Develop your resume with a senior employee +
+
+
+
+
+
+ + ); +}; + +export default MenteeDashboard; diff --git a/app/src/pages/MentorDashboard.tsx b/app/src/pages/MentorDashboard.tsx index 6f2b98c..4c7c25e 100644 --- a/app/src/pages/MentorDashboard.tsx +++ b/app/src/pages/MentorDashboard.tsx @@ -1,13 +1,48 @@ import React, { useState } from "react"; import Navbar from "../components/Navbar"; + const MentorDashboard = () => { + return ( <> -

Mentor Dashboard

-
-
Example Button
+
+
+ +
+
My Mentees
+
Courses
+
+ +
+
+
+
+

JaneDoe

+
+
+ +
+
+
+

Jane Doe

+
+
+ +
+
+
+

Jane Doe

+
+
+
+ +
+
+
Upcoming Events!
+
+
); diff --git a/app/src/pages/auth-callback.tsx b/app/src/pages/auth-callback.tsx new file mode 100644 index 0000000..d3a4b6f --- /dev/null +++ b/app/src/pages/auth-callback.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import Navbar from "../components/Navbar"; + +const AuthCallback = () => { + return ; +}; + +export default AuthCallback; diff --git a/app/src/styles/_components.scss b/app/src/styles/_components.scss index f79bd2b..f66a4ee 100644 --- a/app/src/styles/_components.scss +++ b/app/src/styles/_components.scss @@ -5,22 +5,50 @@ &-body { display: flex; - flex-direction: row; - align-items: center; - background-color: var(--pww-color-light-1000); - padding: 10px 32px; - box-shadow: var(--pww-shadow-dark); - - &-logo { + flex-direction: column; + z-index: var(--pww-z-50); + + &-body { + display: flex; + flex-direction: row; + align-items: center; + background-color: var(--pww-color-light-1000); + padding: 10px 32px; + box-shadow: var(--pww-shadow-dark); + + &-logo { + font-size: var(--pww-font-size-30); + font-weight: 900; + background-image: url('../assets/logo.png'); + background-size: contain; /* Ensure the entire logo fits within the defined area */ + background-repeat: no-repeat; /* Prevent repeating */ + background-position: center; /* Center the logo within its container */ + width: 125px; + height: 75px; /* Adjust height based on the image’s aspect ratio */ + } + &-home { + font-size: var(--pww-font-size-30); + font-weight: 900; + background-image: url('../assets/home.png'); + background-size: contain; /* Ensure the entire logo fits within the defined area */ + background-repeat: no-repeat; /* Prevent repeating */ + background-position: center; /* Center the logo within its container */ + width: 50px; // Reduced from 125px to be more proportional + height: 50px; // Reduced from 70px to match navbar height + margin-right: 20px; + } + + &-home { font-size: var(--pww-font-size-30); font-weight: 900; - background-image: url("../assets/logo.png"); + background-image: url('../assets/home.png'); background-size: contain; /* Ensure the entire logo fits within the defined area */ background-repeat: no-repeat; /* Prevent repeating */ background-position: center; /* Center the logo within its container */ - width: 125px; - height: 75px; /* Adjust height based on the image’s aspect ratio */ - } + width: 50px; // Reduced from 125px to be more proportional + height: 50px; // Reduced from 70px to match navbar height + margin-right: 20px; + } &-link { color: var(--pww-color-dark-1000); @@ -121,3 +149,4 @@ } } } +} diff --git a/app/src/styles/_utilities.scss b/app/src/styles/_utilities.scss index 0f590c0..c4525ac 100644 --- a/app/src/styles/_utilities.scss +++ b/app/src/styles/_utilities.scss @@ -117,6 +117,10 @@ justify-content: space-between; } +.Justify-content--center { + justify-content: center; +} + .Align-items--center { align-items: center; } @@ -332,6 +336,11 @@ padding: 2px 5px; } + &--large { + font-size: 20px; + padding: 8px 16px; + } + &:hover { background-color: transparent; cursor: pointer; @@ -390,6 +399,7 @@ background-color: var(--pww-color-light-50); padding: 10px; border-radius: var(--pww-border-radius-8); + border: 1px solid var(--pww-color-gray-200); box-shadow: var(--pww-shadow-dark); height: fit-content; @@ -399,6 +409,13 @@ box-shadow: 0 0 3px 3px var(--pww-color-light-500); } } + &--noPadding { + padding: 0; + } + + &--noPadding { + padding: 0; + } &-dark { background-color: var(--pww-color-dark-1000); @@ -406,14 +423,15 @@ } .Block { - padding: 56px 48px; + padding: 30px 30px; box-shadow: var(--pww-shadow-dark); background-color: var(--pww-color-light-1000); border-radius: var(--pww-border-radius-8); + border: 1px solid var(--pww-color-gray-200); &-header { font-size: var(--pww-font-size-24); - margin-bottom: 10px; + margin-bottom: 30px; } &-subtitle { @@ -425,13 +443,19 @@ } } + +.Image { + width: 50%; + height: auto; +} + .Modal--overlay { background-color: rgba(0, 0, 0, 0.4); position: fixed; top: 0; left: 0; right: 0; - bottom: 0; + bottom: 0x; z-index: var(--pww-z-100); overflow: hidden; } @@ -458,6 +482,11 @@ // } } +.Flex-grid { + display: flex; + flex-wrap: wrap; +} + .Flex-column { display: flex; flex-direction: column; @@ -520,6 +549,7 @@ // } } + .Display--gteDesktop { display: none; @@ -555,6 +585,11 @@ // } // } } +.Width--40 { width: 40%; } +.Width--60 { width: 60%; } + +.Width--40 { width: 40%; } +.Width--60 { width: 60%; } .Width--50 { width: 50%; @@ -566,6 +601,13 @@ .Height--100 { height: 100%; } +.Height--100vh { + height: 100vh; +} + +.Height--100vh { + height: 100vh; +} ul { list-style-type: none; diff --git a/app/src/styles/_variables.scss b/app/src/styles/_variables.scss index 310e609..a608e19 100644 --- a/app/src/styles/_variables.scss +++ b/app/src/styles/_variables.scss @@ -3,12 +3,17 @@ //add spaces so that the hex codes are lined up $pww-color-yellow: #b1cf24; -$pww-color-teal: #149baf; -$pww-color-blue: #32559a; +//$pww-color-teal: #149baf; +$pww-color-teal: #0096C0; +//$pww-color-blue: #32559a; +$pww-color-blue: #4C65A6; $pww-color-red: #db504a; -$pww-color-green: #4a9a4e; +//$pww-color-green: #4a9a4e; +$pww-color-green: #BFD71B; $pww-color-light: #ffffff; -$pww-color-gray: #626262; +$pww-color-neutral: #f6f4ef; +//$pww-color-gray: #626262; +$pww-color-gray: #545454; $pww-color-dark: #000000; // allow the colors to be used in inline styles @@ -19,6 +24,7 @@ $palette: ( "red": $pww-color-red, "green": $pww-color-green, "light": $pww-color-light, + "neutral": $pww-color-neutral, "gray": $pww-color-gray, "dark": $pww-color-dark, ); @@ -64,6 +70,7 @@ $zIndex: -10 -5 0 5 10 50 100 1000; // initializes all the vairations of the variables :root { + @each $fontSize in $fontSizes { --pww-font-size-#{$fontSize}: #{$fontSize}px; } diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 3de83af..363de19 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -1,5 +1,6 @@ // Imports for all of the external libraries @import url("https://fonts.googleapis.com/css?family=Rubik"); +@import url('https://fonts.googleapis.com/css?family=Lato'); @import url("https://pro.fontawesome.com/releases/v5.10.0/css/all.css"); @import "~bootstrap/scss/bootstrap-utilities.scss"; @@ -32,14 +33,15 @@ // Fonts html, body { - font-family: "Quattrocento"; + //font-family: "Quattrocento"; + font-family: Lato; margin: 0; } // default settings for the body body { font-size: var(--pww-font-size-4); - background-color: var(--pww-color-blue-100); + background-color: var(--pww-color-neutral-1000); color: var(--pww-color-dark-1000); } diff --git a/app/src/utils/auth0-provider.tsx b/app/src/utils/auth0-provider.tsx new file mode 100644 index 0000000..717efe7 --- /dev/null +++ b/app/src/utils/auth0-provider.tsx @@ -0,0 +1,36 @@ +import { Auth0Provider } from "@auth0/auth0-react"; +import React from "react"; +import { useNavigate } from "react-router-dom"; + +export const Auth0ProviderWithNavigate = ({ + children, +}: { + children: React.ReactNode; +}) => { + const navigate = useNavigate(); + + const domain = process.env.REACT_APP_AUTH0_DOMAIN; + const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID; + const redirectUri = process.env.REACT_APP_AUTH0_CALLBACK_URL; + + const onRedirectCallback = (appState: any) => { + navigate(appState?.returnTo || window.location.pathname); + }; + + if (!(domain && clientId && redirectUri)) { + return null; + } + + return ( + + {children} + + ); +};