master 39fa1770ab67 cached
120 files
145.6 KB
45.3k tokens
284 symbols
1 requests
Download .txt
Repository: chnirt/nestjs-restful-best-practice
Branch: master
Commit: 39fa1770ab67
Files: 120
Total size: 145.6 KB

Directory structure:
gitextract_xs5wvu5j/

├── .gitignore
├── .prettierrc
├── .well-known/
│   └── acme-challenge/
│       └── 3fEzNe2klZ1GLASfExbFL6LPbdmqZf2YmefYhRT-kwk
├── LICENSE
├── Procfile
├── README.md
├── nest-cli.json
├── package.json
├── src/
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   ├── assets/
│   │   └── templates/
│   │       └── udacity-index.html
│   ├── auth/
│   │   ├── auth.module.ts
│   │   ├── auth.service.ts
│   │   ├── facebook.strategy.ts
│   │   ├── google.strategy.ts
│   │   ├── jwt.strategy.ts
│   │   └── local.strategy.ts
│   ├── common/
│   │   ├── filters/
│   │   │   └── http-exception.filter.ts
│   │   ├── index.ts
│   │   ├── interceptors/
│   │   │   ├── exception.interceptor.ts
│   │   │   ├── http-cache.interceptor.ts
│   │   │   ├── logging.interceptor.ts
│   │   │   ├── timeout.interceptor.ts
│   │   │   └── transform.interceptor.ts
│   │   ├── middleware/
│   │   │   └── logger.middleware.ts
│   │   ├── pipes/
│   │   │   └── validation.pipe.ts
│   │   └── wiston/
│   │       └── index.ts
│   ├── config/
│   │   ├── cache/
│   │   │   └── index.ts
│   │   ├── index.ts
│   │   ├── logger/
│   │   │   └── index.ts
│   │   └── typeorm/
│   │       └── index.ts
│   ├── config.orm.ts
│   ├── environments/
│   │   └── index.ts
│   ├── main.ts
│   ├── modules/
│   │   ├── addresses/
│   │   │   ├── address.entity.ts
│   │   │   ├── addresses.controller.ts
│   │   │   ├── addresses.module.ts
│   │   │   ├── addresses.service.ts
│   │   │   ├── dto/
│   │   │   │   ├── create-address.dto.ts
│   │   │   │   └── query-address.dto.ts
│   │   │   └── enum/
│   │   │       └── address.enum.ts
│   │   ├── attendance/
│   │   │   ├── attendance.controller.ts
│   │   │   ├── attendance.module.ts
│   │   │   ├── attendance.service.ts
│   │   │   ├── dto/
│   │   │   │   ├── create-attendance.dto.ts
│   │   │   │   └── replace-attendance.dto.ts
│   │   │   └── entity/
│   │   │       └── attendance.entity.ts
│   │   ├── banners/
│   │   │   ├── banner.entity.ts
│   │   │   ├── banners.controller.ts
│   │   │   ├── banners.module.ts
│   │   │   ├── banners.service.ts
│   │   │   └── dto/
│   │   │       ├── create-banner.dto.ts
│   │   │       └── replace-banner.dto.ts
│   │   ├── bidders/
│   │   │   ├── bidders.controller.ts
│   │   │   ├── bidders.module.ts
│   │   │   └── bidders.service.ts
│   │   ├── chats/
│   │   │   ├── chats.gateway.ts
│   │   │   └── chats.module.ts
│   │   ├── classes/
│   │   │   ├── classes.controller.ts
│   │   │   ├── classes.module.ts
│   │   │   ├── classes.service.ts
│   │   │   ├── dto/
│   │   │   │   ├── create-class.dto.ts
│   │   │   │   └── replace-class.dto.ts
│   │   │   └── entity/
│   │   │       └── class.entity.ts
│   │   ├── connections/
│   │   │   ├── connection.entity.ts
│   │   │   ├── connections.controller.ts
│   │   │   ├── connections.module.ts
│   │   │   ├── connections.service.ts
│   │   │   ├── dto/
│   │   │   │   └── create-connection.dto.ts
│   │   │   └── enum/
│   │   │       └── connection.enum.ts
│   │   ├── deals/
│   │   │   ├── deal.entity.ts
│   │   │   ├── deals.controller.ts
│   │   │   ├── deals.module.ts
│   │   │   ├── deals.service.ts
│   │   │   ├── dto/
│   │   │   │   ├── create-deal.dto.ts
│   │   │   │   └── deal-response.dto.ts
│   │   │   ├── entity/
│   │   │   │   └── position.entity.ts
│   │   │   └── enum/
│   │   │       └── deal.enum.ts
│   │   ├── events/
│   │   │   ├── events.gateway.ts
│   │   │   └── events.module.ts
│   │   ├── students/
│   │   │   ├── dto/
│   │   │   │   ├── create-student.dto.ts
│   │   │   │   └── replace-student.dto.ts
│   │   │   ├── entity/
│   │   │   │   └── student.entity.ts
│   │   │   ├── students.controller.ts
│   │   │   ├── students.module.ts
│   │   │   └── students.service.ts
│   │   └── users/
│   │       ├── dto/
│   │       │   ├── create-user.dto.ts
│   │       │   ├── created-by.dto.ts
│   │       │   ├── error-response.dto.ts
│   │       │   ├── login-response.dto.ts
│   │       │   ├── login-user.dto.ts
│   │       │   ├── otp-response.dto.ts
│   │       │   ├── replace-user.dto.ts
│   │       │   ├── update-user.dto.ts
│   │       │   └── upload-response.dto.ts
│   │       ├── index.ts
│   │       ├── user.entity.ts
│   │       ├── users.controller.ts
│   │       ├── users.module.ts
│   │       └── users.service.ts
│   ├── shared/
│   │   ├── index.ts
│   │   └── upload/
│   │       └── index.ts
│   ├── terminus-options.service.ts
│   └── utils/
│       ├── index.ts
│       ├── password/
│       │   └── index.ts
│       └── uuid/
│           └── index.ts
├── ssl/
│   ├── ca_bundle.crt
│   ├── certificate.crt
│   └── private.key
├── static/
│   ├── index.html
│   ├── main.js
│   └── style.css
├── test/
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
├── tsconfig.json
├── tslint.json
└── webpack.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# compiled output
/dist
/node_modules

# others
package-lock.json
yarn.lock

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# next.js build output
.next


================================================
FILE: .prettierrc
================================================
{
	"printWidth": 80,
	"tabWidth": 2,
	"useTabs": true,
	"semi": false,
	"singleQuote": true,
	"trailingComma": "none",
	"bracketSpacing": true,
	"jsxBracketSameLine": false,
	"fluid": false
}


================================================
FILE: .well-known/acme-challenge/3fEzNe2klZ1GLASfExbFL6LPbdmqZf2YmefYhRT-kwk
================================================
3fEzNe2klZ1GLASfExbFL6LPbdmqZf2YmefYhRT-kwk.G8p-XYVA4Y6MBmzrD23IQJ2p48OttBbjGc68r6pxqWo

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Chnirt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Procfile
================================================
web: npm run start:prod

================================================
FILE: README.md
================================================
# nestjs-restful-best-practice


================================================
FILE: nest-cli.json
================================================
{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src"
}


================================================
FILE: package.json
================================================
{
	"name": "nestjs-restful-best-practice",
	"version": "0.0.1",
	"description": "",
	"author": "",
	"license": "MIT",
	"scripts": {
		"prebuild": "rimraf dist",
		"build": "NODE_ENV=production nest build",
		"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
		"start": "nest start",
		"start:dev": "nest start --watch",
		"start:debug": "nest start --debug --watch",
		"start:prod": "NODE_ENV=production node dist/main",
		"lint": "tslint -p tsconfig.json -c tslint.json",
		"test": "jest",
		"test:watch": "jest --watch",
		"test:cov": "jest --coverage",
		"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
		"test:e2e": "jest --config ./test/jest-e2e.json",
		"webpack": "NODE_ENV=development nest build --watch --webpack",
		"start:hmr": "node dist/main",
		"heroku-postbuild": "npm i --only=dev --no-shrinkwrap && npm run build",
		"doc": "npx compodoc --port 11045 -p tsconfig.json -s",
		"doc:serve": "npm run doc && npx compodoc -s"
	},
	"dependencies": {
		"@godaddy/terminus": "^4.2.1",
		"@nestjs/common": "^6.7.2",
		"@nestjs/core": "^6.7.2",
		"@nestjs/jwt": "^6.1.1",
		"@nestjs/passport": "^6.1.0",
		"@nestjs/platform-express": "^6.9.0",
		"@nestjs/platform-socket.io": "^6.9.0",
		"@nestjs/serve-static": "^1.0.1",
		"@nestjs/swagger": "^3.1.0",
		"@nestjs/terminus": "^6.5.2",
		"@nestjs/typeorm": "^6.2.0",
		"@nestjs/websockets": "^6.9.0",
		"@nestjsx/crud": "^4.2.0",
		"@nestjsx/crud-typeorm": "^4.2.0",
		"@types/mongodb": "^3.3.8",
		"bcrypt": "^3.0.6",
		"cache-manager": "^2.10.0",
		"class-transformer": "^0.2.3",
		"class-validator": "^0.11.0",
		"cloudinary": "1.16.0",
		"dotenv": "^8.2.0",
		"express-rate-limit": "^5.0.0",
		"geolib": "^3.1.0",
		"googleapis": "^45.0.0",
		"handlebars": "^4.5.2",
		"helmet": "^3.21.2",
		"http": "^0.0.0",
		"https": "^1.0.0",
		"mongodb": "^3.3.3",
		"nodemailer": "^6.3.1",
		"passport": "^0.4.0",
		"passport-facebook-token": "^3.3.0",
		"passport-google-oauth20": "^2.0.0",
		"passport-jwt": "^4.0.0",
		"passport-local": "^1.0.0",
		"reflect-metadata": "^0.1.13",
		"rimraf": "^3.0.0",
		"rxjs": "^6.5.3",
		"speakeasy": "^2.0.0",
		"swagger-ui-express": "^4.1.2",
		"typeorm": "^0.2.20",
		"uuid": "^3.3.3",
		"webpack-bundle-analyzer": "^3.6.0",
		"winston": "^3.2.1"
	},
	"devDependencies": {
		"@compodoc/compodoc": "^1.1.11",
		"@nestjs/cli": "^6.9.0",
		"@nestjs/schematics": "^6.7.0",
		"@nestjs/testing": "^6.7.1",
		"@types/bcrypt": "^3.0.0",
		"@types/dotenv": "^8.2.0",
		"@types/express": "^4.17.1",
		"@types/jest": "^24.0.18",
		"@types/node": "^12.7.5",
		"@types/passport-facebook-token": "^0.4.33",
		"@types/passport-jwt": "^3.0.2",
		"@types/passport-local": "^1.0.33",
		"@types/socket.io": "^2.1.4",
		"@types/speakeasy": "^2.0.5",
		"@types/supertest": "^2.0.8",
		"jest": "^24.9.0",
		"prettier": "^1.18.2",
		"progress-bar-webpack-plugin": "^1.12.1",
		"supertest": "^4.0.2",
		"ts-jest": "^24.1.0",
		"ts-loader": "^6.1.1",
		"ts-node": "^8.4.1",
		"tsconfig-paths": "^3.9.0",
		"tslint": "^5.20.0",
		"typescript": "^3.6.3",
		"webpack-node-externals": "^1.7.2"
	},
	"jest": {
		"moduleFileExtensions": [
			"js",
			"json",
			"ts"
		],
		"rootDir": "src",
		"testRegex": ".spec.ts$",
		"transform": {
			"^.+\\.(t|j)s$": "ts-jest"
		},
		"coverageDirectory": "./coverage",
		"testEnvironment": "node"
	}
}


================================================
FILE: src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';

describe('AppController', () => {
  let appController: AppController;

  beforeEach(async () => {
    const app: TestingModule = await Test.createTestingModule({
      controllers: [AppController],
      providers: [AppService],
    }).compile();

    appController = app.get<AppController>(AppController);
  });

  describe('root', () => {
    it('should return "Hello World!"', () => {
      expect(appController.getHello()).toBe('Hello World!');
    });
  });
});


================================================
FILE: src/app.controller.ts
================================================
import {
	Controller,
	Get,
	Request,
	Post,
	UseGuards,
	Param,
	Res,
	UseInterceptors,
	UploadedFile,
	CacheInterceptor,
	Body,
	Query,
	Logger
} from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport'
import {
	ApiBearerAuth,
	ApiConsumes,
	ApiImplicitFile,
	ApiImplicitBody,
	ApiUseTags,
	ApiOperation,
	ApiResponse,
	ApiImplicitQuery
} from '@nestjs/swagger'
import { FileInterceptor } from '@nestjs/platform-express'
import chalk from 'chalk'
import { AppService } from './app.service'
import { AuthService } from './auth/auth.service'
import { LoginUserDto } from './modules/users/dto/login-user.dto'
import { STATIC, SSL } from './environments'
import { uploadFile } from './shared/upload'
import { LoginResponseDto } from './modules/users/dto/login-response.dto'
import { ErrorResponseDto } from './modules/users/dto/error-response.dto'
import { UserEntity } from './modules/users/user.entity'
import { UploadResponseDto } from './modules/users/dto/upload-response.dto'
import { DealsService } from './modules/deals/deals.service'
import { DealResponseDto } from './modules/deals/dto/deal-response.dto'
import { DealType } from './modules/deals/enum/deal.enum'

@ApiResponse({
	status: 401,
	description: 'Unauthorized.',
	type: ErrorResponseDto
})
@ApiResponse({ status: 403, description: 'Forbidden.', type: ErrorResponseDto })
@ApiUseTags('basic')
@Controller()
@UseInterceptors(CacheInterceptor)
export class AppController {
	constructor(
		private readonly appService: AppService,
		private readonly authService: AuthService,
		private readonly dealService: DealsService
	) {}

	@Get()
	getHello(): string {
		return this.appService.getHello()
	}

	@Post('/req')
	postHello(@Request() req) {
		Logger.log(`🤬 ${chalk.hex('#87e8de').bold(`${req.body}`)}`, 'Check')
		return req.body
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: LoginResponseDto
	})
	@UseGuards(AuthGuard('local'))
	@ApiOperation({
		title: 'Retrieve one Acess token 👻'
	})
	@Post('login')
	@ApiImplicitBody({ name: 'input', type: LoginUserDto })
	login(@Request() req): Promise<LoginResponseDto> {
		return this.authService.login(req.user)
	}

	@ApiResponse({
		status: 200,
		description: 'The found profile',
		type: UserEntity
	})
	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Retrieve one Profile 👻'
	})
	@Get('profile')
	getProfile(@Request() req) {
		return req.user
	}

	@ApiResponse({
		status: 200,
		description: 'The found records',
		type: [DealResponseDto]
	})
	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Retrieve one My deal 👻'
	})
	@Get('myDeal')
	@ApiImplicitQuery({
		name: 'dealType',
		description: 'The dealType of the Deal',
		required: false,
		type: DealType,
		enum: ['Request', 'Offer']
	})
	getMyDeal(@Request() req, @Query() query) {
		const myDeal = this.dealService.findByUserId(req, query)
		return myDeal
	}

	@ApiResponse({
		status: 201,
		description: 'The record has been successfully created.',
		type: UploadResponseDto
	})
	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Create one File 👻'
	})
	@Post('upload')
	@ApiConsumes('multipart/form-data')
	@ApiImplicitFile({
		name: 'file',
		required: true
	})
	@UseInterceptors(FileInterceptor('file'))
	async uploadFile(@UploadedFile() file): Promise<UploadResponseDto> {
		const url = await uploadFile(file)

		return { url }
	}

	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Retrieve many Files',
		deprecated: true
	})
	@Post('uploads')
	@UseInterceptors(FileInterceptor('files'))
	@ApiConsumes('multipart/form-data')
	@ApiImplicitFile({
		name: 'files',
		required: true,
		description: 'List of files.'
	})
	uploadFiles(@UploadedFile() files: any) {
		// console.log(files);
		return ['path', 'path1']
	}

	@ApiOperation({
		title: 'Retrieve one File'
		// deprecated: true
	})
	@Get(`${STATIC!}/:fileId`)
	getUpload(@Param('fileId') fileId: string, @Res() res): any {
		return res.sendFile(fileId, {
			root: `${STATIC!}`
		})
	}

	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Verify one Ssl',
		deprecated: true
	})
	@Get(`${SSL!}/:fileId`)
	getSSLKey(@Param('fileId') fileId: string, @Res() res): any {
		return res.sendFile(fileId, {
			root: `${SSL!}`
		})
	}
}


================================================
FILE: src/app.module.ts
================================================
import { Module, CacheModule, OnModuleInit } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm'
import { ServeStaticModule } from '@nestjs/serve-static'
import { join } from 'path'
import { TerminusModule } from '@nestjs/terminus'

import { AppController } from './app.controller'
import { AppService } from './app.service'

import { CacheService, TypeormService } from './config'
import { UsersModule } from './modules/users/users.module'
import { AuthModule } from './auth/auth.module'
import { TerminusOptionsService } from './terminus-options.service'
import { EventsModule } from './modules/events/events.module'
import { EventsGateway } from './modules/events/events.gateway'
import { STATIC } from './environments'
import { DealsModule } from './modules/deals/deals.module'
import { BiddersModule } from './modules/bidders/bidders.module'
import { AddressesModule } from './modules/addresses/addresses.module'
import { ConnectionsModule } from './modules/connections/connections.module'
import { BannersModule } from './modules/banners/banners.module'
import { ClassesModule } from './modules/classes/classes.module'
import { StudentsModule } from './modules/students/students.module'
import { AttendanceModule } from './modules/attendance/attendance.module'
import { ChatsModule } from './modules/chats/chats.module'
import { ChatsGateway } from './modules/chats/chats.gateway'

@Module({
	imports: [
		TypeOrmModule.forRootAsync({
			useClass: TypeormService
		}),
		CacheModule.registerAsync({
			useClass: CacheService
		}),
		ServeStaticModule.forRoot({
			rootPath: join(__dirname, '..', STATIC)
		}),
		ServeStaticModule.forRoot({
			rootPath: join(__dirname, '..', '.well-known/acme-challenge')
		}),
		TerminusModule.forRootAsync({
			useClass: TerminusOptionsService
		}),
		AuthModule,
		UsersModule,
		AddressesModule,
		DealsModule,
		ConnectionsModule,
		EventsModule,
		BiddersModule,
		BannersModule,
		ClassesModule,
		StudentsModule,
		AttendanceModule,
		ChatsModule
	],
	controllers: [AppController],
	providers: [AppService, EventsGateway, ChatsGateway]
})
export class AppModule implements OnModuleInit {
	onModuleInit() {
		console.log('The module has been initialized.')
	}
}


================================================
FILE: src/app.service.ts
================================================
import { Injectable } from '@nestjs/common'

@Injectable()
export class AppService {
	getHello() {
		return 'Hello World!'
	}
}


================================================
FILE: src/assets/templates/udacity-index.html
================================================
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>Verify Your Email on {{ author }}</title>
	</head>

	<body>
		<head>
			<!--[if gte mso 9
				]><xml>
					<o:OfficeDocumentSettings>
						<o:AllowPNG />
						<o:PixelsPerInch>96</o:PixelsPerInch>
					</o:OfficeDocumentSettings>
				</xml><!
			[endif]-->
			<title>{{ author }}_email</title>
			<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
			<meta content="width=device-width, initial-scale=1.0" name="viewport" />
			<!--[if !mso]><!-- -->
			<meta content="IE=edge" http-equiv="X-UA-Compatible" />
			<!--<![endif]-->
			<!--[if !mso]><!-- -->
			<link
				href="https://fonts.googleapis.com/css?family=Open+Sans:600,400,300"
				rel="stylesheet"
				type="text/css"
			/>
			<!--<![endif]-->
			<style type="text/css">
				html,
				body {
					background-color: #fafbfc;
				}

				img {
					display: block;
				}

				.ReadMsgBody {
					width: 100%;
				}

				.ExternalClass {
					width: 100%;
				}

				* {
					-webkit-text-size-adjust: none;
				}

				.whiteLinks a:link,
				.whiteLinks a:visited {
					color: #ffffff !important;
				}

				.appleLinksGrey a {
					color: #b7bdc1 !important;
					text-decoration: none !important;
				}

				table {
					border-collapse: collapse;
				}

				.preheader {
					font-size: 1px;
					line-height: 1px;
					display: none !important;
					mso-hide: all;
				}

				/* AOL Mail td overrides */
				#maincontent td {
					color: #525c65;
				}
			</style>
			<!--[if mso]>
				<style type="text/css">
					body,
					table,
					td,
					a {
						font-family: Arial, Helvetica, sans-serif !important;
					}
				</style>
			<![endif]-->
		</head>

		<body bgcolor="#fafbfc" style="Margin:0; padding:0;" yahoo="fix">
			<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
					<tr>
						<td style="background-color:#fafbfc">
							<center
								bgcolor="#fafbfc"
								style="width:100%;background-color:#fafbfc;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;"
							>
								<div
									id="maincontent"
									style="max-width:620px; font-size:0;margin:0 auto;"
								>
									<div
										class="preheader"
										style="font-size: 1px; line-height:1px; display: none!important; mso-hide:all;"
									>
										One more step to get started
									</div>
									<!--[if gte mso 9]>
              <table border="0" cellpadding="0" cellspacing="0" style="width:620px;">
                <tr>
                  <td valign="top">
            <![endif]-->

									<table
										border="0"
										cellpadding="0"
										cellspacing="0"
										style="width:100%;"
									>
										<tbody>
											<tr>
												<td>
													<table
														border="0"
														cellpadding="0"
														cellspacing="0"
														style="width:100%;"
													>
														<tbody>
															<tr>
																<td align="center" style="padding-bottom:20px;">
																	<table
																		border="0"
																		cellpadding="0"
																		cellspacing="0"
																		style="font-family:'Open+Sans', 'Open Sans', Helvetica, Arial, sans-serif; font-size:13px; line-height:18px; color:#00C0EA; text-align:center;"
																	>
																		<tbody>
																			<tr>
																				<td style="padding:20px 0 10px 0;">
																					<a
																						href="{{ issuer }}"
																						style="text-decoration:none;"
																						target="_blank"
																						><img
																							alt="{{ author }}"
																							border="0"
																							height="50"
																							src="cid:unique@kreata.ee"
																							style="display:block; font-family:'Open+Sans', 'Open Sans', Helvetica, Arial, sans-serif; font-size:22px; line-height:26px; color:#000000; text-transform:uppercase; text-align:center; letter-spacing:1px;"
																							width="50"
																					/></a>
																				</td>
																			</tr>
																		</tbody>
																	</table>
																</td>
															</tr>
														</tbody>
													</table>
												</td>
											</tr>

											<tr>
												<td>
													<table
														border="0"
														cellpadding="0"
														cellspacing="0"
														style="width:100%;"
													>
														<tbody>
															<tr>
																<td
																	bgcolor="#fafbfc"
																	style="width:7px; font-size:1px;"
																>
																	&nbsp;
																</td>
																<td
																	bgcolor="#f5f6f7"
																	style="width:1px; font-size:1px;"
																>
																	&nbsp;
																</td>
																<td
																	bgcolor="#f0f2f3"
																	style="width:1px; font-size:1px;"
																>
																	&nbsp;
																</td>
																<td
																	bgcolor="#edeef1"
																	style="width:1px; font-size:1px;"
																>
																	&nbsp;
																</td>
																<td bgcolor="#ffffff">
																	<table
																		border="0"
																		cellpadding="0"
																		cellspacing="0"
																		style="width:100%;"
																	>
																		<tbody>
																			<tr>
																				<td
																					style="text-align:center; padding:40px 40px 40px 40px; border-top:3px solid #5A71AF;"
																				>
																					<!--[if gte mso 9]>
                              <table border="0" cellpadding="0" cellspacing="0" style="width:520px;">
                                <tr>
                                  <td valign="top">
                            <![endif]-->
																					<div
																						style="display:inline-block; width:100%; max-width:520px;"
																					>
																						<table
																							border="0"
																							cellpadding="0"
																							cellspacing="0"
																							style="font-family:'Open+Sans', 'Open Sans', Helvetica, Arial, sans-serif; font-size:14px; line-height:24px; color:#525C65; text-align:left; width:100%;"
																						>
																							<tbody>
																								<tr>
																									<td>
																										<p
																											style="Margin:0; font-size:18px; line-height:23px; color:#102231; font-weight:bold;"
																										>
																											<strong>
																												Hi {{ to }},</strong
																											><br /><br />
																										</p>
																									</td>
																								</tr>

																								<tr>
																									<td>
																										{{ text1 }}<br /><br />
																									</td>
																								</tr>
																								<tr>
																									<td
																										align="center"
																										style="padding:15px 0 40px 0; border-bottom:1px solid #f3f6f9; "
																									>
																										<table
																											border="0"
																											cellpadding="0"
																											cellspacing="0"
																											style="border-collapse:separate !important; border-radius:15px; width:210px;"
																										>
																											<tbody>
																												<tr>
																													<td
																														align="center"
																														valign="top"
																													>
																														<!--[if gte mso 9]>
					<table border="0" cellspacing="0" cellpadding="0" style="width:210px">
						<tr>
							<td bgcolor="#01b3e3" style="padding:0px 10px; text-align:center;" valign="top">
				<![endif]-->
																														<a
																															target="_blank"
																															style="background-color:#5A71AF; border-collapse:separate !important; border-top:10px solid #5A71AF; border-bottom:10px solid #5A71AF; border-right:45px solid #5A71AF; border-left:45px solid #5A71AF; border-radius:4px; color:#ffffff; display:inline-block; font-family:'Open+Sans','Open Sans',Helvetica, Arial, sans-serif; font-size:13px; font-weight:bold; text-align:center; text-decoration:none; letter-spacing:2px;"
																															>{{ button }}</a
																														>
																														<!--[if gte mso 9]>
							</td>
						</tr>
					</table>
				<![endif]-->
																													</td>
																												</tr>
																											</tbody>
																										</table>
																									</td>
																								</tr>
																								<tr>
																									<td style="padding-top:30px;">
																										<p
																											style="Margin:20px 0 20px 0;"
																										>
																											{{ text2 }}
																										</p>
																									</td>
																								</tr>

																								<tr>
																									<td
																										style="font:14px/16px Arial, Helvetica, sans-serif; color:#363636; padding:0 0 14px;"
																									>
																										Cheers,
																									</td>
																								</tr>
																								<tr>
																									<td
																										style="font:bold 14px/16px Arial, Helvetica, sans-serif; color:#363636; padding:0 0 7px;"
																									>
																										{{ author }}'s Team
																									</td>
																								</tr>
																							</tbody>
																						</table>
																					</div>
																					<!--[if gte mso 9]>
                                    </td>
                                  </tr>
                                </table>
                              <![endif]-->
																				</td>
																			</tr>
																			<tr>
																				<td
																					bgcolor="#e0e2e5"
																					style="height:1px; width:100%; line-height:1px; font-size:0;"
																				>
																					&nbsp;
																				</td>
																			</tr>
																			<tr>
																				<td
																					bgcolor="#e0e2e4"
																					style="height:1px; width:100%; line-height:1px; font-size:0;"
																				>
																					&nbsp;
																				</td>
																			</tr>
																			<tr>
																				<td
																					bgcolor="#e8ebed"
																					style="height:1px; width:100%; line-height:1px; font-size:0;"
																				>
																					&nbsp;
																				</td>
																			</tr>
																			<tr>
																				<td
																					bgcolor="#f1f3f6"
																					style="height:1px; width:100%; line-height:1px; font-size:0;"
																				>
																					&nbsp;
																				</td>
																			</tr>
																		</tbody>
																	</table>
																</td>
																<td
																	bgcolor="#edeef1"
																	style="width:1px; font-size:1px;"
																>
																	&nbsp;
																</td>
																<td
																	bgcolor="#f0f2f3"
																	style="width:1px; font-size:1px;"
																>
																	&nbsp;
																</td>
																<td
																	bgcolor="#f5f6f7"
																	style="width:1px; font-size:1px;"
																>
																	&nbsp;
																</td>
																<td
																	bgcolor="#fafbfc"
																	style="width:7px; font-size:1px;"
																>
																	&nbsp;
																</td>
															</tr>
														</tbody>
													</table>
												</td>
											</tr>
											<tr>
												<td style="text-align:center; padding:0;">
													<!--[if gte mso 9]>
                  <table border="0" cellpadding="0" cellspacing="0" style="width:520px;">
                    <tr>
                      <td valign="top">
                <![endif]-->

													<div
														style="display:inline-block; width:100%; max-width:520px; vertical-align:top;"
													>
														<table
															border="0"
															cellpadding="0"
															cellspacing="0"
															style="width:100%;"
														>
															<tbody>
																<tr>
																	<td
																		align="center"
																		style="padding:30px 0; border-bottom:1px solid #e5ebef;"
																	>
																		<table
																			border="0"
																			cellpadding="0"
																			cellspacing="0"
																			style="width:265px;"
																		>
																			<tbody>
																				<tr>
																					<td align="center">
																						<a href="{{ ios }}" target="_blank"
																							><img
																								alt="IOS"
																								border="0"
																								height="44"
																								src="cid:ios@chnirt.ee"
																								style="display:block; width:120px!important;"
																								width="120"
																						/></a>
																					</td>
																					<td align="center">
																						<a
																							href="{{ android }}"
																							target="_blank"
																							><img
																								alt="Android"
																								border="0"
																								height="44"
																								src="cid:android@chnirt.ee"
																								style="display:block; width:120px!important;"
																								width="120"
																						/></a>
																					</td>
																				</tr>
																			</tbody>
																		</table>
																	</td>
																</tr>
															</tbody>
														</table>
													</div>

													<!--[if gte mso 9]>
                        </td>
                      </tr>
                    </table>
                  <![endif]-->
												</td>
											</tr>
											<tr>
												<td align="center" style="padding:30px 0 20px 0;">
													<table
														border="0"
														cellpadding="0"
														cellspacing="0"
														style="width:220px;"
													>
														<tbody>
															<tr>
																<td align="center">
																	<a href="{{ twitter }}" target="_blank"
																		><img
																			alt="Twitter"
																			border="0"
																			height="26"
																			src="cid:twitter@chnirt.ee"
																			style="display:block; width:26px!important; height:26px!important;"
																			width="26"
																	/></a>
																</td>
																<td align="center">
																	<a href="{{ facebook }}" target="_blank"
																		><img
																			alt="Facebook"
																			border="0"
																			height="26"
																			src="cid:facebook@chnirt.ee"
																			style="display:block; width:26px!important; height:26px!important;"
																			width="26"
																	/></a>
																</td>
																<td align="center">
																	<a href="{{ googleplus }}" target="_blank"
																		><img
																			alt="Google"
																			border="0"
																			height="26"
																			src="cid:googleplus@chnirt.ee"
																			style="display:block; width:28px!important; height:26px!important;"
																			width="28"
																	/></a>
																</td>
																<td align="center">
																	<a href="{{ linkedin }}" target="_blank"
																		><img
																			alt="Linkedin"
																			border="0"
																			height="26"
																			src="cid:linkedin@chnirt.ee"
																			style="display:block; width:26px!important; height:26px!important;"
																			width="26"
																	/></a>
																</td>
															</tr>
														</tbody>
													</table>
												</td>
											</tr>
											<tr>
												<td align="center" style="padding-bottom:40px;">
													<table
														border="0"
														cellpadding="0"
														cellspacing="0"
														style="font-family:'Open+Sans', 'Open Sans', Helvetica, Arial, sans-serif; font-size:12px; line-height:18px;  text-align:center; width:auto;"
													>
														<tbody>
															<tr>
																<td style="color:#b7bdc1;">
																	<p style="Margin:0;">
																		<span class="appleLinksGrey"
																			>{{ number }} {{ street }} St.</span
																		>
																		&nbsp;•&nbsp;
																		<span class="appleLinksGrey"
																			>{{ city }} City, {{ country }}</span
																		>
																	</p>
																</td>
															</tr>
														</tbody>
													</table>
												</td>
											</tr>
										</tbody>
									</table>
									<!--[if gte mso 9]>
                </td>
              </tr>
            </table>
          <![endif]-->
								</div>

								<div style=" width:100%;">
									<table
										cellpadding="0"
										cellspacing="0"
										border="0"
										style="width:100%;"
									>
										<tbody>
											<tr>
												<td
													align="center"
													bgcolor="#7d97ad"
													style="padding:10px 0;"
												>
													<table
														border="0"
														cellpadding="0"
														cellspacing="0"
														style="font-family:'Open+Sans', 'Open Sans', Helvetica, Arial, sans-serif; font-size:14px; line-height:19px;  text-align:center; width:auto;"
													>
														<tbody>
															<tr>
																<td style="color:#ffffff;">
																	Be in demand
																</td>
															</tr>
														</tbody>
													</table>
												</td>
											</tr>
										</tbody>
									</table>
								</div>
							</center>
						</td>
					</tr>
				</tbody>
			</table>
		</body>
	</body>
