From 9e73116df2430d490a6e9b61785c73f24be489c7 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Tue, 27 Jan 2026 16:06:55 +0100 Subject: [PATCH] =?UTF-8?q?feat(#75):=20Seed=20Super=20Administrateur=20pa?= =?UTF-8?q?r=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajout INSERT super_admin dans BDD.sql - Email: admin@ptits-pas.fr - MDP: 4dm1n1strateur (hashé bcrypt) - Role: super_admin, Statut: actif - changement_mdp_obligatoire: true - Profil API (/auth/me) retourne changement_mdp_obligatoire - Nettoyage fichiers Prisma obsolètes : - admin/*, scripts/initAdmin.ts - routes/auth.ts, routes/theme.routes.ts - controllers/theme.controller.ts - services/theme.service.ts --- backend/src/admin/admin.controller.ts | 18 ---- backend/src/admin/admin.module.ts | 18 ---- backend/src/admin/admin.service.ts | 40 -------- backend/src/controllers/theme.controller.ts | 72 -------------- backend/src/routes/auth.ts | 95 ------------------- backend/src/routes/auth/auth.controller.ts | 1 + .../routes/auth/dto/profile_response.dto.ts | 3 + backend/src/routes/theme.routes.ts | 14 --- backend/src/scripts/initAdmin.ts | 39 -------- backend/src/services/theme.service.ts | 77 --------------- database/BDD.sql | 25 +++++ 11 files changed, 29 insertions(+), 373 deletions(-) delete mode 100644 backend/src/admin/admin.controller.ts delete mode 100644 backend/src/admin/admin.module.ts delete mode 100644 backend/src/admin/admin.service.ts delete mode 100644 backend/src/controllers/theme.controller.ts delete mode 100644 backend/src/routes/auth.ts delete mode 100644 backend/src/routes/theme.routes.ts delete mode 100644 backend/src/scripts/initAdmin.ts delete mode 100644 backend/src/services/theme.service.ts diff --git a/backend/src/admin/admin.controller.ts b/backend/src/admin/admin.controller.ts deleted file mode 100644 index 52a81db..0000000 --- a/backend/src/admin/admin.controller.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Controller, Post, Body, UseGuards, Req } from '@nestjs/common'; -import { AdminService } from './admin.service'; -import { JwtAuthGuard } from '../auth/jwt-auth.guard'; - -@Controller('admin') -export class AdminController { - constructor(private readonly adminService: AdminService) {} - - @Post('change-password') - @UseGuards(JwtAuthGuard) - async changePassword( - @Req() req, - @Body('oldPassword') oldPassword: string, - @Body('newPassword') newPassword: string, - ) { - return this.adminService.changePassword(req.user.id, oldPassword, newPassword); - } -} \ No newline at end of file diff --git a/backend/src/admin/admin.module.ts b/backend/src/admin/admin.module.ts deleted file mode 100644 index 9d47b62..0000000 --- a/backend/src/admin/admin.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Module } from '@nestjs/common'; -import { AdminController } from './admin.controller'; -import { AdminService } from './admin.service'; -import { PrismaModule } from '../prisma/prisma.module'; -import { JwtModule } from '@nestjs/jwt'; - -@Module({ - imports: [ - PrismaModule, - JwtModule.register({ - secret: process.env.JWT_SECRET, - signOptions: { expiresIn: '1d' }, - }), - ], - controllers: [AdminController], - providers: [AdminService], -}) -export class AdminModule {} \ No newline at end of file diff --git a/backend/src/admin/admin.service.ts b/backend/src/admin/admin.service.ts deleted file mode 100644 index c671645..0000000 --- a/backend/src/admin/admin.service.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Injectable, UnauthorizedException } from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; -import { PrismaService } from '../prisma/prisma.service'; -import * as bcrypt from 'bcrypt'; - -@Injectable() -export class AdminService { - constructor( - private prisma: PrismaService, - private jwtService: JwtService, - ) {} - - async changePassword(adminId: string, oldPassword: string, newPassword: string) { - // Récupérer l'administrateur - const admin = await this.prisma.admin.findUnique({ - where: { id: adminId }, - }); - - if (!admin) { - throw new UnauthorizedException('Administrateur non trouvé'); - } - - // Vérifier l'ancien mot de passe - const isPasswordValid = await bcrypt.compare(oldPassword, admin.password); - if (!isPasswordValid) { - throw new UnauthorizedException('Ancien mot de passe incorrect'); - } - - // Hasher le nouveau mot de passe - const hashedPassword = await bcrypt.hash(newPassword, 10); - - // Mettre à jour le mot de passe - await this.prisma.admin.update({ - where: { id: adminId }, - data: { password: hashedPassword }, - }); - - return { message: 'Mot de passe modifié avec succès' }; - } -} \ No newline at end of file diff --git a/backend/src/controllers/theme.controller.ts b/backend/src/controllers/theme.controller.ts deleted file mode 100644 index 30c1108..0000000 --- a/backend/src/controllers/theme.controller.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Request, Response } from 'express'; -import { ThemeService, ThemeData } from '../services/theme.service'; - -export class ThemeController { - // Créer un nouveau thème - static async createTheme(req: Request, res: Response) { - try { - const themeData: ThemeData = req.body; - const theme = await ThemeService.createTheme(themeData); - res.status(201).json(theme); - } catch (error) { - res.status(500).json({ error: 'Erreur lors de la création du thème' }); - } - } - - // Récupérer tous les thèmes - static async getAllThemes(req: Request, res: Response) { - try { - const themes = await ThemeService.getAllThemes(); - res.json(themes); - } catch (error) { - res.status(500).json({ error: 'Erreur lors de la récupération des thèmes' }); - } - } - - // Récupérer le thème actif - static async getActiveTheme(req: Request, res: Response) { - try { - const theme = await ThemeService.getActiveTheme(); - if (!theme) { - return res.status(404).json({ error: 'Aucun thème actif trouvé' }); - } - res.json(theme); - } catch (error) { - res.status(500).json({ error: 'Erreur lors de la récupération du thème actif' }); - } - } - - // Activer un thème - static async activateTheme(req: Request, res: Response) { - try { - const { themeId } = req.params; - const theme = await ThemeService.activateTheme(themeId); - res.json(theme); - } catch (error) { - res.status(500).json({ error: 'Erreur lors de l\'activation du thème' }); - } - } - - // Mettre à jour un thème - static async updateTheme(req: Request, res: Response) { - try { - const { themeId } = req.params; - const themeData: Partial = req.body; - const theme = await ThemeService.updateTheme(themeId, themeData); - res.json(theme); - } catch (error) { - res.status(500).json({ error: 'Erreur lors de la mise à jour du thème' }); - } - } - - // Supprimer un thème - static async deleteTheme(req: Request, res: Response) { - try { - const { themeId } = req.params; - await ThemeService.deleteTheme(themeId); - res.status(204).send(); - } catch (error) { - res.status(500).json({ error: 'Erreur lors de la suppression du thème' }); - } - } -} \ No newline at end of file diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts deleted file mode 100644 index 31cef32..0000000 --- a/backend/src/routes/auth.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Router } from 'express'; -import { PrismaClient } from '@prisma/client'; -import * as bcrypt from 'bcrypt'; -import jwt from 'jsonwebtoken'; - -const router = Router(); -const prisma = new PrismaClient(); -const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; - -// Route de connexion -router.post('/login', async (req, res) => { - try { - const { email, password } = req.body; - - // Vérifier les identifiants - const admin = await prisma.admin.findUnique({ - where: { email } - }); - - if (!admin) { - return res.status(401).json({ error: 'Identifiants invalides' }); - } - - // Vérifier le mot de passe - const validPassword = await bcrypt.compare(password, admin.password); - if (!validPassword) { - return res.status(401).json({ error: 'Identifiants invalides' }); - } - - // Vérifier si le mot de passe doit être changé - if (!admin.passwordChanged) { - return res.status(403).json({ - error: 'Changement de mot de passe requis', - requiresPasswordChange: true - }); - } - - // Générer le token JWT - const token = jwt.sign( - { - id: admin.id, - email: admin.email, - role: 'admin' - }, - JWT_SECRET, - { expiresIn: '24h' } - ); - - res.json({ token }); - } catch (error) { - console.error('Erreur lors de la connexion:', error); - res.status(500).json({ error: 'Erreur serveur' }); - } -}); - -// Route de changement de mot de passe -router.post('/change-password', async (req, res) => { - try { - const { email, currentPassword, newPassword } = req.body; - - // Vérifier l'administrateur - const admin = await prisma.admin.findUnique({ - where: { email } - }); - - if (!admin) { - return res.status(404).json({ error: 'Administrateur non trouvé' }); - } - - // Vérifier l'ancien mot de passe - const validPassword = await bcrypt.compare(currentPassword, admin.password); - if (!validPassword) { - return res.status(401).json({ error: 'Mot de passe actuel incorrect' }); - } - - // Hasher le nouveau mot de passe - const hashedPassword = await bcrypt.hash(newPassword, 10); - - // Mettre à jour le mot de passe - await prisma.admin.update({ - where: { id: admin.id }, - data: { - password: hashedPassword, - passwordChanged: true - } - }); - - res.json({ message: 'Mot de passe changé avec succès' }); - } catch (error) { - console.error('Erreur lors du changement de mot de passe:', error); - res.status(500).json({ error: 'Erreur serveur' }); - } -}); - -export default router; \ No newline at end of file diff --git a/backend/src/routes/auth/auth.controller.ts b/backend/src/routes/auth/auth.controller.ts index a68ea36..ef7cd90 100644 --- a/backend/src/routes/auth/auth.controller.ts +++ b/backend/src/routes/auth/auth.controller.ts @@ -87,6 +87,7 @@ export class AuthController { prenom: user.prenom ?? '', nom: user.nom ?? '', statut: user.statut, + changement_mdp_obligatoire: user.changement_mdp_obligatoire, }; } diff --git a/backend/src/routes/auth/dto/profile_response.dto.ts b/backend/src/routes/auth/dto/profile_response.dto.ts index 4ae9d65..1e2b32d 100644 --- a/backend/src/routes/auth/dto/profile_response.dto.ts +++ b/backend/src/routes/auth/dto/profile_response.dto.ts @@ -19,4 +19,7 @@ export class ProfileResponseDto { @ApiProperty({ enum: StatutUtilisateurType }) statut: StatutUtilisateurType; + + @ApiProperty({ description: 'Indique si le changement de mot de passe est obligatoire à la première connexion' }) + changement_mdp_obligatoire: boolean; } diff --git a/backend/src/routes/theme.routes.ts b/backend/src/routes/theme.routes.ts deleted file mode 100644 index e43e388..0000000 --- a/backend/src/routes/theme.routes.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Router } from 'express'; -import { ThemeController } from '../controllers/theme.controller'; - -const router = Router(); - -// Routes pour les thèmes -router.post('/', ThemeController.createTheme); -router.get('/', ThemeController.getAllThemes); -router.get('/active', ThemeController.getActiveTheme); -router.put('/:themeId/activate', ThemeController.activateTheme); -router.put('/:themeId', ThemeController.updateTheme); -router.delete('/:themeId', ThemeController.deleteTheme); - -export default router; \ No newline at end of file diff --git a/backend/src/scripts/initAdmin.ts b/backend/src/scripts/initAdmin.ts deleted file mode 100644 index dd59a96..0000000 --- a/backend/src/scripts/initAdmin.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import * as bcrypt from 'bcrypt'; - -const prisma = new PrismaClient(); - -async function main() { - try { - // Vérifier si l'administrateur existe déjà - const existingAdmin = await prisma.admin.findUnique({ - where: { email: 'administrateur@ptitspas.fr' } - }); - - if (!existingAdmin) { - // Hasher le mot de passe - const hashedPassword = await bcrypt.hash('password', 10); - - // Créer l'administrateur - await prisma.admin.create({ - data: { - email: 'administrateur@ptitspas.fr', - password: hashedPassword, - firstName: 'Administrateur', - lastName: 'P\'titsPas', - passwordChanged: false - } - }); - - console.log('✅ Administrateur créé avec succès'); - } else { - console.log('ℹ️ L\'administrateur existe déjà'); - } - } catch (error) { - console.error('❌ Erreur lors de la création de l\'administrateur:', error); - } finally { - await prisma.$disconnect(); - } -} - -main(); \ No newline at end of file diff --git a/backend/src/services/theme.service.ts b/backend/src/services/theme.service.ts deleted file mode 100644 index 47a0c79..0000000 --- a/backend/src/services/theme.service.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { PrismaClient } from '@prisma/client'; - -const prisma = new PrismaClient(); - -export interface ThemeData { - name: string; - primaryColor: string; - secondaryColor: string; - backgroundColor: string; - textColor: string; -} - -export class ThemeService { - // Créer un nouveau thème - static async createTheme(data: ThemeData) { - return prisma.theme.create({ - data: { - ...data, - isActive: false, - }, - }); - } - - // Récupérer tous les thèmes - static async getAllThemes() { - return prisma.theme.findMany(); - } - - // Récupérer le thème actif - static async getActiveTheme() { - const settings = await prisma.appSettings.findFirst({ - include: { - currentTheme: true, - }, - }); - return settings?.currentTheme; - } - - // Activer un thème - static async activateTheme(themeId: string) { - // Désactiver tous les thèmes - await prisma.theme.updateMany({ - where: { isActive: true }, - data: { isActive: false }, - }); - - // Activer le thème sélectionné - const updatedTheme = await prisma.theme.update({ - where: { id: themeId }, - data: { isActive: true }, - }); - - // Mettre à jour les paramètres de l'application - await prisma.appSettings.upsert({ - where: { id: '1' }, - update: { currentThemeId: themeId }, - create: { id: '1', currentThemeId: themeId }, - }); - - return updatedTheme; - } - - // Mettre à jour un thème - static async updateTheme(themeId: string, data: Partial) { - return prisma.theme.update({ - where: { id: themeId }, - data, - }); - } - - // Supprimer un thème - static async deleteTheme(themeId: string) { - return prisma.theme.delete({ - where: { id: themeId }, - }); - } -} \ No newline at end of file diff --git a/database/BDD.sql b/database/BDD.sql index c5e7c5a..1e7bc0b 100644 --- a/database/BDD.sql +++ b/database/BDD.sql @@ -342,3 +342,28 @@ ALTER TABLE utilisateurs INSERT INTO documents_legaux (type, version, fichier_nom, fichier_path, fichier_hash, actif, televerse_le, active_le) VALUES ('cgu', 1, 'cgu_v1_default.pdf', '/documents/legaux/cgu_v1_default.pdf', 'a3f8b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2', true, now(), now()), ('privacy', 1, 'privacy_v1_default.pdf', '/documents/legaux/privacy_v1_default.pdf', 'b4f9c3d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4', true, now(), now()); + +-- ========================================================== +-- Seed : Super Administrateur par défaut +-- ========================================================== +-- Email: admin@ptits-pas.fr +-- Mot de passe: 4dm1n1strateur (hashé bcrypt) +-- IMPORTANT: Changer ce mot de passe en production ! +-- ========================================================== +INSERT INTO utilisateurs ( + email, + password, + prenom, + nom, + role, + statut, + changement_mdp_obligatoire +) VALUES ( + 'admin@ptits-pas.fr', + '$2b$12$plOZCW7lzLFkWgDPcE6p6u10EA4yErQt6Xcp5nyH3Sp/2.6EpNW.6', + 'Super', + 'Administrateur', + 'super_admin', + 'actif', + true +);