From cb88ad13c086b2d64b1b10b8738371dca6e8d311 Mon Sep 17 00:00:00 2001 From: Karlen Date: Thu, 31 Oct 2024 08:59:53 +0300 Subject: [PATCH] Add specification --- package-lock.json | 339 ++++++++++++++++++ package.json | 1 + specification/specification.yml | 101 ++++++ .../category/category-service.interface.ts | 14 + .../modules/category/category.container.ts | 18 + .../modules/category/category.entity.ts | 14 + .../category/default-category.service.ts | 49 +++ .../category/dto/create-category.dto.ts | 3 + src/shared/modules/category/index.ts | 5 + .../comment/comment-service.interface.ts | 7 + .../modules/comment/comment.container.ts | 18 + src/shared/modules/comment/comment.entity.ts | 23 ++ .../comment/default-comment.service.ts | 23 ++ .../modules/comment/dto/create-comment.dto.ts | 5 + src/shared/modules/comment/index.ts | 5 + .../modules/user/default-user.service.ts | 2 +- src/shared/types/category.type.ts | 3 + src/shared/types/component.enum.ts | 6 +- src/shared/types/index.ts | 2 + src/shared/types/sort-type.enum.ts | 4 + 20 files changed, 640 insertions(+), 2 deletions(-) create mode 100644 specification/specification.yml create mode 100644 src/shared/modules/category/category-service.interface.ts create mode 100644 src/shared/modules/category/category.container.ts create mode 100644 src/shared/modules/category/category.entity.ts create mode 100644 src/shared/modules/category/default-category.service.ts create mode 100644 src/shared/modules/category/dto/create-category.dto.ts create mode 100644 src/shared/modules/category/index.ts create mode 100644 src/shared/modules/comment/comment-service.interface.ts create mode 100644 src/shared/modules/comment/comment.container.ts create mode 100644 src/shared/modules/comment/comment.entity.ts create mode 100644 src/shared/modules/comment/default-comment.service.ts create mode 100644 src/shared/modules/comment/dto/create-comment.dto.ts create mode 100644 src/shared/modules/comment/index.ts create mode 100644 src/shared/types/category.type.ts create mode 100644 src/shared/types/sort-type.enum.ts diff --git a/package-lock.json b/package-lock.json index e61222b..3d9060b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "eslint-config-prettier": "9.1.0", "eslint-plugin-prettier": "5.2.1", "json-server": "0.17.3", + "nodemon": "3.1.7", "pino-pretty": "11.2.2", "prettier": "3.3.3", "rimraf": "5.0.1", @@ -835,6 +836,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1072,6 +1086,18 @@ "node": ">= 0.8" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -1259,6 +1285,42 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -3165,6 +3227,20 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3608,6 +3684,12 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3736,6 +3818,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -4810,6 +4904,55 @@ "node": ">= 0.6" } }, + "node_modules/nodemon": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", + "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -4831,6 +4974,15 @@ "semver": "bin/semver" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/normalize-url": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", @@ -5426,6 +5578,12 @@ "node": ">= 0.10" } }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -5643,6 +5801,18 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", @@ -6101,6 +6271,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -6561,6 +6743,15 @@ "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/tr46": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", @@ -6783,6 +6974,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -7719,6 +7916,16 @@ "color-convert": "^2.0.1" } }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -7882,6 +8089,12 @@ "safe-buffer": "5.1.2" } }, + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true + }, "body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -8016,6 +8229,33 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" }, + "chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, "ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -9414,6 +9654,13 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -9714,6 +9961,12 @@ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -9808,6 +10061,15 @@ "has-bigints": "^1.0.1" } }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -10557,6 +10819,41 @@ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, + "nodemon": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", + "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -10577,6 +10874,12 @@ } } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "normalize-url": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", @@ -11004,6 +11307,12 @@ "ipaddr.js": "1.9.1" } }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -11156,6 +11465,15 @@ "string_decoder": "^1.3.0" } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", @@ -11489,6 +11807,15 @@ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true }, + "simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -11811,6 +12138,12 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, + "touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true + }, "tr46": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", @@ -11956,6 +12289,12 @@ "which-boxed-primitive": "^1.0.2" } }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", diff --git a/package.json b/package.json index 101e501..ed14d90 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "eslint-config-prettier": "9.1.0", "eslint-plugin-prettier": "5.2.1", "json-server": "0.17.3", + "nodemon": "3.1.7", "pino-pretty": "11.2.2", "prettier": "3.3.3", "rimraf": "5.0.1", diff --git a/specification/specification.yml b/specification/specification.yml new file mode 100644 index 0000000..46802f9 --- /dev/null +++ b/specification/specification.yml @@ -0,0 +1,101 @@ +openapi: 3.1.0 + +info: + title: API-сервер для демо-проекта «Шесть городов» + description: Список ресурсов и маршрутов сервера «Шесть городов» + license: + name: MIT + url: https://opensource.org/licenses/MIT + version: 2.0.0 + +tags: + - name: offers + description: Действия с объявлениями. + - name: categories + description: Действия с категориями. + - name: comments + description: Действия с комментариями. + - name: users + description: Действия с пользователем. + +paths: + /users/register: + post: + tags: + - users + summary: Регистрация пользователя + description: Регистрирует нового пользователя. + + requestBody: + description: Информация для создания нового пользователя. + content: + application/json: + schema: + $ref: '#/components/schemas/createUser' + required: true + + responses: + "201": + description: Пользователь зарегистрирован. Объект пользователя. + content: + application/json: + schema: + $ref: '#/components/schemas/user' + + "409": + description: Пользователь с таким email уже существует. + + /users/login: + post: + tags: + - users + summary: Авторизация пользователя + description: Авторизует пользователя на основе логина и пароля + + get: + tags: + - users + summary: Проверка состояния пользователя + description: Возвращает информацию по авторизованному пользователю + + /users/{userId}/avatar: + post: + tags: + - users + summary: Загрузить изображение аватара + description: Загружает изображение аватара пользователя. + Изображение аватара должно быть в формате `png` или `jpg`. + +components: + schemas: + createUser: + type: object + + properties: + email: + type: string + example: keks@htmlacademy.ru + + firstname: + type: string + example: Keks + + lastname: + type: string + example: Cat + + password: + type: string + example: 123456 + + user: + type: object + + properties: + id: + type: string + example: 6329c3d6a04ab1061c6425ea + + email: + type: string + example: keks@htmlacademy.ru diff --git a/src/shared/modules/category/category-service.interface.ts b/src/shared/modules/category/category-service.interface.ts new file mode 100644 index 0000000..91188ee --- /dev/null +++ b/src/shared/modules/category/category-service.interface.ts @@ -0,0 +1,14 @@ +import { DocumentType } from '@typegoose/typegoose'; +import { CreateCategoryDto } from './dto/create-category.dto.js'; +import { CategoryEntity } from './category.entity.js'; + +export interface CategoryService { + create(dto: CreateCategoryDto): Promise>; + findByCategoryId(categoryId: string): Promise | null>; + findByCategoryName(categoryName: string): Promise | null>; + findByCategoryNameOrCreate( + categoryName: string, + dto: CreateCategoryDto + ): Promise>; + find(): Promise[]>; +} diff --git a/src/shared/modules/category/category.container.ts b/src/shared/modules/category/category.container.ts new file mode 100644 index 0000000..04ab900 --- /dev/null +++ b/src/shared/modules/category/category.container.ts @@ -0,0 +1,18 @@ +import { Container } from 'inversify'; +import { types } from '@typegoose/typegoose'; + +import { Component } from '../../types/index.js'; +import { CategoryService } from './category-service.interface.js'; +import { DefaultCategoryService } from './default-category.service.js'; +import { CategoryEntity, CategoryModel } from './category.entity.js'; + +export function createCategoryContainer() { + const categoryContainer = new Container(); + + categoryContainer.bind(Component.CategoryService).to(DefaultCategoryService); + categoryContainer + .bind>(Component.CategoryModel) + .toConstantValue(CategoryModel); + + return categoryContainer; +} diff --git a/src/shared/modules/category/category.entity.ts b/src/shared/modules/category/category.entity.ts new file mode 100644 index 0000000..eb43785 --- /dev/null +++ b/src/shared/modules/category/category.entity.ts @@ -0,0 +1,14 @@ +import { defaultClasses, getModelForClass, modelOptions, prop } from '@typegoose/typegoose'; +import { Category } from '../../types/index.js'; + +export interface CategoryEntity extends defaultClasses.Base {} + +@modelOptions({ + schemaOptions: { collection: 'categories', timestamps: true }, +}) +export class CategoryEntity extends defaultClasses.TimeStamps implements Category { + @prop({ required: true, trim: true }) + public name!: string; +} + +export const CategoryModel = getModelForClass(CategoryEntity); diff --git a/src/shared/modules/category/default-category.service.ts b/src/shared/modules/category/default-category.service.ts new file mode 100644 index 0000000..d7040c4 --- /dev/null +++ b/src/shared/modules/category/default-category.service.ts @@ -0,0 +1,49 @@ +import { inject, injectable } from 'inversify'; +import { DocumentType, types } from '@typegoose/typegoose'; + +import { CategoryService } from './category-service.interface.js'; +import { Component } from '../../types/index.js'; +import { Logger } from '../../libs/logger/index.js'; +import { CategoryEntity } from './category.entity.js'; +import { CreateCategoryDto } from './dto/create-category.dto.js'; + +@injectable() +export class DefaultCategoryService implements CategoryService { + constructor( + @inject(Component.Logger) private readonly logger: Logger, + @inject(Component.CategoryModel) private readonly categoryModel: types.ModelType + ) {} + + public async create(dto: CreateCategoryDto): Promise> { + const result = await this.categoryModel.create(dto); + this.logger.info(`Новая категория создана: ${dto.name}`); + return result; + } + + public async findByCategoryId(categoryId: string): Promise | null> { + return this.categoryModel.findById(categoryId).exec(); + } + + public async findByCategoryName( + categoryName: string + ): Promise | null> { + return this.categoryModel.findOne({ name: categoryName }).exec(); + } + + public async findByCategoryNameOrCreate( + categoryName: string, + dto: CreateCategoryDto + ): Promise> { + const existedCategory = await this.findByCategoryName(categoryName); + + if (existedCategory) { + return existedCategory; + } + + return this.create(dto); + } + + public async find(): Promise[]> { + return this.categoryModel.find(); + } +} diff --git a/src/shared/modules/category/dto/create-category.dto.ts b/src/shared/modules/category/dto/create-category.dto.ts new file mode 100644 index 0000000..ac60e10 --- /dev/null +++ b/src/shared/modules/category/dto/create-category.dto.ts @@ -0,0 +1,3 @@ +export class CreateCategoryDto { + public name: string; +} diff --git a/src/shared/modules/category/index.ts b/src/shared/modules/category/index.ts new file mode 100644 index 0000000..a1b9d77 --- /dev/null +++ b/src/shared/modules/category/index.ts @@ -0,0 +1,5 @@ +export * from './category.entity.js'; +export * from './dto/create-category.dto.js'; +export * from './category-service.interface.js'; +export * from './default-category.service.js'; +export * from './category.container.js'; diff --git a/src/shared/modules/comment/comment-service.interface.ts b/src/shared/modules/comment/comment-service.interface.ts new file mode 100644 index 0000000..d03a3b2 --- /dev/null +++ b/src/shared/modules/comment/comment-service.interface.ts @@ -0,0 +1,7 @@ +import { DocumentType } from '@typegoose/typegoose'; +import { CreateCommentDto } from './dto/create-comment.dto.js'; +import { CommentEntity } from './comment.entity.js'; + +export interface CommentService { + create(dto: CreateCommentDto): Promise>; +} diff --git a/src/shared/modules/comment/comment.container.ts b/src/shared/modules/comment/comment.container.ts new file mode 100644 index 0000000..ca94880 --- /dev/null +++ b/src/shared/modules/comment/comment.container.ts @@ -0,0 +1,18 @@ +import { Container } from 'inversify'; +import { types } from '@typegoose/typegoose'; + +import { Component } from '../../types/index.js'; +import { CommentService } from './comment-service.interface.js'; +import { DefaultCommentService } from './default-comment.service.js'; +import { CommentEntity, CommentModel } from './comment.entity.js'; + +export function createCommentContainer() { + const commentContainer = new Container(); + + commentContainer.bind(Component.CommentService).to(DefaultCommentService); + commentContainer + .bind>(Component.CommentModel) + .toConstantValue(CommentModel); + + return commentContainer; +} diff --git a/src/shared/modules/comment/comment.entity.ts b/src/shared/modules/comment/comment.entity.ts new file mode 100644 index 0000000..f81bbb0 --- /dev/null +++ b/src/shared/modules/comment/comment.entity.ts @@ -0,0 +1,23 @@ +import { defaultClasses, getModelForClass, modelOptions, prop } from '@typegoose/typegoose'; +import { Comment } from '../../types/index.js'; + +export interface CommentEntity extends defaultClasses.Base {} + +@modelOptions({ + schemaOptions: { collection: 'comment', timestamps: true }, +}) +export class CommentEntity extends defaultClasses.TimeStamps implements Comment { + @prop({ required: true, trim: true }) + public text!: string; + + @prop({ required: true }) + public publishedAt!: string; + + @prop({ required: true }) + public rating!: number; + + @prop({ required: true }) + public authorId!: string; +} + +export const CommentModel = getModelForClass(CommentEntity); diff --git a/src/shared/modules/comment/default-comment.service.ts b/src/shared/modules/comment/default-comment.service.ts new file mode 100644 index 0000000..1562d9f --- /dev/null +++ b/src/shared/modules/comment/default-comment.service.ts @@ -0,0 +1,23 @@ +import { inject, injectable } from 'inversify'; +import { DocumentType, types } from '@typegoose/typegoose'; + +import { CommentService } from './comment-service.interface.js'; +import { Component } from '../../types/index.js'; +import { Logger } from '../../libs/logger/index.js'; +import { CommentEntity } from './comment.entity.js'; +import { CreateCommentDto } from './dto/create-comment.dto.js'; + +@injectable() +export class DefaultCommentService implements CommentService { + constructor( + @inject(Component.Logger) private readonly logger: Logger, + @inject(Component.CommentModel) private readonly CommentModel: types.ModelType + ) {} + + public async create(dto: CreateCommentDto): Promise> { + const result = await this.CommentModel.create(dto); + + this.logger.info(`Новый комментарий создан: ${dto.text}`); + return result; + } +} diff --git a/src/shared/modules/comment/dto/create-comment.dto.ts b/src/shared/modules/comment/dto/create-comment.dto.ts new file mode 100644 index 0000000..8193860 --- /dev/null +++ b/src/shared/modules/comment/dto/create-comment.dto.ts @@ -0,0 +1,5 @@ +export class CreateCommentDto { + public text: string; + public offerId: string; + public userId: string; +} diff --git a/src/shared/modules/comment/index.ts b/src/shared/modules/comment/index.ts new file mode 100644 index 0000000..ac2a6dd --- /dev/null +++ b/src/shared/modules/comment/index.ts @@ -0,0 +1,5 @@ +export * from './comment.entity.js'; +export * from './dto/create-comment.dto.js'; +export * from './comment-service.interface.js'; +export * from './default-comment.service.js'; +export * from './comment.container.js'; diff --git a/src/shared/modules/user/default-user.service.ts b/src/shared/modules/user/default-user.service.ts index bf2f26f..49d73d5 100644 --- a/src/shared/modules/user/default-user.service.ts +++ b/src/shared/modules/user/default-user.service.ts @@ -20,7 +20,7 @@ export class DefaultUserService implements UserService { user.setPassword(dto.password, salt); const result = await this.userModel.create(user); - this.logger.info(`New user created: ${result.email}`); + this.logger.info(`Новый пользователь создан: ${result.email}`); return result; } diff --git a/src/shared/types/category.type.ts b/src/shared/types/category.type.ts new file mode 100644 index 0000000..568dc00 --- /dev/null +++ b/src/shared/types/category.type.ts @@ -0,0 +1,3 @@ +export type Category = { + name: string; +} diff --git a/src/shared/types/component.enum.ts b/src/shared/types/component.enum.ts index a26540c..d92d3a1 100644 --- a/src/shared/types/component.enum.ts +++ b/src/shared/types/component.enum.ts @@ -1,5 +1,6 @@ export const Component = { RestApplication: Symbol.for('RestApplication'), + DatabaseClient: Symbol.for('DatabaseClient'), Logger: Symbol.for('Logger'), Config: Symbol.for('Config'), @@ -7,5 +8,8 @@ export const Component = { UserService: Symbol.for('UserService'), OfferModel: Symbol.for('OfferModel'), OfferService: Symbol.for('OfferService'), - DatabaseClient: Symbol.for('DatabaseClient'), + CommentModel: Symbol.for('CommentModel'), + CommentService: Symbol.for('CommentService'), + CategoryModel: Symbol.for('CategoryModel'), + CategoryService: Symbol.for('CategoryService'), } as const; diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts index 39dc266..4a204b9 100644 --- a/src/shared/types/index.ts +++ b/src/shared/types/index.ts @@ -1,8 +1,10 @@ +export * from './category.type.js'; export * from './comment.type.js'; export * from './common.type.js'; export * from './offer.type.js'; export * from './user.type.js'; export * from './component.enum.js'; +export * from './sort-type.enum.js'; export * from './mock-server-data.type.js'; diff --git a/src/shared/types/sort-type.enum.ts b/src/shared/types/sort-type.enum.ts new file mode 100644 index 0000000..727ba7c --- /dev/null +++ b/src/shared/types/sort-type.enum.ts @@ -0,0 +1,4 @@ +export enum SortType { + Down = -1, + Up = 1, +}