diff --git a/.github/workflows/lint-check.yml b/.github/workflows/lint-check.yml index 1cabed9..9d6d220 100644 --- a/.github/workflows/lint-check.yml +++ b/.github/workflows/lint-check.yml @@ -2,7 +2,7 @@ name: Lint, style, and compilation checks on: pull_request: - branches: + branches: - main jobs: @@ -16,6 +16,16 @@ jobs: run: | npm ci npm run lint-check + env: + PORT: ${{ secrets.PORT }} + MONGODB_URI: ${{ secrets.MONGODB_URI }} + FRONTEND_ORIGIN: ${{ secrets.FRONTEND_ORIGIN }} + EMAIL_USER: ${{ secrets.EMAIL_USER }} + EMAIL_APP_PASSWORD: ${{ secrets.EMAIL_APP_PASSWORD }} + EMAIL_NOTIFICATIONS_RECIPIENT: ${{ secrets.EMAIL_NOTIFICATIONS_RECIPIENT }} + BACKEND_FIREBASE_SETTINGS: ${{ secrets.BACKEND_FIREBASE_SETTINGS }} + SERVICE_ACCOUNT_KEY: ${{ secrets.SERVICE_ACCOUNT_KEY }} + frontend: name: Frontend check runs-on: ubuntu-latest @@ -27,3 +37,6 @@ jobs: npm ci npm run lint-check npm run build + env: + NEXT_PUBLIC_BACKEND_URL: ${{ secrets.NEXT_PUBLIC_BACKEND_URL }} + NEXT_PUBLIC_FIREBASE_SETTINGS: ${{ secrets.NEXT_PUBLIC_FIREBASE_SETTINGS }} diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 115b29b..4a5bbf4 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,8 +1,9 @@ name: Run frontend & backend unit tests on: pull_request: - branches: + branches: - main + jobs: backend: name: Backend tests @@ -12,11 +13,20 @@ jobs: - uses: actions/setup-node@v3 - name: Run tests working-directory: backend - env: - CI: true run: | npm ci npm run test + env: + CI: true + PORT: ${{ secrets.PORT }} + MONGODB_URI: ${{ secrets.MONGODB_URI }} + FRONTEND_ORIGIN: ${{ secrets.FRONTEND_ORIGIN }} + EMAIL_USER: ${{ secrets.EMAIL_USER }} + EMAIL_APP_PASSWORD: ${{ secrets.EMAIL_APP_PASSWORD }} + EMAIL_NOTIFICATIONS_RECIPIENT: ${{ secrets.EMAIL_NOTIFICATIONS_RECIPIENT }} + BACKEND_FIREBASE_SETTINGS: ${{ secrets.BACKEND_FIREBASE_SETTINGS }} + SERVICE_ACCOUNT_KEY: ${{ secrets.SERVICE_ACCOUNT_KEY }} + frontend: name: Frontend tests runs-on: ubuntu-latest @@ -25,8 +35,10 @@ jobs: - uses: actions/setup-node@v3 - name: Run tests working-directory: frontend - env: - CI: true run: | npm ci npm run test + env: + CI: true + NEXT_PUBLIC_BACKEND_URL: ${{ secrets.NEXT_PUBLIC_BACKEND_URL }} + NEXT_PUBLIC_FIREBASE_SETTINGS: ${{ secrets.NEXT_PUBLIC_FIREBASE_SETTINGS }} diff --git a/backend/.env.example b/backend/.env.example index 3a23d39..6715afb 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,8 +1,9 @@ -# Example environment variables for backend. Any ""s should be -# replaced with the real values. -PORT=3001 -MONGODB_URI="mongodb://127.0.0.1:27017/pap" -FRONTEND_ORIGIN="http://localhost:3000" -EMAIL_USER="@gmail.com" -EMAIL_APP_PASSWORD="" -BACKEND_FIREBASE_SETTINGS='' +# Example environment variables for backend. Any ""s should be +# replaced with the real values. +PORT=3001 +MONGODB_URI="mongodb://127.0.0.1:27017/pap" +FRONTEND_ORIGIN="http://localhost:3000" +EMAIL_USER="@gmail.com" +EMAIL_APP_PASSWORD="" +BACKEND_FIREBASE_SETTINGS='' +SERVICE_ACCOUNT_KEY='' \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index c98070d..14dc746 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,7 +13,7 @@ "envalid": "^7.3.1", "express": "^4.18.2", "express-validator": "^7.0.1", - "firebase-admin": "^11.11.1", + "firebase": "^10.7.2", "http-errors": "^2.0.0", "jest": "^29.7.0", "module-alias": "^2.2.3", @@ -40,6 +40,7 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-no-relative-import-paths": "^1.5.2", "eslint-plugin-react": "^7.33.0", + "firebase-admin": "^12.0.0", "husky": "^8.0.3", "nodemon": "^3.0.1", "prettier": "^3.1.1", @@ -1555,6 +1556,7 @@ }, "node_modules/@fastify/busboy": { "version": "1.2.1", + "dev": true, "license": "MIT", "dependencies": { "text-decoding": "^1.0.0" @@ -1563,14 +1565,159 @@ "node": ">=14" } }, + "node_modules/@firebase/analytics": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.0.tgz", + "integrity": "sha512-Locv8gAqx0e+GX/0SI3dzmBY5e9kjVDtD+3zCFLJ0tH2hJwuCAiL+5WkHuxKj92rqQj/rvkBUCfA1ewlX2hehg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.6.tgz", + "integrity": "sha512-4MqpVLFkGK7NJf/5wPEEP7ePBJatwYpyjgJ+wQHQGHfzaCDgntOnl9rL2vbVGGKCnRqWtZDIWhctB86UWXaX2Q==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-types": "0.8.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.0.tgz", + "integrity": "sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==" + }, + "node_modules/@firebase/app": { + "version": "0.9.26", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.26.tgz", + "integrity": "sha512-zCjo6KhNhbuFB+V+Z4H9g4+BZ78E7n3ShxaBtuIcRkpwdm7+1BsafzChOsDYuI86m97HUWsyLPurLBhqcupFFA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.1.tgz", + "integrity": "sha512-zi3vbM5tb/eGRWyiqf+1DXbxFu9Q07dnm46rweodgUpH9B8svxYkHfNwYWx7F5mjHU70SQDuaojH1We5ws9OKA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.8.tgz", + "integrity": "sha512-EaETtChR4UgMokJFw+r6jfcIyCTUZSe0a6ivF37D9MxlG9G3wzK1COyXgxoX96GzXmDPc2aubX4PxCrdVHhrnA==", + "dependencies": { + "@firebase/app-check": "0.8.1", + "@firebase/app-check-types": "0.5.0", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.0.tgz", + "integrity": "sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.26", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.26.tgz", + "integrity": "sha512-tVNOYvB3lIFkN3RmcTieo5qYRIkYak9iC6E7dZMxax52uMIUJiIKKtPkarbwZh6EnUxru5hJRo8tfUZGuaQDQw==", + "dependencies": { + "@firebase/app": "0.9.26", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, "node_modules/@firebase/app-types": { "version": "0.9.0", "license": "Apache-2.0" }, + "node_modules/@firebase/auth": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.5.1.tgz", + "integrity": "sha512-sVi7rq2YneLGJFqHa5S6nDfCHix9yuVV3RLhj/pWPlB4a36ofXal4E6PJwpeMc8uLjWEr1aovYN1jkXWNB6Avw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.1.tgz", + "integrity": "sha512-rgDZnrDoekRvtzXVji8Z61wxxkof6pTkjYEkybILrjM8tGP9tx4xa9qGpF4ax3AzF+rKr7mIa9NnoXEK4UNqmQ==", + "dependencies": { + "@firebase/auth": "1.5.1", + "@firebase/auth-types": "0.12.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, "node_modules/@firebase/auth-interop-types": { "version": "0.2.1", "license": "Apache-2.0" }, + "node_modules/@firebase/auth-types": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.0.tgz", + "integrity": "sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, "node_modules/@firebase/component": { "version": "0.6.4", "license": "Apache-2.0", @@ -1580,9 +1727,11 @@ } }, "node_modules/@firebase/database": { - "version": "0.14.4", - "license": "Apache-2.0", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.2.tgz", + "integrity": "sha512-8X6NBJgUQzDz0xQVaCISoOLINKat594N2eBbMR3Mu/MH/ei4WM+aAMlsNzngF22eljXu1SILP5G3evkyvsG3Ng==", "dependencies": { + "@firebase/app-check-interop-types": "0.3.0", "@firebase/auth-interop-types": "0.2.1", "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -1592,25 +1741,151 @@ } }, "node_modules/@firebase/database-compat": { - "version": "0.3.4", - "license": "Apache-2.0", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.2.tgz", + "integrity": "sha512-09ryJnXDvuycsxn8aXBzLhBTuCos3HEnCOBWY6hosxfYlNCGnLvG8YMlbSAt5eNhf7/00B095AEfDsdrrLjxqA==", "dependencies": { "@firebase/component": "0.6.4", - "@firebase/database": "0.14.4", - "@firebase/database-types": "0.10.4", + "@firebase/database": "1.0.2", + "@firebase/database-types": "1.0.0", "@firebase/logger": "0.4.0", "@firebase/util": "1.9.3", "tslib": "^2.1.0" } }, "node_modules/@firebase/database-types": { - "version": "0.10.4", - "license": "Apache-2.0", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.0.tgz", + "integrity": "sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==", "dependencies": { "@firebase/app-types": "0.9.0", "@firebase/util": "1.9.3" } }, + "node_modules/@firebase/firestore": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.4.1.tgz", + "integrity": "sha512-LCWZZ+rgNET1qw3vpugmGCJZVbz7c5NkgKect5pZn36gaBzGVb8+pRQ8WSZ1veYVMOK6SKrBkS1Rw6EqcmPnyw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "@firebase/webchannel-wrapper": "0.10.5", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "engines": { + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.24.tgz", + "integrity": "sha512-Wj5cgqmQwTnqHS4KabOpXCNIaSTtVDP1NitnhjXff04Q4QK0aeIbeO1TPlSSTmUb6S7KzoKD4XR99hfKZDYbfA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/firestore": "4.4.1", + "@firebase/firestore-types": "3.0.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.0.tgz", + "integrity": "sha512-Meg4cIezHo9zLamw0ymFYBD4SMjLb+ZXIbuN7T7ddXN6MGoICmOTq3/ltdCGoDCS2u+H1XJs2u/cYp75jsX9Qw==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.0.tgz", + "integrity": "sha512-n1PZxKnJ++k73Q8khTPwihlbeKo6emnGzE0hX6QVQJsMq82y/XKmNpw2t/q30VJgwaia3ZXU1fd1C5wHncL+Zg==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.0", + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.6.tgz", + "integrity": "sha512-RQpO3yuHtnkqLqExuAT2d0u3zh8SDbeBYK5EwSCBKI9mjrFeJRXBnd3pEG+x5SxGJLy56/5pQf73mwt0OuH5yg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/functions": "0.11.0", + "@firebase/functions-types": "0.6.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.0.tgz", + "integrity": "sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.4.tgz", + "integrity": "sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "idb": "7.0.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.4.tgz", + "integrity": "sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/installations-types": "0.5.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.0.tgz", + "integrity": "sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/installations/node_modules/idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" + }, "node_modules/@firebase/logger": { "version": "0.4.0", "license": "Apache-2.0", @@ -1618,6 +1893,151 @@ "tslib": "^2.1.0" } }, + "node_modules/@firebase/messaging": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.5.tgz", + "integrity": "sha512-i/rrEI2k9ueFhdIr8KQsptWGskrsnkC5TkohCTrJKz9P0C/PbNv14IAMkwhMJTqIur5VwuOnrUkc9Kdz7awekw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.5.tgz", + "integrity": "sha512-qHQZxm4hEG8/HFU/ls5/bU+rpnlPDoZoqi3ATMeb6s4hovYV9+PfV5I7ZrKV5eFFv47Hx1PWLe5uPnS4e7gMwQ==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/messaging": "0.12.5", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.0.tgz", + "integrity": "sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==" + }, + "node_modules/@firebase/performance": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.4.tgz", + "integrity": "sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.4.tgz", + "integrity": "sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/performance": "0.6.4", + "@firebase/performance-types": "0.2.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.0.tgz", + "integrity": "sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.4.tgz", + "integrity": "sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.4.tgz", + "integrity": "sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-types": "0.3.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.0.tgz", + "integrity": "sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==" + }, + "node_modules/@firebase/storage": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.12.0.tgz", + "integrity": "sha512-SGs02Y/mmWBRsqZiYLpv4Sf7uZYZzMWVNN+aKiDqPsFBCzD6hLvGkXz+u98KAl8FqcjgB8BtSu01wm4pm76KHA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.3.tgz", + "integrity": "sha512-WNtjYPhpOA1nKcRu5lIodX0wZtP8pI0VxDJnk6lr+av7QZNS1s6zvr+ERDTve+Qu4Hq/ZnNaf3kBEQR2ccXn6A==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/storage": "0.12.0", + "@firebase/storage-types": "0.8.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.0.tgz", + "integrity": "sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, "node_modules/@firebase/util": { "version": "1.9.3", "license": "Apache-2.0", @@ -1625,79 +2045,95 @@ "tslib": "^2.1.0" } }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.10.5.tgz", + "integrity": "sha512-eSkJsnhBWv5kCTSU1tSUVl9mpFu+5NXXunZc83le8GMjMlsWwQArSc7cJJ4yl+aDFY0NGLi0AjZWMn1axOrkRg==" + }, "node_modules/@google-cloud/firestore": { - "version": "6.8.0", - "license": "Apache-2.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.2.0.tgz", + "integrity": "sha512-rBIiy3o+OxWwUT0EMAAq0OZUduF1l0/GQ9WTnUyiHxixsLR1qU5Y6pC4BOIsYPnup1OESMhFSX0EEx6oriT0pw==", + "dev": true, "optional": true, "dependencies": { "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", - "google-gax": "^3.5.7", + "google-gax": "^4.0.4", "protobufjs": "^7.2.5" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/@google-cloud/paginator": { - "version": "3.0.7", - "license": "Apache-2.0", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.0.tgz", + "integrity": "sha512-87aeg6QQcEPxGCOthnpUjvw4xAZ57G7pL8FS0C4e/81fr3FjkpUpibf1s2v5XGyGhUVGF4Jfg7yEcxqn2iUw1w==", + "dev": true, "optional": true, "dependencies": { "arrify": "^2.0.0", "extend": "^3.0.2" }, "engines": { - "node": ">=10" + "node": ">=14.0.0" } }, "node_modules/@google-cloud/projectify": { - "version": "3.0.0", - "license": "Apache-2.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", + "dev": true, "optional": true, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/@google-cloud/promisify": { - "version": "3.0.1", - "license": "Apache-2.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", + "dev": true, "optional": true, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/@google-cloud/storage": { - "version": "6.12.0", - "license": "Apache-2.0", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.7.0.tgz", + "integrity": "sha512-EMCEY+6JiIkx7Dt8NXVGGjy1vRdSGdHkoqZoqjJw7cEBkT7ZkX0c7puedfn1MamnzW5SX4xoa2jVq5u7OWBmkQ==", + "dev": true, "optional": true, "dependencies": { - "@google-cloud/paginator": "^3.0.7", - "@google-cloud/projectify": "^3.0.0", - "@google-cloud/promisify": "^3.0.0", + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", "abort-controller": "^3.0.0", "async-retry": "^1.3.3", "compressible": "^2.0.12", "duplexify": "^4.0.0", "ent": "^2.2.0", - "extend": "^3.0.2", - "fast-xml-parser": "^4.2.2", - "gaxios": "^5.0.0", - "google-auth-library": "^8.0.1", + "fast-xml-parser": "^4.3.0", + "gaxios": "^6.0.2", + "google-auth-library": "^9.0.0", "mime": "^3.0.0", "mime-types": "^2.0.8", "p-limit": "^3.0.1", - "retry-request": "^5.0.0", - "teeny-request": "^8.0.0", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", "uuid": "^8.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/@google-cloud/storage/node_modules/mime": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, "optional": true, "bin": { "mime": "cli.js" @@ -1708,18 +2144,20 @@ }, "node_modules/@google-cloud/storage/node_modules/uuid": { "version": "8.3.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, "optional": true, "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/@grpc/grpc-js": { - "version": "1.8.21", - "license": "Apache-2.0", - "optional": true, + "version": "1.9.14", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.14.tgz", + "integrity": "sha512-nOpuzZ2G3IuMFN+UPPpKrC6NsLmWsTqSsm66IRfnBt1D4pwTqE27lmbpcPM+l2Ua4gE7PfjRHI6uedAy7hoXUw==", "dependencies": { - "@grpc/proto-loader": "^0.7.0", + "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" }, "engines": { @@ -1729,7 +2167,6 @@ "node_modules/@grpc/proto-loader": { "version": "0.7.10", "license": "Apache-2.0", - "optional": true, "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", @@ -2175,17 +2612,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@jsdoc/salty": { - "version": "0.2.6", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=v12.0.0" - } - }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.1", "license": "MIT", @@ -2228,28 +2654,23 @@ }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "license": "BSD-3-Clause", - "optional": true, "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -2257,28 +2678,23 @@ }, "node_modules/@protobufjs/float": { "version": "1.0.2", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { "version": "1.1.2", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" }, "node_modules/@sinclair/typebox": { "version": "0.27.8", @@ -3077,7 +3493,9 @@ }, "node_modules/@tootallnate/once": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, "optional": true, "engines": { "node": ">= 10" @@ -3138,14 +3556,23 @@ }, "node_modules/@types/body-parser": { "version": "1.19.5", + "dev": true, "license": "MIT", "dependencies": { "@types/connect": "*", "@types/node": "*" } }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "dev": true, + "optional": true + }, "node_modules/@types/connect": { "version": "3.4.38", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -3166,6 +3593,7 @@ }, "node_modules/@types/express": { "version": "4.17.21", + "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", @@ -3176,6 +3604,7 @@ }, "node_modules/@types/express-serve-static-core": { "version": "4.17.41", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -3184,15 +3613,6 @@ "@types/send": "*" } }, - "node_modules/@types/glob": { - "version": "8.1.0", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "license": "MIT", @@ -3202,6 +3622,7 @@ }, "node_modules/@types/http-errors": { "version": "2.0.4", + "dev": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-coverage": { @@ -3245,47 +3666,28 @@ }, "node_modules/@types/jsonwebtoken": { "version": "9.0.5", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/@types/linkify-it": { - "version": "3.0.5", - "license": "MIT", - "optional": true - }, "node_modules/@types/long": { "version": "4.0.2", - "license": "MIT", - "optional": true - }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "1.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true, "optional": true }, "node_modules/@types/mime": { "version": "1.3.5", + "dev": true, "license": "MIT" }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "license": "MIT", - "optional": true - }, "node_modules/@types/node": { - "version": "20.10.0", - "license": "MIT", + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", "dependencies": { "undici-types": "~5.26.4" } @@ -3300,19 +3702,40 @@ }, "node_modules/@types/qs": { "version": "6.9.10", + "dev": true, "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", + "dev": true, "license": "MIT" }, - "node_modules/@types/rimraf": { - "version": "3.0.2", - "license": "MIT", + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dev": true, "optional": true, "dependencies": { - "@types/glob": "*", - "@types/node": "*" + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" } }, "node_modules/@types/semver": { @@ -3323,6 +3746,7 @@ }, "node_modules/@types/send": { "version": "0.17.4", + "dev": true, "license": "MIT", "dependencies": { "@types/mime": "^1", @@ -3331,6 +3755,7 @@ }, "node_modules/@types/serve-static": { "version": "1.15.5", + "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", @@ -3359,6 +3784,13 @@ "@types/superagent": "*" } }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "optional": true + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "license": "MIT" @@ -3583,7 +4015,9 @@ }, "node_modules/abort-controller": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, "optional": true, "dependencies": { "event-target-shim": "^5.0.0" @@ -3616,7 +4050,7 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -3631,14 +4065,14 @@ } }, "node_modules/agent-base": { - "version": "6.0.2", - "license": "MIT", - "optional": true, + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dependencies": { - "debug": "4" + "debug": "^4.3.4" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/ajv": { @@ -3718,7 +4152,7 @@ }, "node_modules/argparse": { "version": "2.0.1", - "devOptional": true, + "dev": true, "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { @@ -3850,7 +4284,9 @@ }, "node_modules/arrify": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, "optional": true, "engines": { "node": ">=8" @@ -3873,7 +4309,9 @@ }, "node_modules/async-retry": { "version": "1.3.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dev": true, "optional": true, "dependencies": { "retry": "0.13.1" @@ -4014,6 +4452,9 @@ }, "node_modules/base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, "funding": [ { "type": "github", @@ -4028,12 +4469,13 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "optional": true }, "node_modules/bignumber.js": { "version": "9.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "dev": true, "optional": true, "engines": { "node": "*" @@ -4047,11 +4489,6 @@ "node": ">=8" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "license": "MIT", - "optional": true - }, "node_modules/body-parser": { "version": "1.20.1", "license": "MIT", @@ -4172,6 +4609,7 @@ }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/buffer-from": { @@ -4229,17 +4667,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/catharsis": { - "version": "0.9.0", - "license": "MIT", - "optional": true, - "dependencies": { - "lodash": "^4.17.15" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/chalk": { "version": "4.1.2", "license": "MIT", @@ -4376,7 +4803,9 @@ }, "node_modules/compressible": { "version": "2.0.18", - "license": "MIT", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, "optional": true, "dependencies": { "mime-db": ">= 1.43.0 < 2" @@ -4506,7 +4935,7 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/deepmerge": { @@ -4631,7 +5060,9 @@ }, "node_modules/duplexify": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dev": true, "optional": true, "dependencies": { "end-of-stream": "^1.4.1", @@ -4642,6 +5073,7 @@ }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", + "dev": true, "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -4678,7 +5110,9 @@ }, "node_modules/end-of-stream": { "version": "1.4.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, "optional": true, "dependencies": { "once": "^1.4.0" @@ -4699,17 +5133,11 @@ }, "node_modules/ent": { "version": "2.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "dev": true, "optional": true }, - "node_modules/entities": { - "version": "2.1.0", - "license": "BSD-2-Clause", - "optional": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/envalid": { "version": "7.3.1", "license": "MIT", @@ -4859,81 +5287,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escodegen": { - "version": "1.14.3", - "license": "BSD-2-Clause", - "optional": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "4.3.0", - "license": "BSD-2-Clause", - "optional": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "license": "MIT", - "optional": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "license": "MIT", - "optional": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "optional": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "license": "MIT", - "optional": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", @@ -5238,7 +5591,7 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -5249,7 +5602,7 @@ }, "node_modules/espree": { "version": "9.6.1", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", @@ -5298,7 +5651,7 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -5306,7 +5659,7 @@ }, "node_modules/esutils": { "version": "2.0.3", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -5321,7 +5674,9 @@ }, "node_modules/event-target-shim": { "version": "5.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, "optional": true, "engines": { "node": ">=6" @@ -5432,12 +5787,14 @@ }, "node_modules/extend": { "version": "3.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, "optional": true }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/fast-fifo": { @@ -5478,20 +5835,18 @@ }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "license": "MIT" }, - "node_modules/fast-text-encoding": { - "version": "1.0.6", - "license": "Apache-2.0", - "optional": true - }, "node_modules/fast-xml-parser": { - "version": "4.3.2", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.3.tgz", + "integrity": "sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==", + "dev": true, "funding": [ { "type": "github", @@ -5502,7 +5857,6 @@ "url": "https://paypal.me/naturalintelligence" } ], - "license": "MIT", "optional": true, "dependencies": { "strnum": "^1.0.5" @@ -5641,14 +5995,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.7.2.tgz", + "integrity": "sha512-zED3kAJyf+Xx5tXpC3vjmlWTm/SIVoJJ6MOLuXYJkqKAUJLG7Q1Jxy6l1DxCzGgBqZHxc0Jh6q+qG++9kimHsw==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-compat": "0.2.6", + "@firebase/app": "0.9.26", + "@firebase/app-check": "0.8.1", + "@firebase/app-check-compat": "0.3.8", + "@firebase/app-compat": "0.2.26", + "@firebase/app-types": "0.9.0", + "@firebase/auth": "1.5.1", + "@firebase/auth-compat": "0.5.1", + "@firebase/database": "1.0.2", + "@firebase/database-compat": "1.0.2", + "@firebase/firestore": "4.4.1", + "@firebase/firestore-compat": "0.3.24", + "@firebase/functions": "0.11.0", + "@firebase/functions-compat": "0.3.6", + "@firebase/installations": "0.6.4", + "@firebase/installations-compat": "0.2.4", + "@firebase/messaging": "0.12.5", + "@firebase/messaging-compat": "0.2.5", + "@firebase/performance": "0.6.4", + "@firebase/performance-compat": "0.2.4", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-compat": "0.2.4", + "@firebase/storage": "0.12.0", + "@firebase/storage-compat": "0.3.3", + "@firebase/util": "1.9.3" + } + }, "node_modules/firebase-admin": { - "version": "11.11.1", - "license": "Apache-2.0", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.0.0.tgz", + "integrity": "sha512-wBrrSSsKV++/+O8E7O/C7/wL0nbG/x4Xv4yatz/+sohaZ+LsnWtYUcrd3gZutO86hLpDex7xgyrkKbgulmtVyQ==", + "dev": true, "dependencies": { "@fastify/busboy": "^1.2.1", - "@firebase/database-compat": "^0.3.4", - "@firebase/database-types": "^0.10.4", - "@types/node": ">=12.12.47", + "@firebase/database-compat": "^1.0.2", + "@firebase/database-types": "^1.0.0", + "@types/node": "^20.10.3", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", "node-forge": "^1.3.1", @@ -5658,8 +6047,8 @@ "node": ">=14" }, "optionalDependencies": { - "@google-cloud/firestore": "^6.8.0", - "@google-cloud/storage": "^6.9.5" + "@google-cloud/firestore": "^7.1.0", + "@google-cloud/storage": "^7.7.0" } }, "node_modules/flat-cache": { @@ -5775,7 +6164,9 @@ }, "node_modules/functional-red-black-tree": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, "optional": true }, "node_modules/functions-have-names": { @@ -5787,29 +6178,33 @@ } }, "node_modules/gaxios": { - "version": "5.1.3", - "license": "Apache-2.0", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz", + "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", + "dev": true, "optional": true, "dependencies": { "extend": "^3.0.2", - "https-proxy-agent": "^5.0.0", + "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/gcp-metadata": { - "version": "5.3.0", - "license": "Apache-2.0", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dev": true, "optional": true, "dependencies": { - "gaxios": "^5.0.0", + "gaxios": "^6.0.0", "json-bigint": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/gensync": { @@ -5962,88 +6357,45 @@ } }, "node_modules/google-auth-library": { - "version": "8.9.0", - "license": "Apache-2.0", + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.4.2.tgz", + "integrity": "sha512-rTLO4gjhqqo3WvYKL5IdtlCvRqeQ4hxUx/p4lObobY2xotFW3bCQC+Qf1N51CYOfiqfMecdMwW9RIo7dFWYjqw==", + "dev": true, "optional": true, "dependencies": { - "arrify": "^2.0.0", "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", - "fast-text-encoding": "^1.0.0", - "gaxios": "^5.0.0", - "gcp-metadata": "^5.3.0", - "gtoken": "^6.1.0", - "jws": "^4.0.0", - "lru-cache": "^6.0.0" + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/google-gax": { - "version": "3.6.1", - "license": "Apache-2.0", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.2.0.tgz", + "integrity": "sha512-ysBZvCcstvG1vYr5/Nd7IOPH3p4g2sJNmxrxOkFI4pJjWJexH98fpJym1N4LsI2pIVCopVvEcXrDmg5QIaFmfA==", + "dev": true, "optional": true, "dependencies": { - "@grpc/grpc-js": "~1.8.0", + "@grpc/grpc-js": "~1.9.6", "@grpc/proto-loader": "^0.7.0", "@types/long": "^4.0.0", - "@types/rimraf": "^3.0.2", "abort-controller": "^3.0.0", "duplexify": "^4.0.0", - "fast-text-encoding": "^1.0.3", - "google-auth-library": "^8.0.2", - "is-stream-ended": "^0.1.4", + "google-auth-library": "^9.0.0", "node-fetch": "^2.6.1", "object-hash": "^3.0.0", - "proto3-json-serializer": "^1.0.0", - "protobufjs": "7.2.4", - "protobufjs-cli": "1.1.1", - "retry-request": "^5.0.0" - }, - "bin": { - "compileProtos": "build/tools/compileProtos.js", - "minifyProtoJson": "build/tools/minify.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/google-gax/node_modules/protobufjs": { - "version": "7.2.4", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/google-p12-pem": { - "version": "4.0.1", - "license": "MIT", - "optional": true, - "dependencies": { - "node-forge": "^1.3.1" - }, - "bin": { - "gp12-pem": "build/src/bin/gp12-pem.js" + "proto3-json-serializer": "^2.0.0", + "protobufjs": "7.2.6", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14" } }, "node_modules/gopd": { @@ -6066,16 +6418,17 @@ "license": "MIT" }, "node_modules/gtoken": { - "version": "6.1.2", - "license": "MIT", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", + "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", + "dev": true, "optional": true, "dependencies": { - "gaxios": "^5.0.1", - "google-p12-pem": "^4.0.0", + "gaxios": "^6.0.0", "jws": "^4.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/has-bigints": { @@ -6178,7 +6531,9 @@ }, "node_modules/http-proxy-agent": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, "optional": true, "dependencies": { "@tootallnate/once": "2", @@ -6189,16 +6544,29 @@ "node": ">= 6" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "license": "MIT", + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, "optional": true, "dependencies": { - "agent-base": "6", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 6.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" } }, "node_modules/human-signals": { @@ -6233,6 +6601,11 @@ "node": ">=0.10.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ignore": { "version": "5.3.0", "dev": true, @@ -6575,11 +6948,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-stream-ended": { - "version": "0.1.4", - "license": "MIT", - "optional": true - }, "node_modules/is-string": { "version": "1.0.7", "dev": true, @@ -7238,6 +7606,7 @@ }, "node_modules/jose": { "version": "4.15.4", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -7259,50 +7628,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/js2xmlparser": { - "version": "4.0.2", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "xmlcreate": "^2.0.4" - } - }, - "node_modules/jsdoc": { - "version": "4.0.2", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/jsdoc/node_modules/escape-string-regexp": { - "version": "2.0.0", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/jsesc": { "version": "2.5.2", "license": "MIT", @@ -7315,7 +7640,9 @@ }, "node_modules/json-bigint": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, "optional": true, "dependencies": { "bignumber.js": "^9.0.0" @@ -7355,6 +7682,7 @@ }, "node_modules/jsonwebtoken": { "version": "9.0.2", + "dev": true, "license": "MIT", "dependencies": { "jws": "^3.2.2", @@ -7375,6 +7703,7 @@ }, "node_modules/jsonwebtoken/node_modules/jwa": { "version": "1.4.1", + "dev": true, "license": "MIT", "dependencies": { "buffer-equal-constant-time": "1.0.1", @@ -7384,6 +7713,7 @@ }, "node_modules/jsonwebtoken/node_modules/jws": { "version": "3.2.2", + "dev": true, "license": "MIT", "dependencies": { "jwa": "^1.4.1", @@ -7406,7 +7736,9 @@ }, "node_modules/jwa": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dev": true, "optional": true, "dependencies": { "buffer-equal-constant-time": "1.0.1", @@ -7416,6 +7748,7 @@ }, "node_modules/jwks-rsa": { "version": "3.1.0", + "dev": true, "license": "MIT", "dependencies": { "@types/express": "^4.17.17", @@ -7431,7 +7764,9 @@ }, "node_modules/jws": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dev": true, "optional": true, "dependencies": { "jwa": "^2.0.0", @@ -7453,14 +7788,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/klaw": { - "version": "3.0.0", - "license": "MIT", - "optional": true, - "dependencies": { - "graceful-fs": "^4.1.9" - } - }, "node_modules/kleur": { "version": "3.0.3", "license": "MIT", @@ -7488,20 +7815,13 @@ } }, "node_modules/limiter": { - "version": "1.1.5" + "version": "1.1.5", + "dev": true }, "node_modules/lines-and-columns": { "version": "1.2.4", "license": "MIT" }, - "node_modules/linkify-it": { - "version": "3.0.3", - "license": "MIT", - "optional": true, - "dependencies": { - "uc.micro": "^1.0.1" - } - }, "node_modules/locate-path": { "version": "6.0.0", "dev": true, @@ -7522,35 +7842,41 @@ }, "node_modules/lodash.camelcase": { "version": "4.3.0", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", + "dev": true, "license": "MIT" }, "node_modules/lodash.includes": { "version": "4.3.0", + "dev": true, "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", + "dev": true, "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", + "dev": true, "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", + "dev": true, "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", + "dev": true, "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", + "dev": true, "license": "MIT" }, "node_modules/lodash.memoize": { @@ -7564,12 +7890,12 @@ }, "node_modules/lodash.once": { "version": "4.1.1", + "dev": true, "license": "MIT" }, "node_modules/long": { "version": "5.2.3", - "license": "Apache-2.0", - "optional": true + "license": "Apache-2.0" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -7594,6 +7920,7 @@ }, "node_modules/lru-memoizer": { "version": "2.2.0", + "dev": true, "license": "MIT", "dependencies": { "lodash.clonedeep": "^4.5.0", @@ -7602,6 +7929,7 @@ }, "node_modules/lru-memoizer/node_modules/lru-cache": { "version": "4.0.2", + "dev": true, "license": "ISC", "dependencies": { "pseudomap": "^1.0.1", @@ -7610,6 +7938,7 @@ }, "node_modules/lru-memoizer/node_modules/yallist": { "version": "2.1.2", + "dev": true, "license": "ISC" }, "node_modules/make-dir": { @@ -7636,46 +7965,6 @@ "tmpl": "1.0.5" } }, - "node_modules/markdown-it": { - "version": "12.3.2", - "license": "MIT", - "optional": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it-anchor": { - "version": "8.6.7", - "license": "Unlicense", - "optional": true, - "peerDependencies": { - "@types/markdown-it": "*", - "markdown-it": "*" - } - }, - "node_modules/marked": { - "version": "4.3.0", - "license": "MIT", - "optional": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "license": "MIT", - "optional": true - }, "node_modules/media-typer": { "version": "0.3.0", "license": "MIT", @@ -7769,23 +8058,12 @@ }, "node_modules/minimist": { "version": "1.2.8", - "devOptional": true, + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "license": "MIT", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/module-alias": { "version": "2.2.3", "license": "MIT" @@ -7870,16 +8148,6 @@ "node": ">=14.20.1" } }, - "node_modules/mongodb-memory-server-core/node_modules/agent-base": { - "version": "7.1.0", - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/mongodb-memory-server-core/node_modules/camelcase": { "version": "6.3.0", "license": "MIT", @@ -7890,17 +8158,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mongodb-memory-server-core/node_modules/https-proxy-agent": { - "version": "7.0.2", - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/mongodb-memory-server-core/node_modules/tslib": { "version": "2.6.2", "license": "0BSD" @@ -8016,7 +8273,9 @@ }, "node_modules/node-fetch": { "version": "2.7.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, "optional": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -8035,17 +8294,23 @@ }, "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, "optional": true }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, "optional": true }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, "optional": true, "dependencies": { "tr46": "~0.0.3", @@ -8054,7 +8319,9 @@ }, "node_modules/node-forge": { "version": "1.3.1", - "license": "(BSD-3-Clause OR GPL-2.0)", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, "engines": { "node": ">= 6.13.0" } @@ -8166,7 +8433,9 @@ }, "node_modules/object-hash": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, "optional": true, "engines": { "node": ">= 6" @@ -8575,21 +8844,23 @@ } }, "node_modules/proto3-json-serializer": { - "version": "1.1.1", - "license": "Apache-2.0", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.1.tgz", + "integrity": "sha512-8awBvjO+FwkMd6gNoGFZyqkHZXCFd54CIYTb6De7dPaufGJ2XNW+QUNqbMr8MaAocMdb+KpsD4rxEOaTBDCffA==", + "dev": true, "optional": true, "dependencies": { - "protobufjs": "^7.0.0" + "protobufjs": "^7.2.5" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/protobufjs": { - "version": "7.2.5", + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", "hasInstallScript": true, - "license": "BSD-3-Clause", - "optional": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -8608,70 +8879,6 @@ "node": ">=12.0.0" } }, - "node_modules/protobufjs-cli": { - "version": "1.1.1", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "chalk": "^4.0.0", - "escodegen": "^1.13.0", - "espree": "^9.0.0", - "estraverse": "^5.1.0", - "glob": "^8.0.0", - "jsdoc": "^4.0.0", - "minimist": "^1.2.0", - "semver": "^7.1.2", - "tmp": "^0.2.1", - "uglify-js": "^3.7.7" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "protobufjs": "^7.0.0" - } - }, - "node_modules/protobufjs-cli/node_modules/brace-expansion": { - "version": "2.0.1", - "license": "MIT", - "optional": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/protobufjs-cli/node_modules/glob": { - "version": "8.1.0", - "license": "ISC", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/protobufjs-cli/node_modules/minimatch": { - "version": "5.1.6", - "license": "ISC", - "optional": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "license": "MIT", @@ -8685,6 +8892,7 @@ }, "node_modules/pseudomap": { "version": "1.0.2", + "dev": true, "license": "ISC" }, "node_modules/pstree.remy": { @@ -8776,7 +8984,9 @@ }, "node_modules/readable-stream": { "version": "3.6.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, "optional": true, "dependencies": { "inherits": "^2.0.3", @@ -8840,14 +9050,6 @@ "node": ">=0.10.0" } }, - "node_modules/requizzle": { - "version": "0.2.4", - "license": "MIT", - "optional": true, - "dependencies": { - "lodash": "^4.17.21" - } - }, "node_modules/resolve": { "version": "1.22.8", "license": "MIT", @@ -8907,22 +9109,27 @@ }, "node_modules/retry": { "version": "0.13.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, "optional": true, "engines": { "node": ">= 4" } }, "node_modules/retry-request": { - "version": "5.0.2", - "license": "MIT", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "dev": true, "optional": true, "dependencies": { - "debug": "^4.1.1", - "extend": "^3.0.2" + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/reusify": { @@ -8936,7 +9143,7 @@ }, "node_modules/rimraf": { "version": "3.0.2", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -9247,15 +9454,19 @@ }, "node_modules/stream-events": { "version": "1.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dev": true, "optional": true, "dependencies": { "stubs": "^3.0.0" } }, "node_modules/stream-shift": { - "version": "1.0.1", - "license": "MIT", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "dev": true, "optional": true }, "node_modules/streamx": { @@ -9268,7 +9479,9 @@ }, "node_modules/string_decoder": { "version": "1.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "optional": true, "dependencies": { "safe-buffer": "~5.2.0" @@ -9401,7 +9614,9 @@ }, "node_modules/stubs": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "dev": true, "optional": true }, "node_modules/superagent": { @@ -9483,18 +9698,47 @@ } }, "node_modules/teeny-request": { - "version": "8.0.3", - "license": "Apache-2.0", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "dev": true, "optional": true, "dependencies": { "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.9", "stream-events": "^1.0.5", "uuid": "^9.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, "node_modules/test-exclude": { @@ -9511,6 +9755,7 @@ }, "node_modules/text-decoding": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/text-table": { @@ -9518,17 +9763,6 @@ "dev": true, "license": "MIT" }, - "node_modules/tmp": { - "version": "0.2.1", - "license": "MIT", - "optional": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, "node_modules/tmpl": { "version": "1.0.5", "license": "BSD-3-Clause" @@ -9811,22 +10045,6 @@ "node": ">=14.17" } }, - "node_modules/uc.micro": { - "version": "1.0.6", - "license": "MIT", - "optional": true - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "dev": true, @@ -9846,15 +10064,29 @@ "dev": true, "license": "MIT" }, - "node_modules/underscore": { - "version": "1.13.6", - "license": "MIT", - "optional": true + "node_modules/undici": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.26.5.tgz", + "integrity": "sha512-cSb4bPFd5qgR7qr2jYAi0hlX9n5YKK2ONKkLFkxl+v/9BvC0sOpZjBHDBSXc5lWAf5ty9oZdRXytBIHzgUcerw==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } }, "node_modules/undici-types": { "version": "5.26.5", "license": "MIT" }, + "node_modules/undici/node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "engines": { + "node": ">=14" + } + }, "node_modules/unpipe": { "version": "1.0.0", "license": "MIT", @@ -9901,7 +10133,9 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, "optional": true }, "node_modules/utils-merge": { @@ -9913,11 +10147,13 @@ }, "node_modules/uuid": { "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -10090,14 +10326,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "license": "MIT", @@ -10128,11 +10356,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/xmlcreate": { - "version": "2.0.4", - "license": "Apache-2.0", - "optional": true - }, "node_modules/y18n": { "version": "5.0.8", "license": "ISC", diff --git a/backend/package.json b/backend/package.json index c3b9ade..fbd45f6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,7 +17,7 @@ "envalid": "^7.3.1", "express": "^4.18.2", "express-validator": "^7.0.1", - "firebase-admin": "^11.11.1", + "firebase": "^10.7.2", "http-errors": "^2.0.0", "jest": "^29.7.0", "module-alias": "^2.2.3", @@ -44,6 +44,7 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-no-relative-import-paths": "^1.5.2", "eslint-plugin-react": "^7.33.0", + "firebase-admin": "^12.0.0", "husky": "^8.0.3", "nodemon": "^3.0.1", "prettier": "^3.1.1", diff --git a/backend/src/app.ts b/backend/src/app.ts index aba9a1a..5d962d5 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -6,6 +6,8 @@ import "dotenv/config"; import cors from "cors"; import express, { NextFunction, Request, Response } from "express"; import { isHttpError } from "http-errors"; +import { userRouter } from "src/routes/users"; +import env from "src/util/validateEnv"; const app = express(); @@ -19,11 +21,12 @@ app.use(express.json()); // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin app.use( cors({ - origin: process.env.FRONTEND_ORIGIN, + origin: env.FRONTEND_ORIGIN, }), ); // Put routes here (e.g. app.use("/api/example", exampleRoutes); ) +app.use(userRouter); /** * Error handler; all errors thrown by server are handled here. diff --git a/backend/src/errors/auth.ts b/backend/src/errors/auth.ts new file mode 100644 index 0000000..6e84e71 --- /dev/null +++ b/backend/src/errors/auth.ts @@ -0,0 +1,12 @@ +import { CustomError } from "src/errors/errors"; + +const DECODE_ERROR = "Error in decoding the auth token. Make sure the auth token is valid"; +const TOKEN_NOT_IN_HEADER = + "Token was not found in the header. Be sure to use Bearer syntax"; +const INVALID_AUTH_TOKEN = "Token was invalid. Be sure to refresh token if needed."; + +export class AuthError extends CustomError { + static DECODE_ERROR = new AuthError(0, 401, DECODE_ERROR); + static TOKEN_NOT_IN_HEADER = new AuthError(1, 401, TOKEN_NOT_IN_HEADER); + static INVALID_AUTH_TOKEN = new AuthError(2, 401, INVALID_AUTH_TOKEN); +} diff --git a/backend/src/errors/errors.ts b/backend/src/errors/errors.ts new file mode 100644 index 0000000..80fb4a6 --- /dev/null +++ b/backend/src/errors/errors.ts @@ -0,0 +1,24 @@ +export class CustomError extends Error { + public code: number; + public status: number; + public message: string; + public context: Array; + + constructor(code: number, status: number, message: string) { + super(message); + this.code = code; + this.status = status; + this.message = message; + this.context = []; + } + + public displayMessage(clientFacing: boolean) { + if (clientFacing) { + return `${this.message}`; + } + + return `Error: Type ${this.constructor.name}, Code ${this.code}, Context: ${ + this.context.length ? "\n" + this.context.join("\n\n") : null + }`; + } +} diff --git a/backend/src/errors/internal.ts b/backend/src/errors/internal.ts new file mode 100644 index 0000000..af847a9 --- /dev/null +++ b/backend/src/errors/internal.ts @@ -0,0 +1,7 @@ +import { CustomError } from "src/errors/errors"; + +const NO_SERVICE_ACCOUNT_KEY = "Could not find service account key env variable"; + +export class InternalError extends CustomError { + static NO_SERVICE_ACCOUNT_KEY = new InternalError(0, 500, NO_SERVICE_ACCOUNT_KEY); +} diff --git a/backend/src/errors/service.ts b/backend/src/errors/service.ts new file mode 100644 index 0000000..f78049b --- /dev/null +++ b/backend/src/errors/service.ts @@ -0,0 +1,11 @@ +import { CustomError } from "src/errors/errors"; + +const INVALID_MONGO_ID = "User ID is not a valid MONGO ID"; + +const USER_NOT_FOUND = "User not found in mongo database"; + +export class ServiceError extends CustomError { + static INVALID_MONGO_ID = new ServiceError(0, 401, INVALID_MONGO_ID); + + static USER_NOT_FOUND = new ServiceError(1, 401, USER_NOT_FOUND); +} diff --git a/backend/src/middleware/auth.ts b/backend/src/middleware/auth.ts new file mode 100644 index 0000000..778e869 --- /dev/null +++ b/backend/src/middleware/auth.ts @@ -0,0 +1,32 @@ +import { Request, Response, NextFunction } from "express"; +import { decodeAuthToken } from "src/services/auth"; +import { AuthError } from "src/errors/auth"; + +const verifyAuthToken = async (req: Request, res: Response, next: NextFunction) => { + const authHeader = req.headers.authorization; + const token = + authHeader && authHeader.split(" ")[0] === "Bearer" ? authHeader.split(" ")[1] : null; + if (!token) { + return res + .status(AuthError.TOKEN_NOT_IN_HEADER.status) + .send(AuthError.TOKEN_NOT_IN_HEADER.displayMessage(true)); + } + + let userInfo; + try { + userInfo = await decodeAuthToken(token); + } catch (e) { + return res + .status(AuthError.INVALID_AUTH_TOKEN.status) + .send(AuthError.INVALID_AUTH_TOKEN.displayMessage(true)); + } + + if (userInfo) { + req.body.uid = userInfo.uid; + return next(); + } + + return res.status(AuthError.INVALID_AUTH_TOKEN.status).send(AuthError.INVALID_AUTH_TOKEN.message); +}; + +export { verifyAuthToken }; diff --git a/backend/src/models/users.ts b/backend/src/models/users.ts new file mode 100644 index 0000000..35fec50 --- /dev/null +++ b/backend/src/models/users.ts @@ -0,0 +1,31 @@ +import mongoose from "mongoose"; + +interface UserInterface { + role: string; + uid: string; +} + +interface UserDoc extends mongoose.Document { + role: string; + uid: string; +} + +interface UserModelInterface extends mongoose.Model { + // eslint-disable-next-line no-unused-vars + build(attr: UserInterface): UserDoc; +} + +const userSchema = new mongoose.Schema({ + role: { + type: String, + required: true, + }, + uid: { + type: String, + required: true, + }, +}); + +const User = mongoose.model("User", userSchema); + +export { User, userSchema }; diff --git a/backend/src/routes/users.ts b/backend/src/routes/users.ts new file mode 100644 index 0000000..48e0e8e --- /dev/null +++ b/backend/src/routes/users.ts @@ -0,0 +1,38 @@ +import express, { NextFunction, Request, Response } from "express"; +import { ServiceError } from "src/errors/service"; +import { User } from "src/models/users"; +import { verifyAuthToken } from "src/middleware/auth"; + +const router = express.Router(); + +router.get( + "/api/whoami", + [verifyAuthToken], + async (req: Request, res: Response, next: NextFunction) => { + try { + const uid = req.body.uid; + const user = await User.findOne({ uid: uid }); + if (!user) { + throw ServiceError.USER_NOT_FOUND; + } + const { _id: mongoId, role } = user; + res.status(200).send({ + message: "Current user information", + user: { + mongoId, + uid, + role, + }, + }); + return; + } catch (e) { + next(); + console.log(e); + return res.status(400).json({ + error: e, + }); + } + }, +); + +export { router as userRouter }; diff --git a/backend/src/services/auth.ts b/backend/src/services/auth.ts new file mode 100644 index 0000000..90d645a --- /dev/null +++ b/backend/src/services/auth.ts @@ -0,0 +1,13 @@ +import { firebaseAuth } from "src/services/firebase"; +import { AuthError } from "src/errors/auth"; + +async function decodeAuthToken(token: string) { + try { + const userInfo = await firebaseAuth.verifyIdToken(token); + return userInfo; + } catch (e) { + throw AuthError.DECODE_ERROR; + } +} + +export { decodeAuthToken }; diff --git a/backend/src/services/firebase.ts b/backend/src/services/firebase.ts new file mode 100644 index 0000000..10363ab --- /dev/null +++ b/backend/src/services/firebase.ts @@ -0,0 +1,27 @@ +/** + * This file contains the configuration for firebase + * It exports a firebase auth object which will allow users + * to access any firebase services. For this project we will use + * firebase to for authentication. + */ + +import * as firebase from "firebase-admin/app"; +import { getAuth } from "firebase-admin/auth"; +import { InternalError } from "src/errors/internal"; +import env from "src/util/validateEnv"; + +let serviceAccountKey: firebase.ServiceAccount; + +if (!env.SERVICE_ACCOUNT_KEY) { + throw InternalError.NO_SERVICE_ACCOUNT_KEY; +} else { + serviceAccountKey = env.SERVICE_ACCOUNT_KEY; +} + +firebase.initializeApp({ + credential: firebase.cert(serviceAccountKey), +}); + +const firebaseAuth = getAuth(); + +export { firebaseAuth }; diff --git a/backend/src/util/validateEnv.ts b/backend/src/util/validateEnv.ts index 6d04f81..c4b89f5 100644 --- a/backend/src/util/validateEnv.ts +++ b/backend/src/util/validateEnv.ts @@ -13,4 +13,5 @@ export default cleanEnv(process.env, { EMAIL_USER: email(), // Email address to use for sending emails EMAIL_APP_PASSWORD: str(), // App password to use for sending emails BACKEND_FIREBASE_SETTINGS: json(), // Firebase settings for backend, stored as a JSON string + SERVICE_ACCOUNT_KEY: json(), // Private service account key for backend, stored as a JSON string }); diff --git a/frontend/.env.example b/frontend/.env.example index 12eba19..158e9c7 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -1,4 +1,4 @@ -# Example environment variables for frontend. Any ""s should be -# replaced with the real values. -NEXT_PUBLIC_BACKEND_URL="http://localhost:3001" -NEXT_PUBLIC_FIREBASE_SETTINGS='' +# Example environment variables for frontend. Any ""s should be +# replaced with the real values. +NEXT_PUBLIC_BACKEND_URL="http://localhost:3001" +NEXT_PUBLIC_FIREBASE_SETTINGS='' \ No newline at end of file diff --git a/frontend/public/Images/LoginImage.png b/frontend/public/Images/LoginImage.png new file mode 100644 index 0000000..c5b12b8 Binary files /dev/null and b/frontend/public/Images/LoginImage.png differ diff --git a/frontend/public/Images/login_bg.png b/frontend/public/Images/login_bg.png new file mode 100644 index 0000000..6ae4c69 Binary files /dev/null and b/frontend/public/Images/login_bg.png differ diff --git a/frontend/src/app/dummyPage/layout.tsx b/frontend/src/app/dummyPage/layout.tsx new file mode 100644 index 0000000..1914d33 --- /dev/null +++ b/frontend/src/app/dummyPage/layout.tsx @@ -0,0 +1,12 @@ +export const metadata = { + title: "Next.js", + description: "Generated by Next.js", +}; + +export default function DummyLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/frontend/src/app/dummyPage/page.tsx b/frontend/src/app/dummyPage/page.tsx new file mode 100644 index 0000000..879ddf8 --- /dev/null +++ b/frontend/src/app/dummyPage/page.tsx @@ -0,0 +1,5 @@ +const Dummy = () => { + return