</html>


================================================
FILE: src/auth/auth.module.ts
================================================
import { Module } from '@nestjs/common'
import { JwtModule } from '@nestjs/jwt'
import { PassportModule } from '@nestjs/passport'
import { AuthService } from './auth.service'
// import { UsersModule } from '../modules/users/users.module'
import { LocalStrategy } from './local.strategy'
import { JwtStrategy } from './jwt.strategy'
import { ACCESS_TOKEN_SECRET } from '../environments'

@Module({
	imports: [
		// UsersModule,
		PassportModule.register({ defaultStrategy: 'jwt', session: true }),
		JwtModule.register({
			secret: ACCESS_TOKEN_SECRET!,
			signOptions: { expiresIn: '30d' }
		})
	],
	providers: [AuthService, LocalStrategy, JwtStrategy],
	exports: [AuthService]
})
export class AuthModule {}


================================================
FILE: src/auth/auth.service.ts
================================================
import { Injectable } from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'
import { comparePassword } from '../utils'
import { UserEntity } from '../modules/users/user.entity'
import { LoginResponseDto } from 'modules/users/dto/login-response.dto'
import { getMongoRepository } from 'typeorm'

@Injectable()
export class AuthService {
	constructor(private readonly jwtService: JwtService) {}

	async validateUser(email: string, password: string): Promise<any> {
		const user = await getMongoRepository(UserEntity).findOne({
			where: {
				email
			}
		})

		if (user && (await comparePassword(password, user.password))) {
			// tslint:disable-next-line: no-shadowed-variable
			const { password, ...result } = user

			return result
		}

		return null
	}

	async login(user: UserEntity): Promise<LoginResponseDto> {
		const { _id } = user
		const payload = { sub: _id }
		const expiresIn = 60 * 60 * 24 * 30

		return {
			accessToken: this.jwtService.sign(payload, {
				expiresIn
			}),
			user,
			expiresIn
		}
	}
}


================================================
FILE: src/auth/facebook.strategy.ts
================================================
// import { Injectable } from "@nestjs/common";

// @Injectable()
// export class FacebookStrategy {
//   constructor(
//     private readonly userService: UserService,
//   ) {
//     this.init();
//   }
//   init() {
//     use(
//       new FacebookTokenStrategy(
//         {
//           clientID: <YOUR_APP_CLIENT_ID>,
//           clientSecret: <YOUR_APP_CLIENT_SECRET>,
//           fbGraphVersion: 'v3.0',
//         },
//         async (
//           accessToken: string,
//           refreshToken: string,
//           profile: any,
//           done: any,
//         ) => {
//           const user = await this.userService.findOrCreate(
//             profile,
//           );
//           return done(null, user);
//         },
//       ),
//     );
//   }
// }


================================================
FILE: src/auth/google.strategy.ts
================================================
// import { Injectable } from '@nestjs/common'
// import { PassportStrategy } from '@nestjs/passport'
// import { Strategy } from 'passport-google-oauth20'
// import { AuthService, Provider } from './auth.service'

// @Injectable()
// export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
// 	constructor(private readonly authService: AuthService) {
// 		super({
// 			clientID: 'MY_CLIENT_ID', // Not my real client secret, see your own application credentials at Google!
// 			clientSecret: 'MY_CLIENT_SECRET', // Not my real client secret, see your own application credentials at Google!
// 			callbackURL: 'http://localhost:3000/auth/google/callback',
// 			passReqToCallback: true,
// 			scope: ['profile']
// 		})
// 	}

// 	async validate(
// 		request: any,
// 		accessToken: string,
// 		refreshToken: string,
// 		profile,
// 		done: any
// 	) {
// 		try {
// 			console.log(profile)

// 			const jwt: string = await this.authService.validateOAuthLogin(
// 				profile.id,
// 				Provider.GOOGLE
// 			)
// 			const user = {
// 				jwt
// 			}

// 			done(null, user)
// 		} catch (err) {
// 			// console.log(err)
// 			done(err, false)
// 		}
// 	}
// }


================================================
FILE: src/auth/jwt.strategy.ts
================================================
import { ExtractJwt, Strategy } from 'passport-jwt'
import { PassportStrategy } from '@nestjs/passport'
import { Injectable, UnauthorizedException } from '@nestjs/common'
import { ACCESS_TOKEN_SECRET } from '../environments'
import { getMongoRepository } from 'typeorm'
import { UserEntity } from '../modules/users/user.entity'

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
	constructor() {
		super({
			jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
			ignoreExpiration: false,
			secretOrKey: ACCESS_TOKEN_SECRET!
		})
	}

	async validate(payload: any) {
		try {
			const { sub } = payload

			const user = await getMongoRepository(UserEntity).findOne({ _id: sub })

			const { password, ...result } = user

			return result
		} catch (err) {
			throw new UnauthorizedException('Email or password is incorrect.')
		}
	}
}


================================================
FILE: src/auth/local.strategy.ts
================================================
import { Strategy } from 'passport-local'
import { PassportStrategy } from '@nestjs/passport'
import { Injectable, UnauthorizedException } from '@nestjs/common'
import { AuthService } from './auth.service'

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
	constructor(private readonly authService: AuthService) {
		super({
			usernameField: 'email',
			passwordField: 'password'
		})
	}

	async validate(username: string, password: string): Promise<any> {
		try {
			const user = await this.authService.validateUser(username, password)

			if (!user) {
				throw new UnauthorizedException('Email or password is incorrect.')
			}

			return user
		} catch (err) {
			throw new UnauthorizedException('Email or password is incorrect.')
		}
	}
}


================================================
FILE: src/common/filters/http-exception.filter.ts
================================================
import {
	ArgumentsHost,
	Catch,
	ExceptionFilter,
	HttpException
} from '@nestjs/common'

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
	catch(exception: HttpException, host: ArgumentsHost) {
		const ctx = host.switchToHttp()
		const response = ctx.getResponse()
		const request = ctx.getRequest()
		const statusCode = exception.getStatus()

		response.status(statusCode).json({
			statusCode,
			message: exception.message.message || exception.message.error,
			timestamp: new Date().toISOString(),
			path: request.url
		})
	}
}


================================================
FILE: src/common/index.ts
================================================
export * from './filters/http-exception.filter'
export * from './interceptors/exception.interceptor'
export * from './interceptors/http-cache.interceptor'
export * from './interceptors/logging.interceptor'
export * from './interceptors/timeout.interceptor'
export * from './interceptors/transform.interceptor'
export * from './middleware/logger.middleware'
export * from './pipes/validation.pipe'
// export * from './wiston'


================================================
FILE: src/common/interceptors/exception.interceptor.ts
================================================
import {
  CallHandler,
  ExecutionContext,
  HttpException,
  HttpStatus,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorsInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next
      .handle()
      .pipe(
        catchError(err =>
          throwError(new HttpException('New message', HttpStatus.BAD_GATEWAY)),
        ),
      );
  }
}


================================================
FILE: src/common/interceptors/http-cache.interceptor.ts
================================================
import { CacheInterceptor, ExecutionContext, Injectable } from '@nestjs/common'

@Injectable()
class HttpCacheInterceptor extends CacheInterceptor {
	trackBy(context: ExecutionContext): string | undefined {
		const request = context.switchToHttp().getRequest()
		const httpServer = request.applicationRef

		const isGetRequest = httpServer.getRequestMethod(request) === 'GET'
		const excludePaths = []
		if (
			!isGetRequest ||
			(isGetRequest && excludePaths.includes(httpServer.getRequestUrl))
		) {
			return undefined
		}
		return httpServer.getRequestUrl(request)
	}
}


================================================
FILE: src/common/interceptors/logging.interceptor.ts
================================================
import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
  Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import chalk from 'chalk';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const parentType = chalk
      .hex('#87e8de')
      .bold(`${context.getArgs()[0].route.path}`);
    const fieldName = chalk
      .hex('#87e8de')
      .bold(`${context.getArgs()[0].route.stack[0].method}`);
    return next.handle().pipe(
      tap(() => {
        Logger.debug(`⛩  ${parentType} » ${fieldName}`, 'Restful');
        // console.log(context.getArgs()[0]['route']);
      }),
    );
  }
}


================================================
FILE: src/common/interceptors/timeout.interceptor.ts
================================================
import {
	CallHandler,
	ExecutionContext,
	Injectable,
	NestInterceptor
} from '@nestjs/common'
import { Observable } from 'rxjs'
import { timeout } from 'rxjs/operators'

@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
	intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
		return next.handle().pipe(timeout(100000))
	}
}


================================================
FILE: src/common/interceptors/transform.interceptor.ts
================================================
import {
	CallHandler,
	ExecutionContext,
	Injectable,
	NestInterceptor
} from '@nestjs/common'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'

export interface Response<T> {
	data: T
}

@Injectable()
export class TransformInterceptor<T>
	implements NestInterceptor<T, Response<T>> {
	intercept(
		context: ExecutionContext,
		next: CallHandler<T>
	): Observable<Response<T>> {
		return next.handle().pipe(map(data => ({ data })))
	}
}


================================================
FILE: src/common/middleware/logger.middleware.ts
================================================
// import chalk from 'chalk'
// import { logger } from '../wiston'
import { Logger } from '@nestjs/common';

export function LoggerMiddleware(req, res, next) {
  // logger.debug(`📢  ${req.headers['user-agent']}`)
  Logger.debug(
    `📢  ${req.headers['user-agent'].split(') ')[0]})`,
    'Bootstrap',
    false,
  );
  next();
}


================================================
FILE: src/common/pipes/validation.pipe.ts
================================================
import {
  Injectable,
  PipeTransform,
  ArgumentMetadata,
  BadRequestException,
} from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    // destructuring metadata
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    const object = plainToClass(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      throw new BadRequestException(
        `Form Arguments invalid: ${this.formatErrors(errors)}`,
      );
    }
    return value;
  }

  // tslint:disable-next-line:ban-types
  private toValidate(metatype: Function): boolean {
    // tslint:disable-next-line:ban-types
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }

  private formatErrors(errors: any[]) {
    return errors
      .map(err => {
        // tslint:disable-next-line: forin
        for (const property in err.constraints) {
          return err.constraints[property];
        }
      })
      .join(', ');
  }
}


================================================
FILE: src/common/wiston/index.ts
================================================
// import { addColors, createLogger, format, transports } from 'winston';

// const { label, json, timestamp, align, printf } = format;

// const config = {
//   levels: {
//     error: 0,
//     debug: 1,
//     warn: 2,
//     data: 3,
//     info: 4,
//     verbose: 5,
//     silly: 6,
//     custom: 7,
//   },
//   colors: {
//     error: 'red',
//     debug: 'blue',
//     warn: 'yellow',
//     data: 'grey',
//     info: 'green',
//     verbose: 'cyan',
//     silly: 'magenta',
//     custom: 'yellow',
//   },
// };

// // tslint:disable-next-line:no-shadowed-variable
// const myFormat = printf(({ level, message, label, timestamp }) => {
//   // console.log(level)
//   return `{\n\tlabel: ${label},\n\ttimestamp: ${timestamp},\n\tlevel: ${level},\n\tmessage: ${message}\n},`;
// });

// const logger = createLogger({
//   level: 'error',
//   levels: config.levels,
//   format: format.combine(
//     label({ label: '👻  Chnirt!' }),
//     json(),
//     timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
//     align(),
//     // prettyPrint(),
//     // colorize(),
//     myFormat,
//   ),
//   defaultMeta: { service: 'user-service' },
//   transports: [
//     //
//     // - Write to all logs with level `info` and below to `combined.log`
//     // - Write all logs error (and below) to `error.log`.
//     //
//     // new transports.Console(),
//     new transports.File({
//       filename: 'logs/info.log',
//       level: 'info',
//     }),
//     new transports.File({
//       filename: 'logs/error.log',
//       level: 'error',
//     }),
//     new transports.File({
//       filename: 'logs/warn.log',
//       level: 'warn',
//     }),
//     new transports.File({
//       filename: 'logs/debug.log',
//       level: 'debug',
//     }),
//     new transports.File({
//       filename: 'logs/verbose.log',
//       level: 'verbose',
//     }),
//     // new transports.File({ filename: 'src/logs/combined.log' })
//   ],
// });

// addColors(config.colors);

// export { logger };


================================================
FILE: src/config/cache/index.ts
================================================
import {
  Injectable,
  CacheOptionsFactory,
  CacheModuleOptions,
} from '@nestjs/common';

@Injectable()
export class CacheService implements CacheOptionsFactory {
  createCacheOptions(): CacheModuleOptions {
    return {
      ttl: 5, // seconds
      max: 10, // maximum number of items in cache
    };
  }
}


================================================
FILE: src/config/index.ts
================================================
export * from './cache';
export * from './logger';
export * from './typeorm';


================================================
FILE: src/config/logger/index.ts
================================================
import { LoggerService } from '@nestjs/common'

export class MyLogger implements LoggerService {
	log(message: string) {}
	error(message: string, trace: string) {}
	warn(message: string) {}
	debug(message: string) {}
	verbose(message: string) {}
}


================================================
FILE: src/config/typeorm/index.ts
================================================
import { Injectable, Logger } from '@nestjs/common';
import { TypeOrmOptionsFactory, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { getMetadataArgsStorage, createConnection } from 'typeorm';

import config from '../../config.orm';
// import { logger } from '../../common'

@Injectable()
export class TypeormService implements TypeOrmOptionsFactory {
  async createTypeOrmOptions(): Promise<TypeOrmModuleOptions> {
    const options = {
      ...config,
      type: 'mongodb',
      entities: getMetadataArgsStorage().tables.map(tbl => tbl.target),
      // migrations: ['src/modules/**/migration/*.ts'],
      // subscribers: ['src/modules/**/subscriber/*.ts'],
      // cli: {
      // 	entitiesDir: 'src/modules/**/entity',
      // 	migrationsDir: 'src/modules/**/migration',
      // 	subscribersDir: 'src/modules/**/subscriber'
      // },
      synchronize: true,
      useNewUrlParser: true,
      useUnifiedTopology: true,
      keepConnectionAlive: true,
      logging: true,
    };
    createConnection(options)
      .then(data => {
        // logger.info(data)
        Logger.log(`☁️  Database connected`, 'TypeORM', false);
      })
      .catch(err => {
        // logger.error(err)
        Logger.error(`❌  Database connect error`, '', 'TypeORM', false);
      });

    return options;
  }
}


================================================
FILE: src/config.orm.ts
================================================
import { NODE_ENV, MONGO_URL, MONGO_PORT, MONGO_DB } from './environments';

const orm = {
  development: {
    url: MONGO_URL!,
  },
  testing: {
    url: MONGO_URL!,
  },
  staging: {
    host: 'localhost',
    port: MONGO_PORT!,
    username: '',
    password: '',
    database: MONGO_DB!,
  },
  production: {
    url: MONGO_URL!,
  },
};

export default orm[NODE_ENV!];


================================================
FILE: src/environments/index.ts
================================================
import * as dotenv from 'dotenv'
dotenv.config()

// environment
const NODE_ENV: string = process.env.NODE_ENV || 'development'

// author
const AUTHOR: string = process.env.AUTHOR || 'Chnirt'

// application
const DOMAIN: string = process.env.DOMAIN || 'localhost'
const PORT: number = +process.env.PORT || 14047
const END_POINT: string = process.env.END_POINT || 'graphql'
const VOYAGER: string = process.env.VOYAGER || 'voyager'
const FE_URL: string = process.env.FE_URL || 'xxx'
const RATE_LIMIT_MAX: number = +process.env.RATE_LIMIT_MAX || 10000
const GRAPHQL_DEPTH_LIMIT: number = +process.env.GRAPHQL_DEPTH_LIMIT || 3

// static
const STATIC: string = process.env.STATIC || 'static'

// ssl
const SSL: string = process.env.SSL || '.well-known/acme-challenge'

// mlab
const MLAB_USER = process.env.MLAB_USER || 'admin'
const MLAB_PASS = process.env.MLAB_PASS || 'chnirt1803'
const MLAB_HOST = process.env.MLAB_HOST || 'ds243055.mlab.com'
const MLAB_PORT = +process.env.MLAB_PORT || 43055
const MLAB_DATABASE =
	process.env.MLAB_DATABASE || 'nestjs-restful-best-practice'
const MLAB_URL =
	process.env.MLAB_URL ||
	`mongodb://${MLAB_USER}:${MLAB_PASS}@${MLAB_HOST}:${MLAB_PORT}/${MLAB_DATABASE}`

// mongodb
const MONGO_URL: string = process.env.MONGO_PORT
	? `mongodb://localhost:${process.env.MONGO_PORT}`
	: MLAB_URL
const MONGO_PORT: number = +process.env.MONGO_PORT || 11049
const MONGO_DB: string = process.env.MONGO_PORT ? 'chnirt-nest' : MLAB_DATABASE

// jsonwebtoken
const ISSUER: string = process.env.ISSUER || 'http://chnirt.github.io'
const ACCESS_TOKEN: string = process.env.ACCESS_TOKEN || 'access-token'
const ACCESS_TOKEN_SECRET: string = process.env.ACCESS_TOKEN_SECRET || 'basic'
const REFRESH_TOKEN: string = process.env.REFRESH_TOKEN || 'refresh-token'
const REFRESH_TOKEN_SECRET: string =
	process.env.REFRESH_TOKEN_SECRET || 'refresh-token-key'
const EMAIL_TOKEN: string = process.env.EMAIL_TOKEN || 'email-token'
const EMAIL_TOKEN_SECRET: string =
	process.env.EMAIL_TOKEN_SECRET || 'email-token-key'
const RESETPASS_TOKEN: string = process.env.RESETPASS_TOKEN || 'resetpass-token'
const RESETPASS_TOKEN_SECRET: string =
	process.env.RESETPASS_TOKEN_SECRET || 'resetpass-token-key'

// bcrypt
const SALT: number = +process.env.SALT || 10

// nodemailer
const MAIL_USER: string = process.env.MAIL_USER || 'xxx'
const MAIL_PASS: string = process.env.MAIL_PASS || 'xxx'

// cloudinary
const CLOUD_NAME: string = process.env.CLOUD_NAME || 'xxx'
const API_KEY: string = process.env.API_KEY || 'xxx'
const API_SECRET: string = process.env.API_SECRET || 'xxx'

// speakeasy
const SPEAKEASY_SECRET = process.env.SPEAKEASY_SECRET || 'speakeasy-secret'
const SPEAKEASY_DIGITS = +process.env.SPEAKEASY_DIGITS || 6
const SPEAKEASY_STEP = +process.env.SPEAKEASY_STEP || 60

// pubsub
const NOTIFICATION_SUBSCRIPTION: string = 'newNotification'
const USER_SUBSCRIPTION: string = 'newUser'
const MESSAGES_SUBSCRIPTION: string = 'newMessages'

// passport
const GOOGLE_CLIENT_ID: string = process.env.GOOGLE_CLIENT_ID || 'xxx'
const GOOGLE_CLIENT_SECRET: string = process.env.GOOGLE_CLIENT_SECRET || 'xxx'
const GOOGLE_CALLBACK_URL: string =
	process.env.GOOGLE_CALLBACK_URL || 'auth/google/callback'

const FACEBOOK_APP_ID: string = process.env.FACEBOOK_APP_ID || 'xxx'
const FACEBOOK_APP_SECRET: string = process.env.FACEBOOK_APP_SECRET || 'xxx'
const FACEBOOK_CALLBACK_URL: string =
	process.env.FACEBOOK_CALLBACK_URL || 'auth/facebook/callback'

// google cloud
const GOOGLE_APPLICATION_CREDENTIALS: string =
	process.env.GOOGLE_APPLICATION_CREDENTIALS || 'xxx'

// stripe
const STRIPE_PUBLIC_KEY: string = process.env.STRIPE_PUBLIC_KEY || 'xxx'
const STRIPE_SECRET_KEY: string = process.env.STRIPE_SECRET_KEY || 'xxx'
const STRIPE_PLAN: string = process.env.STRIPE_PLAN || 'xxx'

// twilio
const TWILIO_ACCOUNT_SID: string = process.env.TWILIO_ACCOUNT_SID || 'xxx'
const TWILIO_AUTH_TOKEN: string = process.env.TWILIO_AUTH_TOKEN || 'xxx'

export {
	NODE_ENV,
	AUTHOR,
	DOMAIN,
	PORT,
	END_POINT,
	VOYAGER,
	FE_URL,
	RATE_LIMIT_MAX,
	GRAPHQL_DEPTH_LIMIT,
	STATIC,
	SSL,
	MLAB_USER,
	MLAB_PASS,
	MLAB_HOST,
	MLAB_PORT,
	MLAB_DATABASE,
	MLAB_URL,
	MONGO_URL,
	MONGO_PORT,
	MONGO_DB,
	ISSUER,
	ACCESS_TOKEN,
	ACCESS_TOKEN_SECRET,
	REFRESH_TOKEN,
	REFRESH_TOKEN_SECRET,
	RESETPASS_TOKEN,
	RESETPASS_TOKEN_SECRET,
	EMAIL_TOKEN,
	EMAIL_TOKEN_SECRET,
	SALT,
	MAIL_USER,
	MAIL_PASS,
	CLOUD_NAME,
	API_KEY,
	API_SECRET,
	SPEAKEASY_SECRET,
	SPEAKEASY_DIGITS,
	SPEAKEASY_STEP,
	USER_SUBSCRIPTION,
	NOTIFICATION_SUBSCRIPTION,
	MESSAGES_SUBSCRIPTION,
	GOOGLE_CLIENT_ID,
	GOOGLE_CLIENT_SECRET,
	GOOGLE_CALLBACK_URL,
	FACEBOOK_APP_ID,
	FACEBOOK_APP_SECRET,
	FACEBOOK_CALLBACK_URL,
	GOOGLE_APPLICATION_CREDENTIALS,
	STRIPE_PUBLIC_KEY,
	STRIPE_SECRET_KEY,
	STRIPE_PLAN,
	TWILIO_ACCOUNT_SID,
	TWILIO_AUTH_TOKEN
}


================================================
FILE: src/main.ts
================================================
import { NestFactory } from '@nestjs/core'
import { Logger, InternalServerErrorException } from '@nestjs/common'
import chalk from 'chalk'
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'
import * as bodyParser from 'body-parser'
import * as helmet from 'helmet'
import * as rateLimit from 'express-rate-limit'
import * as fs from 'fs'
import { AppModule } from './app.module'

import {
	ValidationPipe,
	LoggerMiddleware,
	TimeoutInterceptor,
	LoggingInterceptor,
	HttpExceptionFilter
} from './common'
import { MyLogger } from './config'

import { NODE_ENV, DOMAIN, PORT } from './environments'

declare const module: any

async function bootstrap() {
	try {
		// const httpsOptions = {
		// 	key: fs.readFileSync('ssl/private.key'),
		// 	cert: fs.readFileSync('ssl/certificate.crt'),
		// 	ca: fs.readFileSync('ssl/ca_bundle.crt'),
		// }

		const app = await NestFactory.create(AppModule, {
			// httpsOptions,
			logger: new MyLogger(),
			cors: true
		})

		app.use(helmet())

		// body parser
		app.use(bodyParser.json())
		app.use(bodyParser.urlencoded())
		// app.use(bodyParser.json({ limit: '2mb' }))
		// app.use(
		// 	bodyParser.urlencoded({
		// 		limit: '2mb',
		// 		extended: true,
		// 		parameterLimit: 2000
		// 	})
		// )

		app.use(
			rateLimit({
				windowMs: 15 * 60 * 1000, // 15 minutes
				max: 10000 // limit each IP to 10000 requests per windowMs
			})
		)

		// // adapter for e2e testing
		const httpAdapter = app.getHttpAdapter()

		// loggerMiddleware
		// tslint:disable-next-line:no-unused-expression
		NODE_ENV !== 'testing' && app.use(LoggerMiddleware)

		// interceptors
		// app.useGlobalInterceptors(new LoggingInterceptor())
		// app.useGlobalInterceptors(new TimeoutInterceptor())
		// app.useGlobalFilters(new HttpExceptionFilter())

		// global nest setup
		app.useGlobalPipes(new ValidationPipe())

		// Starts listening to shutdown hooks
		app.enableShutdownHooks()

		const options = new DocumentBuilder()
			.setTitle('Nestjs Restful Best Practice')
			.setVersion('3.0.0')
			// .setHost('nestjs-restful-best-practice.herokuapp.com')
			.setBasePath('/v1')
			.setDescription('built NestJS, TypeORM, MongoDB')
			.setTermsOfService(
				'https://github.com/chnirt/nestjs-restful-best-practice/blob/master/LICENSE'
			)
			.setContactEmail('trinhchinchin@gmail.com')
			.setLicense(
				'MIT License',
				'https://github.com/chnirt/nestjs-restful-best-practice/blob/master/LICENSE'
			)
			.setExternalDoc('For more information', 'http://swagger.io')
			.setSchemes(NODE_ENV !== 'production' ? 'http' : 'https')
			.addBearerAuth('Authorization', 'header')
			.addTag('chnirt', 'developer')
			.build()

		const document = SwaggerModule.createDocument(app, options)
		SwaggerModule.setup('api', app, document)

		app.setGlobalPrefix('/v1')

		const server = await app.listen(PORT!)

		// hot module replacement
		if (module.hot) {
			module.hot.accept()
			module.hot.dispose(() => app.close())
		}

		NODE_ENV !== 'production'
			? Logger.log(
					`🚀  Server ready at https://${DOMAIN!}:${chalk
						.hex('#87e8de')
						.bold(`${PORT!}`)}`,
					'Bootstrap'
			  )
			: Logger.log(
					`🚀  Server is listening on port ${chalk
						.hex('#87e8de')
						.bold(`${PORT!}`)}`,
					'Bootstrap'
			  )
	} catch (error) {
		// logger.error(error)
		Logger.error(`❌  Error starting server, ${error}`, '', 'Bootstrap', false)
		process.exit()
		throw new InternalServerErrorException(error)
	}
}
bootstrap().catch(e => {
	throw e
})


================================================
FILE: src/modules/addresses/address.entity.ts
================================================
import { Entity, ObjectIdColumn, Column } from 'typeorm'
import { uuidv4 } from '../../utils'
// import { Exclude, plainToClass } from 'class-transformer'
import { ApiModelProperty } from '@nestjs/swagger'

import { Position } from '../deals/entity/position.entity'
import { AddressType } from './enum/address.enum'

@Entity({
	name: 'addresses',
	orderBy: {
		createdAt: 'ASC'
	}
})
export class AddressEntity {
	@ApiModelProperty({ description: 'The _id of the Address' })
	@ObjectIdColumn()
	_id: string

	@ApiModelProperty({ description: 'The name of the Address' })
	@Column()
	name: string

	@ApiModelProperty({ description: 'The addressType of the Address' })
	@Column()
	addressType: AddressType

	@ApiModelProperty({ description: 'The location of the Address' })
	@Column()
	location: Position

	@ApiModelProperty({ description: 'The unitNumber of the Address' })
	@Column()
	unitNumber: number

	@ApiModelProperty({ description: 'The remarks of the Address' })
	@Column()
	remarks: string

	@ApiModelProperty({ description: 'The createdBy of the Address' })
	@Column()
	createdBy: string

	@ApiModelProperty({ description: 'The createdAt of the Address' })
	@Column()
	createdAt: number
	@ApiModelProperty({ description: 'The updatedAt of the Address' })
	@Column()
	updatedAt: number

	constructor(partial: Partial<AddressEntity>) {
		if (partial) {
			Object.assign(this, partial)
			this._id = this._id || uuidv4()
			this.createdAt = this.createdAt || +new Date()
			this.updatedAt = +new Date()
		}
	}
}


================================================
FILE: src/modules/addresses/addresses.controller.ts
================================================
import {
	Controller,
	Post,
	Body,
	Request,
	UseGuards,
	Get,
	Query
} from '@nestjs/common'
import {
	ApiOperation,
	ApiBearerAuth,
	ApiImplicitQuery,
	ApiUseTags,
	ApiResponse
} from '@nestjs/swagger'
import { CreateAddressDto } from './dto/create-address.dto'
import { AddressesService, Address } from './addresses.service'
import { AuthGuard } from '@nestjs/passport'
import { ErrorResponseDto } from '../users/dto/error-response.dto'
import { AddressEntity } from './address.entity'

@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'))
@ApiResponse({
	status: 401,
	description: 'Unauthorized.',
	type: ErrorResponseDto
})
@ApiResponse({ status: 403, description: 'Forbidden.', type: ErrorResponseDto })
@ApiUseTags('addresses')
@Controller('addresses')
export class AddressesController {
	constructor(private readonly addressesService: AddressesService) {}

	@ApiResponse({
		status: 200,
		description: 'The found records',
		type: [AddressEntity]
	})
	@ApiOperation({
		title: 'Retrieve many Addresses 👻'
		// description: 'Aaa',
		// operationId: 'aaaa'
	})
	@Get()
	@ApiImplicitQuery({
		name: 'limit',
		description: 'The maximum number of transactions to return',
		required: false,
		type: Number
	})
	@ApiImplicitQuery({
		name: 'offset',
		description: 'The maximum number of transactions to skip',
		required: false,
		type: Number
	})
	findAll(@Query() query, @Request() req): Promise<Address[]> {
		return this.addressesService.findAll(query, req)
	}

	@ApiResponse({
		status: 201,
		description: 'The record has been successfully created',
		type: Boolean
	})
	@ApiOperation({
		title: 'Create one Address 👻'
	})
	@Post()
	insert(
		@Body() createAddressDto: CreateAddressDto,
		@Request() req
	): Promise<boolean> {
		return this.addressesService.insert(createAddressDto, req)
	}
}


================================================
FILE: src/modules/addresses/addresses.module.ts
================================================
import { Module } from '@nestjs/common';
import { AddressesController } from './addresses.controller';
import { AddressesService } from './addresses.service';

@Module({
  controllers: [AddressesController],
  providers: [AddressesService]
})
export class AddressesModule {}


================================================
FILE: src/modules/addresses/addresses.service.ts
================================================
import { Injectable, ForbiddenException } from '@nestjs/common'
import { CreateAddressDto } from './dto/create-address.dto'
import { AddressEntity } from './address.entity'
import { getMongoRepository } from 'typeorm'

export type Address = any

@Injectable()
export class AddressesService {
	async findAll(query: any, req: any): Promise<Address[]> {
		const { offset, limit } = query
		const { user } = req
		const { _id } = user

		const pipelineArray = []

		if (offset) {
			if (offset < 1) {
				throw new ForbiddenException('The offset must be greater than 0')
			} else {
				pipelineArray.push({
					$skip: +offset
				})
			}
		}

		if (limit) {
			if (limit < 1) {
				throw new ForbiddenException('The limit must be greater than 0')
			} else {
				pipelineArray.push({
					$limit: +limit
				})
			}
		}

		const match = [
			{
				$match: {
					createdBy: _id
				}
			},
			{
				$project: {
					createdBy: 0,
					createdAt: 0,
					updatedAt: 0
				}
			}
		]

		pipelineArray.push(...match)

		return await getMongoRepository(AddressEntity)
			.aggregate(pipelineArray)
			.toArray()
	}

	async insert(createAddressDto: CreateAddressDto, req: any): Promise<boolean> {
		const { user } = req
		const { _id } = user
		const { addressType } = createAddressDto

		if (addressType !== 'Others') {
			const foundAddress = await getMongoRepository(AddressEntity).findOne({
				addressType,
				createdBy: _id
			})

			if (foundAddress) {
				throw new ForbiddenException(
					`Address at ${addressType} already existed`
				)
			}
		}

		const newAddress = await getMongoRepository(AddressEntity).save(
			new AddressEntity({ ...createAddressDto, createdBy: _id })
		)

		return newAddress && true
	}
}


================================================
FILE: src/modules/addresses/dto/create-address.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty, IsEnum, IsOptional } from 'class-validator'

