diff --git a/package.json b/package.json index 89e0bfd..54db1df 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@slack/client": "^5.0.2", "@supabase/supabase-js": "^2.45.1", "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "cross-env": "^7.0.3", "dotenv": "^16.4.5", "joi": "^17.13.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d811fb..12f8cf6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,28 +10,28 @@ importers: dependencies: '@nestjs/axios': specifier: ^4.0.0 - version: 4.0.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@0.21.4)(rxjs@7.8.1) + version: 4.0.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@0.21.4)(rxjs@7.8.1) '@nestjs/common': specifier: ^9.0.0 - version: 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + version: 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/config': specifier: ^3.2.3 - version: 3.2.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1) + version: 3.2.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1) '@nestjs/core': specifier: ^9.0.0 - version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/platform-express': specifier: ^9.0.0 - version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) + version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) '@nestjs/schedule': specifier: ^5.0.1 - version: 5.0.1(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)) + version: 5.0.1(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)) '@nestjs/swagger': specifier: ^7.4.0 - version: 7.4.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(reflect-metadata@0.1.14) + version: 7.4.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) '@nestjs/typeorm': specifier: ^10.0.2 - version: 10.0.2(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4))) + version: 10.0.2(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4))) '@sentry/browser': specifier: ^8.54.0 version: 8.54.0 @@ -40,7 +40,7 @@ importers: version: 6.19.7 '@sentry/nestjs': specifier: ^8.27.0 - version: 8.27.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)) + version: 8.27.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)) '@sentry/node': specifier: ^8.54.0 version: 8.54.0 @@ -56,6 +56,9 @@ importers: class-transformer: specifier: ^0.5.1 version: 0.5.1 + class-validator: + specifier: ^0.14.1 + version: 0.14.1 cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -92,7 +95,7 @@ importers: version: 9.2.0(chokidar@3.5.3)(typescript@5.5.4) '@nestjs/testing': specifier: ^9.0.0 - version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3)) + version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3)) '@snaplet/copycat': specifier: ^5.1.0 version: 5.1.0 @@ -1486,6 +1489,9 @@ packages: '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} + '@types/validator@13.12.2': + resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} + '@types/wrap-ansi@3.0.0': resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} @@ -1954,6 +1960,9 @@ packages: class-transformer@0.5.1: resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} + class-validator@0.14.1: + resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==} + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -3105,6 +3114,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + libphonenumber-js@1.12.6: + resolution: {integrity: sha512-PJiS4ETaUfCOFLpmtKzAbqZQjCCKVu2OhTV4SVNNE7c2nu/dACvtCqj4L0i/KWNnIgRv7yrILvBj5Lonv5Ncxw==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -4434,6 +4446,10 @@ packages: valid-url@1.0.9: resolution: {integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==} + validator@13.12.0: + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} + engines: {node: '>= 0.10'} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -5253,9 +5269,9 @@ snapshots: '@microsoft/tsdoc@0.15.0': {} - '@nestjs/axios@4.0.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@0.21.4)(rxjs@7.8.1)': + '@nestjs/axios@4.0.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@0.21.4)(rxjs@7.8.1)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) axios: 0.21.4 rxjs: 7.8.1 @@ -5289,7 +5305,7 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)': + '@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)': dependencies: iterare: 1.2.1 reflect-metadata: 0.1.14 @@ -5298,18 +5314,19 @@ snapshots: uid: 2.0.2 optionalDependencies: class-transformer: 0.5.1 + class-validator: 0.14.1 - '@nestjs/config@3.2.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1)': + '@nestjs/config@3.2.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) dotenv: 16.4.5 dotenv-expand: 10.0.0 lodash: 4.17.21 rxjs: 7.8.1 - '@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)': + '@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -5319,21 +5336,22 @@ snapshots: tslib: 2.5.3 uid: 2.0.2 optionalDependencies: - '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) + '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) transitivePeerDependencies: - encoding - '@nestjs/mapped-types@2.0.5(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(reflect-metadata@0.1.14)': + '@nestjs/mapped-types@2.0.5(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) reflect-metadata: 0.1.14 optionalDependencies: class-transformer: 0.5.1 + class-validator: 0.14.1 - '@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3)': + '@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) body-parser: 1.20.2 cors: 2.8.5 express: 4.18.2 @@ -5342,10 +5360,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/schedule@5.0.1(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))': + '@nestjs/schedule@5.0.1(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) cron: 3.5.0 '@nestjs/schematics@9.2.0(chokidar@3.5.3)(typescript@4.9.5)': @@ -5368,12 +5386,12 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/swagger@7.4.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(reflect-metadata@0.1.14)': + '@nestjs/swagger@7.4.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)': dependencies: '@microsoft/tsdoc': 0.15.0 - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/mapped-types': 2.0.5(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(reflect-metadata@0.1.14) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/mapped-types': 2.0.5(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) js-yaml: 4.1.0 lodash: 4.17.21 path-to-regexp: 3.2.0 @@ -5381,19 +5399,20 @@ snapshots: swagger-ui-dist: 5.17.14 optionalDependencies: class-transformer: 0.5.1 + class-validator: 0.14.1 - '@nestjs/testing@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3))': + '@nestjs/testing@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3))': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) tslib: 2.5.3 optionalDependencies: - '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) + '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) - '@nestjs/typeorm@10.0.2(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4)))': + '@nestjs/typeorm@10.0.2(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4)))': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) reflect-metadata: 0.1.14 rxjs: 7.8.1 typeorm: 0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4)) @@ -6001,10 +6020,10 @@ snapshots: '@sentry/types': 6.19.7 tslib: 1.14.1 - '@sentry/nestjs@8.27.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))': + '@sentry/nestjs@8.27.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@sentry/core': 8.27.0 '@sentry/node': 8.27.0 '@sentry/types': 8.27.0 @@ -6504,6 +6523,8 @@ snapshots: '@types/uuid@10.0.0': {} + '@types/validator@13.12.2': {} + '@types/wrap-ansi@3.0.0': {} '@types/ws@7.4.7': @@ -7075,6 +7096,12 @@ snapshots: class-transformer@0.5.1: {} + class-validator@0.14.1: + dependencies: + '@types/validator': 13.12.2 + libphonenumber-js: 1.12.6 + validator: 13.12.0 + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 @@ -8483,6 +8510,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libphonenumber-js@1.12.6: {} + lines-and-columns@1.2.4: {} loader-runner@4.3.0: {} @@ -9714,6 +9743,8 @@ snapshots: valid-url@1.0.9: {} + validator@13.12.0: {} + vary@1.1.2: {} walker@1.0.8: diff --git a/src/common/dto/request.dto.ts b/src/common/dto/request.dto.ts deleted file mode 100644 index 60a4124..0000000 --- a/src/common/dto/request.dto.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { SkillPayload } from "src/common/interfaces/request/skillPayload"; -import { Bot } from "../interfaces/request/fields/bot"; -import { Intent } from "../interfaces/request/fields/intent"; -import { UserRequest } from "../interfaces/request/fields/userRequest"; -import { ApiProperty } from "@nestjs/swagger"; -import { Action } from "../interfaces/request/fields/action"; - -export class RequestDTO implements SkillPayload { - @ApiProperty({ - description: "봇 정보", - required: false, - }) - bot: Bot; - - @ApiProperty({ - description: "인텐트 정보", - required: false, - }) - intent: Intent; - - @ApiProperty({ - description: "액션 정보", - required: false, - }) - action: Action; - - @ApiProperty({ - description: "유저 요청 정보", - example: { - user: { - id: "74f7e7ab2bf19e63bb1ec845b760631259d7615440a2d3db7b344ac48ed1bbcde5" - } - } - }) - userRequest: UserRequest; - - @ApiProperty({ - description: "문맥 정보", - required: false, - }) - contexts?: any[]; -} \ No newline at end of file diff --git a/src/common/dto/response.dto.ts b/src/common/dto/response.dto.ts deleted file mode 100644 index 1515038..0000000 --- a/src/common/dto/response.dto.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Expose, Exclude } from 'class-transformer'; -import { ContextControl } from 'src/common/interfaces/response/fields/context'; -import { SkillResponse } from 'src/common/interfaces/response/response'; -import { SkillTemplate } from 'src/common/interfaces/response/fields/template'; - -export class ResponseDTO implements SkillResponse { - @Exclude() - version: string; - - @Expose() - template: SkillTemplate; - - @Expose() - context?: ContextControl; - - @Expose() - data?: Map; - - constructor( - template: SkillTemplate, - context?: ContextControl, - data?: Map, - ) { - this.version = '2.0'; // 버전은 고정값 - this.template = template; - this.context = context; - this.data = data; - } -} diff --git a/src/instrument.ts b/src/instrument.ts index 97b4ccc..b24d6ec 100644 --- a/src/instrument.ts +++ b/src/instrument.ts @@ -27,7 +27,7 @@ Sentry.init({ enableTracing: process.env.NODE_ENV !== 'development', // Enable debug mode to log event submission - debug: process.env.NODE_ENV === 'development', + // debug: process.env.NODE_ENV === 'development', // Advanced, optional: Called for message and error events beforeSend(event) { diff --git a/src/main.ts b/src/main.ts index 738ed16..faa1ac8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,35 +6,63 @@ import * as process from 'process'; import { NestFactory, HttpAdapterHost } from '@nestjs/core'; import { AppModule } from './modules/app/app.module'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { KakaoInterceptor } from './interceptors/kakao.interceptor'; -import { ResponseDTO } from './common/dto/response.dto'; -import { SentryInterceptor } from './interceptors/sentry.interceptor.js'; -import { SentryFilter } from './common/filter/sentry.filter'; +import { KakaoInterceptor } from './modules/interceptors/kakao.interceptor'; +import { ResponseDTO } from './modules/common/dtos/response.dto'; +import { SentryInterceptor } from './modules/interceptors/sentry.interceptor.js'; +import { SentryFilter } from './modules/common/filters/sentry.filter'; import { initializeTransactionalContext } from 'typeorm-transactional'; +import { ValidationPipe } from '@nestjs/common'; +import { HttpExceptionFilter } from './modules/common/filters/http-exception.filter'; async function bootstrap() { initializeTransactionalContext(); const app = await NestFactory.create(AppModule); const nodeEnv = process.env.NODE_ENV; - app.useGlobalInterceptors(new SentryInterceptor()); - + if (nodeEnv == 'production') { + app.useGlobalInterceptors(new SentryInterceptor()); + const { httpAdapter } = app.get(HttpAdapterHost); + app.useGlobalFilters(new SentryFilter(httpAdapter)); + } else { + app.useGlobalFilters(new HttpExceptionFilter()); + } app.useGlobalInterceptors(new KakaoInterceptor(ResponseDTO)); - const { httpAdapter } = app.get(HttpAdapterHost); - app.useGlobalFilters(new SentryFilter(httpAdapter)); + app.useGlobalPipes(new ValidationPipe({ + whitelist: false, + forbidNonWhitelisted: false, + transform: true, + //transformOptions: { enableImplicitConversion: true } + })) + app.setGlobalPrefix('api/node'); const config = new DocumentBuilder() .setTitle('커넥트지누 노드 서버 API') .setDescription('커넥트지누 노드 서버 API 문서입니다.') .setVersion('1.0') + .addApiKey( + { + type: 'apiKey', + name: 'X-USER-ID', + in: 'header', + description: 'userId를 입력하세요 (예: 123456)', + }, + 'X-USER-ID', + ) + .addSecurityRequirements('X-USER-ID') .build(); const document = SwaggerModule.createDocument(app, config); - - SwaggerModule.setup('api/node/docs', app, document); - await app.listen(nodeEnv == 'production' ? 5200 : 5000); + SwaggerModule.setup('api/node/docs', app, document, + { + swaggerOptions: { + persistAuthorization: true, // 새로고침해도 인증 정보 유지 + defaultModelsExpandDepth: -1 + } + } + ); + await app.listen(nodeEnv == 'production' ? 5200 : 5001); } bootstrap(); diff --git a/src/modules/app/app.controller.ts b/src/modules/app/app.controller.ts index 69df980..488ce18 100644 --- a/src/modules/app/app.controller.ts +++ b/src/modules/app/app.controller.ts @@ -1,6 +1,6 @@ import { Controller } from '@nestjs/common'; import { AppService } from './app.service'; -import { ResponseDTO } from '../../common/dto/response.dto'; +import { ResponseDTO } from '../common/dtos/response.dto'; @Controller() export class AppController { diff --git a/src/modules/app/app.module.ts b/src/modules/app/app.module.ts index c47f451..d04158c 100644 --- a/src/modules/app/app.module.ts +++ b/src/modules/app/app.module.ts @@ -2,17 +2,17 @@ import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup'; import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { UtilsModule } from 'src/common/utils/utils.module'; +import { UtilsModule } from 'src/modules/common/utils/utils.module'; import { ConfigModule } from '@nestjs/config'; -import { UserModule } from '../user/user.module'; +import { UsersModule } from '../users/users.module'; import { SupabaseModule } from '../supabase/supabase.module'; -import { validationSchema } from 'src/common/utils/enviornment'; +import { validationSchema } from 'src/modules/common/utils/enviornment'; import { APP_FILTER } from '@nestjs/core'; -import { CafeteriaModule } from '../cafeteria/cafeteria.module'; import { CommonModule } from '../common/common.module'; -import { ClickerModule } from '../clicker/clicker.module'; +import { ReadingRoomsModule } from '../reading-rooms/reading-rooms.module'; import { ScheduleModule } from '@nestjs/schedule'; import { HttpModule } from '@nestjs/axios'; +import { AuthModule } from 'src/modules/auth/auth.module'; @Module({ imports: [ @@ -22,9 +22,9 @@ import { HttpModule } from '@nestjs/axios'; SupabaseModule, UtilsModule, CommonModule, - UserModule, - CafeteriaModule, - ClickerModule, + UsersModule, + ReadingRoomsModule, + AuthModule, ConfigModule.forRoot({ isGlobal: true, envFilePath: process.env.NODE_ENV === 'production' ? '.env.prod' : '.env.dev', diff --git a/src/modules/auth/auth.guard.ts b/src/modules/auth/auth.guard.ts new file mode 100644 index 0000000..165bfa0 --- /dev/null +++ b/src/modules/auth/auth.guard.ts @@ -0,0 +1,15 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Observable } from 'rxjs'; + +@Injectable() +export class AuthGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); + const userId = request.headers['x-user-id'] || request.body.userRequest.user?.id; + if (userId) { + request['userId'] = userId; + } + + return !!userId; + } +} \ No newline at end of file diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts new file mode 100644 index 0000000..0c00ca9 --- /dev/null +++ b/src/modules/auth/auth.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { AuthGuard } from './auth.guard'; +import { APP_GUARD } from '@nestjs/core'; + +@Module({ + providers: [ + AuthGuard, + { + provide: APP_GUARD, + useClass: AuthGuard + } + ], + exports: [AuthGuard] +}) +export class AuthModule {} diff --git a/src/modules/cafeteria/cafeteria.controller.spec.ts b/src/modules/cafeteria/cafeteria.controller.spec.ts deleted file mode 100644 index de876d4..0000000 --- a/src/modules/cafeteria/cafeteria.controller.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { CafeteriaController } from './cafeteria.controller'; -import { CafeteriaService } from './cafeteria.service'; - -describe('CafeteriaController', () => { - let controller: CafeteriaController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [CafeteriaController], - providers: [CafeteriaService], - }).compile(); - - controller = module.get(CafeteriaController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/modules/cafeteria/cafeteria.controller.ts b/src/modules/cafeteria/cafeteria.controller.ts deleted file mode 100644 index 3240ac4..0000000 --- a/src/modules/cafeteria/cafeteria.controller.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller } from '@nestjs/common'; -import { CafeteriaService } from './cafeteria.service'; -import { ApiTags } from '@nestjs/swagger'; - -@ApiTags('cafeteria') -@Controller('cafeteria') -export class CafeteriaController { - constructor(private readonly cafeteriaService: CafeteriaService) {} -} diff --git a/src/modules/cafeteria/cafeteria.module.ts b/src/modules/cafeteria/cafeteria.module.ts deleted file mode 100644 index 8a8cfe2..0000000 --- a/src/modules/cafeteria/cafeteria.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { CafeteriaService } from './cafeteria.service'; -import { CafeteriaController } from './cafeteria.controller'; -import { SupabaseModule } from '../supabase/supabase.module'; - -@Module({ - imports: [SupabaseModule], - controllers: [CafeteriaController], - providers: [CafeteriaService], -}) -export class CafeteriaModule {} diff --git a/src/modules/cafeteria/cafeteria.service.spec.ts b/src/modules/cafeteria/cafeteria.service.spec.ts deleted file mode 100644 index 037a337..0000000 --- a/src/modules/cafeteria/cafeteria.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { CafeteriaService } from './cafeteria.service'; - -describe('CafeteriaService', () => { - let service: CafeteriaService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [CafeteriaService], - }).compile(); - - service = module.get(CafeteriaService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/src/modules/cafeteria/cafeteria.service.ts b/src/modules/cafeteria/cafeteria.service.ts deleted file mode 100644 index cc8d4ed..0000000 --- a/src/modules/cafeteria/cafeteria.service.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class CafeteriaService { - constructor() {} -} diff --git a/src/modules/clicker/clicker.controller.ts b/src/modules/clicker/clicker.controller.ts deleted file mode 100644 index 63dc93e..0000000 --- a/src/modules/clicker/clicker.controller.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Body, Controller, Post } from '@nestjs/common'; -import { ClickerService } from './clicker.service'; -import { ApiBody, ApiTags } from '@nestjs/swagger'; -import { CommonService } from '../common/common.service'; -import { BlockId } from 'src/common/utils/constants'; -import { ResponseDTO } from 'src/common/dto/response.dto'; -import { SkillPayload } from 'src/common/interfaces/request/skillPayload'; - -@ApiTags('clicker') -@Controller('clicker') -export class ClickerController { - constructor( - private readonly clickerService: ClickerService, - private readonly commonService: CommonService, - ) {} - - @Post('get/campus') - async getCampus(): Promise { - const blockId = BlockId.READING_ROOM_LIST; - const template = await this.commonService.getCampusListCard(blockId); - return new ResponseDTO(template); - } - - @Post('get/reading-room') - async getReadingRoom(@Body() body: SkillPayload): Promise { - const { clientExtra } = body.action; - const campusId = clientExtra['campusId']; - const blockId = BlockId.READING_ROOM_DETAIL; - const template = await this.clickerService.getReadingRoomListCard( - campusId, - blockId, - ); - return new ResponseDTO(template); - } - - @Post('get/reading-room-detail') - async getReadingRoomDetail(@Body() body: SkillPayload): Promise { - const { clientExtra } = body.action; - const readingRoomId = clientExtra['readingRoomId']; - const template = await this.clickerService.getReadingRoomDetailCard( - readingRoomId, - ); - return new ResponseDTO(template); - } -} diff --git a/src/modules/clicker/clicker.module.ts b/src/modules/clicker/clicker.module.ts deleted file mode 100644 index d267d1b..0000000 --- a/src/modules/clicker/clicker.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ClickerService } from './clicker.service'; -import { ClickerController } from './clicker.controller'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { SupabaseModule } from '../supabase/supabase.module'; -import { CommonModule } from '../common/common.module'; -import { ClickerRepository } from './repository/clicker.repository'; -import { ClickerEntity } from './entities/clicker.entity'; - -@Module({ - imports: [ - SupabaseModule, - CommonModule, - TypeOrmModule.forFeature([ClickerEntity]), - ], - controllers: [ClickerController], - providers: [ClickerService, ClickerRepository], -}) -export class ClickerModule {} diff --git a/src/modules/clicker/repository/clicker.repository.ts b/src/modules/clicker/repository/clicker.repository.ts deleted file mode 100644 index c6cc947..0000000 --- a/src/modules/clicker/repository/clicker.repository.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { In, Repository } from 'typeorm'; -import { ClickerEntity } from '../entities/clicker.entity'; - -@Injectable() -export class ClickerRepository { - constructor( - @InjectRepository(ClickerEntity) - private readonly clickerRepository: Repository, // Inject the repository here - ) {} - - async findByCampusId(campusId: number): Promise { - return await this.clickerRepository.find({ - where: { - campusId: campusId, - isActive: true, - }, - }); - } -} diff --git a/src/modules/common/common.module.ts b/src/modules/common/common.module.ts index ec2ede5..07550b7 100644 --- a/src/modules/common/common.module.ts +++ b/src/modules/common/common.module.ts @@ -2,23 +2,23 @@ import { Module } from '@nestjs/common'; import { CommonService } from './common.service'; import { SupabaseModule } from '../supabase/supabase.module'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { CampusEntity } from './entities/campus.entity'; -import { CollegeEntity } from './entities/college.entity'; -import { DepartmentEntity } from './entities/department.entity'; -import { CampusRepository } from './repository/campus.repository'; -import { CollegeRepository } from './repository/college.repository'; -import { DepartmentRepository } from './repository/department.repository'; +import { Campus } from './entities/campus.entity'; +import { College } from './entities/college.entity'; +import { Department } from './entities/department.entity'; +import { CampusesRepository } from './repositories/campuses.repository'; +import { CollegesRepository } from './repositories/colleges.repository'; +import { DepartmentsRepository } from './repositories/departments.repository'; @Module({ imports: [ SupabaseModule, - TypeOrmModule.forFeature([CampusEntity, CollegeEntity, DepartmentEntity]), + TypeOrmModule.forFeature([Campus, College, Department]), ], providers: [ CommonService, - CampusRepository, - CollegeRepository, - DepartmentRepository, + CampusesRepository, + CollegesRepository, + DepartmentsRepository, ], exports: [CommonService], }) diff --git a/src/modules/common/common.service.ts b/src/modules/common/common.service.ts index 9f71e8f..44f9697 100644 --- a/src/modules/common/common.service.ts +++ b/src/modules/common/common.service.ts @@ -1,38 +1,38 @@ import { Injectable } from '@nestjs/common'; -import { CampusRepository } from './repository/campus.repository'; -import { CollegeRepository } from './repository/college.repository'; -import { DepartmentRepository } from './repository/department.repository'; -import { SkillTemplate } from 'src/common/interfaces/response/fields/template'; +import { CampusesRepository } from './repositories/campuses.repository'; +import { CollegesRepository } from './repositories/colleges.repository'; +import { DepartmentsRepository } from './repositories/departments.repository'; +import { SkillTemplate } from 'src/modules/common/interfaces/response/fields/template'; import { ApiTags } from '@nestjs/swagger'; -import { Button, ListItem } from 'src/common/interfaces/response/fields/etc'; -import { BlockId, ListCardConfig } from 'src/common/utils/constants'; -import { ListCard } from 'src/common/interfaces/response/fields/component'; -import { createListCard } from 'src/common/utils/component'; +import { ListItem } from 'src/modules/common/interfaces/response/fields/etc'; +import { BlockId, ListCardConfig } from 'src/modules/common/utils/constants'; +import { ListCard } from 'src/modules/common/interfaces/response/fields/component'; +import { createListCard } from 'src/modules/common/utils/component'; @ApiTags('common') @Injectable() export class CommonService { constructor( - private readonly campusRepository: CampusRepository, - private readonly collegeRepository: CollegeRepository, - private readonly departmentRepository: DepartmentRepository, + private readonly campusesRepository: CampusesRepository, + private readonly collegesRepository: CollegesRepository, + private readonly departmentsRepository: DepartmentsRepository, ) {} - async getCampusListCard(blockId: string): Promise { - const campusEntities = await this.campusRepository.findAll(); + public async campusesListCard(blockId: string): Promise { + const campuses = await this.campusesRepository.findAll(); const header: ListItem = { title: '캠퍼스 선택', }; - const items: ListItem[] = campusEntities.map((campusEntity) => { + const items: ListItem[] = campuses.map((campus) => { return { - title: campusEntity.name, - imageUrl: campusEntity.thumbnailUrl, + title: campus.name, + imageUrl: campus.thumbnailUrl, action: 'block', blockId, extra: { - campusId: campusEntity.id, + campusId: campus.id, }, }; }); @@ -44,26 +44,26 @@ export class CommonService { }; } - async getCollegeListCard( + public async collegesListCard( campusId: number, - page: number = 1, + page: number, blockId: string, ): Promise { - const [collegeEntities, total] = await this.collegeRepository.findAll(page); + const [colleges, total] = await this.collegesRepository.findAll(page); const header: ListItem = { title: '단과대학 선택', }; - const items: ListItem[] = collegeEntities.map((collegeEntity) => { + const items: ListItem[] = colleges.map((college) => { return { - title: collegeEntity.name, - imageUrl: collegeEntity.thumbnailUrl, + title: college.name, + imageUrl: college.thumbnailUrl, action: 'block', blockId, extra: { campusId: campusId, - collegeId: collegeEntity.id, + collegeId: college.id, }, }; }); @@ -103,27 +103,27 @@ export class CommonService { }; } - async getDepartmentListCard( + public async departmentsListCard( campusId: number, collegeId: number, page: number = 1, blockId: string, ): Promise { - const [departmentEntities, total] = - await this.departmentRepository.findByCollegeId(collegeId, page); + const [departments, total] = + await this.departmentsRepository.findByCollegeId(collegeId, page); const header: ListItem = { title: '학과 선택', }; - const items: ListItem[] = departmentEntities.map((departmentEntity) => { + const items: ListItem[] = departments.map((department) => { return { - title: departmentEntity.name, + title: department.name, action: 'block', blockId, extra: { campusId, - departmentId: departmentEntity.id, + departmentId: department.id, }, }; }); diff --git a/src/modules/common/decorators/api-skill-body.decorator.ts b/src/modules/common/decorators/api-skill-body.decorator.ts new file mode 100644 index 0000000..ea4577e --- /dev/null +++ b/src/modules/common/decorators/api-skill-body.decorator.ts @@ -0,0 +1,22 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiBody, ApiExtraModels, getSchemaPath } from '@nestjs/swagger'; +import { Type } from '@nestjs/common'; + +export function ApiSkillBody(dto: Type) { + return applyDecorators( + ApiExtraModels(dto), + ApiBody({ + schema: { + type: 'object', + properties: { + action: { + type: 'object', + properties: { + clientExtra: { $ref: getSchemaPath(dto) }, + }, + }, + }, + }, + }), + ); +} \ No newline at end of file diff --git a/src/modules/common/dtos/requests/action/action.dto.ts b/src/modules/common/dtos/requests/action/action.dto.ts new file mode 100644 index 0000000..fb82785 --- /dev/null +++ b/src/modules/common/dtos/requests/action/action.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { Allow, ValidateNested } from "class-validator"; + +export class ClientExtraDto {} + +export class ActionDto { + @ApiProperty({ description: '추가 클라이언트 정보' }) + @ValidateNested() + @Type(() => ClientExtraDto) + clientExtra: ClientExtraDto; +} \ No newline at end of file diff --git a/src/modules/common/dtos/requests/action/index.ts b/src/modules/common/dtos/requests/action/index.ts new file mode 100644 index 0000000..928c21e --- /dev/null +++ b/src/modules/common/dtos/requests/action/index.ts @@ -0,0 +1 @@ +export * from './action.dto' \ No newline at end of file diff --git a/src/modules/common/dtos/requests/index.ts b/src/modules/common/dtos/requests/index.ts new file mode 100644 index 0000000..e959dc6 --- /dev/null +++ b/src/modules/common/dtos/requests/index.ts @@ -0,0 +1,2 @@ +export * from './action'; +export * from './user'; \ No newline at end of file diff --git a/src/modules/common/dtos/requests/skill-payload.dto.ts b/src/modules/common/dtos/requests/skill-payload.dto.ts new file mode 100644 index 0000000..b57ec4e --- /dev/null +++ b/src/modules/common/dtos/requests/skill-payload.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { ValidateNested } from "class-validator"; +import { ActionDto } from "./action"; + +export class SkillPayloadDto { + @ApiProperty({ description: 'Action 정보' }) + @ValidateNested() + @Type(() => ActionDto) + action: ActionDto; +} \ No newline at end of file diff --git a/src/modules/common/dtos/requests/user/index.ts b/src/modules/common/dtos/requests/user/index.ts new file mode 100644 index 0000000..964fd11 --- /dev/null +++ b/src/modules/common/dtos/requests/user/index.ts @@ -0,0 +1 @@ +export * from './user.dto'; \ No newline at end of file diff --git a/src/modules/common/dtos/requests/user/user.dto.ts b/src/modules/common/dtos/requests/user/user.dto.ts new file mode 100644 index 0000000..b1faa60 --- /dev/null +++ b/src/modules/common/dtos/requests/user/user.dto.ts @@ -0,0 +1,16 @@ +import { ApiHideProperty, ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { IsString, ValidateNested } from "class-validator"; + +export class UserDto { + @ApiProperty({ description: '사용자 ID' }) + @IsString() + id: string; +} + +export class UserRequestDto { + @ApiHideProperty() + @ValidateNested() + @Type(() => UserDto) + user: UserDto; +} \ No newline at end of file diff --git a/src/modules/common/dtos/response.dto.ts b/src/modules/common/dtos/response.dto.ts new file mode 100644 index 0000000..e3835c7 --- /dev/null +++ b/src/modules/common/dtos/response.dto.ts @@ -0,0 +1,26 @@ +import { Expose, Exclude } from 'class-transformer'; + +export class ResponseDTO { + @Exclude() + version: string; + + @Expose() + template: any; + + @Expose() + context?: any; + + @Expose() + data?: Map; + + constructor( + template: any, + context?: any, + data?: Map, + ) { + this.version = '2.0'; // 버전은 고정값 + this.template = template; + this.context = context; + this.data = data; + } +} diff --git a/src/modules/common/entities/campus.entity.ts b/src/modules/common/entities/campus.entity.ts index b0abdf4..eee88ff 100644 --- a/src/modules/common/entities/campus.entity.ts +++ b/src/modules/common/entities/campus.entity.ts @@ -1,7 +1,7 @@ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity('campus') -export class CampusEntity { +export class Campus { @PrimaryGeneratedColumn() id: number; diff --git a/src/modules/common/entities/college.entity.ts b/src/modules/common/entities/college.entity.ts index d2fffde..e7aa0dc 100644 --- a/src/modules/common/entities/college.entity.ts +++ b/src/modules/common/entities/college.entity.ts @@ -1,7 +1,7 @@ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity('college') -export class CollegeEntity { +export class College { @PrimaryGeneratedColumn() id: number; diff --git a/src/modules/common/entities/department.entity.ts b/src/modules/common/entities/department.entity.ts index 88dc2cd..eb2d3e7 100644 --- a/src/modules/common/entities/department.entity.ts +++ b/src/modules/common/entities/department.entity.ts @@ -5,16 +5,16 @@ import { ManyToOne, JoinColumn, } from 'typeorm'; -import { CollegeEntity } from './college.entity'; +import { College } from './college.entity'; @Entity('department') -export class DepartmentEntity { +export class Department { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => CollegeEntity, (college) => college.id) + @ManyToOne(() => College, (college) => college.id) @JoinColumn({ name: 'college_id' }) - college: CollegeEntity; + college: College; @Column({ name: 'department_ko' }) name: string; diff --git a/src/modules/common/filters/http-exception.filter.ts b/src/modules/common/filters/http-exception.filter.ts new file mode 100644 index 0000000..f5cbd81 --- /dev/null +++ b/src/modules/common/filters/http-exception.filter.ts @@ -0,0 +1,40 @@ +import { ExceptionFilter, Catch, ArgumentsHost, HttpException, Logger } from '@nestjs/common'; +import { Request, Response } from 'express'; + +@Catch(HttpException) +export class HttpExceptionFilter implements ExceptionFilter { + private readonly logger = new Logger(HttpExceptionFilter.name); + + catch(exception: HttpException, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + const status = exception.getStatus(); + const errorResponse = exception.getResponse(); + + this.logger.error( + JSON.stringify( + { + type: 'HTTP_VALIDATION_ERROR', + status, + url: request.url, + method: request.method, + errors: Array.isArray(errorResponse['message']) + ? errorResponse['message'] + : [errorResponse['message']], // 단일 메시지도 배열로 변환 + }, + null, + 2 // JSON 포맷 적용 (2칸 들여쓰기) + ) + ); + + response + .status(status) + .json({ + statusCode: status, + message: errorResponse['message'] || exception.message, + path: request.url, + timestamp: new Date().toISOString(), + }); + } +} \ No newline at end of file diff --git a/src/common/filter/sentry.filter.ts b/src/modules/common/filters/sentry.filter.ts similarity index 100% rename from src/common/filter/sentry.filter.ts rename to src/modules/common/filters/sentry.filter.ts diff --git a/src/common/interfaces/request/fields/action.ts b/src/modules/common/interfaces/request/fields/action.ts similarity index 100% rename from src/common/interfaces/request/fields/action.ts rename to src/modules/common/interfaces/request/fields/action.ts diff --git a/src/common/interfaces/request/fields/bot.ts b/src/modules/common/interfaces/request/fields/bot.ts similarity index 100% rename from src/common/interfaces/request/fields/bot.ts rename to src/modules/common/interfaces/request/fields/bot.ts diff --git a/src/common/interfaces/request/fields/etc.ts b/src/modules/common/interfaces/request/fields/etc.ts similarity index 100% rename from src/common/interfaces/request/fields/etc.ts rename to src/modules/common/interfaces/request/fields/etc.ts diff --git a/src/common/interfaces/request/fields/intent.ts b/src/modules/common/interfaces/request/fields/intent.ts similarity index 100% rename from src/common/interfaces/request/fields/intent.ts rename to src/modules/common/interfaces/request/fields/intent.ts diff --git a/src/common/interfaces/request/fields/userRequest.ts b/src/modules/common/interfaces/request/fields/userRequest.ts similarity index 100% rename from src/common/interfaces/request/fields/userRequest.ts rename to src/modules/common/interfaces/request/fields/userRequest.ts diff --git a/src/common/interfaces/request/skillPayload.ts b/src/modules/common/interfaces/request/skillPayload.ts similarity index 100% rename from src/common/interfaces/request/skillPayload.ts rename to src/modules/common/interfaces/request/skillPayload.ts diff --git a/src/common/interfaces/response/fields/component.ts b/src/modules/common/interfaces/response/fields/component.ts similarity index 100% rename from src/common/interfaces/response/fields/component.ts rename to src/modules/common/interfaces/response/fields/component.ts diff --git a/src/common/interfaces/response/fields/context.ts b/src/modules/common/interfaces/response/fields/context.ts similarity index 100% rename from src/common/interfaces/response/fields/context.ts rename to src/modules/common/interfaces/response/fields/context.ts diff --git a/src/common/interfaces/response/fields/etc.ts b/src/modules/common/interfaces/response/fields/etc.ts similarity index 100% rename from src/common/interfaces/response/fields/etc.ts rename to src/modules/common/interfaces/response/fields/etc.ts diff --git a/src/common/interfaces/response/fields/template.ts b/src/modules/common/interfaces/response/fields/template.ts similarity index 100% rename from src/common/interfaces/response/fields/template.ts rename to src/modules/common/interfaces/response/fields/template.ts diff --git a/src/common/interfaces/response/response.ts b/src/modules/common/interfaces/response/response.ts similarity index 100% rename from src/common/interfaces/response/response.ts rename to src/modules/common/interfaces/response/response.ts diff --git a/src/modules/common/repository/campus.repository.ts b/src/modules/common/repositories/campuses.repository.ts similarity index 53% rename from src/modules/common/repository/campus.repository.ts rename to src/modules/common/repositories/campuses.repository.ts index ce14b4e..7e1b201 100644 --- a/src/modules/common/repository/campus.repository.ts +++ b/src/modules/common/repositories/campuses.repository.ts @@ -1,17 +1,17 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { In, Repository } from 'typeorm'; -import { CampusEntity } from '../entities/campus.entity'; +import { Campus } from '../entities/campus.entity'; @Injectable() -export class CampusRepository { +export class CampusesRepository { constructor( - @InjectRepository(CampusEntity) - private readonly campusRepository: Repository, // Inject the repository here + @InjectRepository(Campus) + private readonly campusesRepository: Repository, ) {} - async findAll(): Promise { - return await this.campusRepository.find({ + async findAll(): Promise { + return await this.campusesRepository.find({ select: ['id', 'name', 'thumbnailUrl'], where: { id: In([1, 2, 3, 4]), @@ -20,7 +20,7 @@ export class CampusRepository { } async getCampusName(campusId: number): Promise { - const campus = await this.campusRepository.findOne({ + const campus = await this.campusesRepository.findOne({ where: { id: campusId, }, diff --git a/src/modules/common/repository/college.repository.ts b/src/modules/common/repositories/colleges.repository.ts similarity index 50% rename from src/modules/common/repository/college.repository.ts rename to src/modules/common/repositories/colleges.repository.ts index 6936f0d..4e543d7 100644 --- a/src/modules/common/repository/college.repository.ts +++ b/src/modules/common/repositories/colleges.repository.ts @@ -1,18 +1,18 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { CollegeEntity } from '../entities/college.entity'; -import { ListCardConfig } from 'src/common/utils/constants'; +import { College } from '../entities/college.entity'; +import { ListCardConfig } from 'src/modules/common/utils/constants'; @Injectable() -export class CollegeRepository { +export class CollegesRepository { constructor( - @InjectRepository(CollegeEntity) - private readonly collegeRepository: Repository, // Inject the repository here + @InjectRepository(College) + private readonly collegesRepository: Repository, ) {} - async findAll(page: number): Promise<[CollegeEntity[], number]> { - return await this.collegeRepository + async findAll(page: number): Promise<[College[], number]> { + return await this.collegesRepository .createQueryBuilder('college') .where("college.name != '공통'") .orderBy('college.name', 'ASC') diff --git a/src/modules/common/repository/department.repository.ts b/src/modules/common/repositories/departments.repository.ts similarity index 56% rename from src/modules/common/repository/department.repository.ts rename to src/modules/common/repositories/departments.repository.ts index 8d0dd19..f3f7fd5 100644 --- a/src/modules/common/repository/department.repository.ts +++ b/src/modules/common/repositories/departments.repository.ts @@ -1,21 +1,21 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { DepartmentEntity } from '../entities/department.entity'; -import { ListCardConfig } from 'src/common/utils/constants'; +import { Department } from '../entities/department.entity'; +import { ListCardConfig } from 'src/modules/common/utils/constants'; @Injectable() -export class DepartmentRepository { +export class DepartmentsRepository { constructor( - @InjectRepository(DepartmentEntity) - private readonly departmentRepository: Repository, // Inject the repository here + @InjectRepository(Department) + private readonly departmentsRepository: Repository, ) {} async findByCollegeId( collegeId: number, page: number, - ): Promise<[DepartmentEntity[], number]> { - return await this.departmentRepository + ): Promise<[Department[], number]> { + return await this.departmentsRepository .createQueryBuilder('department') .where('department.college_id = :collegeId', { collegeId }) .orderBy('department.name', 'ASC') diff --git a/src/common/utils/component.ts b/src/modules/common/utils/component.ts similarity index 100% rename from src/common/utils/component.ts rename to src/modules/common/utils/component.ts diff --git a/src/common/utils/constants.ts b/src/modules/common/utils/constants.ts similarity index 100% rename from src/common/utils/constants.ts rename to src/modules/common/utils/constants.ts diff --git a/src/common/utils/enviornment.ts b/src/modules/common/utils/enviornment.ts similarity index 100% rename from src/common/utils/enviornment.ts rename to src/modules/common/utils/enviornment.ts diff --git a/src/common/utils/utils.module.ts b/src/modules/common/utils/utils.module.ts similarity index 100% rename from src/common/utils/utils.module.ts rename to src/modules/common/utils/utils.module.ts diff --git a/src/common/utils/utils.service.ts b/src/modules/common/utils/utils.service.ts similarity index 100% rename from src/common/utils/utils.service.ts rename to src/modules/common/utils/utils.service.ts diff --git a/src/interceptors/kakao.interceptor.ts b/src/modules/interceptors/kakao.interceptor.ts similarity index 100% rename from src/interceptors/kakao.interceptor.ts rename to src/modules/interceptors/kakao.interceptor.ts diff --git a/src/interceptors/sentry.interceptor.ts b/src/modules/interceptors/sentry.interceptor.ts similarity index 100% rename from src/interceptors/sentry.interceptor.ts rename to src/modules/interceptors/sentry.interceptor.ts diff --git a/src/modules/reading-rooms/dtos/request/get-reading-room-detail-request.dto.ts b/src/modules/reading-rooms/dtos/request/get-reading-room-detail-request.dto.ts new file mode 100644 index 0000000..2b40c0f --- /dev/null +++ b/src/modules/reading-rooms/dtos/request/get-reading-room-detail-request.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsNumber } from "class-validator"; +import { ClientExtraDto } from "src/modules/common/dtos/requests"; + +export class GetReadingRoomDetailRequestDto extends ClientExtraDto { + @IsNotEmpty() + @IsNumber() + @ApiProperty({ + description: '열람실 ID', + default: 1 + }) + readingRoomId: number +} \ No newline at end of file diff --git a/src/modules/reading-rooms/dtos/request/list-reading-rooms-request.dto.ts b/src/modules/reading-rooms/dtos/request/list-reading-rooms-request.dto.ts new file mode 100644 index 0000000..5eb18a2 --- /dev/null +++ b/src/modules/reading-rooms/dtos/request/list-reading-rooms-request.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsNumber } from "class-validator"; +import { ClientExtraDto } from "src/modules/common/dtos/requests"; + +export class ListReadingRoomsRequestDto extends ClientExtraDto { + @IsNotEmpty() + @IsNumber() + @ApiProperty({ + description: '캠퍼스 ID', + default: 1 + }) + campusId: number +} \ No newline at end of file diff --git a/src/modules/clicker/entities/clicker.entity.ts b/src/modules/reading-rooms/entities/reading-rooms.entity.ts similarity index 93% rename from src/modules/clicker/entities/clicker.entity.ts rename to src/modules/reading-rooms/entities/reading-rooms.entity.ts index 15dd7e9..9899434 100644 --- a/src/modules/clicker/entities/clicker.entity.ts +++ b/src/modules/reading-rooms/entities/reading-rooms.entity.ts @@ -1,7 +1,7 @@ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity('reading_room') -export class ClickerEntity { +export class ReadingRoom { @PrimaryGeneratedColumn() id: number; diff --git a/src/modules/clicker/clicker.controller.spec.ts b/src/modules/reading-rooms/reading-rooms.controller.spec.ts similarity index 79% rename from src/modules/clicker/clicker.controller.spec.ts rename to src/modules/reading-rooms/reading-rooms.controller.spec.ts index 8c7307e..5a29e1a 100644 --- a/src/modules/clicker/clicker.controller.spec.ts +++ b/src/modules/reading-rooms/reading-rooms.controller.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { ClickerController } from './clicker.controller'; -import { ClickerService } from './clicker.service'; +import { ClickerController } from './reading-rooms.controller'; +import { ClickerService } from './reading-rooms.service'; describe('ClickerController', () => { let controller: ClickerController; diff --git a/src/modules/reading-rooms/reading-rooms.controller.ts b/src/modules/reading-rooms/reading-rooms.controller.ts new file mode 100644 index 0000000..2c59380 --- /dev/null +++ b/src/modules/reading-rooms/reading-rooms.controller.ts @@ -0,0 +1,49 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { CommonService } from '../common/common.service'; +import { BlockId } from 'src/modules/common/utils/constants'; +import { ResponseDTO } from 'src/modules/common/dtos/response.dto'; +import { ApiSkillBody } from '../common/decorators/api-skill-body.decorator'; +import { ListReadingRoomsRequestDto } from './dtos/request/list-reading-rooms-request.dto'; +import { GetReadingRoomDetailRequestDto } from './dtos/request/get-reading-room-detail-request.dto'; +import { SkillPayloadDto } from '../common/dtos/requests/skill-payload.dto'; +import { plainToInstance } from 'class-transformer'; +import { ReadingRoomsService } from './reading-rooms.service'; + +@ApiTags('reading-rooms') +@Controller('reading-rooms') +export class ReadingRoomsController { + constructor( + private readonly readingRoomsService: ReadingRoomsService, + private readonly commonService: CommonService, + ) {} + + @Post('campuses/list') + public async listCampuses(): Promise { + const blockId = BlockId.READING_ROOM_LIST; + const template = await this.commonService.campusesListCard(blockId); + return new ResponseDTO(template); + } + + @Post('list') + @ApiSkillBody(ListReadingRoomsRequestDto) + public async listReadingRooms(@Body() body: SkillPayloadDto): Promise { + const { campusId } = plainToInstance(ListReadingRoomsRequestDto, body.action.clientExtra); + const blockId = BlockId.READING_ROOM_DETAIL; + const template = await this.readingRoomsService.readingRoomsListCard( + campusId, + blockId, + ); + return new ResponseDTO(template); + } + + @Post('detail') + @ApiSkillBody(GetReadingRoomDetailRequestDto) + public async getReadingRoomDetail(@Body() body: SkillPayloadDto): Promise { + const { readingRoomId } = plainToInstance(GetReadingRoomDetailRequestDto, body.action.clientExtra); + const template = await this.readingRoomsService.readingRoomDetailComplexCard( + readingRoomId, + ); + return new ResponseDTO(template); + } +} diff --git a/src/modules/reading-rooms/reading-rooms.module.ts b/src/modules/reading-rooms/reading-rooms.module.ts new file mode 100644 index 0000000..1bdf3d6 --- /dev/null +++ b/src/modules/reading-rooms/reading-rooms.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SupabaseModule } from '../supabase/supabase.module'; +import { CommonModule } from '../common/common.module'; +import { ReadingRoom } from './entities/reading-rooms.entity'; +import { ReadingRoomsController } from './reading-rooms.controller'; +import { ReadingRoomsService } from './reading-rooms.service'; +import { ReadingRoomsRepository } from './repositories/reading-rooms.repository'; + +@Module({ + imports: [ + SupabaseModule, + CommonModule, + TypeOrmModule.forFeature([ReadingRoom]), + ], + controllers: [ReadingRoomsController], + providers: [ReadingRoomsService, ReadingRoomsRepository], +}) +export class ReadingRoomsModule {} diff --git a/src/modules/clicker/clicker.service.spec.ts b/src/modules/reading-rooms/reading-rooms.service.spec.ts similarity index 87% rename from src/modules/clicker/clicker.service.spec.ts rename to src/modules/reading-rooms/reading-rooms.service.spec.ts index 48a65b0..9f02734 100644 --- a/src/modules/clicker/clicker.service.spec.ts +++ b/src/modules/reading-rooms/reading-rooms.service.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { ClickerService } from './clicker.service'; +import { ClickerService } from './reading-rooms.service'; describe('ClickerService', () => { let service: ClickerService; diff --git a/src/modules/clicker/clicker.service.ts b/src/modules/reading-rooms/reading-rooms.service.ts similarity index 61% rename from src/modules/clicker/clicker.service.ts rename to src/modules/reading-rooms/reading-rooms.service.ts index 064e810..cb0e620 100644 --- a/src/modules/clicker/clicker.service.ts +++ b/src/modules/reading-rooms/reading-rooms.service.ts @@ -1,18 +1,18 @@ import { Injectable } from '@nestjs/common'; -import { ClickerRepository } from './repository/clicker.repository'; -import { ListItem } from 'src/common/interfaces/response/fields/etc'; -import { ListCard } from 'src/common/interfaces/response/fields/component'; +import { ReadingRoomsRepository } from './repositories/reading-rooms.repository'; +import { ListItem } from 'src/modules/common/interfaces/response/fields/etc'; +import { ListCard } from 'src/modules/common/interfaces/response/fields/component'; import { createListCard, createSimpleImage, createSimpleText, -} from 'src/common/utils/component'; +} from 'src/modules/common/utils/component'; @Injectable() -export class ClickerService { - constructor(private readonly clickerRepository: ClickerRepository) {} +export class ReadingRoomsService { + constructor(private readonly readingRoomsRepository: ReadingRoomsRepository) {} - async getReadingRoomListCard( + public async readingRoomsListCard( campusId: number, blockId: string, ): Promise { @@ -24,11 +24,11 @@ export class ClickerService { }; } - const readingRoomEntities = await this.clickerRepository.findByCampusId( + const readingRooms = await this.readingRoomsRepository.findReadingRoomsByCampusId( campusId, ); - if (readingRoomEntities.length === 0) { + if (!readingRooms.length) { return { outputs: [createSimpleText('해당 캠퍼스는 업데이트 준비중이야!')], }; @@ -38,7 +38,7 @@ export class ClickerService { title: '열람실 선택', }; - const items: ListItem[] = readingRoomEntities.map((readingRoomEntity) => { + const items: ListItem[] = readingRooms.map((readingRoomEntity) => { return { title: `[${readingRoomEntity.libraryName}] ${readingRoomEntity.roomName}`, description: `총 좌석 수: ${readingRoomEntity.totalSeats}석`, @@ -50,14 +50,14 @@ export class ClickerService { }; }); - const campusListCard: ListCard = createListCard(header, items); + const campusesListCard: ListCard = createListCard(header, items); return { - outputs: [campusListCard], + outputs: [campusesListCard], }; } - async getReadingRoomDetailCard(readingRoomId: number): Promise { + public async readingRoomDetailComplexCard(readingRoomId: number): Promise { const imageUrl = `https://zppxqcdwhqqzbwpmcjjt.supabase.co/storage/v1/object/public/clicker/clicker/${readingRoomId}.png`; return { outputs: [ diff --git a/src/modules/reading-rooms/repositories/reading-rooms.repository.ts b/src/modules/reading-rooms/repositories/reading-rooms.repository.ts new file mode 100644 index 0000000..5596590 --- /dev/null +++ b/src/modules/reading-rooms/repositories/reading-rooms.repository.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { ReadingRoom } from '../entities/reading-rooms.entity'; + +@Injectable() +export class ReadingRoomsRepository { + constructor( + @InjectRepository(ReadingRoom) + private readonly readingRoomRepository: Repository, + ) {} + + async findReadingRoomsByCampusId(campusId: number): Promise { + return await this.readingRoomRepository.find({ + where: { + campusId: campusId, + isActive: true, + }, + }); + } +} diff --git a/src/modules/user/repository/user.repository.ts b/src/modules/user/repository/user.repository.ts deleted file mode 100644 index 9ffe86d..0000000 --- a/src/modules/user/repository/user.repository.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { UserEntity } from '../entities/user.entity'; - -@Injectable() -export class UserRepository { - constructor( - @InjectRepository(UserEntity) - private readonly userRepository: Repository, - ) {} - - async findUserProfile(userId: string): Promise { - return await this.userRepository.createQueryBuilder('user') - .leftJoinAndSelect('user.campus', 'campus') - .leftJoinAndSelect('user.department', 'department') - .leftJoinAndSelect('department.college', 'college') - .select([ - 'user.id', - 'campus.name', - 'department.name', - 'college.name' - ]) - .where('user.id = :userId', { userId }) - .cache(true) - .getOne(); - } - - async isExistUser(userId: string): Promise { - return (await this.userRepository.findOne({ where: { id: userId } })) !== null; - } - - async createUserInfo( - userId: string, - campusId: number, - departmentId: number, - ): Promise { - await this.userRepository.insert({ - id: userId, - campusId, - departmentId, - }); - } - - async updateUserInfo( - userId: string, - campusId: number, - departmentId: number, - ): Promise { - await this.userRepository.update(userId, { - campusId, - departmentId, - }); - } -} diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts deleted file mode 100644 index 8757023..0000000 --- a/src/modules/user/user.controller.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Body, Controller, Post } from '@nestjs/common'; -import { UserService } from './user.service'; -import { ResponseDTO } from 'src/common/dto/response.dto'; -import { SkillPayload } from 'src/common/interfaces/request/skillPayload'; -import { ApiBody, ApiTags } from '@nestjs/swagger'; -import { CommonService } from '../common/common.service'; -import { BlockId } from 'src/common/utils/constants'; -import { RequestDTO } from 'src/common/dto/request.dto'; - -@ApiTags('user') -@Controller('user') -export class UserController { - constructor( - private readonly userService: UserService, - private readonly commonService: CommonService, - ) {} - - @Post('get/campus') - async getCampus(): Promise { - const blockId = BlockId.COLLEGE_LIST; - const template = await this.commonService.getCampusListCard(blockId); - return new ResponseDTO(template); - } - - @Post('get/college') - async getCollege(@Body() body: SkillPayload): Promise { - const { clientExtra } = body.action; - const capmusId = clientExtra['campusId']; - const page = clientExtra['page']; - const blockId = BlockId.DEPARTMENT_LIST; - const template = await this.commonService.getCollegeListCard( - capmusId, - page, - blockId, - ); - return new ResponseDTO(template); - } - - @Post('get/department') - async getDepartment(@Body() body: SkillPayload): Promise { - const { clientExtra } = body.action; - const campusId = clientExtra['campusId']; - const collegeId = clientExtra['collegeId']; - const page = clientExtra['page']; - const blockId = BlockId.UPDATE_DEPARTMENT; - const template = await this.commonService.getDepartmentListCard( - campusId, - collegeId, - page, - blockId, - ); - return new ResponseDTO(template); - } - - @Post('update/department') - async upsertUserDepartment(@Body() body: SkillPayload): Promise { - const userId = body.userRequest.user.id; - const { clientExtra } = body.action; - const campusId = clientExtra['campusId']; - const departmentId = clientExtra['departmentId']; - const template = await this.userService.upsertUserDepartment( - userId, - campusId, - departmentId, - ); - return new ResponseDTO(template); - } - - @Post('get/profile') - async getUserProfile(@Body() body: RequestDTO): Promise { - const userId = body.userRequest.user.id; - const template = await this.userService.getUserProfile(userId); - return new ResponseDTO(template); - } -} diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts deleted file mode 100644 index 69c2868..0000000 --- a/src/modules/user/user.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { UserService } from './user.service'; -import { UserController } from './user.controller'; -import { SupabaseModule } from '../supabase/supabase.module'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { UserRepository } from './repository/user.repository'; -import { UserEntity } from './entities/user.entity'; -import { CommonModule } from '../common/common.module'; - -@Module({ - imports: [ - SupabaseModule, - CommonModule, - TypeOrmModule.forFeature([UserEntity]), - ], - controllers: [UserController], - providers: [UserService, UserRepository], -}) -export class UserModule {} diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts deleted file mode 100644 index 156c475..0000000 --- a/src/modules/user/user.service.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { createSimpleText, createTextCard } from 'src/common/utils/component'; -import { SkillTemplate } from 'src/common/interfaces/response/fields/template'; -import { UserRepository } from './repository/user.repository'; -import { TextCard } from 'src/common/interfaces/response/fields/component'; -import { BlockId } from 'src/common/utils/constants'; -import { Button } from 'src/common/interfaces/response/fields/etc'; -import { Transactional } from 'typeorm-transactional'; - -@Injectable() -export class UserService { - constructor(private readonly userRepository: UserRepository) {} - - @Transactional() - async upsertUserDepartment( - userId: string, - campusId: number, - departmentId: number, - ): Promise { - let simpleText = null; - - const isExist = await this.userRepository.isExistUser(userId); - if (isExist) { - await this.userRepository.updateUserInfo(userId, campusId, departmentId); - simpleText = createSimpleText('학과 정보를 수정했어!'); - } else { - await this.userRepository.createUserInfo(userId, campusId, departmentId); - simpleText = createSimpleText('학과 정보를 등록했어!'); - } - return { - outputs: [simpleText], - }; - } - - async getUserProfile(userId: string): Promise { - let affiliation = '미등록'; - let campus = '미등록'; - const user = await this.userRepository.findUserProfile(userId); - if (user) { - campus = user.campus.name; - affiliation = user.department.college.name + ' ' + user.department.name; - } - - const buttons: Array