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