import { AddressType } from '../enum/address.enum'
import { Position } from '../../deals/entity/position.entity'

export class CreateAddressDto {
	@ApiModelProperty({
		enum: ['Home', 'Workplace', 'Others'],
		example: 'Home',
		description: 'The addressType of the Address'
	})
	@IsEnum(AddressType)
	@IsNotEmpty()
	readonly addressType: AddressType

	@ApiModelProperty({
		default: 'Viva coffee',
		example: 'Viva coffee',
		description: 'The name of the Address'
	})
	@IsOptional()
	readonly name: string

	@ApiModelProperty({
		default: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		example: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		description: 'The location of the Address'
	})
	@IsNotEmpty()
	readonly location: Position

	@ApiModelProperty({
		default: 69,
		example: 69,
		description: 'The unitNumber of the Address'
	})
	@IsNotEmpty() // 30m 1h 1h30 2h
	readonly unitNumber: number

	@ApiModelProperty({
		default: 'Đối diện BigC',
		example: 'Đối diện BigC',
		description: 'The remarks of the Address'
	})
	@IsOptional()
	readonly remarks: string
}


================================================
FILE: src/modules/addresses/dto/query-address.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger';

class QueryFilterDto {
  @ApiModelProperty({
    type: String,
    example: 'age',
  })
  readonly field: string;

  @ApiModelProperty({
    type: String,
    example: 'gt',
  })
  readonly comparator: string;

  @ApiModelProperty({
    type: Object,
    example: 25,
  })
  readonly value: any;
}


================================================
FILE: src/modules/addresses/enum/address.enum.ts
================================================
export enum AddressType {
	Home = 'Home',
	Workplace = 'Workplace',
	Others = 'Others'
}


================================================
FILE: src/modules/attendance/attendance.controller.ts
================================================
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common'
import { AttendanceService } from './attendance.service'
import { ApiResponse, ApiOperation, ApiUseTags } from '@nestjs/swagger'
import { AttendanceEntity } from './entity/attendance.entity'
import { ReplaceAttendanceDto } from './dto/replace-attendance.dto'
import { CreateAttendanceDto } from './dto/create-attendance.dto'

@ApiUseTags('attendance')
@Controller('attendance')
export class AttendanceController {
	constructor(private readonly attendanceService: AttendanceService) {}

	@ApiResponse({
		status: 200,
		description: 'The found records',
		type: [AttendanceEntity]
	})
	@ApiOperation({
		title: 'Retrieve many attendance 👾'
	})
	@Get()
	findAll() {
		return this.attendanceService.findAll()
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: AttendanceEntity
	})
	@ApiOperation({
		title: 'Create one Attendance 👾'
	})
	@Post()
	async insert(@Body() createAttendanceDto: CreateAttendanceDto) {
		const newAttendance = await this.attendanceService.insert(
			createAttendanceDto
		)

		return newAttendance
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: AttendanceEntity
	})
	@ApiOperation({
		title: 'Retrieve one Attendance 👾'
	})
	@Get(':id')
	findOne(@Param('id') id: string) {
		return this.attendanceService.findOne(id)
	}

	@ApiOperation({
		title: 'Replace one Attendance 👾'
	})
	@Put(':id')
	replace(
		@Param('id') id: string,
		@Body() replaceAttendanceDto: ReplaceAttendanceDto
	) {
		return this.attendanceService.findOneAndReplace(id, replaceAttendanceDto)
	}

	@ApiResponse({
		status: 200,
		description: 'The found record is executed 👾',
		type: Boolean
	})
	@ApiOperation({
		title: 'Delete one Attendance 👾'
	})
	@Delete(':id')
	remove(@Param('id') id: string) {
		return this.attendanceService.deleteOne(id)
	}
}


================================================
FILE: src/modules/attendance/attendance.module.ts
================================================
import { Module } from '@nestjs/common';
import { AttendanceController } from './attendance.controller';
import { AttendanceService } from './attendance.service';

@Module({
  controllers: [AttendanceController],
  providers: [AttendanceService]
})
export class AttendanceModule {}


================================================
FILE: src/modules/attendance/attendance.service.ts
================================================
import {
	Injectable,
	ForbiddenException,
	NotFoundException
} from '@nestjs/common'
import { AttendanceEntity } from './entity/attendance.entity'
import { CreateAttendanceDto } from './dto/create-attendance.dto'
import { getMongoRepository } from 'typeorm'
import { ReplaceAttendanceDto } from './dto/replace-attendance.dto'
import { StudentEntity } from '../../modules/students/entity/student.entity'

// let date = new Date()
// console.log('startOfDay', date.setHours(0, 0, 0, 0))
// console.log('endOfDay', date.setHours(23, 59, 59, 999))

export type Attendance = any

@Injectable()
export class AttendanceService {
	async findAll(): Promise<Attendance[] | undefined> {
		return getMongoRepository(AttendanceEntity).find()
	}

	async insert(
		createAttendanceDto: CreateAttendanceDto
	): Promise<Attendance | undefined> {
		const { studentId } = createAttendanceDto
		const foundStudent = await getMongoRepository(StudentEntity).findOne({
			_id: studentId
		})
		if (!foundStudent) {
			throw new NotFoundException('Student not found.')
		}
		const date = new Date()
		const existedAttendance = await getMongoRepository(
			AttendanceEntity
		).findOne({
			where: {
				studentId,
				createdAt: {
					$gt: date.setHours(0, 0, 0, 0),
					$lt: date.setHours(23, 59, 59, 999)
				}
			}
		})
		if (existedAttendance) {
			throw new ForbiddenException('Attendance already existed.')
		}
		const newAttendance = await getMongoRepository(AttendanceEntity).save(
			new AttendanceEntity({
				...createAttendanceDto
			})
		)
		return newAttendance
	}
	async findOne(_id: string): Promise<Attendance | undefined> {
		const foundAttendance = await getMongoRepository(AttendanceEntity).findOne({
			_id
		})
		if (!foundAttendance) {
			throw new NotFoundException('Attendance not found.')
		}
		return foundAttendance
	}
	async findOneAndReplace(
		_id: string,
		replaceAttendanceDto: ReplaceAttendanceDto
	): Promise<Attendance | undefined> {
		const foundAttendance = await getMongoRepository(AttendanceEntity).findOne({
			_id
		})
		if (!foundAttendance) {
			throw new NotFoundException('Attendance not found.')
		}
		const updateAttendance = await getMongoRepository(AttendanceEntity).save(
			new AttendanceEntity({
				...foundAttendance,
				...replaceAttendanceDto
			})
		)
		return updateAttendance
	}
	async deleteOne(_id: string): Promise<boolean | undefined> {
		const foundAttendance = await getMongoRepository(AttendanceEntity).findOne({
			_id
		})
		if (!foundAttendance) {
			throw new NotFoundException('Attendance not found.')
		}
		return (
			(await getMongoRepository(AttendanceEntity).delete(foundAttendance)) &&
			true
		)
	}
}


================================================
FILE: src/modules/attendance/dto/create-attendance.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class CreateAttendanceDto {
	@ApiModelProperty({
		default: 'xxxx-xxxx-xxxx-xxxx',
		example: 'xxxx-xxxx-xxxx-xxxx',
		description: 'The studentId of the Student'
	})
	@IsNotEmpty()
	readonly studentId: string

	@ApiModelProperty({
		default: true,
		example: true,
		description: 'The present of the Student'
	})
	@IsNotEmpty()
	readonly present: boolean
}


================================================
FILE: src/modules/attendance/dto/replace-attendance.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class ReplaceAttendanceDto {
	@ApiModelProperty({
		default: true,
		example: true,
		description: 'The present of the Student'
	})
	@IsNotEmpty()
	readonly present: boolean
}


================================================
FILE: src/modules/attendance/entity/attendance.entity.ts
================================================
import { Entity, Column, ObjectIdColumn } from 'typeorm'
import { ApiModelProperty } from '@nestjs/swagger'
import { uuidv4 } from '../../../utils'

@Entity({
	name: 'attendance',
	orderBy: {
		createdAt: 'ASC'
	}
})
export class AttendanceEntity {
	@ApiModelProperty({ description: 'The _id of the Attendance' })
	@ObjectIdColumn()
	_id: string

	@ApiModelProperty({ description: 'The studentId of the Attendance' })
	@Column()
	studentId: string

	@ApiModelProperty({ description: 'The present of the Attendance' })
	@Column()
	present: boolean

	@ApiModelProperty({ description: 'The createdAt of the Attendance' })
	@Column()
	createdAt: number
	@ApiModelProperty({ description: 'The updatedAt of the Attendance' })
	@Column()
	updatedAt: number

	constructor(partial: Partial<AttendanceEntity>) {
		if (partial) {
			Object.assign(this, partial)
			this._id = this._id || uuidv4()
			this.createdAt = this.createdAt || +new Date()
			this.updatedAt = +new Date()
		}
	}
}


================================================
FILE: src/modules/banners/banner.entity.ts
================================================
import { Entity, ObjectIdColumn, Column } from 'typeorm'
import { uuidv4 } from '../../utils'
import { ApiModelProperty } from '@nestjs/swagger'
@Entity({
  name: 'banners',
  orderBy: {
    createdAt: 'ASC'
  }
})
export class BannerEntity {
  @ApiModelProperty({ description: 'The _id of the Banner' })
  @ObjectIdColumn()
  _id: string

  @ApiModelProperty({ description: 'The title of the Banner' })
  @Column()
  title: string

  @ApiModelProperty({ description: 'The imageUrl of the Banner' })
  @Column()
  imageUrl: string

  @ApiModelProperty({ description: 'The position of the Banner' })
  @Column()
  position: number

  @ApiModelProperty({ description: 'The detail of the Banner' })
  @Column()
  detail: string

  @ApiModelProperty({ description: 'The published of the Banner' })
  @Column()
  published: boolean

  @ApiModelProperty({ description: 'The createdAt of the Banner' })
  @Column()
  createdAt: number
  @ApiModelProperty({ description: 'The updatedAt of the Banner' })
  @Column()
  updatedAt: number

  constructor(partial: Partial<BannerEntity>) {
    if (partial) {
      Object.assign(this, partial)
      this._id = this._id || uuidv4()
      this.createdAt = this.createdAt || +new Date()
      this.updatedAt = +new Date()
    }
  }
}


================================================
FILE: src/modules/banners/banners.controller.ts
================================================
import { Controller, Get, Query, Post, Body, Request, UseGuards, Put, Param } from '@nestjs/common';
import { BannersService, Banner } from './banners.service';
import { ApiOperation, ApiImplicitQuery, ApiUseTags, ApiBearerAuth, ApiResponse } from '@nestjs/swagger';
import { CreateBannerDto } from './dto/create-banner.dto';
import { AuthGuard } from '@nestjs/passport';
import { BannerEntity } from './banner.entity';
import { ReplaceBannerDto } from './dto/replace-banner.dto';
import { ErrorResponseDto } from '../users/dto/error-response.dto';

@ApiResponse({ status: 401, description: 'Unauthorized.', type: ErrorResponseDto })
@ApiResponse({ status: 403, description: 'Forbidden.', type: ErrorResponseDto })
@ApiUseTags('banners')
@Controller('banners')
export class BannersController {
  constructor(private readonly bannersService: BannersService) { }

  @ApiResponse({
    status: 200,
    description: 'The found records',
    type: [BannerEntity]
  })
  @ApiOperation({
    title: 'Retrieve many Banners 👻'
    // description: 'Aaa',
    // operationId: 'aaaa'
  })
  @Get()
  @ApiImplicitQuery({
    name: 'limit',
    description: 'The maximum number of transactions to return',
    required: false,
    type: Number
  })
  @ApiImplicitQuery({
    name: 'offset',
    description: 'The maximum number of transactions to skip',
    required: false,
    type: Number
  })
  findAll(@Query() query): Promise<Banner> {
    return this.bannersService.findAll(query)
  }

  @ApiResponse({
    status: 201,
    description: 'The record has been successfully created.',
    type: BannerEntity
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard('jwt'))
  @ApiOperation({
    title: 'Create one Banner 👻'
  })
  @Post()
  insert(@Body() createBannerDto: CreateBannerDto): Promise<boolean> {
    return this.bannersService.insert(createBannerDto)
  }

  @ApiResponse({
    status: 200,
    description: 'The found record is executed',
    type: Boolean
  })
  @ApiBearerAuth()
  @UseGuards(AuthGuard('jwt'))
  @ApiOperation({
    title: 'Replace one Banner 👻'
  })
  @Put(':id')
  replace(@Param('id') id: string, @Body() replaceBannerDto: ReplaceBannerDto): Promise<boolean> {
    return this.bannersService.findOneAndReplace(id, replaceBannerDto)
  }
}


================================================
FILE: src/modules/banners/banners.module.ts
================================================
import { Module } from '@nestjs/common';
import { BannersController } from './banners.controller';
import { BannersService } from './banners.service';

@Module({
  controllers: [BannersController],
  providers: [BannersService]
})
export class BannersModule {}


================================================
FILE: src/modules/banners/banners.service.ts
================================================
import {
	Injectable,
	ForbiddenException,
	NotFoundException
} from '@nestjs/common'
import { getMongoRepository } from 'typeorm'
import { BannerEntity } from './banner.entity'
import { CreateBannerDto } from './dto/create-banner.dto'
import { ReplaceBannerDto } from './dto/replace-banner.dto'

export type Banner = any

@Injectable()
export class BannersService {
	async findAll(query): Promise<Banner[]> {
		const { offset, limit } = query

		if (offset < 1) {
			throw new ForbiddenException('The offset must be greater than 0')
		}

		if (limit < 1) {
			throw new ForbiddenException('The offset must be greater than 0')
		}

		return getMongoRepository(BannerEntity).find({
			where: {
				published: true
			},
			skip: +offset | 0,
			take: +limit | 100
		})
	}

	async insert(createBannerDto: CreateBannerDto): Promise<boolean> {
		const { position } = createBannerDto

		const foundBanner = await getMongoRepository(BannerEntity).findOne({
			position,
			published: true
		})

		if (foundBanner) {
			throw new ForbiddenException('Banner already published')
		}

		const newAddress = await getMongoRepository(BannerEntity).save(
			new BannerEntity({ ...createBannerDto })
		)

		return newAddress && true
	}

	async findOneAndReplace(
		_id: string,
		replaceBannerDto: ReplaceBannerDto
	): Promise<boolean> {
		const { position } = replaceBannerDto
		let foundBanner = await getMongoRepository(BannerEntity).findOne({
			position,
			published: true
		})

		if (foundBanner) {
			throw new ForbiddenException('Banner already published')
		}

		foundBanner = await getMongoRepository(BannerEntity).findOne({ _id })

		if (!foundBanner) {
			throw new NotFoundException('Banner not found')
		}

		const updateBanner = await getMongoRepository(BannerEntity).save(
			new BannerEntity({
				...foundBanner,
				...replaceBannerDto
			})
		)

		return updateBanner && true
	}
}


================================================
FILE: src/modules/banners/dto/create-banner.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import {
	IsNotEmpty,
	IsOptional,
	Min,
	Max,
	IsNumber,
	IsBoolean
} from 'class-validator'

export class CreateBannerDto {
	@ApiModelProperty({
		default: 'Enjoy Chewapp for Free',
		example: 'Enjoy Chewapp for Free',
		description: 'The title of the Banner'
	})
	@IsNotEmpty()
	readonly title: string

	@ApiModelProperty({
		default: 'https://xxxxxxxxx',
		example: 'https://xxxxxxxxx',
		description: 'The imageUrl of the Banner'
	})
	@IsNotEmpty()
	readonly imageUrl: string

	@ApiModelProperty({
		default: 1,
		example: 1,
		description: 'The position of the Banner'
	})
	@Max(20)
	@Min(1)
	@IsNumber()
	@IsNotEmpty()
	readonly position: number

	@ApiModelProperty({
		default: '<h1>Hello</h1>',
		example: '<h1>Hello</h1>',
		description: 'The detail of the Banner'
	})
	@IsOptional()
	readonly detail: string

	@ApiModelProperty({
		default: false,
		example: false,
		description: 'The published of the Banner'
	})
	@IsBoolean()
	@IsNotEmpty()
	readonly published: boolean
}


================================================
FILE: src/modules/banners/dto/replace-banner.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty, IsOptional, Min, Max, IsNumber, IsBoolean } from 'class-validator'

export class ReplaceBannerDto {

	@ApiModelProperty({
		default: 'Enjoy Chewapp for Free',
		example: 'Enjoy Chewapp for Free',
		description: 'The title of the Banner'
	})
	@IsNotEmpty()
	readonly title: string

	@ApiModelProperty({
		default: 'https://xxxxxxxxx',
		example: 'https://xxxxxxxxx',
		description: 'The imageUrl of the Banner'
	})
	@IsNotEmpty()
	readonly imageUrl: string

	@ApiModelProperty({
		default: 1,
		example: 1,
		description: 'The position of the Banner'
	})
	@Max(20)
	@Min(1)
	@IsNumber()
	@IsNotEmpty()
	readonly position: number

	@ApiModelProperty({
		default: '<h1>Hello</h1>',
		example: '<h1>Hello</h1>',
		description: 'The detail of the Banner'
	})
	@IsOptional()
	readonly detail: string

	@ApiModelProperty({
		default: false,
		example: false,
		description: 'The published of the Banner'
	})
	@IsBoolean()
	@IsNotEmpty()
	readonly published: boolean
}


================================================
FILE: src/modules/bidders/bidders.controller.ts
================================================
import { Controller } from '@nestjs/common';

@Controller('bidders')
export class BiddersController {}


================================================
FILE: src/modules/bidders/bidders.module.ts
================================================
import { Module } from '@nestjs/common';
import { BiddersService } from './bidders.service';
import { BiddersController } from './bidders.controller';

@Module({
  providers: [BiddersService],
  controllers: [BiddersController]
})
export class BiddersModule {}


================================================
FILE: src/modules/bidders/bidders.service.ts
================================================
import { Injectable } from '@nestjs/common';

@Injectable()
export class BiddersService {}


================================================
FILE: src/modules/chats/chats.gateway.ts
================================================
import {
	SubscribeMessage,
	WebSocketGateway,
	OnGatewayInit,
	WebSocketServer,
	OnGatewayConnection,
	OnGatewayDisconnect
} from '@nestjs/websockets'
import { Logger } from '@nestjs/common'
import { Socket, Server } from 'socket.io'

