├── .editorconfig ├── .env ├── .gitignore ├── .prettierrc ├── README.md ├── TODO.md ├── nest-cli.json ├── package.json ├── src ├── app.controller.spec.ts ├── app.controller.ts ├── app.module.ts ├── app.service.ts ├── common │ └── README.md ├── main.ts ├── middleware │ └── README.md ├── modules │ ├── auth │ │ ├── auth.controller.ts │ │ ├── auth.module.ts │ │ ├── auth.service.ts │ │ ├── index.ts │ │ ├── jwt-guard.ts │ │ ├── jwt.strategy.ts │ │ ├── login.payload.ts │ │ └── register.payload.ts │ ├── config │ │ ├── config.module.ts │ │ ├── config.service.ts │ │ └── index.ts │ ├── main │ │ ├── app.controller.spec.ts │ │ ├── app.controller.ts │ │ ├── app.module.ts │ │ └── app.service.ts │ ├── message │ │ ├── message.entity.ts │ │ ├── messages.controller.ts │ │ ├── messages.module.ts │ │ └── messages.service.ts │ ├── stark │ │ ├── index.ts │ │ ├── stark.controller.ts │ │ ├── stark.module.ts │ │ └── stark.service.ts │ └── user │ │ ├── index.ts │ │ ├── password.transformer.ts │ │ ├── user.entity.ts │ │ ├── user.module.ts │ │ └── user.service.ts └── swagger │ ├── constants.ts │ └── index.ts ├── test ├── app.e2e-spec.ts └── jest-e2e.json ├── tsconfig.build.json ├── tsconfig.json ├── tslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # APP 2 | APP_ENV=dev 3 | APP_URL=http://localhost:5000 4 | 5 | # JWT AUTH 6 | JWT_SECRET_KEY=uAsBw6WxqD 7 | JWT_EXPIRATION_TIME=3600 8 | 9 | # DATABASE 10 | DB_TYPE=mysql 11 | DB_USERNAME=root 12 | DB_PASSWORD= 13 | DB_HOST=127.0.0.1 14 | DB_PORT=3306 15 | DB_DATABASE=stark 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### nestjs 脚手架 2 | 3 | #### 实现 4 | * [x] jwt 权限 5 | * [x] 用户注册,登录 6 | * [x] mysql 7 | * [x] 环境配置 8 | * [ ] rbac 角色权限管理 9 | * [ ] docker 10 | 11 | #### mysql 12 | mac 13 | 安装:brew install mysql@5.7 14 | 15 | 16 | #### 使用方式 17 | 18 | ``` 19 | yarn 20 | 21 | npm run start:dev 22 | ``` 23 | 24 | #### 访问 25 | http://localhost:3008 26 | 27 | 28 | #### api 29 | 30 | ##### 注册 31 | > POST: http://localhost:3008/api/auth/register 32 | 33 | 参数 34 | ``` 35 | { 36 | "email": "wsd312@163.com", 37 | "name": "stark", 38 | "password": "wsd123" 39 | } 40 | ``` 41 | 42 | ##### 登录 43 | > POST: http://localhost:3008/api/auth/login 44 | 45 | 参数 46 | ``` 47 | { 48 | "email": "wsd312@163.com", 49 | "password": "wsd123" 50 | } 51 | ``` 52 | 返回 53 | ``` 54 | { 55 | "expiresIn": "3600", 56 | "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywiaWF0IjoxNTY2NTQ0MTI4LCJleHAiOjE1NjY1NDc3Mjh9.WULZw705ocEB2PYicyF4U_SJ4tqD61rd57d8sjUM6eI", 57 | "user": { 58 | "id": 3, 59 | "firstName": "stark", 60 | "lastName": "wang", 61 | "email": "wsd312@163.com", 62 | "password": "aed2521bfd53b1d64ece64d82e83630ef2ecd235df42636f6cdb6083867f95e5" 63 | } 64 | } 65 | ``` 66 | 67 | 68 | ##### 获取登录的用户信息 69 | > url: http://localhost:3008/api/auth/me 70 | 71 | 72 | >参数 73 | > hader: 74 | > Authorization : Bearer accessToken 75 | 76 | ``` 77 | {"id":1,"name":"stark","email":"wsd312@163.com","level":10} 78 | ``` 79 | 80 | 81 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | * [x] jwt 权限 2 | * [x] mysql 3 | * [x] 环境配置 4 | * [ ] rbac 角色权限管理 5 | * [ ] docker 6 | -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "ts", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestjs-kit", 3 | "version": "0.0.1", 4 | "description": "nestjs kit", 5 | "author": "starkwang", 6 | "license": "MIT", 7 | "scripts": { 8 | "build": "rimraf dist && tsc -p tsconfig.build.json", 9 | "format": "prettier --write \"src/**/*.ts\"", 10 | "start": "ts-node -r tsconfig-paths/register src/main.ts", 11 | "start:dev": "tsc-watch -p tsconfig.build.json --onSuccess \"node dist/main.js\"", 12 | "start:debug": "tsc-watch -p tsconfig.build.json --onSuccess \"node --inspect-brk dist/main.js\"", 13 | "start:prod": "node dist/main.js", 14 | "lint": "tslint -p tsconfig.json -c tslint.json", 15 | "test": "jest", 16 | "test:watch": "jest --watch", 17 | "test:cov": "jest --coverage", 18 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 19 | "test:e2e": "jest --config ./test/jest-e2e.json" 20 | }, 21 | "dependencies": { 22 | "@nestjs/common": "^6.0.0", 23 | "@nestjs/core": "^6.0.0", 24 | "@nestjs/jwt": "^6.1.1", 25 | "@nestjs/passport": "^6.1.0", 26 | "@nestjs/platform-express": "^6.0.0", 27 | "@nestjs/swagger": "^3.1.0", 28 | "@nestjs/typeorm": "^6.1.3", 29 | "class-transformer": "^0.2.3", 30 | "class-validator": "^0.10.0", 31 | "dotenv": "^8.1.0", 32 | "mysql": "^2.17.1", 33 | "passport": "^0.4.0", 34 | "passport-jwt": "^4.0.0", 35 | "reflect-metadata": "^0.1.12", 36 | "rimraf": "^2.6.2", 37 | "rxjs": "^6.3.3", 38 | "typeorm": "^0.2.18" 39 | }, 40 | "devDependencies": { 41 | "@nestjs/testing": "^6.0.0", 42 | "@types/express": "4.16.1", 43 | "@types/jest": "24.0.11", 44 | "@types/node": "11.13.4", 45 | "@types/supertest": "2.0.7", 46 | "jest": "24.7.1", 47 | "prettier": "1.17.0", 48 | "supertest": "4.0.2", 49 | "ts-jest": "24.0.2", 50 | "ts-node": "8.1.0", 51 | "tsc-watch": "2.2.1", 52 | "tsconfig-paths": "3.8.0", 53 | "tslint": "5.16.0", 54 | "typescript": "3.4.3" 55 | }, 56 | "jest": { 57 | "moduleFileExtensions": [ 58 | "js", 59 | "json", 60 | "ts" 61 | ], 62 | "rootDir": "src", 63 | "testRegex": ".spec.ts$", 64 | "transform": { 65 | "^.+\\.(t|j)s$": "ts-jest" 66 | }, 67 | "coverageDirectory": "../coverage", 68 | "testEnvironment": "node" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.getHello(); 11 | } 12 | 13 | @Get('stark') 14 | getStark(): string { 15 | return this.appService.getStark(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | @Module({ 6 | imports: [], 7 | controllers: [AppController], 8 | providers: [AppService], 9 | }) 10 | export class AppModule {} 11 | -------------------------------------------------------------------------------- /src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello Stark wang!'; 7 | } 8 | 9 | getStark(): string { 10 | return 'Hello Stark xiansen!'; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/common/README.md: -------------------------------------------------------------------------------- 1 | > 站位提交文件 2 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | // import { AppModule } from './app.module'; 3 | import { AppModule } from './modules/main/app.module'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.create(AppModule); 7 | await app.listen(3008); 8 | } 9 | bootstrap(); 10 | -------------------------------------------------------------------------------- /src/middleware/README.md: -------------------------------------------------------------------------------- 1 | > 站位提交文件 2 | -------------------------------------------------------------------------------- /src/modules/auth/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Body, Post, UseGuards, Get, Request } from '@nestjs/common'; 2 | import { ApiResponse, ApiUseTags, ApiBearerAuth } from '@nestjs/swagger'; 3 | import { AuthGuard } from '@nestjs/passport'; 4 | import { AuthService, LoginPayload, RegisterPayload } from './'; 5 | import { UsersService } from './../user'; 6 | 7 | @Controller('api/auth') 8 | @ApiUseTags('authentication') 9 | export class AuthController { 10 | constructor( 11 | private readonly authService: AuthService, 12 | private readonly userService: UsersService, 13 | ) { } 14 | 15 | @Post('login') 16 | @ApiResponse({ status: 201, description: 'Successful Login' }) 17 | @ApiResponse({ status: 400, description: 'Bad Request' }) 18 | @ApiResponse({ status: 401, description: 'Unauthorized' }) 19 | async login(@Body() payload: LoginPayload): Promise { 20 | const user = await this.authService.validateUser(payload); 21 | return await this.authService.createToken(user); 22 | } 23 | 24 | @Post('register') 25 | @ApiResponse({ status: 201, description: 'Successful Registration' }) 26 | @ApiResponse({ status: 400, description: 'Bad Request' }) 27 | @ApiResponse({ status: 401, description: 'Unauthorized' }) 28 | async register(@Body() payload: RegisterPayload): Promise { 29 | const user = await this.userService.create(payload); 30 | return await this.authService.createToken(user); 31 | } 32 | 33 | @ApiBearerAuth() 34 | @UseGuards(AuthGuard()) 35 | @Get('me') 36 | @ApiResponse({ status: 200, description: 'Successful Response' }) 37 | @ApiResponse({ status: 401, description: 'Unauthorized' }) 38 | async getLoggedInUser(@Request() request): Promise { 39 | return request.user; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/modules/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { JwtModule } from '@nestjs/jwt'; 3 | import { PassportModule } from '@nestjs/passport'; 4 | import { ConfigModule, ConfigService } from './../config'; 5 | import { UserModule } from './../user'; 6 | import { AuthService } from './auth.service'; 7 | import { JwtStrategy } from './jwt.strategy'; 8 | import { AuthController } from './auth.controller'; 9 | 10 | @Module({ 11 | imports: [ 12 | UserModule, 13 | ConfigModule, 14 | PassportModule.register({ defaultStrategy: 'jwt' }), 15 | JwtModule.registerAsync({ 16 | imports: [ 17 | ConfigModule, 18 | ], 19 | useFactory: async (configService: ConfigService) => { 20 | return { 21 | secret: configService.get('JWT_SECRET_KEY'), 22 | signOptions: { 23 | ...( 24 | configService.get('JWT_EXPIRATION_TIME') 25 | ? { 26 | expiresIn: Number(configService.get('JWT_EXPIRATION_TIME')), 27 | } 28 | : {} 29 | ), 30 | }, 31 | }; 32 | }, 33 | inject: [ 34 | ConfigService, 35 | ], 36 | }), 37 | ], 38 | controllers: [ 39 | AuthController, 40 | ], 41 | providers: [ 42 | AuthService, 43 | JwtStrategy, 44 | ], 45 | exports: [ 46 | PassportModule.register({ defaultStrategy: 'jwt' }), 47 | ], 48 | }) 49 | export class AuthModule { } 50 | -------------------------------------------------------------------------------- /src/modules/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 2 | import { JwtService } from '@nestjs/jwt'; 3 | import { ConfigService } from './../config'; 4 | import { User, UsersService } from './../user'; 5 | import { LoginPayload } from './login.payload'; 6 | 7 | @Injectable() 8 | export class AuthService { 9 | constructor( 10 | private readonly jwtService: JwtService, 11 | private readonly configService: ConfigService, 12 | private readonly userService: UsersService, 13 | ) { } 14 | 15 | async createToken(user: User) { 16 | return { 17 | expiresIn: this.configService.get('JWT_EXPIRATION_TIME'), 18 | accessToken: this.jwtService.sign({id: user.id}), 19 | user: user 20 | }; 21 | } 22 | 23 | async validateUser(payload: LoginPayload): Promise { 24 | const user = await this.userService.getByEmailAndPass(payload.email, payload.password); 25 | if (!user) { 26 | throw new UnauthorizedException('Wrong login combination!'); 27 | } 28 | return user; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/modules/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login.payload'; 2 | export * from './register.payload'; 3 | export * from './auth.service'; 4 | export * from './jwt.strategy'; 5 | export * from './auth.module'; 6 | export * from './auth.controller'; 7 | export * from './jwt-guard'; 8 | -------------------------------------------------------------------------------- /src/modules/auth/jwt-guard.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ExecutionContext, 3 | Injectable, 4 | UnauthorizedException, 5 | } from '@nestjs/common'; 6 | import { AuthGuard } from '@nestjs/passport'; 7 | 8 | @Injectable() 9 | export class JwtAuthGuard extends AuthGuard('jwt') { 10 | canActivate(context: ExecutionContext) { 11 | // Add your custom authentication logic here 12 | // for example, call super.logIn(request) to establish a session. 13 | return super.canActivate(context); 14 | } 15 | 16 | handleRequest(err, user, info) { 17 | if (err || !user) { 18 | throw err || new UnauthorizedException(); 19 | } 20 | return user; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/modules/auth/jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { ExtractJwt, Strategy, JwtPayload } from 'passport-jwt'; 2 | import { PassportStrategy } from '@nestjs/passport'; 3 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 4 | 5 | import { ConfigService } from './../config'; 6 | import { UsersService } from './../user'; 7 | 8 | @Injectable() 9 | export class JwtStrategy extends PassportStrategy(Strategy) { 10 | constructor( 11 | readonly configService: ConfigService, 12 | private readonly usersService: UsersService, 13 | ) { 14 | super({ 15 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 16 | secretOrKey: configService.get('JWT_SECRET_KEY'), 17 | }); 18 | } 19 | 20 | async validate({ iat, exp, id }: JwtPayload, done) { 21 | const timeDiff = exp - iat; 22 | if (timeDiff <= 0) { 23 | throw new UnauthorizedException(); 24 | } 25 | 26 | const user = await this.usersService.get(id); 27 | if (!user) { 28 | throw new UnauthorizedException(); 29 | } 30 | 31 | delete user.password; 32 | done(null, user); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/modules/auth/login.payload.ts: -------------------------------------------------------------------------------- 1 | import { ApiModelProperty } from '@nestjs/swagger'; 2 | import { IsEmail, IsNotEmpty, MinLength } from 'class-validator'; 3 | 4 | export class LoginPayload { 5 | @ApiModelProperty({ 6 | required: true, 7 | }) 8 | @IsEmail() 9 | email: string; 10 | @ApiModelProperty({ 11 | required: true, 12 | }) 13 | @IsNotEmpty() 14 | @MinLength(5) 15 | password: string; 16 | } 17 | -------------------------------------------------------------------------------- /src/modules/auth/register.payload.ts: -------------------------------------------------------------------------------- 1 | import { ApiModelProperty } from '@nestjs/swagger'; 2 | import { IsEmail, IsNotEmpty, MinLength } from 'class-validator'; 3 | 4 | export class RegisterPayload { 5 | @ApiModelProperty({ 6 | required: true, 7 | }) 8 | @IsEmail() 9 | email: string; 10 | 11 | @ApiModelProperty({ 12 | required: true, 13 | }) 14 | @IsNotEmpty() 15 | name: string; 16 | 17 | @ApiModelProperty({ 18 | required: true, 19 | }) 20 | 21 | @ApiModelProperty({ 22 | required: true, 23 | }) 24 | @IsNotEmpty() 25 | @MinLength(5) 26 | password: string; 27 | } 28 | -------------------------------------------------------------------------------- /src/modules/config/config.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigService } from './config.service'; 3 | 4 | @Module({ 5 | providers: [ 6 | { 7 | provide: ConfigService, 8 | useValue: new ConfigService('.env'), 9 | }, 10 | ], 11 | exports: [ConfigService], 12 | }) 13 | export class ConfigModule {} 14 | -------------------------------------------------------------------------------- /src/modules/config/config.service.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv'; 2 | import * as fs from 'fs'; 3 | 4 | export class ConfigService { 5 | private readonly envConfig: { [key: string]: string }; 6 | 7 | constructor(filePath: string) { 8 | this.envConfig = dotenv.parse(fs.readFileSync(filePath)); 9 | } 10 | 11 | get(key: string): string { 12 | return this.envConfig[key]; 13 | } 14 | 15 | isEnv(env: string) { 16 | return this.envConfig.APP_ENV === env; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/modules/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './config.module'; 2 | export * from './config.service'; 3 | -------------------------------------------------------------------------------- /src/modules/main/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { ConfigModule } from './../config'; 5 | 6 | describe('AppController', () => { 7 | let app: TestingModule; 8 | 9 | beforeAll(async () => { 10 | app = await Test.createTestingModule({ 11 | controllers: [AppController], 12 | providers: [AppService], 13 | imports: [ConfigModule], 14 | }).compile(); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "https://shudong.wang"', () => { 19 | const appController = app.get(AppController); 20 | expect(appController.root()).toBe('https://shudong.wang'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/modules/main/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Get, Controller, UseGuards } from '@nestjs/common'; 2 | // import { AuthGuard } from '@nestjs/passport'; 3 | import { AppService } from './app.service'; 4 | 5 | @Controller() 6 | export class AppController { 7 | constructor(private readonly appService: AppService) {} 8 | 9 | @Get() 10 | // @UseGuards(AuthGuard()) 11 | root(): string { 12 | return this.appService.root(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/modules/main/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule, TypeOrmModuleAsyncOptions } from '@nestjs/typeorm'; 3 | import { AppController } from './app.controller'; 4 | import { AppService } from './app.service'; 5 | import { ConfigModule, ConfigService } from './../config'; 6 | // import { AuthModule } from './../auth'; 7 | import { StarkModule } from '../stark'; 8 | import { MessagesModule } from '../message/messages.module'; 9 | import { AuthModule } from './../auth'; 10 | 11 | 12 | @Module({ 13 | imports: [ 14 | TypeOrmModule.forRootAsync({ 15 | imports: [ConfigModule], 16 | inject: [ConfigService], 17 | useFactory: (configService: ConfigService) => { 18 | return { 19 | type: configService.get('DB_TYPE'), 20 | host: configService.get('DB_HOST'), 21 | port: configService.get('DB_PORT'), 22 | username: configService.get('DB_USERNAME'), 23 | password: configService.get('DB_PASSWORD'), 24 | database: configService.get('DB_DATABASE'), 25 | entities: [__dirname + './../**/**.entity{.ts,.js}'], 26 | synchronize: configService.isEnv('dev'), 27 | } as TypeOrmModuleAsyncOptions; 28 | }, 29 | }), 30 | ConfigModule, 31 | AuthModule, 32 | StarkModule, 33 | MessagesModule, 34 | ], 35 | controllers: [ 36 | AppController, 37 | ], 38 | providers: [ 39 | AppService, 40 | ], 41 | }) 42 | export class AppModule { } 43 | -------------------------------------------------------------------------------- /src/modules/main/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { ConfigService } from './../config'; 3 | 4 | @Injectable() 5 | export class AppService { 6 | constructor(private config: ConfigService) {} 7 | 8 | root(): string { 9 | return this.config.get('APP_URL'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/message/message.entity.ts: -------------------------------------------------------------------------------- 1 | import { Column, Entity, PrimaryGeneratedColumn, BaseEntity } from 'typeorm'; 2 | @Entity() 3 | export class Message extends BaseEntity { 4 | @PrimaryGeneratedColumn() 5 | id: number; 6 | 7 | @Column('int', { name: 'user_id' }) 8 | userId: number; 9 | 10 | @Column('text', { name: 'content' }) 11 | content: string; 12 | } 13 | -------------------------------------------------------------------------------- /src/modules/message/messages.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common'; 2 | import { MessagesService } from './messages.service'; 3 | import { Message } from './message.entity'; 4 | import { AuthGuard } from '@nestjs/passport'; 5 | 6 | @Controller('messages') 7 | export class MessagesController { 8 | constructor(private messagesService: MessagesService) { } 9 | 10 | // @UseGuards(AuthGuard()) 11 | @Get() 12 | findAll(): Promise { 13 | return this.messagesService.findAll(); 14 | } 15 | 16 | @Post('/add') 17 | async add(@Body() payload): Promise { 18 | console.log('payloadpayload', payload) 19 | await this.messagesService.create(payload); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/modules/message/messages.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MessagesService } from './messages.service'; 3 | import { MessagesController } from './messages.controller'; 4 | 5 | import { TypeOrmModule } from '@nestjs/typeorm'; 6 | import { Message } from './message.entity'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([Message])], 10 | providers: [MessagesService], 11 | controllers: [MessagesController] 12 | }) 13 | export class MessagesModule {} 14 | -------------------------------------------------------------------------------- /src/modules/message/messages.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | import { Message } from './message.entity'; 5 | 6 | @Injectable() 7 | export class MessagesService { 8 | constructor( 9 | @InjectRepository(Message) 10 | private readonly messagesRepository: Repository, 11 | ) { } 12 | 13 | private readonly messages: Message[] = []; 14 | async save(): Promise { 15 | const message = new Message(); 16 | message.userId = Math.round(Math.random() * 1000); 17 | message.content = '消息内容'; 18 | return await message.save(); 19 | } 20 | async create(payload) { 21 | await this.messagesRepository.save(payload) 22 | } 23 | async findAll(): Promise { 24 | return await this.messagesRepository.find(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/modules/stark/index.ts: -------------------------------------------------------------------------------- 1 | // export * from './password.transformer'; 2 | export * from './stark.controller'; 3 | export * from './stark.service'; 4 | export * from './stark.module'; 5 | -------------------------------------------------------------------------------- /src/modules/stark/stark.controller.ts: -------------------------------------------------------------------------------- 1 | import { Get, Controller, Request } from '@nestjs/common'; 2 | // import { AuthGuard } from '@nestjs/passport'; 3 | import { StarkService } from './stark.service'; 4 | // import { UsersService } from './../user'; 5 | import { Repository } from 'typeorm'; 6 | import { InjectRepository } from '@nestjs/typeorm'; 7 | // import { User, UserFillableFields } from '../user/user.entity'; 8 | @Controller('stark') 9 | export class StarkController { 10 | constructor( 11 | // private readonly usersService: UsersService, 12 | // @InjectRepository(User) 13 | private readonly starkService: StarkService, 14 | // private readonly userRepository: Repository, 15 | ) {} 16 | 17 | @Get('me') 18 | async getLoggedInUser(@Request() request): Promise { 19 | console.log('request', request.user); 20 | return request.user; 21 | } 22 | 23 | @Get('stark') 24 | getStark(): string { 25 | console.log(this); 26 | return this.starkService.getStark(); 27 | } 28 | 29 | @Get() 30 | getStarkName(): string { 31 | console.log(this); 32 | return this.starkService.getStark(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/modules/stark/stark.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { StarkController } from './stark.controller'; 3 | import { StarkService } from './stark.service'; 4 | 5 | @Module({ 6 | imports: [], 7 | controllers: [StarkController], 8 | providers: [StarkService], 9 | }) 10 | export class StarkModule {} 11 | -------------------------------------------------------------------------------- /src/modules/stark/stark.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class StarkService { 5 | 6 | getHello(): string { 7 | return 'Hello Stark wang!'; 8 | } 9 | 10 | getStark(): string { 11 | return 'Hello Stark xiansen!'; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/modules/user/index.ts: -------------------------------------------------------------------------------- 1 | export * from './password.transformer'; 2 | export * from './user.entity'; 3 | export * from './user.service'; 4 | export * from './user.module'; -------------------------------------------------------------------------------- /src/modules/user/password.transformer.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from 'crypto'; 2 | import { ValueTransformer } from 'typeorm'; 3 | 4 | export class PasswordTransformer implements ValueTransformer { 5 | to(value) { 6 | return crypto.createHmac('sha256', value).digest('hex'); 7 | } 8 | from(value) { 9 | return value; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/user/user.entity.ts: -------------------------------------------------------------------------------- 1 | import { Exclude } from 'class-transformer'; 2 | import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; 3 | import { PasswordTransformer } from './password.transformer'; 4 | 5 | @Entity({ 6 | name: 'user', 7 | }) 8 | export class User { 9 | @PrimaryGeneratedColumn() 10 | id: number; 11 | 12 | @Column({ length: 255 }) 13 | name: string; 14 | 15 | @Column({ length: 255 }) 16 | email: string; 17 | 18 | @Column({default:10}) 19 | level: number; 20 | 21 | @Column({ 22 | name: 'password', 23 | length: 255, 24 | transformer: new PasswordTransformer(), 25 | }) 26 | @Exclude() 27 | password: string; 28 | } 29 | 30 | export class UserFillableFields { 31 | email: string; 32 | name: string; 33 | password: string; 34 | } 35 | -------------------------------------------------------------------------------- /src/modules/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { User } from './user.entity'; 4 | import { UsersService } from './user.service'; 5 | 6 | @Module({ 7 | imports: [ 8 | TypeOrmModule.forFeature([User]), 9 | ], 10 | exports: [UsersService], 11 | providers: [ 12 | UsersService, 13 | ], 14 | }) 15 | export class UserModule { } -------------------------------------------------------------------------------- /src/modules/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from 'crypto'; 2 | import { Injectable, NotAcceptableException } from '@nestjs/common'; 3 | import { InjectRepository } from '@nestjs/typeorm'; 4 | import { Repository } from 'typeorm'; 5 | 6 | import { User, UserFillableFields } from './user.entity'; 7 | 8 | @Injectable() 9 | export class UsersService { 10 | 11 | constructor( 12 | @InjectRepository(User) 13 | private readonly userRepository: Repository, 14 | ) { } 15 | 16 | async get(id: number) { 17 | return this.userRepository.findOne(id); 18 | } 19 | 20 | async getByEmail(email: string) { 21 | return await this.userRepository.createQueryBuilder('users') 22 | .where('users.email = :email') 23 | .setParameter('email', email) 24 | .getOne(); 25 | } 26 | 27 | async getByEmailAndPass(email: string, password: string) { 28 | const passHash = crypto.createHmac('sha256', password).digest('hex'); 29 | return await this.userRepository.createQueryBuilder('users') 30 | .where('users.email = :email and users.password = :password') 31 | .setParameter('email', email) 32 | .setParameter('password', passHash) 33 | .getOne(); 34 | } 35 | 36 | async create( 37 | payload: UserFillableFields, 38 | ) { 39 | const user = await this.getByEmail(payload.email); 40 | 41 | if (user) { 42 | throw new NotAcceptableException( 43 | 'User with provided email already created.', 44 | ); 45 | } 46 | 47 | return await this.userRepository.save( 48 | this.userRepository.create(payload), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/swagger/constants.ts: -------------------------------------------------------------------------------- 1 | export const SWAGGER_API_ROOT = 'api/docs'; 2 | export const SWAGGER_API_NAME = 'Simple API'; 3 | export const SWAGGER_API_DESCRIPTION = 'Simple API Description'; 4 | export const SWAGGER_API_CURRENT_VERSION = '1.0'; 5 | export const SWAGGER_API_AUTH_NAME = 'Authorization'; 6 | export const SWAGGER_API_AUTH_LOCATION = 'header'; 7 | export const SWAGGER_API_SCHEMES: ('http' | 'https')[] = [ 'http', 'https' ]; 8 | -------------------------------------------------------------------------------- /src/swagger/index.ts: -------------------------------------------------------------------------------- 1 | import { INestApplication } from '@nestjs/common'; 2 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 3 | import { 4 | SWAGGER_API_ROOT, 5 | SWAGGER_API_NAME, 6 | SWAGGER_API_DESCRIPTION, 7 | SWAGGER_API_CURRENT_VERSION, 8 | SWAGGER_API_AUTH_NAME, 9 | SWAGGER_API_AUTH_LOCATION, 10 | SWAGGER_API_SCHEMES, 11 | } from './constants'; 12 | 13 | export const setupSwagger = (app: INestApplication) => { 14 | const options = new DocumentBuilder() 15 | .setTitle(SWAGGER_API_NAME) 16 | .setDescription(SWAGGER_API_DESCRIPTION) 17 | .setVersion(SWAGGER_API_CURRENT_VERSION) 18 | .setSchemes(...SWAGGER_API_SCHEMES) 19 | .addBearerAuth(SWAGGER_API_AUTH_NAME, SWAGGER_API_AUTH_LOCATION) 20 | .build(); 21 | const document = SwaggerModule.createDocument(app, options); 22 | SwaggerModule.setup(SWAGGER_API_ROOT, app, document); 23 | }; 24 | -------------------------------------------------------------------------------- /test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import * as request from 'supertest'; 3 | import { AppModule } from './../src/app.module'; 4 | 5 | describe('AppController (e2e)', () => { 6 | let app; 7 | 8 | beforeEach(async () => { 9 | const moduleFixture: TestingModule = await Test.createTestingModule({ 10 | imports: [AppModule], 11 | }).compile(); 12 | 13 | app = moduleFixture.createNestApplication(); 14 | await app.init(); 15 | }); 16 | 17 | it('/ (GET)', () => { 18 | return request(app.getHttpServer()) 19 | .get('/') 20 | .expect(200) 21 | .expect('Hello World!'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": true, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "incremental": true 13 | }, 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": { 5 | "no-unused-expression": true 6 | }, 7 | "rules": { 8 | "quotemark": [true, "single"], 9 | "member-access": [false], 10 | "ordered-imports": [false], 11 | "max-line-length": [true, 150], 12 | "member-ordering": [false], 13 | "interface-name": [false], 14 | "arrow-parens": false, 15 | "object-literal-sort-keys": false 16 | }, 17 | "rulesDirectory": [] 18 | } 19 | --------------------------------------------------------------------------------