API authentifiée
This commit is contained in:
@ -2,9 +2,10 @@ import { Module } from '@nestjs/common'
|
||||
import { PrismaService } from './prisma/prisma.service'
|
||||
import { PrismaModule } from './prisma/prisma.module'
|
||||
import { UsersModule } from './users/users.module'
|
||||
import { AuthModule } from './auth/auth.module'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, UsersModule],
|
||||
imports: [PrismaModule, UsersModule, AuthModule],
|
||||
providers: [PrismaService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
20
server/src/auth/auth.controller.spec.ts
Normal file
20
server/src/auth/auth.controller.spec.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing'
|
||||
import { AuthController } from './auth.controller'
|
||||
import { AuthService } from './auth.service'
|
||||
|
||||
describe('AuthController', () => {
|
||||
let controller: AuthController
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService],
|
||||
}).compile()
|
||||
|
||||
controller = module.get<AuthController>(AuthController)
|
||||
})
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined()
|
||||
})
|
||||
})
|
17
server/src/auth/auth.controller.ts
Normal file
17
server/src/auth/auth.controller.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Body, Controller, Post } from '@nestjs/common'
|
||||
import { AuthService } from './auth.service'
|
||||
import { ApiOkResponse, ApiTags } from '@nestjs/swagger'
|
||||
import { AuthEntity } from './entity/auth.entity'
|
||||
import { LoginDto } from './dto/login.dto'
|
||||
|
||||
@Controller('auth')
|
||||
@ApiTags('auth')
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
@Post('login')
|
||||
@ApiOkResponse({ type: AuthEntity })
|
||||
login(@Body() { name, password }: LoginDto) {
|
||||
return this.authService.login(name, password)
|
||||
}
|
||||
}
|
26
server/src/auth/auth.module.ts
Normal file
26
server/src/auth/auth.module.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { AuthService } from './auth.service'
|
||||
import { AuthController } from './auth.controller'
|
||||
import { PrismaModule } from 'src/prisma/prisma.module'
|
||||
import { PassportModule } from '@nestjs/passport'
|
||||
import { JwtModule } from '@nestjs/jwt'
|
||||
import { env } from 'process'
|
||||
import { UsersModule } from 'src/users/users.module'
|
||||
import { JwtStrategy } from './jwt.strategy'
|
||||
|
||||
export const JWT_SECRET = env.JWT_SECRET
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
PrismaModule,
|
||||
PassportModule,
|
||||
JwtModule.register({
|
||||
secret: JWT_SECRET,
|
||||
signOptions: { expiresIn: '5m' },
|
||||
}),
|
||||
UsersModule,
|
||||
],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService, JwtStrategy],
|
||||
})
|
||||
export class AuthModule {}
|
18
server/src/auth/auth.service.spec.ts
Normal file
18
server/src/auth/auth.service.spec.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing'
|
||||
import { AuthService } from './auth.service'
|
||||
|
||||
describe('AuthService', () => {
|
||||
let service: AuthService
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [AuthService],
|
||||
}).compile()
|
||||
|
||||
service = module.get<AuthService>(AuthService)
|
||||
})
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined()
|
||||
})
|
||||
})
|
27
server/src/auth/auth.service.ts
Normal file
27
server/src/auth/auth.service.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common'
|
||||
import { PrismaService } from './../prisma/prisma.service'
|
||||
import { JwtService } from '@nestjs/jwt'
|
||||
import { AuthEntity } from './entity/auth.entity'
|
||||
import * as bcrypt from 'bcrypt'
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(private prisma: PrismaService, private jwtService: JwtService) {}
|
||||
|
||||
async login(name: string, password: string): Promise<AuthEntity> {
|
||||
const user = await this.prisma.user.findUnique({ where: { name: name } })
|
||||
if (!user) {
|
||||
throw new NotFoundException(`Aucun⋅e utilisateur⋅rice avec pour nom ${name}`)
|
||||
}
|
||||
|
||||
const isPasswordValid = await bcrypt.compare(password, user.password)
|
||||
|
||||
if (!isPasswordValid) {
|
||||
throw new UnauthorizedException('Mot de passe incorrect')
|
||||
}
|
||||
|
||||
return {
|
||||
accessToken: this.jwtService.sign({ userId: user.id }),
|
||||
}
|
||||
}
|
||||
}
|
14
server/src/auth/dto/login.dto.ts
Normal file
14
server/src/auth/dto/login.dto.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { ApiProperty } from '@nestjs/swagger'
|
||||
import { IsNotEmpty, IsString, MinLength } from 'class-validator'
|
||||
|
||||
export class LoginDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty()
|
||||
name: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty()
|
||||
password: string
|
||||
}
|
6
server/src/auth/entity/auth.entity.ts
Normal file
6
server/src/auth/entity/auth.entity.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger'
|
||||
|
||||
export class AuthEntity {
|
||||
@ApiProperty()
|
||||
accessToken: string
|
||||
}
|
5
server/src/auth/jwt-auth.guard.ts
Normal file
5
server/src/auth/jwt-auth.guard.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { AuthGuard } from '@nestjs/passport'
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthGuard extends AuthGuard('jwt') {}
|
23
server/src/auth/jwt.strategy.ts
Normal file
23
server/src/auth/jwt.strategy.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common'
|
||||
import { PassportStrategy } from '@nestjs/passport'
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt'
|
||||
import { JWT_SECRET } from './auth.module'
|
||||
import { UsersService } from 'src/users/users.service'
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
constructor(private usersService: UsersService) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
secretOrKey: JWT_SECRET,
|
||||
})
|
||||
}
|
||||
|
||||
async validate(payload: { userId: number }) {
|
||||
const user = await this.usersService.findOne(payload.userId)
|
||||
if (!user) {
|
||||
throw new UnauthorizedException()
|
||||
}
|
||||
return user
|
||||
}
|
||||
}
|
@ -1,19 +1,23 @@
|
||||
import { NestFactory } from '@nestjs/core'
|
||||
import { NestFactory, Reflector } from '@nestjs/core'
|
||||
import { AppModule } from './app.module'
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
|
||||
import { ValidationPipe } from '@nestjs/common'
|
||||
import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common'
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule)
|
||||
|
||||
app.useGlobalPipes(new ValidationPipe())
|
||||
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)))
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Traintrape-moi')
|
||||
.setDescription('API permettant de stocker les données de Traintrape-moi')
|
||||
.setVersion('1.0')
|
||||
.addBearerAuth()
|
||||
.build()
|
||||
const document = SwaggerModule.createDocument(app, config)
|
||||
|
||||
SwaggerModule.setup('', app, document);
|
||||
SwaggerModule.setup('', app, document)
|
||||
await app.listen(process.env.PORT ?? 3000)
|
||||
}
|
||||
bootstrap()
|
||||
|
@ -1,13 +1,21 @@
|
||||
import { ApiProperty } from "@nestjs/swagger"
|
||||
import { User } from "@prisma/client"
|
||||
import { Exclude } from 'class-transformer'
|
||||
|
||||
export class UserEntity implements User {
|
||||
constructor(partial: Partial<UserEntity>) {
|
||||
Object.assign(this, partial)
|
||||
}
|
||||
|
||||
@ApiProperty({description: "Identifiant unique"})
|
||||
id: number
|
||||
|
||||
@ApiProperty({description: "Nom de læ joueur⋅se"})
|
||||
name: string
|
||||
|
||||
@Exclude()
|
||||
password: string
|
||||
|
||||
@ApiProperty({description: "Nombre de jetons dont dispose actuellement læ joueur⋅se"})
|
||||
money: number
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
import { Test, TestingModule } from '@nestjs/testing'
|
||||
import { UsersController } from './users.controller'
|
||||
import { UsersService } from './users.service'
|
||||
|
||||
describe('UsersController', () => {
|
||||
let controller: UsersController;
|
||||
let controller: UsersController
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
}).compile()
|
||||
|
||||
controller = module.get<UsersController>(UsersController);
|
||||
});
|
||||
controller = module.get<UsersController>(UsersController)
|
||||
})
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
expect(controller).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
@ -1,25 +1,31 @@
|
||||
import { Controller, Get, NotFoundException, Param, ParseIntPipe } from '@nestjs/common'
|
||||
import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } from '@nestjs/common'
|
||||
import { UsersService } from './users.service'
|
||||
import { ApiNotFoundResponse, ApiOkResponse } from '@nestjs/swagger'
|
||||
import { ApiBearerAuth, ApiNotFoundResponse, ApiOkResponse } from '@nestjs/swagger'
|
||||
import { UserEntity } from './entities/user.entity'
|
||||
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard'
|
||||
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
@Get()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiOkResponse({ type: UserEntity, isArray: true })
|
||||
async findAll() {
|
||||
return this.usersService.findAll()
|
||||
const users = await this.usersService.findAll()
|
||||
return users.map(user => new UserEntity(user))
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiOkResponse({ type: UserEntity })
|
||||
@ApiNotFoundResponse({ description: "Utilisateur⋅rice non trouvé⋅e" })
|
||||
async findOne(@Param('id', ParseIntPipe) id: number) {
|
||||
const user = await this.usersService.findOne(id)
|
||||
if (!user)
|
||||
throw new NotFoundException(`L'utilisateur⋅rice avec l'identifiant ${id} n'existe pas`)
|
||||
return user
|
||||
return new UserEntity(user)
|
||||
}
|
||||
}
|
||||
|
@ -7,5 +7,6 @@ import { PrismaModule } from 'src/prisma/prisma.module'
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
imports: [PrismaModule],
|
||||
exports: [UsersService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
|
Reference in New Issue
Block a user