@WebSocketGateway()
export class ChatsGateway
	implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
	@WebSocketServer() server: Server

	private logger: Logger = new Logger('AppGateway')

	@SubscribeMessage('msgToServer')
	handleMessage(client: Socket, payload: string): void {
		this.server.emit('msgToClient', payload)
	}

	afterInit(server: Server) {
		this.logger.log('Init')
	}

	handleDisconnect(client: Socket) {
		this.logger.log(`Client disconnected: ${client.id}`)
	}

	handleConnection(client: Socket, ...args: any[]) {
		this.logger.log(`Client connected: ${client.id}`)
	}
}


================================================
FILE: src/modules/chats/chats.module.ts
================================================
import { Module } from '@nestjs/common'

@Module({})
export class ChatsModule {}


================================================
FILE: src/modules/classes/classes.controller.ts
================================================
import {
	Controller,
	Get,
	Post,
	Patch,
	Delete,
	Param,
	UseGuards,
	Put,
	Body
} from '@nestjs/common'
import {
	ApiUseTags,
	ApiResponse,
	ApiBearerAuth,
	ApiOperation
} from '@nestjs/swagger'
import { ClassEntity } from './entity/class.entity'
import { ClassesService } from './classes.service'
import { AuthGuard } from '@nestjs/passport'
import { CreateClassDto } from './dto/create-class.dto'
import { ReplaceClassDto } from './dto/replace-class.dto'

// @ApiBearerAuth()
// @UseGuards(AuthGuard('jwt'))
@ApiUseTags('classes')
@Controller('classes')
export class ClassesController {
	constructor(private readonly classesService: ClassesService) {}

	@ApiResponse({
		status: 200,
		description: 'The found records',
		type: [ClassEntity]
	})
	@ApiOperation({
		title: 'Retrieve many Classs 👾'
	})
	@Get()
	findAll() {
		return this.classesService.findAll()
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: ClassEntity
	})
	@ApiOperation({
		title: 'Create one Class 👾'
	})
	@Post()
	async insert(@Body() createClassDto: CreateClassDto) {
		const newClass = await this.classesService.insert(createClassDto)

		return newClass
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: ClassEntity
	})
	@ApiOperation({
		title: 'Retrieve one Class 👾'
	})
	@Get(':id')
	findOne(@Param('id') id: string) {
		return this.classesService.findOne(id)
	}

	@ApiOperation({
		title: 'Replace one Class 👾'
	})
	@Put(':id')
	replace(@Param('id') id: string, @Body() replaceClassDto: ReplaceClassDto) {
		return this.classesService.findOneAndReplace(id, replaceClassDto)
	}

	@ApiResponse({
		status: 200,
		description: 'The found record is executed 👾',
		type: Boolean
	})
	@ApiOperation({
		title: 'Delete one Class 👾'
	})
	@Delete(':id')
	remove(@Param('id') id: string) {
		return this.classesService.deleteOne(id)
	}
}


================================================
FILE: src/modules/classes/classes.module.ts
================================================
import { Module } from '@nestjs/common';
import { ClassesController } from './classes.controller';
import { ClassesService } from './classes.service';

@Module({
  controllers: [ClassesController],
  providers: [ClassesService]
})
export class ClassesModule {}


================================================
FILE: src/modules/classes/classes.service.ts
================================================
import {
	Injectable,
	NotFoundException,
	ForbiddenException
} from '@nestjs/common'
import { CreateClassDto } from './dto/create-class.dto'
import { ClassEntity } from './entity/class.entity'
import { getMongoRepository } from 'typeorm'
import { ReplaceClassDto } from './dto/replace-class.dto'

export type Class = any

@Injectable()
export class ClassesService {
	async findAll(): Promise<Class[] | undefined> {
		return getMongoRepository(ClassEntity).find()
	}

	async insert(createClassDto: CreateClassDto): Promise<Class | undefined> {
		const { name } = createClassDto
		const existedClass = await getMongoRepository(ClassEntity).findOne({
			name
		})
		if (existedClass) {
			throw new ForbiddenException('Name already existed.')
		}
		const newClass = await getMongoRepository(ClassEntity).save(
			new ClassEntity({
				...createClassDto
			})
		)
		return newClass
	}
	async findOne(_id: string): Promise<Class | undefined> {
		const foundClass = await getMongoRepository(ClassEntity).findOne({ _id })
		if (!foundClass) {
			throw new NotFoundException('Class not found.')
		}
		return foundClass
	}
	async findOneAndReplace(
		_id: string,
		replaceClassDto: ReplaceClassDto
	): Promise<Class | undefined> {
		const foundClass = await getMongoRepository(ClassEntity).findOne({ _id })
		if (!foundClass) {
			throw new NotFoundException('Class not found.')
		}
		const updateClass = await getMongoRepository(ClassEntity).save(
			new ClassEntity({
				...foundClass,
				...replaceClassDto
			})
		)
		return updateClass
	}
	async deleteOne(_id: string): Promise<boolean | undefined> {
		const foundClass = await getMongoRepository(ClassEntity).findOne({ _id })
		if (!foundClass) {
			throw new NotFoundException('Class not found.')
		}
		return (await getMongoRepository(ClassEntity).delete(foundClass)) && true
	}
}


================================================
FILE: src/modules/classes/dto/create-class.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class CreateClassDto {
	@ApiModelProperty({
		default: '11A1',
		example: '11A1',
		description: 'The name of the Class'
	})
	@IsNotEmpty()
	readonly name: string

	@ApiModelProperty({
		default: 'TVK',
		example: 'TVK',
		description: 'The school of the Class'
	})
	@IsNotEmpty()
	readonly school: string
}


================================================
FILE: src/modules/classes/dto/replace-class.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class ReplaceClassDto {
	@ApiModelProperty({
		default: '11A1',
		example: '11A1',
		description: 'The name of the Class'
	})
	@IsNotEmpty()
	readonly name: string

	@ApiModelProperty({
		default: 'TVK',
		example: 'TVK',
		description: 'The school of the Class'
	})
	@IsNotEmpty()
	readonly school: string
}


================================================
FILE: src/modules/classes/entity/class.entity.ts
================================================
import { Entity, Column, ObjectIdColumn } from 'typeorm'
import { ApiModelProperty } from '@nestjs/swagger'
import { uuidv4 } from '../../../utils'

@Entity({
	name: 'classes',
	orderBy: {
		createdAt: 'ASC'
	}
})
export class ClassEntity {
	@ApiModelProperty({ description: 'The _id of the Class' })
	@ObjectIdColumn()
	_id: string

	@ApiModelProperty({ description: 'The name of the Class' })
	@Column()
	name: string

	@ApiModelProperty({ description: 'The school of the Class' })
	@Column()
	school: string

	@ApiModelProperty({ description: 'The createdAt of the Class' })
	@Column()
	createdAt: number
	@ApiModelProperty({ description: 'The updatedAt of the Class' })
	@Column()
	updatedAt: number

	constructor(partial: Partial<ClassEntity>) {
		if (partial) {
			Object.assign(this, partial)
			this._id = this._id || uuidv4()
			this.createdAt = this.createdAt || +new Date()
			this.updatedAt = +new Date()
		}
	}
}


================================================
FILE: src/modules/connections/connection.entity.ts
================================================
import { Entity, ObjectIdColumn, Column } from 'typeorm'
import { uuidv4 } from '../../utils'
// import { Exclude, plainToClass } from 'class-transformer'
import { ApiModelProperty } from '@nestjs/swagger'

import { ConnectionType } from './enum/connection.enum'
import { CreatedByDto } from '../../modules/users/dto/created-by.dto'

@Entity({
	name: 'connections',
	orderBy: {
		createdAt: 'ASC'
	}
})
export class ConnectionEntity {
	@ApiModelProperty({ description: 'The _id of the Connection' })
	@ObjectIdColumn()
	_id: string

	@ApiModelProperty({ description: 'The deal of the Connection' })
	@Column()
	deal: string

	@ApiModelProperty({ description: 'The amount of the Connection' })
	@Column()
	amount: number

	@ApiModelProperty({ description: 'The connectionType of the Connection' })
	@Column()
	connectionType: ConnectionType

	@ApiModelProperty({ description: 'The createdBy of the Connection' })
	@Column()
	connectedBy: CreatedByDto

	@ApiModelProperty({ description: 'The createdAt of the Connection' })
	@Column()
	createdAt: number
	@ApiModelProperty({ description: 'The updatedAt of the Connection' })
	@Column()
	updatedAt: number

	constructor(partial: Partial<ConnectionEntity>) {
		if (partial) {
			Object.assign(this, partial)
			this._id = this._id || uuidv4()
			this.connectionType = ConnectionType.Connected
			this.createdAt = this.createdAt || +new Date()
			this.updatedAt = +new Date()
		}
	}
}


================================================
FILE: src/modules/connections/connections.controller.ts
================================================
import {
	Controller,
	Get,
	Query,
	Request,
	UseGuards,
	Post,
	Body,
	Param
} from '@nestjs/common'
import { ConnectionsService, Connection } from './connections.service'
import { ConnectionEntity } from './connection.entity'
import {
	ApiResponse,
	ApiOperation,
	ApiImplicitQuery,
	ApiBearerAuth,
	ApiUseTags
} from '@nestjs/swagger'
import { AuthGuard } from '@nestjs/passport'
import { ErrorResponseDto } from '../../modules/users/dto/error-response.dto'

@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'))
@ApiResponse({
	status: 401,
	description: 'Unauthorized.',
	type: ErrorResponseDto
})
@ApiResponse({ status: 403, description: 'Forbidden.', type: ErrorResponseDto })
@ApiUseTags('connections')
@Controller('connections')
export class ConnectionsController {
	constructor(private readonly connectionsService: ConnectionsService) { }

	@ApiResponse({
		status: 200,
		description: 'The found records',
		type: [ConnectionEntity]
	})
	@ApiOperation({
		title: 'Retrieve many Connections 👻'
		// description: 'Aaa',
		// operationId: 'aaaa'
	})
	@Get()
	@ApiImplicitQuery({
		name: 'limit',
		description: 'The maximum number of transactions to return',
		required: false,
		type: Number
	})
	@ApiImplicitQuery({
		name: 'offset',
		description: 'The maximum number of transactions to skip',
		required: false,
		type: Number
	})
	findAll(@Query() query, @Request() req): Promise<Connection[]> {
		return this.connectionsService.findAll(query, req)
	}

	@ApiResponse({
		status: 201,
		description: 'The record has been successfully created',
		type: ConnectionEntity
	})
	@ApiOperation({
		title: 'Create one Connection 👻'
	})
	@Post('/:dealId')
	insert(@Param('dealId') dealId: string, @Request() req): Promise<boolean> {
		return this.connectionsService.insert(dealId, req)
	}
}


================================================
FILE: src/modules/connections/connections.module.ts
================================================
import { Module } from '@nestjs/common';
import { ConnectionsController } from './connections.controller';
import { ConnectionsService } from './connections.service';

@Module({
  controllers: [ConnectionsController],
  providers: [ConnectionsService]
})
export class ConnectionsModule {}


================================================
FILE: src/modules/connections/connections.service.ts
================================================
import { Injectable, ForbiddenException } from '@nestjs/common'
import { getMongoRepository } from 'typeorm'
import { ConnectionEntity } from './connection.entity'
import { UserEntity } from '../../modules/users/user.entity'
import { DealEntity } from '../../modules/deals/deal.entity'

export type Connection = any

@Injectable()
export class ConnectionsService {
	async findAll(query: any, req: any): Promise<Connection[]> {
		const { offset, limit } = query
		const { user } = req
		const { _id } = user

		const pipelineArray = []

		if (offset) {
			if (offset < 1) {
				throw new ForbiddenException('The offset must be greater than 0')
			} else {
				pipelineArray.push({
					$skip: +offset
				})
			}
		}

		if (limit) {
			if (limit < 1) {
				throw new ForbiddenException('The limit must be greater than 0')
			} else {
				pipelineArray.push({
					$limit: +limit
				})
			}
		}

		const deal = [
			{
				$lookup: {
					from: 'deals',
					localField: 'deal',
					foreignField: '_id',
					as: 'deal'
				}
			},
			{
				$project: {
					'deal.serviceType': 0,
					'deal.itemType': 0,
					'deal.description': 0,
					'deal.shopName': 0,
					'deal.location': 0,
					'deal.destination': 0,
					'deal.payment': 0,
					'deal.expiredAt': 0,
					'deal.createdAt': 0,
					'deal.updatedAt': 0
				}
			},
			{
				$unwind: {
					path: '$deal',
					preserveNullAndEmptyArrays: true
				}
			}
		]

		const match = [
			{
				$match: {
					$or: [{ connectedBy: _id }, { 'deal.createdBy': _id }]
				}
			}
		]

		const dealCreatedBy = [
			{
				$lookup: {
					from: 'users',
					localField: 'deal.createdBy',
					foreignField: '_id',
					as: 'deal.createdBy'
				}
			},
			{
				$project: {
					'deal.createdBy.email': 0,
					'deal.createdBy.password': 0,
					'deal.createdBy.referralCode': 0,
					'deal.createdBy.verified': 0,
					'deal.createdBy.createdAt': 0,
					'deal.createdBy.updatedAt': 0,
					'deal.createdBy.phone': 0
				}
			},
			{
				$unwind: {
					path: '$deal.createdBy',
					preserveNullAndEmptyArrays: true
				}
			}
		]

		const createdBy = [
			{
				$lookup: {
					from: 'users',
					localField: 'connectedBy',
					foreignField: '_id',
					as: 'connectedBy'
				}
			},
			{
				$project: {
					'connectedBy.email': 0,
					'connectedBy.password': 0,
					'connectedBy.referralCode': 0,
					'connectedBy.verified': 0,
					'connectedBy.createdAt': 0,
					'connectedBy.updatedAt': 0,
					'connectedBy.phone': 0
				}
			},
			{
				$unwind: {
					path: '$connectedBy',
					preserveNullAndEmptyArrays: true
				}
			}
		]

		pipelineArray.push(...deal, ...match, ...dealCreatedBy, ...createdBy)

		return await getMongoRepository(ConnectionEntity)
			.aggregate(pipelineArray)
			.toArray()
	}

	async insert(deal: string, req: any): Promise<Connection> {
		const { user } = req
		const { _id } = user

		const foundDeal = await getMongoRepository(DealEntity).findOne({
			_id: deal
		})

		if (!foundDeal) {
			throw new ForbiddenException('Deal not found')
		}

		const foundAddress = await getMongoRepository(ConnectionEntity).findOne({
			deal,
			connectedBy: _id
		})

		if (foundAddress) {
			throw new ForbiddenException('Connection already existed')
		}

		const newConnection = await getMongoRepository(ConnectionEntity).save(
			new ConnectionEntity({ deal, connectedBy: _id })
		)

		const connectedBy = await getMongoRepository(UserEntity).findOne({
			where: {
				_id: newConnection.connectedBy
			},
			select: ['_id', 'name', 'avatar']
		})

		newConnection.connectedBy = connectedBy

		return newConnection
	}
}


================================================
FILE: src/modules/connections/dto/create-connection.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty, IsEnum, IsOptional } from 'class-validator'

import { ConnectionType } from '../enum/connection.enum'
import { Position } from '../../deals/entity/position.entity'

export class CreateConnectionDto {
	@ApiModelProperty({
		default: 'xxxx-xxxx-xxxx-xxxx',
		example: 'xxxx-xxxx-xxxx-xxxx',
		description: 'The deal of the Connection'
	})
	@IsOptional()
	readonly deal: string

	@ApiModelProperty({
		default: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		example: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		description: 'The location of the Connection'
	})
	@IsNotEmpty()
	readonly location: Position

	@ApiModelProperty({
		default: 69,
		example: 69,
		description: 'The unitNumber of the Connection'
	})
	@IsNotEmpty() // 30m 1h 1h30 2h
	readonly unitNumber: number

	@ApiModelProperty({
		default: 'Đối diện BigC',
		example: 'Đối diện BigC',
		description: 'The remarks of the Connection'
	})
	@IsOptional()
	readonly remarks: string
}


================================================
FILE: src/modules/connections/enum/connection.enum.ts
================================================
export enum ConnectionType {
	Connected = 'Connected',
	Accepted = 'Accepted',
	Declined = 'Declined',
	Completed = 'Completed'
}


================================================
FILE: src/modules/deals/deal.entity.ts
================================================
import { Entity, ObjectIdColumn, Column } from 'typeorm'
import { uuidv4 } from '../../utils'
// import { Exclude, plainToClass } from 'class-transformer'
import { ApiModelProperty } from '@nestjs/swagger'

import { ItemType, ServiceType, PaymentType, DealType } from './enum/deal.enum'
import { Position } from './entity/position.entity'
import { UserEntity } from '../users/user.entity'
import { CreatedByDto } from '../users/dto/created-by.dto'

@Entity({
	name: 'deals',
	orderBy: {
		createdAt: 'ASC'
	}
})
export class DealEntity {
	@ApiModelProperty({ description: 'The _id of the Deal' })
	@ObjectIdColumn()
	_id: string

	@ApiModelProperty({ description: 'The dealType of the Deal' })
	@Column()
	dealType: DealType

	@ApiModelProperty({ description: 'The serviceType of the Deal' })
	@Column()
	serviceType: ServiceType

	@ApiModelProperty({ description: 'The itemType of the Deal' })
	@Column()
	itemType: ItemType

	@ApiModelProperty({ description: 'The items of the Deal' })
	@Column()
	items: string

	@ApiModelProperty({ description: 'The description of the Deal' })
	@Column()
	description: string

	@ApiModelProperty({ description: 'The shopName of the Deal' })
	@Column()
	shopName: string

	@ApiModelProperty({ description: 'The thumbnail of the Deal' })
	@Column()
	thumbnail: string

	@ApiModelProperty({ description: 'The location of the Deal' })
	@Column()
	location: Position

	@ApiModelProperty({ description: 'The destination of the Deal' })
	@Column() // I have no preference auto get location device
	destination: Position

	@ApiModelProperty({ description: 'The expiredAt of the Deal' })
	@Column() // 30m 1h 1h30 2h
	expiredAt: number

	@ApiModelProperty({ description: 'The payment of the Deal' })
	@Column()
	payment: PaymentType

	@ApiModelProperty({ description: 'The createdBy of the Deal' })
	@Column()
	createdBy: CreatedByDto

	@ApiModelProperty({ description: 'The createdAt of the Deal' })
	@Column()
	createdAt: number
	@ApiModelProperty({ description: 'The updatedAt of the Deal' })
	@Column()
	updatedAt: number

	constructor(partial: Partial<DealEntity>) {
		if (partial) {
			Object.assign(this, partial)
			this._id = this._id || uuidv4()
			this.createdAt = this.createdAt || +new Date()
			this.updatedAt = +new Date()
		}
	}
}


================================================
FILE: src/modules/deals/deals.controller.ts
================================================
import {
	Controller,
	Get,
	Post,
	Body,
	Request,
	UseInterceptors,
	UploadedFile,
	UseGuards,
	Param,
	Query
} from '@nestjs/common'
import { DealsService } from './deals.service'
import {
	ApiOperation,
	ApiResponse,
	ApiUseTags,
	ApiImplicitQuery,
	ApiConsumes,
	ApiImplicitFile,
	ApiBearerAuth
} from '@nestjs/swagger'

import { CreateDealDto } from './dto/create-deal.dto'
import { FileInterceptor } from '@nestjs/platform-express'
import { AuthGuard } from '@nestjs/passport'
import { ItemType, ServiceType, DealType } from './enum/deal.enum'
import { ErrorResponseDto } from '../../modules/users/dto/error-response.dto'
import { DealEntity } from './deal.entity'
import { DealResponseDto } from './dto/deal-response.dto'
import { Position } from './entity/position.entity'

@ApiResponse({
	status: 401,
	description: 'Unauthorized.',
	type: ErrorResponseDto
})
@ApiResponse({ status: 403, description: 'Forbidden.', type: ErrorResponseDto })
@ApiUseTags('deals')
@Controller('deals')
export class DealsController {
	constructor(private readonly dealsService: DealsService) {}

	@ApiResponse({
		status: 200,
		description: 'The found records',
		type: [DealResponseDto]
	})
	@ApiOperation({
		title: 'Retrieve many Deals 👻'
		// description: 'Aaa',
		// operationId: 'aaaa'
	})
	// @Get(':searchIn')
	@Get()
	@ApiImplicitQuery({
		name: 'limit',
		description: 'The maximum number of transactions to return',
		required: false,
		type: Number
	})
	@ApiImplicitQuery({
		name: 'offset',
		description: 'The maximum number of transactions to skip',
		required: false,
		type: Number
	})
	@ApiImplicitQuery({
		name: 'itemType',
		description: 'The itemType of the Deal',
		required: false,
		type: ItemType,
		enum: ['None', 'Meal', 'Drinks', 'Desserts', 'Snacks']
	})
	@ApiImplicitQuery({
		name: 'serviceType',
		description: 'The serviceType of the Deal',
		required: false,
		type: ServiceType,
		enum: [
			'FoodDelivery',
			'Pickup',
			'PharmacyPurchase',
			'Queue',
			'OverseasPurchase',
			'Others'
		]
	})
	@ApiImplicitQuery({
		name: 'dealType',
		description: 'The dealType of the Deal',
		required: false,
		type: DealType,
		enum: ['Request', 'Offer']
	})
	findAll(
		// @Param('searchIn') searchIn: string,
		@Query() query
	) {
		// console.log('sdasd', searchIn)
		return this.dealsService.findAll(query)
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: DealResponseDto
	})
	@ApiOperation({
		title: 'Retrieve one Deal 👻'
	})
	@Get(':id')
	findOne(@Param('id') id: string) {
		return this.dealsService.findOne(id)
	}

	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiResponse({
		status: 201,
		description: 'The record has been successfully created.',
		type: DealResponseDto
	})
	@ApiOperation({
		title: 'Create one Deal 👻'
	})
	@Post()
	insert(@Body() createDealDto: CreateDealDto, @Request() req) {
		return this.dealsService.insert(createDealDto, req)
	}
}


================================================
FILE: src/modules/deals/deals.module.ts
================================================
import { Module } from '@nestjs/common'
import { DealsController } from './deals.controller'
import { DealsService } from './deals.service'

@Module({
	controllers: [DealsController],
	providers: [DealsService],
	exports: [DealsService]
})
export class DealsModule {}


================================================
FILE: src/modules/deals/deals.service.ts
================================================
import {
	Injectable,
	NotFoundException,
	ForbiddenException
} from '@nestjs/common'
import { getMongoRepository } from 'typeorm'
import * as geolib from 'geolib'

import { CreateDealDto } from './dto/create-deal.dto'
import { DealEntity } from './deal.entity'
import { ServiceType, ItemType } from './enum/deal.enum'
import { UserEntity } from '../../modules/users/user.entity'

const from = {
	latitude: 10.783,
	longitude: 106.692
}
const to = {
	latitude: 10.807,
	longitude: 106.709
}

console.log(
	'You are ',
	geolib.getDistance(from, to),
	`meters away from ${from}`
)

export type Deal = any

@Injectable()
export class DealsService {
	// TODO:
	async findAll(query): Promise<Deal[] | undefined> {
		// console.log(query)
		const { dealType, serviceType, itemType, offset, limit } = query

		const pipelineArray = []

		if (offset) {
			if (offset < 1) {
				throw new ForbiddenException('The offset must be greater than 0')
			} else {
				pipelineArray.push({
					$skip: +offset
				})
			}
		}

		if (limit) {
			if (limit < 1) {
				throw new ForbiddenException('The limit must be greater than 0')
			} else {
				pipelineArray.push({
					$limit: +limit
				})
			}
		}

		const connections = [
			{
				$lookup: {
					from: 'connections',
					localField: '_id',
					foreignField: 'dealId',
					as: 'connections'
				}
			},
			{
				$addFields: {
					connections: {
						$size: '$connections'
					}
				}
			}
		]

		const createdBy = [
			{
				$lookup: {
					from: 'users',
					localField: 'createdBy',
					foreignField: '_id',
					as: 'createdBy'
				}
			},
			{
				$project: {
					'createdBy.email': 0,
					'createdBy.password': 0,
					'createdBy.referralCode': 0,
					'createdBy.verified': 0,
					'createdBy.createdAt': 0,
					'createdBy.updatedAt': 0,
					'createdBy.phone': 0
				}
			},
			{
				$unwind: {
					path: '$createdBy',
					preserveNullAndEmptyArrays: true
				}
			}
		]

		pipelineArray.push(...connections, ...createdBy)

		// console.log(dealType)

		if (dealType) {
			pipelineArray.push({ $match: { dealType } })
		}

		if (serviceType) {
			pipelineArray.push({ $match: { serviceType } })
		}

		if (itemType) {
			pipelineArray.push({ $match: { itemType } })
		}

		return await getMongoRepository(DealEntity)
			.aggregate(pipelineArray)
			.toArray()
	}

	async findOne(_id: string): Promise<Deal | undefined> {
		const foundDeal = await getMongoRepository(DealEntity).findOne({ _id })

		if (!foundDeal) {
			throw new NotFoundException('Deal not found')
		}

		return foundDeal
	}

	// TODO:
	async insert(createDealDto: CreateDealDto, req: any) {
		// console.log(createDealDto, file, req.user._id)
		try {
			const { user } = req
			const { _id } = user
			const {
				dealType,
				serviceType,
				itemType,
				location,
				destination,
				duration,
				payment
			} = createDealDto

			let convertCreateDealDto
			let newDeal

			if (
				(createDealDto.serviceType === ServiceType.FoodDelivery &&
					createDealDto.itemType === ItemType.None) ||
				(createDealDto.serviceType !== ServiceType.FoodDelivery &&
					createDealDto.itemType !== ItemType.None)
			) {
				throw new ForbiddenException('Service type and Item type is incorrect.')
			}

			// if (file && file.size > 1024 * 1024 * 2) {
			// 	throw new ForbiddenException('The thumbnail is too large to upload')
			// }

			// console.log(createDealDto)

			if (createDealDto.items === 'Anything') {
				convertCreateDealDto = {
					dealType,
					serviceType,
					itemType,
					location: JSON.parse(location.toString()),
					destination: JSON.parse(destination.toString()),
					duration,
					payment
				}

				newDeal = await getMongoRepository(DealEntity).save(
					new DealEntity(convertCreateDealDto)
				)
			} else {
				// console.log(file)

				// if (!file) {
				// 	throw new ForbiddenException('Thumbnail not found.')
				// }

				// const thumbnail = await uploadFile(file)

				convertCreateDealDto = {
					...createDealDto,
					// thumbnail,
					expiredAt: +new Date() + 1000 * createDealDto.duration,
					createdBy: _id
				}

				delete convertCreateDealDto.duration

				if (createDealDto.serviceType !== ServiceType.FoodDelivery) {
					delete convertCreateDealDto.itemType
				}

				newDeal = await getMongoRepository(DealEntity).save(
					new DealEntity(convertCreateDealDto)
				)
			}

			const createdBy = await getMongoRepository(UserEntity).findOne({
				where: {
					_id: newDeal.createdBy
				},
				select: ['_id', 'name', 'avatar']
			})

			newDeal.createdBy = createdBy

			return newDeal
		} catch (error) {
			throw new Error(error)
		}
	}

	async findByUserId(req: any, query: any): Promise<Deal[] | undefined> {
		const { user } = req
		const { _id } = user

		const { dealType } = query

		console.log(_id)

		const pipelineArray = []

		if (_id) {
			pipelineArray.push({ $match: { createdBy: _id } })
		}

		if (dealType) {
			pipelineArray.push({ $match: { dealType } })
		}

		const connections = [
			{
				$lookup: {
					from: 'connections',
					localField: '_id',
					foreignField: 'dealId',
					as: 'connections'
				}
			},
			{
				$addFields: {
					connections: {
						$size: '$connections'
					}
				}
			}
		]

		const createdBy = [
			{
				$lookup: {
					from: 'users',
					localField: 'createdBy',
					foreignField: '_id',
					as: 'createdBy'
				}
			},
			{
				$project: {
					'createdBy.email': 0,
					'createdBy.password': 0,
					'createdBy.referralCode': 0,
					'createdBy.verified': 0,
					'createdBy.createdAt': 0,
					'createdBy.updatedAt': 0,
					'createdBy.phone': 0
				}
			},
			{
				$unwind: {
					path: '$createdBy',
					preserveNullAndEmptyArrays: true
				}
			}
		]

		pipelineArray.push(...connections, ...createdBy)

		return await getMongoRepository(DealEntity)
			.aggregate(pipelineArray)
			.toArray()
	}
}


