eslint and prettier added

This commit is contained in:
krish 2024-03-12 12:05:13 +05:30
parent f59c2d8e97
commit f5a5955656
30 changed files with 1529 additions and 876 deletions

27
.eslintrc.js Normal file
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
.prettierrc Normal file
View File

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

View File

@ -1,11 +1,11 @@
## 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
@ -155,4 +155,3 @@ Run deploy script:
```bash ```bash
yarn deploy yarn deploy
``` ```

View File

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

View File

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

View File

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

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'

350
package-lock.json generated
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": "^8.57.0",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.25.3", "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": { "node_modules/eslint-compat-utils": {
"version": "15.0.0", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz",
"integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", "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": ">=6.0.0"
"eslint-plugin-import": "^2.25.2"
} }
}, },
"node_modules/eslint-config-airbnb-base/node_modules/semver": { "node_modules/eslint-config-standard": {
"version": "6.3.1", "version": "17.1.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==",
"dev": true, "dev": true,
"bin": { "funding": [
"semver": "bin/semver.js" {
"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",

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", "start:dev": "dotenv -v NODE_ENV=development -- nodemon ./src/index.js",
"dev": "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:fix": "npx eslint --fix",
"lint:watch": "yarn lint --watch", "lint:watch": "npx eslint --watch",
"test": "cross-env NODE_ENV=test nyc --reporter=html --reporter=text mocha --timeout 20000 --exit --recursive src/api/tests", "format": "npx prettier --write .",
"test:unit": "cross-env NODE_ENV=test mocha src/api/tests/unit", "test": "dotenv -v NODE_ENV=test -- nyc --reporter=html --reporter=text mocha --timeout 20000 --exit --recursive src/api/tests",
"test:integration": "cross-env NODE_ENV=test mocha --timeout 20000 --exit src/api/tests/integration", "test:unit": "dotenv -v NODE_ENV=test -- mocha src/api/tests/unit",
"test:watch": "cross-env NODE_ENV=test mocha --watch 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": "^8.57.0",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.25.3", "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"

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);

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() user
.then(savedUser => res.json(savedUser.transform())) .save()
.catch(e => next(User.checkDuplicateEmail(e))); .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));
}; };

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) => exports.authorize =
(roles = User.roles) =>
(req, res, next) =>
passport.authenticate( passport.authenticate(
'jwt', { session: false }, 'jwt',
{ session: false },
handleJWT(req, res, next, roles), handleJWT(req, res, next, roles),
)(req, res, next); )(req, res, next);
exports.oAuth = service => exports.oAuth = (service) => passport.authenticate(service, { session: false });
passport.authenticate(service, { session: false });

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() const expires = moment().add(2, 'hours').toDate();
.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;

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;
}, },
}; };
/** /**

View File

@ -17,7 +17,8 @@ const roles = ['user', 'admin'];
* User Schema * User Schema
* @private * @private
*/ */
const userSchema = new mongoose.Schema({ const userSchema = new mongoose.Schema(
{
email: { email: {
type: String, type: String,
match: /^\S+@\S+\.\S+$/, match: /^\S+@\S+\.\S+$/,
@ -51,9 +52,11 @@ const userSchema = new mongoose.Schema({
type: String, type: String,
trim: true, 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({ list({ page = 1, perPage = 30, name, email, role }) {
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: [{ errors: [
{
field: 'email', field: 'email',
location: 'body', location: 'body',
messages: ['"email" already exists'], 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({ async oAuthLogin({ service, id, email, name, picture }) {
service, id, email, name, picture, const user = await this.findOne({
}) { $or: [{ [`services.${service}`]: id }, { email }],
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,
}); });
}, },
}; };

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') router.route('/register').post(validate(register), controller.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') router.route('/login').post(validate(login), controller.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') router.route('/refresh-token').post(validate(refresh), controller.refresh);
.post(validate(refresh), controller.refresh);
router
router.route('/send-password-reset') .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;

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;

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 { const { id, name, email, picture } = response.data;
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 { const { sub, name, email, picture } = response.data;
sub, name, email, picture,
} = response.data;
return { return {
service: 'google', service: 'google',
picture, picture,

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() expires: moment().add(1, 'day').toDate(),
.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() expires: moment().add(2, 'hours').toDate(),
.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() expires: moment().subtract(1, 'day').toDate(),
.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() expires: moment().subtract(2, 'hours').toDate(),
.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.resetToken).to.be.equal(
expect(PasswordResetTokenObj.userId.toString()).to.be.equal('5947397b323ae82d8c3a333b'); '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() expect(PasswordResetTokenObj.expires).to.be.above(
.add(1, 'hour') moment().add(1, 'hour').toDate(),
.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.resetToken).to.be.equal(
expect(expiredPasswordResetTokenObj.userId.toString()).to.be.equal('5947397b323ae82d8c3a333b'); '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')

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; adminAccessToken = (await User.findAndGenerateToken(dbUsers.branStark))
userAccessToken = (await User.findAndGenerateToken(dbUsers.jonSnow)).accessToken; .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')

View File

@ -4,9 +4,7 @@ const httpStatus = require('http-status');
* @extends Error * @extends Error
*/ */
class ExtendableError extends Error { class ExtendableError extends Error {
constructor({ constructor({ message, errors, status, isPublic, stack }) {
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,
}); });
} }
} }

View File

@ -1,7 +1,5 @@
<!DOCTYPE html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" /> <meta name="x-apple-disable-message-reformatting" />
@ -10,7 +8,7 @@
<style type="text/css" rel="stylesheet" media="all"> <style type="text/css" rel="stylesheet" media="all">
/* Base ------------------------------ */ /* Base ------------------------------ */
@import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&amp;display=swap"); @import url('https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&amp;display=swap');
body { body {
width: 100% !important; width: 100% !important;
@ -20,7 +18,7 @@
} }
a { a {
color: #3869D4; color: #3869d4;
} }
a img { a img {
@ -48,7 +46,7 @@
body, body,
td, td,
th { th {
font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif;
} }
h1 { h1 {
@ -84,7 +82,7 @@
ul, ul,
ol, ol,
blockquote { blockquote {
margin: .4em 0 1.1875em; margin: 0.4em 0 1.1875em;
font-size: 16px; font-size: 16px;
line-height: 1.625; line-height: 1.625;
} }
@ -110,13 +108,13 @@
/* Buttons ------------------------------ */ /* Buttons ------------------------------ */
.button { .button {
background-color: #3869D4; background-color: #3869d4;
border-top: 10px solid #3869D4; border-top: 10px solid #3869d4;
border-right: 18px solid #3869D4; border-right: 18px solid #3869d4;
border-bottom: 10px solid #3869D4; border-bottom: 10px solid #3869d4;
border-left: 18px solid #3869D4; border-left: 18px solid #3869d4;
display: inline-block; display: inline-block;
color: #FFF; color: #fff;
text-decoration: none; text-decoration: none;
border-radius: 3px; border-radius: 3px;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
@ -125,19 +123,19 @@
} }
.button--green { .button--green {
background-color: #22BC66; background-color: #22bc66;
border-top: 10px solid #22BC66; border-top: 10px solid #22bc66;
border-right: 18px solid #22BC66; border-right: 18px solid #22bc66;
border-bottom: 10px solid #22BC66; border-bottom: 10px solid #22bc66;
border-left: 18px solid #22BC66; border-left: 18px solid #22bc66;
} }
.button--red { .button--red {
background-color: #FF6136; background-color: #ff6136;
border-top: 10px solid #FF6136; border-top: 10px solid #ff6136;
border-right: 18px solid #FF6136; border-right: 18px solid #ff6136;
border-bottom: 10px solid #FF6136; border-bottom: 10px solid #ff6136;
border-left: 18px solid #FF6136; border-left: 18px solid #ff6136;
} }
@media only screen and (max-width: 500px) { @media only screen and (max-width: 500px) {
@ -154,7 +152,7 @@
} }
.attributes_content { .attributes_content {
background-color: #F4F4F7; background-color: #f4f4f7;
padding: 16px; padding: 16px;
} }
@ -175,14 +173,14 @@
.related_item { .related_item {
padding: 10px 0; padding: 10px 0;
color: #CBCCCF; color: #cbcccf;
font-size: 15px; font-size: 15px;
line-height: 18px; line-height: 18px;
} }
.related_item-title { .related_item-title {
display: block; display: block;
margin: .5em 0 0; margin: 0.5em 0 0;
} }
.related_item-thumb { .related_item-thumb {
@ -191,7 +189,7 @@
} }
.related_heading { .related_heading {
border-top: 1px solid #CBCCCF; border-top: 1px solid #cbcccf;
text-align: center; text-align: center;
padding: 25px 0 10px; padding: 25px 0 10px;
} }
@ -205,8 +203,8 @@
-premailer-width: 100%; -premailer-width: 100%;
-premailer-cellpadding: 0; -premailer-cellpadding: 0;
-premailer-cellspacing: 0; -premailer-cellspacing: 0;
background-color: #F4F4F7; background-color: #f4f4f7;
border: 2px dashed #CBCCCF; border: 2px dashed #cbcccf;
} }
.discount_heading { .discount_heading {
@ -257,25 +255,25 @@
.purchase_item { .purchase_item {
padding: 10px 0; padding: 10px 0;
color: #51545E; color: #51545e;
font-size: 15px; font-size: 15px;
line-height: 18px; line-height: 18px;
} }
.purchase_heading { .purchase_heading {
padding-bottom: 8px; padding-bottom: 8px;
border-bottom: 1px solid #EAEAEC; border-bottom: 1px solid #eaeaec;
} }
.purchase_heading p { .purchase_heading p {
margin: 0; margin: 0;
color: #85878E; color: #85878e;
font-size: 12px; font-size: 12px;
} }
.purchase_footer { .purchase_footer {
padding-top: 15px; padding-top: 15px;
border-top: 1px solid #EAEAEC; border-top: 1px solid #eaeaec;
} }
.purchase_total { .purchase_total {
@ -290,7 +288,7 @@
} }
body { body {
background-color: #FFF; background-color: #fff;
color: #333; color: #333;
} }
@ -330,7 +328,7 @@
.email-masthead_name { .email-masthead_name {
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
color: #A8AAAF; color: #a8aaaf;
text-decoration: none; text-decoration: none;
text-shadow: 0 1px 0 white; text-shadow: 0 1px 0 white;
} }
@ -366,7 +364,7 @@
} }
.email-footer p { .email-footer p {
color: #A8AAAF; color: #a8aaaf;
} }
.body-action { .body-action {
@ -382,7 +380,7 @@
.body-sub { .body-sub {
margin-top: 25px; margin-top: 25px;
padding-top: 25px; padding-top: 25px;
border-top: 1px solid #EAEAEC; border-top: 1px solid #eaeaec;
} }
.content-cell { .content-cell {
@ -392,7 +390,6 @@
/*Media Queries ------------------------------ */ /*Media Queries ------------------------------ */
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
.email-body_inner, .email-body_inner,
.email-footer { .email-footer {
width: 100% !important; width: 100% !important;
@ -402,7 +399,7 @@
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
body { body {
background-color: #333333 !important; background-color: #333333 !important;
color: #FFF !important; color: #fff !important;
} }
p, p,
@ -412,7 +409,7 @@
h1, h1,
h2, h2,
h3 { h3 {
color: #FFF !important; color: #fff !important;
} }
.attributes_content, .attributes_content,
@ -441,107 +438,331 @@
} }
body { body {
font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif;
} }
body { body {
background-color: #FFF; background-color: #fff;
color: #333; color: #333;
} }
</style> </style>
</head> </head>
<body <body
style="width: 100% !important; height: 100%; -webkit-text-size-adjust: none; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; background-color: #FFF; color: #333; margin: 0;" style="
bgcolor="#FFF"> width: 100% !important;
<span class="preheader" height: 100%;
style="display: none !important; visibility: hidden; mso-hide: all; font-size: 1px; line-height: 1px; max-height: 0; max-width: 0; opacity: 0; overflow: hidden;">Use -webkit-text-size-adjust: none;
this link to reset your password. The link is only valid for 24 hours.</span> font-family: 'Nunito Sans', Helvetica, Arial, sans-serif;
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation" background-color: #fff;
style="width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;"> color: #333;
margin: 0;
"
bgcolor="#FFF"
>
<span
class="preheader"
style="
display: none !important;
visibility: hidden;
mso-hide: all;
font-size: 1px;
line-height: 1px;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
"
>Use this link to reset your password. The link is only valid for 24
hours.</span
>
<table
class="email-wrapper"
width="100%"
cellpadding="0"
cellspacing="0"
role="presentation"
style="
width: 100%;
-premailer-width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
margin: 0;
padding: 0;
"
>
<tr> <tr>
<td align="center" <td
style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px;"> align="center"
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="
style="width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;"> word-break: break-word;
font-family: 'Nunito Sans', Helvetica, Arial, sans-serif;
font-size: 16px;
"
>
<table
class="email-content"
width="100%"
cellpadding="0"
cellspacing="0"
role="presentation"
style="
width: 100%;
-premailer-width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
margin: 0;
padding: 0;
"
>
<tr> <tr>
<td class="email-masthead" <td
style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; text-align: center; padding: 25px 0;" class="email-masthead"
align="center"> style="
<a href="https://example.com" class="f-fallback email-masthead_name" word-break: break-word;
style="color: #A8AAAF; font-size: 16px; font-weight: bold; text-decoration: none; text-shadow: 0 1px 0 white;"> font-family: 'Nunito Sans', Helvetica, Arial, sans-serif;
font-size: 16px;
text-align: center;
padding: 25px 0;
"
align="center"
>
<a
href="https://example.com"
class="f-fallback email-masthead_name"
style="
color: #a8aaaf;
font-size: 16px;
font-weight: bold;
text-decoration: none;
text-shadow: 0 1px 0 white;
"
>
[Product Name] [Product Name]
</a> </a>
</td> </td>
</tr> </tr>
<!-- Email Body --> <!-- Email Body -->
<tr> <tr>
<td class="email-body" width="570" cellpadding="0" cellspacing="0" <td
style="word-break: break-word; margin: 0; padding: 0; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0;"> class="email-body"
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0" width="570"
cellpadding="0"
cellspacing="0"
style="
word-break: break-word;
margin: 0;
padding: 0;
font-family: 'Nunito Sans', Helvetica, Arial, sans-serif;
font-size: 16px;
width: 100%;
-premailer-width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
"
>
<table
class="email-body_inner"
align="center"
width="570"
cellpadding="0"
cellspacing="0"
role="presentation" role="presentation"
style="width: 570px; -premailer-width: 570px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0 auto; padding: 0;"> style="
width: 570px;
-premailer-width: 570px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
margin: 0 auto;
padding: 0;
"
>
<!-- Body content --> <!-- Body content -->
<tr> <tr>
<td class="content-cell" <td
style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; padding: 35px;"> class="content-cell"
style="
word-break: break-word;
font-family: 'Nunito Sans', Helvetica, Arial, sans-serif;
font-size: 16px;
padding: 35px;
"
>
<div class="f-fallback"> <div class="f-fallback">
<h1 style="margin-top: 0; color: #333333; font-size: 22px; font-weight: bold; text-align: left;" <h1
align="left">Hi {{name}},</h1> style="
margin-top: 0;
color: #333333;
font-size: 22px;
font-weight: bold;
text-align: left;
"
align="left"
>
Hi {{name}},
</h1>
<p <p
style="font-size: 16px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;"> style="
You recently requested to reset your password for your [Product Name] font-size: 16px;
account. Use the button below to reset it. <strong>This password reset line-height: 1.625;
is only valid for the next 2 hours.</strong></p> color: #333;
<!-- Action --> margin: 0.4em 0 1.1875em;
<table class="body-action" align="center" width="100%" cellpadding="0" "
cellspacing="0" role="presentation" >
style="width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; text-align: center; margin: 30px auto; padding: 0;"> You recently requested to reset your password for your
<tr> [Product Name] account. Use the button below to reset
<td align="center" it.
style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px;"> <strong
<!-- Border based button >This password reset is only valid for the next 2
https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design --> hours.</strong
<table width="100%" border="0" cellspacing="0" cellpadding="0" >
role="presentation">
<tr>
<td align="center"
style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px;">
<a href="{{action_url}}"
class="f-fallback button button--green"
target="_blank"
style="color: #FFF; border-color: #22bc66; border-style: solid; border-width: 10px 18px; background-color: #22BC66; display: inline-block; text-decoration: none; border-radius: 3px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); -webkit-text-size-adjust: none; box-sizing: border-box;">Reset
your password</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<p
style="font-size: 16px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;">
For security, this request was received from a {{operating_system}}
device using {{browser_name}}. If you did not request a password reset,
please ignore this email or <a href="{{support_url}}"
style="color: #3869D4;">contact support</a> if you have questions.
</p> </p>
<p <!-- Action -->
style="font-size: 16px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;"> <table
Thanks, class="body-action"
<br />The [Product Name] Team</p> align="center"
<!-- Sub copy --> width="100%"
<table class="body-sub" role="presentation" cellpadding="0"
style="margin-top: 25px; padding-top: 25px; border-top-width: 1px; border-top-color: #EAEAEC; border-top-style: solid;"> cellspacing="0"
role="presentation"
style="
width: 100%;
-premailer-width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
text-align: center;
margin: 30px auto;
padding: 0;
"
>
<tr> <tr>
<td <td
style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px;"> align="center"
<p class="f-fallback sub" style="
style="font-size: 13px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;"> word-break: break-word;
If youre having trouble with the button above, copy and font-family: 'Nunito Sans', Helvetica, Arial,
paste the URL below into your web browser.</p> sans-serif;
<p class="f-fallback sub" font-size: 16px;
style="font-size: 13px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;"> "
{{action_url}}</p> >
<!-- Border based button
https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design -->
<table
width="100%"
border="0"
cellspacing="0"
cellpadding="0"
role="presentation"
>
<tr>
<td
align="center"
style="
word-break: break-word;
font-family: 'Nunito Sans', Helvetica,
Arial, sans-serif;
font-size: 16px;
"
>
<a
href="{{action_url}}"
class="f-fallback button button--green"
target="_blank"
style="
color: #fff;
border-color: #22bc66;
border-style: solid;
border-width: 10px 18px;
background-color: #22bc66;
display: inline-block;
text-decoration: none;
border-radius: 3px;
box-shadow: 0 2px 3px
rgba(0, 0, 0, 0.16);
-webkit-text-size-adjust: none;
box-sizing: border-box;
"
>Reset your password</a
>
</td>
</tr>
</table>
</td>
</tr>
</table>
<p
style="
font-size: 16px;
line-height: 1.625;
color: #333;
margin: 0.4em 0 1.1875em;
"
>
For security, this request was received from a
{{operating_system}} device using {{browser_name}}. If
you did not request a password reset, please ignore
this email or
<a href="{{support_url}}" style="color: #3869d4"
>contact support</a
>
if you have questions.
</p>
<p
style="
font-size: 16px;
line-height: 1.625;
color: #333;
margin: 0.4em 0 1.1875em;
"
>
Thanks,
<br />The [Product Name] Team
</p>
<!-- Sub copy -->
<table
class="body-sub"
role="presentation"
style="
margin-top: 25px;
padding-top: 25px;
border-top-width: 1px;
border-top-color: #eaeaec;
border-top-style: solid;
"
>
<tr>
<td
style="
word-break: break-word;
font-family: 'Nunito Sans', Helvetica, Arial,
sans-serif;
font-size: 16px;
"
>
<p
class="f-fallback sub"
style="
font-size: 13px;
line-height: 1.625;
color: #333;
margin: 0.4em 0 1.1875em;
"
>
If youre having trouble with the button above,
copy and paste the URL below into your web
browser.
</p>
<p
class="f-fallback sub"
style="
font-size: 13px;
line-height: 1.625;
color: #333;
margin: 0.4em 0 1.1875em;
"
>
{{action_url}}
</p>
</td> </td>
</tr> </tr>
</table> </table>
@ -553,22 +774,66 @@
</tr> </tr>
<tr> <tr>
<td <td
style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px;"> style="
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0" word-break: break-word;
font-family: 'Nunito Sans', Helvetica, Arial, sans-serif;
font-size: 16px;
"
>
<table
class="email-footer"
align="center"
width="570"
cellpadding="0"
cellspacing="0"
role="presentation" role="presentation"
style="width: 570px; -premailer-width: 570px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; text-align: center; margin: 0 auto; padding: 0;"> style="
width: 570px;
-premailer-width: 570px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
text-align: center;
margin: 0 auto;
padding: 0;
"
>
<tr> <tr>
<td class="content-cell" align="center" <td
style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; padding: 35px;"> class="content-cell"
<p class="f-fallback sub align-center" align="center"
style="font-size: 13px; line-height: 1.625; text-align: center; color: #A8AAAF; margin: .4em 0 1.1875em;" style="
align="center">© 2019 [Product Name]. All rights reserved.</p> word-break: break-word;
<p class="f-fallback sub align-center" font-family: 'Nunito Sans', Helvetica, Arial, sans-serif;
style="font-size: 13px; line-height: 1.625; text-align: center; color: #A8AAAF; margin: .4em 0 1.1875em;" font-size: 16px;
align="center"> padding: 35px;
"
>
<p
class="f-fallback sub align-center"
style="
font-size: 13px;
line-height: 1.625;
text-align: center;
color: #a8aaaf;
margin: 0.4em 0 1.1875em;
"
align="center"
>
© 2019 [Product Name]. All rights reserved.
</p>
<p
class="f-fallback sub align-center"
style="
font-size: 13px;
line-height: 1.625;
text-align: center;
color: #a8aaaf;
margin: 0.4em 0 1.1875em;
"
align="center"
>
[Company Name, LLC] [Company Name, LLC]
<br />1234 Street Rd. <br />1234 Street Rd. <br />Suite 1234
<br />Suite 1234
</p> </p>
</td> </td>
</tr> </tr>
@ -580,5 +845,4 @@
</tr> </tr>
</table> </table>
</body> </body>
</html> </html>

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: Joi.string().email().required(),
.email() password: Joi.string().required().min(6).max(128),
.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: Joi.string().email().required(),
.email() password: Joi.string().required().max(128),
.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: Joi.string().email().required(),
.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: Joi.string().email().required(),
.email()
.required(),
}), }),
}, },
// POST /v1/auth/password-reset // POST /v1/auth/password-reset
passwordReset: { passwordReset: {
body: Joi.object({ body: Joi.object({
email: Joi.string() email: Joi.string().email().required(),
.email() password: Joi.string().required().min(6).max(128),
.required(),
password: Joi.string()
.required()
.min(6)
.max(128),
resetToken: Joi.string().required(), resetToken: Joi.string().required(),
}), }),
}, },

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(),
}, },
}, },
}; };

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({ logger.add(
new winston.transports.Console({
format: winston.format.simple(), format: winston.format.simple(),
})); }),
);
} }
logger.stream = { logger.stream = {

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);

View File

@ -1,8 +1,8 @@
const path = require('path'); const path = require('path');
// import .env variables // import .env variables
require('dotenv-safe').load({ require('dotenv').config({
path: path.join(__dirname, '../../.env'), 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: {

194
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: eslint-compat-utils@^0.1.2:
version "15.0.0" version "0.1.2"
resolved "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz" resolved "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz"
integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== integrity sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==
dependencies:
confusing-browser-globals "^1.0.10" eslint-config-standard@^17.1.0:
object.assign "^4.1.2" version "17.1.0"
object.entries "^1.1.5" resolved "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz"
semver "^6.3.0" 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==