first commit
Browse files- .eslintrc.js +25 -25
- .prettierrc +3 -3
- Docker +20 -0
- README.md +85 -85
- nest-cli.json +8 -8
- package-lock.json +0 -0
- package.json +83 -83
- src/app.controller.spec.ts +22 -22
- src/app.controller.ts +12 -12
- src/app.module.ts +14 -14
- src/app.service.ts +8 -8
- src/constants/collections.constant.ts +3 -3
- src/constants/index.ts +1 -1
- src/constants/jwt.constant.ts +3 -3
- src/constants/repository.constant.ts +3 -3
- src/locales/en.ts +9 -9
- src/main.ts +24 -24
- src/modules/activity/activity.controller.ts +145 -145
- src/modules/activity/activity.module.ts +13 -13
- src/modules/activity/activity.provider.ts +12 -12
- src/modules/activity/activity.schema.ts +41 -41
- src/modules/activity/activity.service.ts +23 -23
- src/modules/auth/auth.controller.ts +113 -113
- src/modules/auth/auth.module.ts +27 -27
- src/modules/auth/auth.service.ts +107 -107
- src/modules/auth/jwt-auth.guard.ts +18 -18
- src/modules/auth/jwt.strategy.ts +29 -29
- src/modules/auth/local-auth.guard.ts +4 -4
- src/modules/auth/local.strategy.ts +37 -37
- src/modules/database/database.module.ts +21 -21
- src/modules/database/database.provider.ts +11 -11
- src/modules/property/property.controller.ts +144 -144
- src/modules/property/property.module.ts +12 -12
- src/modules/property/property.provider.ts +12 -12
- src/modules/property/property.schema.ts +38 -38
- src/modules/property/property.service.ts +90 -90
- src/modules/shared/common.service.ts +26 -26
- src/modules/shared/sharedCrud.services.ts +28 -28
- src/modules/user/user.controller.ts +49 -49
- src/modules/user/user.dto.ts +55 -55
- src/modules/user/user.module.ts +13 -13
- src/modules/user/user.provider.ts +11 -11
- src/modules/user/user.schema.ts +78 -78
- src/modules/user/user.service.ts +25 -25
- test/app.e2e-spec.ts +24 -24
- test/jest-e2e.json +9 -9
- tsconfig.build.json +4 -4
- tsconfig.json +21 -21
- vercel.json +16 -16
.eslintrc.js
CHANGED
@@ -1,25 +1,25 @@
|
|
1 |
-
module.exports = {
|
2 |
-
parser: '@typescript-eslint/parser',
|
3 |
-
parserOptions: {
|
4 |
-
project: 'tsconfig.json',
|
5 |
-
tsconfigRootDir: __dirname,
|
6 |
-
sourceType: 'module',
|
7 |
-
},
|
8 |
-
plugins: ['@typescript-eslint/eslint-plugin'],
|
9 |
-
extends: [
|
10 |
-
'plugin:@typescript-eslint/recommended',
|
11 |
-
'plugin:prettier/recommended',
|
12 |
-
],
|
13 |
-
root: true,
|
14 |
-
env: {
|
15 |
-
node: true,
|
16 |
-
jest: true,
|
17 |
-
},
|
18 |
-
ignorePatterns: ['.eslintrc.js'],
|
19 |
-
rules: {
|
20 |
-
'@typescript-eslint/interface-name-prefix': 'off',
|
21 |
-
'@typescript-eslint/explicit-function-return-type': 'off',
|
22 |
-
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
23 |
-
'@typescript-eslint/no-explicit-any': 'off',
|
24 |
-
},
|
25 |
-
};
|
|
|
1 |
+
module.exports = {
|
2 |
+
parser: '@typescript-eslint/parser',
|
3 |
+
parserOptions: {
|
4 |
+
project: 'tsconfig.json',
|
5 |
+
tsconfigRootDir: __dirname,
|
6 |
+
sourceType: 'module',
|
7 |
+
},
|
8 |
+
plugins: ['@typescript-eslint/eslint-plugin'],
|
9 |
+
extends: [
|
10 |
+
'plugin:@typescript-eslint/recommended',
|
11 |
+
'plugin:prettier/recommended',
|
12 |
+
],
|
13 |
+
root: true,
|
14 |
+
env: {
|
15 |
+
node: true,
|
16 |
+
jest: true,
|
17 |
+
},
|
18 |
+
ignorePatterns: ['.eslintrc.js'],
|
19 |
+
rules: {
|
20 |
+
'@typescript-eslint/interface-name-prefix': 'off',
|
21 |
+
'@typescript-eslint/explicit-function-return-type': 'off',
|
22 |
+
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
23 |
+
'@typescript-eslint/no-explicit-any': 'off',
|
24 |
+
},
|
25 |
+
};
|
.prettierrc
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
{
|
2 |
-
"singleQuote": true,
|
3 |
-
"trailingComma": "all"
|
4 |
}
|
|
|
1 |
+
{
|
2 |
+
"singleQuote": true,
|
3 |
+
"trailingComma": "all"
|
4 |
}
|
Docker
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use an official Node.js runtime as the base image
|
2 |
+
FROM node:14
|
3 |
+
|
4 |
+
# Set the working directory in the container
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Copy package.json and package-lock.json to the working directory
|
8 |
+
COPY package*.json ./
|
9 |
+
|
10 |
+
# Install the project dependencies
|
11 |
+
RUN npm install
|
12 |
+
|
13 |
+
# Copy the rest of the project files to the working directory
|
14 |
+
COPY . .
|
15 |
+
|
16 |
+
# Expose port 8080 for the application
|
17 |
+
EXPOSE 8080
|
18 |
+
|
19 |
+
# Specify the command to run your application
|
20 |
+
CMD ["npm", "run", "start:dev"]
|
README.md
CHANGED
@@ -1,85 +1,85 @@
|
|
1 |
-
<p align="center">
|
2 |
-
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
3 |
-
</p>
|
4 |
-
|
5 |
-
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
6 |
-
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
7 |
-
|
8 |
-
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
9 |
-
<p align="center">
|
10 |
-
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
11 |
-
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
12 |
-
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
13 |
-
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
14 |
-
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
15 |
-
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
16 |
-
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
17 |
-
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
18 |
-
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
|
19 |
-
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
20 |
-
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
|
21 |
-
</p>
|
22 |
-
<!--[](https://opencollective.com/nest#backer)
|
23 |
-
[](https://opencollective.com/nest#sponsor)-->
|
24 |
-
|
25 |
-
## Description
|
26 |
-
|
27 |
-
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
28 |
-
|
29 |
-
## Project setup
|
30 |
-
|
31 |
-
```bash
|
32 |
-
$ npm install
|
33 |
-
```
|
34 |
-
|
35 |
-
## Compile and run the project
|
36 |
-
|
37 |
-
```bash
|
38 |
-
# development
|
39 |
-
$ npm run start
|
40 |
-
|
41 |
-
# watch mode
|
42 |
-
$ npm run start:dev
|
43 |
-
|
44 |
-
# production mode
|
45 |
-
$ npm run start:prod
|
46 |
-
```
|
47 |
-
|
48 |
-
## Run tests
|
49 |
-
|
50 |
-
```bash
|
51 |
-
# unit tests
|
52 |
-
$ npm run test
|
53 |
-
|
54 |
-
# e2e tests
|
55 |
-
$ npm run test:e2e
|
56 |
-
|
57 |
-
# test coverage
|
58 |
-
$ npm run test:cov
|
59 |
-
```
|
60 |
-
|
61 |
-
## Resources
|
62 |
-
|
63 |
-
Check out a few resources that may come in handy when working with NestJS:
|
64 |
-
|
65 |
-
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
|
66 |
-
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
|
67 |
-
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
|
68 |
-
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
|
69 |
-
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
|
70 |
-
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
|
71 |
-
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
|
72 |
-
|
73 |
-
## Support
|
74 |
-
|
75 |
-
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
76 |
-
|
77 |
-
## Stay in touch
|
78 |
-
|
79 |
-
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
80 |
-
- Website - [https://nestjs.com](https://nestjs.com/)
|
81 |
-
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
82 |
-
|
83 |
-
## License
|
84 |
-
|
85 |
-
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
|
|
|
1 |
+
<p align="center">
|
2 |
+
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
3 |
+
</p>
|
4 |
+
|
5 |
+
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
6 |
+
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
7 |
+
|
8 |
+
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
9 |
+
<p align="center">
|
10 |
+
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
11 |
+
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
12 |
+
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
13 |
+
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
14 |
+
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
15 |
+
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
16 |
+
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
17 |
+
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
18 |
+
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
|
19 |
+
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
20 |
+
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
|
21 |
+
</p>
|
22 |
+
<!--[](https://opencollective.com/nest#backer)
|
23 |
+
[](https://opencollective.com/nest#sponsor)-->
|
24 |
+
|
25 |
+
## Description
|
26 |
+
|
27 |
+
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
28 |
+
|
29 |
+
## Project setup
|
30 |
+
|
31 |
+
```bash
|
32 |
+
$ npm install
|
33 |
+
```
|
34 |
+
|
35 |
+
## Compile and run the project
|
36 |
+
|
37 |
+
```bash
|
38 |
+
# development
|
39 |
+
$ npm run start
|
40 |
+
|
41 |
+
# watch mode
|
42 |
+
$ npm run start:dev
|
43 |
+
|
44 |
+
# production mode
|
45 |
+
$ npm run start:prod
|
46 |
+
```
|
47 |
+
|
48 |
+
## Run tests
|
49 |
+
|
50 |
+
```bash
|
51 |
+
# unit tests
|
52 |
+
$ npm run test
|
53 |
+
|
54 |
+
# e2e tests
|
55 |
+
$ npm run test:e2e
|
56 |
+
|
57 |
+
# test coverage
|
58 |
+
$ npm run test:cov
|
59 |
+
```
|
60 |
+
|
61 |
+
## Resources
|
62 |
+
|
63 |
+
Check out a few resources that may come in handy when working with NestJS:
|
64 |
+
|
65 |
+
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
|
66 |
+
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
|
67 |
+
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
|
68 |
+
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
|
69 |
+
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
|
70 |
+
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
|
71 |
+
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
|
72 |
+
|
73 |
+
## Support
|
74 |
+
|
75 |
+
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
76 |
+
|
77 |
+
## Stay in touch
|
78 |
+
|
79 |
+
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
80 |
+
- Website - [https://nestjs.com](https://nestjs.com/)
|
81 |
+
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
82 |
+
|
83 |
+
## License
|
84 |
+
|
85 |
+
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
|
nest-cli.json
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
-
{
|
2 |
-
"$schema": "https://json.schemastore.org/nest-cli",
|
3 |
-
"collection": "@nestjs/schematics",
|
4 |
-
"sourceRoot": "src",
|
5 |
-
"compilerOptions": {
|
6 |
-
"deleteOutDir": true
|
7 |
-
}
|
8 |
-
}
|
|
|
1 |
+
{
|
2 |
+
"$schema": "https://json.schemastore.org/nest-cli",
|
3 |
+
"collection": "@nestjs/schematics",
|
4 |
+
"sourceRoot": "src",
|
5 |
+
"compilerOptions": {
|
6 |
+
"deleteOutDir": true
|
7 |
+
}
|
8 |
+
}
|
package-lock.json
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
package.json
CHANGED
@@ -1,83 +1,83 @@
|
|
1 |
-
{
|
2 |
-
"name": "property",
|
3 |
-
"version": "0.0.1",
|
4 |
-
"description": "",
|
5 |
-
"author": "",
|
6 |
-
"private": true,
|
7 |
-
"license": "UNLICENSED",
|
8 |
-
"scripts": {
|
9 |
-
"build": "nest build",
|
10 |
-
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
11 |
-
"start": "nest start",
|
12 |
-
"start:dev": "nest start --watch",
|
13 |
-
"start:debug": "nest start --debug --watch",
|
14 |
-
"start:prod": "node dist/main
|
15 |
-
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
16 |
-
"test": "jest",
|
17 |
-
"test:watch": "jest --watch",
|
18 |
-
"test:cov": "jest --coverage",
|
19 |
-
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
20 |
-
"test:e2e": "jest --config ./test/jest-e2e.json"
|
21 |
-
},
|
22 |
-
"dependencies": {
|
23 |
-
"@nestjs/common": "^10.0.0",
|
24 |
-
"@nestjs/core": "^10.0.0",
|
25 |
-
"@nestjs/jwt": "^10.2.0",
|
26 |
-
"@nestjs/passport": "^10.0.3",
|
27 |
-
"@nestjs/platform-express": "^10.0.0",
|
28 |
-
"bcrypt": "^5.1.1",
|
29 |
-
"body-parser": "^1.20.2",
|
30 |
-
"class-transformer": "^0.5.1",
|
31 |
-
"class-validator": "^0.14.1",
|
32 |
-
"cors": "^2.8.5",
|
33 |
-
"i18n-ts": "^1.0.5",
|
34 |
-
"jwt-decode": "^4.0.0",
|
35 |
-
"mongoose": "^8.6.0",
|
36 |
-
"passport-custom": "^1.1.1",
|
37 |
-
"passport-jwt": "^4.0.1",
|
38 |
-
"passport-local": "^1.0.0",
|
39 |
-
"reflect-metadata": "^0.2.0",
|
40 |
-
"rxjs": "^7.8.1",
|
41 |
-
"uuid": "^10.0.0"
|
42 |
-
},
|
43 |
-
"devDependencies": {
|
44 |
-
"@nestjs/cli": "^10.0.0",
|
45 |
-
"@nestjs/schematics": "^10.0.0",
|
46 |
-
"@nestjs/testing": "^10.0.0",
|
47 |
-
"@types/express": "^4.17.17",
|
48 |
-
"@types/jest": "^29.5.2",
|
49 |
-
"@types/node": "^20.3.1",
|
50 |
-
"@types/supertest": "^6.0.0",
|
51 |
-
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
52 |
-
"@typescript-eslint/parser": "^8.0.0",
|
53 |
-
"eslint": "^8.42.0",
|
54 |
-
"eslint-config-prettier": "^9.0.0",
|
55 |
-
"eslint-plugin-prettier": "^5.0.0",
|
56 |
-
"jest": "^29.5.0",
|
57 |
-
"prettier": "^3.0.0",
|
58 |
-
"source-map-support": "^0.5.21",
|
59 |
-
"supertest": "^7.0.0",
|
60 |
-
"ts-jest": "^29.1.0",
|
61 |
-
"ts-loader": "^9.4.3",
|
62 |
-
"ts-node": "^10.9.1",
|
63 |
-
"tsconfig-paths": "^4.2.0",
|
64 |
-
"typescript": "^5.1.3"
|
65 |
-
},
|
66 |
-
"jest": {
|
67 |
-
"moduleFileExtensions": [
|
68 |
-
"js",
|
69 |
-
"json",
|
70 |
-
"ts"
|
71 |
-
],
|
72 |
-
"rootDir": "src",
|
73 |
-
"testRegex": ".*\\.spec\\.ts$",
|
74 |
-
"transform": {
|
75 |
-
"^.+\\.(t|j)s$": "ts-jest"
|
76 |
-
},
|
77 |
-
"collectCoverageFrom": [
|
78 |
-
"**/*.(t|j)s"
|
79 |
-
],
|
80 |
-
"coverageDirectory": "../coverage",
|
81 |
-
"testEnvironment": "node"
|
82 |
-
}
|
83 |
-
}
|
|
|
1 |
+
{
|
2 |
+
"name": "property",
|
3 |
+
"version": "0.0.1",
|
4 |
+
"description": "",
|
5 |
+
"author": "",
|
6 |
+
"private": true,
|
7 |
+
"license": "UNLICENSED",
|
8 |
+
"scripts": {
|
9 |
+
"build": "nest build",
|
10 |
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
11 |
+
"start": "nest start",
|
12 |
+
"start:dev": "nest start --watch",
|
13 |
+
"start:debug": "nest start --debug --watch",
|
14 |
+
"start:prod": "node dist/main",
|
15 |
+
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
16 |
+
"test": "jest",
|
17 |
+
"test:watch": "jest --watch",
|
18 |
+
"test:cov": "jest --coverage",
|
19 |
+
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
20 |
+
"test:e2e": "jest --config ./test/jest-e2e.json"
|
21 |
+
},
|
22 |
+
"dependencies": {
|
23 |
+
"@nestjs/common": "^10.0.0",
|
24 |
+
"@nestjs/core": "^10.0.0",
|
25 |
+
"@nestjs/jwt": "^10.2.0",
|
26 |
+
"@nestjs/passport": "^10.0.3",
|
27 |
+
"@nestjs/platform-express": "^10.0.0",
|
28 |
+
"bcrypt": "^5.1.1",
|
29 |
+
"body-parser": "^1.20.2",
|
30 |
+
"class-transformer": "^0.5.1",
|
31 |
+
"class-validator": "^0.14.1",
|
32 |
+
"cors": "^2.8.5",
|
33 |
+
"i18n-ts": "^1.0.5",
|
34 |
+
"jwt-decode": "^4.0.0",
|
35 |
+
"mongoose": "^8.6.0",
|
36 |
+
"passport-custom": "^1.1.1",
|
37 |
+
"passport-jwt": "^4.0.1",
|
38 |
+
"passport-local": "^1.0.0",
|
39 |
+
"reflect-metadata": "^0.2.0",
|
40 |
+
"rxjs": "^7.8.1",
|
41 |
+
"uuid": "^10.0.0"
|
42 |
+
},
|
43 |
+
"devDependencies": {
|
44 |
+
"@nestjs/cli": "^10.0.0",
|
45 |
+
"@nestjs/schematics": "^10.0.0",
|
46 |
+
"@nestjs/testing": "^10.0.0",
|
47 |
+
"@types/express": "^4.17.17",
|
48 |
+
"@types/jest": "^29.5.2",
|
49 |
+
"@types/node": "^20.3.1",
|
50 |
+
"@types/supertest": "^6.0.0",
|
51 |
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
52 |
+
"@typescript-eslint/parser": "^8.0.0",
|
53 |
+
"eslint": "^8.42.0",
|
54 |
+
"eslint-config-prettier": "^9.0.0",
|
55 |
+
"eslint-plugin-prettier": "^5.0.0",
|
56 |
+
"jest": "^29.5.0",
|
57 |
+
"prettier": "^3.0.0",
|
58 |
+
"source-map-support": "^0.5.21",
|
59 |
+
"supertest": "^7.0.0",
|
60 |
+
"ts-jest": "^29.1.0",
|
61 |
+
"ts-loader": "^9.4.3",
|
62 |
+
"ts-node": "^10.9.1",
|
63 |
+
"tsconfig-paths": "^4.2.0",
|
64 |
+
"typescript": "^5.1.3"
|
65 |
+
},
|
66 |
+
"jest": {
|
67 |
+
"moduleFileExtensions": [
|
68 |
+
"js",
|
69 |
+
"json",
|
70 |
+
"ts"
|
71 |
+
],
|
72 |
+
"rootDir": "src",
|
73 |
+
"testRegex": ".*\\.spec\\.ts$",
|
74 |
+
"transform": {
|
75 |
+
"^.+\\.(t|j)s$": "ts-jest"
|
76 |
+
},
|
77 |
+
"collectCoverageFrom": [
|
78 |
+
"**/*.(t|j)s"
|
79 |
+
],
|
80 |
+
"coverageDirectory": "../coverage",
|
81 |
+
"testEnvironment": "node"
|
82 |
+
}
|
83 |
+
}
|
src/app.controller.spec.ts
CHANGED
@@ -1,22 +1,22 @@
|
|
1 |
-
import { Test, TestingModule } from '@nestjs/testing';
|
2 |
-
import { AppController } from './app.controller';
|
3 |
-
import { AppService } from './app.service';
|
4 |
-
|
5 |
-
describe('AppController', () => {
|
6 |
-
let appController: AppController;
|
7 |
-
|
8 |
-
beforeEach(async () => {
|
9 |
-
const app: TestingModule = await Test.createTestingModule({
|
10 |
-
controllers: [AppController],
|
11 |
-
providers: [AppService],
|
12 |
-
}).compile();
|
13 |
-
|
14 |
-
appController = app.get<AppController>(AppController);
|
15 |
-
});
|
16 |
-
|
17 |
-
describe('root', () => {
|
18 |
-
it('should return "Hello World!"', () => {
|
19 |
-
expect(appController.getHello()).toBe('Hello World!');
|
20 |
-
});
|
21 |
-
});
|
22 |
-
});
|
|
|
1 |
+
import { Test, TestingModule } from '@nestjs/testing';
|
2 |
+
import { AppController } from './app.controller';
|
3 |
+
import { AppService } from './app.service';
|
4 |
+
|
5 |
+
describe('AppController', () => {
|
6 |
+
let appController: AppController;
|
7 |
+
|
8 |
+
beforeEach(async () => {
|
9 |
+
const app: TestingModule = await Test.createTestingModule({
|
10 |
+
controllers: [AppController],
|
11 |
+
providers: [AppService],
|
12 |
+
}).compile();
|
13 |
+
|
14 |
+
appController = app.get<AppController>(AppController);
|
15 |
+
});
|
16 |
+
|
17 |
+
describe('root', () => {
|
18 |
+
it('should return "Hello World!"', () => {
|
19 |
+
expect(appController.getHello()).toBe('Hello World!');
|
20 |
+
});
|
21 |
+
});
|
22 |
+
});
|
src/app.controller.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
import { Controller, Get } from '@nestjs/common';
|
2 |
-
import { AppService } from './app.service';
|
3 |
-
|
4 |
-
@Controller()
|
5 |
-
export class AppController {
|
6 |
-
constructor(private readonly appService: AppService) {}
|
7 |
-
|
8 |
-
@Get()
|
9 |
-
getHello(): string {
|
10 |
-
return this.appService.getHello();
|
11 |
-
}
|
12 |
-
}
|
|
|
1 |
+
import { Controller, Get } from '@nestjs/common';
|
2 |
+
import { AppService } from './app.service';
|
3 |
+
|
4 |
+
@Controller()
|
5 |
+
export class AppController {
|
6 |
+
constructor(private readonly appService: AppService) {}
|
7 |
+
|
8 |
+
@Get()
|
9 |
+
getHello(): string {
|
10 |
+
return this.appService.getHello();
|
11 |
+
}
|
12 |
+
}
|
src/app.module.ts
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
-
import { Module } from '@nestjs/common';
|
2 |
-
import { AppController } from './app.controller';
|
3 |
-
import { AppService } from './app.service';
|
4 |
-
import { PropertyModule } from './modules/property/property.module';
|
5 |
-
import { AuthModule } from './modules/auth/auth.module';
|
6 |
-
import { UserModule } from './modules/user/user.module';
|
7 |
-
import { ActivityModule } from './modules/activity/activity.module';
|
8 |
-
|
9 |
-
@Module({
|
10 |
-
imports: [PropertyModule, AuthModule, UserModule, ActivityModule],
|
11 |
-
controllers: [AppController],
|
12 |
-
providers: [AppService],
|
13 |
-
})
|
14 |
-
export class AppModule {}
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { AppController } from './app.controller';
|
3 |
+
import { AppService } from './app.service';
|
4 |
+
import { PropertyModule } from './modules/property/property.module';
|
5 |
+
import { AuthModule } from './modules/auth/auth.module';
|
6 |
+
import { UserModule } from './modules/user/user.module';
|
7 |
+
import { ActivityModule } from './modules/activity/activity.module';
|
8 |
+
|
9 |
+
@Module({
|
10 |
+
imports: [PropertyModule, AuthModule, UserModule, ActivityModule],
|
11 |
+
controllers: [AppController],
|
12 |
+
providers: [AppService],
|
13 |
+
})
|
14 |
+
export class AppModule {}
|
src/app.service.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
-
import { Injectable } from '@nestjs/common';
|
2 |
-
|
3 |
-
@Injectable()
|
4 |
-
export class AppService {
|
5 |
-
getHello(): string {
|
6 |
-
return 'Hello World!';
|
7 |
-
}
|
8 |
-
}
|
|
|
1 |
+
import { Injectable } from '@nestjs/common';
|
2 |
+
|
3 |
+
@Injectable()
|
4 |
+
export class AppService {
|
5 |
+
getHello(): string {
|
6 |
+
return 'Hello World!';
|
7 |
+
}
|
8 |
+
}
|
src/constants/collections.constant.ts
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
-
export const PROPERTIE = 'propertie';
|
2 |
-
export const USERS = 'users';
|
3 |
-
export const ACTIVITY = 'activity';
|
|
|
1 |
+
export const PROPERTIE = 'propertie';
|
2 |
+
export const USERS = 'users';
|
3 |
+
export const ACTIVITY = 'activity';
|
src/constants/index.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1 |
-
export * from './collections.constant'
|
2 |
export * from './repository.constant'
|
|
|
1 |
+
export * from './collections.constant'
|
2 |
export * from './repository.constant'
|
src/constants/jwt.constant.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
export const jwtConstants = {
|
2 |
-
secret: "secretKey",
|
3 |
-
salt: 10
|
4 |
};
|
|
|
1 |
+
export const jwtConstants = {
|
2 |
+
secret: "secretKey",
|
3 |
+
salt: 10
|
4 |
};
|
src/constants/repository.constant.ts
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
-
export const PROPERTY_REPOSITORY = 'PROPERTY_REPOSITORY';
|
2 |
-
export const USER_REPOSITORY = 'USER_REPOSITORY';
|
3 |
-
export const ACTIVITY_REPOSITORY = 'ACTIVITY_REPOSITORY';
|
|
|
1 |
+
export const PROPERTY_REPOSITORY = 'PROPERTY_REPOSITORY';
|
2 |
+
export const USER_REPOSITORY = 'USER_REPOSITORY';
|
3 |
+
export const ACTIVITY_REPOSITORY = 'ACTIVITY_REPOSITORY';
|
src/locales/en.ts
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
-
export const en = {
|
2 |
-
Success: 'Operation is completed Success',
|
3 |
-
Error: 'Something Went Wrong',
|
4 |
-
adminVerificationFaild: 'Admin key verification failed',
|
5 |
-
friendAdded: 'Friend added Successfully.',
|
6 |
-
reqRejected: 'Friend request declined.',
|
7 |
-
friendReqSent: 'Friend request sent Successfully.',
|
8 |
-
userAlreadyExist: 'User already exist.',
|
9 |
-
};
|
|
|
1 |
+
export const en = {
|
2 |
+
Success: 'Operation is completed Success',
|
3 |
+
Error: 'Something Went Wrong',
|
4 |
+
adminVerificationFaild: 'Admin key verification failed',
|
5 |
+
friendAdded: 'Friend added Successfully.',
|
6 |
+
reqRejected: 'Friend request declined.',
|
7 |
+
friendReqSent: 'Friend request sent Successfully.',
|
8 |
+
userAlreadyExist: 'User already exist.',
|
9 |
+
};
|
src/main.ts
CHANGED
@@ -1,24 +1,24 @@
|
|
1 |
-
import { NestFactory } from '@nestjs/core';
|
2 |
-
import { AppModule } from './app.module';
|
3 |
-
import * as bodyParser from 'body-parser';
|
4 |
-
import { ValidationPipe } from '@nestjs/common';
|
5 |
-
|
6 |
-
async function bootstrap() {
|
7 |
-
const app = await NestFactory.create(AppModule);
|
8 |
-
|
9 |
-
app.useGlobalPipes(new ValidationPipe());
|
10 |
-
|
11 |
-
// Enable CORS for all origins and all methods
|
12 |
-
app.enableCors({
|
13 |
-
origin: '*', // Allow all origins
|
14 |
-
// origin: ['https://property-fe-h8e2.vercel.app', 'http://localhost:3000'], // Replace with your frontend domain or use '*'
|
15 |
-
methods: '*', // Allow all methods
|
16 |
-
credentials: true,
|
17 |
-
});
|
18 |
-
|
19 |
-
app.use(bodyParser.urlencoded({ extended: true }));
|
20 |
-
app.use(bodyParser.json());
|
21 |
-
|
22 |
-
await app.listen(8080);
|
23 |
-
}
|
24 |
-
bootstrap();
|
|
|
1 |
+
import { NestFactory } from '@nestjs/core';
|
2 |
+
import { AppModule } from './app.module';
|
3 |
+
import * as bodyParser from 'body-parser';
|
4 |
+
import { ValidationPipe } from '@nestjs/common';
|
5 |
+
|
6 |
+
async function bootstrap() {
|
7 |
+
const app = await NestFactory.create(AppModule);
|
8 |
+
|
9 |
+
app.useGlobalPipes(new ValidationPipe());
|
10 |
+
|
11 |
+
// Enable CORS for all origins and all methods
|
12 |
+
app.enableCors({
|
13 |
+
origin: '*', // Allow all origins
|
14 |
+
// origin: ['https://property-fe-h8e2.vercel.app', 'http://localhost:3000'], // Replace with your frontend domain or use '*'
|
15 |
+
methods: '*', // Allow all methods
|
16 |
+
credentials: true,
|
17 |
+
});
|
18 |
+
|
19 |
+
app.use(bodyParser.urlencoded({ extended: true }));
|
20 |
+
app.use(bodyParser.json());
|
21 |
+
|
22 |
+
await app.listen(8080);
|
23 |
+
}
|
24 |
+
bootstrap();
|
src/modules/activity/activity.controller.ts
CHANGED
@@ -1,145 +1,145 @@
|
|
1 |
-
import {
|
2 |
-
Body,
|
3 |
-
Controller,
|
4 |
-
Get,
|
5 |
-
HttpStatus,
|
6 |
-
Patch,
|
7 |
-
Post,
|
8 |
-
Query,
|
9 |
-
Req,
|
10 |
-
Res,
|
11 |
-
UseGuards,
|
12 |
-
} from '@nestjs/common';
|
13 |
-
import { Response } from 'express';
|
14 |
-
import { CommonServices } from '../shared/common.service';
|
15 |
-
import { ActivityService } from './activity.service';
|
16 |
-
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
17 |
-
import { PropertyService } from '../property/property.service';
|
18 |
-
|
19 |
-
@Controller('activity')
|
20 |
-
export class ActivityController extends CommonServices {
|
21 |
-
constructor(
|
22 |
-
private readonly activityService: ActivityService,
|
23 |
-
private readonly propertyService: PropertyService,
|
24 |
-
) {
|
25 |
-
super();
|
26 |
-
}
|
27 |
-
|
28 |
-
@Post('create')
|
29 |
-
// @UseGuards(JwtAuthGuard)
|
30 |
-
async createActivity(@Body() body: any, @Res() res: Response, @Req() req) {
|
31 |
-
try {
|
32 |
-
// const payload = {
|
33 |
-
// ...body,
|
34 |
-
// userId: req.user.userId,
|
35 |
-
// };
|
36 |
-
const activity = await this.activityService.sharedCreate(body);
|
37 |
-
|
38 |
-
// update views
|
39 |
-
if (body.action == 'view')
|
40 |
-
await this.propertyService.sharedFindOneAndUpdate(
|
41 |
-
{ _id: body.propertyId },
|
42 |
-
{
|
43 |
-
$inc: { views: 1 }, // Increment views and add duration
|
44 |
-
},
|
45 |
-
{},
|
46 |
-
);
|
47 |
-
|
48 |
-
//update duration/ time spend
|
49 |
-
if (body.action == 'time_spent')
|
50 |
-
await this.propertyService.sharedFindOneAndUpdate(
|
51 |
-
{ _id: body.propertyId },
|
52 |
-
{
|
53 |
-
$inc: { total_time_spent: body.duration || 0 }, // Increment views and add duration
|
54 |
-
},
|
55 |
-
{},
|
56 |
-
);
|
57 |
-
|
58 |
-
return this.sendResponse(
|
59 |
-
this.messages.Success,
|
60 |
-
activity,
|
61 |
-
HttpStatus.OK,
|
62 |
-
res,
|
63 |
-
);
|
64 |
-
} catch (error) {
|
65 |
-
console.log(error);
|
66 |
-
return this.sendResponse(
|
67 |
-
'Error',
|
68 |
-
{},
|
69 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
70 |
-
res,
|
71 |
-
);
|
72 |
-
}
|
73 |
-
}
|
74 |
-
|
75 |
-
@Get('')
|
76 |
-
async getActivityListings(@Res() res: Response, @Req() req): Promise<any> {
|
77 |
-
try {
|
78 |
-
const response = await this.activityService.sharedFind({});
|
79 |
-
return this.sendResponse(
|
80 |
-
this.messages.Success,
|
81 |
-
response,
|
82 |
-
HttpStatus.OK,
|
83 |
-
res,
|
84 |
-
);
|
85 |
-
} catch (error) {
|
86 |
-
return this.sendResponse(
|
87 |
-
'Internal server Error',
|
88 |
-
{},
|
89 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
90 |
-
res,
|
91 |
-
);
|
92 |
-
}
|
93 |
-
}
|
94 |
-
|
95 |
-
// New Update API to modify existing activity
|
96 |
-
@Patch('update')
|
97 |
-
@UseGuards(JwtAuthGuard)
|
98 |
-
async updateActivity(
|
99 |
-
@Body() body: any, // should include { userId, propertyId, action, duration (optional) }
|
100 |
-
@Res() res: Response,
|
101 |
-
@Req() req,
|
102 |
-
) {
|
103 |
-
try {
|
104 |
-
// Check if the activity exists for the user and property
|
105 |
-
const existingActivity = await this.activityService.sharedFindOne({
|
106 |
-
userId: body.userId,
|
107 |
-
propertyId: body.propertyId,
|
108 |
-
action: body.action, // Example: 'click' or 'view'
|
109 |
-
});
|
110 |
-
|
111 |
-
if (existingActivity) {
|
112 |
-
// If activity exists, update it
|
113 |
-
const updatedActivity = await this.activityService.sharedUpdate(
|
114 |
-
{ _id: existingActivity._id },
|
115 |
-
{
|
116 |
-
duration: body.duration ? body.duration : existingActivity.duration,
|
117 |
-
timestamp: new Date(), // update the timestamp to the latest interaction
|
118 |
-
},
|
119 |
-
);
|
120 |
-
|
121 |
-
return this.sendResponse(
|
122 |
-
this.messages.Success,
|
123 |
-
updatedActivity,
|
124 |
-
HttpStatus.OK,
|
125 |
-
res,
|
126 |
-
);
|
127 |
-
} else {
|
128 |
-
// If no activity exists, return a not found response
|
129 |
-
return this.sendResponse(
|
130 |
-
'Activity not found',
|
131 |
-
{},
|
132 |
-
HttpStatus.NOT_FOUND,
|
133 |
-
res,
|
134 |
-
);
|
135 |
-
}
|
136 |
-
} catch (error) {
|
137 |
-
return this.sendResponse(
|
138 |
-
'Internal server Error',
|
139 |
-
{},
|
140 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
141 |
-
res,
|
142 |
-
);
|
143 |
-
}
|
144 |
-
}
|
145 |
-
}
|
|
|
1 |
+
import {
|
2 |
+
Body,
|
3 |
+
Controller,
|
4 |
+
Get,
|
5 |
+
HttpStatus,
|
6 |
+
Patch,
|
7 |
+
Post,
|
8 |
+
Query,
|
9 |
+
Req,
|
10 |
+
Res,
|
11 |
+
UseGuards,
|
12 |
+
} from '@nestjs/common';
|
13 |
+
import { Response } from 'express';
|
14 |
+
import { CommonServices } from '../shared/common.service';
|
15 |
+
import { ActivityService } from './activity.service';
|
16 |
+
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
17 |
+
import { PropertyService } from '../property/property.service';
|
18 |
+
|
19 |
+
@Controller('activity')
|
20 |
+
export class ActivityController extends CommonServices {
|
21 |
+
constructor(
|
22 |
+
private readonly activityService: ActivityService,
|
23 |
+
private readonly propertyService: PropertyService,
|
24 |
+
) {
|
25 |
+
super();
|
26 |
+
}
|
27 |
+
|
28 |
+
@Post('create')
|
29 |
+
// @UseGuards(JwtAuthGuard)
|
30 |
+
async createActivity(@Body() body: any, @Res() res: Response, @Req() req) {
|
31 |
+
try {
|
32 |
+
// const payload = {
|
33 |
+
// ...body,
|
34 |
+
// userId: req.user.userId,
|
35 |
+
// };
|
36 |
+
const activity = await this.activityService.sharedCreate(body);
|
37 |
+
|
38 |
+
// update views
|
39 |
+
if (body.action == 'view')
|
40 |
+
await this.propertyService.sharedFindOneAndUpdate(
|
41 |
+
{ _id: body.propertyId },
|
42 |
+
{
|
43 |
+
$inc: { views: 1 }, // Increment views and add duration
|
44 |
+
},
|
45 |
+
{},
|
46 |
+
);
|
47 |
+
|
48 |
+
//update duration/ time spend
|
49 |
+
if (body.action == 'time_spent')
|
50 |
+
await this.propertyService.sharedFindOneAndUpdate(
|
51 |
+
{ _id: body.propertyId },
|
52 |
+
{
|
53 |
+
$inc: { total_time_spent: body.duration || 0 }, // Increment views and add duration
|
54 |
+
},
|
55 |
+
{},
|
56 |
+
);
|
57 |
+
|
58 |
+
return this.sendResponse(
|
59 |
+
this.messages.Success,
|
60 |
+
activity,
|
61 |
+
HttpStatus.OK,
|
62 |
+
res,
|
63 |
+
);
|
64 |
+
} catch (error) {
|
65 |
+
console.log(error);
|
66 |
+
return this.sendResponse(
|
67 |
+
'Error',
|
68 |
+
{},
|
69 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
70 |
+
res,
|
71 |
+
);
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
@Get('')
|
76 |
+
async getActivityListings(@Res() res: Response, @Req() req): Promise<any> {
|
77 |
+
try {
|
78 |
+
const response = await this.activityService.sharedFind({});
|
79 |
+
return this.sendResponse(
|
80 |
+
this.messages.Success,
|
81 |
+
response,
|
82 |
+
HttpStatus.OK,
|
83 |
+
res,
|
84 |
+
);
|
85 |
+
} catch (error) {
|
86 |
+
return this.sendResponse(
|
87 |
+
'Internal server Error',
|
88 |
+
{},
|
89 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
90 |
+
res,
|
91 |
+
);
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
// New Update API to modify existing activity
|
96 |
+
@Patch('update')
|
97 |
+
@UseGuards(JwtAuthGuard)
|
98 |
+
async updateActivity(
|
99 |
+
@Body() body: any, // should include { userId, propertyId, action, duration (optional) }
|
100 |
+
@Res() res: Response,
|
101 |
+
@Req() req,
|
102 |
+
) {
|
103 |
+
try {
|
104 |
+
// Check if the activity exists for the user and property
|
105 |
+
const existingActivity = await this.activityService.sharedFindOne({
|
106 |
+
userId: body.userId,
|
107 |
+
propertyId: body.propertyId,
|
108 |
+
action: body.action, // Example: 'click' or 'view'
|
109 |
+
});
|
110 |
+
|
111 |
+
if (existingActivity) {
|
112 |
+
// If activity exists, update it
|
113 |
+
const updatedActivity = await this.activityService.sharedUpdate(
|
114 |
+
{ _id: existingActivity._id },
|
115 |
+
{
|
116 |
+
duration: body.duration ? body.duration : existingActivity.duration,
|
117 |
+
timestamp: new Date(), // update the timestamp to the latest interaction
|
118 |
+
},
|
119 |
+
);
|
120 |
+
|
121 |
+
return this.sendResponse(
|
122 |
+
this.messages.Success,
|
123 |
+
updatedActivity,
|
124 |
+
HttpStatus.OK,
|
125 |
+
res,
|
126 |
+
);
|
127 |
+
} else {
|
128 |
+
// If no activity exists, return a not found response
|
129 |
+
return this.sendResponse(
|
130 |
+
'Activity not found',
|
131 |
+
{},
|
132 |
+
HttpStatus.NOT_FOUND,
|
133 |
+
res,
|
134 |
+
);
|
135 |
+
}
|
136 |
+
} catch (error) {
|
137 |
+
return this.sendResponse(
|
138 |
+
'Internal server Error',
|
139 |
+
{},
|
140 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
141 |
+
res,
|
142 |
+
);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
}
|
src/modules/activity/activity.module.ts
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
-
import { Module } from '@nestjs/common';
|
2 |
-
import { DatabaseModule } from '../database/database.module';
|
3 |
-
import { ActivityController } from './activity.controller';
|
4 |
-
import { ActivityService } from './activity.service';
|
5 |
-
import { PropertyService } from '../property/property.service';
|
6 |
-
|
7 |
-
@Module({
|
8 |
-
imports: [DatabaseModule],
|
9 |
-
controllers: [ActivityController],
|
10 |
-
providers: [ActivityService, PropertyService],
|
11 |
-
exports: [ActivityService, PropertyService],
|
12 |
-
})
|
13 |
-
export class ActivityModule {}
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { DatabaseModule } from '../database/database.module';
|
3 |
+
import { ActivityController } from './activity.controller';
|
4 |
+
import { ActivityService } from './activity.service';
|
5 |
+
import { PropertyService } from '../property/property.service';
|
6 |
+
|
7 |
+
@Module({
|
8 |
+
imports: [DatabaseModule],
|
9 |
+
controllers: [ActivityController],
|
10 |
+
providers: [ActivityService, PropertyService],
|
11 |
+
exports: [ActivityService, PropertyService],
|
12 |
+
})
|
13 |
+
export class ActivityModule {}
|
src/modules/activity/activity.provider.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
import { Connection } from 'mongoose';
|
2 |
-
import { ACTIVITY, ACTIVITY_REPOSITORY } from 'src/constants';
|
3 |
-
import { UserActivitySchema } from './activity.schema';
|
4 |
-
|
5 |
-
export const activityProviders = [
|
6 |
-
{
|
7 |
-
provide: ACTIVITY_REPOSITORY,
|
8 |
-
useFactory: (connection: Connection) =>
|
9 |
-
connection.model(ACTIVITY, UserActivitySchema),
|
10 |
-
inject: ['DATABASE_CONNECTION'],
|
11 |
-
},
|
12 |
-
];
|
|
|
1 |
+
import { Connection } from 'mongoose';
|
2 |
+
import { ACTIVITY, ACTIVITY_REPOSITORY } from 'src/constants';
|
3 |
+
import { UserActivitySchema } from './activity.schema';
|
4 |
+
|
5 |
+
export const activityProviders = [
|
6 |
+
{
|
7 |
+
provide: ACTIVITY_REPOSITORY,
|
8 |
+
useFactory: (connection: Connection) =>
|
9 |
+
connection.model(ACTIVITY, UserActivitySchema),
|
10 |
+
inject: ['DATABASE_CONNECTION'],
|
11 |
+
},
|
12 |
+
];
|
src/modules/activity/activity.schema.ts
CHANGED
@@ -1,41 +1,41 @@
|
|
1 |
-
import * as mongoose from 'mongoose';
|
2 |
-
import { Document } from 'mongoose';
|
3 |
-
import { IUserDocument } from '../user/user.schema';
|
4 |
-
import { IPropertyDocument } from '../property/property.schema';
|
5 |
-
|
6 |
-
export interface IUserActivityDocument extends Document {
|
7 |
-
// userId: IUserDocument;
|
8 |
-
propertyId: IPropertyDocument;
|
9 |
-
action: string; // E.g., 'click', 'view', 'time_spent'
|
10 |
-
sessionId: string;
|
11 |
-
id: string;
|
12 |
-
timestamp: Date;
|
13 |
-
duration?: number; // Store time spent in seconds for 'time_spent' action
|
14 |
-
searchQuery?: string;
|
15 |
-
}
|
16 |
-
|
17 |
-
const UserActivitySchema = new mongoose.Schema<IUserActivityDocument>(
|
18 |
-
{
|
19 |
-
// userId: {
|
20 |
-
// type: mongoose.Schema.Types.ObjectId,
|
21 |
-
// ref: 'User',
|
22 |
-
// required: true,
|
23 |
-
// },
|
24 |
-
propertyId: {
|
25 |
-
type: mongoose.Schema.Types.ObjectId,
|
26 |
-
ref: 'propertie',
|
27 |
-
// required: true,
|
28 |
-
},
|
29 |
-
action: { type: String, required: true }, // 'click', 'view', 'time_spent'
|
30 |
-
sessionId: { type: String, required: true },
|
31 |
-
id: { type: String },
|
32 |
-
searchQuery: { type: String },
|
33 |
-
timestamp: { type: Date, default: Date.now },
|
34 |
-
duration: { type: Number }, // Only applicable for 'time_spent'
|
35 |
-
},
|
36 |
-
{
|
37 |
-
toJSON: { versionKey: false },
|
38 |
-
},
|
39 |
-
);
|
40 |
-
|
41 |
-
export { UserActivitySchema };
|
|
|
1 |
+
import * as mongoose from 'mongoose';
|
2 |
+
import { Document } from 'mongoose';
|
3 |
+
import { IUserDocument } from '../user/user.schema';
|
4 |
+
import { IPropertyDocument } from '../property/property.schema';
|
5 |
+
|
6 |
+
export interface IUserActivityDocument extends Document {
|
7 |
+
// userId: IUserDocument;
|
8 |
+
propertyId: IPropertyDocument;
|
9 |
+
action: string; // E.g., 'click', 'view', 'time_spent'
|
10 |
+
sessionId: string;
|
11 |
+
id: string;
|
12 |
+
timestamp: Date;
|
13 |
+
duration?: number; // Store time spent in seconds for 'time_spent' action
|
14 |
+
searchQuery?: string;
|
15 |
+
}
|
16 |
+
|
17 |
+
const UserActivitySchema = new mongoose.Schema<IUserActivityDocument>(
|
18 |
+
{
|
19 |
+
// userId: {
|
20 |
+
// type: mongoose.Schema.Types.ObjectId,
|
21 |
+
// ref: 'User',
|
22 |
+
// required: true,
|
23 |
+
// },
|
24 |
+
propertyId: {
|
25 |
+
type: mongoose.Schema.Types.ObjectId,
|
26 |
+
ref: 'propertie',
|
27 |
+
// required: true,
|
28 |
+
},
|
29 |
+
action: { type: String, required: true }, // 'click', 'view', 'time_spent'
|
30 |
+
sessionId: { type: String, required: true },
|
31 |
+
id: { type: String },
|
32 |
+
searchQuery: { type: String },
|
33 |
+
timestamp: { type: Date, default: Date.now },
|
34 |
+
duration: { type: Number }, // Only applicable for 'time_spent'
|
35 |
+
},
|
36 |
+
{
|
37 |
+
toJSON: { versionKey: false },
|
38 |
+
},
|
39 |
+
);
|
40 |
+
|
41 |
+
export { UserActivitySchema };
|
src/modules/activity/activity.service.ts
CHANGED
@@ -1,23 +1,23 @@
|
|
1 |
-
import { Inject, Injectable } from '@nestjs/common';
|
2 |
-
import { Model, Types } from 'mongoose';
|
3 |
-
import { sharedCrudService } from '../shared/sharedCrud.services';
|
4 |
-
import { ACTIVITY_REPOSITORY } from 'src/constants';
|
5 |
-
import { IUserActivityDocument } from './activity.schema';
|
6 |
-
|
7 |
-
@Injectable()
|
8 |
-
export class ActivityService extends sharedCrudService {
|
9 |
-
constructor(
|
10 |
-
@Inject(ACTIVITY_REPOSITORY)
|
11 |
-
readonly activityRepository: Model<IUserActivityDocument>,
|
12 |
-
) {
|
13 |
-
super(activityRepository);
|
14 |
-
}
|
15 |
-
|
16 |
-
async activityLisitng(
|
17 |
-
page: number,
|
18 |
-
resPerPage: number,
|
19 |
-
search: string,
|
20 |
-
): Promise<any> {
|
21 |
-
return 'Hello from Activity service.'
|
22 |
-
}
|
23 |
-
}
|
|
|
1 |
+
import { Inject, Injectable } from '@nestjs/common';
|
2 |
+
import { Model, Types } from 'mongoose';
|
3 |
+
import { sharedCrudService } from '../shared/sharedCrud.services';
|
4 |
+
import { ACTIVITY_REPOSITORY } from 'src/constants';
|
5 |
+
import { IUserActivityDocument } from './activity.schema';
|
6 |
+
|
7 |
+
@Injectable()
|
8 |
+
export class ActivityService extends sharedCrudService {
|
9 |
+
constructor(
|
10 |
+
@Inject(ACTIVITY_REPOSITORY)
|
11 |
+
readonly activityRepository: Model<IUserActivityDocument>,
|
12 |
+
) {
|
13 |
+
super(activityRepository);
|
14 |
+
}
|
15 |
+
|
16 |
+
async activityLisitng(
|
17 |
+
page: number,
|
18 |
+
resPerPage: number,
|
19 |
+
search: string,
|
20 |
+
): Promise<any> {
|
21 |
+
return 'Hello from Activity service.'
|
22 |
+
}
|
23 |
+
}
|
src/modules/auth/auth.controller.ts
CHANGED
@@ -1,113 +1,113 @@
|
|
1 |
-
import {
|
2 |
-
Body,
|
3 |
-
Controller,
|
4 |
-
Get,
|
5 |
-
HttpStatus,
|
6 |
-
Inject,
|
7 |
-
Param,
|
8 |
-
Post,
|
9 |
-
Put,
|
10 |
-
Query,
|
11 |
-
Req,
|
12 |
-
Res,
|
13 |
-
UploadedFile,
|
14 |
-
UseGuards,
|
15 |
-
UseInterceptors,
|
16 |
-
} from '@nestjs/common';
|
17 |
-
import { Response, Request } from 'express';
|
18 |
-
import { AuthService } from './auth.service';
|
19 |
-
import * as bcrypt from 'bcrypt';
|
20 |
-
import { LocalAuthGuard } from './local-auth.guard';
|
21 |
-
import { UserService } from '../user/user.service';
|
22 |
-
import { JwtAuthGuard } from './jwt-auth.guard';
|
23 |
-
import * as jwt from 'jsonwebtoken';
|
24 |
-
import { FileInterceptor } from '@nestjs/platform-express';
|
25 |
-
import { Model } from 'mongoose';
|
26 |
-
import { AuthGuard } from '@nestjs/passport';
|
27 |
-
import { JwtService } from '@nestjs/jwt';
|
28 |
-
import { CommonServices } from '../shared/common.service';
|
29 |
-
import { jwtConstants } from 'src/constants/jwt.constant';
|
30 |
-
|
31 |
-
@Controller('auth')
|
32 |
-
export class AuthController extends CommonServices {
|
33 |
-
constructor(
|
34 |
-
private readonly authService: AuthService,
|
35 |
-
private readonly userService: UserService,
|
36 |
-
) {
|
37 |
-
super();
|
38 |
-
}
|
39 |
-
|
40 |
-
@UseGuards(LocalAuthGuard)
|
41 |
-
@Post('/login')
|
42 |
-
async login(
|
43 |
-
@Req() req: any,
|
44 |
-
@Res() res: Response,
|
45 |
-
@Body() body: any,
|
46 |
-
): Promise<any> {
|
47 |
-
try {
|
48 |
-
const Userresp: any = await this.authService.login(req.user, body);
|
49 |
-
return this.sendResponse(
|
50 |
-
this.messages.Success,
|
51 |
-
Userresp,
|
52 |
-
HttpStatus.OK,
|
53 |
-
res,
|
54 |
-
);
|
55 |
-
} catch (error) {
|
56 |
-
console.log(`error =>`, error);
|
57 |
-
return this.sendResponse(
|
58 |
-
'Internal server Error',
|
59 |
-
{},
|
60 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
61 |
-
res,
|
62 |
-
);
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
@Post('/signup')
|
67 |
-
async signup(
|
68 |
-
@Req() req: any,
|
69 |
-
@Res() res: Response,
|
70 |
-
@Body() body: any,
|
71 |
-
): Promise<any> {
|
72 |
-
try {
|
73 |
-
const email = body.email.toLowerCase().trim();
|
74 |
-
|
75 |
-
const checkUser = await this.userService.sharedFindOne({ email: email });
|
76 |
-
|
77 |
-
if (checkUser && checkUser.email == email) {
|
78 |
-
return this.sendResponse(
|
79 |
-
this.messages.userAlreadyExist,
|
80 |
-
{},
|
81 |
-
HttpStatus.CONFLICT,
|
82 |
-
res,
|
83 |
-
);
|
84 |
-
}
|
85 |
-
|
86 |
-
const createUser = await this.userService.sharedCreate({
|
87 |
-
...body,
|
88 |
-
password: bcrypt.hashSync(body.password, jwtConstants.salt),
|
89 |
-
roles: ['user'],
|
90 |
-
});
|
91 |
-
|
92 |
-
const Userresp: any = await this.authService.login(createUser);
|
93 |
-
|
94 |
-
return this.sendResponse(
|
95 |
-
'Account created!',
|
96 |
-
{
|
97 |
-
...Userresp,
|
98 |
-
},
|
99 |
-
HttpStatus.OK,
|
100 |
-
res,
|
101 |
-
);
|
102 |
-
} catch (error) {
|
103 |
-
console.log(error);
|
104 |
-
|
105 |
-
return this.sendResponse(
|
106 |
-
'Internal server Error',
|
107 |
-
{},
|
108 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
109 |
-
res,
|
110 |
-
);
|
111 |
-
}
|
112 |
-
}
|
113 |
-
}
|
|
|
1 |
+
import {
|
2 |
+
Body,
|
3 |
+
Controller,
|
4 |
+
Get,
|
5 |
+
HttpStatus,
|
6 |
+
Inject,
|
7 |
+
Param,
|
8 |
+
Post,
|
9 |
+
Put,
|
10 |
+
Query,
|
11 |
+
Req,
|
12 |
+
Res,
|
13 |
+
UploadedFile,
|
14 |
+
UseGuards,
|
15 |
+
UseInterceptors,
|
16 |
+
} from '@nestjs/common';
|
17 |
+
import { Response, Request } from 'express';
|
18 |
+
import { AuthService } from './auth.service';
|
19 |
+
import * as bcrypt from 'bcrypt';
|
20 |
+
import { LocalAuthGuard } from './local-auth.guard';
|
21 |
+
import { UserService } from '../user/user.service';
|
22 |
+
import { JwtAuthGuard } from './jwt-auth.guard';
|
23 |
+
import * as jwt from 'jsonwebtoken';
|
24 |
+
import { FileInterceptor } from '@nestjs/platform-express';
|
25 |
+
import { Model } from 'mongoose';
|
26 |
+
import { AuthGuard } from '@nestjs/passport';
|
27 |
+
import { JwtService } from '@nestjs/jwt';
|
28 |
+
import { CommonServices } from '../shared/common.service';
|
29 |
+
import { jwtConstants } from 'src/constants/jwt.constant';
|
30 |
+
|
31 |
+
@Controller('auth')
|
32 |
+
export class AuthController extends CommonServices {
|
33 |
+
constructor(
|
34 |
+
private readonly authService: AuthService,
|
35 |
+
private readonly userService: UserService,
|
36 |
+
) {
|
37 |
+
super();
|
38 |
+
}
|
39 |
+
|
40 |
+
@UseGuards(LocalAuthGuard)
|
41 |
+
@Post('/login')
|
42 |
+
async login(
|
43 |
+
@Req() req: any,
|
44 |
+
@Res() res: Response,
|
45 |
+
@Body() body: any,
|
46 |
+
): Promise<any> {
|
47 |
+
try {
|
48 |
+
const Userresp: any = await this.authService.login(req.user, body);
|
49 |
+
return this.sendResponse(
|
50 |
+
this.messages.Success,
|
51 |
+
Userresp,
|
52 |
+
HttpStatus.OK,
|
53 |
+
res,
|
54 |
+
);
|
55 |
+
} catch (error) {
|
56 |
+
console.log(`error =>`, error);
|
57 |
+
return this.sendResponse(
|
58 |
+
'Internal server Error',
|
59 |
+
{},
|
60 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
61 |
+
res,
|
62 |
+
);
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
@Post('/signup')
|
67 |
+
async signup(
|
68 |
+
@Req() req: any,
|
69 |
+
@Res() res: Response,
|
70 |
+
@Body() body: any,
|
71 |
+
): Promise<any> {
|
72 |
+
try {
|
73 |
+
const email = body.email.toLowerCase().trim();
|
74 |
+
|
75 |
+
const checkUser = await this.userService.sharedFindOne({ email: email });
|
76 |
+
|
77 |
+
if (checkUser && checkUser.email == email) {
|
78 |
+
return this.sendResponse(
|
79 |
+
this.messages.userAlreadyExist,
|
80 |
+
{},
|
81 |
+
HttpStatus.CONFLICT,
|
82 |
+
res,
|
83 |
+
);
|
84 |
+
}
|
85 |
+
|
86 |
+
const createUser = await this.userService.sharedCreate({
|
87 |
+
...body,
|
88 |
+
password: bcrypt.hashSync(body.password, jwtConstants.salt),
|
89 |
+
roles: ['user'],
|
90 |
+
});
|
91 |
+
|
92 |
+
const Userresp: any = await this.authService.login(createUser);
|
93 |
+
|
94 |
+
return this.sendResponse(
|
95 |
+
'Account created!',
|
96 |
+
{
|
97 |
+
...Userresp,
|
98 |
+
},
|
99 |
+
HttpStatus.OK,
|
100 |
+
res,
|
101 |
+
);
|
102 |
+
} catch (error) {
|
103 |
+
console.log(error);
|
104 |
+
|
105 |
+
return this.sendResponse(
|
106 |
+
'Internal server Error',
|
107 |
+
{},
|
108 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
109 |
+
res,
|
110 |
+
);
|
111 |
+
}
|
112 |
+
}
|
113 |
+
}
|
src/modules/auth/auth.module.ts
CHANGED
@@ -1,27 +1,27 @@
|
|
1 |
-
import { Module } from '@nestjs/common';
|
2 |
-
import { AuthService } from './auth.service';
|
3 |
-
import { UserModule } from '../user/user.module';
|
4 |
-
import { AuthController } from './auth.controller';
|
5 |
-
import { DatabaseModule } from '../database/database.module';
|
6 |
-
import { UserService } from '../user/user.service';
|
7 |
-
import { JwtModule } from '@nestjs/jwt';
|
8 |
-
import { PassportModule } from '@nestjs/passport';
|
9 |
-
import { jwtConstants } from 'src/constants/jwt.constant';
|
10 |
-
import { JwtStrategy } from './jwt.strategy';
|
11 |
-
import { LocalStrategy } from './local.strategy';
|
12 |
-
|
13 |
-
@Module({
|
14 |
-
imports: [
|
15 |
-
DatabaseModule,
|
16 |
-
UserModule,
|
17 |
-
PassportModule,
|
18 |
-
JwtModule.register({
|
19 |
-
secret: 'secretKey',
|
20 |
-
signOptions: { expiresIn: '60d' },
|
21 |
-
}),
|
22 |
-
],
|
23 |
-
controllers: [AuthController],
|
24 |
-
providers: [AuthService, UserService, JwtStrategy, LocalStrategy],
|
25 |
-
exports: [AuthService, UserService],
|
26 |
-
})
|
27 |
-
export class AuthModule {}
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { AuthService } from './auth.service';
|
3 |
+
import { UserModule } from '../user/user.module';
|
4 |
+
import { AuthController } from './auth.controller';
|
5 |
+
import { DatabaseModule } from '../database/database.module';
|
6 |
+
import { UserService } from '../user/user.service';
|
7 |
+
import { JwtModule } from '@nestjs/jwt';
|
8 |
+
import { PassportModule } from '@nestjs/passport';
|
9 |
+
import { jwtConstants } from 'src/constants/jwt.constant';
|
10 |
+
import { JwtStrategy } from './jwt.strategy';
|
11 |
+
import { LocalStrategy } from './local.strategy';
|
12 |
+
|
13 |
+
@Module({
|
14 |
+
imports: [
|
15 |
+
DatabaseModule,
|
16 |
+
UserModule,
|
17 |
+
PassportModule,
|
18 |
+
JwtModule.register({
|
19 |
+
secret: 'secretKey',
|
20 |
+
signOptions: { expiresIn: '60d' },
|
21 |
+
}),
|
22 |
+
],
|
23 |
+
controllers: [AuthController],
|
24 |
+
providers: [AuthService, UserService, JwtStrategy, LocalStrategy],
|
25 |
+
exports: [AuthService, UserService],
|
26 |
+
})
|
27 |
+
export class AuthModule {}
|
src/modules/auth/auth.service.ts
CHANGED
@@ -1,107 +1,107 @@
|
|
1 |
-
import { Injectable, Inject } from '@nestjs/common';
|
2 |
-
import * as bcrypt from 'bcrypt';
|
3 |
-
import { JwtService } from '@nestjs/jwt';
|
4 |
-
import { UserService } from '../user/user.service';
|
5 |
-
import { Model } from 'mongoose';
|
6 |
-
import { CommonServices } from '../shared/common.service';
|
7 |
-
var qs = require('qs');
|
8 |
-
|
9 |
-
@Injectable()
|
10 |
-
export class AuthService extends CommonServices {
|
11 |
-
constructor(
|
12 |
-
private readonly userService: UserService,
|
13 |
-
private jwtService: JwtService,
|
14 |
-
) {
|
15 |
-
super();
|
16 |
-
}
|
17 |
-
|
18 |
-
async login(user: any, body: any = {}) {
|
19 |
-
const payload = {
|
20 |
-
userId: user._id,
|
21 |
-
name: user.name,
|
22 |
-
email: user.email,
|
23 |
-
roles: user.roles,
|
24 |
-
};
|
25 |
-
|
26 |
-
return {
|
27 |
-
access_token: this.jwtService.sign(payload, {
|
28 |
-
secret: 'secretKey',
|
29 |
-
expiresIn: '60d',
|
30 |
-
}),
|
31 |
-
user: {
|
32 |
-
_id: user._id,
|
33 |
-
name: user.name,
|
34 |
-
email: user.email,
|
35 |
-
roles: user.roles,
|
36 |
-
createdAt: user.createdAt,
|
37 |
-
updatedAt: user.updatedAt,
|
38 |
-
},
|
39 |
-
};
|
40 |
-
}
|
41 |
-
|
42 |
-
async validateUser(identifier: string, password: string): Promise<any> {
|
43 |
-
console.log(`IDENTIFIER =>`, identifier);
|
44 |
-
console.log(`password =>`, password);
|
45 |
-
const user = await this.userService.userRepository
|
46 |
-
.findOne({
|
47 |
-
$or: [{ email: identifier }, { username: identifier }],
|
48 |
-
})
|
49 |
-
.select('+password');
|
50 |
-
if (user && bcrypt.compareSync(password, user.password)) {
|
51 |
-
return user;
|
52 |
-
} else {
|
53 |
-
const userInLowerCase = await this.userService.userRepository
|
54 |
-
.findOne({
|
55 |
-
$or: [
|
56 |
-
{ email: identifier.toLowerCase() },
|
57 |
-
{ username: identifier.toLowerCase() },
|
58 |
-
],
|
59 |
-
})
|
60 |
-
.select('+password');
|
61 |
-
if (
|
62 |
-
userInLowerCase &&
|
63 |
-
bcrypt.compareSync(password, userInLowerCase.password)
|
64 |
-
) {
|
65 |
-
return userInLowerCase;
|
66 |
-
}
|
67 |
-
}
|
68 |
-
return null;
|
69 |
-
}
|
70 |
-
|
71 |
-
async validateByEmail(identifier: string): Promise<any> {
|
72 |
-
const user = await this.userService.userRepository.findOne({
|
73 |
-
$or: [{ email: identifier }, { username: identifier }],
|
74 |
-
});
|
75 |
-
|
76 |
-
if (user) {
|
77 |
-
const rUser = {
|
78 |
-
userId: user._id,
|
79 |
-
name: user.name,
|
80 |
-
username: user.username,
|
81 |
-
email: user.email,
|
82 |
-
roles: user.roles,
|
83 |
-
// iat: user.iat,
|
84 |
-
// exp: user.exp
|
85 |
-
};
|
86 |
-
return rUser;
|
87 |
-
} else {
|
88 |
-
const userInLowerCase = await this.userService.userRepository.findOne({
|
89 |
-
$or: [
|
90 |
-
{ email: identifier.toLowerCase() },
|
91 |
-
{ username: identifier.toLowerCase() },
|
92 |
-
],
|
93 |
-
});
|
94 |
-
if (userInLowerCase) {
|
95 |
-
const rUser = {
|
96 |
-
userId: userInLowerCase._id,
|
97 |
-
name: userInLowerCase.name,
|
98 |
-
username: user.username,
|
99 |
-
email: userInLowerCase.email,
|
100 |
-
roles: userInLowerCase.roles,
|
101 |
-
};
|
102 |
-
return rUser;
|
103 |
-
}
|
104 |
-
}
|
105 |
-
return null;
|
106 |
-
}
|
107 |
-
}
|
|
|
1 |
+
import { Injectable, Inject } from '@nestjs/common';
|
2 |
+
import * as bcrypt from 'bcrypt';
|
3 |
+
import { JwtService } from '@nestjs/jwt';
|
4 |
+
import { UserService } from '../user/user.service';
|
5 |
+
import { Model } from 'mongoose';
|
6 |
+
import { CommonServices } from '../shared/common.service';
|
7 |
+
var qs = require('qs');
|
8 |
+
|
9 |
+
@Injectable()
|
10 |
+
export class AuthService extends CommonServices {
|
11 |
+
constructor(
|
12 |
+
private readonly userService: UserService,
|
13 |
+
private jwtService: JwtService,
|
14 |
+
) {
|
15 |
+
super();
|
16 |
+
}
|
17 |
+
|
18 |
+
async login(user: any, body: any = {}) {
|
19 |
+
const payload = {
|
20 |
+
userId: user._id,
|
21 |
+
name: user.name,
|
22 |
+
email: user.email,
|
23 |
+
roles: user.roles,
|
24 |
+
};
|
25 |
+
|
26 |
+
return {
|
27 |
+
access_token: this.jwtService.sign(payload, {
|
28 |
+
secret: 'secretKey',
|
29 |
+
expiresIn: '60d',
|
30 |
+
}),
|
31 |
+
user: {
|
32 |
+
_id: user._id,
|
33 |
+
name: user.name,
|
34 |
+
email: user.email,
|
35 |
+
roles: user.roles,
|
36 |
+
createdAt: user.createdAt,
|
37 |
+
updatedAt: user.updatedAt,
|
38 |
+
},
|
39 |
+
};
|
40 |
+
}
|
41 |
+
|
42 |
+
async validateUser(identifier: string, password: string): Promise<any> {
|
43 |
+
console.log(`IDENTIFIER =>`, identifier);
|
44 |
+
console.log(`password =>`, password);
|
45 |
+
const user = await this.userService.userRepository
|
46 |
+
.findOne({
|
47 |
+
$or: [{ email: identifier }, { username: identifier }],
|
48 |
+
})
|
49 |
+
.select('+password');
|
50 |
+
if (user && bcrypt.compareSync(password, user.password)) {
|
51 |
+
return user;
|
52 |
+
} else {
|
53 |
+
const userInLowerCase = await this.userService.userRepository
|
54 |
+
.findOne({
|
55 |
+
$or: [
|
56 |
+
{ email: identifier.toLowerCase() },
|
57 |
+
{ username: identifier.toLowerCase() },
|
58 |
+
],
|
59 |
+
})
|
60 |
+
.select('+password');
|
61 |
+
if (
|
62 |
+
userInLowerCase &&
|
63 |
+
bcrypt.compareSync(password, userInLowerCase.password)
|
64 |
+
) {
|
65 |
+
return userInLowerCase;
|
66 |
+
}
|
67 |
+
}
|
68 |
+
return null;
|
69 |
+
}
|
70 |
+
|
71 |
+
async validateByEmail(identifier: string): Promise<any> {
|
72 |
+
const user = await this.userService.userRepository.findOne({
|
73 |
+
$or: [{ email: identifier }, { username: identifier }],
|
74 |
+
});
|
75 |
+
|
76 |
+
if (user) {
|
77 |
+
const rUser = {
|
78 |
+
userId: user._id,
|
79 |
+
name: user.name,
|
80 |
+
username: user.username,
|
81 |
+
email: user.email,
|
82 |
+
roles: user.roles,
|
83 |
+
// iat: user.iat,
|
84 |
+
// exp: user.exp
|
85 |
+
};
|
86 |
+
return rUser;
|
87 |
+
} else {
|
88 |
+
const userInLowerCase = await this.userService.userRepository.findOne({
|
89 |
+
$or: [
|
90 |
+
{ email: identifier.toLowerCase() },
|
91 |
+
{ username: identifier.toLowerCase() },
|
92 |
+
],
|
93 |
+
});
|
94 |
+
if (userInLowerCase) {
|
95 |
+
const rUser = {
|
96 |
+
userId: userInLowerCase._id,
|
97 |
+
name: userInLowerCase.name,
|
98 |
+
username: user.username,
|
99 |
+
email: userInLowerCase.email,
|
100 |
+
roles: userInLowerCase.roles,
|
101 |
+
};
|
102 |
+
return rUser;
|
103 |
+
}
|
104 |
+
}
|
105 |
+
return null;
|
106 |
+
}
|
107 |
+
}
|
src/modules/auth/jwt-auth.guard.ts
CHANGED
@@ -1,18 +1,18 @@
|
|
1 |
-
|
2 |
-
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
3 |
-
import { AuthGuard } from '@nestjs/passport';
|
4 |
-
import { AuthService } from './auth.service';
|
5 |
-
|
6 |
-
@Injectable()
|
7 |
-
export class JwtAuthGuard extends AuthGuard('jwt') {
|
8 |
-
handleRequest(err, user, info) {
|
9 |
-
// console.log( user, err, info )
|
10 |
-
// You can throw an exception based on either "info" or "err" arguments
|
11 |
-
// console.log(user, info, '##################JWT');
|
12 |
-
if (err || !user) {
|
13 |
-
// console.log('ERROR', err);
|
14 |
-
throw err || new UnauthorizedException();
|
15 |
-
}
|
16 |
-
return user;
|
17 |
-
}
|
18 |
-
}
|
|
|
1 |
+
|
2 |
+
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
3 |
+
import { AuthGuard } from '@nestjs/passport';
|
4 |
+
import { AuthService } from './auth.service';
|
5 |
+
|
6 |
+
@Injectable()
|
7 |
+
export class JwtAuthGuard extends AuthGuard('jwt') {
|
8 |
+
handleRequest(err, user, info) {
|
9 |
+
// console.log( user, err, info )
|
10 |
+
// You can throw an exception based on either "info" or "err" arguments
|
11 |
+
// console.log(user, info, '##################JWT');
|
12 |
+
if (err || !user) {
|
13 |
+
// console.log('ERROR', err);
|
14 |
+
throw err || new UnauthorizedException();
|
15 |
+
}
|
16 |
+
return user;
|
17 |
+
}
|
18 |
+
}
|
src/modules/auth/jwt.strategy.ts
CHANGED
@@ -1,30 +1,30 @@
|
|
1 |
-
import { ExtractJwt } from 'passport-jwt';
|
2 |
-
import { PassportStrategy } from '@nestjs/passport';
|
3 |
-
import { Injectable } from '@nestjs/common';
|
4 |
-
import { Strategy } from 'passport-custom';
|
5 |
-
import { AuthService } from './auth.service';
|
6 |
-
import { jwtDecode } from 'jwt-decode';
|
7 |
-
|
8 |
-
@Injectable()
|
9 |
-
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
10 |
-
constructor(private authService: AuthService) {
|
11 |
-
super();
|
12 |
-
}
|
13 |
-
async validate(req: Request): Promise<any> {
|
14 |
-
try {
|
15 |
-
const authHeader = String(req.headers['authorization'] || '');
|
16 |
-
if (authHeader.startsWith('Bearer ')) {
|
17 |
-
const token = authHeader.substring(7, authHeader.length);
|
18 |
-
const payload:any = jwtDecode(token) as any;
|
19 |
-
const user = this.authService.validateByEmail(payload.email)
|
20 |
-
return user ? user : false
|
21 |
-
}
|
22 |
-
else{
|
23 |
-
return false
|
24 |
-
}
|
25 |
-
} catch (error) {
|
26 |
-
return error
|
27 |
-
}
|
28 |
-
|
29 |
-
}
|
30 |
}
|
|
|
1 |
+
import { ExtractJwt } from 'passport-jwt';
|
2 |
+
import { PassportStrategy } from '@nestjs/passport';
|
3 |
+
import { Injectable } from '@nestjs/common';
|
4 |
+
import { Strategy } from 'passport-custom';
|
5 |
+
import { AuthService } from './auth.service';
|
6 |
+
import { jwtDecode } from 'jwt-decode';
|
7 |
+
|
8 |
+
@Injectable()
|
9 |
+
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
10 |
+
constructor(private authService: AuthService) {
|
11 |
+
super();
|
12 |
+
}
|
13 |
+
async validate(req: Request): Promise<any> {
|
14 |
+
try {
|
15 |
+
const authHeader = String(req.headers['authorization'] || '');
|
16 |
+
if (authHeader.startsWith('Bearer ')) {
|
17 |
+
const token = authHeader.substring(7, authHeader.length);
|
18 |
+
const payload:any = jwtDecode(token) as any;
|
19 |
+
const user = this.authService.validateByEmail(payload.email)
|
20 |
+
return user ? user : false
|
21 |
+
}
|
22 |
+
else{
|
23 |
+
return false
|
24 |
+
}
|
25 |
+
} catch (error) {
|
26 |
+
return error
|
27 |
+
}
|
28 |
+
|
29 |
+
}
|
30 |
}
|
src/modules/auth/local-auth.guard.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
import { Injectable } from '@nestjs/common';
|
2 |
-
import { AuthGuard } from '@nestjs/passport';
|
3 |
-
|
4 |
-
@Injectable()
|
5 |
export class LocalAuthGuard extends AuthGuard('local') {}
|
|
|
1 |
+
import { Injectable } from '@nestjs/common';
|
2 |
+
import { AuthGuard } from '@nestjs/passport';
|
3 |
+
|
4 |
+
@Injectable()
|
5 |
export class LocalAuthGuard extends AuthGuard('local') {}
|
src/modules/auth/local.strategy.ts
CHANGED
@@ -1,37 +1,37 @@
|
|
1 |
-
import { Strategy } from 'passport-local';
|
2 |
-
import { PassportStrategy } from '@nestjs/passport';
|
3 |
-
import {
|
4 |
-
HttpException,
|
5 |
-
HttpStatus,
|
6 |
-
Injectable,
|
7 |
-
UnauthorizedException,
|
8 |
-
} from '@nestjs/common';
|
9 |
-
import { AuthService } from './auth.service';
|
10 |
-
|
11 |
-
@Injectable()
|
12 |
-
export class LocalStrategy extends PassportStrategy(Strategy) {
|
13 |
-
constructor(private authService: AuthService) {
|
14 |
-
super({ usernameField: 'identifier' });
|
15 |
-
}
|
16 |
-
|
17 |
-
async validate(
|
18 |
-
identifier: string,
|
19 |
-
password: string,
|
20 |
-
isAdmin: boolean = false,
|
21 |
-
): Promise<any> {
|
22 |
-
console.log(`IDENTIFIER =>`, identifier);
|
23 |
-
|
24 |
-
const user = await this.authService.validateUser(identifier, password);
|
25 |
-
if (!user) {
|
26 |
-
throw new UnauthorizedException('Incorrect username or password');
|
27 |
-
}
|
28 |
-
|
29 |
-
if ((user && user.deletedAt) || (user && user.isDeleted === true)) {
|
30 |
-
throw new HttpException(
|
31 |
-
'Your account has been deleted',
|
32 |
-
HttpStatus.FORBIDDEN,
|
33 |
-
);
|
34 |
-
}
|
35 |
-
return user;
|
36 |
-
}
|
37 |
-
}
|
|
|
1 |
+
import { Strategy } from 'passport-local';
|
2 |
+
import { PassportStrategy } from '@nestjs/passport';
|
3 |
+
import {
|
4 |
+
HttpException,
|
5 |
+
HttpStatus,
|
6 |
+
Injectable,
|
7 |
+
UnauthorizedException,
|
8 |
+
} from '@nestjs/common';
|
9 |
+
import { AuthService } from './auth.service';
|
10 |
+
|
11 |
+
@Injectable()
|
12 |
+
export class LocalStrategy extends PassportStrategy(Strategy) {
|
13 |
+
constructor(private authService: AuthService) {
|
14 |
+
super({ usernameField: 'identifier' });
|
15 |
+
}
|
16 |
+
|
17 |
+
async validate(
|
18 |
+
identifier: string,
|
19 |
+
password: string,
|
20 |
+
isAdmin: boolean = false,
|
21 |
+
): Promise<any> {
|
22 |
+
console.log(`IDENTIFIER =>`, identifier);
|
23 |
+
|
24 |
+
const user = await this.authService.validateUser(identifier, password);
|
25 |
+
if (!user) {
|
26 |
+
throw new UnauthorizedException('Incorrect username or password');
|
27 |
+
}
|
28 |
+
|
29 |
+
if ((user && user.deletedAt) || (user && user.isDeleted === true)) {
|
30 |
+
throw new HttpException(
|
31 |
+
'Your account has been deleted',
|
32 |
+
HttpStatus.FORBIDDEN,
|
33 |
+
);
|
34 |
+
}
|
35 |
+
return user;
|
36 |
+
}
|
37 |
+
}
|
src/modules/database/database.module.ts
CHANGED
@@ -1,21 +1,21 @@
|
|
1 |
-
import { Module } from '@nestjs/common';
|
2 |
-
import { databaseProviders } from './database.provider';
|
3 |
-
import { propertyProviders } from '../property/property.provider';
|
4 |
-
import { usersProviders } from '../user/user.provider';
|
5 |
-
import { activityProviders } from '../activity/activity.provider';
|
6 |
-
|
7 |
-
@Module({
|
8 |
-
providers: [
|
9 |
-
...databaseProviders,
|
10 |
-
...propertyProviders,
|
11 |
-
...usersProviders,
|
12 |
-
...activityProviders,
|
13 |
-
],
|
14 |
-
exports: [
|
15 |
-
...databaseProviders,
|
16 |
-
...propertyProviders,
|
17 |
-
...usersProviders,
|
18 |
-
...activityProviders,
|
19 |
-
],
|
20 |
-
})
|
21 |
-
export class DatabaseModule {}
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { databaseProviders } from './database.provider';
|
3 |
+
import { propertyProviders } from '../property/property.provider';
|
4 |
+
import { usersProviders } from '../user/user.provider';
|
5 |
+
import { activityProviders } from '../activity/activity.provider';
|
6 |
+
|
7 |
+
@Module({
|
8 |
+
providers: [
|
9 |
+
...databaseProviders,
|
10 |
+
...propertyProviders,
|
11 |
+
...usersProviders,
|
12 |
+
...activityProviders,
|
13 |
+
],
|
14 |
+
exports: [
|
15 |
+
...databaseProviders,
|
16 |
+
...propertyProviders,
|
17 |
+
...usersProviders,
|
18 |
+
...activityProviders,
|
19 |
+
],
|
20 |
+
})
|
21 |
+
export class DatabaseModule {}
|
src/modules/database/database.provider.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
import * as mongoose from 'mongoose';
|
2 |
-
import { env } from 'process';
|
3 |
-
|
4 |
-
export const databaseProviders = [
|
5 |
-
{
|
6 |
-
provide: 'DATABASE_CONNECTION',
|
7 |
-
useFactory: (): Promise<typeof mongoose> =>
|
8 |
-
mongoose.connect(
|
9 |
-
'mongodb+srv://rehmanwahlah248:[email protected]/property-listing?retryWrites=true&w=majority',
|
10 |
-
),
|
11 |
-
},
|
12 |
];
|
|
|
1 |
+
import * as mongoose from 'mongoose';
|
2 |
+
import { env } from 'process';
|
3 |
+
|
4 |
+
export const databaseProviders = [
|
5 |
+
{
|
6 |
+
provide: 'DATABASE_CONNECTION',
|
7 |
+
useFactory: (): Promise<typeof mongoose> =>
|
8 |
+
mongoose.connect(
|
9 |
+
'mongodb+srv://rehmanwahlah248:[email protected]/property-listing?retryWrites=true&w=majority',
|
10 |
+
),
|
11 |
+
},
|
12 |
];
|
src/modules/property/property.controller.ts
CHANGED
@@ -1,144 +1,144 @@
|
|
1 |
-
import {
|
2 |
-
Body,
|
3 |
-
Controller,
|
4 |
-
Get,
|
5 |
-
HttpStatus,
|
6 |
-
Post,
|
7 |
-
Query,
|
8 |
-
Req,
|
9 |
-
Res,
|
10 |
-
UseGuards,
|
11 |
-
} from '@nestjs/common';
|
12 |
-
import { Response } from 'express';
|
13 |
-
import { CommonServices } from '../shared/common.service';
|
14 |
-
import { PropertyService } from './property.service';
|
15 |
-
import { v4 as uuidv4 } from 'uuid';
|
16 |
-
|
17 |
-
@Controller('property')
|
18 |
-
export class PropertyController extends CommonServices {
|
19 |
-
constructor(private readonly propertyService: PropertyService) {
|
20 |
-
super();
|
21 |
-
}
|
22 |
-
|
23 |
-
@Get('')
|
24 |
-
async getPropertListings(@Res() res: Response, @Req() req): Promise<any> {
|
25 |
-
try {
|
26 |
-
const response = await this.propertyService.sharedFind({});
|
27 |
-
return this.sendResponse(
|
28 |
-
this.messages.Success,
|
29 |
-
response ?? 'NOT_FOUND',
|
30 |
-
HttpStatus.OK,
|
31 |
-
res,
|
32 |
-
);
|
33 |
-
} catch (error) {
|
34 |
-
return this.sendResponse(
|
35 |
-
'Internal server Error',
|
36 |
-
{},
|
37 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
38 |
-
res,
|
39 |
-
);
|
40 |
-
}
|
41 |
-
}
|
42 |
-
|
43 |
-
/**
|
44 |
-
*
|
45 |
-
* @param query
|
46 |
-
* @param res
|
47 |
-
* @param req
|
48 |
-
*/
|
49 |
-
@Get('/listings/dashboard')
|
50 |
-
async getDashboardLisings(@Res() res, @Req() req) {
|
51 |
-
try {
|
52 |
-
const listings = await this.propertyService.propertyRepository
|
53 |
-
.find({})
|
54 |
-
.sort({ _id: -1 })
|
55 |
-
.limit(20);
|
56 |
-
|
57 |
-
this.sendResponse(this.messages.Success, listings, HttpStatus.OK, res);
|
58 |
-
} catch (error) {
|
59 |
-
console.error('error', error);
|
60 |
-
this.sendResponse(
|
61 |
-
this.messages.Error,
|
62 |
-
null,
|
63 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
64 |
-
res,
|
65 |
-
);
|
66 |
-
}
|
67 |
-
}
|
68 |
-
|
69 |
-
/**
|
70 |
-
*
|
71 |
-
* @param query
|
72 |
-
* @param res
|
73 |
-
* @param req
|
74 |
-
*/
|
75 |
-
@Get('/listings')
|
76 |
-
async getDashboardActiveUsers(@Query() query, @Res() res, @Req() req) {
|
77 |
-
try {
|
78 |
-
|
79 |
-
const page = Number(query.page);
|
80 |
-
const resPerPage = query.resPerPage ? Number(query.resPerPage) : 20;
|
81 |
-
const search = query.search || ''; // Default to empty search if not provided
|
82 |
-
let sessionId = null;
|
83 |
-
|
84 |
-
if (!query.sessionId) sessionId = uuidv4();
|
85 |
-
|
86 |
-
let listings;
|
87 |
-
if (!query.sessionId) {
|
88 |
-
listings = await this.propertyService.propertyLisitng(
|
89 |
-
page,
|
90 |
-
resPerPage,
|
91 |
-
search,
|
92 |
-
);
|
93 |
-
} else {
|
94 |
-
listings = await this.propertyService.propertyLisitngRecommendedSearch(
|
95 |
-
page,
|
96 |
-
resPerPage,
|
97 |
-
query.recommendations,
|
98 |
-
);
|
99 |
-
}
|
100 |
-
|
101 |
-
this.sendResponse(
|
102 |
-
this.messages.Success,
|
103 |
-
{ listings, sessionId: sessionId },
|
104 |
-
HttpStatus.OK,
|
105 |
-
res,
|
106 |
-
);
|
107 |
-
} catch (error) {
|
108 |
-
console.error('error', error);
|
109 |
-
this.sendResponse(
|
110 |
-
this.messages.Error,
|
111 |
-
null,
|
112 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
113 |
-
res,
|
114 |
-
);
|
115 |
-
}
|
116 |
-
}
|
117 |
-
|
118 |
-
/**
|
119 |
-
*
|
120 |
-
* @param query
|
121 |
-
* @param res
|
122 |
-
* @param req
|
123 |
-
*/
|
124 |
-
@Get('/detail')
|
125 |
-
async getPropertyDetails(@Query() query, @Res() res, @Req() req) {
|
126 |
-
try {
|
127 |
-
// property id
|
128 |
-
const id = query.id;
|
129 |
-
|
130 |
-
const property =
|
131 |
-
await this.propertyService.propertyRepository.findById(id);
|
132 |
-
|
133 |
-
this.sendResponse(this.messages.Success, property, HttpStatus.OK, res);
|
134 |
-
} catch (error) {
|
135 |
-
console.error('error', error);
|
136 |
-
this.sendResponse(
|
137 |
-
this.messages.Error,
|
138 |
-
null,
|
139 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
140 |
-
res,
|
141 |
-
);
|
142 |
-
}
|
143 |
-
}
|
144 |
-
}
|
|
|
1 |
+
import {
|
2 |
+
Body,
|
3 |
+
Controller,
|
4 |
+
Get,
|
5 |
+
HttpStatus,
|
6 |
+
Post,
|
7 |
+
Query,
|
8 |
+
Req,
|
9 |
+
Res,
|
10 |
+
UseGuards,
|
11 |
+
} from '@nestjs/common';
|
12 |
+
import { Response } from 'express';
|
13 |
+
import { CommonServices } from '../shared/common.service';
|
14 |
+
import { PropertyService } from './property.service';
|
15 |
+
import { v4 as uuidv4 } from 'uuid';
|
16 |
+
|
17 |
+
@Controller('property')
|
18 |
+
export class PropertyController extends CommonServices {
|
19 |
+
constructor(private readonly propertyService: PropertyService) {
|
20 |
+
super();
|
21 |
+
}
|
22 |
+
|
23 |
+
@Get('')
|
24 |
+
async getPropertListings(@Res() res: Response, @Req() req): Promise<any> {
|
25 |
+
try {
|
26 |
+
const response = await this.propertyService.sharedFind({});
|
27 |
+
return this.sendResponse(
|
28 |
+
this.messages.Success,
|
29 |
+
response ?? 'NOT_FOUND',
|
30 |
+
HttpStatus.OK,
|
31 |
+
res,
|
32 |
+
);
|
33 |
+
} catch (error) {
|
34 |
+
return this.sendResponse(
|
35 |
+
'Internal server Error',
|
36 |
+
{},
|
37 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
38 |
+
res,
|
39 |
+
);
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
*
|
45 |
+
* @param query
|
46 |
+
* @param res
|
47 |
+
* @param req
|
48 |
+
*/
|
49 |
+
@Get('/listings/dashboard')
|
50 |
+
async getDashboardLisings(@Res() res, @Req() req) {
|
51 |
+
try {
|
52 |
+
const listings = await this.propertyService.propertyRepository
|
53 |
+
.find({})
|
54 |
+
.sort({ _id: -1 })
|
55 |
+
.limit(20);
|
56 |
+
|
57 |
+
this.sendResponse(this.messages.Success, listings, HttpStatus.OK, res);
|
58 |
+
} catch (error) {
|
59 |
+
console.error('error', error);
|
60 |
+
this.sendResponse(
|
61 |
+
this.messages.Error,
|
62 |
+
null,
|
63 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
64 |
+
res,
|
65 |
+
);
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
*
|
71 |
+
* @param query
|
72 |
+
* @param res
|
73 |
+
* @param req
|
74 |
+
*/
|
75 |
+
@Get('/listings')
|
76 |
+
async getDashboardActiveUsers(@Query() query, @Res() res, @Req() req) {
|
77 |
+
try {
|
78 |
+
|
79 |
+
const page = Number(query.page);
|
80 |
+
const resPerPage = query.resPerPage ? Number(query.resPerPage) : 20;
|
81 |
+
const search = query.search || ''; // Default to empty search if not provided
|
82 |
+
let sessionId = null;
|
83 |
+
|
84 |
+
if (!query.sessionId) sessionId = uuidv4();
|
85 |
+
|
86 |
+
let listings;
|
87 |
+
if (!query.sessionId) {
|
88 |
+
listings = await this.propertyService.propertyLisitng(
|
89 |
+
page,
|
90 |
+
resPerPage,
|
91 |
+
search,
|
92 |
+
);
|
93 |
+
} else {
|
94 |
+
listings = await this.propertyService.propertyLisitngRecommendedSearch(
|
95 |
+
page,
|
96 |
+
resPerPage,
|
97 |
+
query.recommendations,
|
98 |
+
);
|
99 |
+
}
|
100 |
+
|
101 |
+
this.sendResponse(
|
102 |
+
this.messages.Success,
|
103 |
+
{ listings, sessionId: sessionId },
|
104 |
+
HttpStatus.OK,
|
105 |
+
res,
|
106 |
+
);
|
107 |
+
} catch (error) {
|
108 |
+
console.error('error', error);
|
109 |
+
this.sendResponse(
|
110 |
+
this.messages.Error,
|
111 |
+
null,
|
112 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
113 |
+
res,
|
114 |
+
);
|
115 |
+
}
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
*
|
120 |
+
* @param query
|
121 |
+
* @param res
|
122 |
+
* @param req
|
123 |
+
*/
|
124 |
+
@Get('/detail')
|
125 |
+
async getPropertyDetails(@Query() query, @Res() res, @Req() req) {
|
126 |
+
try {
|
127 |
+
// property id
|
128 |
+
const id = query.id;
|
129 |
+
|
130 |
+
const property =
|
131 |
+
await this.propertyService.propertyRepository.findById(id);
|
132 |
+
|
133 |
+
this.sendResponse(this.messages.Success, property, HttpStatus.OK, res);
|
134 |
+
} catch (error) {
|
135 |
+
console.error('error', error);
|
136 |
+
this.sendResponse(
|
137 |
+
this.messages.Error,
|
138 |
+
null,
|
139 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
140 |
+
res,
|
141 |
+
);
|
142 |
+
}
|
143 |
+
}
|
144 |
+
}
|
src/modules/property/property.module.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
import { Module } from '@nestjs/common';
|
2 |
-
import { PropertyController } from './property.controller';
|
3 |
-
import { PropertyService } from './property.service';
|
4 |
-
import { DatabaseModule } from '../database/database.module';
|
5 |
-
|
6 |
-
@Module({
|
7 |
-
imports: [DatabaseModule],
|
8 |
-
controllers: [PropertyController],
|
9 |
-
providers: [PropertyService],
|
10 |
-
exports: [PropertyService],
|
11 |
-
})
|
12 |
-
export class PropertyModule {}
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { PropertyController } from './property.controller';
|
3 |
+
import { PropertyService } from './property.service';
|
4 |
+
import { DatabaseModule } from '../database/database.module';
|
5 |
+
|
6 |
+
@Module({
|
7 |
+
imports: [DatabaseModule],
|
8 |
+
controllers: [PropertyController],
|
9 |
+
providers: [PropertyService],
|
10 |
+
exports: [PropertyService],
|
11 |
+
})
|
12 |
+
export class PropertyModule {}
|
src/modules/property/property.provider.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
import { Connection } from 'mongoose';
|
2 |
-
import { PROPERTIE, PROPERTY_REPOSITORY } from 'src/constants';
|
3 |
-
import { PropertySchema } from './property.schema';
|
4 |
-
|
5 |
-
export const propertyProviders = [
|
6 |
-
{
|
7 |
-
provide: PROPERTY_REPOSITORY,
|
8 |
-
useFactory: (connection: Connection) =>
|
9 |
-
connection.model(PROPERTIE, PropertySchema),
|
10 |
-
inject: ['DATABASE_CONNECTION'],
|
11 |
-
},
|
12 |
-
];
|
|
|
1 |
+
import { Connection } from 'mongoose';
|
2 |
+
import { PROPERTIE, PROPERTY_REPOSITORY } from 'src/constants';
|
3 |
+
import { PropertySchema } from './property.schema';
|
4 |
+
|
5 |
+
export const propertyProviders = [
|
6 |
+
{
|
7 |
+
provide: PROPERTY_REPOSITORY,
|
8 |
+
useFactory: (connection: Connection) =>
|
9 |
+
connection.model(PROPERTIE, PropertySchema),
|
10 |
+
inject: ['DATABASE_CONNECTION'],
|
11 |
+
},
|
12 |
+
];
|
src/modules/property/property.schema.ts
CHANGED
@@ -1,38 +1,38 @@
|
|
1 |
-
import * as mongoose from 'mongoose';
|
2 |
-
import { Document } from 'mongoose';
|
3 |
-
|
4 |
-
export interface IPropertyDocument extends Document {
|
5 |
-
title?: string;
|
6 |
-
price?: string;
|
7 |
-
id?: string;
|
8 |
-
imgs?: Array<[]>;
|
9 |
-
desc?: string;
|
10 |
-
features?: object;
|
11 |
-
thumbnail_summary?: string;
|
12 |
-
listing_url?: string;
|
13 |
-
views?: number; // Track the number of views
|
14 |
-
total_time_spent?: number; // Track total time spent on the property in seconds
|
15 |
-
createdAt?: Date;
|
16 |
-
updatedAt?: Date;
|
17 |
-
}
|
18 |
-
|
19 |
-
const PropertySchema = new mongoose.Schema<IPropertyDocument>(
|
20 |
-
{
|
21 |
-
title: { type: String },
|
22 |
-
price: { type: String },
|
23 |
-
id: { type: String },
|
24 |
-
desc: { type: String, unique: true },
|
25 |
-
features: { type: Object },
|
26 |
-
listing_url: { type: String },
|
27 |
-
imgs: [],
|
28 |
-
views: { type: Number, default: 0 }, // New field to store the number of views
|
29 |
-
total_time_spent: { type: Number, default: 0 }, // New field to store total time spent in seconds
|
30 |
-
createdAt: { type: Date, default: Date.now },
|
31 |
-
updatedAt: { type: Date, default: Date.now },
|
32 |
-
},
|
33 |
-
{
|
34 |
-
toJSON: { versionKey: false },
|
35 |
-
},
|
36 |
-
);
|
37 |
-
|
38 |
-
export { PropertySchema };
|
|
|
1 |
+
import * as mongoose from 'mongoose';
|
2 |
+
import { Document } from 'mongoose';
|
3 |
+
|
4 |
+
export interface IPropertyDocument extends Document {
|
5 |
+
title?: string;
|
6 |
+
price?: string;
|
7 |
+
id?: string;
|
8 |
+
imgs?: Array<[]>;
|
9 |
+
desc?: string;
|
10 |
+
features?: object;
|
11 |
+
thumbnail_summary?: string;
|
12 |
+
listing_url?: string;
|
13 |
+
views?: number; // Track the number of views
|
14 |
+
total_time_spent?: number; // Track total time spent on the property in seconds
|
15 |
+
createdAt?: Date;
|
16 |
+
updatedAt?: Date;
|
17 |
+
}
|
18 |
+
|
19 |
+
const PropertySchema = new mongoose.Schema<IPropertyDocument>(
|
20 |
+
{
|
21 |
+
title: { type: String },
|
22 |
+
price: { type: String },
|
23 |
+
id: { type: String },
|
24 |
+
desc: { type: String, unique: true },
|
25 |
+
features: { type: Object },
|
26 |
+
listing_url: { type: String },
|
27 |
+
imgs: [],
|
28 |
+
views: { type: Number, default: 0 }, // New field to store the number of views
|
29 |
+
total_time_spent: { type: Number, default: 0 }, // New field to store total time spent in seconds
|
30 |
+
createdAt: { type: Date, default: Date.now },
|
31 |
+
updatedAt: { type: Date, default: Date.now },
|
32 |
+
},
|
33 |
+
{
|
34 |
+
toJSON: { versionKey: false },
|
35 |
+
},
|
36 |
+
);
|
37 |
+
|
38 |
+
export { PropertySchema };
|
src/modules/property/property.service.ts
CHANGED
@@ -1,90 +1,90 @@
|
|
1 |
-
import { Inject, Injectable } from '@nestjs/common';
|
2 |
-
import { Model, Types } from 'mongoose';
|
3 |
-
import { sharedCrudService } from '../shared/sharedCrud.services';
|
4 |
-
import { PROPERTY_REPOSITORY } from 'src/constants';
|
5 |
-
import { IPropertyDocument } from './property.schema';
|
6 |
-
|
7 |
-
@Injectable()
|
8 |
-
export class PropertyService extends sharedCrudService {
|
9 |
-
constructor(
|
10 |
-
@Inject(PROPERTY_REPOSITORY)
|
11 |
-
readonly propertyRepository: Model<IPropertyDocument>,
|
12 |
-
) {
|
13 |
-
super(propertyRepository);
|
14 |
-
}
|
15 |
-
|
16 |
-
async propertyLisitng(
|
17 |
-
page: number,
|
18 |
-
resPerPage: number,
|
19 |
-
search: string,
|
20 |
-
): Promise<any> {
|
21 |
-
const query = [];
|
22 |
-
query.push({ id: { $exists: true } });
|
23 |
-
|
24 |
-
if (search) query.push({ title: { $regex: search, $options: 'i' } });
|
25 |
-
|
26 |
-
const [listings, tLisitngsCount] = await Promise.all([
|
27 |
-
this.propertyRepository
|
28 |
-
.find({ $and: [...query] })
|
29 |
-
.sort({ createdAt: -1 })
|
30 |
-
.skip(resPerPage * (page - 1))
|
31 |
-
.limit(resPerPage)
|
32 |
-
.exec(),
|
33 |
-
this.propertyRepository.countDocuments({ $and: [...query] }).exec(),
|
34 |
-
]);
|
35 |
-
|
36 |
-
return {
|
37 |
-
listings,
|
38 |
-
current_page: page,
|
39 |
-
pages: Math.ceil(tLisitngsCount / resPerPage),
|
40 |
-
total_listings: tLisitngsCount,
|
41 |
-
per_page: resPerPage,
|
42 |
-
};
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* It will recieve array of IDs to get the recommended listing
|
47 |
-
* @param page
|
48 |
-
* @param resPerPage
|
49 |
-
* @param search
|
50 |
-
* @returns
|
51 |
-
*/
|
52 |
-
async propertyLisitngRecommendedSearch(
|
53 |
-
page: number,
|
54 |
-
resPerPage: number,
|
55 |
-
recommendations: string[],
|
56 |
-
): Promise<any> {
|
57 |
-
const query = [];
|
58 |
-
|
59 |
-
// Check if recommendations is a string and split it into an array
|
60 |
-
if (typeof recommendations === 'string') {
|
61 |
-
//@ts-ignore
|
62 |
-
recommendations = recommendations.split(',');
|
63 |
-
}
|
64 |
-
|
65 |
-
query.push({ id: { $exists: true } });
|
66 |
-
|
67 |
-
// Modify the query to handle an array of IDs
|
68 |
-
if (recommendations && recommendations.length > 0) {
|
69 |
-
query.push({ id: { $in: recommendations } });
|
70 |
-
}
|
71 |
-
|
72 |
-
const [listings, tLisitngsCount] = await Promise.all([
|
73 |
-
this.propertyRepository
|
74 |
-
.find({ $and: [...query] })
|
75 |
-
.sort({ createdAt: -1 })
|
76 |
-
.skip(resPerPage * (page - 1))
|
77 |
-
.limit(resPerPage)
|
78 |
-
.exec(),
|
79 |
-
this.propertyRepository.countDocuments({ $and: [...query] }).exec(),
|
80 |
-
]);
|
81 |
-
|
82 |
-
return {
|
83 |
-
listings,
|
84 |
-
current_page: page,
|
85 |
-
pages: Math.ceil(tLisitngsCount / resPerPage),
|
86 |
-
total_listings: tLisitngsCount,
|
87 |
-
per_page: resPerPage,
|
88 |
-
};
|
89 |
-
}
|
90 |
-
}
|
|
|
1 |
+
import { Inject, Injectable } from '@nestjs/common';
|
2 |
+
import { Model, Types } from 'mongoose';
|
3 |
+
import { sharedCrudService } from '../shared/sharedCrud.services';
|
4 |
+
import { PROPERTY_REPOSITORY } from 'src/constants';
|
5 |
+
import { IPropertyDocument } from './property.schema';
|
6 |
+
|
7 |
+
@Injectable()
|
8 |
+
export class PropertyService extends sharedCrudService {
|
9 |
+
constructor(
|
10 |
+
@Inject(PROPERTY_REPOSITORY)
|
11 |
+
readonly propertyRepository: Model<IPropertyDocument>,
|
12 |
+
) {
|
13 |
+
super(propertyRepository);
|
14 |
+
}
|
15 |
+
|
16 |
+
async propertyLisitng(
|
17 |
+
page: number,
|
18 |
+
resPerPage: number,
|
19 |
+
search: string,
|
20 |
+
): Promise<any> {
|
21 |
+
const query = [];
|
22 |
+
query.push({ id: { $exists: true } });
|
23 |
+
|
24 |
+
if (search) query.push({ title: { $regex: search, $options: 'i' } });
|
25 |
+
|
26 |
+
const [listings, tLisitngsCount] = await Promise.all([
|
27 |
+
this.propertyRepository
|
28 |
+
.find({ $and: [...query] })
|
29 |
+
.sort({ createdAt: -1 })
|
30 |
+
.skip(resPerPage * (page - 1))
|
31 |
+
.limit(resPerPage)
|
32 |
+
.exec(),
|
33 |
+
this.propertyRepository.countDocuments({ $and: [...query] }).exec(),
|
34 |
+
]);
|
35 |
+
|
36 |
+
return {
|
37 |
+
listings,
|
38 |
+
current_page: page,
|
39 |
+
pages: Math.ceil(tLisitngsCount / resPerPage),
|
40 |
+
total_listings: tLisitngsCount,
|
41 |
+
per_page: resPerPage,
|
42 |
+
};
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* It will recieve array of IDs to get the recommended listing
|
47 |
+
* @param page
|
48 |
+
* @param resPerPage
|
49 |
+
* @param search
|
50 |
+
* @returns
|
51 |
+
*/
|
52 |
+
async propertyLisitngRecommendedSearch(
|
53 |
+
page: number,
|
54 |
+
resPerPage: number,
|
55 |
+
recommendations: string[],
|
56 |
+
): Promise<any> {
|
57 |
+
const query = [];
|
58 |
+
|
59 |
+
// Check if recommendations is a string and split it into an array
|
60 |
+
if (typeof recommendations === 'string') {
|
61 |
+
//@ts-ignore
|
62 |
+
recommendations = recommendations.split(',');
|
63 |
+
}
|
64 |
+
|
65 |
+
query.push({ id: { $exists: true } });
|
66 |
+
|
67 |
+
// Modify the query to handle an array of IDs
|
68 |
+
if (recommendations && recommendations.length > 0) {
|
69 |
+
query.push({ id: { $in: recommendations } });
|
70 |
+
}
|
71 |
+
|
72 |
+
const [listings, tLisitngsCount] = await Promise.all([
|
73 |
+
this.propertyRepository
|
74 |
+
.find({ $and: [...query] })
|
75 |
+
.sort({ createdAt: -1 })
|
76 |
+
.skip(resPerPage * (page - 1))
|
77 |
+
.limit(resPerPage)
|
78 |
+
.exec(),
|
79 |
+
this.propertyRepository.countDocuments({ $and: [...query] }).exec(),
|
80 |
+
]);
|
81 |
+
|
82 |
+
return {
|
83 |
+
listings,
|
84 |
+
current_page: page,
|
85 |
+
pages: Math.ceil(tLisitngsCount / resPerPage),
|
86 |
+
total_listings: tLisitngsCount,
|
87 |
+
per_page: resPerPage,
|
88 |
+
};
|
89 |
+
}
|
90 |
+
}
|
src/modules/shared/common.service.ts
CHANGED
@@ -1,27 +1,27 @@
|
|
1 |
-
import { en } from 'src/locales/en';
|
2 |
-
import { I18nResolver } from 'i18n-ts';
|
3 |
-
import { Inject, Injectable } from '@nestjs/common';
|
4 |
-
import * as constants from '../../constants'
|
5 |
-
|
6 |
-
const i18n = {
|
7 |
-
en: en,
|
8 |
-
default: en,
|
9 |
-
};
|
10 |
-
|
11 |
-
@Injectable()
|
12 |
-
export class CommonServices{
|
13 |
-
public messages = new I18nResolver(i18n, 'en').translation;
|
14 |
-
public constants = constants;
|
15 |
-
constructor() {
|
16 |
-
}
|
17 |
-
|
18 |
-
/**
|
19 |
-
* name
|
20 |
-
*/
|
21 |
-
public sendResponse = (mesage, data, status, res) => {
|
22 |
-
return res.status(status).json({
|
23 |
-
message: mesage,
|
24 |
-
data: data,
|
25 |
-
});
|
26 |
-
};
|
27 |
}
|
|
|
1 |
+
import { en } from 'src/locales/en';
|
2 |
+
import { I18nResolver } from 'i18n-ts';
|
3 |
+
import { Inject, Injectable } from '@nestjs/common';
|
4 |
+
import * as constants from '../../constants'
|
5 |
+
|
6 |
+
const i18n = {
|
7 |
+
en: en,
|
8 |
+
default: en,
|
9 |
+
};
|
10 |
+
|
11 |
+
@Injectable()
|
12 |
+
export class CommonServices{
|
13 |
+
public messages = new I18nResolver(i18n, 'en').translation;
|
14 |
+
public constants = constants;
|
15 |
+
constructor() {
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* name
|
20 |
+
*/
|
21 |
+
public sendResponse = (mesage, data, status, res) => {
|
22 |
+
return res.status(status).json({
|
23 |
+
message: mesage,
|
24 |
+
data: data,
|
25 |
+
});
|
26 |
+
};
|
27 |
}
|
src/modules/shared/sharedCrud.services.ts
CHANGED
@@ -1,28 +1,28 @@
|
|
1 |
-
import { Injectable } from '@nestjs/common';
|
2 |
-
|
3 |
-
@Injectable()
|
4 |
-
export class sharedCrudService {
|
5 |
-
private service;
|
6 |
-
constructor(private serviceObj) {
|
7 |
-
// super();
|
8 |
-
this.service = serviceObj;
|
9 |
-
}
|
10 |
-
sharedCreate = (body) => {
|
11 |
-
return this.service.create(body);
|
12 |
-
};
|
13 |
-
sharedUpdate = (clause, body) => {
|
14 |
-
return this.service.updateOne(clause, body);
|
15 |
-
};
|
16 |
-
sharedFindOne = (clause) => {
|
17 |
-
return this.service.findOne(clause);
|
18 |
-
};
|
19 |
-
sharedFindOneAndUpdate = (clause, body, options) => {
|
20 |
-
return this.service.findOneAndUpdate(clause, body, options);
|
21 |
-
};
|
22 |
-
sharedFind = (clause) => {
|
23 |
-
return this.service.find(clause);
|
24 |
-
};
|
25 |
-
sharedDelete = (clause) => {
|
26 |
-
return this.service.deleteOne(clause);
|
27 |
-
};
|
28 |
-
}
|
|
|
1 |
+
import { Injectable } from '@nestjs/common';
|
2 |
+
|
3 |
+
@Injectable()
|
4 |
+
export class sharedCrudService {
|
5 |
+
private service;
|
6 |
+
constructor(private serviceObj) {
|
7 |
+
// super();
|
8 |
+
this.service = serviceObj;
|
9 |
+
}
|
10 |
+
sharedCreate = (body) => {
|
11 |
+
return this.service.create(body);
|
12 |
+
};
|
13 |
+
sharedUpdate = (clause, body) => {
|
14 |
+
return this.service.updateOne(clause, body);
|
15 |
+
};
|
16 |
+
sharedFindOne = (clause) => {
|
17 |
+
return this.service.findOne(clause);
|
18 |
+
};
|
19 |
+
sharedFindOneAndUpdate = (clause, body, options) => {
|
20 |
+
return this.service.findOneAndUpdate(clause, body, options);
|
21 |
+
};
|
22 |
+
sharedFind = (clause) => {
|
23 |
+
return this.service.find(clause);
|
24 |
+
};
|
25 |
+
sharedDelete = (clause) => {
|
26 |
+
return this.service.deleteOne(clause);
|
27 |
+
};
|
28 |
+
}
|
src/modules/user/user.controller.ts
CHANGED
@@ -1,49 +1,49 @@
|
|
1 |
-
import {
|
2 |
-
Body,
|
3 |
-
Controller,
|
4 |
-
Get,
|
5 |
-
HttpStatus,
|
6 |
-
Post,
|
7 |
-
Query,
|
8 |
-
Req,
|
9 |
-
Res,
|
10 |
-
UseGuards,
|
11 |
-
} from '@nestjs/common';
|
12 |
-
import { UserService } from './user.service';
|
13 |
-
import { Response } from 'express';
|
14 |
-
import { CommonServices } from '../shared/common.service';
|
15 |
-
import { JwtAuthGuard } from '../auth/jwt-auth.guard'
|
16 |
-
|
17 |
-
|
18 |
-
@Controller('user')
|
19 |
-
export class UserController extends CommonServices {
|
20 |
-
constructor(
|
21 |
-
private readonly userService: UserService,
|
22 |
-
) {
|
23 |
-
super();
|
24 |
-
}
|
25 |
-
|
26 |
-
@Get('')
|
27 |
-
@UseGuards(JwtAuthGuard)
|
28 |
-
async getUserDetails(@Res() res: Response, @Req() req): Promise<any> {
|
29 |
-
try {
|
30 |
-
const response = await this.userService.userRepository.findById(
|
31 |
-
req.user.userId,
|
32 |
-
);
|
33 |
-
|
34 |
-
return this.sendResponse(
|
35 |
-
this.messages.Success,
|
36 |
-
response ?? 'NOT_FOUND',
|
37 |
-
HttpStatus.OK,
|
38 |
-
res,
|
39 |
-
);
|
40 |
-
} catch (error) {
|
41 |
-
return this.sendResponse(
|
42 |
-
'Internal server Error',
|
43 |
-
{},
|
44 |
-
HttpStatus.INTERNAL_SERVER_ERROR,
|
45 |
-
res,
|
46 |
-
);
|
47 |
-
}
|
48 |
-
}
|
49 |
-
}
|
|
|
1 |
+
import {
|
2 |
+
Body,
|
3 |
+
Controller,
|
4 |
+
Get,
|
5 |
+
HttpStatus,
|
6 |
+
Post,
|
7 |
+
Query,
|
8 |
+
Req,
|
9 |
+
Res,
|
10 |
+
UseGuards,
|
11 |
+
} from '@nestjs/common';
|
12 |
+
import { UserService } from './user.service';
|
13 |
+
import { Response } from 'express';
|
14 |
+
import { CommonServices } from '../shared/common.service';
|
15 |
+
import { JwtAuthGuard } from '../auth/jwt-auth.guard'
|
16 |
+
|
17 |
+
|
18 |
+
@Controller('user')
|
19 |
+
export class UserController extends CommonServices {
|
20 |
+
constructor(
|
21 |
+
private readonly userService: UserService,
|
22 |
+
) {
|
23 |
+
super();
|
24 |
+
}
|
25 |
+
|
26 |
+
@Get('')
|
27 |
+
@UseGuards(JwtAuthGuard)
|
28 |
+
async getUserDetails(@Res() res: Response, @Req() req): Promise<any> {
|
29 |
+
try {
|
30 |
+
const response = await this.userService.userRepository.findById(
|
31 |
+
req.user.userId,
|
32 |
+
);
|
33 |
+
|
34 |
+
return this.sendResponse(
|
35 |
+
this.messages.Success,
|
36 |
+
response ?? 'NOT_FOUND',
|
37 |
+
HttpStatus.OK,
|
38 |
+
res,
|
39 |
+
);
|
40 |
+
} catch (error) {
|
41 |
+
return this.sendResponse(
|
42 |
+
'Internal server Error',
|
43 |
+
{},
|
44 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
45 |
+
res,
|
46 |
+
);
|
47 |
+
}
|
48 |
+
}
|
49 |
+
}
|
src/modules/user/user.dto.ts
CHANGED
@@ -1,55 +1,55 @@
|
|
1 |
-
import {
|
2 |
-
IsEmail,
|
3 |
-
IsNotEmpty,
|
4 |
-
IsOptional,
|
5 |
-
IsString,
|
6 |
-
ValidateNested,
|
7 |
-
} from 'class-validator';
|
8 |
-
export class UpdateProfileDto {
|
9 |
-
@IsOptional()
|
10 |
-
firstName: string;
|
11 |
-
|
12 |
-
@IsOptional()
|
13 |
-
lastName: string;
|
14 |
-
|
15 |
-
@IsOptional()
|
16 |
-
username: string;
|
17 |
-
|
18 |
-
@IsOptional()
|
19 |
-
email: string;
|
20 |
-
|
21 |
-
avatar: any;
|
22 |
-
ats: any;
|
23 |
-
}
|
24 |
-
|
25 |
-
export class updatePasswordDto {
|
26 |
-
@IsNotEmpty()
|
27 |
-
password: string;
|
28 |
-
|
29 |
-
@IsNotEmpty()
|
30 |
-
newPassword: string;
|
31 |
-
|
32 |
-
@IsNotEmpty()
|
33 |
-
confirmPassword: string;
|
34 |
-
}
|
35 |
-
|
36 |
-
export class addRemoveFriendDto {
|
37 |
-
@IsNotEmpty()
|
38 |
-
userId: string;
|
39 |
-
|
40 |
-
@IsNotEmpty()
|
41 |
-
type: string;
|
42 |
-
}
|
43 |
-
export class validateEmailDto {
|
44 |
-
@IsString()
|
45 |
-
identifier: string;
|
46 |
-
}
|
47 |
-
export class NewsletterDto {
|
48 |
-
@IsString()
|
49 |
-
email: string;
|
50 |
-
}
|
51 |
-
export class validateEmailRegDto {
|
52 |
-
@IsString()
|
53 |
-
@IsEmail()
|
54 |
-
email: string;
|
55 |
-
}
|
|
|
1 |
+
import {
|
2 |
+
IsEmail,
|
3 |
+
IsNotEmpty,
|
4 |
+
IsOptional,
|
5 |
+
IsString,
|
6 |
+
ValidateNested,
|
7 |
+
} from 'class-validator';
|
8 |
+
export class UpdateProfileDto {
|
9 |
+
@IsOptional()
|
10 |
+
firstName: string;
|
11 |
+
|
12 |
+
@IsOptional()
|
13 |
+
lastName: string;
|
14 |
+
|
15 |
+
@IsOptional()
|
16 |
+
username: string;
|
17 |
+
|
18 |
+
@IsOptional()
|
19 |
+
email: string;
|
20 |
+
|
21 |
+
avatar: any;
|
22 |
+
ats: any;
|
23 |
+
}
|
24 |
+
|
25 |
+
export class updatePasswordDto {
|
26 |
+
@IsNotEmpty()
|
27 |
+
password: string;
|
28 |
+
|
29 |
+
@IsNotEmpty()
|
30 |
+
newPassword: string;
|
31 |
+
|
32 |
+
@IsNotEmpty()
|
33 |
+
confirmPassword: string;
|
34 |
+
}
|
35 |
+
|
36 |
+
export class addRemoveFriendDto {
|
37 |
+
@IsNotEmpty()
|
38 |
+
userId: string;
|
39 |
+
|
40 |
+
@IsNotEmpty()
|
41 |
+
type: string;
|
42 |
+
}
|
43 |
+
export class validateEmailDto {
|
44 |
+
@IsString()
|
45 |
+
identifier: string;
|
46 |
+
}
|
47 |
+
export class NewsletterDto {
|
48 |
+
@IsString()
|
49 |
+
email: string;
|
50 |
+
}
|
51 |
+
export class validateEmailRegDto {
|
52 |
+
@IsString()
|
53 |
+
@IsEmail()
|
54 |
+
email: string;
|
55 |
+
}
|
src/modules/user/user.module.ts
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
-
import { Module } from '@nestjs/common';
|
2 |
-
import { UserController } from './user.controller';
|
3 |
-
import { UserService } from './user.service';
|
4 |
-
import { DatabaseModule } from '../database/database.module';
|
5 |
-
import { AuthService } from '../auth/auth.service';
|
6 |
-
import { JwtService } from '@nestjs/jwt';
|
7 |
-
@Module({
|
8 |
-
imports: [DatabaseModule],
|
9 |
-
controllers: [UserController],
|
10 |
-
providers: [UserService, AuthService, JwtService],
|
11 |
-
exports: [UserService, AuthService, JwtService],
|
12 |
-
})
|
13 |
-
export class UserModule {}
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { UserController } from './user.controller';
|
3 |
+
import { UserService } from './user.service';
|
4 |
+
import { DatabaseModule } from '../database/database.module';
|
5 |
+
import { AuthService } from '../auth/auth.service';
|
6 |
+
import { JwtService } from '@nestjs/jwt';
|
7 |
+
@Module({
|
8 |
+
imports: [DatabaseModule],
|
9 |
+
controllers: [UserController],
|
10 |
+
providers: [UserService, AuthService, JwtService],
|
11 |
+
exports: [UserService, AuthService, JwtService],
|
12 |
+
})
|
13 |
+
export class UserModule {}
|
src/modules/user/user.provider.ts
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
-
import { Connection } from 'mongoose';
|
2 |
-
import { USERS, USER_REPOSITORY } from 'src/constants';
|
3 |
-
import { UserSchema } from './user.schema';
|
4 |
-
|
5 |
-
export const usersProviders = [
|
6 |
-
{
|
7 |
-
provide: USER_REPOSITORY,
|
8 |
-
useFactory: (connection: Connection) => connection.model(USERS, UserSchema),
|
9 |
-
inject: ['DATABASE_CONNECTION'],
|
10 |
-
},
|
11 |
-
];
|
|
|
1 |
+
import { Connection } from 'mongoose';
|
2 |
+
import { USERS, USER_REPOSITORY } from 'src/constants';
|
3 |
+
import { UserSchema } from './user.schema';
|
4 |
+
|
5 |
+
export const usersProviders = [
|
6 |
+
{
|
7 |
+
provide: USER_REPOSITORY,
|
8 |
+
useFactory: (connection: Connection) => connection.model(USERS, UserSchema),
|
9 |
+
inject: ['DATABASE_CONNECTION'],
|
10 |
+
},
|
11 |
+
];
|
src/modules/user/user.schema.ts
CHANGED
@@ -1,78 +1,78 @@
|
|
1 |
-
import * as mongoose from 'mongoose';
|
2 |
-
import { Document } from 'mongoose';
|
3 |
-
import { USERS } from 'src/constants';
|
4 |
-
|
5 |
-
export interface IUserDocument extends Document {
|
6 |
-
name?: string;
|
7 |
-
username?: string;
|
8 |
-
email?: string;
|
9 |
-
avatar?: string;
|
10 |
-
isActive?: boolean;
|
11 |
-
isDeleted: boolean;
|
12 |
-
createdAt?: Date;
|
13 |
-
updatedAt?: Date;
|
14 |
-
deletedAt?: Date;
|
15 |
-
roles?: Array<string>;
|
16 |
-
password?: string;
|
17 |
-
session_id?: number;
|
18 |
-
login_location?: string;
|
19 |
-
preferred_location?: string;
|
20 |
-
preferred_listing_type?: string;
|
21 |
-
property_id?: string;
|
22 |
-
page_visit_duration?: number;
|
23 |
-
carousel_button_click_result_page?: number;
|
24 |
-
next_image_clicks_listing_page?: number;
|
25 |
-
read_more_click_result_page?: number;
|
26 |
-
read_more_click_listing_page?: number;
|
27 |
-
clicked_more_pictures?: number;
|
28 |
-
click_rate?: number;
|
29 |
-
bounce?: number;
|
30 |
-
title?: string;
|
31 |
-
price?: string;
|
32 |
-
bedrooms_?: string;
|
33 |
-
bathrooms?: string;
|
34 |
-
living_space_sqm?: string;
|
35 |
-
pool_?: number;
|
36 |
-
}
|
37 |
-
|
38 |
-
const UserSchema = new mongoose.Schema<IUserDocument>(
|
39 |
-
{
|
40 |
-
name: { type: String },
|
41 |
-
username: { type: String },
|
42 |
-
email: { type: String, unique: true },
|
43 |
-
avatar: { type: String },
|
44 |
-
isActive: { type: Boolean, default: true },
|
45 |
-
isDeleted: { type: Boolean, default: false },
|
46 |
-
roles: [{ type: String, required: true, default: 'user' }],
|
47 |
-
password: { type: String, required: false },
|
48 |
-
// New fields added from IUserDocument
|
49 |
-
session_id: { type: Number },
|
50 |
-
login_location: { type: String },
|
51 |
-
preferred_location: { type: String },
|
52 |
-
preferred_listing_type: { type: String },
|
53 |
-
property_id: { type: String },
|
54 |
-
page_visit_duration: { type: Number, default: 0 },
|
55 |
-
carousel_button_click_result_page: { type: Number, default: 0 },
|
56 |
-
next_image_clicks_listing_page: { type: Number, default: 0 },
|
57 |
-
read_more_click_result_page: { type: Number, default: 0 },
|
58 |
-
read_more_click_listing_page: { type: Number, default: 0 },
|
59 |
-
clicked_more_pictures: { type: Number, default: 0 },
|
60 |
-
click_rate: { type: Number, default: 0 },
|
61 |
-
bounce: { type: Number, default: 0 },
|
62 |
-
title: { type: String },
|
63 |
-
price: { type: String },
|
64 |
-
bedrooms_: { type: String },
|
65 |
-
bathrooms: { type: String },
|
66 |
-
living_space_sqm: { type: String },
|
67 |
-
pool_: { type: Number },
|
68 |
-
|
69 |
-
createdAt: { type: Date, default: Date.now },
|
70 |
-
updatedAt: { type: Date, default: Date.now },
|
71 |
-
deletedAt: { type: Date },
|
72 |
-
},
|
73 |
-
{
|
74 |
-
toJSON: { versionKey: false },
|
75 |
-
},
|
76 |
-
);
|
77 |
-
|
78 |
-
export { UserSchema };
|
|
|
1 |
+
import * as mongoose from 'mongoose';
|
2 |
+
import { Document } from 'mongoose';
|
3 |
+
import { USERS } from 'src/constants';
|
4 |
+
|
5 |
+
export interface IUserDocument extends Document {
|
6 |
+
name?: string;
|
7 |
+
username?: string;
|
8 |
+
email?: string;
|
9 |
+
avatar?: string;
|
10 |
+
isActive?: boolean;
|
11 |
+
isDeleted: boolean;
|
12 |
+
createdAt?: Date;
|
13 |
+
updatedAt?: Date;
|
14 |
+
deletedAt?: Date;
|
15 |
+
roles?: Array<string>;
|
16 |
+
password?: string;
|
17 |
+
session_id?: number;
|
18 |
+
login_location?: string;
|
19 |
+
preferred_location?: string;
|
20 |
+
preferred_listing_type?: string;
|
21 |
+
property_id?: string;
|
22 |
+
page_visit_duration?: number;
|
23 |
+
carousel_button_click_result_page?: number;
|
24 |
+
next_image_clicks_listing_page?: number;
|
25 |
+
read_more_click_result_page?: number;
|
26 |
+
read_more_click_listing_page?: number;
|
27 |
+
clicked_more_pictures?: number;
|
28 |
+
click_rate?: number;
|
29 |
+
bounce?: number;
|
30 |
+
title?: string;
|
31 |
+
price?: string;
|
32 |
+
bedrooms_?: string;
|
33 |
+
bathrooms?: string;
|
34 |
+
living_space_sqm?: string;
|
35 |
+
pool_?: number;
|
36 |
+
}
|
37 |
+
|
38 |
+
const UserSchema = new mongoose.Schema<IUserDocument>(
|
39 |
+
{
|
40 |
+
name: { type: String },
|
41 |
+
username: { type: String },
|
42 |
+
email: { type: String, unique: true },
|
43 |
+
avatar: { type: String },
|
44 |
+
isActive: { type: Boolean, default: true },
|
45 |
+
isDeleted: { type: Boolean, default: false },
|
46 |
+
roles: [{ type: String, required: true, default: 'user' }],
|
47 |
+
password: { type: String, required: false },
|
48 |
+
// New fields added from IUserDocument
|
49 |
+
session_id: { type: Number },
|
50 |
+
login_location: { type: String },
|
51 |
+
preferred_location: { type: String },
|
52 |
+
preferred_listing_type: { type: String },
|
53 |
+
property_id: { type: String },
|
54 |
+
page_visit_duration: { type: Number, default: 0 },
|
55 |
+
carousel_button_click_result_page: { type: Number, default: 0 },
|
56 |
+
next_image_clicks_listing_page: { type: Number, default: 0 },
|
57 |
+
read_more_click_result_page: { type: Number, default: 0 },
|
58 |
+
read_more_click_listing_page: { type: Number, default: 0 },
|
59 |
+
clicked_more_pictures: { type: Number, default: 0 },
|
60 |
+
click_rate: { type: Number, default: 0 },
|
61 |
+
bounce: { type: Number, default: 0 },
|
62 |
+
title: { type: String },
|
63 |
+
price: { type: String },
|
64 |
+
bedrooms_: { type: String },
|
65 |
+
bathrooms: { type: String },
|
66 |
+
living_space_sqm: { type: String },
|
67 |
+
pool_: { type: Number },
|
68 |
+
|
69 |
+
createdAt: { type: Date, default: Date.now },
|
70 |
+
updatedAt: { type: Date, default: Date.now },
|
71 |
+
deletedAt: { type: Date },
|
72 |
+
},
|
73 |
+
{
|
74 |
+
toJSON: { versionKey: false },
|
75 |
+
},
|
76 |
+
);
|
77 |
+
|
78 |
+
export { UserSchema };
|
src/modules/user/user.service.ts
CHANGED
@@ -1,25 +1,25 @@
|
|
1 |
-
import { Inject, Injectable } from '@nestjs/common';
|
2 |
-
import { Model, Types } from 'mongoose';
|
3 |
-
import { USER_REPOSITORY } from 'src/constants';
|
4 |
-
import { IUserDocument } from './user.schema';
|
5 |
-
import { sharedCrudService } from '../shared/sharedCrud.services';
|
6 |
-
|
7 |
-
interface UserDocument extends IUserDocument {
|
8 |
-
_id: Types.ObjectId;
|
9 |
-
}
|
10 |
-
|
11 |
-
@Injectable()
|
12 |
-
export class UserService extends sharedCrudService {
|
13 |
-
constructor(
|
14 |
-
@Inject(USER_REPOSITORY) readonly userRepository: Model<IUserDocument>,
|
15 |
-
) {
|
16 |
-
super(userRepository);
|
17 |
-
}
|
18 |
-
|
19 |
-
async getUserData(reqUser: any) {
|
20 |
-
const user = await this.userRepository.findById({
|
21 |
-
_id: reqUser._id,
|
22 |
-
});
|
23 |
-
return user;
|
24 |
-
}
|
25 |
-
}
|
|
|
1 |
+
import { Inject, Injectable } from '@nestjs/common';
|
2 |
+
import { Model, Types } from 'mongoose';
|
3 |
+
import { USER_REPOSITORY } from 'src/constants';
|
4 |
+
import { IUserDocument } from './user.schema';
|
5 |
+
import { sharedCrudService } from '../shared/sharedCrud.services';
|
6 |
+
|
7 |
+
interface UserDocument extends IUserDocument {
|
8 |
+
_id: Types.ObjectId;
|
9 |
+
}
|
10 |
+
|
11 |
+
@Injectable()
|
12 |
+
export class UserService extends sharedCrudService {
|
13 |
+
constructor(
|
14 |
+
@Inject(USER_REPOSITORY) readonly userRepository: Model<IUserDocument>,
|
15 |
+
) {
|
16 |
+
super(userRepository);
|
17 |
+
}
|
18 |
+
|
19 |
+
async getUserData(reqUser: any) {
|
20 |
+
const user = await this.userRepository.findById({
|
21 |
+
_id: reqUser._id,
|
22 |
+
});
|
23 |
+
return user;
|
24 |
+
}
|
25 |
+
}
|
test/app.e2e-spec.ts
CHANGED
@@ -1,24 +1,24 @@
|
|
1 |
-
import { Test, TestingModule } from '@nestjs/testing';
|
2 |
-
import { INestApplication } from '@nestjs/common';
|
3 |
-
import * as request from 'supertest';
|
4 |
-
import { AppModule } from './../src/app.module';
|
5 |
-
|
6 |
-
describe('AppController (e2e)', () => {
|
7 |
-
let app: INestApplication;
|
8 |
-
|
9 |
-
beforeEach(async () => {
|
10 |
-
const moduleFixture: TestingModule = await Test.createTestingModule({
|
11 |
-
imports: [AppModule],
|
12 |
-
}).compile();
|
13 |
-
|
14 |
-
app = moduleFixture.createNestApplication();
|
15 |
-
await app.init();
|
16 |
-
});
|
17 |
-
|
18 |
-
it('/ (GET)', () => {
|
19 |
-
return request(app.getHttpServer())
|
20 |
-
.get('/')
|
21 |
-
.expect(200)
|
22 |
-
.expect('Hello World!');
|
23 |
-
});
|
24 |
-
});
|
|
|
1 |
+
import { Test, TestingModule } from '@nestjs/testing';
|
2 |
+
import { INestApplication } from '@nestjs/common';
|
3 |
+
import * as request from 'supertest';
|
4 |
+
import { AppModule } from './../src/app.module';
|
5 |
+
|
6 |
+
describe('AppController (e2e)', () => {
|
7 |
+
let app: INestApplication;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
const moduleFixture: TestingModule = await Test.createTestingModule({
|
11 |
+
imports: [AppModule],
|
12 |
+
}).compile();
|
13 |
+
|
14 |
+
app = moduleFixture.createNestApplication();
|
15 |
+
await app.init();
|
16 |
+
});
|
17 |
+
|
18 |
+
it('/ (GET)', () => {
|
19 |
+
return request(app.getHttpServer())
|
20 |
+
.get('/')
|
21 |
+
.expect(200)
|
22 |
+
.expect('Hello World!');
|
23 |
+
});
|
24 |
+
});
|
test/jest-e2e.json
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
-
{
|
2 |
-
"moduleFileExtensions": ["js", "json", "ts"],
|
3 |
-
"rootDir": ".",
|
4 |
-
"testEnvironment": "node",
|
5 |
-
"testRegex": ".e2e-spec.ts$",
|
6 |
-
"transform": {
|
7 |
-
"^.+\\.(t|j)s$": "ts-jest"
|
8 |
-
}
|
9 |
-
}
|
|
|
1 |
+
{
|
2 |
+
"moduleFileExtensions": ["js", "json", "ts"],
|
3 |
+
"rootDir": ".",
|
4 |
+
"testEnvironment": "node",
|
5 |
+
"testRegex": ".e2e-spec.ts$",
|
6 |
+
"transform": {
|
7 |
+
"^.+\\.(t|j)s$": "ts-jest"
|
8 |
+
}
|
9 |
+
}
|
tsconfig.build.json
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
{
|
2 |
-
"extends": "./tsconfig.json",
|
3 |
-
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
4 |
-
}
|
|
|
1 |
+
{
|
2 |
+
"extends": "./tsconfig.json",
|
3 |
+
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
4 |
+
}
|
tsconfig.json
CHANGED
@@ -1,21 +1,21 @@
|
|
1 |
-
{
|
2 |
-
"compilerOptions": {
|
3 |
-
"module": "commonjs",
|
4 |
-
"declaration": true,
|
5 |
-
"removeComments": true,
|
6 |
-
"emitDecoratorMetadata": true,
|
7 |
-
"experimentalDecorators": true,
|
8 |
-
"allowSyntheticDefaultImports": true,
|
9 |
-
"target": "ES2021",
|
10 |
-
"sourceMap": true,
|
11 |
-
"outDir": "./dist",
|
12 |
-
"baseUrl": "./",
|
13 |
-
"incremental": true,
|
14 |
-
"skipLibCheck": true,
|
15 |
-
"strictNullChecks": false,
|
16 |
-
"noImplicitAny": false,
|
17 |
-
"strictBindCallApply": false,
|
18 |
-
"forceConsistentCasingInFileNames": false,
|
19 |
-
"noFallthroughCasesInSwitch": false
|
20 |
-
}
|
21 |
-
}
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"module": "commonjs",
|
4 |
+
"declaration": true,
|
5 |
+
"removeComments": true,
|
6 |
+
"emitDecoratorMetadata": true,
|
7 |
+
"experimentalDecorators": true,
|
8 |
+
"allowSyntheticDefaultImports": true,
|
9 |
+
"target": "ES2021",
|
10 |
+
"sourceMap": true,
|
11 |
+
"outDir": "./dist",
|
12 |
+
"baseUrl": "./",
|
13 |
+
"incremental": true,
|
14 |
+
"skipLibCheck": true,
|
15 |
+
"strictNullChecks": false,
|
16 |
+
"noImplicitAny": false,
|
17 |
+
"strictBindCallApply": false,
|
18 |
+
"forceConsistentCasingInFileNames": false,
|
19 |
+
"noFallthroughCasesInSwitch": false
|
20 |
+
}
|
21 |
+
}
|
vercel.json
CHANGED
@@ -1,16 +1,16 @@
|
|
1 |
-
{
|
2 |
-
"version": 2,
|
3 |
-
"builds": [
|
4 |
-
{
|
5 |
-
"src": "
|
6 |
-
"use": "@vercel/node"
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
"
|
13 |
-
"
|
14 |
-
}
|
15 |
-
]
|
16 |
-
}
|
|
|
1 |
+
{
|
2 |
+
"version": 2,
|
3 |
+
"builds": [
|
4 |
+
{
|
5 |
+
"src": "dist/main.js",
|
6 |
+
"use": "@vercel/node"
|
7 |
+
}
|
8 |
+
],
|
9 |
+
"routes": [
|
10 |
+
{
|
11 |
+
"src": "/(.*)",
|
12 |
+
"dest": "dist/main.js",
|
13 |
+
"methods": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]
|
14 |
+
}
|
15 |
+
]
|
16 |
+
}
|