================================================
FILE: src/modules/deals/dto/create-deal.dto.ts
================================================
import { ApiModelProperty, ApiModelPropertyOptional } from '@nestjs/swagger'
import { IsNotEmpty, IsEnum, IsOptional } from 'class-validator'

import { ItemType, ServiceType, PaymentType, DealType } from '../enum/deal.enum'
import { Position } from '../entity/position.entity'

export class CreateDealDto {
	@ApiModelProperty({
		enum: ['Request', 'Offer'],
		example: 'Request',
		description: 'The deal type of the Deal'
	})
	@IsEnum(DealType)
	@IsNotEmpty()
	readonly dealType: DealType

	@ApiModelProperty({
		enum: [
			'FoodDelivery',
			'Pickup',
			'PharmacyPurchase',
			'Queue',
			'OverseasPurchase',
			'Others'
		],
		example: 'FoodDelivery',
		description: 'The service type of the Deal'
	})
	@IsEnum(ServiceType)
	@IsNotEmpty()
	readonly serviceType: ServiceType

	@ApiModelProperty({
		enum: ['None', 'Meal', 'Drinks', 'Desserts', 'Snacks'],
		example: 'Meal',
		description: 'The item type of the Deal'
	})
	@IsEnum(ItemType)
	@IsNotEmpty()
	readonly itemType: ItemType

	@ApiModelProperty({
		default: 'Nui xào bò lúc lắc',
		example: 'Nui xào bò lúc lắc',
		required: false,
		description: 'The items of the Deal'
	})
	@IsOptional()
	readonly items: string

	@ApiModelProperty({
		default: 'Noodle beef cube',
		example: 'Noodle beef cube',
		required: false,
		description: 'The description of the Deal'
	})
	@IsOptional()
	readonly description: string

	@ApiModelProperty({
		default: 'Tâm Ký',
		example: 'Tâm Ký',
		required: false,
		description: 'The shopName of the Deal'
	})
	@IsOptional()
	readonly shopName: string

	@ApiModelProperty({
		default: 'https://xxx.xxx',
		example: 'https://xxx.xxx',
		required: false,
		description: 'The thumbnail of the Deal'
	})
	@IsOptional()
	thumbnail: string

	@ApiModelPropertyOptional({
		default: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		example: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		description: 'The location of the Deal'
	})
	@IsNotEmpty()
	readonly location: Position

	@ApiModelPropertyOptional({
		default: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		example: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		description: 'The destination of the Deal'
	})
	@IsNotEmpty() // I have no preference auto get location device
	readonly destination: Position

	@ApiModelProperty({
		default: 60 * 60 * 30,
		example: 60 * 60 * 30,
		description: 'The duration for the expiredAt of the Deal'
	})
	@IsNotEmpty() // 30m 1h 1h30 2h
	readonly duration: number

	@ApiModelProperty({
		enum: ['Chewpay', 'Cod'],
		description: 'The payment of the Deal'
	})
	@IsEnum(PaymentType)
	@IsNotEmpty()
	readonly payment: PaymentType
}


================================================
FILE: src/modules/deals/dto/deal-response.dto.ts
================================================
import { ApiModelProperty, ApiModelPropertyOptional } from '@nestjs/swagger'
import { IsNotEmpty, IsEnum, IsOptional } from 'class-validator'

import { ItemType, ServiceType, PaymentType, DealType } from '../enum/deal.enum'
import { Position } from '../entity/position.entity'

export class DealResponseDto {
	@ApiModelProperty({
		enum: ['Request', 'Offer'],
		example: 'Request',
		description: 'The deal type of the Deal'
	})
	@IsEnum(DealType)
	@IsNotEmpty()
	readonly dealType: DealType

	@ApiModelProperty({
		enum: [
			'FoodDelivery',
			'Pickup',
			'PharmacyPurchase',
			'Queue',
			'OverseasPurchase',
			'Others'
		],
		example: 'FoodDelivery',
		description: 'The service type of the Deal'
	})
	@IsEnum(ServiceType)
	@IsNotEmpty()
	readonly serviceType: ServiceType

	@ApiModelProperty({
		enum: ['None', 'Meal', 'Drinks', 'Desserts', 'Snacks'],
		example: 'Meal',
		description: 'The item type of the Deal'
	})
	@IsEnum(ItemType)
	@IsNotEmpty()
	readonly itemType: ItemType

	@ApiModelProperty({
		example: 'Nui xào bò lúc lắc',
		required: false,
		description: 'The items of the Deal'
	})
	@IsOptional()
	readonly items: string

	@ApiModelProperty({
		example: 'Noodle beef cube',
		required: false,
		description: 'The description of the Deal'
	})
	@IsOptional()
	readonly description: string

	@ApiModelProperty({
		example: 'Tâm Ký',
		required: false,
		description: 'The shopName of the Deal'
	})
	@IsOptional()
	readonly shopName: string

	@ApiModelProperty({
		example: 'https://xxx.xxx',
		required: false,
		description: 'The thumbnail of the Deal'
	})
	@IsOptional()
	thumbnail: string

	@ApiModelPropertyOptional({
		example: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		description: 'The location of the Deal'
	})
	@IsNotEmpty()
	readonly location: Position

	@ApiModelPropertyOptional({
		example: {
			latitude: 10.780230999999999,
			longitude: 106.6645121
		},
		description: 'The destination of the Deal'
	})
	@IsNotEmpty() // I have no preference auto get location device
	readonly destination: Position

	@ApiModelProperty({
		example: +new Date() + 60 * 60 * 30,
		description: 'The expiredAtt of the Deal'
	})
	@IsNotEmpty() // 30m 1h 1h30 2h
	readonly expiredAt: number

	@ApiModelProperty({
		enum: ['Chewpay', 'Cod'],
		description: 'The payment of the Deal'
	})
	@IsEnum(PaymentType)
	@IsNotEmpty()
	readonly payment: PaymentType

	@ApiModelProperty({
		example: 10,
		description: 'The connections of the Deal'
	})
	@IsNotEmpty()
	readonly connections: number

	@ApiModelProperty({
		example: +new Date(),
		description: 'The createdAt of the Deal'
	})
	@IsNotEmpty()
	readonly createdAt: number

	@ApiModelProperty({
		example: +new Date(),
		description: 'The updatedAt of the Deal'
	})
	@IsNotEmpty()
	readonly updatedAt: number
}


================================================
FILE: src/modules/deals/entity/position.entity.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'
import { Column } from 'typeorm'

export class Position {
	@ApiModelProperty({
		default: 10.780230999999999,
		example: 10.780230999999999,
		description: 'The latitude of the Position',
		type: Number
	})
	@Column()
	@IsNotEmpty()
	latitude: number

	@ApiModelProperty({
		default: 106.6645121,
		example: 106.6645121,
		description: 'The longitude of the Position',
		type: Number
	})
	@Column()
	@IsNotEmpty()
	longitude: number
}


================================================
FILE: src/modules/deals/enum/deal.enum.ts
================================================
export enum DealType {
	Request = 'Request',
	Offer = 'Offer'
}

export enum ServiceType {
	FoodDelivery = 'FoodDelivery',
	Pickup = 'Pickup',
	PharmacyPurchase = 'PharmacyPurchase',
	Queue = 'Queue',
	OverseasPurchase = 'OverseasPurchase',
	Others = 'Others'
}

export enum ItemType {
	None = 'None',
	Meal = 'Meal',
	Drinks = 'Drinks',
	Desserts = 'Desserts',
	Snacks = 'Snacks'
}

export enum PaymentType {
	Chewpay = 'Chewpay',
	Cod = 'Cod'
}


================================================
FILE: src/modules/events/events.gateway.ts
================================================
import {
  SubscribeMessage,
  WebSocketGateway,
  WebSocketServer,
  MessageBody,
  WsResponse,
} from '@nestjs/websockets';
import { Server } from 'socket.io';
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';

@WebSocketGateway()
export class EventsGateway {
  @WebSocketServer()
  server: Server;

  @SubscribeMessage('message')
  handleMessage(client: any, payload: any): string {
    return 'Hello world!';
  }

  @SubscribeMessage('events')
  findAll(@MessageBody() data: any): Observable<WsResponse<number>> {
    return from([1, 2, 3]).pipe(map(item => ({ event: 'events', data: item })));
  }

  @SubscribeMessage('identity')
  async identity(@MessageBody() data: number): Promise<number> {
    return data;
  }
}


================================================
FILE: src/modules/events/events.module.ts
================================================
import { Module } from '@nestjs/common';

@Module({})
export class EventsModule {}


================================================
FILE: src/modules/students/dto/create-student.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class CreateStudentDto {
	@ApiModelProperty({
		default: 'xxxx-xxxx-xxxx-xxxx',
		example: 'xxxx-xxxx-xxxx-xxxx',
		description: 'The classId of the Student'
	})
	@IsNotEmpty()
	readonly classId: string

	@ApiModelProperty({
		default: 1,
		example: 1,
		description: 'The stt of the Student'
	})
	@IsNotEmpty()
	readonly stt: number

	@ApiModelProperty({
		default: 'Trần Thế Anh',
		example: 'Trần Thế Anh',
		description: 'The fullName of the Student'
	})
	@IsNotEmpty()
	readonly fullName: string
}


================================================
FILE: src/modules/students/dto/replace-student.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class ReplaceStudentDto {
	@ApiModelProperty({
		default: 'xxxx-xxxx-xxxx-xxxx',
		example: 'xxxx-xxxx-xxxx-xxxx',
		description: 'The classId of the Student'
	})
	@IsNotEmpty()
	readonly classId: string

	@ApiModelProperty({
		default: 1,
		example: 1,
		description: 'The stt of the Student'
	})
	@IsNotEmpty()
	readonly stt: number

	@ApiModelProperty({
		default: 'Trần Thế Anh',
		example: 'Trần Thế Anh',
		description: 'The fullName of the Student'
	})
	@IsNotEmpty()
	readonly fullName: string
}


================================================
FILE: src/modules/students/entity/student.entity.ts
================================================
import { Entity, Column, ObjectIdColumn } from 'typeorm'
import { ApiModelProperty } from '@nestjs/swagger'
import { uuidv4 } from '../../../utils'

@Entity({
	name: 'students',
	orderBy: {
		createdAt: 'ASC'
	}
})
export class StudentEntity {
	@ApiModelProperty({ description: 'The _id of the Student' })
	@ObjectIdColumn()
	_id: string

	@ApiModelProperty({ description: 'The classId of the Student' })
	@Column()
	classId: string

	@ApiModelProperty({ description: 'The stt of the Student' })
	@Column()
	stt: number

	@ApiModelProperty({ description: 'The fullname of the Student' })
	@Column()
	fullName: string

	@ApiModelProperty({ description: 'The createdAt of the Student' })
	@Column()
	createdAt: number
	@ApiModelProperty({ description: 'The updatedAt of the Student' })
	@Column()
	updatedAt: number

	constructor(partial: Partial<StudentEntity>) {
		if (partial) {
			Object.assign(this, partial)
			this._id = this._id || uuidv4()
			this.createdAt = this.createdAt || +new Date()
			this.updatedAt = +new Date()
		}
	}
}


================================================
FILE: src/modules/students/students.controller.ts
================================================
import { Controller, Get, Body, Param, Put, Delete, Post } from '@nestjs/common'
import { StudentsService } from './students.service'
import { ApiResponse, ApiOperation, ApiUseTags } from '@nestjs/swagger'
import { StudentEntity } from './entity/student.entity'
import { CreateStudentDto } from './dto/create-student.dto'
import { ReplaceStudentDto } from './dto/replace-student.dto'

// @ApiBearerAuth()
// @UseGuards(AuthGuard('jwt'))
@ApiUseTags('students')
@Controller('students')
export class StudentsController {
	constructor(private readonly studentsService: StudentsService) {}

	@ApiResponse({
		status: 200,
		description: 'The found records',
		type: [StudentEntity]
	})
	@ApiOperation({
		title: 'Retrieve many Students 👾'
	})
	@Get()
	findAll() {
		return this.studentsService.findAll()
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: StudentEntity
	})
	@ApiOperation({
		title: 'Create one Student 👾'
	})
	@Post()
	async insert(@Body() createStudentDto: CreateStudentDto) {
		const newStudent = await this.studentsService.insert(createStudentDto)

		return newStudent
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: StudentEntity
	})
	@ApiOperation({
		title: 'Retrieve one Student 👾'
	})
	@Get(':id')
	findOne(@Param('id') id: string) {
		return this.studentsService.findOne(id)
	}

	@ApiOperation({
		title: 'Replace one Student 👾'
	})
	@Put(':id')
	replace(
		@Param('id') id: string,
		@Body() replaceStudentDto: ReplaceStudentDto
	) {
		return this.studentsService.findOneAndReplace(id, replaceStudentDto)
	}

	@ApiResponse({
		status: 200,
		description: 'The found record is executed 👾',
		type: Boolean
	})
	@ApiOperation({
		title: 'Delete one Student 👾'
	})
	@Delete(':id')
	remove(@Param('id') id: string) {
		return this.studentsService.deleteOne(id)
	}
}


================================================
FILE: src/modules/students/students.module.ts
================================================
import { Module } from '@nestjs/common';
import { StudentsController } from './students.controller';
import { StudentsService } from './students.service';

@Module({
  controllers: [StudentsController],
  providers: [StudentsService]
})
export class StudentsModule {}


================================================
FILE: src/modules/students/students.service.ts
================================================
import {
	Injectable,
	NotFoundException,
	ForbiddenException
} from '@nestjs/common'
import { getMongoRepository } from 'typeorm'
import { StudentEntity } from './entity/student.entity'
import { ReplaceStudentDto } from './dto/replace-student.dto'
import { CreateStudentDto } from './dto/create-student.dto'
import { ClassEntity } from '../../modules/classes/entity/class.entity'

export type Student = any

@Injectable()
export class StudentsService {
	async findAll(): Promise<Student[] | undefined> {
		return getMongoRepository(StudentEntity).find()
	}

	async insert(
		createStudentDto: CreateStudentDto
	): Promise<Student | undefined> {
		const { classId, stt, fullName } = createStudentDto
		const foundClass = await getMongoRepository(ClassEntity).findOne({
			_id: classId
		})
		if (!foundClass) {
			throw new NotFoundException('Class not found.')
		}
		const existedStudent = await getMongoRepository(StudentEntity).findOne({
			where: {
				$or: [
					{ classId, stt },
					{ classId, fullName }
				]
			}
		})
		if (existedStudent) {
			throw new ForbiddenException('Stt or fullName already existed.')
		}
		const newStudent = await getMongoRepository(StudentEntity).save(
			new StudentEntity({
				...createStudentDto
			})
		)
		return newStudent
	}
	async findOne(_id: string): Promise<Student | undefined> {
		const foundStudent = await getMongoRepository(StudentEntity).findOne({
			_id
		})
		if (!foundStudent) {
			throw new NotFoundException('Student not found.')
		}
		return foundStudent
	}
	async findOneAndReplace(
		_id: string,
		replaceStudentDto: ReplaceStudentDto
	): Promise<Student | undefined> {
		const { classId, stt, fullName } = replaceStudentDto
		const foundStudent = await getMongoRepository(StudentEntity).findOne({
			_id
		})
		if (!foundStudent) {
			throw new NotFoundException('Student not found.')
		}
		const foundClass = await getMongoRepository(ClassEntity).findOne({
			_id: classId
		})
		if (!foundClass) {
			throw new NotFoundException('Class not found.')
		}
		const existedStudent = await getMongoRepository(StudentEntity).findOne({
			where: {
				$or: [{ stt }, { fullName }]
			}
		})
		if (existedStudent) {
			throw new ForbiddenException('Stt or fullName already existed.')
		}
		const updateStudent = await getMongoRepository(StudentEntity).save(
			new StudentEntity({
				...foundStudent,
				...replaceStudentDto
			})
		)
		return updateStudent
	}
	async deleteOne(_id: string): Promise<boolean | undefined> {
		const foundStudent = await getMongoRepository(StudentEntity).findOne({
			_id
		})
		if (!foundStudent) {
			throw new NotFoundException('Student not found.')
		}
		return (
			(await getMongoRepository(StudentEntity).delete(foundStudent)) && true
		)
	}
}


================================================
FILE: src/modules/users/dto/create-user.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty, IsEmail, Length } from 'class-validator'

export class CreateUserDto {
	@ApiModelProperty({
		example: 'trinhchin',
		description: 'The name of the User'
	})
	@Length(5, 20)
	@IsNotEmpty()
	readonly name: string

	@ApiModelProperty({
		example: 'trinhchin.innos@gmail.com',
		description: 'The email of the User'
	})
	@IsEmail()
	@IsNotEmpty()
	readonly email: string

	@ApiModelProperty({
		example: '0',
		description: 'The password of the User'
	})
	@IsNotEmpty()
	readonly password: string

	@ApiModelProperty({
		example: '12341234',
		description: 'The referralCode of the User'
	})
	@Length(8, 8)
	@IsNotEmpty()
	readonly referralCode: string
}


================================================
FILE: src/modules/users/dto/created-by.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class CreatedByDto {
  @ApiModelProperty({
    example: 'xxxx-xxxx-xxxx-xxxx',
    description: 'The _id of the CreatedBy'
  })
  @IsNotEmpty()
  readonly _id: string

  @ApiModelProperty({
    example: 'xxxxxxxxxx',
    description: 'The name of the CreatedBy'
  })
  @IsNotEmpty()
  readonly name: string

  @ApiModelProperty({
    example: 'https://xxx.xxx',
    description: 'The avatar of the CreatedBy'
  })
  @IsNotEmpty()
  readonly avatar: string
}


================================================
FILE: src/modules/users/dto/error-response.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class ErrorResponseDto {
  @ApiModelProperty({
    example: '40x',
    description: 'The statusCode of the ErrorResponse'
  })
  @IsNotEmpty()
  readonly statusCode: string

  @ApiModelProperty({
    example: 'xxxxxxxxxx',
    description: 'The message of the ErrorResponse'
  })
  @IsNotEmpty()
  readonly message: string

  @ApiModelProperty({
    example: '2019-11-28T03:08:10.980Z',
    description: 'The timestamp of the ErrorResponse'
  })
  @IsNotEmpty()
  readonly timestamp: string

  @ApiModelProperty({
    example: '/v1',
    description: 'The path of the ErrorResponse'
  })
  @IsNotEmpty()
  readonly path: string
}


================================================
FILE: src/modules/users/dto/login-response.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'
import { UserEntity } from '../user.entity'

export class LoginResponseDto {
	@ApiModelProperty({
		example: UserEntity,
		description: 'The user of the LoginResponse'
	})
	@IsNotEmpty()
	readonly user: UserEntity

	@ApiModelProperty({
		example: 'xxxxxxxxxx',
		description: 'The accessToken of the LoginResponse'
	})
	@IsNotEmpty()
	readonly accessToken: string

	@ApiModelProperty({
		example: 60 * 60 * 24 * 30,
		description: 'The expiresIn of the LoginResponse'
	})
	@IsNotEmpty()
	readonly expiresIn: number
}


================================================
FILE: src/modules/users/dto/login-user.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty, IsEmail } from 'class-validator'

export class LoginUserDto {
	@ApiModelProperty({
		default: 'trinhchin.innos@gmail.com',
		example: 'trinhchin.innos@gmail.com',
		description: 'The email of the User'
	})
	@IsEmail()
	@IsNotEmpty()
	readonly email: string

	@ApiModelProperty({
		default: '0',
		example: '0',
		description: 'The password of the User'
	})
	@IsNotEmpty()
	readonly password: string
}


================================================
FILE: src/modules/users/dto/otp-response.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class OtpResponseDto {
	@ApiModelProperty({
		default: 678900,
		example: 678900,
		description: 'The otp of the OtpResponseDto'
	})
	@IsNotEmpty()
	readonly otp: number

	@ApiModelProperty({
		default: '60s',
		example: '60s',
		description: 'The remaining of the OtpResponseDto'
	})
	@IsNotEmpty()
	readonly remaining: string
}


================================================
FILE: src/modules/users/dto/replace-user.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger';
import { IsNotEmpty, Length } from 'class-validator';

export class ReplaceUserDto {
  @ApiModelProperty({ description: 'The name of the User' })
  @Length(5, 20)
  @IsNotEmpty()
  readonly name: string;

  @ApiModelProperty({ description: 'The referralCode of the User' })
  @Length(8, 8)
  @IsNotEmpty()
  readonly referralCode: string;
}


================================================
FILE: src/modules/users/dto/update-user.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger';
import { Length, IsOptional } from 'class-validator';

export class UpdateUserDto {
  @ApiModelProperty({
    required: false,
    description: 'The name of the User',
  })
  @Length(5, 20)
  @IsOptional()
  readonly name: string;

  @ApiModelProperty({
    required: false,
    description: 'The referralCode of the User',
  })
  @Length(8, 8)
  @IsOptional()
  readonly referralCode: string;
}


================================================
FILE: src/modules/users/dto/upload-response.dto.ts
================================================
import { ApiModelProperty } from '@nestjs/swagger'
import { IsNotEmpty } from 'class-validator'

export class UploadResponseDto {
  @ApiModelProperty({
    example: 'https://xxx.xxx',
    description: 'The path of the UploadResponse'
  })
  @IsNotEmpty()
  readonly url: string
}


================================================
FILE: src/modules/users/index.ts
================================================
import * as speakeasy from 'speakeasy'

const token = speakeasy.totp({
	secret: '123456',
	encoding: 'base32'
})

const tokenValidates = speakeasy.totp.verify({
	secret: '123456',
	encoding: 'base32',
	token,
	window: 0
})

console.log(tokenValidates)


================================================
FILE: src/modules/users/user.entity.ts
================================================
import { Entity, ObjectIdColumn, Column } from 'typeorm'
import { uuidv4 } from '../../utils'
import { Exclude, plainToClass } from 'class-transformer'
import { ApiModelProperty } from '@nestjs/swagger'
import { Position } from '../../modules/deals/entity/position.entity'

@Entity({
	name: 'users',
	orderBy: {
		createdAt: 'ASC'
	}
})
export class UserEntity {
	@ApiModelProperty({ description: 'The _id of the User' })
	@ObjectIdColumn()
	_id: string

	// basic

	@ApiModelProperty({ description: 'The name of the User' })
	@Column()
	name: string

	@ApiModelProperty({ description: 'The email of the User' })
	@Column()
	email: string

	@ApiModelProperty({ description: 'The password of the User' })
	@Exclude()
	@Column()
	password: string

	@ApiModelProperty({ description: 'The referralCode of the User' })
	@Column()
	referralCode: string

	@ApiModelProperty({ description: 'The search location of the User' })
	@Column()
	searchIn: Position

	// @Column()
	// countryCode: string; // Vietname +84
	// @Column()
	// phone: string; // 0704498756
	// @Column()
	// verified: boolean; // false
	// @Column()
	// authyId: string; // null

	@ApiModelProperty({ description: 'The avatar of the User' })
	@Column()
	avatar: string

	@ApiModelProperty({ description: 'The phone of the User' })
	@Column()
	phone: string

	@ApiModelProperty({ description: 'The verified of the User' })
	@Column()
	verified: boolean

	@ApiModelProperty({ description: 'The createdAt of the User' })
	@Column()
	createdAt: number
	@ApiModelProperty({ description: 'The updatedAt of the User' })
	@Column()
	updatedAt: number

	constructor(partial: Partial<UserEntity>) {
		if (partial) {
			Object.assign(this, partial)
			this._id = this._id || uuidv4()
			this.avatar =
				this.avatar ||
				'https://res.cloudinary.com/chnirt/image/upload/v1573662028/rest/2019-11-13T16:20:22.699Z.png'
			this.verified = this.verified || false
			this.createdAt = this.createdAt || +new Date()
			this.updatedAt = +new Date()
		}
	}
}


================================================
FILE: src/modules/users/users.controller.ts
================================================
import {
	Controller,
	UseGuards,
	Post,
	Get,
	Param,
	ClassSerializerInterceptor,
	UseInterceptors,
	Put,
	Patch,
	Body,
	Delete,
	UploadedFile,
	Request
} from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport'
import {
	ApiBearerAuth,
	ApiUseTags,
	ApiOperation,
	ApiResponse,
	ApiConsumes,
	ApiImplicitFile
} from '@nestjs/swagger'
import { FileInterceptor } from '@nestjs/platform-express'
import * as jwt from 'jsonwebtoken'

import { UserEntity } from './user.entity'
import { CreateUserDto } from './dto/create-user.dto'
import { UsersService } from './users.service'
import { UpdateUserDto } from './dto/update-user.dto'
import { ReplaceUserDto } from './dto/replace-user.dto'
import { ErrorResponseDto } from './dto/error-response.dto'
import { LoginResponseDto } from './dto/login-response.dto'
import { OtpResponseDto } from './dto/otp-response.dto'
import { AuthService } from '../../auth/auth.service'

@ApiResponse({
	status: 401,
	description: 'Unauthorized.',
	type: ErrorResponseDto
})
@ApiResponse({ status: 403, description: 'Forbidden.', type: ErrorResponseDto })
@UseInterceptors(ClassSerializerInterceptor)
@ApiUseTags('users')
@Controller('users')
export class UsersController {
	constructor(
		private readonly authService: AuthService,
		private readonly userService: UsersService
	) {}

	@ApiResponse({
		status: 200,
		description: 'The found records',
		type: [UserEntity]
	})
	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Retrieve many Users 👻'
	})
	@Get()
	findAll() {
		return this.userService.findAll()
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: LoginResponseDto
	})
	@ApiOperation({
		title: 'Create one User 👻'
	})
	@Post()
	async insert(@Body() createUserDto: CreateUserDto) {
		const newUser = await this.userService.insert(createUserDto)

		const loginResponseDto = await this.authService.login(newUser)

		return loginResponseDto
	}

	@ApiResponse({
		status: 200,
		description: 'The found record',
		type: UserEntity
	})
	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Retrieve one User 👻'
	})
	@Get(':id')
	findOne(@Param('id') id: string) {
		return this.userService.findOne(id)
	}

	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Update one User 👻'
	})
	@Patch(':id')
	update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
		return this.userService.findOneAndUpdate(id, updateUserDto)
	}

	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Replace one User 👻'
	})
	@Put(':id')
	replace(@Param('id') id: string, @Body() replaceUserDto: ReplaceUserDto) {
		return this.userService.findOneAndReplace(id, replaceUserDto)
	}

	@ApiResponse({
		status: 200,
		description: 'The found record is executed 👻',
		type: Boolean
	})
	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Delete one User 👻'
	})
	@Delete(':id')
	remove(@Param('id') id: string) {
		return this.userService.deleteOne(id)
	}

	@ApiResponse({
		status: 200,
		description: 'The found record is executed',
		type: Boolean
	})
	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiOperation({
		title: 'Update one Avatar for current User 👻'
	})
	@Post('avatar')
	@ApiConsumes('multipart/form-data')
	@ApiImplicitFile({
		name: 'avatar',
		required: true,
		description: 'Send one file'
	})
	@UseInterceptors(FileInterceptor('avatar'))
	updateAvatar(@Request() req, @UploadedFile() file) {
		const { user } = req
		const { _id } = user

		return this.userService.updateAvatar(_id, file)
	}

	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiResponse({
		status: 201,
		description: 'The found record is executed',
		type: OtpResponseDto
	})
	@ApiOperation({
		title: 'Otp one User 👻'
	})
	@Post('/otp/:phone')
	otp1(@Request() req, @Param('phone') phone: string) {
		const { user } = req
		const { _id } = user

		return this.userService.otp(_id, phone)
	}

	@ApiBearerAuth()
	@UseGuards(AuthGuard('jwt'))
	@ApiResponse({
		status: 200,
		description: 'The found record is executed',
		type: LoginResponseDto
	})
	@ApiOperation({
		title: 'Verify one User 👻'
	})
	@Post('/verify/:otp')
	async verify(
		@Request() req,
		@Param('otp') otp: string
	): Promise<LoginResponseDto | undefined> {
		const { user } = req
		const { _id } = user

		const updateUser = await this.userService.verify(_id, otp)

		const loginResponseDto = await this.authService.login(updateUser)

		return loginResponseDto
	}
}


