migrate NestJS boilerplate
This commit is contained in:
parent
05cf6313db
commit
07baec4577
73
README.md
73
README.md
@ -0,0 +1,73 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||||
|
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||||
|
|
||||||
|
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||||
|
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||||
|
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
||||||
|
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||||
|
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||||
|
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
||||||
|
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||||
|
</p>
|
||||||
|
<!--[](https://opencollective.com/nest#backer)
|
||||||
|
[](https://opencollective.com/nest#sponsor)-->
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the app
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# development
|
||||||
|
$ yarn run start
|
||||||
|
|
||||||
|
# watch mode
|
||||||
|
$ yarn run start:dev
|
||||||
|
|
||||||
|
# production mode
|
||||||
|
$ yarn run start:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# unit tests
|
||||||
|
$ yarn run test
|
||||||
|
|
||||||
|
# e2e tests
|
||||||
|
$ yarn run test:e2e
|
||||||
|
|
||||||
|
# test coverage
|
||||||
|
$ yarn run test:cov
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||||
|
|
||||||
|
## Stay in touch
|
||||||
|
|
||||||
|
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
||||||
|
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||||
|
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Nest is [MIT licensed](LICENSE).
|
||||||
14
migrations/1690475833727-migrations.ts
Normal file
14
migrations/1690475833727-migrations.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class Migrations1690475833727 implements MigrationInterface {
|
||||||
|
name = 'Migrations1690475833727'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TABLE "users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "username" character varying NOT NULL, "password_hash" character varying, "deleted_at" TIMESTAMP, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_fe0bb3f6520ee0469504521e710" UNIQUE ("username"), CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id"))`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP TABLE "users"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
migrations/1691602467422-migrations.ts
Normal file
14
migrations/1691602467422-migrations.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class Migrations1691602467422 implements MigrationInterface {
|
||||||
|
name = 'Migrations1691602467422'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TABLE "companies" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "deleted_at" TIMESTAMP, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_3dacbb3eb4f095e29372ff8e131" UNIQUE ("name"), CONSTRAINT "PK_d4bc3e82a314fa9e29f652c2c22" PRIMARY KEY ("id"))`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP TABLE "companies"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
8
nest-cli.json
Normal file
8
nest-cli.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": {
|
||||||
|
"deleteOutDir": true
|
||||||
|
}
|
||||||
|
}
|
||||||
18
orm.config.ts
Normal file
18
orm.config.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { config } from 'dotenv';
|
||||||
|
|
||||||
|
config();
|
||||||
|
|
||||||
|
const configService = new ConfigService();
|
||||||
|
|
||||||
|
export default new DataSource({
|
||||||
|
type: 'postgres',
|
||||||
|
host: configService.get('DATABASE_HOST') || 'localhost',
|
||||||
|
port: configService.get('DATABASE_PORT') || 5432,
|
||||||
|
username: configService.get('DATABASE_USERNAME') || 'postgres',
|
||||||
|
password: configService.get('DATABASE_PASSWORD') || '1234',
|
||||||
|
database: configService.get('DATABASE_NAME') || 'boilerplatenestjs',
|
||||||
|
entities: ['./src/**/*.entity.ts'],
|
||||||
|
migrations: ['migrations/*-migrations.ts'],
|
||||||
|
});
|
||||||
9554
package-lock.json
generated
Normal file
9554
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
87
package.json
Normal file
87
package.json
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"name": "nestjs_typeorm",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"author": "",
|
||||||
|
"private": true,
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"scripts": {
|
||||||
|
"build": "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 dist/main",
|
||||||
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
|
"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",
|
||||||
|
"typeorm": "node -r ts-node/register ./node_modules/typeorm/cli",
|
||||||
|
"typeorm:run": "npm run typeorm migration:run -- -d ./orm.config.ts",
|
||||||
|
"typeorm:generate": "npm run typeorm -- -d ./orm.config.ts migration:generate ./migrations/migrations",
|
||||||
|
"typeorm:create": "npm run typeorm -- migration:create ./migrations/migrations",
|
||||||
|
"typeorm:revert": "npm run typeorm -- -d ./orm.config.ts migration:revert"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/common": "^9.0.0",
|
||||||
|
"@nestjs/config": "^3.0.0",
|
||||||
|
"@nestjs/core": "^9.0.0",
|
||||||
|
"@nestjs/jwt": "^10.1.0",
|
||||||
|
"@nestjs/passport": "^10.0.0",
|
||||||
|
"@nestjs/platform-express": "^9.0.0",
|
||||||
|
"@nestjs/typeorm": "^10.0.0",
|
||||||
|
"bcrypt": "^5.1.0",
|
||||||
|
"class-validator": "^0.14.0",
|
||||||
|
"passport": "^0.6.0",
|
||||||
|
"passport-jwt": "^4.0.1",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
|
"pg": "^8.11.1",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"rxjs": "^7.2.0",
|
||||||
|
"typeorm": "^0.3.17"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^9.0.0",
|
||||||
|
"@nestjs/schematics": "^9.0.0",
|
||||||
|
"@nestjs/testing": "^9.0.0",
|
||||||
|
"@types/bcrypt": "^5.0.0",
|
||||||
|
"@types/express": "^4.17.13",
|
||||||
|
"@types/jest": "29.2.4",
|
||||||
|
"@types/node": "18.11.18",
|
||||||
|
"@types/passport-jwt": "^3.0.9",
|
||||||
|
"@types/supertest": "^2.0.11",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||||
|
"@typescript-eslint/parser": "^5.0.0",
|
||||||
|
"eslint": "^8.0.1",
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"jest": "29.3.1",
|
||||||
|
"prettier": "^2.3.2",
|
||||||
|
"source-map-support": "^0.5.20",
|
||||||
|
"supertest": "^6.1.3",
|
||||||
|
"ts-jest": "29.0.3",
|
||||||
|
"ts-loader": "^9.2.3",
|
||||||
|
"ts-node": "^10.0.0",
|
||||||
|
"tsconfig-paths": "4.1.1",
|
||||||
|
"typescript": "^4.7.4"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"json",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"rootDir": "src",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"**/*.(t|j)s"
|
||||||
|
],
|
||||||
|
"coverageDirectory": "../coverage",
|
||||||
|
"testEnvironment": "node"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/app.controller.spec.ts
Normal file
22
src/app.controller.spec.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
12
src/app.controller.ts
Normal file
12
src/app.controller.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class AppController {
|
||||||
|
constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getHello(): string {
|
||||||
|
return this.appService.getHello();
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/app.module.ts
Normal file
54
src/app.module.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { APP_GUARD } from '@nestjs/core';
|
||||||
|
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
|
||||||
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
|
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
import { AppController } from './app.controller';
|
||||||
|
|
||||||
|
import databaseConfig from './config/database.config';
|
||||||
|
import jwtConfig from './config/jwt.config';
|
||||||
|
|
||||||
|
import { UsersDatabaseModule } from './database/user/users.module';
|
||||||
|
|
||||||
|
import { JwtAuthGuard } from './libs/jwt/jwtAuth.guard';
|
||||||
|
|
||||||
|
import { AuthorizationModule } from './application/authorization/authorization.module';
|
||||||
|
import { UsersModule } from './application/users/users.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true,
|
||||||
|
cache: true,
|
||||||
|
load: [databaseConfig, jwtConfig],
|
||||||
|
}),
|
||||||
|
TypeOrmModule.forRootAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: (configService: ConfigService) => {
|
||||||
|
const databaseConfig = configService.get<TypeOrmModuleOptions>('database');
|
||||||
|
return databaseConfig;
|
||||||
|
},
|
||||||
|
}), JwtModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: (configService: ConfigService) => {
|
||||||
|
const jwtConfig = configService.get<JwtModuleOptions>('jwt')
|
||||||
|
return jwtConfig
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
UsersModule,
|
||||||
|
AuthorizationModule
|
||||||
|
],
|
||||||
|
controllers: [AppController],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_GUARD,
|
||||||
|
useClass: JwtAuthGuard,
|
||||||
|
},
|
||||||
|
AppService
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
8
src/app.service.ts
Normal file
8
src/app.service.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AppService {
|
||||||
|
getHello(): string {
|
||||||
|
return 'Hello World!';
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/application/authorization/authorization.controller.ts
Normal file
17
src/application/authorization/authorization.controller.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Controller, Post, Request, UseGuards } from '@nestjs/common';
|
||||||
|
import { Public } from 'src/libs/jwt/jwtAuth.guard';
|
||||||
|
import { AuthorizationService } from './authorization.service';
|
||||||
|
import { LocalAuthGuard } from './localAuth.guard';
|
||||||
|
|
||||||
|
@Controller('authorization')
|
||||||
|
export class AuthorizationController {
|
||||||
|
|
||||||
|
constructor(private authorizationService: AuthorizationService) { }
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@UseGuards(LocalAuthGuard)
|
||||||
|
@Post('login')
|
||||||
|
async login(@Request() req) {
|
||||||
|
return this.authorizationService.login(req.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/application/authorization/authorization.module.ts
Normal file
30
src/application/authorization/authorization.module.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AuthorizationService } from './authorization.service';
|
||||||
|
import { AuthorizationController } from './authorization.controller';
|
||||||
|
import { UsersDatabaseModule } from 'src/database/user/users.module';
|
||||||
|
import { LocalStrategy } from './local.strategy';
|
||||||
|
import { PassportModule } from '@nestjs/passport';
|
||||||
|
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
|
||||||
|
import { JwtAuthModule } from 'src/libs/jwt/jwtAuth.module';
|
||||||
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
UsersDatabaseModule,
|
||||||
|
PassportModule,
|
||||||
|
JwtAuthModule,
|
||||||
|
JwtModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: (configService: ConfigService) => {
|
||||||
|
const jwtConfig = configService.get<JwtModuleOptions>('jwt')
|
||||||
|
return jwtConfig
|
||||||
|
}
|
||||||
|
})],
|
||||||
|
providers: [AuthorizationService, LocalStrategy],
|
||||||
|
controllers: [AuthorizationController]
|
||||||
|
})
|
||||||
|
export class AuthorizationModule { }
|
||||||
36
src/application/authorization/authorization.service.ts
Normal file
36
src/application/authorization/authorization.service.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { UsersRepository } from 'src/database/user/users.repository';
|
||||||
|
import { AuthorizationDto } from './dto/authorization.dto';
|
||||||
|
import * as bcrypt from 'bcrypt';
|
||||||
|
import { UserEntity } from 'src/database/user/user.entity';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthorizationService {
|
||||||
|
constructor(
|
||||||
|
private usersRepository: UsersRepository,
|
||||||
|
private jwtService: JwtService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
async validateUser(authorizationDto: AuthorizationDto): Promise<UserEntity> {
|
||||||
|
const userEntity = await this.usersRepository.getUserByUsername(authorizationDto.username)
|
||||||
|
const isMatch = await bcrypt.compare(authorizationDto.password, userEntity.passwordHash)
|
||||||
|
if (isMatch) {
|
||||||
|
return await this.usersRepository.getUser(userEntity.id)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
async login(user: UserEntity) {
|
||||||
|
if (!user) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const payload = { id: user.id }
|
||||||
|
|
||||||
|
return {
|
||||||
|
access_token: this.jwtService.sign(payload),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/application/authorization/dto/authorization.dto.ts
Normal file
10
src/application/authorization/dto/authorization.dto.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { IsNotEmpty } from "class-validator";
|
||||||
|
|
||||||
|
export class AuthorizationDto {
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
21
src/application/authorization/local.strategy.ts
Normal file
21
src/application/authorization/local.strategy.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Strategy } from 'passport-local';
|
||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||||
|
import { AuthorizationService } from './authorization.service';
|
||||||
|
import { UserEntity } from 'src/database/user/user.entity';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LocalStrategy extends PassportStrategy(Strategy, 'local') {
|
||||||
|
constructor(private authorizationService: AuthorizationService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(username: string, password: string): Promise<UserEntity> {
|
||||||
|
const user = await this.authorizationService.validateUser({ username: username, password: password });
|
||||||
|
if (!user) {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/application/authorization/localAuth.guard.ts
Normal file
5
src/application/authorization/localAuth.guard.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Injectable, SetMetadata } from '@nestjs/common';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LocalAuthGuard extends AuthGuard('local') { }
|
||||||
19
src/application/users/dto/user.dto.ts
Normal file
19
src/application/users/dto/user.dto.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Optional } from "@nestjs/common";
|
||||||
|
import { IsEmail, IsUUID, IsString } from "class-validator";
|
||||||
|
|
||||||
|
import { IUser } from "src/database/user/interfaces/user.interface";
|
||||||
|
|
||||||
|
|
||||||
|
export class UserDto implements IUser {
|
||||||
|
|
||||||
|
@Optional()
|
||||||
|
@IsUUID()
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
passwordHash: string;
|
||||||
|
|
||||||
|
@IsEmail()
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
}
|
||||||
20
src/application/users/users.controller.ts
Normal file
20
src/application/users/users.controller.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Controller, Post, Body,} from '@nestjs/common';
|
||||||
|
import { Public } from 'src/libs/jwt/jwtAuth.guard';
|
||||||
|
import { UsersService } from './users.service';
|
||||||
|
import { UserDto } from './dto/user.dto';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Controller('users')
|
||||||
|
export class UsersController {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private usersService: UsersService
|
||||||
|
|
||||||
|
) { }
|
||||||
|
@Public()
|
||||||
|
@Post()
|
||||||
|
async create(@Body() createUserDto: UserDto) {
|
||||||
|
await this.usersService.createUser(createUserDto)
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/application/users/users.module.ts
Normal file
29
src/application/users/users.module.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
|
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
|
||||||
|
import { UsersController } from './users.controller';
|
||||||
|
import { UsersService } from './users.service';
|
||||||
|
import { UsersDatabaseModule } from 'src/database/user/users.module';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
UsersDatabaseModule,
|
||||||
|
ConfigModule,
|
||||||
|
JwtModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: (configService: ConfigService) => {
|
||||||
|
const jwtConfig = configService.get<JwtModuleOptions>('jwt')
|
||||||
|
return jwtConfig
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
UsersService
|
||||||
|
],
|
||||||
|
controllers: [UsersController]
|
||||||
|
})
|
||||||
|
|
||||||
|
export class UsersModule { }
|
||||||
27
src/application/users/users.service.ts
Normal file
27
src/application/users/users.service.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { UsersRepository } from 'src/database/user/users.repository';
|
||||||
|
import { randomUUID } from 'crypto'
|
||||||
|
|
||||||
|
import * as bcrypt from 'bcrypt';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { UserDto } from './dto/user.dto';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UsersService {
|
||||||
|
constructor(
|
||||||
|
private usersRepository: UsersRepository,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
|
||||||
|
async createUser(createUserDto: UserDto) {
|
||||||
|
|
||||||
|
const userEntity = await this.usersRepository.create(createUserDto)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
25
src/config/database.config.ts
Normal file
25
src/config/database.config.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { registerAs } from '@nestjs/config';
|
||||||
|
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
export default registerAs<TypeOrmModuleOptions>('database', () => {
|
||||||
|
return {
|
||||||
|
type: 'postgres',
|
||||||
|
host: process.env.DATABASE_HOST || 'localhost',
|
||||||
|
port:
|
||||||
|
Number(process.env.DATABASE_PORT) || 5432,
|
||||||
|
username:
|
||||||
|
process.env.DATABASE_USERNAME ||
|
||||||
|
'postgres',
|
||||||
|
password:
|
||||||
|
process.env.DATABASE_PASSWORD ||
|
||||||
|
'1234',
|
||||||
|
database:
|
||||||
|
process.env.DATABASE_NAME ||
|
||||||
|
'boilerplatenestjs',
|
||||||
|
logging: process.env.DATABASE_LOGGING === 'true',
|
||||||
|
autoLoadEntities: true,
|
||||||
|
synchronize: false,
|
||||||
|
migrations: ['dist/migrations/*-migrations.js'],
|
||||||
|
migrationsRun: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
9
src/config/jwt.config.ts
Normal file
9
src/config/jwt.config.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { registerAs } from '@nestjs/config';
|
||||||
|
import {JwtModuleOptions} from '@nestjs/jwt';
|
||||||
|
|
||||||
|
export default registerAs<JwtModuleOptions>('jwt', ()=>{
|
||||||
|
return{
|
||||||
|
secret: process.env.JWT_SECRET_KEY,
|
||||||
|
signOptions: { expiresIn: process.env.JWT_EXPIRATION_TIME}
|
||||||
|
}
|
||||||
|
});
|
||||||
21
src/database/company/company.entity.ts
Normal file
21
src/database/company/company.entity.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
|
||||||
|
|
||||||
|
@Entity({ name: "companies" })
|
||||||
|
export class CompanyEntity {
|
||||||
|
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({unique:true})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@DeleteDateColumn({ name: "deleted_at", nullable: true })
|
||||||
|
deletedAt: Date;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: "created_at" })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: "updated_at" })
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
}
|
||||||
12
src/database/company/company.module.ts
Normal file
12
src/database/company/company.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { CompanyEntity } from './company.entity';
|
||||||
|
import { CompaniesRepository } from './company.repository';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([CompanyEntity])],
|
||||||
|
providers: [CompaniesRepository],
|
||||||
|
exports: [CompaniesRepository]
|
||||||
|
})
|
||||||
|
|
||||||
|
export class UsersDatabaseModule { }
|
||||||
40
src/database/company/company.repository.ts
Normal file
40
src/database/company/company.repository.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Equal, Repository } from 'typeorm';
|
||||||
|
import { ICompany } from './interfaces/company.interface';
|
||||||
|
import { CompanyEntity } from './company.entity';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CompaniesRepository {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(CompanyEntity)
|
||||||
|
private repository: Repository<CompanyEntity>,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
async create(company: ICompany) {
|
||||||
|
const userEntity = this.repository.create(company)
|
||||||
|
return this.repository.save(userEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUser(id:string){
|
||||||
|
const user = await this.repository.findOne({
|
||||||
|
select:['id','name'],
|
||||||
|
where:{
|
||||||
|
id:Equal(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCompanyByName(name:string){
|
||||||
|
const user = await this.repository.findOne({
|
||||||
|
select:['id','name'],
|
||||||
|
where:{
|
||||||
|
name:Equal(name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
5
src/database/company/interfaces/company.interface.ts
Normal file
5
src/database/company/interfaces/company.interface.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
export interface ICompany {
|
||||||
|
id: string
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
6
src/database/user/interfaces/user.interface.ts
Normal file
6
src/database/user/interfaces/user.interface.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
export interface IUser {
|
||||||
|
id: string
|
||||||
|
username: string;
|
||||||
|
passwordHash: string;
|
||||||
|
}
|
||||||
24
src/database/user/user.entity.ts
Normal file
24
src/database/user/user.entity.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
|
||||||
|
|
||||||
|
@Entity({ name: "users" })
|
||||||
|
export class UserEntity {
|
||||||
|
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({unique:true})
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Column({ name: "password_hash", nullable: true, select: false })
|
||||||
|
passwordHash: string;
|
||||||
|
|
||||||
|
@DeleteDateColumn({ name: "deleted_at", nullable: true })
|
||||||
|
deletedAt: Date;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: "created_at" })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: "updated_at" })
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
}
|
||||||
12
src/database/user/users.module.ts
Normal file
12
src/database/user/users.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { UserEntity } from './user.entity';
|
||||||
|
import { UsersRepository } from './users.repository';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([UserEntity])],
|
||||||
|
providers: [UsersRepository],
|
||||||
|
exports: [UsersRepository]
|
||||||
|
})
|
||||||
|
|
||||||
|
export class UsersDatabaseModule { }
|
||||||
40
src/database/user/users.repository.ts
Normal file
40
src/database/user/users.repository.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Equal, Repository } from 'typeorm';
|
||||||
|
import { IUser } from './interfaces/user.interface';
|
||||||
|
import { UserEntity } from './user.entity';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UsersRepository {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(UserEntity)
|
||||||
|
private repository: Repository<UserEntity>,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
async create(user: IUser) {
|
||||||
|
const userEntity = this.repository.create(user)
|
||||||
|
return this.repository.save(userEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUser(id:string){
|
||||||
|
const user = await this.repository.findOne({
|
||||||
|
select:['id','username'],
|
||||||
|
where:{
|
||||||
|
id:Equal(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserByUsername(username:string){
|
||||||
|
const user = await this.repository.findOne({
|
||||||
|
select:['id','username','passwordHash'],
|
||||||
|
where:{
|
||||||
|
username:Equal(username)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
36
src/libs/jwt/jwtAuth.guard.ts
Normal file
36
src/libs/jwt/jwtAuth.guard.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
|
||||||
|
export const IS_PUBLIC_KEY = 'isPublic';
|
||||||
|
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtAuthGuard extends AuthGuard('jwt') {
|
||||||
|
|
||||||
|
constructor(private reflector: Reflector) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
canActivate(context: ExecutionContext) {
|
||||||
|
// Add your custom authentication logic here
|
||||||
|
// for example, call super.logIn(request) to establish a session.
|
||||||
|
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
||||||
|
context.getHandler(),
|
||||||
|
context.getClass(),
|
||||||
|
]);
|
||||||
|
if (isPublic) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.canActivate(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRequest(err, user, info) {
|
||||||
|
// You can throw an exception based on either "info" or "err" arguments
|
||||||
|
if (err || !user) {
|
||||||
|
throw err || new UnauthorizedException();
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/libs/jwt/jwtAuth.module.ts
Normal file
12
src/libs/jwt/jwtAuth.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { JwtAuthGuard } from './jwtAuth.guard';
|
||||||
|
import { JwtStrategy } from './jwtAuth.strategy';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [JwtAuthGuard, JwtStrategy],
|
||||||
|
exports: [JwtAuthGuard, JwtStrategy]
|
||||||
|
})
|
||||||
|
|
||||||
|
export class JwtAuthModule { }
|
||||||
22
src/libs/jwt/jwtAuth.strategy.ts
Normal file
22
src/libs/jwt/jwtAuth.strategy.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { JwtModuleOptions } from '@nestjs/jwt';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||||
|
constructor(private configService: ConfigService) {
|
||||||
|
const jtwconfig = configService.get<JwtModuleOptions>('jwt')
|
||||||
|
super({
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
ignoreExpiration: false,
|
||||||
|
secretOrKey: jtwconfig.secret,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(payload: any) {
|
||||||
|
return { userId: payload.id};
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/main.ts
Normal file
8
src/main.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
await app.listen(3000);
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
24
test/app.e2e-spec.ts
Normal file
24
test/app.e2e-spec.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { INestApplication } from '@nestjs/common';
|
||||||
|
import * as request from 'supertest';
|
||||||
|
import { AppModule } from './../src/app.module';
|
||||||
|
|
||||||
|
describe('AppController (e2e)', () => {
|
||||||
|
let app: INestApplication;
|
||||||
|
|
||||||
|
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!');
|
||||||
|
});
|
||||||
|
});
|
||||||
9
test/jest-e2e.json
Normal file
9
test/jest-e2e.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"moduleFileExtensions": ["js", "json", "ts"],
|
||||||
|
"rootDir": ".",
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"testRegex": ".e2e-spec.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
tsconfig.build.json
Normal file
4
tsconfig.build.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||||
|
}
|
||||||
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"target": "es2017",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"incremental": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"strictBindCallApply": false,
|
||||||
|
"forceConsistentCasingInFileNames": false,
|
||||||
|
"noFallthroughCasesInSwitch": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user