203 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { RoleType, StatutUtilisateurType, Users } from "src/entities/users.entity";
import { Repository } from "typeorm";
import { CreateUserDto } from "./dto/create_user.dto";
import { UpdateUserDto } from "./dto/update_user.dto";
import * as bcrypt from 'bcrypt';
import { StatutValidationType, Validation } from "src/entities/validations.entity";
@Injectable()
export class UserService {
constructor(
@InjectRepository(Users)
private readonly usersRepository: Repository<Users>,
@InjectRepository(Validation)
private readonly validationRepository: Repository<Validation>
) { }
// Création utilisateur
async createUser(dto: CreateUserDto, currentUser?: Users): Promise<Users> {
// Vérification CGU
if (!dto.cguAccepted) {
throw new BadRequestException(
'Vous devez accepter les CGU et la Politique de confidentialité pour créer un compte.',
);
}
// Déterminer si le créateur est super admin
const isSuperAdmin = currentUser?.role === RoleType.SUPER_ADMIN;
let role: RoleType;
if (isSuperAdmin) {
// Un super admin peut créer n'importe quel rôle
role = dto.role;
} else if (dto.role === RoleType.ASSISTANTE_MATERNELLE) {
// Autoriser l'auto-inscription des AM
role = RoleType.ASSISTANTE_MATERNELLE;
} else {
// Tous les autres cas → forcer en PARENT
role = RoleType.PARENT;
}
// Statut par défaut : EN_ATTENTE (sauf si super admin qui peut forcer)
const statut = isSuperAdmin
? dto.statut ?? StatutUtilisateurType.EN_ATTENTE
: StatutUtilisateurType.EN_ATTENTE;
// Vérification spécifique pour assistantes maternelles
if (role === RoleType.ASSISTANTE_MATERNELLE && !dto.photo_url) {
throw new BadRequestException(
'La photo de profil est obligatoire pour les assistantes maternelles.',
);
}
// Vérification des champs obligatoires
if (!dto.nom?.trim()) {
throw new BadRequestException('Nom est obligatoire.');
} else if (!dto.prenom?.trim()) {
throw new BadRequestException('Prénom est obligatoire.');
} else if (!dto.adresse?.trim()) {
throw new BadRequestException('Adresse est obligatoire.');
} else if (!dto.telephone?.trim()) {
throw new BadRequestException('Téléphone est obligatoire.');
}
// Gestion consentement photo
let consentDate: Date | undefined;
if (dto.consentement_photo && dto.date_consentement_photo) {
const parsed = new Date(dto.date_consentement_photo);
if (!isNaN(parsed.getTime())) {
consentDate = parsed;
}
}
// Hash mot de passe
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(dto.password, salt);
// Création de lentité
const entity = this.usersRepository.create({
email: dto.email,
password: hashedPassword,
prenom: dto.prenom,
nom: dto.nom,
role,
statut,
genre: dto.genre,
telephone: dto.telephone,
ville: dto.ville,
code_postal: dto.code_postal,
adresse: dto.adresse,
photo_url: dto.photo_url,
consentement_photo: dto.consentement_photo ?? false,
date_consentement_photo: consentDate,
changement_mdp_obligatoire: dto.changement_mdp_obligatoire ?? false,
});
const saved = await this.usersRepository.save(entity);
return this.findOne(saved.id);
}
async findAll(): Promise<Users[]> {
return this.usersRepository.find();
}
async findOneBy(where: Partial<Users>): Promise<Users | null> {
return this.usersRepository.findOne({ where });
}
async findOne(id: string): Promise<Users> {
const user = await this.usersRepository.findOne({ where: { id } });
if (!user) {
throw new NotFoundException('Utilisateur introuvable');
}
return user;
}
async findByEmailOrNull(email: string): Promise<Users | null> {
return this.usersRepository.findOne({ where: { email } });
}
async updateUser(id: string, dto: UpdateUserDto, currentUser: Users): Promise<Users> {
const user = await this.findOne(id);
// Interdire changement de rôle si pas super admin
if (dto.role && currentUser.role !== RoleType.SUPER_ADMIN) {
throw new ForbiddenException('Accès réservé aux super admins');
}
// Gestion du mot de passe
if (dto.password) {
const salt = await bcrypt.genSalt();
user.password = await bcrypt.hash(dto.password, salt);
delete (dto as any).password;
}
// Conversion de la date de consentement
if (dto.date_consentement_photo !== undefined) {
user.date_consentement_photo = dto.date_consentement_photo
? new Date(dto.date_consentement_photo)
: undefined;
delete (dto as any).date_consentement_photo;
}
Object.assign(user, dto);
return this.usersRepository.save(user);
}
async validateUser(user_id: string, currentUser: Users, comment?: string): Promise<Users> {
if (![RoleType.SUPER_ADMIN, RoleType.ADMINISTRATEUR, RoleType.GESTIONNAIRE].includes(currentUser.role)) {
throw new ForbiddenException('Accès réservé aux super admins, administrateurs et gestionnaires');
}
const user = await this.usersRepository.findOne({ where: { id: user_id } });
if (!user) throw new NotFoundException('Utilisateur introuvable');
user.statut = StatutUtilisateurType.ACTIF;
const savedUser = await this.usersRepository.save(user);
const validation = this.validationRepository.create({
user: savedUser,
type: 'validation_compte',
status: StatutValidationType.VALIDE,
validated_by: currentUser,
comment
});
await this.validationRepository.save(validation);
return savedUser;
}
async suspendUser(user_id: string, currentUser: Users, comment?: string): Promise<Users> {
if (![RoleType.SUPER_ADMIN, RoleType.ADMINISTRATEUR, RoleType.GESTIONNAIRE].includes(currentUser.role)) {
throw new ForbiddenException('Accès réservé aux super admins, administrateurs et gestionnaires');
}
const user = await this.usersRepository.findOne({ where: { id: user_id } });
if (!user) throw new NotFoundException('Utilisateur introuvable');
user.statut = StatutUtilisateurType.SUSPENDU;
const savedUser = await this.usersRepository.save(user);
const suspend = this.validationRepository.create({
user: savedUser,
type: 'suspension_compte',
status: StatutValidationType.VALIDE,
validated_by: currentUser,
comment
})
await this.validationRepository.save(suspend);
return savedUser;
}
async remove(id: string, currentUser: Users): Promise<void> {
if (currentUser.role !== RoleType.SUPER_ADMIN) {
throw new ForbiddenException('Accès réservé aux super admins');
}
const result = await this.usersRepository.delete(id);
if (result.affected === 0) {
throw new NotFoundException('Utilisateur introuvable');
}
}
}