From 45e53ecd9d7162c8e5914dc3395102cc2c4f2c65 Mon Sep 17 00:00:00 2001 From: Carlo M Date: Mon, 21 Oct 2024 06:12:01 -0600 Subject: [PATCH 01/28] Cleaned up CICD, removed fly, removed husky --- .dockerignore | 4 ---- .github/workflows/ci.yml | 5 ----- .github/workflows/deploy.yml | 21 ------------------ .husky/pre-commit | 4 ---- DB_SETUP.md | 18 ---------------- Dockerfile | 36 ------------------------------- fly.toml | 42 ------------------------------------ package.json | 2 -- 8 files changed, 132 deletions(-) delete mode 100644 .dockerignore delete mode 100644 .github/workflows/deploy.yml delete mode 100644 .husky/pre-commit delete mode 100644 DB_SETUP.md delete mode 100644 Dockerfile delete mode 100644 fly.toml diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 79297ed5..00000000 --- a/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -Dockerfile -.dockerignore -node_modules -.git diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56cddc0a..1e2ff854 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,5 @@ name: CI on: [push, pull_request] -env: - DATABASE_URL: ${{ secrets.DATABASE_URL }} - JWT_TOKEN: ${{ secrets.JWT_TOKEN }} - JWT_EXPIRY: ${{ secrets.JWT_EXPIRY }} jobs: test: runs-on: ubuntu-latest @@ -14,4 +10,3 @@ jobs: node-version: 'lts/*' - run: npm ci - run: npx eslint src - - run: npx prisma migrate reset --force --skip-seed diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 471052aa..00000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Fly Deploy -on: - push: - branches: - - main -env: - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} - DATABASE_URL: ${{ secrets.DATABASE_URL }} - JWT_SECRET: ${{ secrets.JWT_SECRET }} - JWT_EXPIRY: ${{ secrets.JWT_EXPIRY }} -jobs: - deploy: - name: Deploy app to fly.io - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: superfly/flyctl-actions/setup-flyctl@master - - run: echo DATABASE_URL=$DATABASE_URL >> .env - - run: echo JWT_SECRET=$JWT_SECRET >> .env - - run: echo JWT_EXPIRY=$JWT_EXPIRY >> .env - - run: flyctl deploy --remote-only diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100644 index a41979b3..00000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -npx eslint src \ No newline at end of file diff --git a/DB_SETUP.md b/DB_SETUP.md deleted file mode 100644 index 5a966b81..00000000 --- a/DB_SETUP.md +++ /dev/null @@ -1,18 +0,0 @@ -## Creating your databases with ElephantSQL - -1. [Sign in to ElephantSQL](https://customer.elephantsql.com/login) using your GitHub account - - If it asks you to create a team, create one with any name - -2. When logged in, click the green *Create New Instance* button in the top right -![](./assets/db-setup/1.PNG) - -3. Enter a name for your new database instance and choose the *Tiny Turtle (Free)* plan. **This will be your PRIMARY database** -![](./assets/db-setup/2.PNG) - -4. Repeat the same steps to create a second database, this time give it the same name but add `-shadow` to the end. **This will be your SHADOW database** - -5. In your shadow instance: - - click the *Browser* menu item on the left side of the screen - - in the SQL Browser text input, enter `CREATE SCHEMA shadow;` - - click the *Execute* button -![](./assets/db-setup/3.PNG) \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index b0c8ba77..00000000 --- a/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -FROM debian:bullseye as builder - -ARG NODE_VERSION=18.12.1 - -RUN apt-get update; apt install -y curl -RUN curl https://get.volta.sh | bash -ENV VOLTA_HOME /root/.volta -ENV PATH /root/.volta/bin:$PATH -RUN volta install node@${NODE_VERSION} - -####################################################################### - -RUN mkdir /app -WORKDIR /app - -# NPM will not install any package listed in "devDependencies" when NODE_ENV is set to "production", -# to install all modules: "npm install --production=false". -# Ref: https://docs.npmjs.com/cli/v9/commands/npm-install#description - -ENV NODE_ENV production - -COPY . . - -RUN npm install -FROM debian:bullseye - -LABEL fly_launch_runtime="nodejs" - -COPY --from=builder /root/.volta /root/.volta -COPY --from=builder /app /app - -WORKDIR /app -ENV NODE_ENV production -ENV PATH /root/.volta/bin:$PATH - -CMD [ "npm", "run", "start" ] diff --git a/fly.toml b/fly.toml deleted file mode 100644 index eac058e7..00000000 --- a/fly.toml +++ /dev/null @@ -1,42 +0,0 @@ -# fly.toml file generated for team-dev-backend-api on 2022-11-30T11:30:34Z - -app = "team-dev-backend-api" -kill_signal = "SIGINT" -kill_timeout = 5 -processes = [] - -[env] - PORT = "8080" - -[experimental] - allowed_public_ports = [] - auto_rollback = true - -[deploy] - release_command = "npx prisma migrate deploy" - -[[services]] - http_checks = [] - internal_port = 8080 - processes = ["app"] - protocol = "tcp" - script_checks = [] - [services.concurrency] - hard_limit = 25 - soft_limit = 20 - type = "connections" - - [[services.ports]] - force_https = true - handlers = ["http"] - port = 80 - - [[services.ports]] - handlers = ["tls", "http"] - port = 443 - - [[services.tcp_checks]] - grace_period = "1s" - interval = "15s" - restart_limit = 0 - timeout = "2s" diff --git a/package.json b/package.json index 41e19e5e..4efc3e40 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "scripts": { "start": "node src/index.js", "dev": "nodemon src/index.js", - "prepare": "husky install", "db-reset": "prisma migrate reset" }, "prisma": { @@ -33,7 +32,6 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^5.1.0", - "husky": "^7.0.4", "nodemon": "^2.0.15", "prettier": "^2.6.2", "prisma": "^3.12.0" From f60740cc89b19cacc01f1f2980bf2486a4cc4e7c Mon Sep 17 00:00:00 2001 From: Carlo M Date: Thu, 24 Oct 2024 06:04:08 -0600 Subject: [PATCH 02/28] fixed package json merge conflict artifacts --- package.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/package.json b/package.json index 47c96c2f..3021b44f 100644 --- a/package.json +++ b/package.json @@ -7,15 +7,10 @@ "scripts": { "start": "node src/index.js", "dev": "nodemon src/index.js", -<<<<<<< HEAD - "db-reset": "prisma migrate reset" -======= - "prepare": "husky install", "db-reset": "prisma migrate reset", "lint": "eslint .", "lint:fix": "eslint --fix", "format": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc" ->>>>>>> upstream/main }, "prisma": { "seed": "node prisma/seed.js" From b18ffb1c6da23b3ef83d64e066ca9d9a36076e50 Mon Sep 17 00:00:00 2001 From: Carlo M Date: Fri, 25 Oct 2024 05:20:57 -0600 Subject: [PATCH 03/28] Super big contribution to the project --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e2dda48..01595aee 100644 --- a/README.md +++ b/README.md @@ -42,4 +42,4 @@ The users that are seeded can be used to test the API endpoints and to log into [todo]: -TODO +TODO - still not done From 96fc9d1238c1f109503a08ee71a2d64d0768eb23 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Fri, 25 Oct 2024 14:37:34 +0200 Subject: [PATCH 04/28] Update prisma --- .env.example | 5 - package.json | 4 +- pnpm-lock.yaml | 2955 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2957 insertions(+), 7 deletions(-) delete mode 100644 .env.example create mode 100644 pnpm-lock.yaml diff --git a/.env.example b/.env.example deleted file mode 100644 index 932b9f1e..00000000 --- a/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -PORT=4000 -DATABASE_URL="?schema=prisma" -SHADOW_DATABASE_URL="?schema=shadow" -JWT_SECRET="somesecurestring" -JWT_EXPIRY="24h" \ No newline at end of file diff --git a/package.json b/package.json index 3021b44f..000af406 100644 --- a/package.json +++ b/package.json @@ -37,10 +37,10 @@ "eslint-plugin-promise": "^5.1.0", "nodemon": "^2.0.15", "prettier": "^2.6.2", - "prisma": "^3.12.0" + "prisma": "^5.21.1" }, "dependencies": { - "@prisma/client": "^3.12.0", + "@prisma/client": "^5.21.1", "bcrypt": "^5.0.1", "cors": "^2.8.5", "dotenv": "^16.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..83652697 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2955 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@prisma/client': + specifier: ^5.21.1 + version: 5.21.1(prisma@5.21.1) + bcrypt: + specifier: ^5.0.1 + version: 5.1.1 + cors: + specifier: ^2.8.5 + version: 2.8.5 + dotenv: + specifier: ^16.0.0 + version: 16.4.5 + express: + specifier: ^4.17.3 + version: 4.21.1 + jsonwebtoken: + specifier: ^8.5.1 + version: 8.5.1 + swagger-ui-express: + specifier: ^5.0.0 + version: 5.0.1(express@4.21.1) + yaml: + specifier: ^2.3.4 + version: 2.6.0 + devDependencies: + '@boolean-uk/boolean-prettier-config': + specifier: ^1.0.2 + version: 1.0.2 + eslint: + specifier: ^7.32.0 + version: 7.32.0 + eslint-config-prettier: + specifier: ^8.5.0 + version: 8.10.0(eslint@7.32.0) + eslint-config-standard: + specifier: ^16.0.3 + version: 16.0.3(eslint-plugin-import@2.31.0(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint@7.32.0) + eslint-plugin-import: + specifier: ^2.25.2 + version: 2.31.0(eslint@7.32.0) + eslint-plugin-node: + specifier: ^11.1.0 + version: 11.1.0(eslint@7.32.0) + eslint-plugin-prettier: + specifier: ^4.0.0 + version: 4.2.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8) + eslint-plugin-promise: + specifier: ^5.1.0 + version: 5.2.0(eslint@7.32.0) + nodemon: + specifier: ^2.0.15 + version: 2.0.22 + prettier: + specifier: ^2.6.2 + version: 2.8.8 + prisma: + specifier: ^5.21.1 + version: 5.21.1 + +packages: + + '@babel/code-frame@7.12.11': + resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} + + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.25.9': + resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} + engines: {node: '>=6.9.0'} + + '@boolean-uk/boolean-prettier-config@1.0.2': + resolution: {integrity: sha512-I2Bv0GNk5vFcLgY9XLAoILkZ4EgEOclYmpy2dL/kgNG2ZZ7iuvPtY9Oaw5xWM2rc9L4LPWiATKdrESHYebOQ9w==} + + '@eslint/eslintrc@0.4.3': + resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} + engines: {node: ^10.12.0 || >=12.0.0} + + '@humanwhocodes/config-array@0.5.0': + resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/object-schema@1.2.1': + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + deprecated: Use @eslint/object-schema instead + + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + + '@prisma/client@5.21.1': + resolution: {integrity: sha512-3n+GgbAZYjaS/k0M03yQsQfR1APbr411r74foknnsGpmhNKBG49VuUkxIU6jORgvJPChoD4WC4PqoHImN1FP0w==} + engines: {node: '>=16.13'} + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + + '@prisma/debug@5.21.1': + resolution: {integrity: sha512-uY8SAhcnORhvgtOrNdvWS98Aq/nkQ9QDUxrWAgW8XrCZaI3j2X7zb7Xe6GQSh6xSesKffFbFlkw0c2luHQviZA==} + + '@prisma/engines-version@5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36': + resolution: {integrity: sha512-qvnEflL0//lh44S/T9NcvTMxfyowNeUxTunPcDfKPjyJNrCNf2F1zQLcUv5UHAruECpX+zz21CzsC7V2xAeM7Q==} + + '@prisma/engines@5.21.1': + resolution: {integrity: sha512-hGVTldUkIkTwoV8//hmnAAiAchi4oMEKD3aW5H2RrnI50tTdwza7VQbTTAyN3OIHWlK5DVg6xV7X8N/9dtOydA==} + + '@prisma/fetch-engine@5.21.1': + resolution: {integrity: sha512-70S31vgpCGcp9J+mh/wHtLCkVezLUqe/fGWk3J3JWZIN7prdYSlr1C0niaWUyNK2VflLXYi8kMjAmSxUVq6WGQ==} + + '@prisma/get-platform@5.21.1': + resolution: {integrity: sha512-sRxjL3Igst3ct+e8ya/x//cDXmpLbZQ5vfps2N4tWl4VGKQAmym77C/IG/psSMsQKszc8uFC/q1dgmKFLUgXZQ==} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bcrypt@5.1.1: + resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==} + engines: {node: '>= 10.0.0'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@8.10.0: + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-config-standard@16.0.3: + resolution: {integrity: sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==} + peerDependencies: + eslint: ^7.12.1 + eslint-plugin-import: ^2.22.1 + eslint-plugin-node: ^11.1.0 + eslint-plugin-promise: ^4.2.1 || ^5.0.0 + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.12.0: + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-es@3.0.1: + resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + + eslint-plugin-import@2.31.0: + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-node@11.1.0: + resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=5.16.0' + + eslint-plugin-prettier@4.2.1: + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + + eslint-plugin-promise@5.2.0: + resolution: {integrity: sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + eslint: ^7.0.0 + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + + eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + + eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + + eslint@7.32.0: + resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==} + engines: {node: ^10.12.0 || >=12.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@7.3.1: + resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} + engines: {node: ^10.12.0 || >=12.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + express@4.21.1: + resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==} + engines: {node: '>= 0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.0.3: + resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} + engines: {node: '>= 0.8'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + + ignore@4.0.6: + resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} + engines: {node: '>= 4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + jsonwebtoken@8.5.1: + resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} + engines: {node: '>=4', npm: '>=1.4.28'} + + jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + nodemon@2.0.22: + resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} + engines: {node: '>=8.10.0'} + hasBin: true + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@0.1.10: + resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prisma@5.21.1: + resolution: {integrity: sha512-PB+Iqzld/uQBPaaw2UVIk84kb0ITsLajzsxzsadxxl54eaU5Gyl2/L02ysivHxK89t7YrfQJm+Ggk37uvM70oQ==} + engines: {node: '>=16.13'} + hasBin: true + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + regexp.prototype.flags@1.5.3: + resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} + engines: {node: '>= 0.4'} + + regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.0.0: + resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + engines: {node: '>= 0.8.0'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + simple-update-notifier@1.1.0: + resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} + engines: {node: '>=8.10.0'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + swagger-ui-dist@5.17.14: + resolution: {integrity: sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==} + + swagger-ui-express@5.0.1: + resolution: {integrity: sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==} + engines: {node: '>= v0.10.32'} + peerDependencies: + express: '>=4.0.0 || >=5.0.0-beta' + + table@6.8.2: + resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} + engines: {node: '>=10.0.0'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + v8-compile-cache@2.4.0: + resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.6.0: + resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==} + engines: {node: '>= 14'} + hasBin: true + +snapshots: + + '@babel/code-frame@7.12.11': + dependencies: + '@babel/highlight': 7.25.9 + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/highlight@7.25.9': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@boolean-uk/boolean-prettier-config@1.0.2': {} + + '@eslint/eslintrc@0.4.3': + dependencies: + ajv: 6.12.6 + debug: 4.3.7 + espree: 7.3.1 + globals: 13.24.0 + ignore: 4.0.6 + import-fresh: 3.3.0 + js-yaml: 3.14.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/config-array@0.5.0': + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.7 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/object-schema@1.2.1': {} + + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.3 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@prisma/client@5.21.1(prisma@5.21.1)': + optionalDependencies: + prisma: 5.21.1 + + '@prisma/debug@5.21.1': {} + + '@prisma/engines-version@5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36': {} + + '@prisma/engines@5.21.1': + dependencies: + '@prisma/debug': 5.21.1 + '@prisma/engines-version': 5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36 + '@prisma/fetch-engine': 5.21.1 + '@prisma/get-platform': 5.21.1 + + '@prisma/fetch-engine@5.21.1': + dependencies: + '@prisma/debug': 5.21.1 + '@prisma/engines-version': 5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36 + '@prisma/get-platform': 5.21.1 + + '@prisma/get-platform@5.21.1': + dependencies: + '@prisma/debug': 5.21.1 + + '@rtsao/scc@1.1.0': {} + + '@types/json5@0.0.29': {} + + abbrev@1.1.1: {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn-jsx@5.3.2(acorn@7.4.1): + dependencies: + acorn: 7.4.1 + + acorn@7.4.1: {} + + agent-base@6.0.2: + dependencies: + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-regex@5.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + aproba@2.0.0: {} + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + array-buffer-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + + array-flatten@1.1.1: {} + + array-includes@3.1.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + is-string: 1.0.7 + + array.prototype.findlastindex@1.2.5: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-shim-unscopables: 1.0.2 + + array.prototype.flat@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + array.prototype.flatmap@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + arraybuffer.prototype.slice@1.0.3: + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + + astral-regex@2.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.0.0 + + balanced-match@1.0.2: {} + + bcrypt@5.1.1: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + node-addon-api: 5.1.0 + transitivePeerDependencies: + - encoding + - supports-color + + binary-extensions@2.3.0: {} + + body-parser@1.20.3: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.13.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer-equal-constant-time@1.0.1: {} + + bytes@3.1.2: {} + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + callsites@3.1.0: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chownr@2.0.0: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-support@1.1.3: {} + + concat-map@0.0.1: {} + + console-control-strings@1.1.0: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.0.6: {} + + cookie@0.7.1: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + data-view-buffer@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-offset@1.0.0: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.2.7(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delegates@1.0.0: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + detect-libc@2.0.3: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dotenv@16.4.5: {} + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + emoji-regex@8.0.0: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + es-abstract@1.23.3: + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.2 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.3 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + es-object-atoms@1.0.0: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.0.3: + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.0.2: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.2.1: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@8.10.0(eslint@7.32.0): + dependencies: + eslint: 7.32.0 + + eslint-config-standard@16.0.3(eslint-plugin-import@2.31.0(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint@7.32.0): + dependencies: + eslint: 7.32.0 + eslint-plugin-import: 2.31.0(eslint@7.32.0) + eslint-plugin-node: 11.1.0(eslint@7.32.0) + eslint-plugin-promise: 5.2.0(eslint@7.32.0) + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7(supports-color@5.5.0) + is-core-module: 2.15.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.0(eslint-import-resolver-node@0.3.9)(eslint@7.32.0): + dependencies: + debug: 3.2.7(supports-color@5.5.0) + optionalDependencies: + eslint: 7.32.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-es@3.0.1(eslint@7.32.0): + dependencies: + eslint: 7.32.0 + eslint-utils: 2.1.0 + regexpp: 3.2.0 + + eslint-plugin-import@2.31.0(eslint@7.32.0): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7(supports-color@5.5.0) + doctrine: 2.1.0 + eslint: 7.32.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.0(eslint-import-resolver-node@0.3.9)(eslint@7.32.0) + hasown: 2.0.2 + is-core-module: 2.15.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 + semver: 6.3.1 + string.prototype.trimend: 1.0.8 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-node@11.1.0(eslint@7.32.0): + dependencies: + eslint: 7.32.0 + eslint-plugin-es: 3.0.1(eslint@7.32.0) + eslint-utils: 2.1.0 + ignore: 5.3.2 + minimatch: 3.1.2 + resolve: 1.22.8 + semver: 6.3.1 + + eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8): + dependencies: + eslint: 7.32.0 + prettier: 2.8.8 + prettier-linter-helpers: 1.0.0 + optionalDependencies: + eslint-config-prettier: 8.10.0(eslint@7.32.0) + + eslint-plugin-promise@5.2.0(eslint@7.32.0): + dependencies: + eslint: 7.32.0 + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-utils@2.1.0: + dependencies: + eslint-visitor-keys: 1.3.0 + + eslint-visitor-keys@1.3.0: {} + + eslint-visitor-keys@2.1.0: {} + + eslint@7.32.0: + dependencies: + '@babel/code-frame': 7.12.11 + '@eslint/eslintrc': 0.4.3 + '@humanwhocodes/config-array': 0.5.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.7 + doctrine: 3.0.0 + enquirer: 2.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 5.1.1 + eslint-utils: 2.1.0 + eslint-visitor-keys: 2.1.0 + espree: 7.3.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + functional-red-black-tree: 1.0.1 + glob-parent: 5.1.2 + globals: 13.24.0 + ignore: 4.0.6 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 3.14.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + progress: 2.0.3 + regexpp: 3.2.0 + semver: 7.6.3 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + table: 6.8.2 + text-table: 0.2.0 + v8-compile-cache: 2.4.0 + transitivePeerDependencies: + - supports-color + + espree@7.3.1: + dependencies: + acorn: 7.4.1 + acorn-jsx: 5.3.2(acorn@7.4.1) + eslint-visitor-keys: 1.3.0 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + express@4.21.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.10 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.0.3: {} + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.3.1: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.1: {} + + for-each@0.3.3: + dependencies: + is-callable: 1.2.7 + + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + functions-have-names: 1.2.3 + + functional-red-black-tree@1.0.1: {} + + functions-have-names@1.2.3: {} + + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + get-symbol-description@1.0.2: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.0.1 + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + has-bigints@1.0.2: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.0.3 + + has-unicode@2.0.1: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + ignore-by-default@1.0.1: {} + + ignore@4.0.6: {} + + ignore@5.3.2: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.0.7: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 + + ipaddr.js@1.9.1: {} + + is-array-buffer@3.0.4: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + + is-bigint@1.0.4: + dependencies: + has-bigints: 1.0.2 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.1.2: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.1: + dependencies: + is-typed-array: 1.1.13 + + is-date-object@1.0.5: + dependencies: + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-negative-zero@2.0.3: {} + + is-number-object@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-regex@1.1.4: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-shared-array-buffer@1.0.3: + dependencies: + call-bind: 1.0.7 + + is-string@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-symbol@1.0.4: + dependencies: + has-symbols: 1.0.3 + + is-typed-array@1.1.13: + dependencies: + which-typed-array: 1.1.15 + + is-weakref@1.0.2: + dependencies: + call-bind: 1.0.7 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + jsonwebtoken@8.5.1: + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 5.7.2 + + jwa@1.4.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.2: + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.merge@4.6.2: {} + + lodash.once@4.1.1: {} + + lodash.truncate@4.4.2: {} + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + media-typer@0.3.0: {} + + merge-descriptors@1.0.3: {} + + methods@1.1.2: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimist@1.2.8: {} + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp@1.0.4: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + negotiator@0.6.3: {} + + node-addon-api@5.1.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + nodemon@2.0.22: + dependencies: + chokidar: 3.6.0 + debug: 3.2.7(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 5.7.2 + simple-update-notifier: 1.1.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + + normalize-path@3.0.0: {} + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + + object-assign@4.1.1: {} + + object-inspect@1.13.2: {} + + object-keys@1.1.1: {} + + object.assign@4.1.5: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + + object.values@1.2.0: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parseurl@1.3.3: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@0.1.10: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + possible-typed-array-names@1.0.0: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@2.8.8: {} + + prisma@5.21.1: + dependencies: + '@prisma/engines': 5.21.1 + optionalDependencies: + fsevents: 2.3.3 + + progress@2.0.3: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + pstree.remy@1.1.8: {} + + punycode@2.3.1: {} + + qs@6.13.0: + dependencies: + side-channel: 1.0.6 + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + regexp.prototype.flags@1.5.3: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 + + regexpp@3.2.0: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + safe-array-concat@1.1.2: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-regex-test@1.0.3: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-regex: 1.1.4 + + safer-buffer@2.1.2: {} + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.0.0: {} + + semver@7.6.3: {} + + send@0.19.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.2: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.0 + transitivePeerDependencies: + - supports-color + + set-blocking@2.0.0: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.2 + + signal-exit@3.0.7: {} + + simple-update-notifier@1.1.0: + dependencies: + semver: 7.0.0 + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + sprintf-js@1.0.3: {} + + statuses@2.0.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string.prototype.trim@1.2.9: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + + string.prototype.trimend@1.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + swagger-ui-dist@5.17.14: {} + + swagger-ui-express@5.0.1(express@4.21.1): + dependencies: + express: 4.21.1 + swagger-ui-dist: 5.17.14 + + table@6.8.2: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + text-table@0.2.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + touch@3.1.1: {} + + tr46@0.0.3: {} + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typed-array-buffer@1.0.2: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + + typed-array-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + typed-array-byte-offset@1.0.2: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + typed-array-length@1.0.6: + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + + unbox-primitive@1.0.2: + dependencies: + call-bind: 1.0.7 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + + undefsafe@2.0.5: {} + + unpipe@1.0.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + utils-merge@1.0.1: {} + + v8-compile-cache@2.4.0: {} + + vary@1.1.2: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-boxed-primitive@1.0.2: + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + + which-typed-array@1.1.15: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + yallist@4.0.0: {} + + yaml@2.6.0: {} From db1ebd9de7f573aec889ef8df415d3df09aa16e2 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Fri, 25 Oct 2024 14:41:50 +0200 Subject: [PATCH 05/28] Re-add .env.example --- .env.example | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..1d6f07e0 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +PORT=4000 +DATABASE_URL="?schema=prisma" +SHADOW_DATABASE_URL="?schema=shadow" +JWT_SECRET="somesecurestring" +JWT_EXPIRY="24h" From 6b3e7bb8eaf9d4b03eaeeda21677b7233576972e Mon Sep 17 00:00:00 2001 From: gremble0 Date: Mon, 28 Oct 2024 11:12:31 +0100 Subject: [PATCH 06/28] Add some fields to database --- .../migration.sql | 4 +++ prisma/schema.prisma | 6 ++++ src/domain/user.js | 32 +++++++++++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 prisma/migrations/20241028101023_add_profile_fields/migration.sql diff --git a/prisma/migrations/20241028101023_add_profile_fields/migration.sql b/prisma/migrations/20241028101023_add_profile_fields/migration.sql new file mode 100644 index 00000000..788241e0 --- /dev/null +++ b/prisma/migrations/20241028101023_add_profile_fields/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "Profile" ADD COLUMN "imageUrl" TEXT, +ADD COLUMN "mobile" TEXT, +ADD COLUMN "specialism" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 72ec5632..3d62bd02 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -36,6 +36,12 @@ model Profile { lastName String bio String? githubUrl String? + mobile String? + specialism String? + imageUrl String? + // TODO: + // startDate + // endDate } model Cohort { diff --git a/src/domain/user.js b/src/domain/user.js index fd7734c7..f947a44a 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -18,15 +18,29 @@ export default class User { user.profile?.lastName, user.email, user.profile?.bio, - user.profile?.githubUrl, + user.profile?.githubUrl, // TODO: username + user.profile?.mobile, + user.profile?.specialism, + user.profile?.imageUrl, user.password, user.role ) } static async fromJson(json) { + // TODO: get cohortId // eslint-disable-next-line camelcase - const { firstName, lastName, email, biography, githubUrl, password } = json + const { + firstName, + lastName, + email, + biography, + githubUrl, + mobile, + specialism, + imageUrl, + password + } = json const passwordHash = await bcrypt.hash(password, 8) @@ -38,6 +52,9 @@ export default class User { email, biography, githubUrl, + mobile, + specialism, + imageUrl, passwordHash ) } @@ -50,6 +67,9 @@ export default class User { email, bio, githubUrl, + mobile, + specialism, + imageUrl, passwordHash = null, role = 'STUDENT' ) { @@ -60,6 +80,9 @@ export default class User { this.email = email this.bio = bio this.githubUrl = githubUrl + this.mobile = mobile + this.specialism = specialism + this.imageUrl = imageUrl this.passwordHash = passwordHash this.role = role } @@ -74,7 +97,10 @@ export default class User { lastName: this.lastName, email: this.email, biography: this.bio, - githubUrl: this.githubUrl + githubUrl: this.githubUrl, + mobile: this.mobile, + specialism: this.specialism, + imageUrl: this.imageUrl } } } From b5539306eecb8c71fa02c603901d64052436338b Mon Sep 17 00:00:00 2001 From: ISecka Date: Mon, 28 Oct 2024 11:38:35 +0100 Subject: [PATCH 07/28] Added domain model for Post and edited getAll posts function to fetch posts from the database --- package-lock.json | 184 +++++++++++++++++++++++++--------------- src/controllers/post.js | 34 ++++---- src/domain/post.js | 110 ++++++++++++++++++++++++ 3 files changed, 246 insertions(+), 82 deletions(-) create mode 100644 src/domain/post.js diff --git a/package-lock.json b/package-lock.json index 044145e5..27cae28b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@prisma/client": "^3.12.0", + "@prisma/client": "^5.21.1", "bcrypt": "^5.0.1", "cors": "^2.8.5", "dotenv": "^16.0.0", @@ -27,10 +27,9 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^5.1.0", - "husky": "^7.0.4", "nodemon": "^2.0.15", "prettier": "^2.6.2", - "prisma": "^3.12.0" + "prisma": "^5.21.1" } }, "node_modules/@babel/code-frame": { @@ -202,15 +201,13 @@ } }, "node_modules/@prisma/client": { - "version": "3.15.2", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.15.2.tgz", - "integrity": "sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.21.1.tgz", + "integrity": "sha512-3n+GgbAZYjaS/k0M03yQsQfR1APbr411r74foknnsGpmhNKBG49VuUkxIU6jORgvJPChoD4WC4PqoHImN1FP0w==", "hasInstallScript": true, - "dependencies": { - "@prisma/engines-version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" - }, + "license": "Apache-2.0", "engines": { - "node": ">=12.6" + "node": ">=16.13" }, "peerDependencies": { "prisma": "*" @@ -221,17 +218,55 @@ } } }, + "node_modules/@prisma/debug": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.21.1.tgz", + "integrity": "sha512-uY8SAhcnORhvgtOrNdvWS98Aq/nkQ9QDUxrWAgW8XrCZaI3j2X7zb7Xe6GQSh6xSesKffFbFlkw0c2luHQviZA==", + "devOptional": true, + "license": "Apache-2.0" + }, "node_modules/@prisma/engines": { - "version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz", - "integrity": "sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.21.1.tgz", + "integrity": "sha512-hGVTldUkIkTwoV8//hmnAAiAchi4oMEKD3aW5H2RrnI50tTdwza7VQbTTAyN3OIHWlK5DVg6xV7X8N/9dtOydA==", "devOptional": true, - "hasInstallScript": true + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.21.1", + "@prisma/engines-version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", + "@prisma/fetch-engine": "5.21.1", + "@prisma/get-platform": "5.21.1" + } }, "node_modules/@prisma/engines-version": { - "version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz", - "integrity": "sha512-e3k2Vd606efd1ZYy2NQKkT4C/pn31nehyLhVug6To/q8JT8FpiMrDy7zmm3KLF0L98NOQQcutaVtAPhzKhzn9w==" + "version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36.tgz", + "integrity": "sha512-qvnEflL0//lh44S/T9NcvTMxfyowNeUxTunPcDfKPjyJNrCNf2F1zQLcUv5UHAruECpX+zz21CzsC7V2xAeM7Q==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.21.1.tgz", + "integrity": "sha512-70S31vgpCGcp9J+mh/wHtLCkVezLUqe/fGWk3J3JWZIN7prdYSlr1C0niaWUyNK2VflLXYi8kMjAmSxUVq6WGQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.21.1", + "@prisma/engines-version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", + "@prisma/get-platform": "5.21.1" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.21.1.tgz", + "integrity": "sha512-sRxjL3Igst3ct+e8ya/x//cDXmpLbZQ5vfps2N4tWl4VGKQAmym77C/IG/psSMsQKszc8uFC/q1dgmKFLUgXZQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.21.1" + } }, "node_modules/@types/json5": { "version": "0.0.29", @@ -1521,11 +1556,12 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1765,21 +1801,6 @@ "node": ">= 6" } }, - "node_modules/husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", - "dev": true, - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2765,20 +2786,23 @@ } }, "node_modules/prisma": { - "version": "3.15.2", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.15.2.tgz", - "integrity": "sha512-nMNSMZvtwrvoEQ/mui8L/aiCLZRCj5t6L3yujKpcDhIPk7garp8tL4nMx2+oYsN0FWBacevJhazfXAbV1kfBzA==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.21.1.tgz", + "integrity": "sha512-PB+Iqzld/uQBPaaw2UVIk84kb0ITsLajzsxzsadxxl54eaU5Gyl2/L02ysivHxK89t7YrfQJm+Ggk37uvM70oQ==", "devOptional": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "@prisma/engines": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" + "@prisma/engines": "5.21.1" }, "bin": { - "prisma": "build/index.js", - "prisma2": "build/index.js" + "prisma": "build/index.js" }, "engines": { - "node": ">=12.6" + "node": ">=16.13" + }, + "optionalDependencies": { + "fsevents": "2.3.3" } }, "node_modules/progress": { @@ -3722,23 +3746,54 @@ } }, "@prisma/client": { - "version": "3.15.2", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.15.2.tgz", - "integrity": "sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.21.1.tgz", + "integrity": "sha512-3n+GgbAZYjaS/k0M03yQsQfR1APbr411r74foknnsGpmhNKBG49VuUkxIU6jORgvJPChoD4WC4PqoHImN1FP0w==", + "requires": {} + }, + "@prisma/debug": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.21.1.tgz", + "integrity": "sha512-uY8SAhcnORhvgtOrNdvWS98Aq/nkQ9QDUxrWAgW8XrCZaI3j2X7zb7Xe6GQSh6xSesKffFbFlkw0c2luHQviZA==", + "devOptional": true + }, + "@prisma/engines": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.21.1.tgz", + "integrity": "sha512-hGVTldUkIkTwoV8//hmnAAiAchi4oMEKD3aW5H2RrnI50tTdwza7VQbTTAyN3OIHWlK5DVg6xV7X8N/9dtOydA==", + "devOptional": true, "requires": { - "@prisma/engines-version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" + "@prisma/debug": "5.21.1", + "@prisma/engines-version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", + "@prisma/fetch-engine": "5.21.1", + "@prisma/get-platform": "5.21.1" } }, - "@prisma/engines": { - "version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz", - "integrity": "sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg==", + "@prisma/engines-version": { + "version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36.tgz", + "integrity": "sha512-qvnEflL0//lh44S/T9NcvTMxfyowNeUxTunPcDfKPjyJNrCNf2F1zQLcUv5UHAruECpX+zz21CzsC7V2xAeM7Q==", "devOptional": true }, - "@prisma/engines-version": { - "version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz", - "integrity": "sha512-e3k2Vd606efd1ZYy2NQKkT4C/pn31nehyLhVug6To/q8JT8FpiMrDy7zmm3KLF0L98NOQQcutaVtAPhzKhzn9w==" + "@prisma/fetch-engine": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.21.1.tgz", + "integrity": "sha512-70S31vgpCGcp9J+mh/wHtLCkVezLUqe/fGWk3J3JWZIN7prdYSlr1C0niaWUyNK2VflLXYi8kMjAmSxUVq6WGQ==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.21.1", + "@prisma/engines-version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", + "@prisma/get-platform": "5.21.1" + } + }, + "@prisma/get-platform": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.21.1.tgz", + "integrity": "sha512-sRxjL3Igst3ct+e8ya/x//cDXmpLbZQ5vfps2N4tWl4VGKQAmym77C/IG/psSMsQKszc8uFC/q1dgmKFLUgXZQ==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.21.1" + } }, "@types/json5": { "version": "0.0.29", @@ -4722,9 +4777,9 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "optional": true }, @@ -4893,12 +4948,6 @@ "debug": "4" } }, - "husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", - "dev": true - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -5616,12 +5665,13 @@ } }, "prisma": { - "version": "3.15.2", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.15.2.tgz", - "integrity": "sha512-nMNSMZvtwrvoEQ/mui8L/aiCLZRCj5t6L3yujKpcDhIPk7garp8tL4nMx2+oYsN0FWBacevJhazfXAbV1kfBzA==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.21.1.tgz", + "integrity": "sha512-PB+Iqzld/uQBPaaw2UVIk84kb0ITsLajzsxzsadxxl54eaU5Gyl2/L02ysivHxK89t7YrfQJm+Ggk37uvM70oQ==", "devOptional": true, "requires": { - "@prisma/engines": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" + "@prisma/engines": "5.21.1", + "fsevents": "2.3.3" } }, "progress": { diff --git a/src/controllers/post.js b/src/controllers/post.js index 7b168039..f435ef37 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -1,4 +1,5 @@ import { sendDataResponse } from '../utils/responses.js' +import Post from '../domain/post.js' export const create = async (req, res) => { const { content } = req.body @@ -11,18 +12,21 @@ export const create = async (req, res) => { } export const getAll = async (req, res) => { - return sendDataResponse(res, 200, { - posts: [ - { - id: 1, - content: 'Hello world!', - author: { ...req.user } - }, - { - id: 2, - content: 'Hello from the void!', - author: { ...req.user } - } - ] - }) -} + + + + try { + // Use the Post class to retrieve all posts + const foundPosts = await Post.findAll() + + // Format the posts for the response + const formattedPosts = foundPosts.map((post) => post.toJSON()) + + return sendDataResponse(res, 200, { posts: formattedPosts }) + } catch (error) { + console.error('Error fetching posts:', error) + return sendDataResponse(res, 500, { error: 'Failed to fetch posts' }) + } + } + + diff --git a/src/domain/post.js b/src/domain/post.js new file mode 100644 index 00000000..16ab3c3c --- /dev/null +++ b/src/domain/post.js @@ -0,0 +1,110 @@ +import dbClient from '../utils/dbClient.js' + +export default class Post { + /** + * Create a Post instance from a database record. + * @param { { id: int, content: string, createdAt: Date, updatedAt: Date, authorId: int } } post + * @returns {Post} + */ + static fromDb(post) { + return new Post( + post.id, + post.content, + post.createdAt, + post.updatedAt, + post.authorId + ) + } + + static async fromJson(json) { + const { content, authorId } = json + return new Post( + null, // id will be generated by the database + content, + null, // createdAt will be set by the database + null, // updatedAt will also be set by the database + authorId + ) + } + + constructor(id, content, createdAt, updatedAt, authorId) { + this.id = id + this.content = content + this.createdAt = createdAt + this.updatedAt = updatedAt + this.authorId = authorId + } + + toJSON() { + return { + post: { + id: this.id, + content: this.content, + createdAt: this.createdAt, + updatedAt: this.updatedAt, + authorId: this.authorId + } + } + } + + /** + * Save the post instance to the database. + * @returns {Post} A post instance containing an ID, representing the post data created in the database + */ + async save() { + const data = { + content: this.content, + author: { + connect: { + id: this.authorId + } + } + } + + const createdPost = await dbClient.post.create({ + data + }) + + return Post.fromDb(createdPost) + } + + static async findById(id) { + return Post._findByUnique('id', id) + } + + static async findAll() { + return Post._findMany() + } + + static async findManyByAuthorId(authorId) { + return Post._findMany('authorId', authorId) + } + + static async _findByUnique(key, value) { + const foundPost = await dbClient.post.findUnique({ + where: { + [key]: value + } + }) + + if (foundPost) { + return Post.fromDb(foundPost) + } + + return null + } + + static async _findMany(key, value) { + const query = {} + + if (key !== undefined && value !== undefined) { + query.where = { + [key]: value + } + } + + const foundPosts = await dbClient.post.findMany(query) + + return foundPosts.map((post) => Post.fromDb(post)) + } +} From 2b2d4cb411e4b98e436b7bc130b77c5585b4d791 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Mon, 28 Oct 2024 14:31:09 +0100 Subject: [PATCH 08/28] Fix patch endpoint for /users --- docs/openapi.yml | 34 ++++++++++++++++++------ prisma/seed.js | 28 ++++++++++++++------ src/controllers/user.js | 57 ++++++++++++++++++++++++++++++++++++---- src/domain/user.js | 58 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 21 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 5f2a05f2..3374458c 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -134,7 +134,7 @@ paths: tags: - user summary: Update a user - description: Only users with a TEACHER role can update the cohortId or role. Users with Students role can only update their own details. + description: Only users with a TEACHER role can update the cohort_id or role. Users with Students role can only update their own details. operationId: userUpdate security: - bearerAuth: [] @@ -234,7 +234,7 @@ paths: properties: date: type: string - cohortId: + cohort_id: type: integer lines: type: array @@ -341,7 +341,7 @@ components: type: string role: type: string - cohortId: + cohort_id: type: integer firstName: type: string @@ -367,6 +367,12 @@ components: type: string password: type: string + mobile: + type: string + specialism: + type: string + imageUrl: + type: string UpdateUser: type: object @@ -375,7 +381,7 @@ components: type: string password: type: string - cohortId: + cohort_id: type: integer role: type: string @@ -387,6 +393,12 @@ components: type: string githubUrl: type: string + mobile: + type: string + specialism: + type: string + imageUrl: + type: string Posts: type: object @@ -416,7 +428,7 @@ components: properties: id: type: integer - cohortId: + cohort_id: type: integer role: type: string @@ -445,7 +457,7 @@ components: type: integer email: type: string - cohortId: + cohort_id: type: integer role: type: string @@ -457,6 +469,12 @@ components: type: string githubUrl: type: string + mobile: + type: string + specialism: + type: string + imageUrl: + type: string login: type: object properties: @@ -480,7 +498,7 @@ components: type: integer email: type: string - cohortId: + cohort_id: type: integer role: type: string @@ -513,7 +531,7 @@ components: properties: id: type: integer - cohortId: + cohort_id: type: integer date: type: string diff --git a/prisma/seed.js b/prisma/seed.js index 21684795..83db2c47 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -6,13 +6,16 @@ async function seed() { const cohort = await createCohort() const student = await createUser( - 'student@test.com', - 'Testpassword1!', - cohort.id, - 'Joe', - 'Bloggs', - 'Hello, world!', - 'student1' + 'student@test.com', // email + 'Testpassword1!', // password + cohort.id, // id + 'Joe', // first + 'Bloggs', // last + 'Hello, world!', //bio + 'student1', // url + '123', // mobile + 'pro', // spec + 'boolean.co.uk' // url ) const teacher = await createUser( 'teacher@test.com', @@ -22,6 +25,9 @@ async function seed() { 'Sanchez', 'Hello there!', 'teacher1', + '123', + 'noob', + 'boolean.co.uk', 'TEACHER' ) @@ -65,6 +71,9 @@ async function createUser( lastName, bio, githubUrl, + mobile, + specialism, + imageUrl, role = 'STUDENT' ) { const user = await prisma.user.create({ @@ -78,7 +87,10 @@ async function createUser( firstName, lastName, bio, - githubUrl + githubUrl, + mobile, + specialism, + imageUrl } } }, diff --git a/src/controllers/user.js b/src/controllers/user.js index 40ff0f1c..4228c2f4 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -56,12 +56,59 @@ export const getAll = async (req, res) => { return sendDataResponse(res, 200, { users: formattedUsers }) } +/** + * Updates a user by ID + * @param {import('express').Request} req Express request object + * @param {import('express').Response} res Express response object + */ export const updateById = async (req, res) => { - const { cohort_id: cohortId } = req.body + try { + const userId = parseInt(req.params.id) - if (!cohortId) { - return sendDataResponse(res, 400, { cohort_id: 'Cohort ID is required' }) - } + if (isNaN(userId)) { + return res.status(400).json({ + error: 'Invalid user ID' + }) + } + + const existingUser = await User.findById(userId) + if (!existingUser) { + return res.status(404).json({ + error: 'User not found' + }) + } - return sendDataResponse(res, 201, { user: { cohort_id: cohortId } }) + const updates = req.body + const allowedUpdates = [ + 'firstName', + 'lastName', + 'email', + 'bio', + 'githubUrl', + 'mobile', + 'specialism', + 'imageUrl', + 'cohortId' + ] + + allowedUpdates.forEach((field) => { + if (updates[field] !== undefined) { + existingUser[field] = updates[field] + } + }) + + if (updates.password) { + existingUser.passwordHash = await bcrypt.hash(updates.password, 8) + } + + const updatedUser = await existingUser.update() + + return res.json(updatedUser.toJSON()) + } catch (error) { + console.error('Error updating user:', error) + return res.status(500).json({ + error: 'Internal server error', + message: error.message + }) + } } diff --git a/src/domain/user.js b/src/domain/user.js index f947a44a..4e102603 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -196,4 +196,62 @@ export default class User { return foundUsers.map((user) => User.fromDb(user)) } + + /** + * Updates the user in the database with current instance values + * @returns {Promise} Updated user instance + */ + async update() { + const data = { + email: this.email, + role: this.role + } + + if (this.passwordHash) { + data.password = this.passwordHash + } + + data.profile = { + upsert: { + create: { + firstName: this.firstName, + lastName: this.lastName, + bio: this.bio, + githubUrl: this.githubUrl, + mobile: this.mobile, + specialism: this.specialism, + imageUrl: this.imageUrl + }, + update: { + firstName: this.firstName, + lastName: this.lastName, + bio: this.bio, + githubUrl: this.githubUrl, + mobile: this.mobile, + specialism: this.specialism, + imageUrl: this.imageUrl + } + } + } + + if (this.cohortId) { + data.cohort = { + connect: { + id: this.cohortId + } + } + } + + const updatedUser = await dbClient.user.update({ + where: { + id: this.id + }, + data, + include: { + profile: true + } + }) + + return User.fromDb(updatedUser) + } } From 9322b48431ca82ad47b3c91c8a94ea6c572481db Mon Sep 17 00:00:00 2001 From: gremble0 Date: Mon, 28 Oct 2024 14:55:58 +0100 Subject: [PATCH 09/28] Use sendMessageRequest for errors --- prisma/seed.js | 2 +- src/controllers/user.js | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/prisma/seed.js b/prisma/seed.js index 83db2c47..ec2e4862 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -28,7 +28,7 @@ async function seed() { '123', 'noob', 'boolean.co.uk', - 'TEACHER' + 'TEACHER' // role ) await createPost(student.id, 'My first post!') diff --git a/src/controllers/user.js b/src/controllers/user.js index 4228c2f4..1150f7fa 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -8,7 +8,7 @@ export const create = async (req, res) => { const existingUser = await User.findByEmail(userToCreate.email) if (existingUser) { - return sendDataResponse(res, 400, { email: 'Email already in use' }) + return sendMessageResponse(res, 400, 'Email already in use') } const createdUser = await userToCreate.save() @@ -26,7 +26,7 @@ export const getById = async (req, res) => { const foundUser = await User.findById(id) if (!foundUser) { - return sendDataResponse(res, 404, { id: 'User not found' }) + return sendMessageResponse(res, 404, 'User not found') } return sendDataResponse(res, 200, foundUser) @@ -66,16 +66,12 @@ export const updateById = async (req, res) => { const userId = parseInt(req.params.id) if (isNaN(userId)) { - return res.status(400).json({ - error: 'Invalid user ID' - }) + return sendMessageResponse(res, 400, 'Invalid user ID') } const existingUser = await User.findById(userId) if (!existingUser) { - return res.status(404).json({ - error: 'User not found' - }) + return sendMessageResponse(res, 404, 'User not found') } const updates = req.body @@ -103,12 +99,12 @@ export const updateById = async (req, res) => { const updatedUser = await existingUser.update() - return res.json(updatedUser.toJSON()) + // return res.json(updatedUser.toJSON()) + return sendDataResponse(res, 200, updatedUser.toJSON()) } catch (error) { console.error('Error updating user:', error) - return res.status(500).json({ - error: 'Internal server error', - message: error.message + return sendDataResponse(res, 400, { + error: 'Internal server error' }) } } From 8ca1979e09fa98212b514fed537b49eefc784299 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Mon, 28 Oct 2024 15:24:26 +0100 Subject: [PATCH 10/28] Add /users delete endpoint --- src/controllers/user.js | 31 +++++++++++++++++++++++++++---- src/domain/user.js | 6 ++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 1150f7fa..d843f943 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -1,6 +1,11 @@ import User from '../domain/user.js' import { sendDataResponse, sendMessageResponse } from '../utils/responses.js' +/** + * Updates a user by ID + * @param {import('express').Request} req Express request object + * @param {import('express').Response} res Express response object + */ export const create = async (req, res) => { const userToCreate = await User.fromJson(req.body) @@ -19,6 +24,11 @@ export const create = async (req, res) => { } } +/** + * Updates a user by ID + * @param {import('express').Request} req Express request object + * @param {import('express').Response} res Express response object + */ export const getById = async (req, res) => { const id = parseInt(req.params.id) @@ -99,12 +109,25 @@ export const updateById = async (req, res) => { const updatedUser = await existingUser.update() - // return res.json(updatedUser.toJSON()) return sendDataResponse(res, 200, updatedUser.toJSON()) } catch (error) { console.error('Error updating user:', error) - return sendDataResponse(res, 400, { - error: 'Internal server error' - }) + return sendMessageResponse(res, 400, 'Internal server error') } } + +/** + * Updates a user by ID + * @param {import('express').Request} req Express request object + * @param {import('express').Response} res Express response object + */ +export const removeById = async (req, res) => { + const id = parseInt(req.params.id) + const deleted = await User.deleteById(id) + + if (!deleted) { + return sendMessageResponse(res, 404, 'User not found') + } + + return sendDataResponse(res, 200, deleted) +} diff --git a/src/domain/user.js b/src/domain/user.js index 4e102603..70435a29 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -254,4 +254,10 @@ export default class User { return User.fromDb(updatedUser) } + + static async deleteById(id) { + return dbClient.user.delete({ + where: { id } + }) + } } From 51c62aa7fe702c24680a2abe67cb21afc2c88516 Mon Sep 17 00:00:00 2001 From: ISecka Date: Mon, 28 Oct 2024 16:26:53 +0100 Subject: [PATCH 11/28] Get all posts still under development, but working code, added an exapmle of how create post can look --- src/controllers/post.js | 41 +++++++++++++++++++++++++++++++++-------- src/domain/post.js | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index f435ef37..b5305b11 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -1,7 +1,7 @@ import { sendDataResponse } from '../utils/responses.js' import Post from '../domain/post.js' -export const create = async (req, res) => { +/*export const create = async (req, res) => { const { content } = req.body if (!content) { @@ -9,24 +9,49 @@ export const create = async (req, res) => { } return sendDataResponse(res, 201, { post: { id: 1, content } }) -} +}*/ export const getAll = async (req, res) => { - - try { - // Use the Post class to retrieve all posts const foundPosts = await Post.findAll() - - // Format the posts for the response const formattedPosts = foundPosts.map((post) => post.toJSON()) - return sendDataResponse(res, 200, { posts: formattedPosts }) + return sendDataResponse(res, 200, { + posts: formattedPosts + }) } catch (error) { console.error('Error fetching posts:', error) return sendDataResponse(res, 500, { error: 'Failed to fetch posts' }) } } + export const create = async (req, res) => { + const { userId, content } = req.body + + + if (!userId || !content) { + return sendDataResponse(res, 400, { + error: 'User ID and content are required' + }) + } + + try { + + const newPost = await Post.fromJson({ content, userId }) + const savedPost = await newPost.save() + + + const formattedPost = savedPost.toJSON() + + return sendDataResponse(res, 201, { + status: 'success', + post: formattedPost + }) + } catch (error) { + console.error('Error creating post:', error) + return sendDataResponse(res, 500, { error: 'Failed to create post' }) + } + } + diff --git a/src/domain/post.js b/src/domain/post.js index 16ab3c3c..d799f16e 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -12,39 +12,49 @@ export default class Post { post.content, post.createdAt, post.updatedAt, - post.authorId + post.authorId, + post.user ) } static async fromJson(json) { const { content, authorId } = json return new Post( - null, // id will be generated by the database + null, content, - null, // createdAt will be set by the database + null, null, // updatedAt will also be set by the database authorId ) } - constructor(id, content, createdAt, updatedAt, authorId) { + constructor(id, content, createdAt, updatedAt, authorId, user = null) { this.id = id this.content = content this.createdAt = createdAt this.updatedAt = updatedAt this.authorId = authorId + this.user = user } toJSON() { return { - post: { + id: this.id, content: this.content, createdAt: this.createdAt, updatedAt: this.updatedAt, - authorId: this.authorId + author: { + id: this.user.id, + cohortId: this.user.cohort?.id || null, + firstName: this.user.profile?.firstName || null, + lastName: this.user.profile?.lastName || null, + email: this.user.email, + bio: this.user.profile?.bio || null, + githubUrl: this.user.profile?.githubUrl || null, + role: this.user.role + } } - } } /** @@ -62,7 +72,10 @@ export default class Post { } const createdPost = await dbClient.post.create({ - data + data, + include: { + author: true, + } }) return Post.fromDb(createdPost) @@ -95,7 +108,16 @@ export default class Post { } static async _findMany(key, value) { - const query = {} + const query = { + include: { + user: { + include: { + profile: true, + cohort: true + } + } + } + } if (key !== undefined && value !== undefined) { query.where = { From aeb75e02790ea69130748768e86231e7c78b687c Mon Sep 17 00:00:00 2001 From: Daniil Panfilov Date: Mon, 28 Oct 2024 17:51:36 +0100 Subject: [PATCH 12/28] editted the code slightly so it looks better --- src/controllers/post.js | 80 +++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index b5305b11..7fd4ab59 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -1,57 +1,41 @@ import { sendDataResponse } from '../utils/responses.js' import Post from '../domain/post.js' -/*export const create = async (req, res) => { - const { content } = req.body - - if (!content) { - return sendDataResponse(res, 400, { content: 'Must provide content' }) - } - - return sendDataResponse(res, 201, { post: { id: 1, content } }) -}*/ - export const getAll = async (req, res) => { + try { + const foundPosts = await Post.findAll() + const formattedPosts = foundPosts.map((post) => post.toJSON()) + + return sendDataResponse(res, 200, { + posts: formattedPosts + }) + } catch (error) { + console.error('Error fetching posts:', error) + return sendDataResponse(res, 500, { error: 'Failed to fetch posts' }) + } +} - try { - const foundPosts = await Post.findAll() - const formattedPosts = foundPosts.map((post) => post.toJSON()) - - return sendDataResponse(res, 200, { - posts: formattedPosts - }) - } catch (error) { - console.error('Error fetching posts:', error) - return sendDataResponse(res, 500, { error: 'Failed to fetch posts' }) - } - } - - export const create = async (req, res) => { - const { userId, content } = req.body - - - if (!userId || !content) { - return sendDataResponse(res, 400, { - error: 'User ID and content are required' - }) - } - - try { - - const newPost = await Post.fromJson({ content, userId }) - const savedPost = await newPost.save() +export const create = async (req, res) => { + const { userId, content } = req.body - - const formattedPost = savedPost.toJSON() + if (!userId || !content) { + return sendDataResponse(res, 400, { + error: 'User ID and content are required' + }) + } - return sendDataResponse(res, 201, { - status: 'success', - post: formattedPost - }) - } catch (error) { - console.error('Error creating post:', error) - return sendDataResponse(res, 500, { error: 'Failed to create post' }) - } - } + try { + const newPost = await Post.fromJson({ content, userId }) + const savedPost = await newPost.save() + const formattedPost = savedPost.toJSON() + return sendDataResponse(res, 201, { + status: 'success', + post: formattedPost + }) + } catch (error) { + console.error('Error creating post:', error) + return sendDataResponse(res, 500, { error: 'Failed to create post' }) + } +} From 6636847d5c546befc32f0bd9dc1279d27e7f5cc9 Mon Sep 17 00:00:00 2001 From: Daniil Panfilov Date: Mon, 28 Oct 2024 21:00:03 +0100 Subject: [PATCH 13/28] create method creates post now in db, there is connection between database and endpoints now, need to tweek the response so the password is not showing and split some of the function to domain --- src/controllers/post.js | 32 +++++++++++++++++---------- src/domain/post.js | 49 ++++++++++++++++++++--------------------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 7fd4ab59..15c8ed5c 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -1,5 +1,6 @@ import { sendDataResponse } from '../utils/responses.js' import Post from '../domain/post.js' +import dbClient from '../utils/dbClient.js' export const getAll = async (req, res) => { try { @@ -7,7 +8,7 @@ export const getAll = async (req, res) => { const formattedPosts = foundPosts.map((post) => post.toJSON()) return sendDataResponse(res, 200, { - posts: formattedPosts + somasdfething: formattedPosts }) } catch (error) { console.error('Error fetching posts:', error) @@ -16,24 +17,31 @@ export const getAll = async (req, res) => { } export const create = async (req, res) => { - const { userId, content } = req.body + const { content } = req.body + const userId = 1 - if (!userId || !content) { + if (!content) { return sendDataResponse(res, 400, { - error: 'User ID and content are required' + error: 'Content is required' }) } try { - const newPost = await Post.fromJson({ content, userId }) - const savedPost = await newPost.save() - - const formattedPost = savedPost.toJSON() - - return sendDataResponse(res, 201, { - status: 'success', - post: formattedPost + const newPost = await dbClient.post.create({ + data: { + content, + user: { + connect: { + id: userId + } + } + }, + include: { + user: true + } }) + + return sendDataResponse(res, 201, newPost) } catch (error) { console.error('Error creating post:', error) return sendDataResponse(res, 500, { error: 'Failed to create post' }) diff --git a/src/domain/post.js b/src/domain/post.js index d799f16e..c3b2475b 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -20,9 +20,9 @@ export default class Post { static async fromJson(json) { const { content, authorId } = json return new Post( - null, + null, content, - null, + null, null, // updatedAt will also be set by the database authorId ) @@ -39,22 +39,21 @@ export default class Post { toJSON() { return { - - id: this.id, - content: this.content, - createdAt: this.createdAt, - updatedAt: this.updatedAt, - author: { - id: this.user.id, - cohortId: this.user.cohort?.id || null, - firstName: this.user.profile?.firstName || null, - lastName: this.user.profile?.lastName || null, - email: this.user.email, - bio: this.user.profile?.bio || null, - githubUrl: this.user.profile?.githubUrl || null, - role: this.user.role - } + id: this.id, + content: this.content, + createdAt: this.createdAt, + updatedAt: this.updatedAt, + author: { + id: this.user.id, + cohortId: this.user.cohort?.id || null, + firstName: this.user.profile?.firstName || null, + lastName: this.user.profile?.lastName || null, + email: this.user.email, + bio: this.user.profile?.bio || null, + githubUrl: this.user.profile?.githubUrl || null, + role: this.user.role } + } } /** @@ -74,7 +73,7 @@ export default class Post { const createdPost = await dbClient.post.create({ data, include: { - author: true, + author: true } }) @@ -109,14 +108,14 @@ export default class Post { static async _findMany(key, value) { const query = { - include: { - user: { - include: { - profile: true, - cohort: true - } - } + include: { + user: { + include: { + profile: true, + cohort: true + } } + } } if (key !== undefined && value !== undefined) { From 65108fee370214e855126bbc66aa45786f7b068e Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 09:15:38 +0100 Subject: [PATCH 14/28] Add delete to docs, add routing for delete users --- docs/openapi.yml | 54 +++++++++++++++++++++++++++++++++++++++++ src/controllers/user.js | 2 +- src/domain/user.js | 12 +++++++-- src/routes/user.js | 10 +++++++- 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 3374458c..dd176732 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -164,6 +164,60 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + delete: + summary: Delete a user by ID + description: Removes a user from the system using their unique identifier + operationId: removeById + tags: + - user + parameters: + - name: id + in: path + required: true + description: Unique identifier of the user + schema: + type: integer + responses: + '200': + description: User successfully deleted + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: [success] + example: success + data: + type: object + properties: + id: + type: integer + example: 123 + email: + type: string + example: "user@example.com" + required: + - status + - data + '404': + description: User not found + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: [fail] + example: fail + message: + type: string + example: "User not found" + required: + - status + - message /posts: post: tags: diff --git a/src/controllers/user.js b/src/controllers/user.js index d843f943..0ef97c36 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -121,7 +121,7 @@ export const updateById = async (req, res) => { * @param {import('express').Request} req Express request object * @param {import('express').Response} res Express response object */ -export const removeById = async (req, res) => { +export const deleteById = async (req, res) => { const id = parseInt(req.params.id) const deleted = await User.deleteById(id) diff --git a/src/domain/user.js b/src/domain/user.js index 70435a29..6395721e 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -256,8 +256,16 @@ export default class User { } static async deleteById(id) { - return dbClient.user.delete({ - where: { id } + return dbClient.$transaction(async (tx) => { + // Then delete the user + await tx.user.delete({ + where: { id } + }) + + // First delete the associated profile + return await tx.profile.delete({ + where: { userId: id } + }) }) } } diff --git a/src/routes/user.js b/src/routes/user.js index 9f63d162..c0e6e0e0 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -1,5 +1,11 @@ import { Router } from 'express' -import { create, getById, getAll, updateById } from '../controllers/user.js' +import { + create, + getById, + getAll, + updateById, + deleteById +} from '../controllers/user.js' import { validateAuthentication, validateTeacherRole @@ -11,5 +17,7 @@ router.post('/', create) router.get('/', validateAuthentication, getAll) router.get('/:id', validateAuthentication, getById) router.patch('/:id', validateAuthentication, validateTeacherRole, updateById) +// TODO: validate +router.delete('/:id', deleteById) export default router From fb713ac2cc6235c5927ada8a89e0233cd5c5b185 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 09:29:43 +0100 Subject: [PATCH 15/28] Add cascading deletes for delete users --- .../migration.sql | 5 +++++ .../migration.sql | 17 +++++++++++++++++ prisma/schema.prisma | 8 ++++---- src/domain/user.js | 13 +++++-------- 4 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 prisma/migrations/20241029082502_add_cascade_delete/migration.sql create mode 100644 prisma/migrations/20241029082758_add_cascade_deletes/migration.sql diff --git a/prisma/migrations/20241029082502_add_cascade_delete/migration.sql b/prisma/migrations/20241029082502_add_cascade_delete/migration.sql new file mode 100644 index 00000000..fef1142c --- /dev/null +++ b/prisma/migrations/20241029082502_add_cascade_delete/migration.sql @@ -0,0 +1,5 @@ +-- DropForeignKey +ALTER TABLE "Profile" DROP CONSTRAINT "Profile_userId_fkey"; + +-- AddForeignKey +ALTER TABLE "Profile" ADD CONSTRAINT "Profile_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241029082758_add_cascade_deletes/migration.sql b/prisma/migrations/20241029082758_add_cascade_deletes/migration.sql new file mode 100644 index 00000000..81021c7c --- /dev/null +++ b/prisma/migrations/20241029082758_add_cascade_deletes/migration.sql @@ -0,0 +1,17 @@ +-- DropForeignKey +ALTER TABLE "DeliveryLog" DROP CONSTRAINT "DeliveryLog_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "DeliveryLogLine" DROP CONSTRAINT "DeliveryLogLine_logId_fkey"; + +-- DropForeignKey +ALTER TABLE "Post" DROP CONSTRAINT "Post_userId_fkey"; + +-- AddForeignKey +ALTER TABLE "Post" ADD CONSTRAINT "Post_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DeliveryLog" ADD CONSTRAINT "DeliveryLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DeliveryLogLine" ADD CONSTRAINT "DeliveryLogLine_logId_fkey" FOREIGN KEY ("logId") REFERENCES "DeliveryLog"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3d62bd02..146d437d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -31,7 +31,7 @@ model User { model Profile { id Int @id @default(autoincrement()) userId Int @unique - user User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) firstName String lastName String bio String? @@ -54,14 +54,14 @@ model Post { id Int @id @default(autoincrement()) content String userId Int - user User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model DeliveryLog { id Int @id @default(autoincrement()) date DateTime userId Int - user User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) cohortId Int cohort Cohort @relation(fields: [cohortId], references: [id]) lines DeliveryLogLine[] @@ -71,5 +71,5 @@ model DeliveryLogLine { id Int @id @default(autoincrement()) content String logId Int - log DeliveryLog @relation(fields: [logId], references: [id]) + log DeliveryLog @relation(fields: [logId], references: [id], onDelete: Cascade) } diff --git a/src/domain/user.js b/src/domain/user.js index 6395721e..3658c130 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -257,14 +257,11 @@ export default class User { static async deleteById(id) { return dbClient.$transaction(async (tx) => { - // Then delete the user - await tx.user.delete({ - where: { id } - }) - - // First delete the associated profile - return await tx.profile.delete({ - where: { userId: id } + return await tx.user.delete({ + where: { id }, + include: { + profile: true + } }) }) } From 3cd4ecdc7a9a60edfa1c689444bfab69e065d63d Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 09:42:01 +0100 Subject: [PATCH 16/28] Add verification for delete users, update docs --- docs/openapi.yml | 44 +++++++++----------------------------------- src/routes/user.js | 3 +-- 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index dd176732..c4227187 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -165,11 +165,13 @@ paths: schema: $ref: '#/components/schemas/Error' delete: - summary: Delete a user by ID - description: Removes a user from the system using their unique identifier - operationId: removeById tags: - user + summary: Delete a user by ID + description: Removes a user from the system using their unique identifier + operationId: deleteById + security: + - bearerAuth: [] parameters: - name: id in: path @@ -178,46 +180,18 @@ paths: schema: type: integer responses: - '200': - description: User successfully deleted + '201': + description: Successful operation content: application/json: schema: - type: object - properties: - status: - type: string - enum: [success] - example: success - data: - type: object - properties: - id: - type: integer - example: 123 - email: - type: string - example: "user@example.com" - required: - - status - - data + $ref: '#/components/schemas/CreatedUser' '404': description: User not found content: application/json: schema: - type: object - properties: - status: - type: string - enum: [fail] - example: fail - message: - type: string - example: "User not found" - required: - - status - - message + $ref: '#/components/schemas/Error' /posts: post: tags: diff --git a/src/routes/user.js b/src/routes/user.js index c0e6e0e0..ec627cc7 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -17,7 +17,6 @@ router.post('/', create) router.get('/', validateAuthentication, getAll) router.get('/:id', validateAuthentication, getById) router.patch('/:id', validateAuthentication, validateTeacherRole, updateById) -// TODO: validate -router.delete('/:id', deleteById) +router.delete('/:id', validateAuthentication, validateTeacherRole, deleteById) export default router From 42a49655b6798fac72ce4fb2abd6afb3df2b994d Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 09:46:33 +0100 Subject: [PATCH 17/28] Fix error handling for delete /users --- src/controllers/user.js | 9 ++++----- src/domain/user.js | 12 +++++------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 0ef97c36..ab65d19f 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -123,11 +123,10 @@ export const updateById = async (req, res) => { */ export const deleteById = async (req, res) => { const id = parseInt(req.params.id) - const deleted = await User.deleteById(id) - - if (!deleted) { + try { + return sendDataResponse(res, 200, await User.deleteById(id)) + } catch (error) { + console.error('Error deleting user:', error) return sendMessageResponse(res, 404, 'User not found') } - - return sendDataResponse(res, 200, deleted) } diff --git a/src/domain/user.js b/src/domain/user.js index 3658c130..e74c9aba 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -256,13 +256,11 @@ export default class User { } static async deleteById(id) { - return dbClient.$transaction(async (tx) => { - return await tx.user.delete({ - where: { id }, - include: { - profile: true - } - }) + return await dbClient.user.delete({ + where: { id }, + include: { + profile: true + } }) } } From 52031f96911182a37a43d4a0366198f95d5d523a Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 09:55:46 +0100 Subject: [PATCH 18/28] Parse object to and from db for delete /users --- src/controllers/user.js | 4 +++- src/domain/user.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index ab65d19f..08dee1d6 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -124,7 +124,9 @@ export const updateById = async (req, res) => { export const deleteById = async (req, res) => { const id = parseInt(req.params.id) try { - return sendDataResponse(res, 200, await User.deleteById(id)) + const deleted = await User.deleteById(id) + + return sendDataResponse(res, 200, deleted.toJSON()) } catch (error) { console.error('Error deleting user:', error) return sendMessageResponse(res, 404, 'User not found') diff --git a/src/domain/user.js b/src/domain/user.js index e74c9aba..1f77e2e1 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -256,11 +256,13 @@ export default class User { } static async deleteById(id) { - return await dbClient.user.delete({ + const deletedUser = await dbClient.user.delete({ where: { id }, include: { profile: true } }) + + return User.fromDb(deletedUser) } } From e283e2e12165c1d156579fd52a2b7d56b5475645 Mon Sep 17 00:00:00 2001 From: Daniil Panfilov Date: Tue, 29 Oct 2024 10:01:06 +0100 Subject: [PATCH 19/28] create and get works, refined code sligtly --- src/controllers/post.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 15c8ed5c..be4c33ed 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -7,12 +7,10 @@ export const getAll = async (req, res) => { const foundPosts = await Post.findAll() const formattedPosts = foundPosts.map((post) => post.toJSON()) - return sendDataResponse(res, 200, { - somasdfething: formattedPosts - }) + return sendDataResponse(res, 200, formattedPosts) } catch (error) { console.error('Error fetching posts:', error) - return sendDataResponse(res, 500, { error: 'Failed to fetch posts' }) + return sendDataResponse(res, 401, { error: 'fail' }) } } @@ -21,7 +19,7 @@ export const create = async (req, res) => { const userId = 1 if (!content) { - return sendDataResponse(res, 400, { + return sendDataResponse(res, 404, { error: 'Content is required' }) } @@ -44,6 +42,6 @@ export const create = async (req, res) => { return sendDataResponse(res, 201, newPost) } catch (error) { console.error('Error creating post:', error) - return sendDataResponse(res, 500, { error: 'Failed to create post' }) + return sendDataResponse(res, 400, { error: 'Failed to create post' }) } } From 216f23183331e0bb8e7f7d2bc292dcd6a555bf86 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 11:11:47 +0100 Subject: [PATCH 20/28] Rename CreatedUser -> User in openapi docs --- docs/openapi.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index c4227187..5de80d5c 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -31,7 +31,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreatedUser' + $ref: '#/components/schemas/User' get: tags: - user @@ -157,7 +157,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreatedUser' + $ref: '#/components/schemas/User' '401': description: fail content: @@ -185,7 +185,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreatedUser' + $ref: '#/components/schemas/User' '404': description: User not found content: @@ -471,7 +471,7 @@ components: profileImageUrl: type: string - CreatedUser: + User: type: object properties: status: From a90fc38df6c5c7429b6b7b9949688e252828161e Mon Sep 17 00:00:00 2001 From: Thomas Aleksander Flier Date: Tue, 29 Oct 2024 11:26:56 +0100 Subject: [PATCH 21/28] added search functionality for getAll, where one can pass a name as an argument --- docs/openapi.yml | 6 ++--- src/controllers/user.js | 11 ++++++--- src/domain/user.js | 55 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index dd176732..c97bf438 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -35,15 +35,15 @@ paths: get: tags: - user - summary: Get all users by first name if provided + summary: Get all users by name if provided description: '' operationId: getAllUsers security: - bearerAuth: [] parameters: - - name: firstName + - name: name in: query - description: Search all users by first name if provided (case-sensitive and exact string matches only) + description: Search all users by name if provided (case-sensitive and exact string matches only) can be full name or only last or first name schema: type: string responses: diff --git a/src/controllers/user.js b/src/controllers/user.js index 0ef97c36..ea21cf1c 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -46,17 +46,20 @@ export const getById = async (req, res) => { } export const getAll = async (req, res) => { - // eslint-disable-next-line camelcase - const { first_name: firstName } = req.query + const { name } = req.query let foundUsers - if (firstName) { - foundUsers = await User.findManyByFirstName(firstName) + if (name) { + foundUsers = await User.findByName(name) } else { foundUsers = await User.findAll() } + if (!foundUsers || foundUsers.length === 0) { + return sendMessageResponse(res, 404, 'User not found') + } + const formattedUsers = foundUsers.map((user) => { return { ...user.toJSON().user diff --git a/src/domain/user.js b/src/domain/user.js index 3658c130..0ad3d935 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -1,3 +1,4 @@ +/* eslint-disable prettier/prettier */ import dbClient from '../utils/dbClient.js' import bcrypt from 'bcrypt' @@ -156,6 +157,41 @@ export default class User { return User._findMany('firstName', firstName) } + static async findManyByLastName(lastName) { + return User._findMany('lastName', lastName) + } + + static async findByName(name) { + // Split the name into first and last name if it contains a space + let [firstName, lastName] = name.split(' '); + + // If it's a full name + if (lastName) { + console.log("firstName: ", firstName, "lastName: ", lastName); + let users = await User._findWithFullName({ + firstName: firstName, + lastName: lastName + }); + console.log("users: ", users); + if (users.length > 0) { + return users; + } + } + console.log("only one name") + // If it's a single name + let users = await this.findManyByFirstName(name); + if (users.length > 0) { + return users; + } else { + users = await this.findManyByLastName(name); + if (users.length > 0) { + return users; + } + } + + return null; + } + static async findAll() { return User._findMany() } @@ -197,6 +233,24 @@ export default class User { return foundUsers.map((user) => User.fromDb(user)) } + static async _findWithFullName(where) { + const query = { + include: { + profile: true + } + }; + + if (where) { + query.where = { + profile: where + }; + } + + const foundUsers = await dbClient.user.findMany(query); + + return foundUsers.map((user) => User.fromDb(user)); +} + /** * Updates the user in the database with current instance values * @returns {Promise} Updated user instance @@ -266,3 +320,4 @@ export default class User { }) } } + From 379be804793b3dad1a92e782d38176488429673d Mon Sep 17 00:00:00 2001 From: Thomas Aleksander Flier Date: Tue, 29 Oct 2024 11:40:03 +0100 Subject: [PATCH 22/28] removed console.log(s) --- src/domain/user.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/domain/user.js b/src/domain/user.js index 0ad3d935..7580c4cd 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -167,17 +167,14 @@ export default class User { // If it's a full name if (lastName) { - console.log("firstName: ", firstName, "lastName: ", lastName); let users = await User._findWithFullName({ firstName: firstName, lastName: lastName }); - console.log("users: ", users); if (users.length > 0) { return users; } } - console.log("only one name") // If it's a single name let users = await this.findManyByFirstName(name); if (users.length > 0) { From 258a1396d919224741768562a3ac4605685caa36 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 11:47:47 +0100 Subject: [PATCH 23/28] Add dates to user model --- .../20241029103452_add_dates/migration.sql | 3 + prisma/schema.prisma | 5 +- prisma/seed.js | 12 ++- src/domain/user.js | 75 +++++++++---------- 4 files changed, 49 insertions(+), 46 deletions(-) create mode 100644 prisma/migrations/20241029103452_add_dates/migration.sql diff --git a/prisma/migrations/20241029103452_add_dates/migration.sql b/prisma/migrations/20241029103452_add_dates/migration.sql new file mode 100644 index 00000000..995b3ff4 --- /dev/null +++ b/prisma/migrations/20241029103452_add_dates/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Profile" ADD COLUMN "endDate" TIMESTAMP(3), +ADD COLUMN "startDate" TIMESTAMP(3); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 146d437d..91db6a3e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -39,9 +39,8 @@ model Profile { mobile String? specialism String? imageUrl String? - // TODO: - // startDate - // endDate + startDate DateTime? + endDate DateTime? } model Cohort { diff --git a/prisma/seed.js b/prisma/seed.js index ec2e4862..403b050d 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -15,7 +15,9 @@ async function seed() { 'student1', // url '123', // mobile 'pro', // spec - 'boolean.co.uk' // url + 'boolean.co.uk', // url + new Date('2024-01-01'), // startDate + new Date('2024-06-01') // endDate ) const teacher = await createUser( 'teacher@test.com', @@ -28,6 +30,8 @@ async function seed() { '123', 'noob', 'boolean.co.uk', + null, + null, 'TEACHER' // role ) @@ -74,6 +78,8 @@ async function createUser( mobile, specialism, imageUrl, + startDate, + endDate, role = 'STUDENT' ) { const user = await prisma.user.create({ @@ -90,7 +96,9 @@ async function createUser( githubUrl, mobile, specialism, - imageUrl + imageUrl, + startDate, + endDate } } }, diff --git a/src/domain/user.js b/src/domain/user.js index 1f77e2e1..71cc6480 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -22,6 +22,8 @@ export default class User { user.profile?.mobile, user.profile?.specialism, user.profile?.imageUrl, + user.profile?.startDate, + user.profile?.endDate, user.password, user.role ) @@ -29,32 +31,21 @@ export default class User { static async fromJson(json) { // TODO: get cohortId - // eslint-disable-next-line camelcase - const { - firstName, - lastName, - email, - biography, - githubUrl, - mobile, - specialism, - imageUrl, - password - } = json - - const passwordHash = await bcrypt.hash(password, 8) + const passwordHash = await bcrypt.hash(json.password, 8) return new User( null, null, - firstName, - lastName, - email, - biography, - githubUrl, - mobile, - specialism, - imageUrl, + json.firstName, + json.lastName, + json.email, + json.biography, + json.githubUrl, + json.mobile, + json.specialism, + json.imageUrl, + json.startDate, + json.endDate, passwordHash ) } @@ -70,6 +61,8 @@ export default class User { mobile, specialism, imageUrl, + startDate = null, + endDate = null, passwordHash = null, role = 'STUDENT' ) { @@ -83,6 +76,8 @@ export default class User { this.mobile = mobile this.specialism = specialism this.imageUrl = imageUrl + this.startDate = startDate + this.endDate = endDate this.passwordHash = passwordHash this.role = role } @@ -100,7 +95,9 @@ export default class User { githubUrl: this.githubUrl, mobile: this.mobile, specialism: this.specialism, - imageUrl: this.imageUrl + imageUrl: this.imageUrl, + startDate: this.startDate, + endDate: this.endDate } } } @@ -211,26 +208,22 @@ export default class User { data.password = this.passwordHash } + const updated = { + firstName: this.firstName, + lastName: this.lastName, + bio: this.bio, + githubUrl: this.githubUrl, + mobile: this.mobile, + specialism: this.specialism, + imageUrl: this.imageUrl, + startDate: this.startDate, + endDate: this.endDate + } + data.profile = { upsert: { - create: { - firstName: this.firstName, - lastName: this.lastName, - bio: this.bio, - githubUrl: this.githubUrl, - mobile: this.mobile, - specialism: this.specialism, - imageUrl: this.imageUrl - }, - update: { - firstName: this.firstName, - lastName: this.lastName, - bio: this.bio, - githubUrl: this.githubUrl, - mobile: this.mobile, - specialism: this.specialism, - imageUrl: this.imageUrl - } + create: updated, + update: updated } } From abb90cb1c49818b328845cb67874e1a40e72b694 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 11:57:07 +0100 Subject: [PATCH 24/28] githubUrl -> githubUsername --- docs/openapi.yml | 32 ++-------- .../migration.sql | 2 +- prisma/schema.prisma | 24 ++++---- prisma/seed.js | 6 +- src/controllers/user.js | 2 +- src/domain/user.js | 59 +++++++++---------- 6 files changed, 52 insertions(+), 73 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index d4a78041..8973b341 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -360,26 +360,6 @@ components: items: $ref: '#/components/schemas/User' - User: - type: object - properties: - id: - type: integer - email: - type: string - role: - type: string - cohort_id: - type: integer - firstName: - type: string - lastName: - type: string - bio: - type: string - githubUrl: - type: string - CreateUser: type: object properties: @@ -391,7 +371,7 @@ components: type: string bio: type: string - githubUrl: + githubUsername: type: string password: type: string @@ -419,7 +399,7 @@ components: type: string bio: type: string - githubUrl: + githubUsername: type: string mobile: type: string @@ -466,11 +446,11 @@ components: type: string bio: type: string - githubUrl: + githubUsername: type: string profileImageUrl: type: string - + User: type: object properties: @@ -495,7 +475,7 @@ components: type: string bio: type: string - githubUrl: + githubUsername: type: string mobile: type: string @@ -536,7 +516,7 @@ components: type: string bio: type: string - githubUrl: + githubUsername: type: string Error: type: object diff --git a/prisma/migrations/20220407150157_create_user_profile/migration.sql b/prisma/migrations/20220407150157_create_user_profile/migration.sql index ac6a38a8..e7b61c28 100644 --- a/prisma/migrations/20220407150157_create_user_profile/migration.sql +++ b/prisma/migrations/20220407150157_create_user_profile/migration.sql @@ -14,7 +14,7 @@ CREATE TABLE "Profile" ( "firstName" TEXT NOT NULL, "lastName" TEXT NOT NULL, "bio" TEXT, - "githubUrl" TEXT, + "githubUsername" TEXT, CONSTRAINT "Profile_pkey" PRIMARY KEY ("id") ); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 91db6a3e..8bf9b949 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -29,18 +29,18 @@ model User { } model Profile { - id Int @id @default(autoincrement()) - userId Int @unique - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - firstName String - lastName String - bio String? - githubUrl String? - mobile String? - specialism String? - imageUrl String? - startDate DateTime? - endDate DateTime? + id Int @id @default(autoincrement()) + userId Int @unique + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + firstName String + lastName String + bio String? + githubUsername String? + mobile String? + specialism String? + imageUrl String? + startDate DateTime? + endDate DateTime? } model Cohort { diff --git a/prisma/seed.js b/prisma/seed.js index 403b050d..79422b1d 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -11,7 +11,7 @@ async function seed() { cohort.id, // id 'Joe', // first 'Bloggs', // last - 'Hello, world!', //bio + 'Hello, world!', // bio 'student1', // url '123', // mobile 'pro', // spec @@ -74,7 +74,7 @@ async function createUser( firstName, lastName, bio, - githubUrl, + githubUsername, mobile, specialism, imageUrl, @@ -93,7 +93,7 @@ async function createUser( firstName, lastName, bio, - githubUrl, + githubUsername, mobile, specialism, imageUrl, diff --git a/src/controllers/user.js b/src/controllers/user.js index 9d9cc83f..3cd07a97 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -93,7 +93,7 @@ export const updateById = async (req, res) => { 'lastName', 'email', 'bio', - 'githubUrl', + 'githubUsername', 'mobile', 'specialism', 'imageUrl', diff --git a/src/domain/user.js b/src/domain/user.js index 2bb700e7..85f6bd17 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -8,7 +8,7 @@ export default class User { * take as inputs, what types they return, and other useful information that JS doesn't have built in * @tutorial https://www.valentinog.com/blog/jsdoc * - * @param { { id: int, cohortId: int, email: string, profile: { firstName: string, lastName: string, bio: string, githubUrl: string } } } user + * @param { { id: int, cohortId: int, email: string, profile: { firstName: string, lastName: string, bio: string, githubUsername: string } } } user * @returns {User} */ static fromDb(user) { @@ -19,7 +19,7 @@ export default class User { user.profile?.lastName, user.email, user.profile?.bio, - user.profile?.githubUrl, // TODO: username + user.profile?.githubUsername, user.profile?.mobile, user.profile?.specialism, user.profile?.imageUrl, @@ -41,7 +41,7 @@ export default class User { json.lastName, json.email, json.biography, - json.githubUrl, + json.githubUsername, json.mobile, json.specialism, json.imageUrl, @@ -58,7 +58,7 @@ export default class User { lastName, email, bio, - githubUrl, + githubUsername, mobile, specialism, imageUrl, @@ -73,7 +73,7 @@ export default class User { this.lastName = lastName this.email = email this.bio = bio - this.githubUrl = githubUrl + this.githubUsername = githubUsername this.mobile = mobile this.specialism = specialism this.imageUrl = imageUrl @@ -93,7 +93,7 @@ export default class User { lastName: this.lastName, email: this.email, biography: this.bio, - githubUrl: this.githubUrl, + githubUsername: this.githubUsername, mobile: this.mobile, specialism: this.specialism, imageUrl: this.imageUrl, @@ -128,7 +128,7 @@ export default class User { firstName: this.firstName, lastName: this.lastName, bio: this.bio, - githubUrl: this.githubUrl + githubUsername: this.githubUsername } } } @@ -160,30 +160,30 @@ export default class User { static async findByName(name) { // Split the name into first and last name if it contains a space - let [firstName, lastName] = name.split(' '); - + let [firstName, lastName] = name.split(' ') + // If it's a full name if (lastName) { let users = await User._findWithFullName({ firstName: firstName, lastName: lastName - }); + }) if (users.length > 0) { - return users; + return users } } // If it's a single name - let users = await this.findManyByFirstName(name); + let users = await this.findManyByFirstName(name) if (users.length > 0) { - return users; + return users } else { - users = await this.findManyByLastName(name); - if (users.length > 0) { - return users; - } + users = await this.findManyByLastName(name) + if (users.length > 0) { + return users + } } - return null; + return null } static async findAll() { @@ -229,21 +229,21 @@ export default class User { static async _findWithFullName(where) { const query = { - include: { - profile: true - } - }; + include: { + profile: true + } + } if (where) { - query.where = { - profile: where - }; + query.where = { + profile: where + } } - const foundUsers = await dbClient.user.findMany(query); + const foundUsers = await dbClient.user.findMany(query) - return foundUsers.map((user) => User.fromDb(user)); -} + return foundUsers.map((user) => User.fromDb(user)) + } /** * Updates the user in the database with current instance values @@ -263,7 +263,7 @@ export default class User { firstName: this.firstName, lastName: this.lastName, bio: this.bio, - githubUrl: this.githubUrl, + githubUsername: this.githubUsername, mobile: this.mobile, specialism: this.specialism, imageUrl: this.imageUrl, @@ -310,4 +310,3 @@ export default class User { return User.fromDb(deletedUser) } } - From 89d42df6e61365d36096d286ac121424e9477f95 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 13:51:47 +0100 Subject: [PATCH 25/28] Only teachers can change user roles and cohorts --- src/controllers/user.js | 56 ++++++++++++++++++++++++----------------- src/routes/user.js | 2 +- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 3cd07a97..9c73aad0 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -1,4 +1,6 @@ import User from '../domain/user.js' +import bcrypt from 'bcrypt' +import { validateTeacherRole } from '../middleware/auth.js' import { sendDataResponse, sendMessageResponse } from '../utils/responses.js' /** @@ -78,7 +80,7 @@ export const updateById = async (req, res) => { try { const userId = parseInt(req.params.id) - if (isNaN(userId)) { + if (!userId) { return sendMessageResponse(res, 400, 'Invalid user ID') } @@ -88,34 +90,42 @@ export const updateById = async (req, res) => { } const updates = req.body - const allowedUpdates = [ - 'firstName', - 'lastName', - 'email', - 'bio', - 'githubUsername', - 'mobile', - 'specialism', - 'imageUrl', - 'cohortId' - ] - - allowedUpdates.forEach((field) => { - if (updates[field] !== undefined) { - existingUser[field] = updates[field] + const updateUnchecked = async () => { + if (updates.password) { + existingUser.passwordHash = await bcrypt.hash(updates.password, 8) } - }) - if (updates.password) { - existingUser.passwordHash = await bcrypt.hash(updates.password, 8) + ;[ + 'firstName', + 'lastName', + 'email', + 'bio', + 'githubUsername', + 'mobile', + 'specialism', + 'imageUrl', + 'startDate', + 'endDate', + 'role' + ].forEach((field) => { + if (updates[field] !== undefined) { + existingUser[field] = updates[field] + } + }) + + const updatedUser = await existingUser.update() + return sendDataResponse(res, 201, updatedUser.toJSON()) } - const updatedUser = await existingUser.update() - - return sendDataResponse(res, 200, updatedUser.toJSON()) + if (updates.cohort_id || updates.role) { + // If user attempts to update privileged fields, validate theyre a teacher before updating + return validateTeacherRole(req, res, updateUnchecked) + } else { + updateUnchecked() + } } catch (error) { console.error('Error updating user:', error) - return sendMessageResponse(res, 400, 'Internal server error') + return sendMessageResponse(res, 500, 'Internal server error') } } diff --git a/src/routes/user.js b/src/routes/user.js index ec627cc7..b06a38a1 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -16,7 +16,7 @@ const router = Router() router.post('/', create) router.get('/', validateAuthentication, getAll) router.get('/:id', validateAuthentication, getById) -router.patch('/:id', validateAuthentication, validateTeacherRole, updateById) +router.patch('/:id', validateAuthentication, updateById) router.delete('/:id', validateAuthentication, validateTeacherRole, deleteById) export default router From 5569b3e780a6194b3f8ceb2b53020300eda5bc1a Mon Sep 17 00:00:00 2001 From: ISecka Date: Tue, 29 Oct 2024 13:58:58 +0100 Subject: [PATCH 26/28] Edited on response wgen getting all posts --- src/controllers/post.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 15c8ed5c..8b066faf 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -8,7 +8,7 @@ export const getAll = async (req, res) => { const formattedPosts = foundPosts.map((post) => post.toJSON()) return sendDataResponse(res, 200, { - somasdfething: formattedPosts + posts: formattedPosts }) } catch (error) { console.error('Error fetching posts:', error) From c6736204ad4a7ac5572acf7ae8320c756da47044 Mon Sep 17 00:00:00 2001 From: gremble0 Date: Tue, 29 Oct 2024 14:34:59 +0100 Subject: [PATCH 27/28] Add jobTitle to model --- .../migrations/20241029130428_add_job_title/migration.sql | 2 ++ prisma/schema.prisma | 1 + prisma/seed.js | 6 +++++- src/domain/user.js | 7 ++++++- 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 prisma/migrations/20241029130428_add_job_title/migration.sql diff --git a/prisma/migrations/20241029130428_add_job_title/migration.sql b/prisma/migrations/20241029130428_add_job_title/migration.sql new file mode 100644 index 00000000..0c01461b --- /dev/null +++ b/prisma/migrations/20241029130428_add_job_title/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Profile" ADD COLUMN "jobTitle" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8bf9b949..392ad9de 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -39,6 +39,7 @@ model Profile { mobile String? specialism String? imageUrl String? + jobTitle String? startDate DateTime? endDate DateTime? } diff --git a/prisma/seed.js b/prisma/seed.js index 79422b1d..dba721c2 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -15,7 +15,8 @@ async function seed() { 'student1', // url '123', // mobile 'pro', // spec - 'boolean.co.uk', // url + 'boolean.co.uk', // imageUrl + null, // jobTitle new Date('2024-01-01'), // startDate new Date('2024-06-01') // endDate ) @@ -30,6 +31,7 @@ async function seed() { '123', 'noob', 'boolean.co.uk', + 'Software Engineer', null, null, 'TEACHER' // role @@ -78,6 +80,7 @@ async function createUser( mobile, specialism, imageUrl, + jobTitle, startDate, endDate, role = 'STUDENT' @@ -97,6 +100,7 @@ async function createUser( mobile, specialism, imageUrl, + jobTitle, startDate, endDate } diff --git a/src/domain/user.js b/src/domain/user.js index 85f6bd17..3c0c7a05 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -23,6 +23,7 @@ export default class User { user.profile?.mobile, user.profile?.specialism, user.profile?.imageUrl, + user.profile?.jobTitle, user.profile?.startDate, user.profile?.endDate, user.password, @@ -31,7 +32,6 @@ export default class User { } static async fromJson(json) { - // TODO: get cohortId const passwordHash = await bcrypt.hash(json.password, 8) return new User( @@ -45,6 +45,7 @@ export default class User { json.mobile, json.specialism, json.imageUrl, + json.jobTitle, json.startDate, json.endDate, passwordHash @@ -62,6 +63,7 @@ export default class User { mobile, specialism, imageUrl, + jobTitle = null, startDate = null, endDate = null, passwordHash = null, @@ -77,6 +79,7 @@ export default class User { this.mobile = mobile this.specialism = specialism this.imageUrl = imageUrl + this.jobTitle = jobTitle this.startDate = startDate this.endDate = endDate this.passwordHash = passwordHash @@ -97,6 +100,7 @@ export default class User { mobile: this.mobile, specialism: this.specialism, imageUrl: this.imageUrl, + jobTitle: this.jobTitle, startDate: this.startDate, endDate: this.endDate } @@ -267,6 +271,7 @@ export default class User { mobile: this.mobile, specialism: this.specialism, imageUrl: this.imageUrl, + jobTitle: this.jobTitle, startDate: this.startDate, endDate: this.endDate } From 6a572bc1cc870828c0ba377bd0877b92a7dee449 Mon Sep 17 00:00:00 2001 From: alinjo Date: Tue, 29 Oct 2024 14:58:01 +0100 Subject: [PATCH 28/28] Added functionality to delete post by ID --- docs/openapi.yml | 49 +++++++++++++++++++++++++++++++++++++++++ src/controllers/post.js | 41 ++++++++++++++++++++++++++++++++++ src/domain/post.js | 7 ++++++ src/routes/post.js | 3 ++- 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index c4227187..6132fabe 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -244,6 +244,55 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + + /posts/{id}: + delete: + tags: + - post + summary: Delete a post + description: Delete a post by its ID. This action can only be performed by the post owner or an admin. + operationId: deletePost + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + description: ID of the post to delete + schema: + type: integer + responses: + '200': + description: Post deleted successfully + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: success + message: + type: string + example: Post deleted successfully + '400': + description: Invalid ID supplied + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Post not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /logs: post: tags: diff --git a/src/controllers/post.js b/src/controllers/post.js index be4c33ed..a5803c76 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -45,3 +45,44 @@ export const create = async (req, res) => { return sendDataResponse(res, 400, { error: 'Failed to create post' }) } } +export const deletePost = async (req, res) => { + const postId = parseInt(req.params.id, 10) + + // Check if the ID is valid + if (isNaN(postId)) { + return sendDataResponse(res, 400, { + status: 'error', + data: { + error: 'Invalid post ID' + } + }) + } + + try { + // Attempt to delete the post + const deletedPost = await Post.deleteById(postId) + + // If the post does not exist + if (!deletedPost) { + return sendDataResponse(res, 404, { + status: 'error', + data: { + error: 'Post does not exist' + } + }) + } + + // Send a success response with the deleted post info + return sendDataResponse(res, 200, { + post: deletedPost + }) + } catch (error) { + console.error('Error deleting post:', error) + return sendDataResponse(res, 500, { + status: 'error', + data: { + error: 'Internal server error' + } + }) + } +} diff --git a/src/domain/post.js b/src/domain/post.js index c3b2475b..9f32165f 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -128,4 +128,11 @@ export default class Post { return foundPosts.map((post) => Post.fromDb(post)) } + + static async deleteById(id) { + const deletedPost = await dbClient.post.delete({ + where: { id } + }) + return deletedPost + } } diff --git a/src/routes/post.js b/src/routes/post.js index a7fbbfb3..008d6679 100644 --- a/src/routes/post.js +++ b/src/routes/post.js @@ -1,10 +1,11 @@ import { Router } from 'express' -import { create, getAll } from '../controllers/post.js' +import { create, getAll, deletePost } from '../controllers/post.js' import { validateAuthentication } from '../middleware/auth.js' const router = Router() router.post('/', validateAuthentication, create) router.get('/', validateAuthentication, getAll) +router.delete('/:id', validateAuthentication, deletePost) export default router