================================================
FILE: src/modules/users/users.module.ts
================================================
import { Module } from '@nestjs/common'
import { UsersService } from './users.service'
import { UsersController } from './users.controller'
import { AuthModule } from '../../auth/auth.module'

@Module({
	imports: [AuthModule],
	controllers: [UsersController],
	providers: [UsersService],
	exports: [UsersService]
})
export class UsersModule {}


================================================
FILE: src/modules/users/users.service.ts
================================================
import {
	Injectable,
	ForbiddenException,
	NotFoundException
} from '@nestjs/common'
import { CreateUserDto } from './dto/create-user.dto'
import { getMongoRepository } from 'typeorm'
import * as speakeasy from 'speakeasy'
import { Validator } from 'class-validator'

import { UserEntity } from './user.entity'
import { hashPassword } from '../../utils'
import { UpdateUserDto } from './dto/update-user.dto'
import { ReplaceUserDto } from './dto/replace-user.dto'

import { uploadFile } from '../../shared'

import { SPEAKEASY_SECRET, SPEAKEASY_STEP } from '../../environments'
import { OtpResponseDto } from './dto/otp-response.dto'

const validator = new Validator()
export type User = any

@Injectable()
export class UsersService {
	async insert(createUserDto: CreateUserDto): Promise<User | undefined> {
		const { email } = createUserDto

		const existedUser = await getMongoRepository(UserEntity).findOne({ email })

		if (existedUser) {
			throw new ForbiddenException('Email already existed.')
		}

		const newUser = await getMongoRepository(UserEntity).save(
			new UserEntity({
				...createUserDto,
				password: await hashPassword(createUserDto.password)
			})
		)

		return newUser
	}

	async findAll(): Promise<User[] | undefined> {
		return getMongoRepository(UserEntity).find()
	}

	async findOne(_id: string): Promise<User | undefined> {
		const foundUser = await getMongoRepository(UserEntity).findOne({ _id })

		if (!foundUser) {
			throw new NotFoundException('User not found.')
		}

		return foundUser
	}

	async findOneAndReplace(
		_id: string,
		replaceUserDto: ReplaceUserDto
	): Promise<User | undefined> {
		const foundUser = await getMongoRepository(UserEntity).findOne({ _id })

		if (!foundUser) {
			throw new NotFoundException('User not found.')
		}

		const updateUser = await getMongoRepository(UserEntity).save(
			new UserEntity({
				...foundUser,
				...replaceUserDto
			})
		)

		return updateUser
	}

	async findOneAndUpdate(
		_id: string,
		updateUserDto: UpdateUserDto
	): Promise<User | undefined> {
		const foundUser = await getMongoRepository(UserEntity).findOne({ _id })

		if (!foundUser) {
			throw new NotFoundException('User not found.')
		}

		const updateUser = await getMongoRepository(UserEntity).save(
			new UserEntity({
				...foundUser,
				...updateUserDto
			})
		)

		return updateUser
	}

	async deleteOne(_id: string): Promise<boolean | undefined> {
		const foundUser = await getMongoRepository(UserEntity).findOne({ _id })

		if (!foundUser) {
			throw new NotFoundException('User not found.')
		}

		return (await getMongoRepository(UserEntity).delete(foundUser))
			? true
			: false
	}

	async findOneWithEmail(email: string): Promise<User | undefined> {
		const foundUser = await getMongoRepository(UserEntity).findOne({ email })

		if (!foundUser) {
			throw new NotFoundException('User not found.')
		}

		return foundUser
	}

	async updateAvatar(_id: string, file: any): Promise<boolean | undefined> {
		// console.log(_id, file)
		const foundUser = await getMongoRepository(UserEntity).findOne({ _id })

		if (!foundUser) {
			throw new NotFoundException('User not found.')
		}

		foundUser.avatar = await uploadFile(file)

		const updateUser = await getMongoRepository(UserEntity).save(foundUser)

		return updateUser ? true : false
	}

	async otp(_id: string, phone: string): Promise<OtpResponseDto | undefined> {
		validator.isMobilePhone(phone, 'en-SG')

		const foundUser = await getMongoRepository(UserEntity).findOne({
			where: {
				_id,
				verified: false
			}
		})

		if (!foundUser) {
			throw new NotFoundException('User not found.')
		}

		const token = await speakeasy.totp({
			secret: SPEAKEASY_SECRET!,
			encoding: 'base32',
			// digits: SPEAKEASY_DIGITS!
			step: SPEAKEASY_STEP! // 30s
			// window: 1 // pre 30s cur 30s nxt 30s
		})

		const remaining =
			SPEAKEASY_STEP - Math.floor((+new Date() / 1000.0) % SPEAKEASY_STEP) + 's'

		foundUser.phone = phone

		await getMongoRepository(UserEntity).save(foundUser)

		return {
			otp: +token,
			remaining
		}
	}

	async verify(_id: string, otp: string) {
		const foundUser = await getMongoRepository(UserEntity).findOne({
			where: {
				_id,
				verified: false
			}
		})

		if (!foundUser) {
			throw new NotFoundException('User not found.')
		}

		// console.log(otp)

		const verified = await speakeasy.totp.verify({
			secret: SPEAKEASY_SECRET!,
			encoding: 'base32',
			token: otp,
			step: SPEAKEASY_STEP!, // 30s
			window: 1
		})

		// console.log(verified)

		if (verified) {
			foundUser.verified = true

			return await getMongoRepository(UserEntity).save(foundUser)
		}

		throw new ForbiddenException('Otp is incorrect.')
	}
}


================================================
FILE: src/shared/index.ts
================================================
export * from './upload'


================================================
FILE: src/shared/upload/index.ts
================================================
import * as cloudinary from 'cloudinary'

import { CLOUD_NAME, API_KEY, API_SECRET } from '../../environments'

/**
 * Returns image url by upload file.
 *
 * @remarks
 * This method is part of the {@link shared/upload}.
 *
 * @param createReadStream - 1st input number
 * @returns The string mean of `createReadStream`
 *
 * @beta
 */
export const uploadFile = async (file: any): Promise<string> => {
	cloudinary.config({
		cloud_name: CLOUD_NAME!,
		api_key: API_KEY!,
		api_secret: API_SECRET!
	})

	const uniqueFilename = new Date().toISOString()

	const result = await new Promise(async (resolve, reject) => {
		cloudinary.v2.uploader
			.upload_stream(
				{
					folder: 'rest',
					public_id: uniqueFilename,
					tags: 'rest'
				}, // directory and tags are optional
				(err, image) => {
					if (err) {
						reject(err)
					}
					resolve(image)
				}
			)
			.end(file.buffer)
	})

	// tslint:disable-next-line:no-string-literal
	return result['secure_url']
}


================================================
FILE: src/terminus-options.service.ts
================================================
import {
  TerminusEndpoint,
  TerminusOptionsFactory,
  DNSHealthIndicator,
  TerminusModuleOptions,
} from '@nestjs/terminus';
import { Injectable } from '@nestjs/common';

@Injectable()
export class TerminusOptionsService implements TerminusOptionsFactory {
  constructor(private readonly dns: DNSHealthIndicator) {}

  createTerminusOptions(): TerminusModuleOptions {
    const healthEndpoint: TerminusEndpoint = {
      url: '/health',
      healthIndicators: [
        async () => this.dns.pingCheck('google', 'https://google.com'),
      ],
    };
    return {
      endpoints: [healthEndpoint],
    };
  }
}


================================================
FILE: src/utils/index.ts
================================================
export * from './password';
export * from './uuid';


================================================
FILE: src/utils/password/index.ts
================================================
import { hash, compare } from 'bcrypt';

import { SALT } from '../../environments';

/**
 * Returns hashed password by hash password.
 *
 * @remarks
 * This method is part of the {@link utils/password}.
 *
 * @param password - 1st input number
 * @returns The hashed password mean of `password`
 *
 * @beta
 */
export const hashPassword = async (password: string): Promise<string> => {
  return await hash(password, SALT);
};

/**
 * Returns boolean by compare password.
 *
 * @remarks
 * This method is part of the {@link utils/password}.
 *
 * @param password - 1st input number
 * @param hash - The second input number
 * @returns The boolean mean of `password` and `hash`
 *
 * @beta
 */
export const comparePassword = async (
  password: string,
  hash: string,
): Promise<boolean> => {
  return await compare(password, hash);
};


================================================
FILE: src/utils/uuid/index.ts
================================================
/**
 * Returns string by uuidv4.
 *
 * @remarks
 * This method is part of the {@link utils/uuid}.
 *
 * @returns The string
 *
 * @beta
 */
export const uuidv4 = () => {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    const r = (Math.random() * 16) | 0;
    const v = c === 'x' ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
};

/**
 * Returns string by generateUID.
 *
 * @remarks
 * This method is part of the {@link utils/uuid}.
 *
 * @returns The string
 *
 * @beta
 */
export const generateUID = () => {
  // I generate the UID from two parts here
  // to ensure the random number provide enough bits.
  const firstPart = (Math.random() * 46656) | 0;
  const secondPart = (Math.random() * 46656) | 0;
  const newFirstPart = ('000' + firstPart.toString(36)).slice(-3);
  const newSecondPart = ('000' + secondPart.toString(36)).slice(-3);
  return newFirstPart + newSecondPart;
};

// console.log(uuidv4())
// console.log(generateUID())


================================================
FILE: ssl/ca_bundle.crt
================================================
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----

================================================
FILE: ssl/certificate.crt
================================================
-----BEGIN CERTIFICATE-----
MIIFjDCCBHSgAwIBAgISAxzigJJ7e4L0h878QlVRGOH6MA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTExMTAwMzM2MDFaFw0y
MDAyMDgwMzM2MDFaMDUxMzAxBgNVBAMTKm5lc3Rqcy1yZXN0ZnVsLWJlc3QtcHJh
Y3RpY2UuaGVyb2t1YXBwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAKOMUnWgi/uPB3Kr8Ay51rzVES/Tr8EaebjRKSSOGw8h4ZNaPAeDg7ZH1PvV
m5JexIJ4EAfd2jSV8GZZl7xBmG1GWgyjnWnQTwJ643F2EK2UNC5KLaCvMyeDkNTl
JW2Ur/0nCR8oUEBAx9QsbIJ0HhWofDRTOttOcrfsjnqtnWJl20PNeDS7NGjewUhe
+Yk1Z705mGZezxeU3dNmQnpGEFxo6p5Y/M8maD04LxZUUKxReeLPDHGYuNkY+klq
Y8FReaXJTTFH92XIOS4ECwvLk6XgCwiX+xCH3gerdBIiuP6LTA2RNf0lhs24duhI
eGHwJeYSvmVfUXS3V11OyDcv7qUCAwEAAaOCAn8wggJ7MA4GA1UdDwEB/wQEAwIF
oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAd
BgNVHQ4EFgQUSk/oIsx9g6CBliPWqcsK52nF6CUwHwYDVR0jBBgwFoAUqEpqYwR9
3brm0Tm3pkVl7/Oo7KEwbwYIKwYBBQUHAQEEYzBhMC4GCCsGAQUFBzABhiJodHRw
Oi8vb2NzcC5pbnQteDMubGV0c2VuY3J5cHQub3JnMC8GCCsGAQUFBzAChiNodHRw
Oi8vY2VydC5pbnQteDMubGV0c2VuY3J5cHQub3JnLzA1BgNVHREELjAsgipuZXN0
anMtcmVzdGZ1bC1iZXN0LXByYWN0aWNlLmhlcm9rdWFwcC5jb20wTAYDVR0gBEUw
QzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDov
L2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdgCy
HgXMi6LNiiBOh2b5K7mKJSBna9r6cOeySVMt74uQXgAAAW5TmOwuAAAEAwBHMEUC
ICFD2zRfc3lLDyFv8ziOkIhGAqm3m0OfsLUfEfBsOFD3AiEA1RW78rTM9GSCvcfz
ng7JtgocQGoCwRtHUOwueNxRlGUAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkG
jbIImjfZEwAAAW5TmOxgAAAEAwBHMEUCIQDG6XXZbQvg+OKdAJ6F5bEtRvdH2ZOO
MFrErGSfbA5QdgIgFzhbbaFKGwk4G+kO2+mJYo7clgLR+WIuW/C8fQSIOI8wDQYJ
KoZIhvcNAQELBQADggEBACksWIQEdpa9cuDVw/gsOqnbNirDZCJI3JqM58zWRxSl
kOlBmsmQRzHeveR6CSu/GOiR4HeFF+To0zjBX8U14ufwNOj5eS0jVkwEgN5weWbh
inbWc1PsFuwzISYrG0ssDXP2ty+8Dpg3WjfZg9RE36PsQuH3rQl1A6i0uz9vVASu
bccgjJWVvsJF72DPNzOAD9T8wdcXvKrVb/GU19D1s5NHDUcogBqRSPrjUuLiW8BN
p6F8UMV1iXbPDSVdI71sDhpaBt7EP6jwBgfDnGuNLrUDBg7WsmtQrBdbg5oRhTw0
5FiaP0roSCHV9gpH3R+VgLmLbRa8o6ssWYlS8vEoA7A=
-----END CERTIFICATE-----

================================================
FILE: ssl/private.key
================================================
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCjjFJ1oIv7jwdy
q/AMuda81REv06/BGnm40SkkjhsPIeGTWjwHg4O2R9T71ZuSXsSCeBAH3do0lfBm
WZe8QZhtRloMo51p0E8CeuNxdhCtlDQuSi2grzMng5DU5SVtlK/9JwkfKFBAQMfU
LGyCdB4VqHw0UzrbTnK37I56rZ1iZdtDzXg0uzRo3sFIXvmJNWe9OZhmXs8XlN3T
ZkJ6RhBcaOqeWPzPJmg9OC8WVFCsUXnizwxxmLjZGPpJamPBUXmlyU0xR/dlyDku
BAsLy5Ol4AsIl/sQh94Hq3QSIrj+i0wNkTX9JYbNuHboSHhh8CXmEr5lX1F0t1dd
Tsg3L+6lAgMBAAECggEAB+R4Vj+D1y/1JrjawhfLH8cTJ+u0n5es0sPFPdkIapfd
UERv1oJSPjRZODimuU7kIiezyQK7yliHXBPBbs77kXUZA2Mh8D6ikXa8uHeqIQug
jltQuGBmvObB0S79uhFi13n6xrYl/p53BkDekQJphpdgPGYygO2C5mf+uDeIpibl
u4TwRZmELPjY7ZPJlq0Sv+G93FDbF5yLa4/sQwrjfPFBq0XDEmN4r2kcCGZwpq0L
BIw4Jl+XnNU/HHPvBShe5Nx+URgQuGkWCHmpChYRAfXjLZk65ZomcfFVgTgxg786
gmZZdpHr129F9BAwDcp5QMfl1zs4g4qlN8c9ttxgQQKBgQDcBhGdvt4aNNFbxVWw
pr7hwmryYYzR64Wnp3IjaIMpCabOzsIVoS/8kq60l7c9aag5KgJoqLNZfUeue38H
oDXnAkflXveI41VbpwleAAb3+R1d6jlIJjJaJtAX1byivLkqXl4qjoau+040myKj
7POhUgNjcGEtyClvNgkAY4nfPwKBgQC+SkC6F9LQSeDinhFWty9kjaeL15hnjJMR
QJ7fzPkziFZLlEa9ggv5CBJD+k/uXguLRxt6IaHCE4uwQPALGL0iMC6xaAfCOLJz
ojOJxlYOuLjDSPeylAQ+QjlM62zF23JvQlc1/QZBsgBFfCNWkDHpHhyiQi6Cv7mp
yr42fxTdGwKBgCi+VvTHK4nezgYYfM3BkwdrYTKRLeqRmqZ5M4GrEN7AksspLnei
6afz4bY/ggc1UZmEVf3bf5rKwENnSxa2bETi/z1SYLRQpLXcMLffeWriDrYdcY4S
xLA9D7vaMJxSJlfaMcXfrsEoeEr1j2ybrGHrNgVsAhLgRgv6DaCszhMxAoGBALQW
Q7GacEntUSZHH/OoQ/Lu2LzQ2gxNjrWKKZF2Q/WQNtMqTdR1qe0RxW+OCm11lYlH
T2rDP3oT02SH4GUwEXa0kMwWvxkBXWlv/USLbtBZ44n1mW3pBScCt4XjXDrYFzHS
YATZJD2yPu2DsVHv/zw24jRxW+Ejn4tgM6oRlOY3AoGAFKbGbnHHP39shBPWKi5A
yQwy1pmgrShiptzHCghrRyT0zuXsOI/570DsdG8bbo2QolF5vtGVjJO1x8inDjGH
hJMk0CS3IZYLctM1/H1gZMT50sAyyTEsTxcHmEXROfV0X9cLzmGxC592nCr6NRwz
U0RWwhjG3bKfzRWTy2trtQs=
-----END PRIVATE KEY-----

================================================
FILE: static/index.html
================================================
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<meta http-equiv="X-UA-Compatible" content="ie=edge" />
		<link
			rel="stylesheet"
			href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
			integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ"
			crossorigin="anonymous"
		/>
		<title>Nestjs SocketIO</title>
		<link rel="stylesheet" href="styles.css" />
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script
			type="text/javascript"
			src="https://cdn.socket.io/socket.io-1.4.5.js"
		></script>
	</head>
	<body>
		<div id="app" class="container">
			<div class="row">
				<div class="col-md-6 offset-md-3 col-sm-12">
					<h1 class="text-center">{{ title }}</h1>
					<br />
					<div id="status"></div>
					<div id="chat">
						<input
							type="text"
							v-model="name"
							id="username"
							class="form-control"
							placeholder="Enter name..."
						/>
						<br />
						<div class="card">
							<div id="messages" class="card-block">
								<ul>
									<li v-for="message of messages">
										{{ message.name }}: {{ message.text }}
									</li>
								</ul>
							</div>
						</div>
						<br />
						<textarea
							id="textarea"
							class="form-control"
							v-model="text"
							placeholder="Enter message..."
						></textarea>
						<br />
						<button id="send" class="btn" @click.prevent="sendMessage">
							Send
						</button>
					</div>
				</div>
			</div>
		</div>

		<script src="main.js"></script>
	</body>
</html>


================================================
FILE: static/main.js
================================================
const app = new Vue({
	el: '#app',
	data: {
		title: 'Nestjs Websockets Chat',
		name: '',
		text: '',
		messages: [],
		socket: null
	},
	methods: {
		sendMessage() {
			if (this.validateInput()) {
				const message = {
					name: this.name,
					text: this.text
				}
				this.socket.emit('msgToServer', message)
				this.text = ''
			}
		},
		receivedMessage(message) {
			this.messages.push(message)
		},
		validateInput() {
			return this.name.length > 0 && this.text.length > 0
		}
	},
	created() {
		this.socket = io('https://nestjs-restful-best-practice.herokuapp.com')
		this.socket.on('msgToClient', message => {
			this.receivedMessage(message)
		})
	}
})


================================================
FILE: static/style.css
================================================
  #messages {
    height: 300px;
    overflow-y: scroll;
  }

  #app {
    margin-top: 2rem;
  }

================================================
FILE: test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('AppController (e2e)', () => {
  let app;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  it('/ (GET)', () => {
    return request(app.getHttpServer())
      .get('/')
      .expect(200)
      .expect('Hello World!');
  });
});


================================================
FILE: test/jest-e2e.json
================================================
{
  "moduleFileExtensions": ["js", "json", "ts"],
  "rootDir": ".",
  "testEnvironment": "node",
  "testRegex": ".e2e-spec.ts$",
  "transform": {
    "^.+\\.(t|j)s$": "ts-jest"
  }
}


================================================
FILE: tsconfig.build.json
================================================
{
  "extends": "./tsconfig.json",
  "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./src",
    "incremental": true,
    "paths": {
      "@auth": ["auth"],
      "@common": ["common"],
      "@config": ["config"],
      "@environments": ["environments"],
      "@models": ["models"],
      "@shared": ["shared"],
      "@utils": ["utils"],
      "@validations": ["validations"]
    }
  },
  "exclude": ["node_modules", "dist"]
}


================================================
FILE: tslint.json
================================================
{
	"defaultSeverity": "error",
	"extends": ["tslint:recommended"],
	"jsRules": {
		"no-unused-expression": true
	},
	"rules": {
		"quotemark": [true, "single"],
		"member-access": [false],
		"ordered-imports": [false],
		"max-line-length": [true, 150],
		"member-ordering": [false],
		"interface-name": [false],
		"arrow-parens": false,
		"object-literal-sort-keys": false,

		"variable-name": [true, "allow-leading-underscore"],
		"indent": false,
		"trailing-comma": false,
		"semicolon": false,
		"no-bitwise": false
	},
	"rulesDirectory": []
}


================================================
FILE: webpack.config.js
================================================
const webpack = require('webpack')
const path = require('path')
const nodeExternals = require('webpack-node-externals')
const chalk = require('chalk')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
	.BundleAnalyzerPlugin

module.exports = {
	entry: ['webpack/hot/poll?100', './src/main.ts'],
	watch: true,
	target: 'node',
	externals: [
		nodeExternals({
			whitelist: ['webpack/hot/poll?100']
		})
	],
	module: {
		rules: [
			{
				test: /.tsx?$/,
				use: 'ts-loader',
				exclude: /node_modules/
			}
		]
	},
	mode: 'development',
	resolve: {
		extensions: ['.tsx', '.ts', '.js']
	},
	plugins: [
		new webpack.HotModuleReplacementPlugin(),
		new webpack.WatchIgnorePlugin([/\.js$/, /\.d\.ts$/]),
		new ProgressBarPlugin({
			format:
				chalk.hex('#6c5ce7')('build ') +
				chalk.hex('#0984e3')('▯:bar▯ ') +
				// chalk.red('▯ :bar ▯ ') +
				chalk.hex('#00b894')('(:percent) ') +
				// chalk.green(':percent ') +
				chalk.hex('#ffeaa7')(':msg'),
			// chalk.blue('( :elapsed s )')
			complete: '▰',
			incomplete: '▱',
			clear: false
		}),
		new BundleAnalyzerPlugin({
			analyzerMode: 'static',
			analyzerHost: '127.0.0.1',
			analyzerPort: '8888',
			reportFilename: process.env.NODE_ENV === 'development' && 'report.html',
			openAnalyzer: false,
			generateStatsFile: false,
			statsFilename: 'stats.json'
		}),
		new webpack.BannerPlugin({
			banner: 'require("source-map-support").install();',
			raw: true,
			entryOnly: false
		})
	],
	optimization: {
		removeAvailableModules: false,
		removeEmptyChunks: false,
		splitChunks: false
	},
	output: {
		pathinfo: false
		// path: path.join(__dirname, 'dist'),
		// filename: 'server.js'
	}
}
Download .txt
gitextract_xs5wvu5j/

├── .gitignore
├── .prettierrc
├── .well-known/
│   └── acme-challenge/
│       └── 3fEzNe2klZ1GLASfExbFL6LPbdmqZf2YmefYhRT-kwk
├── LICENSE
├── Procfile
├── README.md
├── nest-cli.json
├── package.json
├── src/
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   ├── assets/
│   │   └── templates/
│   │       └── udacity-index.html
│   ├── auth/
│   │   ├── auth.module.ts
│   │   ├── auth.service.ts
│   │   ├── facebook.strategy.ts
│   │   ├── google.strategy.ts
│   │   ├── jwt.strategy.ts
│   │   └── local.strategy.ts
│   ├── common/
│   │   ├── filters/
│   │   │   └── http-exception.filter.ts
│   │   ├── index.ts
│   │   ├── interceptors/
│   │   │   ├── exception.interceptor.ts
│   │   │   ├── http-cache.interceptor.ts
│   │   │   ├── logging.interceptor.ts
│   │   │   ├── timeout.interceptor.ts
│   │   │   └── transform.interceptor.ts
│   │   ├── middleware/
│   │   │   └── logger.middleware.ts
│   │   ├── pipes/
│   │   │   └── validation.pipe.ts
│   │   └── wiston/
│   │       └── index.ts
│   ├── config/
│   │   ├── cache/
│   │   │   └── index.ts
│   │   ├── index.ts
│   │   ├── logger/
│   │   │   └── index.ts
│   │   └── typeorm/
│   │       └── index.ts
│   ├── config.orm.ts
│   ├── environments/
│   │   └── index.ts
│   ├── main.ts
│   ├── modules/
│   │   ├── addresses/
│   │   │   ├── address.entity.ts
│   │   │   ├── addresses.controller.ts
│   │   │   ├── addresses.module.ts
│   │   │   ├── addresses.service.ts
│   │   │   ├── dto/
│   │   │   │   ├── create-address.dto.ts
│   │   │   │   └── query-address.dto.ts
│   │   │   └── enum/
│   │   │       └── address.enum.ts
│   │   ├── attendance/
│   │   │   ├── attendance.controller.ts
│   │   │   ├── attendance.module.ts
│   │   │   ├── attendance.service.ts
│   │   │   ├── dto/
│   │   │   │   ├── create-attendance.dto.ts
│   │   │   │   └── replace-attendance.dto.ts
│   │   │   └── entity/
│   │   │       └── attendance.entity.ts
│   │   ├── banners/
│   │   │   ├── banner.entity.ts
│   │   │   ├── banners.controller.ts
│   │   │   ├── banners.module.ts
│   │   │   ├── banners.service.ts
│   │   │   └── dto/
│   │   │       ├── create-banner.dto.ts
│   │   │       └── replace-banner.dto.ts
│   │   ├── bidders/
│   │   │   ├── bidders.controller.ts
│   │   │   ├── bidders.module.ts
│   │   │   └── bidders.service.ts
│   │   ├── chats/
│   │   │   ├── chats.gateway.ts
│   │   │   └── chats.module.ts
│   │   ├── classes/
│   │   │   ├── classes.controller.ts
│   │   │   ├── classes.module.ts
│   │   │   ├── classes.service.ts
│   │   │   ├── dto/
│   │   │   │   ├── create-class.dto.ts
│   │   │   │   └── replace-class.dto.ts
│   │   │   └── entity/
│   │   │       └── class.entity.ts
│   │   ├── connections/
│   │   │   ├── connection.entity.ts
│   │   │   ├── connections.controller.ts
│   │   │   ├── connections.module.ts
│   │   │   ├── connections.service.ts
│   │   │   ├── dto/
│   │   │   │   └── create-connection.dto.ts
│   │   │   └── enum/
│   │   │       └── connection.enum.ts
│   │   ├── deals/
│   │   │   ├── deal.entity.ts
│   │   │   ├── deals.controller.ts
│   │   │   ├── deals.module.ts
│   │   │   ├── deals.service.ts
│   │   │   ├── dto/
│   │   │   │   ├── create-deal.dto.ts
│   │   │   │   └── deal-response.dto.ts
│   │   │   ├── entity/
│   │   │   │   └── position.entity.ts
│   │   │   └── enum/
│   │   │       └── deal.enum.ts
│   │   ├── events/
│   │   │   ├── events.gateway.ts
│   │   │   └── events.module.ts
│   │   ├── students/
│   │   │   ├── dto/
│   │   │   │   ├── create-student.dto.ts
│   │   │   │   └── replace-student.dto.ts
│   │   │   ├── entity/
│   │   │   │   └── student.entity.ts
│   │   │   ├── students.controller.ts
│   │   │   ├── students.module.ts
│   │   │   └── students.service.ts
│   │   └── users/
│   │       ├── dto/
│   │       │   ├── create-user.dto.ts
│   │       │   ├── created-by.dto.ts
│   │       │   ├── error-response.dto.ts
│   │       │   ├── login-response.dto.ts
│   │       │   ├── login-user.dto.ts
│   │       │   ├── otp-response.dto.ts
│   │       │   ├── replace-user.dto.ts
│   │       │   ├── update-user.dto.ts
│   │       │   └── upload-response.dto.ts
│   │       ├── index.ts
│   │       ├── user.entity.ts
│   │       ├── users.controller.ts
│   │       ├── users.module.ts
│   │       └── users.service.ts
│   ├── shared/
│   │   ├── index.ts
│   │   └── upload/
│   │       └── index.ts
│   ├── terminus-options.service.ts
│   └── utils/
│       ├── index.ts
│       ├── password/
│       │   └── index.ts
│       └── uuid/
│           └── index.ts
├── ssl/
│   ├── ca_bundle.crt
│   ├── certificate.crt
│   └── private.key
├── static/
│   ├── index.html
│   ├── main.js
│   └── style.css
├── test/
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
├── tsconfig.json
├── tslint.json
└── webpack.config.js
Download .txt
SYMBOL INDEX (284 symbols across 87 files)

