Browse Source

eslint and prettier added

master
krish 10 months ago
parent
commit
f5a5955656
30 changed files with 1471 additions and 818 deletions
  1. +27
    -0
      .eslintrc.js
  2. +7
    -0
      .prettierrc
  3. +23
    -24
      README.md
  4. +1
    -1
      docker-compose.dev.yml
  5. +1
    -1
      docker-compose.prod.yml
  6. +1
    -1
      docker-compose.test.yml
  7. +3
    -3
      docker-compose.yml
  8. +290
    -60
      package-lock.json
  9. +18
    -12
      package.json
  10. +7
    -2
      src/api/controllers/auth.controller.js
  11. +8
    -7
      src/api/controllers/user.controller.js
  12. +10
    -8
      src/api/middlewares/auth.js
  13. +5
    -4
      src/api/models/passwordResetToken.model.js
  14. +4
    -3
      src/api/models/refreshToken.model.js
  15. +62
    -53
      src/api/models/user.model.js
  16. +12
    -15
      src/api/routes/v1/auth.route.js
  17. +1
    -5
      src/api/routes/v1/user.route.js
  18. +2
    -6
      src/api/services/authProviders.js
  19. +28
    -22
      src/api/tests/integration/auth.test.js
  20. +16
    -8
      src/api/tests/integration/user.test.js
  21. +6
    -4
      src/api/utils/APIError.js
  22. +755
    -491
      src/api/utils/passwordResetEmailTemplate.html
  23. +8
    -26
      src/api/validations/auth.validation.js
  24. +8
    -5
      src/api/validations/user.validation.js
  25. +3
    -3
      src/config/express.js
  26. +5
    -3
      src/config/logger.js
  27. +1
    -1
      src/config/passport.js
  28. +6
    -3
      src/config/vars.js
  29. +3
    -3
      src/index.js
  30. +150
    -44
      yarn.lock

+ 27
- 0
.eslintrc.js View File

@ -0,0 +1,27 @@
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true,
},
extends: 'standard',
plugins: ['prettier'],
overrides: [
{
env: {
node: true,
},
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script',
},
},
],
parserOptions: {
ecmaVersion: 'latest',
},
rules: {
'comma-dangle': ['error', 'never'],
'no-extra-semi': ['error', 'never'],
},
};

+ 7
- 0
.prettierrc View File

@ -0,0 +1,7 @@
{
"tabWidth": 2,
"singleQuote": true,
"semi": true,
"trailingComma": "all",
"endOfLine": "lf"
}

+ 23
- 24
README.md View File

