diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index 9e7f209..7069fb0 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -3,6 +3,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { UsersModule } from './users/users.module'; @Module({ imports: [ @@ -31,6 +32,8 @@ import { AppService } from './app.service'; synchronize: true, }), }), + + UsersModule, ], controllers: [AppController], providers: [AppService], diff --git a/packages/backend/src/main.ts b/packages/backend/src/main.ts index f76bc8d..f951358 100644 --- a/packages/backend/src/main.ts +++ b/packages/backend/src/main.ts @@ -1,8 +1,16 @@ import { NestFactory } from '@nestjs/core'; +import { ValidationPipe } from '@nestjs/common'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + forbidNonWhitelisted: true, + transform: true, + }), + ); await app.listen(process.env.PORT ?? 3000); } bootstrap(); diff --git a/packages/backend/src/users/dto/create-user.dto.ts b/packages/backend/src/users/dto/create-user.dto.ts new file mode 100644 index 0000000..99d3a64 --- /dev/null +++ b/packages/backend/src/users/dto/create-user.dto.ts @@ -0,0 +1,16 @@ +import { IsEmail, IsNotEmpty, MinLength } from 'class-validator'; + +export class CreateUserDto { + @IsEmail({}, { message: "L'email doit être valide" }) + email!: string; + + @IsNotEmpty() + @MinLength(8, { message: 'Le mot de passe doit faire au moins 8 caractères' }) + motDePasse!: string; + + @IsNotEmpty() + nom!: string; + + @IsNotEmpty() + prenom!: string; +} diff --git a/packages/backend/src/users/dto/update-user.dto.ts b/packages/backend/src/users/dto/update-user.dto.ts new file mode 100644 index 0000000..dfd37fb --- /dev/null +++ b/packages/backend/src/users/dto/update-user.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateUserDto } from './create-user.dto'; + +export class UpdateUserDto extends PartialType(CreateUserDto) {} diff --git a/packages/backend/src/users/entities/user.entity.ts b/packages/backend/src/users/entities/user.entity.ts new file mode 100644 index 0000000..f7e9ceb --- /dev/null +++ b/packages/backend/src/users/entities/user.entity.ts @@ -0,0 +1,27 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, +} from 'typeorm'; + +@Entity('utilisateurs') +export class User { + @PrimaryGeneratedColumn('uuid') + id!: string; + + @Column({ unique: true }) + email!: string; + + @Column() + motDePasse!: string; + + @Column() + nom!: string; + + @Column() + prenom!: string; + + @CreateDateColumn() + dateCreation!: Date; +} diff --git a/packages/backend/src/users/users.controller.spec.ts b/packages/backend/src/users/users.controller.spec.ts new file mode 100644 index 0000000..a76d310 --- /dev/null +++ b/packages/backend/src/users/users.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UsersController } from './users.controller'; +import { UsersService } from './users.service'; + +describe('UsersController', () => { + let controller: UsersController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [UsersController], + providers: [UsersService], + }).compile(); + + controller = module.get(UsersController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/packages/backend/src/users/users.controller.ts b/packages/backend/src/users/users.controller.ts new file mode 100644 index 0000000..b21a836 --- /dev/null +++ b/packages/backend/src/users/users.controller.ts @@ -0,0 +1,42 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, +} from '@nestjs/common'; +import { UsersService } from './users.service'; +import { CreateUserDto } from './dto/create-user.dto'; +import { UpdateUserDto } from './dto/update-user.dto'; + +@Controller('users') +export class UsersController { + constructor(private readonly usersService: UsersService) {} + + @Post() + create(@Body() createUserDto: CreateUserDto) { + return this.usersService.create(createUserDto); + } + + @Get() + findAll() { + return this.usersService.findAll(); + } + + @Get(':id') + findOne(@Param('id') id: string) { + return this.usersService.findOne(id); + } + + @Patch(':id') + update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { + return this.usersService.update(id, updateUserDto); + } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.usersService.remove(id); + } +} diff --git a/packages/backend/src/users/users.module.ts b/packages/backend/src/users/users.module.ts new file mode 100644 index 0000000..1c38291 --- /dev/null +++ b/packages/backend/src/users/users.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UsersService } from './users.service'; +import { UsersController } from './users.controller'; +import { User } from './entities/user.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([User])], + controllers: [UsersController], + providers: [UsersService], +}) +export class UsersModule {} diff --git a/packages/backend/src/users/users.service.spec.ts b/packages/backend/src/users/users.service.spec.ts new file mode 100644 index 0000000..62815ba --- /dev/null +++ b/packages/backend/src/users/users.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UsersService } from './users.service'; + +describe('UsersService', () => { + let service: UsersService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [UsersService], + }).compile(); + + service = module.get(UsersService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/packages/backend/src/users/users.service.ts b/packages/backend/src/users/users.service.ts new file mode 100644 index 0000000..91bae74 --- /dev/null +++ b/packages/backend/src/users/users.service.ts @@ -0,0 +1,70 @@ +import { + Injectable, + ConflictException, + NotFoundException, +} from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import * as bcrypt from 'bcrypt'; +import { CreateUserDto } from './dto/create-user.dto'; +import { User } from './entities/user.entity'; +import { UpdateUserDto } from './dto/update-user.dto'; + +@Injectable() +export class UsersService { + constructor( + @InjectRepository(User) + private readonly userRepository: Repository, + ) {} + + async create(createUserDto: CreateUserDto): Promise { + const utilisateurExistant = await this.userRepository.findOne({ + where: { email: createUserDto.email }, + }); + + if (utilisateurExistant) { + throw new ConflictException('Cet email est déjà utilisé.'); + } + + const motDePasseHache = await bcrypt.hash(createUserDto.motDePasse, 10); + + const nouvelUtilisateur = this.userRepository.create({ + ...createUserDto, + motDePasse: motDePasseHache, + }); + + return this.userRepository.save(nouvelUtilisateur); + } + + findAll(): Promise { + return this.userRepository.find(); + } + + async findOne(id: string): Promise { + const user = await this.userRepository.findOne({ where: { id } }); + if (!user) { + throw new NotFoundException(`Utilisateur avec l'ID ${id} introuvable.`); + } + return user; + } + + async update(id: string, updateUserDto: UpdateUserDto): Promise { + const user = await this.findOne(id); + + if (updateUserDto.motDePasse) { + updateUserDto.motDePasse = await bcrypt.hash( + updateUserDto.motDePasse, + 10, + ); + } + + Object.assign(user, updateUserDto); + + return this.userRepository.save(user); + } + + async remove(id: string): Promise { + const user = await this.findOne(id); + await this.userRepository.remove(user); + } +}