Login Successful!

; +}; + +export default Dummy; diff --git a/frontend/src/app/login/layout.tsx b/frontend/src/app/login/layout.tsx new file mode 100644 index 0000000..05464f6 --- /dev/null +++ b/frontend/src/app/login/layout.tsx @@ -0,0 +1,12 @@ +export const metadata = { + title: "Next.js", + description: "Generated by Next.js", +}; + +export default function LoginLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/frontend/src/app/login/login.css b/frontend/src/app/login/login.css new file mode 100644 index 0000000..b085b59 --- /dev/null +++ b/frontend/src/app/login/login.css @@ -0,0 +1,101 @@ +/* Login.css */ + +body, +html { + height: 100%; + margin: 0; +} + +.login-container { + position: relative; + height: 100%; +} + +.login-box { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: flex; + flex-direction: column; + align-items: center; + padding: 15px; + border-radius: 12px; + background: #fff; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + width: 70%; + max-width: 500px; +} + +.logo-container { + position: absolute; + top: -20%; + display: inline-flex; + padding: 15px; + align-items: flex-start; + gap: 10px; + border-radius: 12px; + background: #fff; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); +} + +.logo-image { + background: lightgray no-repeat; + width: 190px; + height: 90px; +} + +.welcome-text { + color: #000; + text-align: center; + font-family: Lora; + font-size: 40px; + font-style: normal; + font-weight: 700; + line-height: normal; + margin-top: 50px; /* Gap between Welcome and Name field */ +} + +.login-form { + width: 100%; + margin-top: 22px; /* Gap between Name and Password */ +} + +.input-group { + margin-bottom: 22px; /* Gap between Password and Forgot Password */ +} + +label { + display: block; + margin-bottom: 5px; + color: gray; +} + +input { + width: 100%; + padding: 8px; + border: 1px solid #ccc; + border-radius: 4px; +} + +.forgot-password { + color: blue; + text-align: center; + margin-bottom: 48px; /* Gap between Forgot password and Log In */ +} + +.login-button { + width: 100%; + height: 50px; + flex-shrink: 0; + border-radius: 4px; + background: var(--Secondary-1, #102d5f); + color: #fff; + cursor: pointer; + text-align: center; + font-family: Lora; + font-size: 24px; + font-style: normal; + font-weight: 700; + line-height: normal; +} diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx new file mode 100644 index 0000000..b389db5 --- /dev/null +++ b/frontend/src/app/login/page.tsx @@ -0,0 +1,126 @@ +// Login.tsx +"use client"; + +import React, { useState } from "react"; +import InputField from "@/components/InputField"; +import Image from "next/image"; +import "@/app/login/login.css"; +import { signInWithEmailAndPassword } from "firebase/auth"; +import { initFirebase } from "@/firebase/firebase"; +import { useRouter } from "next/navigation"; + +const Login = () => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const { auth } = initFirebase(); + + const router = useRouter(); + + const sendTokenToBackend = async (token: string) => { + try { + const response = await fetch(`http://localhost:3001/api/whoami`, { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + }); + + if (response.ok) { + const userInfo = await response.json(); + console.log(userInfo); + router.push("/dummyPage"); + } else { + console.error("Failed to get user info from JWT Token"); + } + } catch (error) { + console.error("error sending JWT token to backend", error); + } + }; + + const login = async (email: string, password: string) => { + try { + const userCredential = await signInWithEmailAndPassword(auth, email, password); + const token = await userCredential.user?.getIdToken(); + if (!token) { + console.error("JWT token not retrieved."); + } else { + await sendTokenToBackend(token); + } + } catch (error) { + console.error("login failed: ", error); + } + }; + + const handleLogin = (e: React.FormEvent) => { + e.preventDefault(); + login(email, password); + }; + + return ( + +
+ +
+
+
+
+ Logo +
+
+
Welcome!
+
+
+ setEmail(e.target.value)} + /> +
+
+ setPassword(e.target.value)} + type="password" + /> +
+
Forgot Password?
+ +
+
+
+ + ); +}; + +export default Login; diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 94bf4de..6767c5d 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -1,94 +1,14 @@ -import Image from "next/image"; +"use client"; -import styles from "@/app/page.module.css"; +import { useEffect } from "react"; +import { useRouter } from "next/navigation"; -export default function Home() { - return ( -
-
-

- Get started by editing  - src/app/page.tsx -

- -
+const Home = () => { + const router = useRouter(); + useEffect(() => { + router.push("/login"); + }); + return null; +}; -
- Next.js Logo -
- - -
- ); -} +export default Home; diff --git a/frontend/src/components/InputField.module.css b/frontend/src/components/InputField.module.css new file mode 100644 index 0000000..fde5652 --- /dev/null +++ b/frontend/src/components/InputField.module.css @@ -0,0 +1,32 @@ +.inputField { + margin-bottom: 20px; +} + +.label { + font-size: 16px; + font-family: "Open Sans", sans-serif; + color: #818181; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.input { + border: 1px solid #d8d8d8; + border-radius: 4px; + padding: 8px; + width: 100%; + font-size: 16px; + font-family: "Open Sans", sans-serif; + color: #232220; + background: #fff; +} + +.input::placeholder { + font-style: italic; + font-family: "Open Sans", sans-serif; + font-size: 16px; + color: #484848; + font-weight: 300; + line-height: normal; +} diff --git a/frontend/src/components/InputField.tsx b/frontend/src/components/InputField.tsx new file mode 100644 index 0000000..70d9a6a --- /dev/null +++ b/frontend/src/components/InputField.tsx @@ -0,0 +1,38 @@ +import React, { ChangeEvent } from "react"; +import styles from "src/components/InputField.module.css"; // Create a CSS module for styling + +interface InputFieldProps { + label: string; + type?: string; + id: string; + placeholder: string; + value: string; + onChange: (e: ChangeEvent) => void; +} + +const InputField: React.FC = ({ + label, + id, + placeholder, + value, + onChange, + type = "text", +}) => { + return ( +
+ + +
+ ); +}; + +export default InputField; diff --git a/frontend/src/firebase/firebase.ts b/frontend/src/firebase/firebase.ts new file mode 100644 index 0000000..137e33a --- /dev/null +++ b/frontend/src/firebase/firebase.ts @@ -0,0 +1,16 @@ +import { initializeApp } from "firebase/app"; +import { getAuth } from "firebase/auth"; +import env from "@/util/validateEnv"; + +export const initFirebase = () => { + if (!env.NEXT_PUBLIC_FIREBASE_SETTINGS) { + throw new Error("Cannot get firebase settings"); + } + + const firebaseConfig = env.NEXT_PUBLIC_FIREBASE_SETTINGS; + + const app = initializeApp(firebaseConfig); + const auth = getAuth(app); + + return { app, auth }; +}; diff --git a/frontend/src/util/validateEnv.ts b/frontend/src/util/validateEnv.ts index 3d16d63..46df19c 100644 --- a/frontend/src/util/validateEnv.ts +++ b/frontend/src/util/validateEnv.ts @@ -7,10 +7,16 @@ import { cleanEnv } from "envalid"; import { json, str } from "envalid/dist/validators"; /** - * Note that in NextJS, environment variables' names must start with - * "NEXT_PUBLIC" in order to be exposed to the frontend + * NextJS only allows the frontend to access environment variables if they start with + * "NEXT_PUBLIC", so we have to manually acccess attributes of process.env here. */ -export default cleanEnv(process.env, { - NEXT_PUBLIC_BACKEND_URL: str(), // URL of our backend - NEXT_PUBLIC_FIREBASE_SETTINGS: json(), // Firebase settings for frontend, stored as a JSON string -}); +export default cleanEnv( + { + NEXT_PUBLIC_BACKEND_URL: process.env.NEXT_PUBLIC_BACKEND_URL, + NEXT_PUBLIC_FIREBASE_SETTINGS: process.env.NEXT_PUBLIC_FIREBASE_SETTINGS, + }, + { + NEXT_PUBLIC_BACKEND_URL: str(), // URL of our backend + NEXT_PUBLIC_FIREBASE_SETTINGS: json(), // Firebase settings for frontend, stored as a JSON string + }, +);