@ -1,40 +1,40 @@
## Important Information ## Important Information
This repository is exclusively meant for presenting samples of code. This repository is exclusively meant for presenting samples of code.
The Files and Codes present in this repository are part of a running project. In order to ensure the security of the project several files have been intentionally removed. The codes are exclusively for viewing purpose and will not execute properly if tried. The Files and Codes present in this repository are part of a running project. In order to ensure the security of the project several files have been intentionally removed. The codes are exclusively for viewing purpose and will not execute properly if tried.
# Express ES2017 REST API Boilerplate # Express ES2017 REST API Boilerplate
Boilerplate/Generator/Starter Project for building RESTful APIs and microservices using Node.js, Express and MongoDB Boilerplate/Generator/Starter Project for building RESTful APIs and microservices using Node.js, Express and MongoDB
## Features ## Features
- No transpilers, just vanilla javascript
- ES2017 latest features like Async/Await
- CORS enabled
- Uses [yarn](https://yarnpkg.com)
- Express + MongoDB ([Mongoose](http://mongoosejs.com/))
- Consistent coding styles with [editorconfig](http://editorconfig.org)
- [Docker](https://www.docker.com/) support
- Uses [helmet](https://github.com/helmetjs/helmet) to set some HTTP headers for security
- Load environment variables from .env files with [dotenv](https://github.com/rolodato/dotenv-safe)
- Request validation with [joi](https://github.com/hapijs/joi)
- Gzip compression with [compression](https://github.com/expressjs/compression)
- Linting with [eslint](http://eslint.org)
- Tests with [mocha](https://mochajs.org), [chai](http://chaijs.com) and [sinon](http://sinonjs.org)
- Code coverage with [istanbul](https://istanbul.js.org) and [coveralls](https://coveralls.io)
- Git hooks with [husky](https://github.com/typicode/husky)
- Logging with [morgan](https://github.com/expressjs/morgan)
- Authentication and Authorization with [passport](http://passportjs.org)
- API documentation generation with [apidoc](http://apidocjs.com)
- Continuous integration support with [travisCI](https://travis-ci.org)
- Monitoring with [pm2](https://github.com/Unitech/pm2)
- No transpilers, just vanilla javascript
- ES2017 latest features like Async/Await
- CORS enabled
- Uses [yarn](https://yarnpkg.com)
- Express + MongoDB ([Mongoose](http://mongoosejs.com/))
- Consistent coding styles with [editorconfig](http://editorconfig.org)
- [Docker](https://www.docker.com/) support
- Uses [helmet](https://github.com/helmetjs/helmet) to set some HTTP headers for security
- Load environment variables from .env files with [dotenv](https://github.com/rolodato/dotenv-safe)
- Request validation with [joi](https://github.com/hapijs/joi)
- Gzip compression with [compression](https://github.com/expressjs/compression)
- Linting with [eslint](http://eslint.org)
- Tests with [mocha](https://mochajs.org), [chai](http://chaijs.com) and [sinon](http://sinonjs.org)
- Code coverage with [istanbul](https://istanbul.js.org) and [coveralls](https://coveralls.io)
- Git hooks with [husky](https://github.com/typicode/husky)
- Logging with [morgan](https://github.com/expressjs/morgan)
- Authentication and Authorization with [passport](http://passportjs.org)
- API documentation generation with [apidoc](http://apidocjs.com)
- Continuous integration support with [travisCI](https://travis-ci.org)
- Monitoring with [pm2](https://github.com/Unitech/pm2)
## Requirements ## Requirements
- [Node v7.6+](https://nodejs.org/en/download/current/) or [Docker](https://www.docker.com/)
- [Yarn](https://yarnpkg.com/en/docs/install)
- [Node v7.6+](https://nodejs.org/en/download/current/) or [Docker](https://www.docker.com/)
- [Yarn](https://yarnpkg.com/en/docs/install)
## Getting Started ## Getting Started
@ -155,4 +155,3 @@ Run deploy script:
```bash ```bash
yarn deploy yarn deploy
``` ```

+ 1
- 1
docker-compose.dev.yml View File

@ -1,4 +1,4 @@
version: "2"
version: '2'
services: services:
boilerplate-api: boilerplate-api:
command: yarn dev -- -L command: yarn dev -- -L


+ 1
- 1
docker-compose.prod.yml View File

@ -1,4 +1,4 @@
version: "2"
version: '2'
services: services:
boilerplate-api: boilerplate-api:
command: yarn start command: yarn start


+ 1
- 1
docker-compose.test.yml View File

@ -1,4 +1,4 @@
version: "2"
version: '2'
services: services:
boilerplate-api: boilerplate-api:
command: yarn test command: yarn test


+ 3
- 3
docker-compose.yml View File

@ -1,4 +1,4 @@
version: "2"
version: '2'
services: services:
boilerplate-api: boilerplate-api:
build: . build: .
@ -7,11 +7,11 @@ services:
volumes: volumes:
- .:/app - .:/app
ports: ports:
- "3000:3000"
- '3000:3000'
depends_on: depends_on:
- mongodb - mongodb
mongodb: mongodb:
image: mongo image: mongo
ports: ports:
- "27017:27017"
- '27017:27017'

+ 290
- 60
package-lock.json View File

@ -14,8 +14,8 @@
"body-parser": "^1.19.1", "body-parser": "^1.19.1",
"compression": "^1.7.4", "compression": "^1.7.4",
"cors": "^2.8.5", "cors": "^2.8.5",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"dotenv-cli": "^7.4.1",
"email-templates": "^11.1.1", "email-templates": "^11.1.1",
"express": "^4.17.2", "express": "^4.17.2",
"express-validation": "^4.1.0", "express-validation": "^4.1.0",
@ -24,6 +24,7 @@
"joi": "^17.5.0", "joi": "^17.5.0",
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",
"method-override": "^3.0.0", "method-override": "^3.0.0",
"moment-timezone": "^0.5.45",
"mongoose": "^8.2.1", "mongoose": "^8.2.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"nodemailer": "^6.7.2", "nodemailer": "^6.7.2",
@ -40,14 +41,18 @@
"chai": "^4.4.1", "chai": "^4.4.1",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"coveralls": "^3.1.1", "coveralls": "^3.1.1",
"eslint": "^8.5.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.25.3",
"eslint": "^8.57.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-promise": "^6.1.1",
"husky": "^9.0.11", "husky": "^9.0.11",
"mocha": "^10.3.0", "mocha": "^10.3.0",
"nodemon": "^3.1.0", "nodemon": "^3.1.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"opn-cli": "^5.0.0", "opn-cli": "^5.0.0",
"prettier": "^3.2.5",
"sinon": "^17.0.1", "sinon": "^17.0.1",
"sinon-chai": "^3.7.0", "sinon-chai": "^3.7.0",
"supertest": "^6.1.6" "supertest": "^6.1.6"
@ -1341,6 +1346,18 @@
"uuid": "bin/uuid" "uuid": "bin/uuid"
} }
}, },
"node_modules/@pkgr/core": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
"integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/@pm2/agent": { "node_modules/@pm2/agent": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.3.tgz",
@ -2898,6 +2915,27 @@
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
}, },
"node_modules/builtin-modules": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
"dev": true,
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/builtins": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
"integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
"dev": true,
"dependencies": {
"semver": "^7.0.0"
}
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -3369,12 +3407,6 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
}, },
"node_modules/confusing-browser-globals": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
"integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
"dev": true
},
"node_modules/constantinople": { "node_modules/constantinople": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz",
@ -3498,23 +3530,6 @@
"resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz", "resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz",
"integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==" "integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ=="
}, },
"node_modules/cross-env": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
"dependencies": {
"cross-spawn": "^7.0.1"
},
"bin": {
"cross-env": "src/bin/cross-env.js",
"cross-env-shell": "src/bin/cross-env-shell.js"
},
"engines": {
"node": ">=10.14",
"npm": ">=6",
"yarn": ">=1"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -3891,6 +3906,28 @@
"url": "https://dotenvx.com" "url": "https://dotenvx.com"
} }
}, },
"node_modules/dotenv-cli": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.4.1.tgz",
"integrity": "sha512-fE1aywjRrWGxV3miaiUr3d2zC/VAiuzEGghi+QzgIA9fEf/M5hLMaRSXb4IxbUAwGmaLi0IozdZddnVU96acag==",
"dependencies": {
"cross-spawn": "^7.0.3",
"dotenv": "^16.3.0",
"dotenv-expand": "^10.0.0",
"minimist": "^1.2.6"
},
"bin": {
"dotenv": "cli.js"
}
},
"node_modules/dotenv-expand": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz",
"integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==",
"engines": {
"node": ">=12"
}
},
"node_modules/ecc-jsbn": { "node_modules/ecc-jsbn": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@ -4352,32 +4389,45 @@
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
} }
}, },
"node_modules/eslint-config-airbnb-base": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
"integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
"node_modules/eslint-compat-utils": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz",
"integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==",
"dev": true, "dev": true,
"dependencies": {
"confusing-browser-globals": "^1.0.10",
"object.assign": "^4.1.2",
"object.entries": "^1.1.5",
"semver": "^6.3.0"
},
"engines": { "engines": {
"node": "^10.12.0 || >=12.0.0"
"node": ">=12"
}, },
"peerDependencies": { "peerDependencies": {
"eslint": "^7.32.0 || ^8.2.0",
"eslint-plugin-import": "^2.25.2"
"eslint": ">=6.0.0"
} }
}, },
"node_modules/eslint-config-airbnb-base/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"node_modules/eslint-config-standard": {
"version": "17.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz",
"integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==",
"dev": true, "dev": true,
"bin": {
"semver": "bin/semver.js"
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"eslint": "^8.0.1",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
"eslint-plugin-promise": "^6.0.0"
} }
}, },
"node_modules/eslint-import-resolver-node": { "node_modules/eslint-import-resolver-node": {
@ -4438,6 +4488,26 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true "dev": true
}, },
"node_modules/eslint-plugin-es-x": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz",
"integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.1.2",
"@eslint-community/regexpp": "^4.6.0",
"eslint-compat-utils": "^0.1.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ota-meshi"
},
"peerDependencies": {
"eslint": ">=8"
}
},
"node_modules/eslint-plugin-import": { "node_modules/eslint-plugin-import": {
"version": "2.29.1", "version": "2.29.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
@ -4505,6 +4575,76 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/eslint-plugin-n": {
"version": "16.6.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz",
"integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"builtins": "^5.0.1",
"eslint-plugin-es-x": "^7.5.0",
"get-tsconfig": "^4.7.0",
"globals": "^13.24.0",
"ignore": "^5.2.4",
"is-builtin-module": "^3.2.1",
"is-core-module": "^2.12.1",
"minimatch": "^3.1.2",
"resolve": "^1.22.2",
"semver": "^7.5.3"
},
"engines": {
"node": ">=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/mysticatea"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-plugin-prettier": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
"integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==",
"dev": true,
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
"synckit": "^0.8.6"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint-plugin-prettier"
},
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
"eslint-config-prettier": "*",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
"@types/eslint": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-plugin-promise": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz",
"integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
}
},
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "7.2.2", "version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
@ -4876,6 +5016,12 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true
},
"node_modules/fast-json-patch": { "node_modules/fast-json-patch": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz",
@ -5361,6 +5507,18 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/get-tsconfig": {
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz",
"integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==",
"dev": true,
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/get-uri": { "node_modules/get-uri": {
"version": "6.0.3", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz",
@ -6117,6 +6275,21 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-builtin-module": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
"integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
"dev": true,
"dependencies": {
"builtin-modules": "^3.3.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-callable": { "node_modules/is-callable": {
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@ -7673,6 +7846,25 @@
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
"integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A=="
}, },
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
},
"node_modules/moment-timezone": {
"version": "0.5.45",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz",
"integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==",
"dependencies": {
"moment": "^2.29.4"
},
"engines": {
"node": "*"
}
},
"node_modules/mongodb": { "node_modules/mongodb": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz",
@ -8393,20 +8585,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/object.entries": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz",
"integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.2.0",
"es-abstract": "^1.22.1"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/object.fromentries": { "node_modules/object.fromentries": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz",
@ -9288,6 +9466,33 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prettier": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/preview-email": { "node_modules/preview-email": {
"version": "3.0.19", "version": "3.0.19",
"resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.0.19.tgz", "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.0.19.tgz",
@ -10040,6 +10245,15 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/reusify": { "node_modules/reusify": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@ -10897,6 +11111,22 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/synckit": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz",
"integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==",
"dev": true,
"dependencies": {
"@pkgr/core": "^0.1.0",
"tslib": "^2.6.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/systeminformation": { "node_modules/systeminformation": {
"version": "5.22.0", "version": "5.22.0",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.0.tgz", "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.0.tgz",


+ 18
- 12
package.json View File

@ -11,15 +11,16 @@
"scripts": { "scripts": {
"precommit": "yarn lint", "precommit": "yarn lint",
"prestart": "yarn docs", "prestart": "yarn docs",
"start": "cross-env NODE_ENV=production pm2 start ./src/index.js",
"dev": "nodemon ./src/index.js",
"start:dev": "dotenv -v NODE_ENV=development -- nodemon ./src/index.js",
"start:prod": "dotenv -v NODE_ENV=production -- pm2 start ./src/index.js",
"lint": "eslint ./src/ --ignore-path .gitignore --ignore-pattern internals/scripts", "lint": "eslint ./src/ --ignore-path .gitignore --ignore-pattern internals/scripts",
"lint:fix": "yarn lint --fix",
"lint:watch": "yarn lint --watch",
"test": "cross-env NODE_ENV=test nyc --reporter=html --reporter=text mocha --timeout 20000 --exit --recursive src/api/tests",
"test:unit": "cross-env NODE_ENV=test mocha src/api/tests/unit",
"test:integration": "cross-env NODE_ENV=test mocha --timeout 20000 --exit src/api/tests/integration",
"test:watch": "cross-env NODE_ENV=test mocha --watch src/api/tests/unit",
"lint:fix": "npx eslint --fix",
"lint:watch": "npx eslint --watch",
"format": "npx prettier --write .",
"test": "dotenv -v NODE_ENV=test -- nyc --reporter=html --reporter=text mocha --timeout 20000 --exit --recursive src/api/tests",
"test:unit": "dotenv -v NODE_ENV=test -- mocha src/api/tests/unit",
"test:integration": "dotenv -v NODE_ENV=test -- mocha --timeout 20000 --exit src/api/tests/integration",
"test:watch": "dotenv -v NODE_ENV=test -- mocha --watch src/api/tests/unit",
"coverage": "nyc report --reporter=text-lcov | coveralls", "coverage": "nyc report --reporter=text-lcov | coveralls",
"postcoverage": "open-cli coverage/lcov-report/index.html", "postcoverage": "open-cli coverage/lcov-report/index.html",
"validate": "yarn lint && yarn test", "validate": "yarn lint && yarn test",
@ -66,8 +67,8 @@
"body-parser": "^1.19.1", "body-parser": "^1.19.1",
"compression": "^1.7.4", "compression": "^1.7.4",
"cors": "^2.8.5", "cors": "^2.8.5",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"dotenv-cli": "^7.4.1",
"email-templates": "^11.1.1", "email-templates": "^11.1.1",
"express": "^4.17.2", "express": "^4.17.2",
"express-validation": "^4.1.0", "express-validation": "^4.1.0",
@ -76,6 +77,7 @@
"joi": "^17.5.0", "joi": "^17.5.0",
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",
"method-override": "^3.0.0", "method-override": "^3.0.0",
"moment-timezone": "^0.5.45",
"mongoose": "^8.2.1", "mongoose": "^8.2.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"nodemailer": "^6.7.2", "nodemailer": "^6.7.2",
@ -92,14 +94,18 @@
"chai": "^4.4.1", "chai": "^4.4.1",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"coveralls": "^3.1.1", "coveralls": "^3.1.1",
"eslint": "^8.5.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.25.3",
"eslint": "^8.57.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-promise": "^6.1.1",
"husky": "^9.0.11", "husky": "^9.0.11",
"mocha": "^10.3.0", "mocha": "^10.3.0",
"nodemon": "^3.1.0", "nodemon": "^3.1.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"opn-cli": "^5.0.0", "opn-cli": "^5.0.0",
"prettier": "^3.2.5",
"sinon": "^17.0.1", "sinon": "^17.0.1",
"sinon-chai": "^3.7.0", "sinon-chai": "^3.7.0",
"supertest": "^6.1.6" "supertest": "^6.1.6"


+ 7
- 2
src/api/controllers/auth.controller.js View File

@ -84,7 +84,10 @@ exports.refresh = async (req, res, next) => {
userEmail: email, userEmail: email,
token: refreshToken, token: refreshToken,
}); });
const { user, accessToken } = await User.findAndGenerateToken({ email, refreshObject });
const { user, accessToken } = await User.findAndGenerateToken({
email,
refreshObject,
});
const response = generateTokenResponse(user, accessToken); const response = generateTokenResponse(user, accessToken);
return res.json(response); return res.json(response);
} catch (error) { } catch (error) {
@ -133,7 +136,9 @@ exports.resetPassword = async (req, res, next) => {
throw new APIError(err); throw new APIError(err);
} }
const user = await User.findOne({ email: resetTokenObject.userEmail }).exec();
const user = await User.findOne({
email: resetTokenObject.userEmail,
}).exec();
user.password = password; user.password = password;
await user.save(); await user.save();
emailProvider.sendPasswordChangeEmail(user); emailProvider.sendPasswordChangeEmail(user);


+ 8
- 7
src/api/controllers/user.controller.js View File

@ -54,7 +54,6 @@ exports.replace = async (req, res, next) => {
const ommitRole = user.role !== 'admin' ? 'role' : ''; const ommitRole = user.role !== 'admin' ? 'role' : '';
const newUserObject = omit(newUser.toObject(), '_id', ommitRole); const newUserObject = omit(newUser.toObject(), '_id', ommitRole);
res.json(savedUser.transform()); res.json(savedUser.transform());
} catch (error) { } catch (error) {
next(User.checkDuplicateEmail(error)); next(User.checkDuplicateEmail(error));
@ -70,9 +69,10 @@ exports.update = (req, res, next) => {
const updatedUser = omit(req.body, ommitRole); const updatedUser = omit(req.body, ommitRole);
const user = Object.assign(req.locals.user, updatedUser); const user = Object.assign(req.locals.user, updatedUser);
user.save()
.then(savedUser => res.json(savedUser.transform()))
.catch(e => next(User.checkDuplicateEmail(e)));
user
.save()
.then((savedUser) => res.json(savedUser.transform()))
.catch((e) => next(User.checkDuplicateEmail(e)));
}; };
/** /**
@ -82,7 +82,7 @@ exports.update = (req, res, next) => {
exports.list = async (req, res, next) => { exports.list = async (req, res, next) => {
try { try {
const users = await User.list(req.query); const users = await User.list(req.query);
const transformedUsers = users.map(user => user.transform());
const transformedUsers = users.map((user) => user.transform());
res.json(transformedUsers); res.json(transformedUsers);
} catch (error) { } catch (error) {
next(error); next(error);
@ -96,7 +96,8 @@ exports.list = async (req, res, next) => {
exports.remove = (req, res, next) => { exports.remove = (req, res, next) => {
const { user } = req.locals; const { user } = req.locals;
user.remove()
user
.remove()
.then(() => res.status(httpStatus.NO_CONTENT).end()) .then(() => res.status(httpStatus.NO_CONTENT).end())
.catch(e => next(e));
.catch((e) => next(e));
}; };

+ 10
- 8
src/api/middlewares/auth.js View File

@ -44,11 +44,13 @@ const handleJWT = (req, res, next, roles) => async (err, user, info) => {
exports.ADMIN = ADMIN; exports.ADMIN = ADMIN;
exports.LOGGED_USER = LOGGED_USER; exports.LOGGED_USER = LOGGED_USER;
exports.authorize = (roles = User.roles) => (req, res, next) =>
passport.authenticate(
'jwt', { session: false },
handleJWT(req, res, next, roles),
)(req, res, next);
exports.oAuth = service =>
passport.authenticate(service, { session: false });
exports.authorize =
(roles = User.roles) =>
(req, res, next) =>
passport.authenticate(
'jwt',
{ session: false },
handleJWT(req, res, next, roles),
)(req, res, next);
exports.oAuth = (service) => passport.authenticate(service, { session: false });

+ 5
- 4
src/api/models/passwordResetToken.model.js View File

@ -36,9 +36,7 @@ passwordResetTokenSchema.statics = {
const userId = user._id; const userId = user._id;
const userEmail = user.email; const userEmail = user.email;
const resetToken = `${userId}.${crypto.randomBytes(40).toString('hex')}`; const resetToken = `${userId}.${crypto.randomBytes(40).toString('hex')}`;
const expires = moment()
.add(2, 'hours')
.toDate();
const expires = moment().add(2, 'hours').toDate();
const ResetTokenObject = new PasswordResetToken({ const ResetTokenObject = new PasswordResetToken({
resetToken, resetToken,
userId, userId,
@ -53,5 +51,8 @@ passwordResetTokenSchema.statics = {
/** /**
* @typedef RefreshToken * @typedef RefreshToken
*/ */
const PasswordResetToken = mongoose.model('PasswordResetToken', passwordResetTokenSchema);
const PasswordResetToken = mongoose.model(
'PasswordResetToken',
passwordResetTokenSchema,
);
module.exports = PasswordResetToken; module.exports = PasswordResetToken;

+ 4
- 3
src/api/models/refreshToken.model.js View File

@ -26,7 +26,6 @@ const refreshTokenSchema = new mongoose.Schema({
}); });
refreshTokenSchema.statics = { refreshTokenSchema.statics = {
/** /**
* Generate a refresh token object and saves it into the database * Generate a refresh token object and saves it into the database
* *
@ -39,12 +38,14 @@ refreshTokenSchema.statics = {
const token = `${userId}.${crypto.randomBytes(40).toString('hex')}`; const token = `${userId}.${crypto.randomBytes(40).toString('hex')}`;
const expires = moment().add(30, 'days').toDate(); const expires = moment().add(30, 'days').toDate();
const tokenObject = new RefreshToken({ const tokenObject = new RefreshToken({
token, userId, userEmail, expires,
token,
userId,
userEmail,
expires,
}); });
tokenObject.save(); tokenObject.save();
return tokenObject; return tokenObject;
}, },
}; };
/** /**


+ 62
- 53
src/api/models/user.model.js View File

@ -9,51 +9,54 @@ const APIError = require('../utils/APIError');
const { env, jwtSecret, jwtExpirationInterval } = require('../../config/vars'); const { env, jwtSecret, jwtExpirationInterval } = require('../../config/vars');
/** /**
* User Roles
*/
* User Roles
*/
const roles = ['user', 'admin']; const roles = ['user', 'admin'];
/** /**
* User Schema * User Schema
* @private * @private
*/ */
const userSchema = new mongoose.Schema({
email: {
type: String,
match: /^\S+@\S+\.\S+$/,
required: true,
unique: true,
trim: true,
lowercase: true,
},
password: {
type: String,
required: true,
minlength: 6,
maxlength: 128,
},
name: {
type: String,
maxlength: 128,
index: true,
trim: true,
},
services: {
facebook: String,
google: String,
},
role: {
type: String,
enum: roles,
default: 'user',
const userSchema = new mongoose.Schema(
{
email: {
type: String,
match: /^\S+@\S+\.\S+$/,
required: true,
unique: true,
trim: true,
lowercase: true,
},
password: {
type: String,
required: true,
minlength: 6,
maxlength: 128,
},
name: {
type: String,
maxlength: 128,
index: true,
trim: true,
},
services: {
facebook: String,
google: String,
},
role: {
type: String,
enum: roles,
default: 'user',
},
picture: {
type: String,
trim: true,
},
}, },
picture: {
type: String,
trim: true,
{
timestamps: true,
}, },
}, {
timestamps: true,
});
);
/** /**
* Add your * Add your
@ -109,7 +112,6 @@ userSchema.method({
* Statics * Statics
*/ */
userSchema.statics = { userSchema.statics = {
roles, roles,
/** /**
@ -146,7 +148,10 @@ userSchema.statics = {
*/ */
async findAndGenerateToken(options) { async findAndGenerateToken(options) {
const { email, password, refreshObject } = options; const { email, password, refreshObject } = options;
if (!email) throw new APIError({ message: 'An email is required to generate a token' });
if (!email)
throw new APIError({
message: 'An email is required to generate a token',
});
const user = await this.findOne({ email }).exec(); const user = await this.findOne({ email }).exec();
const err = { const err = {
@ -154,7 +159,7 @@ userSchema.statics = {
isPublic: true, isPublic: true,
}; };
if (password) { if (password) {
if (user && await user.passwordMatches(password)) {
if (user && (await user.passwordMatches(password))) {
return { user, accessToken: user.token() }; return { user, accessToken: user.token() };
} }
err.message = 'Incorrect email or password'; err.message = 'Incorrect email or password';
@ -177,9 +182,7 @@ userSchema.statics = {
* @param {number} limit - Limit number of users to be returned. * @param {number} limit - Limit number of users to be returned.
* @returns {Promise<User[]>} * @returns {Promise<User[]>}
*/ */
list({
page = 1, perPage = 30, name, email, role,
}) {
list({ page = 1, perPage = 30, name, email, role }) {
const options = omitBy({ name, email, role }, isNil); const options = omitBy({ name, email, role }, isNil);
return this.find(options) return this.find(options)
@ -200,11 +203,13 @@ userSchema.statics = {
if (error.name === 'MongoError' && error.code === 11000) { if (error.name === 'MongoError' && error.code === 11000) {
return new APIError({ return new APIError({
message: 'Validation Error', message: 'Validation Error',
errors: [{
field: 'email',
location: 'body',
messages: ['"email" already exists'],
}],
errors: [
{
field: 'email',
location: 'body',
messages: ['"email" already exists'],
},
],
status: httpStatus.CONFLICT, status: httpStatus.CONFLICT,
isPublic: true, isPublic: true,
stack: error.stack, stack: error.stack,
@ -213,10 +218,10 @@ userSchema.statics = {
return error; return error;
}, },
async oAuthLogin({
service, id, email, name, picture,
}) {
const user = await this.findOne({ $or: [{ [`services.${service}`]: id }, { email }] });
async oAuthLogin({ service, id, email, name, picture }) {
const user = await this.findOne({
$or: [{ [`services.${service}`]: id }, { email }],
});
if (user) { if (user) {
user.services[service] = id; user.services[service] = id;
if (!user.name) user.name = name; if (!user.name) user.name = name;
@ -225,7 +230,11 @@ userSchema.statics = {
} }
const password = uuidv4(); const password = uuidv4();
return this.create({ return this.create({
services: { [service]: id }, email, password, name, picture,
services: { [service]: id },
email,
password,
name,
picture,
}); });
}, },
}; };


+ 12
- 15
src/api/routes/v1/auth.route.js View File

@ -1,5 +1,5 @@
const express = require('express'); const express = require('express');
const { validate, ValidationError, Joi } = require('express-validation')
const { validate, ValidationError, Joi } = require('express-validation');
const controller = require('../../controllers/auth.controller'); const controller = require('../../controllers/auth.controller');
const oAuthLogin = require('../../middlewares/auth').oAuth; const oAuthLogin = require('../../middlewares/auth').oAuth;
const { const {
@ -40,9 +40,7 @@ const router = express.Router();
* *
* @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values
*/ */
router.route('/register')
.post(validate(register), controller.register);
router.route('/register').post(validate(register), controller.register);
/** /**
* @api {post} v1/auth/login Login * @api {post} v1/auth/login Login
@ -71,9 +69,7 @@ router.route('/register')
* @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values
* @apiError (Unauthorized 401) Unauthorized Incorrect email or password * @apiError (Unauthorized 401) Unauthorized Incorrect email or password
*/ */
router.route('/login')
.post(validate(login), controller.login);
router.route('/login').post(validate(login), controller.login);
/** /**
* @api {post} v1/auth/refresh-token Refresh Token * @api {post} v1/auth/refresh-token Refresh Token
@ -94,14 +90,14 @@ router.route('/login')
* @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values
* @apiError (Unauthorized 401) Unauthorized Incorrect email or refreshToken * @apiError (Unauthorized 401) Unauthorized Incorrect email or refreshToken
*/ */
router.route('/refresh-token')
.post(validate(refresh), controller.refresh);
router.route('/refresh-token').post(validate(refresh), controller.refresh);
router.route('/send-password-reset')
router
.route('/send-password-reset')
.post(validate(sendPasswordReset), controller.sendPasswordReset); .post(validate(sendPasswordReset), controller.sendPasswordReset);
router.route('/reset-password')
router
.route('/reset-password')
.post(validate(passwordReset), controller.resetPassword); .post(validate(passwordReset), controller.resetPassword);
/** /**
@ -122,7 +118,8 @@ router.route('/reset-password')
* @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values
* @apiError (Unauthorized 401) Unauthorized Incorrect access_token * @apiError (Unauthorized 401) Unauthorized Incorrect access_token
*/ */
router.route('/facebook')
router
.route('/facebook')
.post(validate(oAuth), oAuthLogin('facebook'), controller.oAuth); .post(validate(oAuth), oAuthLogin('facebook'), controller.oAuth);
/** /**
@ -143,8 +140,8 @@ router.route('/facebook')
* @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values
* @apiError (Unauthorized 401) Unauthorized Incorrect access_token * @apiError (Unauthorized 401) Unauthorized Incorrect access_token
*/ */
router.route('/google')
router
.route('/google')
.post(validate(oAuth), oAuthLogin('google'), controller.oAuth); .post(validate(oAuth), oAuthLogin('google'), controller.oAuth);
module.exports = router; module.exports = router;

+ 1
- 5
src/api/routes/v1/user.route.js View File

@ -1,5 +1,5 @@
const express = require('express'); const express = require('express');
const { validate, ValidationError, Joi } = require('express-validation')
const { validate, ValidationError, Joi } = require('express-validation');
const controller = require('../../controllers/user.controller'); const controller = require('../../controllers/user.controller');
const { authorize, ADMIN, LOGGED_USER } = require('../../middlewares/auth'); const { authorize, ADMIN, LOGGED_USER } = require('../../middlewares/auth');
const { const {
@ -16,7 +16,6 @@ const router = express.Router();
*/ */
router.param('userId', controller.load); router.param('userId', controller.load);
router router
.route('/') .route('/')
/** /**
@ -68,7 +67,6 @@ router
*/ */
.post(authorize(ADMIN), validate(createUser), controller.create); .post(authorize(ADMIN), validate(createUser), controller.create);
router router
.route('/profile') .route('/profile')
/** /**
@ -91,7 +89,6 @@ router
*/ */
.get(authorize(), controller.loggedIn); .get(authorize(), controller.loggedIn);
router router
.route('/:userId') .route('/:userId')
/** /**
@ -189,5 +186,4 @@ router
*/ */
.delete(authorize(LOGGED_USER), controller.remove); .delete(authorize(LOGGED_USER), controller.remove);
module.exports = router; module.exports = router;

+ 2
- 6
src/api/services/authProviders.js View File

@ -6,9 +6,7 @@ exports.facebook = async (access_token) => {
const url = 'https://graph.facebook.com/me'; const url = 'https://graph.facebook.com/me';
const params = { access_token, fields }; const params = { access_token, fields };
const response = await axios.get(url, { params }); const response = await axios.get(url, { params });
const {
id, name, email, picture,
} = response.data;
const { id, name, email, picture } = response.data;
return { return {
service: 'facebook', service: 'facebook',
picture: picture.data.url, picture: picture.data.url,
@ -22,9 +20,7 @@ exports.google = async (access_token) => {
const url = 'https://www.googleapis.com/oauth2/v3/userinfo'; const url = 'https://www.googleapis.com/oauth2/v3/userinfo';
const params = { access_token }; const params = { access_token };
const response = await axios.get(url, { params }); const response = await axios.get(url, { params });
const {
sub, name, email, picture,
} = response.data;
const { sub, name, email, picture } = response.data;
return { return {
service: 'google', service: 'google',
picture, picture,


+ 28
- 22
src/api/tests/integration/auth.test.js View File

@ -49,9 +49,7 @@ describe('Authentication API', () => {
'5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d', '5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d',
userId: '5947397b323ae82d8c3a333b', userId: '5947397b323ae82d8c3a333b',
userEmail: dbUser.email, userEmail: dbUser.email,
expires: moment()
.add(1, 'day')
.toDate(),
expires: moment().add(1, 'day').toDate(),
}; };
resetToken = { resetToken = {
@ -59,9 +57,7 @@ describe('Authentication API', () => {
'5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d', '5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d',
userId: '5947397b323ae82d8c3a333b', userId: '5947397b323ae82d8c3a333b',
userEmail: dbUser.email, userEmail: dbUser.email,
expires: moment()
.add(2, 'hours')
.toDate(),
expires: moment().add(2, 'hours').toDate(),
}; };
expiredRefreshToken = { expiredRefreshToken = {
@ -69,9 +65,7 @@ describe('Authentication API', () => {
'5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d', '5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d',
userId: '5947397b323ae82d8c3a333b', userId: '5947397b323ae82d8c3a333b',
userEmail: dbUser.email, userEmail: dbUser.email,
expires: moment()
.subtract(1, 'day')
.toDate(),
expires: moment().subtract(1, 'day').toDate(),
}; };
expiredResetToken = { expiredResetToken = {
@ -79,9 +73,7 @@ describe('Authentication API', () => {
'5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d', '5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d',
userId: '5947397b323ae82d8c3a333b', userId: '5947397b323ae82d8c3a333b',
userEmail: dbUser.email, userEmail: dbUser.email,
expires: moment()
.subtract(2, 'hours')
.toDate(),
expires: moment().subtract(2, 'hours').toDate(),
}; };
await User.deleteMany({}); await User.deleteMany({});
@ -373,12 +365,16 @@ describe('Authentication API', () => {
it('should send an email with password reset link when email matches a user', async () => { it('should send an email with password reset link when email matches a user', async () => {
const PasswordResetTokenObj = await PasswordResetToken.create(resetToken); const PasswordResetTokenObj = await PasswordResetToken.create(resetToken);
expect(PasswordResetTokenObj.resetToken).to.be.equal('5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d');
expect(PasswordResetTokenObj.userId.toString()).to.be.equal('5947397b323ae82d8c3a333b');
expect(PasswordResetTokenObj.resetToken).to.be.equal(
'5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d',
);
expect(PasswordResetTokenObj.userId.toString()).to.be.equal(
'5947397b323ae82d8c3a333b',
);
expect(PasswordResetTokenObj.userEmail).to.be.equal(dbUser.email); expect(PasswordResetTokenObj.userEmail).to.be.equal(dbUser.email);
expect(PasswordResetTokenObj.expires).to.be.above(moment()
.add(1, 'hour')
.toDate());
expect(PasswordResetTokenObj.expires).to.be.above(
moment().add(1, 'hour').toDate(),
);
sandbox sandbox
.stub(emailProvider, 'sendPasswordReset') .stub(emailProvider, 'sendPasswordReset')
@ -463,7 +459,10 @@ describe('Authentication API', () => {
it('should report error when email is not provided', () => { it('should report error when email is not provided', () => {
return request(app) return request(app)
.post('/v1/auth/reset-password') .post('/v1/auth/reset-password')
.send({ password: 'updatedPassword', resetToken: resetToken.resetToken })
.send({
password: 'updatedPassword',
resetToken: resetToken.resetToken,
})
.expect(httpStatus.BAD_REQUEST) .expect(httpStatus.BAD_REQUEST)
.then((res) => { .then((res) => {
const field1 = res.body.errors[0].field; const field1 = res.body.errors[0].field;
@ -504,12 +503,19 @@ describe('Authentication API', () => {
}); });
it('should report error when the resetToken is expired', async () => { it('should report error when the resetToken is expired', async () => {
const expiredPasswordResetTokenObj = await PasswordResetToken.create(expiredResetToken);
const expiredPasswordResetTokenObj =
await PasswordResetToken.create(expiredResetToken);
expect(expiredPasswordResetTokenObj.resetToken).to.be.equal('5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d');
expect(expiredPasswordResetTokenObj.userId.toString()).to.be.equal('5947397b323ae82d8c3a333b');
expect(expiredPasswordResetTokenObj.resetToken).to.be.equal(
'5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d',
);
expect(expiredPasswordResetTokenObj.userId.toString()).to.be.equal(
'5947397b323ae82d8c3a333b',
);
expect(expiredPasswordResetTokenObj.userEmail).to.be.equal(dbUser.email); expect(expiredPasswordResetTokenObj.userEmail).to.be.equal(dbUser.email);
expect(expiredPasswordResetTokenObj.expires).to.be.below(moment().toDate());
expect(expiredPasswordResetTokenObj.expires).to.be.below(
moment().toDate(),
);
return request(app) return request(app)
.post('/v1/auth/reset-password') .post('/v1/auth/reset-password')


+ 16
- 8
src/api/tests/integration/user.test.js View File

@ -69,8 +69,10 @@ describe('Users API', async () => {
await User.insertMany([dbUsers.branStark, dbUsers.jonSnow]); await User.insertMany([dbUsers.branStark, dbUsers.jonSnow]);
dbUsers.branStark.password = password; dbUsers.branStark.password = password;
dbUsers.jonSnow.password = password; dbUsers.jonSnow.password = password;
adminAccessToken = (await User.findAndGenerateToken(dbUsers.branStark)).accessToken;
userAccessToken = (await User.findAndGenerateToken(dbUsers.jonSnow)).accessToken;
adminAccessToken = (await User.findAndGenerateToken(dbUsers.branStark))
.accessToken;
userAccessToken = (await User.findAndGenerateToken(dbUsers.jonSnow))
.accessToken;
}); });
describe('POST /v1/users', () => { describe('POST /v1/users', () => {
@ -147,7 +149,9 @@ describe('Users API', async () => {
const { messages } = res.body.errors[0]; const { messages } = res.body.errors[0];
expect(field).to.be.equal('password'); expect(field).to.be.equal('password');
expect(location).to.be.equal('body'); expect(location).to.be.equal('body');
expect(messages).to.include('"password" length must be at least 6 characters long');
expect(messages).to.include(
'"password" length must be at least 6 characters long',
);
}); });
}); });
@ -224,7 +228,7 @@ describe('Users API', async () => {
}); });
}); });
it('should report error when pagination\'s parameters are not a number', () => {
it("should report error when pagination's parameters are not a number", () => {
return request(app) return request(app)
.get('/v1/users') .get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`) .set('Authorization', `Bearer ${adminAccessToken}`)
@ -362,7 +366,9 @@ describe('Users API', async () => {
const { messages } = res.body.errors[0]; const { messages } = res.body.errors[0];
expect(field).to.be.equal('password'); expect(field).to.be.equal('password');
expect(location).to.be.equal('body'); expect(location).to.be.equal('body');
expect(messages).to.include('"password" length must be at least 6 characters long');
expect(messages).to.include(
'"password" length must be at least 6 characters long',
);
}); });
}); });
@ -516,7 +522,7 @@ describe('Users API', async () => {
}); });
describe('GET /v1/users/profile', () => { describe('GET /v1/users/profile', () => {
it('should get the logged user\'s info', () => {
it("should get the logged user's info", () => {
delete dbUsers.jonSnow.password; delete dbUsers.jonSnow.password;
return request(app) return request(app)
@ -531,10 +537,12 @@ describe('Users API', async () => {
it('should report error without stacktrace when accessToken is expired', async () => { it('should report error without stacktrace when accessToken is expired', async () => {
// fake time // fake time
const clock = sinon.useFakeTimers(); const clock = sinon.useFakeTimers();
const expiredAccessToken = (await User.findAndGenerateToken(dbUsers.branStark)).accessToken;
const expiredAccessToken = (
await User.findAndGenerateToken(dbUsers.branStark)
).accessToken;
// move clock forward by minutes set in config + 1 minute // move clock forward by minutes set in config + 1 minute
clock.tick((JWT_EXPIRATION * 60000) + 60000);
clock.tick(JWT_EXPIRATION * 60000 + 60000);
return request(app) return request(app)
.get('/v1/users/profile') .get('/v1/users/profile')


+ 6
- 4
src/api/utils/APIError.js View File

@ -4,9 +4,7 @@ const httpStatus = require('http-status');
* @extends Error * @extends Error
*/ */
class ExtendableError extends Error { class ExtendableError extends Error {
constructor({
message, errors, status, isPublic, stack,
}) {
constructor({ message, errors, status, isPublic, stack }) {
super(message); super(message);
this.name = this.constructor.name; this.name = this.constructor.name;
this.message = message; this.message = message;
@ -38,7 +36,11 @@ class APIError extends ExtendableError {
isPublic = false, isPublic = false,
}) { }) {
super({ super({
message, errors, status, isPublic, stack,
message,
errors,
status,
isPublic,
stack,
}); });
} }
} }


+ 755
- 491
src/api/utils/passwordResetEmailTemplate.html
File diff suppressed because it is too large
View File


+ 8
- 26
src/api/validations/auth.validation.js View File

@ -4,25 +4,16 @@ module.exports = {
// POST /v1/auth/register // POST /v1/auth/register
register: { register: {
body: Joi.object({ body: Joi.object({
email: Joi.string()
.email()
.required(),
password: Joi.string()
.required()
.min(6)
.max(128),
email: Joi.string().email().required(),
password: Joi.string().required().min(6).max(128),
}), }),
}, },
// POST /v1/auth/login // POST /v1/auth/login
login: { login: {
body: Joi.object({ body: Joi.object({
email: Joi.string()
.email()
.required(),
password: Joi.string()
.required()
.max(128),
email: Joi.string().email().required(),
password: Joi.string().required().max(128),
}), }),
}, },
@ -37,9 +28,7 @@ module.exports = {
// POST /v1/auth/refresh // POST /v1/auth/refresh
refresh: { refresh: {
body: Joi.object({ body: Joi.object({
email: Joi.string()
.email()
.required(),
email: Joi.string().email().required(),
refreshToken: Joi.string().required(), refreshToken: Joi.string().required(),
}), }),
}, },
@ -47,22 +36,15 @@ module.exports = {
// POST /v1/auth/refresh // POST /v1/auth/refresh
sendPasswordReset: { sendPasswordReset: {
body: Joi.object({ body: Joi.object({
email: Joi.string()
.email()
.required(),
email: Joi.string().email().required(),
}), }),
}, },
// POST /v1/auth/password-reset // POST /v1/auth/password-reset
passwordReset: { passwordReset: {
body: Joi.object({ body: Joi.object({
email: Joi.string()
.email()
.required(),
password: Joi.string()
.required()
.min(6)
.max(128),
email: Joi.string().email().required(),
password: Joi.string().required().min(6).max(128),
resetToken: Joi.string().required(), resetToken: Joi.string().required(),
}), }),
}, },


+ 8
- 5
src/api/validations/user.validation.js View File

@ -2,7 +2,6 @@ const Joi = require('joi');
const User = require('../models/user.model'); const User = require('../models/user.model');
module.exports = { module.exports = {
// GET /v1/users // GET /v1/users
listUsers: { listUsers: {
query: Joi.object({ query: Joi.object({
@ -20,7 +19,7 @@ module.exports = {
email: Joi.string().email().required(), email: Joi.string().email().required(),
password: Joi.string().min(6).max(128).required(), password: Joi.string().min(6).max(128).required(),
name: Joi.string().max(128), name: Joi.string().max(128),
role: Joi.string()
role: Joi.string(),
}), }),
}, },
@ -30,10 +29,12 @@ module.exports = {
email: Joi.string().email().required(), email: Joi.string().email().required(),
password: Joi.string().min(6).max(128).required(), password: Joi.string().min(6).max(128).required(),
name: Joi.string().max(128), name: Joi.string().max(128),
role: Joi.string()
role: Joi.string(),
}), }),
params: { params: {
userId: Joi.string().regex(/^[a-fA-F0-9]{24}$/).required(),
userId: Joi.string()
.regex(/^[a-fA-F0-9]{24}$/)
.required(),
}, },
}, },
@ -46,7 +47,9 @@ module.exports = {
role: Joi.string(), role: Joi.string(),
}), }),
params: { params: {
userId: Joi.string().regex(/^[a-fA-F0-9]{24}$/).required(),
userId: Joi.string()
.regex(/^[a-fA-F0-9]{24}$/)
.required(),
}, },
}, },
}; };

+ 3
- 3
src/config/express.js View File

@ -12,9 +12,9 @@ const strategies = require('./passport');
const error = require('../api/middlewares/error'); const error = require('../api/middlewares/error');
/** /**
* Express instance
* @public
*/
* Express instance
* @public
*/
const app = express(); const app = express();
// request logging. dev: console | production: file // request logging. dev: console | production: file


+ 5
- 3
src/config/logger.js View File

@ -18,9 +18,11 @@ const logger = winston.createLogger({
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) ` // `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
// //
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple(),
}));
logger.add(
new winston.transports.Console({
format: winston.format.simple(),
}),
);
} }
logger.stream = { logger.stream = {


+ 1
- 1
src/config/passport.js View File

@ -20,7 +20,7 @@ const jwt = async (payload, done) => {
} }
}; };
const oAuth = service => async (token, done) => {
const oAuth = (service) => async (token, done) => {
try { try {
const userData = await authProviders[service](token); const userData = await authProviders[service](token);
const user = await User.oAuthLogin(userData); const user = await User.oAuthLogin(userData);


+ 6
- 3
src/config/vars.js View File

@ -1,8 +1,8 @@
const path = require('path'); const path = require('path');
// import .env variables // import .env variables
require('dotenv-safe').load({
path: path.join(__dirname, '../../.env'),
require('dotenv').config({
path: path.join(__dirname, `../../.env.${process.env.NODE_ENV}`),
sample: path.join(__dirname, '../../.env.example'), sample: path.join(__dirname, '../../.env.example'),
}); });
@ -12,7 +12,10 @@ module.exports = {
jwtSecret: process.env.JWT_SECRET, jwtSecret: process.env.JWT_SECRET,
jwtExpirationInterval: process.env.JWT_EXPIRATION_MINUTES, jwtExpirationInterval: process.env.JWT_EXPIRATION_MINUTES,
mongo: { mongo: {
uri: process.env.NODE_ENV === 'test' ? process.env.MONGO_URI_TESTS : process.env.MONGO_URI,
uri:
process.env.NODE_ENV === 'test'
? process.env.MONGO_URI_TESTS
: process.env.MONGO_URI,
}, },
logs: process.env.NODE_ENV === 'production' ? 'combined' : 'dev', logs: process.env.NODE_ENV === 'production' ? 'combined' : 'dev',
emailConfig: { emailConfig: {


+ 3
- 3
src/index.js View File

@ -10,7 +10,7 @@ mongoose.connect();
app.listen(port, () => logger.info(`server started on port ${port} (${env})`)); app.listen(port, () => logger.info(`server started on port ${port} (${env})`));
/** /**
* Exports express
* @public
*/
* Exports express
* @public
*/
module.exports = app; module.exports = app;

+ 150
- 44
yarn.lock View File

@ -218,14 +218,14 @@
resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz" resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz"
integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q== integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==
"@eslint-community/eslint-utils@^4.2.0":
"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0" version "4.4.0"
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
dependencies: dependencies:
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
"@eslint-community/regexpp@^4.6.1":
"@eslint-community/regexpp@^4.6.0", "@eslint-community/regexpp@^4.6.1":
version "4.10.0" version "4.10.0"
resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz" resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz"
integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
@ -478,6 +478,11 @@
"@opencensus/core" "^0.0.8" "@opencensus/core" "^0.0.8"
uuid "^3.2.1" uuid "^3.2.1"
"@pkgr/core@^0.1.0":
version "0.1.1"
resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz"
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
"@pm2/agent@~2.0.0": "@pm2/agent@~2.0.0":
version "2.0.3" version "2.0.3"
resolved "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.3.tgz" resolved "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.3.tgz"
@ -619,7 +624,7 @@
"@types/eslint" "*" "@types/eslint" "*"
"@types/estree" "*" "@types/estree" "*"
"@types/eslint@*":
"@types/eslint@*", "@types/eslint@>=8.0.0":
version "8.56.5" version "8.56.5"
resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz" resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz"
integrity sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw== integrity sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==
@ -1394,6 +1399,18 @@ buffer-from@^1.0.0:
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
builtin-modules@^3.3.0:
version "3.3.0"
resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz"
integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
builtins@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz"
integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==
dependencies:
semver "^7.0.0"
bytes@3.0.0: bytes@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz"
@ -1760,11 +1777,6 @@ concat-map@0.0.1:
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
confusing-browser-globals@^1.0.10:
version "1.0.11"
resolved "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz"
integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==
constantinople@^4.0.1: constantinople@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz" resolved "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz"
@ -1847,13 +1859,6 @@ croner@~4.1.92:
resolved "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz" resolved "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz"
integrity sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ== integrity sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz"
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
dependencies:
cross-spawn "^7.0.1"
cross-spawn@^6.0.0: cross-spawn@^6.0.0:
version "6.0.5" version "6.0.5"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz"
@ -1865,7 +1870,7 @@ cross-spawn@^6.0.0:
shebang-command "^1.2.0" shebang-command "^1.2.0"
which "^1.2.9" which "^1.2.9"
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3" version "7.0.3"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@ -2240,7 +2245,22 @@ domutils@^3.0.1:
domelementtype "^2.3.0" domelementtype "^2.3.0"
domhandler "^5.0.3" domhandler "^5.0.3"
dotenv@^16.4.5:
dotenv-cli@^7.4.1:
version "7.4.1"
resolved "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.4.1.tgz"
integrity sha512-fE1aywjRrWGxV3miaiUr3d2zC/VAiuzEGghi+QzgIA9fEf/M5hLMaRSXb4IxbUAwGmaLi0IozdZddnVU96acag==
dependencies:
cross-spawn "^7.0.3"
dotenv "^16.3.0"
dotenv-expand "^10.0.0"
minimist "^1.2.6"
dotenv-expand@^10.0.0:
version "10.0.0"
resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz"
integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==
dotenv@^16.3.0, dotenv@^16.4.5:
version "16.4.5" version "16.4.5"
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz" resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz"
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
@ -2538,15 +2558,15 @@ escodegen@^2.1.0:
optionalDependencies: optionalDependencies:
source-map "~0.6.1" source-map "~0.6.1"
eslint-config-airbnb-base@^15.0.0:
version "15.0.0"
resolved "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz"
integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==
dependencies:
confusing-browser-globals "^1.0.10"
object.assign "^4.1.2"
object.entries "^1.1.5"
semver "^6.3.0"
eslint-compat-utils@^0.1.2:
version "0.1.2"
resolved "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz"
integrity sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==
eslint-config-standard@^17.1.0:
version "17.1.0"
resolved "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz"
integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==
eslint-import-resolver-node@^0.3.9: eslint-import-resolver-node@^0.3.9:
version "0.3.9" version "0.3.9"
@ -2564,7 +2584,16 @@ eslint-module-utils@^2.8.0:
dependencies: dependencies:
debug "^3.2.7" debug "^3.2.7"
eslint-plugin-import@^2.25.2, eslint-plugin-import@^2.25.3:
eslint-plugin-es-x@^7.5.0:
version "7.5.0"
resolved "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz"
integrity sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==
dependencies:
"@eslint-community/eslint-utils" "^4.1.2"
"@eslint-community/regexpp" "^4.6.0"
eslint-compat-utils "^0.1.2"
eslint-plugin-import@^2.25.2, eslint-plugin-import@^2.29.1:
version "2.29.1" version "2.29.1"
resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz" resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz"
integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==
@ -2587,6 +2616,36 @@ eslint-plugin-import@^2.25.2, eslint-plugin-import@^2.25.3:
semver "^6.3.1" semver "^6.3.1"
tsconfig-paths "^3.15.0" tsconfig-paths "^3.15.0"
"eslint-plugin-n@^15.0.0 || ^16.0.0 ", eslint-plugin-n@^16.6.2:
version "16.6.2"
resolved "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz"
integrity sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
builtins "^5.0.1"
eslint-plugin-es-x "^7.5.0"
get-tsconfig "^4.7.0"
globals "^13.24.0"
ignore "^5.2.4"
is-builtin-module "^3.2.1"
is-core-module "^2.12.1"
minimatch "^3.1.2"
resolve "^1.22.2"
semver "^7.5.3"
eslint-plugin-prettier@^5.1.3:
version "5.1.3"
resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz"
integrity sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==
dependencies:
prettier-linter-helpers "^1.0.0"
synckit "^0.8.6"
eslint-plugin-promise@^6.0.0, eslint-plugin-promise@^6.1.1:
version "6.1.1"
resolved "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz"
integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==
eslint-scope@^7.2.2: eslint-scope@^7.2.2:
version "7.2.2" version "7.2.2"
resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz"
@ -2608,7 +2667,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.32.0 || ^8.2.0", eslint@^8.5.0:
"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", eslint@^8.0.1, eslint@^8.57.0, eslint@>=6.0.0, eslint@>=7.0.0, eslint@>=8, eslint@>=8.0.0:
version "8.57.0" version "8.57.0"
resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz"
integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==
@ -2821,6 +2880,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-diff@^1.1.2:
version "1.3.0"
resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz"
integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
fast-json-patch@^3.0.0-1: fast-json-patch@^3.0.0-1:
version "3.1.1" version "3.1.1"
resolved "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz" resolved "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz"
@ -3151,6 +3215,13 @@ get-symbol-description@^1.0.2:
es-errors "^1.3.0" es-errors "^1.3.0"
get-intrinsic "^1.2.4" get-intrinsic "^1.2.4"
get-tsconfig@^4.7.0:
version "4.7.3"
resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz"
integrity sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==
dependencies:
resolve-pkg-maps "^1.0.0"
get-uri@^6.0.1: get-uri@^6.0.1:
version "6.0.3" version "6.0.3"
resolved "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz" resolved "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz"
@ -3225,7 +3296,7 @@ globals@^11.1.0:
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globals@^13.19.0:
globals@^13.19.0, globals@^13.24.0:
version "13.24.0" version "13.24.0"
resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz" resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz"
integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
@ -3482,7 +3553,7 @@ ignore-by-default@^1.0.1:
resolved "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz" resolved "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz"
integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==
ignore@^5.2.0:
ignore@^5.2.0, ignore@^5.2.4:
version "5.3.1" version "5.3.1"
resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz" resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz"
integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
@ -3603,12 +3674,19 @@ is-boolean-object@^1.1.0:
call-bind "^1.0.2" call-bind "^1.0.2"
has-tostringtag "^1.0.0" has-tostringtag "^1.0.0"
is-builtin-module@^3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz"
integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==
dependencies:
builtin-modules "^3.3.0"
is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
version "1.2.7" version "1.2.7"
resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz"
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
is-core-module@^2.13.0, is-core-module@^2.13.1:
is-core-module@^2.12.1, is-core-module@^2.13.0, is-core-module@^2.13.1:
version "2.13.1" version "2.13.1"
resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz"
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
@ -4557,6 +4635,18 @@ module-details-from-path@^1.0.3:
resolved "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz" resolved "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz"
integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==
moment-timezone@^0.5.45:
version "0.5.45"
resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz"
integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==
dependencies:
moment "^2.29.4"
moment@^2.29.4:
version "2.30.1"
resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz"
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
mongodb-connection-string-url@^3.0.0: mongodb-connection-string-url@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz" resolved "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz"
@ -4859,7 +4949,7 @@ object-keys@^1.1.1:
resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object.assign@^4.1.2, object.assign@^4.1.5:
object.assign@^4.1.5:
version "4.1.5" version "4.1.5"
resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz" resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz"
integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
@ -4869,15 +4959,6 @@ object.assign@^4.1.2, object.assign@^4.1.5:
has-symbols "^1.0.3" has-symbols "^1.0.3"
object-keys "^1.1.1" object-keys "^1.1.1"
object.entries@^1.1.5:
version "1.1.7"
resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz"
integrity sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==
dependencies:
call-bind "^1.0.2"
define-properties "^1.2.0"
es-abstract "^1.22.1"
object.fromentries@^2.0.7: object.fromentries@^2.0.7:
version "2.0.7" version "2.0.7"
resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz" resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz"
@ -5375,6 +5456,18 @@ prelude-ls@^1.2.1:
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prettier-linter-helpers@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz"
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
dependencies:
fast-diff "^1.1.2"
prettier@^3.2.5, prettier@>=3.0.0:
version "3.2.5"
resolved "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz"
integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==
preview-email@^3.0.17: preview-email@^3.0.17:
version "3.0.19" version "3.0.19"
resolved "https://registry.npmjs.org/preview-email/-/preview-email-3.0.19.tgz" resolved "https://registry.npmjs.org/preview-email/-/preview-email-3.0.19.tgz"
@ -5761,7 +5854,12 @@ resolve-from@^5.0.0:
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve@^1.10.0, resolve@^1.15.1, resolve@^1.22.1, resolve@^1.22.4, resolve@^1.9.0:
resolve-pkg-maps@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz"
integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==
resolve@^1.10.0, resolve@^1.15.1, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4, resolve@^1.9.0:
version "1.22.8" version "1.22.8"
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
@ -5906,7 +6004,7 @@ semver@^6.3.1:
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4:
semver@^7.0.0, semver@^7.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4:
version "7.6.0" version "7.6.0"
resolved "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz" resolved "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz"
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
@ -6385,6 +6483,14 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
synckit@^0.8.6:
version "0.8.8"
resolved "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz"
integrity sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==
dependencies:
"@pkgr/core" "^0.1.0"
tslib "^2.6.2"
systeminformation@^5.7: systeminformation@^5.7:
version "5.22.0" version "5.22.0"
resolved "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.0.tgz" resolved "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.0.tgz"
@ -6535,7 +6641,7 @@ tsconfig-paths@^3.15.0:
minimist "^1.2.6" minimist "^1.2.6"
strip-bom "^3.0.0" strip-bom "^3.0.0"
tslib@^2.0.1:
tslib@^2.0.1, tslib@^2.6.2:
version "2.6.2" version "2.6.2"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==


Loading…
Cancel
Save