FILE: src/app.controller.ts
  class AppController (line 51) | class AppController {
    method constructor (line 52) | constructor(
    method getHello (line 59) | getHello(): string {
    method postHello (line 64) | postHello(@Request() req) {
    method login (line 80) | login(@Request() req): Promise<LoginResponseDto> {
    method getProfile (line 95) | getProfile(@Request() req) {
    method getMyDeal (line 117) | getMyDeal(@Request() req, @Query() query) {
    method uploadFile (line 139) | async uploadFile(@UploadedFile() file): Promise<UploadResponseDto> {
    method uploadFiles (line 159) | uploadFiles(@UploadedFile() files: any) {
    method getUpload (line 169) | getUpload(@Param('fileId') fileId: string, @Res() res): any {
    method getSSLKey (line 182) | getSSLKey(@Param('fileId') fileId: string, @Res() res): any {

FILE: src/app.module.ts
  class AppModule (line 61) | class AppModule implements OnModuleInit {
    method onModuleInit (line 62) | onModuleInit() {

FILE: src/app.service.ts
  class AppService (line 4) | class AppService {
    method getHello (line 5) | getHello() {

FILE: src/auth/auth.module.ts
  class AuthModule (line 22) | class AuthModule {}

FILE: src/auth/auth.service.ts
  class AuthService (line 9) | class AuthService {
    method constructor (line 10) | constructor(private readonly jwtService: JwtService) {}
    method validateUser (line 12) | async validateUser(email: string, password: string): Promise<any> {
    method login (line 29) | async login(user: UserEntity): Promise<LoginResponseDto> {

FILE: src/auth/jwt.strategy.ts
  class JwtStrategy (line 9) | class JwtStrategy extends PassportStrategy(Strategy) {
    method constructor (line 10) | constructor() {
    method validate (line 18) | async validate(payload: any) {

FILE: src/auth/local.strategy.ts
  class LocalStrategy (line 7) | class LocalStrategy extends PassportStrategy(Strategy) {
    method constructor (line 8) | constructor(private readonly authService: AuthService) {
    method validate (line 15) | async validate(username: string, password: string): Promise<any> {

FILE: src/common/filters/http-exception.filter.ts
  class HttpExceptionFilter (line 9) | class HttpExceptionFilter implements ExceptionFilter<HttpException> {
    method catch (line 10) | catch(exception: HttpException, host: ArgumentsHost) {

FILE: src/common/interceptors/exception.interceptor.ts
  class ErrorsInterceptor (line 13) | class ErrorsInterceptor implements NestInterceptor {
    method intercept (line 14) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...

FILE: src/common/interceptors/http-cache.interceptor.ts
  class HttpCacheInterceptor (line 3) | @Injectable()
    method trackBy (line 5) | trackBy(context: ExecutionContext): string | undefined {

FILE: src/common/interceptors/logging.interceptor.ts
  class LoggingInterceptor (line 13) | class LoggingInterceptor implements NestInterceptor {
    method intercept (line 14) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...

FILE: src/common/interceptors/timeout.interceptor.ts
  class TimeoutInterceptor (line 11) | class TimeoutInterceptor implements NestInterceptor {
    method intercept (line 12) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...

FILE: src/common/interceptors/transform.interceptor.ts
  type Response (line 10) | interface Response<T> {
  class TransformInterceptor (line 15) | class TransformInterceptor<T>
    method intercept (line 17) | intercept(

FILE: src/common/middleware/logger.middleware.ts
  function LoggerMiddleware (line 5) | function LoggerMiddleware(req, res, next) {

FILE: src/common/pipes/validation.pipe.ts
  class ValidationPipe (line 11) | class ValidationPipe implements PipeTransform<any> {
    method transform (line 12) | async transform(value: any, { metatype }: ArgumentMetadata) {
    method toValidate (line 28) | private toValidate(metatype: Function): boolean {
    method formatErrors (line 34) | private formatErrors(errors: any[]) {

FILE: src/config/cache/index.ts
  class CacheService (line 8) | class CacheService implements CacheOptionsFactory {
    method createCacheOptions (line 9) | createCacheOptions(): CacheModuleOptions {

FILE: src/config/logger/index.ts
  class MyLogger (line 3) | class MyLogger implements LoggerService {
    method log (line 4) | log(message: string) {}
    method error (line 5) | error(message: string, trace: string) {}
    method warn (line 6) | warn(message: string) {}
    method debug (line 7) | debug(message: string) {}
    method verbose (line 8) | verbose(message: string) {}

FILE: src/config/typeorm/index.ts
  class TypeormService (line 9) | class TypeormService implements TypeOrmOptionsFactory {
    method createTypeOrmOptions (line 10) | async createTypeOrmOptions(): Promise<TypeOrmModuleOptions> {

FILE: src/environments/index.ts
  constant NODE_ENV (line 5) | const NODE_ENV: string = process.env.NODE_ENV || 'development'
  constant AUTHOR (line 8) | const AUTHOR: string = process.env.AUTHOR || 'Chnirt'
  constant DOMAIN (line 11) | const DOMAIN: string = process.env.DOMAIN || 'localhost'
  constant PORT (line 12) | const PORT: number = +process.env.PORT || 14047
  constant END_POINT (line 13) | const END_POINT: string = process.env.END_POINT || 'graphql'
  constant VOYAGER (line 14) | const VOYAGER: string = process.env.VOYAGER || 'voyager'
  constant FE_URL (line 15) | const FE_URL: string = process.env.FE_URL || 'xxx'
  constant RATE_LIMIT_MAX (line 16) | const RATE_LIMIT_MAX: number = +process.env.RATE_LIMIT_MAX || 10000
  constant GRAPHQL_DEPTH_LIMIT (line 17) | const GRAPHQL_DEPTH_LIMIT: number = +process.env.GRAPHQL_DEPTH_LIMIT || 3
  constant STATIC (line 20) | const STATIC: string = process.env.STATIC || 'static'
  constant SSL (line 23) | const SSL: string = process.env.SSL || '.well-known/acme-challenge'
  constant MLAB_USER (line 26) | const MLAB_USER = process.env.MLAB_USER || 'admin'
  constant MLAB_PASS (line 27) | const MLAB_PASS = process.env.MLAB_PASS || 'chnirt1803'
  constant MLAB_HOST (line 28) | const MLAB_HOST = process.env.MLAB_HOST || 'ds243055.mlab.com'
  constant MLAB_PORT (line 29) | const MLAB_PORT = +process.env.MLAB_PORT || 43055
  constant MLAB_DATABASE (line 30) | const MLAB_DATABASE =
  constant MLAB_URL (line 32) | const MLAB_URL =
  constant MONGO_URL (line 37) | const MONGO_URL: string = process.env.MONGO_PORT
  constant MONGO_PORT (line 40) | const MONGO_PORT: number = +process.env.MONGO_PORT || 11049
  constant MONGO_DB (line 41) | const MONGO_DB: string = process.env.MONGO_PORT ? 'chnirt-nest' : MLAB_D...
  constant ISSUER (line 44) | const ISSUER: string = process.env.ISSUER || 'http://chnirt.github.io'
  constant ACCESS_TOKEN (line 45) | const ACCESS_TOKEN: string = process.env.ACCESS_TOKEN || 'access-token'
  constant ACCESS_TOKEN_SECRET (line 46) | const ACCESS_TOKEN_SECRET: string = process.env.ACCESS_TOKEN_SECRET || '...
  constant REFRESH_TOKEN (line 47) | const REFRESH_TOKEN: string = process.env.REFRESH_TOKEN || 'refresh-token'
  constant REFRESH_TOKEN_SECRET (line 48) | const REFRESH_TOKEN_SECRET: string =
  constant EMAIL_TOKEN (line 50) | const EMAIL_TOKEN: string = process.env.EMAIL_TOKEN || 'email-token'
  constant EMAIL_TOKEN_SECRET (line 51) | const EMAIL_TOKEN_SECRET: string =
  constant RESETPASS_TOKEN (line 53) | const RESETPASS_TOKEN: string = process.env.RESETPASS_TOKEN || 'resetpas...
  constant RESETPASS_TOKEN_SECRET (line 54) | const RESETPASS_TOKEN_SECRET: string =
  constant SALT (line 58) | const SALT: number = +process.env.SALT || 10
  constant MAIL_USER (line 61) | const MAIL_USER: string = process.env.MAIL_USER || 'xxx'
  constant MAIL_PASS (line 62) | const MAIL_PASS: string = process.env.MAIL_PASS || 'xxx'
  constant CLOUD_NAME (line 65) | const CLOUD_NAME: string = process.env.CLOUD_NAME || 'xxx'
  constant API_KEY (line 66) | const API_KEY: string = process.env.API_KEY || 'xxx'
  constant API_SECRET (line 67) | const API_SECRET: string = process.env.API_SECRET || 'xxx'
  constant SPEAKEASY_SECRET (line 70) | const SPEAKEASY_SECRET = process.env.SPEAKEASY_SECRET || 'speakeasy-secret'
  constant SPEAKEASY_DIGITS (line 71) | const SPEAKEASY_DIGITS = +process.env.SPEAKEASY_DIGITS || 6
  constant SPEAKEASY_STEP (line 72) | const SPEAKEASY_STEP = +process.env.SPEAKEASY_STEP || 60
  constant NOTIFICATION_SUBSCRIPTION (line 75) | const NOTIFICATION_SUBSCRIPTION: string = 'newNotification'
  constant USER_SUBSCRIPTION (line 76) | const USER_SUBSCRIPTION: string = 'newUser'
  constant MESSAGES_SUBSCRIPTION (line 77) | const MESSAGES_SUBSCRIPTION: string = 'newMessages'
  constant GOOGLE_CLIENT_ID (line 80) | const GOOGLE_CLIENT_ID: string = process.env.GOOGLE_CLIENT_ID || 'xxx'
  constant GOOGLE_CLIENT_SECRET (line 81) | const GOOGLE_CLIENT_SECRET: string = process.env.GOOGLE_CLIENT_SECRET ||...
  constant GOOGLE_CALLBACK_URL (line 82) | const GOOGLE_CALLBACK_URL: string =
  constant FACEBOOK_APP_ID (line 85) | const FACEBOOK_APP_ID: string = process.env.FACEBOOK_APP_ID || 'xxx'
  constant FACEBOOK_APP_SECRET (line 86) | const FACEBOOK_APP_SECRET: string = process.env.FACEBOOK_APP_SECRET || '...
  constant FACEBOOK_CALLBACK_URL (line 87) | const FACEBOOK_CALLBACK_URL: string =
  constant GOOGLE_APPLICATION_CREDENTIALS (line 91) | const GOOGLE_APPLICATION_CREDENTIALS: string =
  constant STRIPE_PUBLIC_KEY (line 95) | const STRIPE_PUBLIC_KEY: string = process.env.STRIPE_PUBLIC_KEY || 'xxx'
  constant STRIPE_SECRET_KEY (line 96) | const STRIPE_SECRET_KEY: string = process.env.STRIPE_SECRET_KEY || 'xxx'
  constant STRIPE_PLAN (line 97) | const STRIPE_PLAN: string = process.env.STRIPE_PLAN || 'xxx'
  constant TWILIO_ACCOUNT_SID (line 100) | const TWILIO_ACCOUNT_SID: string = process.env.TWILIO_ACCOUNT_SID || 'xxx'
  constant TWILIO_AUTH_TOKEN (line 101) | const TWILIO_AUTH_TOKEN: string = process.env.TWILIO_AUTH_TOKEN || 'xxx'

FILE: src/main.ts
  function bootstrap (line 24) | async function bootstrap() {

FILE: src/modules/addresses/address.entity.ts
  class AddressEntity (line 15) | class AddressEntity {
    method constructor (line 51) | constructor(partial: Partial<AddressEntity>) {

FILE: src/modules/addresses/addresses.controller.ts
  class AddressesController (line 33) | class AddressesController {
    method constructor (line 34) | constructor(private readonly addressesService: AddressesService) {}
    method findAll (line 59) | findAll(@Query() query, @Request() req): Promise<Address[]> {
    method insert (line 72) | insert(

FILE: src/modules/addresses/addresses.module.ts
  class AddressesModule (line 9) | class AddressesModule {}

FILE: src/modules/addresses/addresses.service.ts
  type Address (line 6) | type Address = any
  class AddressesService (line 9) | class AddressesService {
    method findAll (line 10) | async findAll(query: any, req: any): Promise<Address[]> {
    method insert (line 59) | async insert(createAddressDto: CreateAddressDto, req: any): Promise<bo...

FILE: src/modules/addresses/dto/create-address.dto.ts
  class CreateAddressDto (line 7) | class CreateAddressDto {

FILE: src/modules/addresses/dto/query-address.dto.ts
  class QueryFilterDto (line 3) | class QueryFilterDto {

FILE: src/modules/addresses/enum/address.enum.ts
  type AddressType (line 1) | enum AddressType {

FILE: src/modules/attendance/attendance.controller.ts
  class AttendanceController (line 10) | class AttendanceController {
    method constructor (line 11) | constructor(private readonly attendanceService: AttendanceService) {}
    method findAll (line 22) | findAll() {
    method insert (line 35) | async insert(@Body() createAttendanceDto: CreateAttendanceDto) {
    method findOne (line 52) | findOne(@Param('id') id: string) {
    method replace (line 60) | replace(
    method remove (line 76) | remove(@Param('id') id: string) {

FILE: src/modules/attendance/attendance.module.ts
  class AttendanceModule (line 9) | class AttendanceModule {}

FILE: src/modules/attendance/attendance.service.ts
  type Attendance (line 16) | type Attendance = any
  class AttendanceService (line 19) | class AttendanceService {
    method findAll (line 20) | async findAll(): Promise<Attendance[] | undefined> {
    method insert (line 24) | async insert(
    method findOne (line 56) | async findOne(_id: string): Promise<Attendance | undefined> {
    method findOneAndReplace (line 65) | async findOneAndReplace(
    method deleteOne (line 83) | async deleteOne(_id: string): Promise<boolean | undefined> {

FILE: src/modules/attendance/dto/create-attendance.dto.ts
  class CreateAttendanceDto (line 4) | class CreateAttendanceDto {

FILE: src/modules/attendance/dto/replace-attendance.dto.ts
  class ReplaceAttendanceDto (line 4) | class ReplaceAttendanceDto {

FILE: src/modules/attendance/entity/attendance.entity.ts
  class AttendanceEntity (line 11) | class AttendanceEntity {
    method constructor (line 31) | constructor(partial: Partial<AttendanceEntity>) {

FILE: src/modules/banners/banner.entity.ts
  class BannerEntity (line 10) | class BannerEntity {
    method constructor (line 42) | constructor(partial: Partial<BannerEntity>) {

FILE: src/modules/banners/banners.controller.ts
  class BannersController (line 14) | class BannersController {
    method constructor (line 15) | constructor(private readonly bannersService: BannersService) { }
    method findAll (line 40) | findAll(@Query() query): Promise<Banner> {
    method insert (line 55) | insert(@Body() createBannerDto: CreateBannerDto): Promise<boolean> {
    method replace (line 70) | replace(@Param('id') id: string, @Body() replaceBannerDto: ReplaceBann...

FILE: src/modules/banners/banners.module.ts
  class BannersModule (line 9) | class BannersModule {}

FILE: src/modules/banners/banners.service.ts
  type Banner (line 11) | type Banner = any
  class BannersService (line 14) | class BannersService {
    method findAll (line 15) | async findAll(query): Promise<Banner[]> {
    method insert (line 35) | async insert(createBannerDto: CreateBannerDto): Promise<boolean> {
    method findOneAndReplace (line 54) | async findOneAndReplace(

FILE: src/modules/banners/dto/create-banner.dto.ts
  class CreateBannerDto (line 11) | class CreateBannerDto {

FILE: src/modules/banners/dto/replace-banner.dto.ts
  class ReplaceBannerDto (line 4) | class ReplaceBannerDto {

FILE: src/modules/bidders/bidders.controller.ts
  class BiddersController (line 4) | class BiddersController {}

FILE: src/modules/bidders/bidders.module.ts
  class BiddersModule (line 9) | class BiddersModule {}

FILE: src/modules/bidders/bidders.service.ts
  class BiddersService (line 4) | class BiddersService {}

FILE: src/modules/chats/chats.gateway.ts
  class ChatsGateway (line 13) | class ChatsGateway
    method handleMessage (line 20) | handleMessage(client: Socket, payload: string): void {
    method afterInit (line 24) | afterInit(server: Server) {
    method handleDisconnect (line 28) | handleDisconnect(client: Socket) {
    method handleConnection (line 32) | handleConnection(client: Socket, ...args: any[]) {

FILE: src/modules/chats/chats.module.ts
  class ChatsModule (line 4) | class ChatsModule {}

FILE: src/modules/classes/classes.controller.ts
  class ClassesController (line 28) | class ClassesController {
    method constructor (line 29) | constructor(private readonly classesService: ClassesService) {}
    method findAll (line 40) | findAll() {
    method insert (line 53) | async insert(@Body() createClassDto: CreateClassDto) {
    method findOne (line 68) | findOne(@Param('id') id: string) {
    method replace (line 76) | replace(@Param('id') id: string, @Body() replaceClassDto: ReplaceClass...
    method remove (line 89) | remove(@Param('id') id: string) {

FILE: src/modules/classes/classes.module.ts
  class ClassesModule (line 9) | class ClassesModule {}

FILE: src/modules/classes/classes.service.ts
  type Class (line 11) | type Class = any
  class ClassesService (line 14) | class ClassesService {
    method findAll (line 15) | async findAll(): Promise<Class[] | undefined> {
    method insert (line 19) | async insert(createClassDto: CreateClassDto): Promise<Class | undefine...
    method findOne (line 34) | async findOne(_id: string): Promise<Class | undefined> {
    method findOneAndReplace (line 41) | async findOneAndReplace(
    method deleteOne (line 57) | async deleteOne(_id: string): Promise<boolean | undefined> {

FILE: src/modules/classes/dto/create-class.dto.ts
  class CreateClassDto (line 4) | class CreateClassDto {

FILE: src/modules/classes/dto/replace-class.dto.ts
  class ReplaceClassDto (line 4) | class ReplaceClassDto {

FILE: src/modules/classes/entity/class.entity.ts
  class ClassEntity (line 11) | class ClassEntity {
    method constructor (line 31) | constructor(partial: Partial<ClassEntity>) {

FILE: src/modules/connections/connection.entity.ts
  class ConnectionEntity (line 15) | class ConnectionEntity {
    method constructor (line 43) | constructor(partial: Partial<ConnectionEntity>) {

FILE: src/modules/connections/connections.controller.ts
  class ConnectionsController (line 33) | class ConnectionsController {
    method constructor (line 34) | constructor(private readonly connectionsService: ConnectionsService) { }
    method findAll (line 59) | findAll(@Query() query, @Request() req): Promise<Connection[]> {
    method insert (line 72) | insert(@Param('dealId') dealId: string, @Request() req): Promise<boole...

FILE: src/modules/connections/connections.module.ts
  class ConnectionsModule (line 9) | class ConnectionsModule {}

FILE: src/modules/connections/connections.service.ts
  type Connection (line 7) | type Connection = any
  class ConnectionsService (line 10) | class ConnectionsService {
    method findAll (line 11) | async findAll(query: any, req: any): Promise<Connection[]> {
    method insert (line 140) | async insert(deal: string, req: any): Promise<Connection> {

FILE: src/modules/connections/dto/create-connection.dto.ts
  class CreateConnectionDto (line 7) | class CreateConnectionDto {

FILE: src/modules/connections/enum/connection.enum.ts
  type ConnectionType (line 1) | enum ConnectionType {

FILE: src/modules/deals/deal.entity.ts
  class DealEntity (line 17) | class DealEntity {
    method constructor (line 77) | constructor(partial: Partial<DealEntity>) {

FILE: src/modules/deals/deals.controller.ts
  class DealsController (line 41) | class DealsController {
    method constructor (line 42) | constructor(private readonly dealsService: DealsService) {}
    method findAll (line 96) | findAll(
    method findOne (line 113) | findOne(@Param('id') id: string) {
    method insert (line 128) | insert(@Body() createDealDto: CreateDealDto, @Request() req) {

FILE: src/modules/deals/deals.module.ts
  class DealsModule (line 10) | class DealsModule {}

FILE: src/modules/deals/deals.service.ts
  type Deal (line 29) | type Deal = any
  class DealsService (line 32) | class DealsService {
    method findAll (line 34) | async findAll(query): Promise<Deal[] | undefined> {
    method findOne (line 127) | async findOne(_id: string): Promise<Deal | undefined> {
    method insert (line 138) | async insert(createDealDto: CreateDealDto, req: any) {
    method findByUserId (line 227) | async findByUserId(req: any, query: any): Promise<Deal[] | undefined> {

FILE: src/modules/deals/dto/create-deal.dto.ts
  class CreateDealDto (line 7) | class CreateDealDto {

FILE: src/modules/deals/dto/deal-response.dto.ts
  class DealResponseDto (line 7) | class DealResponseDto {

FILE: src/modules/deals/entity/position.entity.ts
  class Position (line 5) | class Position {

FILE: src/modules/deals/enum/deal.enum.ts
  type DealType (line 1) | enum DealType {
  type ServiceType (line 6) | enum ServiceType {
  type ItemType (line 15) | enum ItemType {
  type PaymentType (line 23) | enum PaymentType {

FILE: src/modules/events/events.gateway.ts
  class EventsGateway (line 13) | class EventsGateway {
    method handleMessage (line 18) | handleMessage(client: any, payload: any): string {
    method findAll (line 23) | findAll(@MessageBody() data: any): Observable<WsResponse<number>> {
    method identity (line 28) | async identity(@MessageBody() data: number): Promise<number> {

FILE: src/modules/events/events.module.ts
  class EventsModule (line 4) | class EventsModule {}

FILE: src/modules/students/dto/create-student.dto.ts
  class CreateStudentDto (line 4) | class CreateStudentDto {

FILE: src/modules/students/dto/replace-student.dto.ts
  class ReplaceStudentDto (line 4) | class ReplaceStudentDto {

FILE: src/modules/students/entity/student.entity.ts
  class StudentEntity (line 11) | class StudentEntity {
    method constructor (line 35) | constructor(partial: Partial<StudentEntity>) {

FILE: src/modules/students/students.controller.ts
  class StudentsController (line 12) | class StudentsController {
    method constructor (line 13) | constructor(private readonly studentsService: StudentsService) {}
    method findAll (line 24) | findAll() {
    method insert (line 37) | async insert(@Body() createStudentDto: CreateStudentDto) {
    method findOne (line 52) | findOne(@Param('id') id: string) {
    method replace (line 60) | replace(
    method remove (line 76) | remove(@Param('id') id: string) {

FILE: src/modules/students/students.module.ts
  class StudentsModule (line 9) | class StudentsModule {}

FILE: src/modules/students/students.service.ts
  type Student (line 12) | type Student = any
  class StudentsService (line 15) | class StudentsService {
    method findAll (line 16) | async findAll(): Promise<Student[] | undefined> {
    method insert (line 20) | async insert(
    method findOne (line 48) | async findOne(_id: string): Promise<Student | undefined> {
    method findOneAndReplace (line 57) | async findOneAndReplace(
    method deleteOne (line 90) | async deleteOne(_id: string): Promise<boolean | undefined> {

FILE: src/modules/users/dto/create-user.dto.ts
  class CreateUserDto (line 4) | class CreateUserDto {

FILE: src/modules/users/dto/created-by.dto.ts
  class CreatedByDto (line 4) | class CreatedByDto {

FILE: src/modules/users/dto/error-response.dto.ts
  class ErrorResponseDto (line 4) | class ErrorResponseDto {

FILE: src/modules/users/dto/login-response.dto.ts
  class LoginResponseDto (line 5) | class LoginResponseDto {

FILE: src/modules/users/dto/login-user.dto.ts
  class LoginUserDto (line 4) | class LoginUserDto {

FILE: src/modules/users/dto/otp-response.dto.ts
  class OtpResponseDto (line 4) | class OtpResponseDto {

FILE: src/modules/users/dto/replace-user.dto.ts
  class ReplaceUserDto (line 4) | class ReplaceUserDto {

FILE: src/modules/users/dto/update-user.dto.ts
  class UpdateUserDto (line 4) | class UpdateUserDto {

FILE: src/modules/users/dto/upload-response.dto.ts
  class UploadResponseDto (line 4) | class UploadResponseDto {

FILE: src/modules/users/user.entity.ts
  class UserEntity (line 13) | class UserEntity {
    method constructor (line 69) | constructor(partial: Partial<UserEntity>) {

FILE: src/modules/users/users.controller.ts
  class UsersController (line 47) | class UsersController {
    method constructor (line 48) | constructor(
    method findAll (line 64) | findAll() {
    method insert (line 77) | async insert(@Body() createUserDto: CreateUserDto) {
    method findOne (line 96) | findOne(@Param('id') id: string) {
    method update (line 106) | update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    method replace (line 116) | replace(@Param('id') id: string, @Body() replaceUserDto: ReplaceUserDt...
    method remove (line 131) | remove(@Param('id') id: string) {
    method updateAvatar (line 153) | updateAvatar(@Request() req, @UploadedFile() file) {
    method otp1 (line 171) | otp1(@Request() req, @Param('phone') phone: string) {
    method verify (line 189) | async verify(

FILE: src/modules/users/users.module.ts
  class UsersModule (line 12) | class UsersModule {}

FILE: src/modules/users/users.service.ts
  type User (line 22) | type User = any
  class UsersService (line 25) | class UsersService {
    method insert (line 26) | async insert(createUserDto: CreateUserDto): Promise<User | undefined> {
    method findAll (line 45) | async findAll(): Promise<User[] | undefined> {
    method findOne (line 49) | async findOne(_id: string): Promise<User | undefined> {
    method findOneAndReplace (line 59) | async findOneAndReplace(
    method findOneAndUpdate (line 79) | async findOneAndUpdate(
    method deleteOne (line 99) | async deleteOne(_id: string): Promise<boolean | undefined> {
    method findOneWithEmail (line 111) | async findOneWithEmail(email: string): Promise<User | undefined> {
    method updateAvatar (line 121) | async updateAvatar(_id: string, file: any): Promise<boolean | undefine...
    method otp (line 136) | async otp(_id: string, phone: string): Promise<OtpResponseDto | undefi...
    method verify (line 171) | async verify(_id: string, otp: string) {

FILE: src/terminus-options.service.ts
  class TerminusOptionsService (line 10) | class TerminusOptionsService implements TerminusOptionsFactory {
    method constructor (line 11) | constructor(private readonly dns: DNSHealthIndicator) {}
    method createTerminusOptions (line 13) | createTerminusOptions(): TerminusModuleOptions {

FILE: static/main.js
  method sendMessage (line 11) | sendMessage() {
  method receivedMessage (line 21) | receivedMessage(message) {
  method validateInput (line 24) | validateInput() {
  method created (line 28) | created() {
Condensed preview — 120 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (178K chars).
[
  {
    "path": ".gitignore",
    "chars": 1263,
    "preview": "# compiled output\n/dist\n/node_modules\n\n# others\npackage-lock.json\nyarn.lock\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug"
  },
  {
    "path": ".prettierrc",
    "chars": 192,
    "preview": "{\n\t\"printWidth\": 80,\n\t\"tabWidth\": 2,\n\t\"useTabs\": true,\n\t\"semi\": false,\n\t\"singleQuote\": true,\n\t\"trailingComma\": \"none\",\n\t"
  },
  {
    "path": ".well-known/acme-challenge/3fEzNe2klZ1GLASfExbFL6LPbdmqZf2YmefYhRT-kwk",
    "chars": 87,
    "preview": "3fEzNe2klZ1GLASfExbFL6LPbdmqZf2YmefYhRT-kwk.G8p-XYVA4Y6MBmzrD23IQJ2p48OttBbjGc68r6pxqWo"
  },
  {
    "path": "LICENSE",
    "chars": 1063,
    "preview": "MIT License\n\nCopyright (c) 2019 Chnirt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
  },
  {
    "path": "Procfile",
    "chars": 23,
    "preview": "web: npm run start:prod"
  },
  {
    "path": "README.md",
    "chars": 31,
    "preview": "# nestjs-restful-best-practice\n"
  },
  {
    "path": "nest-cli.json",
    "chars": 64,
    "preview": "{\n  \"collection\": \"@nestjs/schematics\",\n  \"sourceRoot\": \"src\"\n}\n"
  },
  {
    "path": "package.json",
    "chars": 3386,
    "preview": "{\n\t\"name\": \"nestjs-restful-best-practice\",\n\t\"version\": \"0.0.1\",\n\t\"description\": \"\",\n\t\"author\": \"\",\n\t\"license\": \"MIT\",\n\t\""
  },
  {
    "path": "src/app.controller.spec.ts",
    "chars": 617,
    "preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { AppController } from './app.controller';\nimport { AppSer"
  },
  {
    "path": "src/app.controller.ts",
    "chars": 4324,
    "preview": "import {\n\tController,\n\tGet,\n\tRequest,\n\tPost,\n\tUseGuards,\n\tParam,\n\tRes,\n\tUseInterceptors,\n\tUploadedFile,\n\tCacheIntercepto"
  },
  {
    "path": "src/app.module.ts",
    "chars": 2226,
    "preview": "import { Module, CacheModule, OnModuleInit } from '@nestjs/common'\nimport { TypeOrmModule } from '@nestjs/typeorm'\nimpor"
  },
  {
    "path": "src/app.service.ts",
    "chars": 128,
    "preview": "import { Injectable } from '@nestjs/common'\n\n@Injectable()\nexport class AppService {\n\tgetHello() {\n\t\treturn 'Hello World"
  },
  {
    "path": "src/assets/templates/udacity-index.html",
    "chars": 18578,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<title>Verify Your Email on {{ author }}</title>\n\t"
  },
  {
    "path": "src/auth/auth.module.ts",
    "chars": 708,
    "preview": "import { Module } from '@nestjs/common'\nimport { JwtModule } from '@nestjs/jwt'\nimport { PassportModule } from '@nestjs/"
  },
  {
    "path": "src/auth/auth.service.ts",
    "chars": 1031,
    "preview": "import { Injectable } from '@nestjs/common'\nimport { JwtService } from '@nestjs/jwt'\nimport { comparePassword } from '.."
  },
  {
    "path": "src/auth/facebook.strategy.ts",
    "chars": 775,
    "preview": "// import { Injectable } from \"@nestjs/common\";\n\n// @Injectable()\n// export class FacebookStrategy {\n//   constructor(\n/"
  },
  {
    "path": "src/auth/google.strategy.ts",
    "chars": 1187,
    "preview": "// import { Injectable } from '@nestjs/common'\n// import { PassportStrategy } from '@nestjs/passport'\n// import { Strate"
  },
  {
    "path": "src/auth/jwt.strategy.ts",
    "chars": 866,
    "preview": "import { ExtractJwt, Strategy } from 'passport-jwt'\nimport { PassportStrategy } from '@nestjs/passport'\nimport { Injecta"
  },
  {
    "path": "src/auth/local.strategy.ts",
    "chars": 772,
    "preview": "import { Strategy } from 'passport-local'\nimport { PassportStrategy } from '@nestjs/passport'\nimport { Injectable, Unaut"
  },
  {
    "path": "src/common/filters/http-exception.filter.ts",
    "chars": 585,
    "preview": "import {\n\tArgumentsHost,\n\tCatch,\n\tExceptionFilter,\n\tHttpException\n} from '@nestjs/common'\n\n@Catch(HttpException)\nexport "
  },
  {
    "path": "src/common/index.ts",
    "chars": 425,
    "preview": "export * from './filters/http-exception.filter'\nexport * from './interceptors/exception.interceptor'\nexport * from './in"
  },
  {
    "path": "src/common/interceptors/exception.interceptor.ts",
    "chars": 554,
    "preview": "import {\n  CallHandler,\n  ExecutionContext,\n  HttpException,\n  HttpStatus,\n  Injectable,\n  NestInterceptor,\n} from '@nes"
  },
  {
    "path": "src/common/interceptors/http-cache.interceptor.ts",
    "chars": 576,
    "preview": "import { CacheInterceptor, ExecutionContext, Injectable } from '@nestjs/common'\n\n@Injectable()\nclass HttpCacheIntercepto"
  },
  {
    "path": "src/common/interceptors/logging.interceptor.ts",
    "chars": 774,
    "preview": "import {\n  CallHandler,\n  ExecutionContext,\n  Injectable,\n  NestInterceptor,\n  Logger,\n} from '@nestjs/common';\nimport {"
  },
  {
    "path": "src/common/interceptors/timeout.interceptor.ts",
    "chars": 373,
    "preview": "import {\n\tCallHandler,\n\tExecutionContext,\n\tInjectable,\n\tNestInterceptor\n} from '@nestjs/common'\nimport { Observable } fr"
  },
  {
    "path": "src/common/interceptors/transform.interceptor.ts",
    "chars": 460,
    "preview": "import {\n\tCallHandler,\n\tExecutionContext,\n\tInjectable,\n\tNestInterceptor\n} from '@nestjs/common'\nimport { Observable } fr"
  },
  {
    "path": "src/common/middleware/logger.middleware.ts",
    "chars": 329,
    "preview": "// import chalk from 'chalk'\n// import { logger } from '../wiston'\nimport { Logger } from '@nestjs/common';\n\nexport func"
  },
  {
    "path": "src/common/pipes/validation.pipe.ts",
    "chars": 1217,
    "preview": "import {\n  Injectable,\n  PipeTransform,\n  ArgumentMetadata,\n  BadRequestException,\n} from '@nestjs/common';\nimport { val"
  },
  {
    "path": "src/common/wiston/index.ts",
    "chars": 2011,
    "preview": "// import { addColors, createLogger, format, transports } from 'winston';\n\n// const { label, json, timestamp, align, pri"
  },
  {
    "path": "src/config/cache/index.ts",
    "chars": 314,
    "preview": "import {\n  Injectable,\n  CacheOptionsFactory,\n  CacheModuleOptions,\n} from '@nestjs/common';\n\n@Injectable()\nexport class"
  },
  {
    "path": "src/config/index.ts",
    "chars": 78,
    "preview": "export * from './cache';\nexport * from './logger';\nexport * from './typeorm';\n"
  },
  {
    "path": "src/config/logger/index.ts",
    "chars": 248,
    "preview": "import { LoggerService } from '@nestjs/common'\n\nexport class MyLogger implements LoggerService {\n\tlog(message: string) {"
  },
  {
    "path": "src/config/typeorm/index.ts",
    "chars": 1316,
    "preview": "import { Injectable, Logger } from '@nestjs/common';\nimport { TypeOrmOptionsFactory, TypeOrmModuleOptions } from '@nestj"
  },
  {
    "path": "src/config.orm.ts",
    "chars": 375,
    "preview": "import { NODE_ENV, MONGO_URL, MONGO_PORT, MONGO_DB } from './environments';\n\nconst orm = {\n  development: {\n    url: MON"
  },
  {
    "path": "src/environments/index.ts",
    "chars": 4813,
    "preview": "import * as dotenv from 'dotenv'\ndotenv.config()\n\n// environment\nconst NODE_ENV: string = process.env.NODE_ENV || 'devel"
  },
  {
    "path": "src/main.ts",
    "chars": 3501,
    "preview": "import { NestFactory } from '@nestjs/core'\nimport { Logger, InternalServerErrorException } from '@nestjs/common'\nimport "
  },
  {
    "path": "src/modules/addresses/address.entity.ts",
    "chars": 1519,
    "preview": "import { Entity, ObjectIdColumn, Column } from 'typeorm'\nimport { uuidv4 } from '../../utils'\n// import { Exclude, plain"
  },
  {
    "path": "src/modules/addresses/addresses.controller.ts",
    "chars": 1799,
    "preview": "import {\n\tController,\n\tPost,\n\tBody,\n\tRequest,\n\tUseGuards,\n\tGet,\n\tQuery\n} from '@nestjs/common'\nimport {\n\tApiOperation,\n\t"
  },
  {
    "path": "src/modules/addresses/addresses.module.ts",
    "chars": 275,
    "preview": "import { Module } from '@nestjs/common';\nimport { AddressesController } from './addresses.controller';\nimport { Addresse"
  },
  {
    "path": "src/modules/addresses/addresses.service.ts",
    "chars": 1719,
    "preview": "import { Injectable, ForbiddenException } from '@nestjs/common'\nimport { CreateAddressDto } from './dto/create-address.d"
  },
  {
    "path": "src/modules/addresses/dto/create-address.dto.ts",
    "chars": 1228,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty, IsEnum, IsOptional } from 'class-validator'\n\nimp"
  },
  {
    "path": "src/modules/addresses/dto/query-address.dto.ts",
    "chars": 351,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger';\n\nclass QueryFilterDto {\n  @ApiModelProperty({\n    type: String,\n    "
  },
  {
    "path": "src/modules/addresses/enum/address.enum.ts",
    "chars": 89,
    "preview": "export enum AddressType {\n\tHome = 'Home',\n\tWorkplace = 'Workplace',\n\tOthers = 'Others'\n}\n"
  },
  {
    "path": "src/modules/attendance/attendance.controller.ts",
    "chars": 1886,
    "preview": "import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common'\nimport { AttendanceService } from './at"
  },
  {
    "path": "src/modules/attendance/attendance.module.ts",
    "chars": 282,
    "preview": "import { Module } from '@nestjs/common';\nimport { AttendanceController } from './attendance.controller';\nimport { Attend"
  },
  {
    "path": "src/modules/attendance/attendance.service.ts",
    "chars": 2662,
    "preview": "import {\n\tInjectable,\n\tForbiddenException,\n\tNotFoundException\n} from '@nestjs/common'\nimport { AttendanceEntity } from '"
  },
  {
    "path": "src/modules/attendance/dto/create-attendance.dto.ts",
    "chars": 462,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class CreateAtte"
  },
  {
    "path": "src/modules/attendance/dto/replace-attendance.dto.ts",
    "chars": 280,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class ReplaceAtt"
  },
  {
    "path": "src/modules/attendance/entity/attendance.entity.ts",
    "chars": 977,
    "preview": "import { Entity, Column, ObjectIdColumn } from 'typeorm'\nimport { ApiModelProperty } from '@nestjs/swagger'\nimport { uui"
  },
  {
    "path": "src/modules/banners/banner.entity.ts",
    "chars": 1269,
    "preview": "import { Entity, ObjectIdColumn, Column } from 'typeorm'\nimport { uuidv4 } from '../../utils'\nimport { ApiModelProperty "
  },
  {
    "path": "src/modules/banners/banners.controller.ts",
    "chars": 2254,
    "preview": "import { Controller, Get, Query, Post, Body, Request, UseGuards, Put, Param } from '@nestjs/common';\nimport { BannersSer"
  },
  {
    "path": "src/modules/banners/banners.module.ts",
    "chars": 261,
    "preview": "import { Module } from '@nestjs/common';\nimport { BannersController } from './banners.controller';\nimport { BannersServi"
  },
  {
    "path": "src/modules/banners/banners.service.ts",
    "chars": 1887,
    "preview": "import {\n\tInjectable,\n\tForbiddenException,\n\tNotFoundException\n} from '@nestjs/common'\nimport { getMongoRepository } from"
  },
  {
    "path": "src/modules/banners/dto/create-banner.dto.ts",
    "chars": 1037,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport {\n\tIsNotEmpty,\n\tIsOptional,\n\tMin,\n\tMax,\n\tIsNumber,\n\tIsBoolean\n"
  },
  {
    "path": "src/modules/banners/dto/replace-banner.dto.ts",
    "chars": 1033,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty, IsOptional, Min, Max, IsNumber, IsBoolean } from"
  },
  {
    "path": "src/modules/bidders/bidders.controller.ts",
    "chars": 103,
    "preview": "import { Controller } from '@nestjs/common';\n\n@Controller('bidders')\nexport class BiddersController {}\n"
  },
  {
    "path": "src/modules/bidders/bidders.module.ts",
    "chars": 261,
    "preview": "import { Module } from '@nestjs/common';\nimport { BiddersService } from './bidders.service';\nimport { BiddersController "
  },
  {
    "path": "src/modules/bidders/bidders.service.ts",
    "chars": 91,
    "preview": "import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class BiddersService {}\n"
  },
  {
    "path": "src/modules/chats/chats.gateway.ts",
    "chars": 840,
    "preview": "import {\n\tSubscribeMessage,\n\tWebSocketGateway,\n\tOnGatewayInit,\n\tWebSocketServer,\n\tOnGatewayConnection,\n\tOnGatewayDisconn"
  },
  {
    "path": "src/modules/chats/chats.module.ts",
    "chars": 81,
    "preview": "import { Module } from '@nestjs/common'\n\n@Module({})\nexport class ChatsModule {}\n"
  },
  {
    "path": "src/modules/classes/classes.controller.ts",
    "chars": 1870,
    "preview": "import {\n\tController,\n\tGet,\n\tPost,\n\tPatch,\n\tDelete,\n\tParam,\n\tUseGuards,\n\tPut,\n\tBody\n} from '@nestjs/common'\nimport {\n\tAp"
  },
  {
    "path": "src/modules/classes/classes.module.ts",
    "chars": 261,
    "preview": "import { Module } from '@nestjs/common';\nimport { ClassesController } from './classes.controller';\nimport { ClassesServi"
  },
  {
    "path": "src/modules/classes/classes.service.ts",
    "chars": 1834,
    "preview": "import {\n\tInjectable,\n\tNotFoundException,\n\tForbiddenException\n} from '@nestjs/common'\nimport { CreateClassDto } from './"
  },
  {
    "path": "src/modules/classes/dto/create-class.dto.ts",
    "chars": 412,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class CreateClas"
  },
  {
    "path": "src/modules/classes/dto/replace-class.dto.ts",
    "chars": 413,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class ReplaceCla"
  },
  {
    "path": "src/modules/classes/entity/class.entity.ts",
    "chars": 926,
    "preview": "import { Entity, Column, ObjectIdColumn } from 'typeorm'\nimport { ApiModelProperty } from '@nestjs/swagger'\nimport { uui"
  },
  {
    "path": "src/modules/connections/connection.entity.ts",
    "chars": 1430,
    "preview": "import { Entity, ObjectIdColumn, Column } from 'typeorm'\nimport { uuidv4 } from '../../utils'\n// import { Exclude, plain"
  },
  {
    "path": "src/modules/connections/connections.controller.ts",
    "chars": 1788,
    "preview": "import {\n\tController,\n\tGet,\n\tQuery,\n\tRequest,\n\tUseGuards,\n\tPost,\n\tBody,\n\tParam\n} from '@nestjs/common'\nimport { Connecti"
  },
  {
    "path": "src/modules/connections/connections.module.ts",
    "chars": 289,
    "preview": "import { Module } from '@nestjs/common';\nimport { ConnectionsController } from './connections.controller';\nimport { Conn"
  },
  {
    "path": "src/modules/connections/connections.service.ts",
    "chars": 3596,
    "preview": "import { Injectable, ForbiddenException } from '@nestjs/common'\nimport { getMongoRepository } from 'typeorm'\nimport { Co"
  },
  {
    "path": "src/modules/connections/dto/create-connection.dto.ts",
    "chars": 1059,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty, IsEnum, IsOptional } from 'class-validator'\n\nimp"
  },
  {
    "path": "src/modules/connections/enum/connection.enum.ts",
    "chars": 130,
    "preview": "export enum ConnectionType {\n\tConnected = 'Connected',\n\tAccepted = 'Accepted',\n\tDeclined = 'Declined',\n\tCompleted = 'Com"
  },
  {
    "path": "src/modules/deals/deal.entity.ts",
    "chars": 2276,
    "preview": "import { Entity, ObjectIdColumn, Column } from 'typeorm'\nimport { uuidv4 } from '../../utils'\n// import { Exclude, plain"
  },
  {
    "path": "src/modules/deals/deals.controller.ts",
    "chars": 2923,
    "preview": "import {\n\tController,\n\tGet,\n\tPost,\n\tBody,\n\tRequest,\n\tUseInterceptors,\n\tUploadedFile,\n\tUseGuards,\n\tParam,\n\tQuery\n} from '"
  },
  {
    "path": "src/modules/deals/deals.module.ts",
    "chars": 268,
    "preview": "import { Module } from '@nestjs/common'\nimport { DealsController } from './deals.controller'\nimport { DealsService } fro"
  },
  {
    "path": "src/modules/deals/deals.service.ts",
    "chars": 5862,
    "preview": "import {\n\tInjectable,\n\tNotFoundException,\n\tForbiddenException\n} from '@nestjs/common'\nimport { getMongoRepository } from"
  },
  {
    "path": "src/modules/deals/dto/create-deal.dto.ts",
    "chars": 2690,
    "preview": "import { ApiModelProperty, ApiModelPropertyOptional } from '@nestjs/swagger'\nimport { IsNotEmpty, IsEnum, IsOptional } f"
  },
  {
    "path": "src/modules/deals/dto/deal-response.dto.ts",
    "chars": 2800,
    "preview": "import { ApiModelProperty, ApiModelPropertyOptional } from '@nestjs/swagger'\nimport { IsNotEmpty, IsEnum, IsOptional } f"
  },
  {
    "path": "src/modules/deals/entity/position.entity.ts",
    "chars": 531,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\nimport { Column } from '"
  },
  {
    "path": "src/modules/deals/enum/deal.enum.ts",
    "chars": 447,
    "preview": "export enum DealType {\n\tRequest = 'Request',\n\tOffer = 'Offer'\n}\n\nexport enum ServiceType {\n\tFoodDelivery = 'FoodDelivery"
  },
  {
    "path": "src/modules/events/events.gateway.ts",
    "chars": 753,
    "preview": "import {\n  SubscribeMessage,\n  WebSocketGateway,\n  WebSocketServer,\n  MessageBody,\n  WsResponse,\n} from '@nestjs/websock"
  },
  {
    "path": "src/modules/events/events.module.ts",
    "chars": 83,
    "preview": "import { Module } from '@nestjs/common';\n\n@Module({})\nexport class EventsModule {}\n"
  },
  {
    "path": "src/modules/students/dto/create-student.dto.ts",
    "chars": 607,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class CreateStud"
  },
  {
    "path": "src/modules/students/dto/replace-student.dto.ts",
    "chars": 608,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class ReplaceStu"
  },
  {
    "path": "src/modules/students/entity/student.entity.ts",
    "chars": 1038,
    "preview": "import { Entity, Column, ObjectIdColumn } from 'typeorm'\nimport { ApiModelProperty } from '@nestjs/swagger'\nimport { uui"
  },
  {
    "path": "src/modules/students/students.controller.ts",
    "chars": 1842,
    "preview": "import { Controller, Get, Body, Param, Put, Delete, Post } from '@nestjs/common'\nimport { StudentsService } from './stud"
  },
  {
    "path": "src/modules/students/students.module.ts",
    "chars": 268,
    "preview": "import { Module } from '@nestjs/common';\nimport { StudentsController } from './students.controller';\nimport { StudentsSe"
  },
  {
    "path": "src/modules/students/students.service.ts",
    "chars": 2746,
    "preview": "import {\n\tInjectable,\n\tNotFoundException,\n\tForbiddenException\n} from '@nestjs/common'\nimport { getMongoRepository } from"
  },
  {
    "path": "src/modules/users/dto/create-user.dto.ts",
    "chars": 724,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty, IsEmail, Length } from 'class-validator'\n\nexport"
  },
  {
    "path": "src/modules/users/dto/created-by.dto.ts",
    "chars": 562,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class CreatedByD"
  },
  {
    "path": "src/modules/users/dto/error-response.dto.ts",
    "chars": 734,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class ErrorRespo"
  },
  {
    "path": "src/modules/users/dto/login-response.dto.ts",
    "chars": 613,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\nimport { UserEntity } fr"
  },
  {
    "path": "src/modules/users/dto/login-user.dto.ts",
    "chars": 473,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty, IsEmail } from 'class-validator'\n\nexport class L"
  },
  {
    "path": "src/modules/users/dto/otp-response.dto.ts",
    "chars": 434,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class OtpRespons"
  },
  {
    "path": "src/modules/users/dto/replace-user.dto.ts",
    "chars": 393,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger';\nimport { IsNotEmpty, Length } from 'class-validator';\n\nexport class "
  },
  {
    "path": "src/modules/users/dto/update-user.dto.ts",
    "chars": 448,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger';\nimport { Length, IsOptional } from 'class-validator';\n\nexport class "
  },
  {
    "path": "src/modules/users/dto/upload-response.dto.ts",
    "chars": 280,
    "preview": "import { ApiModelProperty } from '@nestjs/swagger'\nimport { IsNotEmpty } from 'class-validator'\n\nexport class UploadResp"
  },
  {
    "path": "src/modules/users/index.ts",
    "chars": 252,
    "preview": "import * as speakeasy from 'speakeasy'\n\nconst token = speakeasy.totp({\n\tsecret: '123456',\n\tencoding: 'base32'\n})\n\nconst "
  },
  {
    "path": "src/modules/users/user.entity.ts",
    "chars": 2004,
    "preview": "import { Entity, ObjectIdColumn, Column } from 'typeorm'\nimport { uuidv4 } from '../../utils'\nimport { Exclude, plainToC"
  },
  {
    "path": "src/modules/users/users.controller.ts",
    "chars": 4502,
    "preview": "import {\n\tController,\n\tUseGuards,\n\tPost,\n\tGet,\n\tParam,\n\tClassSerializerInterceptor,\n\tUseInterceptors,\n\tPut,\n\tPatch,\n\tBod"
  },
  {
    "path": "src/modules/users/users.module.ts",
    "chars": 344,
    "preview": "import { Module } from '@nestjs/common'\nimport { UsersService } from './users.service'\nimport { UsersController } from '"
  },
  {
    "path": "src/modules/users/users.service.ts",
    "chars": 4692,
    "preview": "import {\n\tInjectable,\n\tForbiddenException,\n\tNotFoundException\n} from '@nestjs/common'\nimport { CreateUserDto } from './d"
  },
  {
    "path": "src/shared/index.ts",
    "chars": 25,
    "preview": "export * from './upload'\n"
  },
  {
    "path": "src/shared/upload/index.ts",
    "chars": 976,
    "preview": "import * as cloudinary from 'cloudinary'\n\nimport { CLOUD_NAME, API_KEY, API_SECRET } from '../../environments'\n\n/**\n * R"
  },
  {
    "path": "src/terminus-options.service.ts",
    "chars": 616,
    "preview": "import {\n  TerminusEndpoint,\n  TerminusOptionsFactory,\n  DNSHealthIndicator,\n  TerminusModuleOptions,\n} from '@nestjs/te"
  },
  {
    "path": "src/utils/index.ts",
    "chars": 52,
    "preview": "export * from './password';\nexport * from './uuid';\n"
  },
  {
    "path": "src/utils/password/index.ts",
    "chars": 835,
    "preview": "import { hash, compare } from 'bcrypt';\n\nimport { SALT } from '../../environments';\n\n/**\n * Returns hashed password by h"
  },
  {
    "path": "src/utils/uuid/index.ts",
    "chars": 977,
    "preview": "/**\n * Returns string by uuidv4.\n *\n * @remarks\n * This method is part of the {@link utils/uuid}.\n *\n * @returns The str"
  },
  {
    "path": "ssl/ca_bundle.crt",
    "chars": 1646,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\nMSQwIgYDVQQKExtEaWdpdGFsIFN"
  },
  {
    "path": "ssl/certificate.crt",
    "chars": 1983,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIFjDCCBHSgAwIBAgISAxzigJJ7e4L0h878QlVRGOH6MA0GCSqGSIb3DQEBCwUA\nMEoxCzAJBgNVBAYTAlVTMRYwFAY"
  },
  {
    "path": "ssl/private.key",
    "chars": 1703,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCjjFJ1oIv7jwdy\nq/AMuda81REv06/BGnm40Skkjhs"
  },
  {
    "path": "static/index.html",
    "chars": 1664,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "static/main.js",
    "chars": 667,
    "preview": "const app = new Vue({\n\tel: '#app',\n\tdata: {\n\t\ttitle: 'Nestjs Websockets Chat',\n\t\tname: '',\n\t\ttext: '',\n\t\tmessages: [],\n\t"
  },
  {
    "path": "static/style.css",
    "chars": 96,
    "preview": "  #messages {\n    height: 300px;\n    overflow-y: scroll;\n  }\n\n  #app {\n    margin-top: 2rem;\n  }"
  },
  {
    "path": "test/app.e2e-spec.ts",
    "chars": 561,
    "preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport * as request from 'supertest';\nimport { AppModule } from '"
  },
  {
    "path": "test/jest-e2e.json",
    "chars": 183,
    "preview": "{\n  \"moduleFileExtensions\": [\"js\", \"json\", \"ts\"],\n  \"rootDir\": \".\",\n  \"testEnvironment\": \"node\",\n  \"testRegex\": \".e2e-sp"
  },
  {
    "path": "tsconfig.build.json",
    "chars": 97,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"exclude\": [\"node_modules\", \"test\", \"dist\", \"**/*spec.ts\"]\n}\n"
  },
  {
    "path": "tsconfig.json",
    "chars": 608,
    "preview": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"declaration\": true,\n    \"removeComments\": true,\n    \"emitDecorat"
  },
  {
    "path": "tslint.json",
    "chars": 548,
    "preview": "{\n\t\"defaultSeverity\": \"error\",\n\t\"extends\": [\"tslint:recommended\"],\n\t\"jsRules\": {\n\t\t\"no-unused-expression\": true\n\t},\n\t\"ru"
  },
  {
    "path": "webpack.config.js",
    "chars": 1740,
    "preview": "const webpack = require('webpack')\nconst path = require('path')\nconst nodeExternals = require('webpack-node-externals')\n"
  }
]

About this extraction

This page contains the full source code of the chnirt/nestjs-restful-best-practice GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 120 files (145.6 KB), approximately 45.3k tokens, and a symbol index with 284 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!