diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c9b6fc5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "command": "npm run start:dev -b swc", + "name": "Start Nest", + "request": "launch", + "type": "node-terminal" + }, + ] +} \ No newline at end of file diff --git a/nest-cli.json b/nest-cli.json index f9aa683..e8552c2 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -3,6 +3,7 @@ "collection": "@nestjs/schematics", "sourceRoot": "src", "compilerOptions": { - "deleteOutDir": true + "deleteOutDir": true, + "plugins": ["@nestjs/swagger"] } } diff --git a/package-lock.json b/package-lock.json index 0582a3b..3cc38b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,14 @@ "license": "MIT", "dependencies": { "@nestjs/common": "^10.3.2", + "@nestjs/config": "^3.2.2", "@nestjs/core": "^10.3.2", + "@nestjs/mapped-types": "^2.0.5", + "@nestjs/mongoose": "^10.0.6", "@nestjs/platform-express": "^10.3.2", + "@nestjs/swagger": "^7.3.1", + "express-session": "^1.18.0", + "mongoose": "^8.4.1", "reflect-metadata": "^0.2.1", "rxjs": "^7.8.1" }, @@ -22,6 +28,7 @@ "@swc/cli": "^0.3.9", "@swc/core": "^1.4.0", "@types/express": "^4.17.21", + "@types/express-session": "^1.18.0", "@types/jest": "^29.5.12", "@types/node": "^20.11.16", "@types/supertest": "^6.0.2", @@ -90,6 +97,30 @@ } } }, + "node_modules/@angular-devkit/core/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@angular-devkit/core/node_modules/picomatch": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", @@ -975,28 +1006,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "node_modules/@eslint/js": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", @@ -1599,6 +1608,12 @@ "node": ">=8" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "license": "MIT" + }, "node_modules/@mole-inc/bin-wrapper": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", @@ -1618,6 +1633,15 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz", + "integrity": "sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@nestjs/cli": { "version": "10.3.1", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.3.1.tgz", @@ -1749,6 +1773,22 @@ } } }, + "node_modules/@nestjs/config": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.2.2.tgz", + "integrity": "sha512-vGICPOui5vE6kPz1iwQ7oCnp3qWgqxldPmBQ9onkVoKlBtyc83KJCr7CjuVtf4OdovMAVcux1d8Q6jglU2ZphA==", + "license": "MIT", + "dependencies": { + "dotenv": "16.4.5", + "dotenv-expand": "10.0.0", + "lodash": "4.17.21", + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "rxjs": "^7.1.0" + } + }, "node_modules/@nestjs/core": { "version": "10.3.2", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.2.tgz", @@ -1786,6 +1826,38 @@ } } }, + "node_modules/@nestjs/mapped-types": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/mongoose": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-10.0.6.tgz", + "integrity": "sha512-J8jFgSvCDEKMXU57QAIdXIlWQsOFWK+x0PM1KI/0zHe3/4JrAtFGTFD08hRX3IHk+WJT9g/XQIpMSNM7/10Jlg==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "mongoose": "^6.0.2 || ^7.0.0 || ^8.0.0", + "rxjs": "^7.0.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "10.3.2", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.2.tgz", @@ -1867,6 +1939,30 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@nestjs/schematics/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@nestjs/schematics/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@nestjs/schematics/node_modules/picomatch": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", @@ -1879,6 +1975,39 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@nestjs/swagger": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.3.1.tgz", + "integrity": "sha512-LUC4mr+5oAleEC/a2j8pNRh1S5xhKXJ1Gal5ZdRjt9XebQgbngXCdW7JTA9WOEcwGtFZN9EnKYdquzH971LZfw==", + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "^0.14.2", + "@nestjs/mapped-types": "2.0.5", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0", + "swagger-ui-dist": "5.11.2" + }, + "peerDependencies": { + "@fastify/static": "^6.0.0 || ^7.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { "version": "10.3.2", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.2.tgz", @@ -2463,6 +2592,16 @@ "@types/send": "*" } }, + "node_modules/@types/express-session": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-27JdDRgor6PoYlURY+Y5kCakqp5ulC0kmf7y+QwaY+hv9jEFuQOThgkjyA53RP3jmKuBsH5GR6qEfFmvb8mwOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", @@ -2629,6 +2768,21 @@ "@types/superagent": "^8.1.0" } }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.25", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.25.tgz", @@ -3074,14 +3228,15 @@ } }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" }, "funding": { @@ -3106,6 +3261,30 @@ } } }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -3221,8 +3400,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { "version": "1.1.1", @@ -3648,6 +3826,15 @@ "node-int64": "^0.4.0" } }, + "node_modules/bson": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.7.0.tgz", + "integrity": "sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -4212,7 +4399,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -4403,6 +4589,27 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4639,22 +4846,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -4692,12 +4883,6 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -4961,6 +5146,55 @@ "node": ">= 0.10.0" } }, + "node_modules/express-session": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", + "license": "MIT", + "dependencies": { + "cookie": "0.6.0", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/express/node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -6786,7 +7020,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -6819,10 +7052,11 @@ "dev": true }, "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -6860,6 +7094,15 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/keyv": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", @@ -6933,8 +7176,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.memoize": { "version": "4.1.2", @@ -7044,6 +7286,12 @@ "node": ">= 4.0.0" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -7173,11 +7421,149 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mongodb": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", + "integrity": "sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "license": "MIT", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mongoose": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.1.tgz", + "integrity": "sha512-odQ2WEWGL3hb0Qex+QMN4eH6D34WdMEw7F1If2MGABApSDmG9cMmqv/G1H6WsXmuaH9mkuuadW/WbLE5+tHJwA==", + "license": "MIT", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "6.6.2", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multer": { "version": "1.4.4-lts.1", @@ -7371,6 +7757,15 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -7875,7 +8270,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, "engines": { "node": ">=6" } @@ -7942,6 +8336,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -8080,6 +8483,7 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8323,28 +8727,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -8541,6 +8923,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -8614,6 +9002,15 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -8874,6 +9271,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.2.tgz", + "integrity": "sha512-jQG0cRgJNMZ7aCoiFofnoojeSaa/+KgWaDlfgs8QN+BXoGMpxeMVY5OEnjq4OlNvF3yjftO8c9GRAgcHlO+u7A==", + "license": "Apache-2.0" + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -9357,6 +9760,18 @@ "node": ">=8" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -9432,6 +9847,19 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/package.json b/package.json index 77dc893..9246793 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,14 @@ }, "dependencies": { "@nestjs/common": "^10.3.2", + "@nestjs/config": "^3.2.2", "@nestjs/core": "^10.3.2", + "@nestjs/mapped-types": "^2.0.5", + "@nestjs/mongoose": "^10.0.6", "@nestjs/platform-express": "^10.3.2", + "@nestjs/swagger": "^7.3.1", + "express-session": "^1.18.0", + "mongoose": "^8.4.1", "reflect-metadata": "^0.2.1", "rxjs": "^7.8.1" }, @@ -32,6 +38,7 @@ "@swc/cli": "^0.3.9", "@swc/core": "^1.4.0", "@types/express": "^4.17.21", + "@types/express-session": "^1.18.0", "@types/jest": "^29.5.12", "@types/node": "^20.11.16", "@types/supertest": "^6.0.2", diff --git a/src/app.module.ts b/src/app.module.ts index 8662803..2ee0b62 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,10 +1,23 @@ import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { ConfigModule } from '@nestjs/config'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { GamesModule } from './games/games.module'; +import { UsersModule } from './users/users.module'; +import { CardsService } from './cards/cards.service'; @Module({ - imports: [], + imports: [ + ConfigModule.forRoot(), + MongooseModule.forRoot('mongodb://localhost:27017', { + auth: { password: 'example', username: 'root' }, + dbName: 'heron', + }), + GamesModule, + UsersModule, + ], controllers: [AppController], - providers: [AppService], + providers: [AppService, CardsService], }) export class AppModule {} diff --git a/src/cards/cards.module.ts b/src/cards/cards.module.ts new file mode 100644 index 0000000..f3c32d7 --- /dev/null +++ b/src/cards/cards.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { Card, CardSchema } from './schemas/cards.schema'; + +@Module({ + imports: [ + MongooseModule.forFeature([{ name: Card.name, schema: CardSchema }]), + ], + exports: [MongooseModule], +}) +export class CardsModule { + schema = CardSchema; +} diff --git a/src/cards/cards.service.spec.ts b/src/cards/cards.service.spec.ts new file mode 100644 index 0000000..2e26f28 --- /dev/null +++ b/src/cards/cards.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CardsService } from './cards.service'; + +describe('CardsService', () => { + let service: CardsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [CardsService], + }).compile(); + + service = module.get(CardsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/cards/cards.service.ts b/src/cards/cards.service.ts new file mode 100644 index 0000000..136936a --- /dev/null +++ b/src/cards/cards.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import path from 'node:path'; +import fs from 'node:fs/promises'; + +@Injectable() +export class CardsService { + async loadCards(): Promise { + const cardPath = path.join(path.resolve(), 'cards'); + const files = await fs.readdir(cardPath); + const processed = await Promise.all( + files.map(async (f) => { + return [ + path.parse(f).name, + ( + await fs + .readFile(path.join(cardPath, f)) + .then((e) => JSON.parse(e.toString())) + ).cards, + ]; + }), + ); + + const packs = Object.fromEntries(processed); + console.log(packs); + } +} diff --git a/src/cards/schemas/cards.schema.ts b/src/cards/schemas/cards.schema.ts new file mode 100644 index 0000000..2c6f65e --- /dev/null +++ b/src/cards/schemas/cards.schema.ts @@ -0,0 +1,18 @@ +import { Schema, SchemaFactory } from '@nestjs/mongoose'; +import { CardFaction, CardRole, CardType, EffectParser } from './cards.types'; + +@Schema() +export class Card { + cardId: number; + role: CardRole; + cardType: CardType; + faction: CardFaction; + name: string; + initAmount: number; + cost?: number; + defense?: number; + guard?: boolean; + effects: EffectParser[]; +} + +export const CardSchema = SchemaFactory.createForClass(Card); diff --git a/src/cards/schemas/cards.types.ts b/src/cards/schemas/cards.types.ts new file mode 100644 index 0000000..78f27e0 --- /dev/null +++ b/src/cards/schemas/cards.types.ts @@ -0,0 +1,102 @@ +/** + * + */ +export enum CardRole { + PERSONAL = 'personal', + MARKET = 'market', + FIRE_GEM = 'fire_gem', +} + +/** + * Playable card types + */ +export enum CardType { + HERO = 'hero', + HERO_ABILITY = 'hero_ability', + CURRENCY = 'currency', + WEAPON = 'weapon', + CHAMPION = 'champion', + ACTION = 'action', + ITEM = 'item', +} + +/** + * Playable card factions + */ +export enum CardFaction { + BASE = 'base', + + BLUE = 'blue', + RED = 'red', + GREEN = 'green', + YELLOW = 'yellow', +} + +export enum CardEffects { + GOLD = 'gold', + DAMAGE = 'damage', + HEAL = 'heal', + PREPARE = 'prepare', + STUN = 'stun', + SACRIFICE = 'sacrifice', + DRAW = 'draw', + DRAW_AND_DISCARD = 'draw_and_discard', + MAKE_DISCARD = 'make_discard', + RESTACK_DISCARDED_CHAMPION = 'restack_discarded_champion', + RESTACK_DISCARDED_CARD = 'restack_discarded_card', + STACK_NEXT_ACTION_BOUGHT = 'stack_next_action_bought', + STACK_NEXT_CARD_BOUGHT = 'stack_next_card_bought', + PLAY_NEXT_CARD_BOUGHT = 'play_next_card_bought', +} + +export enum Per { + CHAMPION = 'champion', + OTHER_CHAMPION = 'other_champion', + OTHER_GUARD = 'other_guard', + CARD_OF_SAME_FACTION = 'card_of_same_faction', + OTHER_CARD_OF_SAME_FACTION = 'other_card_of_same_faction', +} + +export enum EffectType { + CHAMPION_ACTION = 'champion_action', + SUICIDE = 'suicide', + FACTION_COMBO = 'faction_combo', +} + +export enum EffectTypeSpecial { + CHAMPION_AMOUNT = 'champion_amount', + TOTAL_DAMAGE = 'total_damage', + CARD_AMOUNT = 'card_amount', + ACTION_AMOUNT = 'action_amount', +} + +export type EffectTypeVerbose = { + id: EffectType | EffectTypeSpecial; + faction?: string; + amount?: number; + card_id?: number; +}; + +export type EffectParser = { + effect: CardEffects; + amount: number; + times?: number; + sub_effects?: EffectParser[]; + effect_type?: EffectType | EffectTypeVerbose; + per?: Per; + mutex?: number; +}; + +export type CardParser = { + id: number; + role: CardRole; + card_type: CardType; + faction: CardFaction; + name: string; + init_amount: number; + cost?: number; + amount: number; + defense?: number; + guard?: boolean; + effects: EffectParser[]; +}; diff --git a/src/games/dto/create-game.dto.ts b/src/games/dto/create-game.dto.ts new file mode 100644 index 0000000..717d4f6 --- /dev/null +++ b/src/games/dto/create-game.dto.ts @@ -0,0 +1 @@ +export class CreateGameDto {} diff --git a/src/games/dto/update-game.dto.ts b/src/games/dto/update-game.dto.ts new file mode 100644 index 0000000..94f8a53 --- /dev/null +++ b/src/games/dto/update-game.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateGameDto } from './create-game.dto'; + +export class UpdateGameDto extends PartialType(CreateGameDto) {} diff --git a/src/games/games.controller.spec.ts b/src/games/games.controller.spec.ts new file mode 100644 index 0000000..d2eb47d --- /dev/null +++ b/src/games/games.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { GamesController } from './games.controller'; +import { GamesService } from './games.service'; + +describe('GamesController', () => { + let controller: GamesController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [GamesController], + providers: [GamesService], + }).compile(); + + controller = module.get(GamesController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/games/games.controller.ts b/src/games/games.controller.ts new file mode 100644 index 0000000..e7e9bd5 --- /dev/null +++ b/src/games/games.controller.ts @@ -0,0 +1,42 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, +} from '@nestjs/common'; +import { GamesService } from './games.service'; +import { CreateGameDto } from './dto/create-game.dto'; +import { UpdateGameDto } from './dto/update-game.dto'; + +@Controller('games') +export class GamesController { + constructor(private readonly gamesService: GamesService) {} + + @Post() + create() { + return this.gamesService.create(); + } + + @Get() + findAll() { + return this.gamesService.findAll(); + } + + @Get(':id') + findOne(@Param('id') id: string) { + return this.gamesService.findOne(id); + } + + // @Patch(':id') + // update(@Param('id') id: string, @Body() updateGameDto: UpdateGameDto) { + // return this.gamesService.update(+id, updateGameDto); + // } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.gamesService.remove(+id); + } +} diff --git a/src/games/games.module.ts b/src/games/games.module.ts new file mode 100644 index 0000000..7eb3d7d --- /dev/null +++ b/src/games/games.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { GamesService } from './games.service'; +import { GamesController } from './games.controller'; +import { Game, GameSchema } from './schemas/game.schema'; +import { MongooseModule } from '@nestjs/mongoose'; +import { UsersModule } from 'src/users/users.module'; +import { CardsModule } from 'src/cards/cards.module'; + +@Module({ + imports: [ + CardsModule, + UsersModule, + MongooseModule.forFeature([{ name: Game.name, schema: GameSchema }]), + ], + controllers: [GamesController], + providers: [GamesService], +}) +export class GamesModule {} diff --git a/src/games/games.service.spec.ts b/src/games/games.service.spec.ts new file mode 100644 index 0000000..c65ca09 --- /dev/null +++ b/src/games/games.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { GamesService } from './games.service'; + +describe('GamesService', () => { + let service: GamesService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [GamesService], + }).compile(); + + service = module.get(GamesService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/games/games.service.ts b/src/games/games.service.ts new file mode 100644 index 0000000..258db81 --- /dev/null +++ b/src/games/games.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@nestjs/common'; +import { CreateGameDto } from './dto/create-game.dto'; +import { UpdateGameDto } from './dto/update-game.dto'; +import { InjectModel } from '@nestjs/mongoose'; +import { Game } from './schemas/game.schema'; +import { Model } from 'mongoose'; + +@Injectable() +export class GamesService { + constructor(@InjectModel(Game.name) private gameModel: Model) {} + async create(): Promise { + const createdGame = new this.gameModel(); + return createdGame.save(); + } + + findAll() { + return this.gameModel.find(); + } + + findOne(id: string) { + return this.gameModel.find({ _id: id }).exec(); + } + + // update(id: number, updateGameDto: UpdateGameDto) { + // return `This action updates a #${id} game`; + // } + + remove(id: number) { + return `This action removes a #${id} game`; + } +} diff --git a/src/games/schemas/game.schema.ts b/src/games/schemas/game.schema.ts new file mode 100644 index 0000000..55ebd13 --- /dev/null +++ b/src/games/schemas/game.schema.ts @@ -0,0 +1,51 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import mongoose, { HydratedDocument } from 'mongoose'; +import { Card } from 'src/cards/schemas/cards.schema'; +import { User } from 'src/users/schemas/user.entity'; + +export type GameDocument = HydratedDocument; + +@Schema() +class GameObject {} + +@Schema() +export class Team extends GameObject {} + +export const TeamSchema = SchemaFactory.createForClass(Team); + +@Schema() +export class Container extends GameObject { + @Prop([{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]) + cards: Card[]; +} + +export const ContainerSchema = SchemaFactory.createForClass(Container); + +@Schema() +export class Game extends GameObject { + @Prop([Team]) + teams: Team[]; + + @Prop({ type: ContainerSchema, default: {} }) + market: Container; + + @Prop({ type: ContainerSchema, default: {} }) + marketStack: Container; + + @Prop({ type: ContainerSchema, default: {} }) + fireGems: Container; + + @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'Game.teams' }) + currentTurn?: Team; + + @Prop({ default: false }) + started: boolean; + + @Prop({ default: false }) + private ended: boolean; + + @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' }) + owner: User; +} + +export const GameSchema = SchemaFactory.createForClass(Game); diff --git a/src/main.ts b/src/main.ts index 13cad38..a00ad84 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,28 @@ import { NestFactory } from '@nestjs/core'; +import * as session from 'express-session'; import { AppModule } from './app.module'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; async function bootstrap() { const app = await NestFactory.create(AppModule); - await app.listen(3000); + + const config = new DocumentBuilder() + .setTitle('Heron Game') + .setDescription('Heron') + .setVersion('1.0') + .build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); + + app.use( + session({ + secret: 'my-secret', + resave: false, + saveUninitialized: false, + name: 'heron.session', + }), + ); + + await app.listen(8765); } bootstrap(); diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts new file mode 100644 index 0000000..be5dbc9 --- /dev/null +++ b/src/users/dto/create-user.dto.ts @@ -0,0 +1,5 @@ +export class CreateUserDto { + method: 'discord' | 'password'; + username?: string; + password: string; +} diff --git a/src/users/dto/login.dto.ts b/src/users/dto/login.dto.ts new file mode 100644 index 0000000..05978df --- /dev/null +++ b/src/users/dto/login.dto.ts @@ -0,0 +1,20 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateUserDto } from './create-user.dto'; +import { ApiProperty } from '@nestjs/swagger'; + +export class LoginDto extends PartialType(CreateUserDto) { + constructor(id: string, global_name: string, avatar: string) { + super(); + this.id = id; + this.global_name = global_name; + this.avatar = avatar; + } + @ApiProperty() + id: string; + + @ApiProperty() + global_name: string; + + @ApiProperty() + avatar: string; +} diff --git a/src/users/schemas/user.entity.ts b/src/users/schemas/user.entity.ts new file mode 100644 index 0000000..bc55547 --- /dev/null +++ b/src/users/schemas/user.entity.ts @@ -0,0 +1,9 @@ +import { Schema, SchemaFactory } from '@nestjs/mongoose'; +import { HydratedDocument } from 'mongoose'; + +export type UserDocument = HydratedDocument; + +@Schema() +export class User {} + +export const UserSchema = SchemaFactory.createForClass(User); diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts new file mode 100644 index 0000000..a76d310 --- /dev/null +++ b/src/users/users.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UsersController } from './users.controller'; +import { UsersService } from './users.service'; + +describe('UsersController', () => { + let controller: UsersController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [UsersController], + providers: [UsersService], + }).compile(); + + controller = module.get(UsersController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts new file mode 100644 index 0000000..bd45aff --- /dev/null +++ b/src/users/users.controller.ts @@ -0,0 +1,46 @@ +import { + Controller, + Get, + Query, + Session, + HttpException, + HttpStatus, +} from '@nestjs/common'; +import { UsersService } from './users.service'; +import { LoginDto } from './dto/login.dto'; +import { ApiTags } from '@nestjs/swagger'; + +@ApiTags('auth') +@Controller() +export class UsersController { + constructor(private readonly usersService: UsersService) {} + + @Get('/login') + async login( + @Query('code') code: string, + @Session() session: Record, + ) { + const tokenData = await this.usersService.saveDiscordToken(code); + session.tokenData = tokenData; + const user = await this.usersService.useDiscordToken( + tokenData.token_type, + tokenData.token, + ); + return user; + } + + @Get('/whoami') + async remove(@Session() session: Record): Promise { + if (!session.tokenData) { + throw new HttpException( + "Session doesn't contains auth information", + HttpStatus.UNAUTHORIZED, + ); + } + const user = await this.usersService.useDiscordToken( + session.tokenData.token_type, + session.tokenData.token, + ); + return user; + } +} diff --git a/src/users/users.module.ts b/src/users/users.module.ts new file mode 100644 index 0000000..b17801c --- /dev/null +++ b/src/users/users.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { UsersService } from './users.service'; +import { UsersController } from './users.controller'; +import { MongooseModule } from '@nestjs/mongoose'; +import { User, UserSchema } from './schemas/user.entity'; + +@Module({ + imports: [ + MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), + ], + controllers: [UsersController], + providers: [UsersService], + exports: [MongooseModule], +}) +export class UsersModule { + schema = UserSchema; +} diff --git a/src/users/users.service.spec.ts b/src/users/users.service.spec.ts new file mode 100644 index 0000000..62815ba --- /dev/null +++ b/src/users/users.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UsersService } from './users.service'; + +describe('UsersService', () => { + let service: UsersService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [UsersService], + }).compile(); + + service = module.get(UsersService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/users/users.service.ts b/src/users/users.service.ts new file mode 100644 index 0000000..7539e99 --- /dev/null +++ b/src/users/users.service.ts @@ -0,0 +1,63 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { LoginDto } from './dto/login.dto'; +import { URLSearchParams } from 'url'; + +@Injectable() +export class UsersService { + async saveDiscordToken(code: string) { + const req = await fetch('https://discord.com/api/oauth2/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + client_id: process.env.DISCORD_CLIENTID, + client_secret: process.env.DISCORD_CLIENTSECRET, + code: code, + grant_type: 'authorization_code', + redirect_uri: process.env.API_ENDPOINT + '/login', + scope: 'identify', + }), + }); + if (!req.ok) { + console.error(await req.json()); + throw new HttpException( + "Couldn't fetch Discord api", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + const data = await req.json(); + if (!data.token_type || !data.access_token) { + console.error(data); + throw new HttpException( + 'Invalid Discord API response', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + const token_type = data.token_type; + const token = data.access_token; + + return { token, token_type }; + } + + async useDiscordToken(token_type: string, token: string) { + const req2 = await fetch('https://discord.com/api/users/@me', { + headers: { + authorization: token_type + ' ' + token, + }, + }); + + if (!req2.ok) { + throw new HttpException( + "Couldn't fetch Discord api", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + const data2 = await req2.json(); + return new LoginDto(data2.id, data2.global_name, data2.avatar); + } + + remove(id: number) { + return `This action removes a #${id} user`; + } +}