diff --git a/package-lock.json b/package-lock.json index 4b4526c..e86726e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,15 @@ "name": "six-cities", "version": "5.0.0", "dependencies": { + "convict": "6.2.4", + "convict-format-with-validator": "6.2.0", "dotenv": "16.3.1", "got": "13.0.0", "pino": "8.15.1" }, "devDependencies": { + "@types/convict": "6.1.4", + "@types/convict-format-with-validator": "6.0.3", "@types/node": "18.17.17", "@typescript-eslint/eslint-plugin": "6.7.0", "@typescript-eslint/parser": "6.7.0", @@ -777,6 +781,26 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "node_modules/@types/convict": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/convict/-/convict-6.1.4.tgz", + "integrity": "sha512-e2RQtTfJGej3vNXp2YyVVaxm934C+kVGMwwSGyRs1KEg3DETS3jLyVOBHtSKHLH/18lYqxuq+aUlv8oUAXE9sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/convict-format-with-validator": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/convict-format-with-validator/-/convict-format-with-validator-6.0.3.tgz", + "integrity": "sha512-GyMCYgkCr+1TtIDEKEbP4uNlrD09/OvAmKRBoBniIj5pCZ0NQfVrjwjd4FI3j78wSDlnkjM4XfJSzE2cDhFZgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/convict": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -1751,6 +1775,40 @@ "node": ">= 0.6" } }, + "node_modules/convict": { + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/convict/-/convict-6.2.4.tgz", + "integrity": "sha512-qN60BAwdMVdofckX7AlohVJ2x9UvjTNoKVXCL2LxFk1l7757EJqf1nySdMkPQer0bt8kQ5lQiyZ9/2NvrFBuwQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "yargs-parser": "^20.2.7" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/convict-format-with-validator": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/convict-format-with-validator/-/convict-format-with-validator-6.2.0.tgz", + "integrity": "sha512-2LIL3yEZY27M13UHLIP4mGivusP9h2M+X4mYsRBLexwUp8+0sgVk2MgB2b2bnQwkn293lkbkxgdevzn0nZdyzQ==", + "license": "Apache-2.0", + "dependencies": { + "validator": "^13.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/convict/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -4538,6 +4596,12 @@ "node": ">= 4" } }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6888,6 +6952,15 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -7603,6 +7676,24 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "@types/convict": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/convict/-/convict-6.1.4.tgz", + "integrity": "sha512-e2RQtTfJGej3vNXp2YyVVaxm934C+kVGMwwSGyRs1KEg3DETS3jLyVOBHtSKHLH/18lYqxuq+aUlv8oUAXE9sQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/convict-format-with-validator": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/convict-format-with-validator/-/convict-format-with-validator-6.0.3.tgz", + "integrity": "sha512-GyMCYgkCr+1TtIDEKEbP4uNlrD09/OvAmKRBoBniIj5pCZ0NQfVrjwjd4FI3j78wSDlnkjM4XfJSzE2cDhFZgg==", + "dev": true, + "requires": { + "@types/convict": "*" + } + }, "@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -8252,6 +8343,30 @@ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true }, + "convict": { + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/convict/-/convict-6.2.4.tgz", + "integrity": "sha512-qN60BAwdMVdofckX7AlohVJ2x9UvjTNoKVXCL2LxFk1l7757EJqf1nySdMkPQer0bt8kQ5lQiyZ9/2NvrFBuwQ==", + "requires": { + "lodash.clonedeep": "^4.5.0", + "yargs-parser": "^20.2.7" + }, + "dependencies": { + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + } + } + }, + "convict-format-with-validator": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/convict-format-with-validator/-/convict-format-with-validator-6.2.0.tgz", + "integrity": "sha512-2LIL3yEZY27M13UHLIP4mGivusP9h2M+X4mYsRBLexwUp8+0sgVk2MgB2b2bnQwkn293lkbkxgdevzn0nZdyzQ==", + "requires": { + "validator": "^13.6.0" + } + }, "cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -10197,6 +10312,11 @@ "integrity": "sha512-ikQPBTiq/d5m6dfKQlFdIXFzvThPi2Be9/AHxktOnDSfSxE1j9ICbBT5Elk1ke7HSTgM38LHTpmJovo9/klnLg==", "dev": true }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -11822,6 +11942,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 9dd5636..134df97 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "mock:server": "json-server mocks/mock-server-data.json --port 3123" }, "devDependencies": { + "@types/convict": "6.1.4", + "@types/convict-format-with-validator": "6.0.3", "@types/node": "18.17.17", "@typescript-eslint/eslint-plugin": "6.7.0", "@typescript-eslint/parser": "6.7.0", @@ -39,6 +41,8 @@ "npm": ">=8" }, "dependencies": { + "convict": "6.2.4", + "convict-format-with-validator": "6.2.0", "dotenv": "16.3.1", "got": "13.0.0", "pino": "8.15.1" diff --git a/src/rest/rest.application.ts b/src/rest/rest.application.ts index 1c670aa..e4b088f 100644 --- a/src/rest/rest.application.ts +++ b/src/rest/rest.application.ts @@ -1,10 +1,10 @@ import { Logger } from '../shared/libs/logger/index.js'; -import {Config} from '../shared/libs/config/index.js'; +import {Config, RestSchema} from '../shared/libs/config/index.js'; export class RestApplication { constructor( private readonly logger: Logger, - private readonly config: Config, + private readonly config: Config, ) {} public async init() { diff --git a/src/shared/libs/config/config.interface.ts b/src/shared/libs/config/config.interface.ts index 78bfe29..4310393 100644 --- a/src/shared/libs/config/config.interface.ts +++ b/src/shared/libs/config/config.interface.ts @@ -1,3 +1,3 @@ -export interface Config { - get(key: string): string | undefined; +export interface Config { + get(key: T): U[T]; } diff --git a/src/shared/libs/config/index.ts b/src/shared/libs/config/index.ts index 7f52b47..5a4c363 100644 --- a/src/shared/libs/config/index.ts +++ b/src/shared/libs/config/index.ts @@ -1,2 +1,3 @@ export * from './rest.config.js'; export * from './config.interface.js'; +export * from './rest.schema.js'; diff --git a/src/shared/libs/config/rest.config.ts b/src/shared/libs/config/rest.config.ts index 405eee2..77810ab 100644 --- a/src/shared/libs/config/rest.config.ts +++ b/src/shared/libs/config/rest.config.ts @@ -1,9 +1,10 @@ -import { Config } from './config.interface.js'; +import {Config} from './config.interface.js'; import {Logger} from '../logger/index.js'; -import {config, DotenvParseOutput} from 'dotenv'; +import {config} from 'dotenv'; +import {configRestSchema, RestSchema} from './rest.schema.js'; -export class RestConfig implements Config { - private readonly config: NodeJS.ProcessEnv; +export class RestConfig implements Config { + private readonly config: RestSchema; constructor( private readonly logger: Logger @@ -14,11 +15,14 @@ export class RestConfig implements Config { throw new Error('Can\'t read .env file. Perhaps the file does not exists.'); } - this.config = parsedOutput.parsed; + configRestSchema.load({}); + configRestSchema.validate({allowed: 'strict', output: this.logger.info}); + + this.config = configRestSchema.getProperties(); this.logger.info('.env file found and successfully parsed!'); } - public get(key: string): string | undefined { + public get(key: T): RestSchema[T] { return this.config[key]; } } diff --git a/src/shared/libs/config/rest.schema.ts b/src/shared/libs/config/rest.schema.ts new file mode 100644 index 0000000..dbb9fb5 --- /dev/null +++ b/src/shared/libs/config/rest.schema.ts @@ -0,0 +1,32 @@ +import convict from 'convict'; +import validator from 'convict-format-with-validator'; + +convict.addFormats(validator); + + +export type RestSchema = { + PORT: number; + SALT: string; + DB_HOST: string; +} + +export const configRestSchema = convict({ + PORT: { + doc: 'Port for incoming connections', + format: 'port', + env: 'PORT', + default: 4000 + }, + SALT: { + doc: 'Salt for password hash', + format: String, + env: 'SALT', + default: null + }, + DB_HOST: { + doc: 'IP address of the database server (MongoDB)', + format: 'ipaddress', + env: 'DB_HOST', + default: '127.0.0.1' + }, +});