docs: add comprehensive project documentation files

Added detailed documentation files, including project overview, current status, specifications, implementation guide, and README structure. Organized content to improve navigation and streamline project understanding.
This commit is contained in:
Mathis H (Avnyr) 2025-05-15 17:08:53 +02:00
parent 6d6ecdaec1
commit f6f0888bd7
11 changed files with 3064 additions and 0 deletions

View File

@ -0,0 +1,148 @@
# Business Flow Diagrams
This document contains diagrams illustrating the main business flows of the "Application de Création de Groupes" project.
## Authentication Flow
```mermaid
sequenceDiagram
participant User as Utilisateur
participant Frontend as Frontend (Next.js)
participant Backend as Backend (NestJS)
participant GitHub as GitHub OAuth
User->>Frontend: Clic sur "Se connecter avec GitHub"
Frontend->>Backend: Redirection vers /auth/github
Backend->>GitHub: Redirection vers GitHub OAuth
GitHub->>User: Demande d'autorisation
User->>GitHub: Accepte l'autorisation
GitHub->>Backend: Redirection avec code d'autorisation
Backend->>GitHub: Échange code contre token d'accès
GitHub->>Backend: Retourne token d'accès
Backend->>GitHub: Requête informations utilisateur
GitHub->>Backend: Retourne informations utilisateur
Backend->>Backend: Crée/Met à jour l'utilisateur en BDD
Backend->>Backend: Génère JWT (access + refresh tokens)
Backend->>Frontend: Redirection avec tokens JWT
Frontend->>Frontend: Stocke les tokens
Frontend->>User: Affiche interface authentifiée
```
## Project Creation Flow
```mermaid
sequenceDiagram
participant User as Utilisateur
participant Frontend as Frontend (Next.js)
participant Backend as Backend (NestJS)
participant DB as Base de données
User->>Frontend: Crée un nouveau projet
Frontend->>Backend: POST /api/projects
Backend->>Backend: Valide les données
Backend->>DB: Insère le projet
DB->>Backend: Confirme l'insertion
Backend->>Frontend: Retourne le projet créé
Frontend->>User: Affiche le projet créé
```
## Person Management Flow
```mermaid
sequenceDiagram
participant User as Utilisateur
participant Frontend as Frontend (Next.js)
participant Backend as Backend (NestJS)
participant DB as Base de données
User->>Frontend: Ajoute une personne au projet
Frontend->>Backend: POST /api/projects/{id}/persons
Backend->>Backend: Valide les données
Backend->>DB: Insère la personne
DB->>Backend: Confirme l'insertion
Backend->>Frontend: Retourne la personne créée
Frontend->>User: Affiche la personne dans le projet
User->>Frontend: Modifie les attributs d'une personne
Frontend->>Backend: PATCH /api/persons/{id}
Backend->>Backend: Valide les données
Backend->>DB: Met à jour la personne
DB->>Backend: Confirme la mise à jour
Backend->>Frontend: Retourne la personne mise à jour
Frontend->>User: Affiche la personne mise à jour
```
## Group Creation Flow
```mermaid
sequenceDiagram
participant User as Utilisateur
participant Frontend as Frontend (Next.js)
participant Backend as Backend (NestJS)
participant DB as Base de données
alt Création manuelle
User->>Frontend: Crée un groupe manuellement
Frontend->>Backend: POST /api/projects/{id}/groups
Backend->>Backend: Valide les données
Backend->>DB: Insère le groupe
DB->>Backend: Confirme l'insertion
Backend->>Frontend: Retourne le groupe créé
Frontend->>User: Affiche le groupe créé
User->>Frontend: Ajoute des personnes au groupe (drag & drop)
Frontend->>Backend: PATCH /api/groups/{id}/persons
Backend->>Backend: Valide les données
Backend->>DB: Met à jour les relations groupe-personnes
DB->>Backend: Confirme la mise à jour
Backend->>Frontend: Retourne le groupe mis à jour
Frontend->>User: Affiche le groupe mis à jour
else Création automatique
User->>Frontend: Demande la création automatique de groupes
Frontend->>Backend: POST /api/projects/{id}/auto-groups
Backend->>Backend: Exécute l'algorithme de création de groupes
Backend->>DB: Insère les groupes générés
DB->>Backend: Confirme l'insertion
Backend->>Frontend: Retourne les groupes créés
Frontend->>User: Affiche les groupes créés
end
```
## Real-time Collaboration Flow
```mermaid
sequenceDiagram
participant User1 as Utilisateur 1
participant User2 as Utilisateur 2
participant Frontend1 as Frontend (User 1)
participant Frontend2 as Frontend (User 2)
participant Backend as Backend (NestJS)
participant WebSocket as WebSocket Server
participant DB as Base de données
User1->>Frontend1: Modifie un projet
Frontend1->>Backend: PATCH /api/projects/{id}
Backend->>DB: Met à jour le projet
DB->>Backend: Confirme la mise à jour
Backend->>WebSocket: Émet événement "project:updated"
WebSocket->>Frontend2: Transmet événement "project:updated"
Frontend2->>User2: Affiche les modifications en temps réel
```
## Export Data Flow (GDPR)
```mermaid
sequenceDiagram
participant User as Utilisateur
participant Frontend as Frontend (Next.js)
participant Backend as Backend (NestJS)
participant DB as Base de données
User->>Frontend: Demande l'export de ses données
Frontend->>Backend: GET /api/users/me/export
Backend->>DB: Récupère toutes les données de l'utilisateur
DB->>Backend: Retourne les données
Backend->>Backend: Formate les données en JSON
Backend->>Frontend: Retourne le fichier JSON
Frontend->>User: Télécharge le fichier de données
```

View File

@ -0,0 +1,104 @@
# Guide d'Implémentation
Ce document sert de guide complet pour l'implémentation de l'application "Application de Création de Groupes". Il regroupe les différents plans d'implémentation et fournit une feuille de route claire pour le développement.
## Vue d'Ensemble
L'application est construite avec une architecture moderne, utilisant NestJS pour le backend et Next.js pour le frontend. Elle permet aux utilisateurs de créer et gérer des groupes de personnes selon différents critères, avec des fonctionnalités de collaboration en temps réel.
## Plans d'Implémentation Détaillés
Pour faciliter le développement, nous avons divisé l'implémentation en plusieurs plans détaillés, chacun se concentrant sur un aspect spécifique de l'application :
1. [Plan d'Implémentation du Backend](implementation/BACKEND_IMPLEMENTATION_PLAN.md) - Plan détaillé pour l'implémentation du backend avec NestJS
2. [Plan du Schéma de Base de Données](implementation/DATABASE_SCHEMA_PLAN.md) - Définition du schéma de base de données avec DrizzleORM
3. [Plan d'Implémentation de l'Authentification](implementation/AUTH_IMPLEMENTATION_PLAN.md) - Détails sur l'implémentation de l'authentification OAuth avec GitHub
4. [Plan d'Implémentation des WebSockets](implementation/WEBSOCKET_IMPLEMENTATION_PLAN.md) - Plan pour la communication en temps réel
## Ordre d'Implémentation Recommandé
Pour une implémentation efficace, nous recommandons de suivre l'ordre suivant :
### Phase 1 : Configuration et Base de Données
1. Configurer l'environnement de développement
2. Mettre en place la structure de base du projet NestJS
3. Implémenter le schéma de base de données avec DrizzleORM
4. Configurer le système de migrations
### Phase 2 : Authentification et Utilisateurs
1. Implémenter l'authentification OAuth avec GitHub
2. Mettre en place la gestion des JWT
3. Développer le module utilisateurs
4. Configurer les guards et décorateurs pour la protection des routes
### Phase 3 : Modules Principaux
1. Implémenter le module projets
2. Implémenter le module personnes
3. Implémenter le module groupes
4. Implémenter le module tags
5. Établir les relations entre les modules
### Phase 4 : Communication en Temps Réel
1. Configurer Socket.IO avec NestJS
2. Implémenter les gateways WebSocket
3. Mettre en place la gestion des salles et des événements
4. Intégrer les WebSockets avec les services existants
### Phase 5 : Frontend
1. Configurer la structure de base du projet Next.js
2. Implémenter les pages d'authentification
3. Développer les pages principales (projets, personnes, groupes)
4. Intégrer les fonctionnalités de collaboration en temps réel
### Phase 6 : Tests et Documentation
1. Écrire des tests unitaires et e2e
2. Documenter l'API avec Swagger
3. Finaliser la documentation utilisateur
## Bonnes Pratiques de Développement
### Architecture et Structure
- Suivre le principe de responsabilité unique (SRP)
- Utiliser l'injection de dépendances pour faciliter les tests
- Séparer clairement les couches (contrôleurs, services, repositories)
- Utiliser des DTOs pour la validation des entrées
### Sécurité
- Valider toutes les entrées utilisateur
- Utiliser des tokens JWT avec une durée de vie limitée
- Mettre en place des protections contre les attaques courantes (CSRF, XSS, injections SQL)
- Respecter les principes de la RGPD
### Performance
- Optimiser les requêtes de base de données
- Utiliser le caching lorsque c'est approprié
- Implémenter la pagination pour les listes volumineuses
- Optimiser les assets frontend (lazy loading, code splitting)
### Collaboration
- Suivre les conventions de nommage
- Documenter le code
- Utiliser des messages de commit descriptifs
- Effectuer des revues de code régulières
## Outils et Ressources
### Outils de Développement
- **IDE** : Visual Studio Code avec les extensions appropriées
- **Gestion de Packages** : PNPM pour la gestion des dépendances
- **Base de Données** : PostgreSQL avec DrizzleORM
- **API Testing** : Postman ou Insomnia
- **Versioning** : Git avec GitHub
### Ressources Utiles
- [Documentation NestJS](https://docs.nestjs.com/)
- [Documentation Next.js](https://nextjs.org/docs)
- [Documentation DrizzleORM](https://orm.drizzle.team/docs/overview)
- [Documentation Socket.IO](https://socket.io/docs/v4/)
- [Documentation OAuth 2.0](https://oauth.net/2/)
## Conclusion
Ce guide d'implémentation fournit une feuille de route complète pour le développement de l'application. En suivant les plans détaillés et les bonnes pratiques recommandées, vous pourrez construire une application robuste, sécurisée et performante.
Pour plus de détails sur l'état actuel du projet et les tâches restantes, consultez le document [État d'Avancement du Projet](PROJECT_STATUS.md).

127
docs/PROJECT_OVERVIEW.md Normal file
View File

@ -0,0 +1,127 @@
# Comprehensive Project Overview
## Introduction
This document provides a comprehensive analysis of the "Application de Création de Groupes" project, examining its architecture, technologies, features, and implementation details.
## Project Purpose
The application is designed to facilitate the creation and management of groups of people based on various criteria. It allows users to create projects, add people with different attributes, and organize them into groups either manually or automatically using balancing algorithms.
## Architecture Overview
The project follows a modern full-stack architecture with clear separation between frontend and backend:
### Frontend (Next.js)
- Uses Next.js with App Router pattern
- Implements ShadcnUI for consistent UI components
- Utilizes SWR for data fetching with caching
- Implements real-time updates using Socket.IO client
- Follows a component-based architecture with custom hooks
### Backend (NestJS)
- Built with NestJS framework for scalable server-side applications
- Uses PostgreSQL with DrizzleORM for database operations
- Implements OAuth 2.0 with GitHub for authentication
- Uses JWT for session management
- Provides WebSocket support via Socket.IO for real-time collaboration
- Follows modular architecture with clear separation of concerns
### Database
- PostgreSQL with DrizzleORM
- Well-defined schema with proper relationships
- Optimized data types and indexing strategy
- Support for migrations
### Communication
- REST API for CRUD operations
- WebSockets for real-time updates and collaboration
- JWT-based authentication for securing both REST and WebSocket endpoints
## Key Features
1. **User Authentication**
- OAuth 2.0 with GitHub
- JWT-based session management
- Role-based access control
2. **Project Management**
- Create, read, update, delete projects
- Associate tags with projects
- Track project history
3. **Person Management**
- Add people with various attributes (technical level, gender, language skills, etc.)
- Tag people for easier categorization
- Track person attributes
4. **Group Creation**
- Manual creation via drag-and-drop interface
- Automatic creation using balancing algorithms
- Real-time collaboration between users
5. **Real-time Collaboration**
- See changes made by other users in real-time
- Notifications for important events
- Room-based communication for project-specific updates
## Implementation Details
### Authentication Flow
The authentication flow uses OAuth 2.0 with GitHub as the identity provider:
1. User clicks "Login with GitHub"
2. User is redirected to GitHub for authorization
3. GitHub redirects back to the application with an authorization code
4. Backend exchanges the code for an access token
5. Backend retrieves user information from GitHub
6. Backend creates or updates the user in the database
7. Backend generates JWT tokens (access and refresh)
8. Frontend stores the tokens and uses them for subsequent requests
### Database Schema
The database schema includes the following main entities:
1. Users - Storing user information
2. Projects - Storing project information
3. Persons - Storing information about people to be placed in groups
4. Groups - Storing information about created groups
5. Tags - For categorizing persons and projects
6. Relation tables - For many-to-many relationships
### WebSocket Implementation
The WebSocket implementation uses Socket.IO for real-time communication:
1. Authentication using JWT
2. Room-based communication for project-specific updates
3. Event-based messaging for different types of updates
4. Proper connection and disconnection handling
### Deployment
The application is containerized using Docker, with separate containers for:
1. Frontend (Next.js)
2. Backend (NestJS)
3. PostgreSQL database
## Development Workflow
The project uses:
- PNPM for package management
- ESLint and Prettier for code quality
- TypeScript for type safety
- Jest for testing
- Docker for containerization
- Drizzle for database migrations
## Security Considerations
The application implements several security measures:
1. OAuth 2.0 for secure authentication
2. JWT with short-lived access tokens and refresh tokens
3. CORS configuration to prevent unauthorized access
4. Input validation using class-validator
5. Protection against common attacks (CSRF, XSS, SQL injection)
6. GDPR compliance features
## Performance Optimization
The application is optimized for performance:
1. Efficient database schema with proper indexing
2. Optimized data types for reduced storage requirements
3. Caching strategies for frequently accessed data
4. Lazy loading of components and data
## Conclusion
The "Application de Création de Groupes" is a well-designed, modern web application that follows best practices in software development. It provides a comprehensive solution for creating and managing groups of people, with a focus on user experience, security, and performance.
The clear separation between frontend and backend, the use of modern technologies, and the implementation of real-time collaboration features make it a robust and scalable application that can be extended with additional features in the future.

241
docs/PROJECT_STATUS.md Normal file
View File

@ -0,0 +1,241 @@
# État d'Avancement du Projet
Ce document présente l'état d'avancement actuel du projet "Application de Création de Groupes", les tâches restantes et les prochaines étapes prioritaires.
## Résumé du Travail Effectué
Nous avons élaboré un plan de bataille complet pour l'implémentation du backend de l'application de création de groupes, basé sur les spécifications du cahier des charges. Ce travail a abouti à la création de plusieurs documents détaillés qui fournissent une base solide pour le développement, avec des instructions détaillées pour chaque composant du système.
## État Actuel
### Backend
#### Composants Implémentés
- ✅ Structure de base du projet NestJS
- ✅ Configuration de l'environnement et des variables d'environnement
- ✅ Schéma de base de données complet avec DrizzleORM
- ✅ Module de base de données configuré
- ✅ Module utilisateurs (contrôleurs, services, DTOs)
- ✅ Module projets (contrôleurs, services, DTOs)
- ✅ Module personnes (contrôleurs, services, DTOs)
- ✅ Configuration Docker pour le déploiement
#### Composants En Cours
- ⏳ Système de migrations de base de données
- ⏳ Relations entre les modules existants
#### Composants Non Implémentés
- ❌ Module d'authentification avec GitHub OAuth
- ❌ Stratégies JWT pour la gestion des sessions
- ❌ Guards et décorateurs pour la protection des routes
- ❌ Module groupes
- ❌ Module tags
- ❌ Communication en temps réel avec Socket.IO
- ❌ Fonctionnalités de conformité RGPD
- ❌ Tests unitaires et e2e
- ❌ Documentation API avec Swagger
### Frontend
#### Composants Implémentés
- ✅ Structure de base du projet Next.js
- ✅ Configuration de ShadcnUI pour les composants UI
- ✅ Configuration Docker pour le déploiement
#### Composants Non Implémentés
- ❌ Pages d'authentification (login, callback)
- ❌ Page d'accueil et tableau de bord
- ❌ Pages de gestion de projets
- ❌ Pages de gestion de personnes
- ❌ Pages de création et gestion de groupes
- ❌ Fonctionnalités de collaboration en temps réel
- ❌ Optimisations de performance et d'expérience utilisateur
## Tâches Restantes
### Backend
#### Priorité Haute
##### Migrations de Base de Données
- [ ] Configurer le système de migrations avec DrizzleORM
- [ ] Générer les migrations initiales
- [ ] Créer un script pour exécuter les migrations automatiquement au démarrage
##### Authentification
- [ ] Implémenter le module d'authentification
- [ ] Configurer l'authentification OAuth avec GitHub
- [ ] Implémenter les stratégies JWT pour la gestion des sessions
- [ ] Créer les guards et décorateurs pour la protection des routes
- [ ] Implémenter le refresh token
##### Modules Manquants
- [ ] Implémenter le module groupes (contrôleurs, services, DTOs)
- [ ] Implémenter le module tags (contrôleurs, services, DTOs)
- [ ] Compléter les relations entre les modules existants
#### Priorité Moyenne
##### Communication en Temps Réel
- [ ] Configurer Socket.IO avec NestJS
- [ ] Implémenter les gateways WebSocket pour les projets
- [ ] Implémenter les gateways WebSocket pour les groupes
- [ ] Implémenter les gateways WebSocket pour les notifications
- [ ] Mettre en place le service WebSocket pour la gestion des connexions
##### Sécurité et Conformité RGPD
- [ ] Implémenter la validation des entrées avec class-validator
- [ ] Configurer CORS pour sécuriser les API
- [ ] Mettre en place la protection contre les attaques CSRF
- [ ] Implémenter les fonctionnalités d'export de données utilisateur (RGPD)
- [ ] Implémenter le renouvellement du consentement utilisateur
#### Priorité Basse
##### Tests et Documentation
- [ ] Écrire des tests unitaires pour les services
- [ ] Écrire des tests unitaires pour les contrôleurs
- [ ] Développer des tests e2e pour les API
- [ ] Configurer Swagger pour la documentation API
- [ ] Documenter les endpoints API
### Frontend
#### Priorité Haute
##### Authentification
- [ ] Créer la page de login avec le bouton "Login with GitHub"
- [ ] Implémenter la page de callback OAuth
- [ ] Configurer le stockage sécurisé des tokens JWT
- [ ] Implémenter la logique de refresh token
- [ ] Créer les composants de protection des routes authentifiées
##### Pages Principales
- [ ] Implémenter la page d'accueil
- [ ] Créer le tableau de bord utilisateur
- [ ] Développer les pages de gestion de projets (liste, création, détail, édition)
- [ ] Développer les pages de gestion de personnes (liste, création, détail, édition)
- [ ] Implémenter les pages de création et gestion de groupes
#### Priorité Moyenne
##### Fonctionnalités Avancées
- [ ] Implémenter l'interface de création manuelle de groupes (drag-and-drop)
- [ ] Développer l'assistant de création automatique de groupes équilibrés
- [ ] Intégrer les fonctionnalités de collaboration en temps réel avec Socket.IO
- [ ] Implémenter le système de notifications en temps réel
- [ ] Créer les composants pour la gestion des tags
##### Expérience Utilisateur
- [ ] Améliorer la réactivité de l'interface
- [ ] Implémenter les animations et transitions
- [ ] Optimiser les formulaires avec React Hook Form
- [ ] Ajouter des retours visuels pour les actions utilisateur
- [ ] Implémenter la gestion des erreurs et les messages d'information
#### Priorité Basse
##### Optimisation et Finalisation
- [ ] Optimiser les performances (lazy loading, code splitting)
- [ ] Implémenter le mode hors ligne pour certaines fonctionnalités
- [ ] Ajouter le support pour les thèmes (clair/sombre)
- [ ] Optimiser pour les appareils mobiles
- [ ] Réaliser des tests d'accessibilité et corriger les problèmes identifiés
### Intégration et Déploiement
#### Priorité Haute
- [ ] Finaliser la configuration Docker Compose pour le développement local
- [ ] Configurer les variables d'environnement pour les différents environnements
- [ ] Mettre en place un environnement de staging
#### Priorité Moyenne
- [ ] Configurer le monitoring et les alertes
- [ ] Mettre en place un système de logging centralisé
- [ ] Configurer les sauvegardes automatiques de la base de données
#### Priorité Basse
- [ ] Optimiser les images Docker pour la production
- [ ] Configurer un CDN pour les assets statiques
- [ ] Mettre en place un système de déploiement blue/green
## Prochaines Étapes Prioritaires
### Backend (Priorité Haute)
1. **Migrations de Base de Données**
- Configurer le système de migrations avec DrizzleORM
- Générer les migrations initiales
- Créer un script pour exécuter les migrations automatiquement
2. **Authentification**
- Implémenter le module d'authentification avec GitHub OAuth
- Configurer les stratégies JWT pour la gestion des sessions
- Créer les guards et décorateurs pour la protection des routes
3. **Modules Manquants**
- Implémenter le module groupes
- Implémenter le module tags
- Compléter les relations entre les modules existants
### Frontend (Priorité Haute)
1. **Authentification**
- Créer la page de login avec le bouton "Login with GitHub"
- Implémenter la page de callback OAuth
- Configurer le stockage sécurisé des tokens JWT
2. **Pages Principales**
- Implémenter la page d'accueil
- Créer le tableau de bord utilisateur
- Développer les pages de gestion de projets et de personnes
## Progression Globale
| Composant | Progression |
|-----------|-------------|
| Backend - Structure de Base | 90% |
| Backend - Base de Données | 80% |
| Backend - Modules Fonctionnels | 60% |
| Backend - Authentification | 0% |
| Backend - WebSockets | 0% |
| Backend - Tests et Documentation | 0% |
| Frontend - Structure de Base | 70% |
| Frontend - Pages et Composants | 10% |
| Frontend - Authentification | 0% |
| Frontend - Fonctionnalités Avancées | 0% |
| Déploiement | 70% |
## Estimation du Temps Restant
Basé sur l'état d'avancement actuel et les tâches restantes, l'estimation du temps nécessaire pour compléter le projet est la suivante:
- **Backend**: ~4-5 semaines
- Authentification: 1 semaine
- Modules manquants: 1-2 semaines
- WebSockets: 1 semaine
- Tests et documentation: 1 semaine
- **Frontend**: ~5-6 semaines
- Authentification: 1 semaine
- Pages principales: 2 semaines
- Fonctionnalités avancées: 1-2 semaines
- Optimisation et finalisation: 1 semaine
- **Intégration et Tests**: ~1-2 semaines
**Temps total estimé**: 10-13 semaines
## Recommandations
1. **Approche Itérative** : Suivre une approche itérative en implémentant d'abord les fonctionnalités de base, puis en ajoutant progressivement les fonctionnalités plus avancées.
2. **Tests Continus** : Écrire des tests au fur et à mesure du développement pour s'assurer que les fonctionnalités sont correctement implémentées.
3. **Documentation** : Documenter le code et les API au fur et à mesure pour faciliter la maintenance et l'évolution du projet.
4. **Revue de Code** : Effectuer des revues de code régulières pour s'assurer de la qualité du code et du respect des bonnes pratiques.
5. **Suivi du Calendrier** : Suivre le calendrier d'implémentation proposé pour s'assurer que le projet progresse selon le planning prévu.
## Conclusion
Le projet a bien avancé sur la structure de base et la définition du schéma de données, mais il reste encore un travail significatif à réaliser. Les prochaines étapes prioritaires devraient se concentrer sur l'authentification et les fonctionnalités de base pour avoir rapidement une version minimale fonctionnelle.

46
docs/README.md Normal file
View File

@ -0,0 +1,46 @@
# Documentation du Projet
Ce répertoire contient la documentation complète pour l'application "Application de Création de Groupes". La documentation a été organisée de manière à faciliter la navigation et la compréhension du projet.
## Structure de la Documentation
### Vue d'Ensemble
- [Vue d'Ensemble du Projet](PROJECT_OVERVIEW.md) - Analyse complète de l'architecture, des technologies et des fonctionnalités
- [État d'Avancement du Projet](PROJECT_STATUS.md) - État actuel, tâches restantes et prochaines étapes
- [Diagrammes de Flux Métier](BUSINESS_FLOW_DIAGRAMS.md) - Diagrammes de séquence pour les principaux flux métier
- [Cahier des Charges](SPECIFICATIONS.md) - Spécifications initiales du projet
### Guides d'Implémentation
- [Guide d'Implémentation](IMPLEMENTATION_GUIDE.md) - Guide complet pour l'implémentation de l'application
- [Plans d'Implémentation Détaillés](implementation/README.md) - Plans détaillés pour chaque composant du système
## Organisation des Fichiers
La documentation est organisée comme suit :
```
docs/
├── PROJECT_OVERVIEW.md # Vue d'ensemble du projet
├── PROJECT_STATUS.md # État d'avancement et tâches restantes
├── BUSINESS_FLOW_DIAGRAMS.md # Diagrammes de flux métier
├── SPECIFICATIONS.md # Cahier des charges
├── IMPLEMENTATION_GUIDE.md # Guide d'implémentation
├── implementation/ # Plans d'implémentation détaillés
│ ├── README.md # Index des plans d'implémentation
│ ├── BACKEND_IMPLEMENTATION_PLAN.md
│ ├── DATABASE_SCHEMA_PLAN.md
│ ├── AUTH_IMPLEMENTATION_PLAN.md
│ └── WEBSOCKET_IMPLEMENTATION_PLAN.md
└── README.md # Ce fichier
```
## Utilisation de la Documentation
- Commencez par la [Vue d'Ensemble du Projet](PROJECT_OVERVIEW.md) pour comprendre l'architecture et les fonctionnalités de l'application
- Consultez l'[État d'Avancement du Projet](PROJECT_STATUS.md) pour connaître l'état actuel et les prochaines étapes
- Référez-vous au [Guide d'Implémentation](IMPLEMENTATION_GUIDE.md) pour obtenir des instructions détaillées sur l'implémentation de l'application
- Explorez les [Plans d'Implémentation Détaillés](implementation/README.md) pour des informations spécifiques sur chaque composant du système
## Maintenance de la Documentation
La documentation doit être maintenue à jour au fur et à mesure que le projet évolue. Assurez-vous de mettre à jour les documents pertinents lorsque vous apportez des modifications significatives au code ou à l'architecture.

283
docs/SPECIFICATIONS.md Normal file
View File

@ -0,0 +1,283 @@
# Cahier des Charges - Application de Création de Groupes
*Note: Ce document est une copie du fichier cdc.md original, renommé en SPECIFICATIONS.md pour une meilleure organisation de la documentation.*
## Introduction
Ce document présente le cahier des charges pour le développement d'une application web dédiée à la création et à la gestion de groupes. L'application permettra aux utilisateurs de créer des groupes selon différents critères et de conserver un historique des groupes précédemment créés.
## Objectifs du Projet
- Développer une application permettant la création de groupes selon différents critères
- Maintenir un historique des groupes créés pour éviter les duplications
- Offrir une interface intuitive et responsive
- Assurer la sécurité des données utilisateurs
- Respecter les normes RGPD
## Fonctionnalités Principales
### Gestion des Utilisateurs
- Authentification via OAuth2.0 avec GitHub
- Gestion des sessions utilisateur avec JWT
- Gestion des autorisations basée sur les rôles (RBAC)
- Profil utilisateur personnalisable
- Tableau de bord personnel avec statistiques d'utilisation
- Export des données personnelles (RGPD)
### Création et Gestion de Groupes
- Création de projets de groupe avec liste de personnes
- Attribution de tags aux personnes
- Définition d'échelles de niveau personnalisées
- Interface de création manuelle de groupes
- Assistant à la création automatique de groupes équilibrés
- Collaboration en temps réel entre utilisateurs
### Administration
- Tableau de bord administrateur
- Gestion des tags globaux
- Surveillance de l'activité des utilisateurs
- Gestion des rôles et permissions
## Spécifications Techniques
### Architecture
L'application suivra une architecture monorepo avec séparation claire entre le frontend et le backend:
- **Backend**: NestJS avec PostgreSQL et DrizzleORM
- **Frontend**: Next.js avec ShadcnUI et SWR
- **Authentification**: OAuth2.0 + OIDC via GitHub
- **Communication en temps réel**: Socket.IO
- **Déploiement**: Docker et Gitea Actions
### Modèle de Données
#### Entités Principales
1. **User**: Utilisateurs de l'application
- id (UUID)
- githubId (string)
- name (string)
- avatar (string)
- role (enum: USER, ADMIN)
- gdprTimestamp (datetime)
- createdAt (datetime)
- updatedAt (datetime)
2. **Project**: Projets de création de groupes
- id (UUID)
- name (string)
- description (text)
- settings (JSON)
- userId (UUID, foreign key)
- isPublic (boolean)
- createdAt (datetime)
- updatedAt (datetime)
3. **Person**: Personnes à placer dans les groupes
- id (UUID)
- name (string)
- email (string)
- technicalLevel (integer)
- gender (string)
- attributes (JSON)
- projectId (UUID, foreign key)
- createdAt (datetime)
- updatedAt (datetime)
4. **Group**: Groupes créés dans le cadre d'un projet
- id (UUID)
- name (string)
- description (text)
- settings (JSON)
- projectId (UUID, foreign key)
- createdAt (datetime)
- updatedAt (datetime)
5. **Tag**: Étiquettes pour catégoriser les personnes et les projets
- id (UUID)
- name (string)
- description (text)
- color (string)
- type (enum: PROJECT, PERSON)
- createdAt (datetime)
- updatedAt (datetime)
#### Relations
- **ProjectCollaborators**: Relation many-to-many entre Project et User
- **PersonToGroup**: Relation many-to-many entre Person et Group
- **PersonToTag**: Relation many-to-many entre Person et Tag
- **ProjectToTag**: Relation many-to-many entre Project et Tag
### API REST
L'API REST suivra les principes RESTful avec les endpoints suivants:
#### Authentification
- `GET /api/auth/github`: Redirection vers GitHub pour l'authentification
- `GET /api/auth/github/callback`: Callback OAuth GitHub
- `POST /api/auth/refresh`: Rafraîchissement du token JWT
- `POST /api/auth/logout`: Déconnexion
- `GET /api/auth/profile`: Récupération du profil utilisateur
#### Utilisateurs
- `GET /api/users`: Liste des utilisateurs (admin)
- `GET /api/users/:id`: Détails d'un utilisateur
- `PATCH /api/users/:id`: Mise à jour d'un utilisateur
- `DELETE /api/users/:id`: Suppression d'un utilisateur
- `GET /api/users/me/export`: Export des données personnelles (RGPD)
#### Projets
- `GET /api/projects`: Liste des projets
- `POST /api/projects`: Création d'un projet
- `GET /api/projects/:id`: Détails d'un projet
- `PATCH /api/projects/:id`: Mise à jour d'un projet
- `DELETE /api/projects/:id`: Suppression d'un projet
- `POST /api/projects/:id/collaborators`: Ajout d'un collaborateur
- `DELETE /api/projects/:id/collaborators/:userId`: Suppression d'un collaborateur
#### Personnes
- `GET /api/projects/:id/persons`: Liste des personnes d'un projet
- `POST /api/projects/:id/persons`: Ajout d'une personne à un projet
- `GET /api/persons/:id`: Détails d'une personne
- `PATCH /api/persons/:id`: Mise à jour d'une personne
- `DELETE /api/persons/:id`: Suppression d'une personne
- `POST /api/persons/:id/tags`: Ajout d'un tag à une personne
- `DELETE /api/persons/:id/tags/:tagId`: Suppression d'un tag d'une personne
#### Groupes
- `GET /api/projects/:id/groups`: Liste des groupes d'un projet
- `POST /api/projects/:id/groups`: Création d'un groupe
- `GET /api/groups/:id`: Détails d'un groupe
- `PATCH /api/groups/:id`: Mise à jour d'un groupe
- `DELETE /api/groups/:id`: Suppression d'un groupe
- `POST /api/groups/:id/persons`: Ajout d'une personne à un groupe
- `DELETE /api/groups/:id/persons/:personId`: Suppression d'une personne d'un groupe
- `POST /api/projects/:id/auto-groups`: Création automatique de groupes
#### Tags
- `GET /api/tags`: Liste des tags
- `POST /api/tags`: Création d'un tag
- `GET /api/tags/:id`: Détails d'un tag
- `PATCH /api/tags/:id`: Mise à jour d'un tag
- `DELETE /api/tags/:id`: Suppression d'un tag
### WebSockets
L'application utilisera Socket.IO pour la communication en temps réel avec les événements suivants:
- `project:updated`: Mise à jour d'un projet
- `project:collaboratorAdded`: Ajout d'un collaborateur à un projet
- `group:created`: Création d'un groupe
- `group:updated`: Mise à jour d'un groupe
- `group:personAdded`: Ajout d'une personne à un groupe
- `group:personRemoved`: Suppression d'une personne d'un groupe
- `notification:new`: Nouvelle notification
### Interface Utilisateur
L'interface utilisateur sera développée avec Next.js et ShadcnUI, offrant une expérience responsive et intuitive:
#### Pages Principales
- **Page d'accueil**: Présentation de l'application
- **Tableau de bord**: Vue d'ensemble des projets et activités récentes
- **Page de projet**: Détails d'un projet avec liste des personnes et groupes
- **Éditeur de groupe**: Interface drag-and-drop pour la création manuelle de groupes
- **Assistant de groupe**: Interface pour la création automatique de groupes équilibrés
- **Profil utilisateur**: Gestion des informations personnelles et préférences
#### Composants UI
- Barre de navigation responsive
- Sidebar pour la navigation entre les sections
- Modals pour les formulaires de création et d'édition
- Tableaux interactifs avec tri et filtrage
- Interface drag-and-drop pour la gestion des groupes
- Notifications en temps réel
- Thème clair/sombre
## Sécurité et Conformité
### Sécurité
- Authentification sécurisée via OAuth2.0
- Tokens JWT avec durée de vie limitée
- Protection CSRF
- Validation des entrées utilisateur
- Protection contre les injections SQL
- Rate limiting pour prévenir les attaques par force brute
### Conformité RGPD
- Minimisation des données collectées
- Finalité claire de la collecte de données
- Mise en œuvre des droits des utilisateurs (accès, rectification, effacement, portabilité)
- Renouvellement du consentement utilisateur tous les 13 mois
## Performance
### Objectifs
- Temps de chargement initial < 2 secondes (95ème percentile)
- Temps de réponse API < 300ms (95ème percentile)
- Disponibilité > 99.9%
- Support de 1000 utilisateurs simultanés minimum
### Optimisations
- Utilisation efficiente des index pour les requêtes fréquentes
- Mise en cache des requêtes fréquentes
- Optimisation des requêtes N+1
- Monitoring et alerting automatique
## Livrables
- Code source complet de l'application (backend et frontend)
- Documentation technique
- Documentation utilisateur
- Scripts de déploiement Docker
- Configuration CI/CD
## Calendrier
Le développement de l'application sera réalisé en plusieurs phases:
1. **Phase 1 (2 semaines)**: Configuration et base de données
- Mise en place de l'environnement de développement
- Implémentation du schéma de base de données
- Configuration de l'authentification
2. **Phase 2 (3 semaines)**: Développement des fonctionnalités de base
- Implémentation des modules backend
- Développement des pages frontend principales
- Intégration de l'authentification
3. **Phase 3 (2 semaines)**: Fonctionnalités avancées
- Implémentation de la communication en temps réel
- Développement de l'assistant de création de groupes
- Mise en place des fonctionnalités de collaboration
4. **Phase 4 (1 semaine)**: Tests et optimisation
- Tests unitaires et e2e
- Optimisation des performances
- Correction des bugs
5. **Phase 5 (1 semaine)**: Déploiement et documentation
- Configuration du déploiement Docker
- Mise en place du CI/CD
- Finalisation de la documentation
## Conclusion
Ce cahier des charges définit les spécifications techniques et fonctionnelles pour le développement de l'application de création de groupes. L'application offrira une solution complète pour la gestion de groupes, avec une interface intuitive et des fonctionnalités avancées de collaboration en temps réel.

View File

@ -0,0 +1,554 @@
# Plan d'Implémentation de l'Authentification
Ce document détaille le plan d'implémentation du système d'authentification pour l'application de création de groupes, basé sur les spécifications du cahier des charges.
## 1. Vue d'Ensemble
L'application utilisera OAuth 2.0 avec GitHub comme fournisseur d'identité, combiné avec une gestion de session basée sur JWT (JSON Web Tokens). Cette approche offre plusieurs avantages :
- Délégation de l'authentification à un service tiers sécurisé (GitHub)
- Pas besoin de gérer les mots de passe des utilisateurs
- Récupération des informations de profil (nom, avatar) depuis l'API GitHub
- Authentification stateless avec JWT pour une meilleure scalabilité
## 2. Flux d'Authentification
```mermaid
sequenceDiagram
participant User as Utilisateur
participant Frontend as Frontend (Next.js)
participant Backend as Backend (NestJS)
participant GitHub as GitHub OAuth
User->>Frontend: Clic sur "Se connecter avec GitHub"
Frontend->>Backend: Redirection vers /auth/github
Backend->>GitHub: Redirection vers GitHub OAuth
GitHub->>User: Demande d'autorisation
User->>GitHub: Accepte l'autorisation
GitHub->>Backend: Redirection avec code d'autorisation
Backend->>GitHub: Échange code contre token d'accès
GitHub->>Backend: Retourne token d'accès
Backend->>GitHub: Requête informations utilisateur
GitHub->>Backend: Retourne informations utilisateur
Backend->>Backend: Crée/Met à jour l'utilisateur en BDD
Backend->>Backend: Génère JWT (access + refresh tokens)
Backend->>Frontend: Redirection avec tokens JWT
Frontend->>Frontend: Stocke les tokens
Frontend->>User: Affiche interface authentifiée
```
## 3. Structure des Modules
### 3.1 Module d'Authentification
```typescript
// src/modules/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthController } from './controllers/auth.controller';
import { AuthService } from './services/auth.service';
import { GithubStrategy } from './strategies/github.strategy';
import { JwtStrategy } from './strategies/jwt.strategy';
import { JwtRefreshStrategy } from './strategies/jwt-refresh.strategy';
import { UsersModule } from '../users/users.module';
@Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
secret: configService.get<string>('JWT_ACCESS_SECRET'),
signOptions: {
expiresIn: configService.get<string>('JWT_ACCESS_EXPIRATION', '15m'),
},
}),
}),
UsersModule,
],
controllers: [AuthController],
providers: [AuthService, GithubStrategy, JwtStrategy, JwtRefreshStrategy],
exports: [AuthService],
})
export class AuthModule {}
```
### 3.2 Stratégies d'Authentification
#### 3.2.1 Stratégie GitHub
```typescript
// src/modules/auth/strategies/github.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-github2';
import { ConfigService } from '@nestjs/config';
import { AuthService } from '../services/auth.service';
@Injectable()
export class GithubStrategy extends PassportStrategy(Strategy, 'github') {
constructor(
private configService: ConfigService,
private authService: AuthService,
) {
super({
clientID: configService.get<string>('GITHUB_CLIENT_ID'),
clientSecret: configService.get<string>('GITHUB_CLIENT_SECRET'),
callbackURL: configService.get<string>('GITHUB_CALLBACK_URL'),
scope: ['read:user'],
});
}
async validate(accessToken: string, refreshToken: string, profile: any) {
const { id, displayName, photos } = profile;
const user = await this.authService.validateGithubUser({
githubId: id,
name: displayName || `user_${id}`,
avatar: photos?.[0]?.value,
});
return user;
}
}
```
#### 3.2.2 Stratégie JWT
```typescript
// src/modules/auth/strategies/jwt.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
import { UsersService } from '../../users/services/users.service';
import { JwtPayload } from '../interfaces/jwt-payload.interface';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(
private configService: ConfigService,
private usersService: UsersService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: configService.get<string>('JWT_ACCESS_SECRET'),
});
}
async validate(payload: JwtPayload) {
const { sub } = payload;
const user = await this.usersService.findById(sub);
if (!user) {
throw new UnauthorizedException('User not found');
}
return user;
}
}
```
#### 3.2.3 Stratégie JWT Refresh
```typescript
// src/modules/auth/strategies/jwt-refresh.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
import { Request } from 'express';
import { UsersService } from '../../users/services/users.service';
import { JwtPayload } from '../interfaces/jwt-payload.interface';
@Injectable()
export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'jwt-refresh') {
constructor(
private configService: ConfigService,
private usersService: UsersService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: configService.get<string>('JWT_REFRESH_SECRET'),
passReqToCallback: true,
});
}
async validate(req: Request, payload: JwtPayload) {
const refreshToken = req.headers.authorization?.replace('Bearer ', '');
if (!refreshToken) {
throw new UnauthorizedException('Refresh token not found');
}
const { sub } = payload;
const user = await this.usersService.findById(sub);
if (!user) {
throw new UnauthorizedException('User not found');
}
return { ...user, refreshToken };
}
}
```
### 3.3 Service d'Authentification
```typescript
// src/modules/auth/services/auth.service.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { UsersService } from '../../users/services/users.service';
import { JwtPayload } from '../interfaces/jwt-payload.interface';
import { TokensResponse } from '../interfaces/tokens-response.interface';
import { GithubUserDto } from '../dto/github-user.dto';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
private configService: ConfigService,
) {}
async validateGithubUser(githubUserDto: GithubUserDto) {
const { githubId, name, avatar } = githubUserDto;
// Recherche de l'utilisateur par githubId
let user = await this.usersService.findByGithubId(githubId);
// Si l'utilisateur n'existe pas, on le crée
if (!user) {
user = await this.usersService.create({
githubId,
name,
avatar,
gdprTimestamp: new Date(),
});
} else {
// Mise à jour des informations de l'utilisateur
user = await this.usersService.update(user.id, {
name,
avatar,
});
}
return user;
}
async login(user: any): Promise<TokensResponse> {
const payload: JwtPayload = { sub: user.id };
const [accessToken, refreshToken] = await Promise.all([
this.generateAccessToken(payload),
this.generateRefreshToken(payload),
]);
return {
accessToken,
refreshToken,
expiresIn: this.configService.get<string>('JWT_ACCESS_EXPIRATION', '15m'),
};
}
async refreshTokens(userId: string, refreshToken: string): Promise<TokensResponse> {
// Vérification du refresh token (à implémenter avec une table de tokens révoqués)
const payload: JwtPayload = { sub: userId };
const [accessToken, newRefreshToken] = await Promise.all([
this.generateAccessToken(payload),
this.generateRefreshToken(payload),
]);
return {
accessToken,
refreshToken: newRefreshToken,
expiresIn: this.configService.get<string>('JWT_ACCESS_EXPIRATION', '15m'),
};
}
async logout(userId: string, refreshToken: string): Promise<void> {
// Ajouter le refresh token à la liste des tokens révoqués
// À implémenter avec une table de tokens révoqués
}
private async generateAccessToken(payload: JwtPayload): Promise<string> {
return this.jwtService.signAsync(payload, {
secret: this.configService.get<string>('JWT_ACCESS_SECRET'),
expiresIn: this.configService.get<string>('JWT_ACCESS_EXPIRATION', '15m'),
});
}
private async generateRefreshToken(payload: JwtPayload): Promise<string> {
return this.jwtService.signAsync(payload, {
secret: this.configService.get<string>('JWT_REFRESH_SECRET'),
expiresIn: this.configService.get<string>('JWT_REFRESH_EXPIRATION', '7d'),
});
}
}
```
### 3.4 Contrôleur d'Authentification
```typescript
// src/modules/auth/controllers/auth.controller.ts
import { Controller, Get, Post, UseGuards, Req, Res, Body, HttpCode } from '@nestjs/common';
import { Response } from 'express';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from '../services/auth.service';
import { JwtRefreshGuard } from '../guards/jwt-refresh.guard';
import { GetUser } from '../decorators/get-user.decorator';
import { RefreshTokenDto } from '../dto/refresh-token.dto';
import { Public } from '../decorators/public.decorator';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@Public()
@Get('github')
@UseGuards(AuthGuard('github'))
githubAuth() {
// Cette route redirige vers GitHub pour l'authentification
}
@Public()
@Get('github/callback')
@UseGuards(AuthGuard('github'))
async githubAuthCallback(@Req() req, @Res() res: Response) {
const { accessToken, refreshToken } = await this.authService.login(req.user);
// Redirection vers le frontend avec les tokens
const redirectUrl = `${this.configService.get<string>('FRONTEND_URL')}/auth/callback?accessToken=${accessToken}&refreshToken=${refreshToken}`;
return res.redirect(redirectUrl);
}
@Public()
@Post('refresh')
@UseGuards(JwtRefreshGuard)
@HttpCode(200)
async refreshTokens(
@GetUser('id') userId: string,
@GetUser('refreshToken') refreshToken: string,
) {
return this.authService.refreshTokens(userId, refreshToken);
}
@Post('logout')
@HttpCode(200)
async logout(
@GetUser('id') userId: string,
@Body() refreshTokenDto: RefreshTokenDto,
) {
await this.authService.logout(userId, refreshTokenDto.refreshToken);
return { message: 'Logout successful' };
}
@Get('profile')
getProfile(@GetUser() user) {
return user;
}
}
```
### 3.5 Guards et Décorateurs
#### 3.5.1 Guard JWT
```typescript
// src/modules/auth/guards/jwt-auth.guard.ts
import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}
```
#### 3.5.2 Guard JWT Refresh
```typescript
// src/modules/auth/guards/jwt-refresh.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtRefreshGuard extends AuthGuard('jwt-refresh') {}
```
#### 3.5.3 Décorateur Public
```typescript
// src/modules/auth/decorators/public.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
```
#### 3.5.4 Décorateur GetUser
```typescript
// src/modules/auth/decorators/get-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const GetUser = createParamDecorator(
(data: string | undefined, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
},
);
```
## 4. Configuration Globale de l'Authentification
### 4.1 Configuration du Module App
```typescript
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { APP_GUARD } from '@nestjs/core';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './modules/auth/auth.module';
import { UsersModule } from './modules/users/users.module';
import { DatabaseModule } from './database/database.module';
import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard';
import { validate } from './config/env.validation';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
validate,
}),
DatabaseModule,
AuthModule,
UsersModule,
],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
],
})
export class AppModule {}
```
## 5. Sécurité et Bonnes Pratiques
### 5.1 Gestion des Tokens
- **Access Token** : Durée de vie courte (15 minutes) pour limiter les risques en cas de vol
- **Refresh Token** : Durée de vie plus longue (7 jours) pour permettre le rafraîchissement de l'access token
- Stockage sécurisé des tokens côté client (localStorage pour l'access token, httpOnly cookie pour le refresh token dans une implémentation plus sécurisée)
- Révocation des tokens en cas de déconnexion ou de suspicion de compromission
### 5.2 Protection contre les Attaques Courantes
- **CSRF** : Utilisation de tokens anti-CSRF pour les opérations sensibles
- **XSS** : Échappement des données utilisateur, utilisation de Content Security Policy
- **Injection** : Validation des entrées avec class-validator, utilisation de paramètres préparés avec DrizzleORM
- **Rate Limiting** : Limitation du nombre de requêtes d'authentification pour prévenir les attaques par force brute
### 5.3 Conformité RGPD
- Enregistrement du timestamp d'acceptation RGPD lors de la création du compte
- Possibilité d'exporter les données personnelles
- Possibilité de supprimer le compte et toutes les données associées
- Renouvellement du consentement tous les 13 mois
## 6. Intégration avec le Frontend
### 6.1 Flux d'Authentification côté Frontend
```typescript
// Exemple de code pour le frontend (Next.js)
// app/auth/github/page.tsx
'use client';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { API_URL } from '@/lib/constants';
export default function GitHubAuthPage() {
const router = useRouter();
useEffect(() => {
// Redirection vers l'endpoint d'authentification GitHub du backend
window.location.href = `${API_URL}/auth/github`;
}, []);
return <div>Redirection vers GitHub pour authentification...</div>;
}
```
```typescript
// app/auth/callback/page.tsx
'use client';
import { useEffect } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useAuth } from '@/hooks/useAuth';
export default function AuthCallbackPage() {
const router = useRouter();
const searchParams = useSearchParams();
const { login } = useAuth();
useEffect(() => {
const accessToken = searchParams.get('accessToken');
const refreshToken = searchParams.get('refreshToken');
if (accessToken && refreshToken) {
// Stockage des tokens
login(accessToken, refreshToken);
// Redirection vers le dashboard
router.push('/dashboard');
} else {
// Erreur d'authentification
router.push('/auth/error');
}
}, [searchParams, login, router]);
return <div>Finalisation de l'authentification...</div>;
}
```
## 7. Conclusion
Ce plan d'implémentation fournit une base solide pour mettre en place un système d'authentification sécurisé et conforme aux bonnes pratiques. L'utilisation d'OAuth 2.0 avec GitHub comme fournisseur d'identité simplifie le processus d'authentification pour les utilisateurs tout en offrant un niveau de sécurité élevé.
La gestion des sessions avec JWT permet une architecture stateless qui facilite la scalabilité de l'application, tandis que le mécanisme de refresh token offre une expérience utilisateur fluide sans compromettre la sécurité.
Les mesures de sécurité et de conformité RGPD intégrées dans ce plan garantissent que l'application respecte les normes actuelles en matière de protection des données et de sécurité des utilisateurs.

View File

@ -0,0 +1,223 @@
# Plan d'Implémentation du Backend
Ce document détaille le plan d'implémentation du backend pour l'application de création de groupes, basé sur les spécifications du cahier des charges.
## 1. Structure des Dossiers
```
backend/
├── src/
│ ├── main.ts # Point d'entrée de l'application
│ ├── app.module.ts # Module principal
│ ├── config/ # Configuration de l'application
│ │ ├── app.config.ts # Configuration générale
│ │ ├── database.config.ts # Configuration de la base de données
│ │ ├── auth.config.ts # Configuration de l'authentification
│ │ └── env.validation.ts # Validation des variables d'environnement
│ ├── common/ # Utilitaires partagés
│ │ ├── decorators/ # Décorateurs personnalisés
│ │ ├── filters/ # Filtres d'exception
│ │ ├── guards/ # Guards d'authentification et d'autorisation
│ │ ├── interceptors/ # Intercepteurs
│ │ ├── pipes/ # Pipes de validation
│ │ └── utils/ # Fonctions utilitaires
│ ├── modules/ # Modules fonctionnels
│ │ ├── auth/ # Module d'authentification
│ │ │ ├── controllers/ # Contrôleurs d'authentification
│ │ │ ├── services/ # Services d'authentification
│ │ │ ├── guards/ # Guards spécifiques à l'authentification
│ │ │ ├── strategies/ # Stratégies d'authentification (GitHub OAuth)
│ │ │ └── auth.module.ts # Module d'authentification
│ │ ├── users/ # Module de gestion des utilisateurs
│ │ │ ├── controllers/ # Contrôleurs utilisateurs
│ │ │ ├── services/ # Services utilisateurs
│ │ │ ├── dto/ # Objets de transfert de données
│ │ │ └── users.module.ts # Module utilisateurs
│ │ ├── projects/ # Module de gestion des projets
│ │ │ ├── controllers/ # Contrôleurs projets
│ │ │ ├── services/ # Services projets
│ │ │ ├── dto/ # Objets de transfert de données
│ │ │ └── projects.module.ts # Module projets
│ │ ├── persons/ # Module de gestion des personnes
│ │ │ ├── controllers/ # Contrôleurs personnes
│ │ │ ├── services/ # Services personnes
│ │ │ ├── dto/ # Objets de transfert de données
│ │ │ └── persons.module.ts # Module personnes
│ │ ├── groups/ # Module de gestion des groupes
│ │ │ ├── controllers/ # Contrôleurs groupes
│ │ │ ├── services/ # Services groupes
│ │ │ ├── dto/ # Objets de transfert de données
│ │ │ └── groups.module.ts # Module groupes
│ │ ├── tags/ # Module de gestion des tags
│ │ │ ├── controllers/ # Contrôleurs tags
│ │ │ ├── services/ # Services tags
│ │ │ ├── dto/ # Objets de transfert de données
│ │ │ └── tags.module.ts # Module tags
│ │ └── websockets/ # Module de gestion des WebSockets
│ │ ├── gateways/ # Gateways WebSocket
│ │ ├── events/ # Définitions des événements
│ │ └── websockets.module.ts # Module WebSockets
│ └── database/ # Configuration de la base de données
│ ├── migrations/ # Migrations de base de données
│ ├── schema/ # Schéma de base de données (DrizzleORM)
│ └── database.module.ts # Module de base de données
├── test/ # Tests
│ ├── e2e/ # Tests end-to-end
│ └── unit/ # Tests unitaires
└── .env.example # Exemple de fichier d'environnement
```
## 2. Dépendances à Ajouter
```bash
# Dépendances principales
pnpm add @nestjs/config @nestjs/passport passport passport-github2 @nestjs/jwt
pnpm add @nestjs/websockets @nestjs/platform-socket.io socket.io
pnpm add drizzle-orm pg
pnpm add @node-rs/argon2 jose
pnpm add class-validator class-transformer
pnpm add zod zod-validation-error
pnpm add uuid
# Dépendances de développement
pnpm add -D drizzle-kit
pnpm add -D @types/passport-github2 @types/socket.io @types/pg @types/uuid
```
## 3. Configuration de l'Environnement
Créer un fichier `.env.example` avec les variables suivantes :
```
# Application
PORT=3000
NODE_ENV=development
API_PREFIX=api
# Database
DATABASE_URL=postgres://postgres:postgres@localhost:5432/groupmaker
# Authentication
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback
# JWT
JWT_ACCESS_SECRET=your_access_token_secret
JWT_REFRESH_SECRET=your_refresh_token_secret
JWT_ACCESS_EXPIRATION=15m
JWT_REFRESH_EXPIRATION=7d
# CORS
CORS_ORIGIN=http://localhost:3000
```
## 4. Étapes d'Implémentation
### 4.1 Configuration de Base
1. **Configuration de l'Application**
- Mettre à jour `main.ts` pour inclure la configuration CORS, les préfixes d'API, et les pipes de validation globaux
- Créer un module de configuration pour charger les variables d'environnement avec validation
2. **Configuration de la Base de Données**
- Configurer DrizzleORM avec PostgreSQL
- Définir le schéma de base de données selon le modèle de données spécifié
- Mettre en place les migrations de base de données
### 4.2 Authentification et Autorisation
1. **Authentification GitHub OAuth**
- Implémenter la stratégie d'authentification GitHub
- Créer les endpoints d'authentification (login, callback, refresh, logout)
- Mettre en place la gestion des JWT (génération, validation, rafraîchissement)
2. **Autorisation RBAC**
- Implémenter les guards pour la vérification des rôles
- Créer des décorateurs pour les rôles et les permissions
- Mettre en place la logique de vérification des autorisations
### 4.3 Modules Fonctionnels
1. **Module Utilisateurs**
- Implémenter les opérations CRUD pour les utilisateurs
- Gérer les profils utilisateurs et les préférences
- Implémenter la logique de consentement RGPD
2. **Module Projets**
- Implémenter les opérations CRUD pour les projets
- Gérer les relations avec les utilisateurs, les personnes et les groupes
- Implémenter la logique de partage de projets
3. **Module Personnes**
- Implémenter les opérations CRUD pour les personnes
- Gérer les attributs des personnes (niveau technique, genre, âge, etc.)
- Implémenter la logique d'association avec les tags
4. **Module Groupes**
- Implémenter les opérations CRUD pour les groupes
- Développer les algorithmes de création automatique de groupes équilibrés
- Gérer les relations avec les personnes
5. **Module Tags**
- Implémenter les opérations CRUD pour les tags
- Gérer les types de tags (PROJECT, PERSON)
- Implémenter la logique d'association avec les projets et les personnes
### 4.4 Communication en Temps Réel
1. **WebSockets avec SocketIO**
- Configurer les gateways WebSocket
- Implémenter les événements pour les mises à jour en temps réel
- Gérer les salles pour les projets collaboratifs
### 4.5 Sécurité et Conformité RGPD
1. **Sécurité**
- Implémenter le hachage des mots de passe avec @node-rs/argon2
- Mettre en place des protections contre les attaques courantes (CSRF, XSS, injections SQL)
- Configurer le rate limiting pour prévenir les attaques par force brute
2. **Conformité RGPD**
- Implémenter les fonctionnalités d'export des données personnelles
- Mettre en place la logique de suppression de compte
- Gérer les consentements utilisateurs et leur renouvellement
### 4.6 Tests et Documentation
1. **Tests**
- Écrire des tests unitaires pour les services et les contrôleurs
- Développer des tests e2e pour les API
- Mettre en place des tests d'intégration pour les modules critiques
2. **Documentation**
- Générer la documentation API avec Swagger
- Documenter les endpoints, les modèles de données et les paramètres
- Fournir des exemples d'utilisation des API
## 5. Calendrier d'Implémentation
1. **Semaine 1: Configuration et Base de Données**
- Configuration de l'environnement
- Mise en place de la base de données avec DrizzleORM
- Définition du schéma et création des migrations
2. **Semaine 2: Authentification et Utilisateurs**
- Implémentation de l'authentification GitHub OAuth
- Développement du module utilisateurs
- Mise en place de la gestion des JWT
3. **Semaine 3: Modules Principaux**
- Développement des modules projets, personnes et groupes
- Implémentation des opérations CRUD
- Mise en place des relations entre entités
4. **Semaine 4: Fonctionnalités Avancées**
- Implémentation des WebSockets pour la communication en temps réel
- Développement des algorithmes de création de groupes
- Mise en place des fonctionnalités de sécurité et de conformité RGPD
5. **Semaine 5: Tests et Finalisation**
- Écriture des tests unitaires et e2e
- Documentation de l'API
- Optimisation des performances et correction des bugs

View File

@ -0,0 +1,405 @@
# Plan du Schéma de Base de Données
Ce document détaille le plan du schéma de base de données pour l'application de création de groupes, utilisant DrizzleORM avec PostgreSQL.
## 1. Vue d'Ensemble
Le schéma de base de données est conçu pour supporter les fonctionnalités suivantes :
- Gestion des utilisateurs et authentification
- Création et gestion de projets
- Gestion des personnes et de leurs attributs
- Création et gestion de groupes
- Système de tags pour catégoriser les personnes et les projets
## 2. Tables Principales
### 2.1 Table `users`
Stocke les informations des utilisateurs de l'application.
```typescript
// src/database/schema/users.ts
import { pgTable, uuid, varchar, timestamp, boolean } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').defaultRandom().primaryKey(),
githubId: varchar('github_id', { length: 255 }).notNull().unique(),
name: varchar('name', { length: 255 }).notNull(),
avatar: varchar('avatar', { length: 1024 }),
role: varchar('role', { length: 50 }).notNull().default('USER'),
gdprTimestamp: timestamp('gdpr_timestamp').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
```
### 2.2 Table `projects`
Stocke les informations des projets créés par les utilisateurs.
```typescript
// src/database/schema/projects.ts
import { pgTable, uuid, varchar, text, timestamp, boolean, jsonb } from 'drizzle-orm/pg-core';
import { users } from './users';
export const projects = pgTable('projects', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
description: text('description'),
settings: jsonb('settings').notNull().default({}),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
isPublic: boolean('is_public').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type Project = typeof projects.$inferSelect;
export type NewProject = typeof projects.$inferInsert;
```
### 2.3 Table `persons`
Stocke les informations des personnes qui seront placées dans les groupes.
```typescript
// src/database/schema/persons.ts
import { pgTable, uuid, varchar, text, integer, timestamp, jsonb } from 'drizzle-orm/pg-core';
import { projects } from './projects';
export const persons = pgTable('persons', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }),
technicalLevel: integer('technical_level'),
gender: varchar('gender', { length: 50 }),
attributes: jsonb('attributes').notNull().default({}),
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type Person = typeof persons.$inferSelect;
export type NewPerson = typeof persons.$inferInsert;
```
### 2.4 Table `groups`
Stocke les informations des groupes créés dans le cadre d'un projet.
```typescript
// src/database/schema/groups.ts
import { pgTable, uuid, varchar, text, timestamp, jsonb } from 'drizzle-orm/pg-core';
import { projects } from './projects';
export const groups = pgTable('groups', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
description: text('description'),
settings: jsonb('settings').notNull().default({}),
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type Group = typeof groups.$inferSelect;
export type NewGroup = typeof groups.$inferInsert;
```
### 2.5 Table `tags`
Stocke les tags qui peuvent être associés aux personnes et aux projets.
```typescript
// src/database/schema/tags.ts
import { pgTable, uuid, varchar, text, timestamp, pgEnum } from 'drizzle-orm/pg-core';
export const tagTypeEnum = pgEnum('tag_type', ['PROJECT', 'PERSON']);
export const tags = pgTable('tags', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
description: text('description'),
color: varchar('color', { length: 50 }),
type: tagTypeEnum('type').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type Tag = typeof tags.$inferSelect;
export type NewTag = typeof tags.$inferInsert;
```
## 3. Tables de Relations
### 3.1 Table `personToGroup`
Relation many-to-many entre les personnes et les groupes.
```typescript
// src/database/schema/personToGroup.ts
import { pgTable, uuid, primaryKey } from 'drizzle-orm/pg-core';
import { persons } from './persons';
import { groups } from './groups';
export const personToGroup = pgTable('person_to_group', {
personId: uuid('person_id').notNull().references(() => persons.id, { onDelete: 'cascade' }),
groupId: uuid('group_id').notNull().references(() => groups.id, { onDelete: 'cascade' }),
}, (t) => ({
pk: primaryKey({ columns: [t.personId, t.groupId] }),
}));
```
### 3.2 Table `personToTag`
Relation many-to-many entre les personnes et les tags.
```typescript
// src/database/schema/personToTag.ts
import { pgTable, uuid, primaryKey } from 'drizzle-orm/pg-core';
import { persons } from './persons';
import { tags } from './tags';
export const personToTag = pgTable('person_to_tag', {
personId: uuid('person_id').notNull().references(() => persons.id, { onDelete: 'cascade' }),
tagId: uuid('tag_id').notNull().references(() => tags.id, { onDelete: 'cascade' }),
}, (t) => ({
pk: primaryKey({ columns: [t.personId, t.tagId] }),
}));
```
### 3.3 Table `projectToTag`
Relation many-to-many entre les projets et les tags.
```typescript
// src/database/schema/projectToTag.ts
import { pgTable, uuid, primaryKey } from 'drizzle-orm/pg-core';
import { projects } from './projects';
import { tags } from './tags';
export const projectToTag = pgTable('project_to_tag', {
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
tagId: uuid('tag_id').notNull().references(() => tags.id, { onDelete: 'cascade' }),
}, (t) => ({
pk: primaryKey({ columns: [t.projectId, t.tagId] }),
}));
```
### 3.4 Table `projectCollaborators`
Relation many-to-many entre les projets et les utilisateurs pour la collaboration.
```typescript
// src/database/schema/projectCollaborators.ts
import { pgTable, uuid, primaryKey, varchar } from 'drizzle-orm/pg-core';
import { projects } from './projects';
import { users } from './users';
export const projectCollaborators = pgTable('project_collaborators', {
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
role: varchar('role', { length: 50 }).notNull().default('VIEWER'),
}, (t) => ({
pk: primaryKey({ columns: [t.projectId, t.userId] }),
}));
```
## 4. Fichier d'Index
Fichier qui exporte toutes les tables pour faciliter leur utilisation.
```typescript
// src/database/schema/index.ts
export * from './users';
export * from './projects';
export * from './persons';
export * from './groups';
export * from './tags';
export * from './personToGroup';
export * from './personToTag';
export * from './projectToTag';
export * from './projectCollaborators';
```
## 5. Configuration de DrizzleORM
### 5.1 Module de Base de Données
```typescript
// src/database/database.module.ts
import { Module, Global } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as schema from './schema';
@Global()
@Module({
imports: [ConfigModule],
providers: [
{
provide: 'DATABASE_CONNECTION',
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
const connectionString = configService.get<string>('DATABASE_URL');
const pool = new Pool({ connectionString });
return drizzle(pool, { schema });
},
},
],
exports: ['DATABASE_CONNECTION'],
})
export class DatabaseModule {}
```
## 6. Migrations
### 6.1 Configuration des Migrations
```typescript
// drizzle.config.ts
import type { Config } from 'drizzle-kit';
import * as dotenv from 'dotenv';
dotenv.config();
export default {
schema: './src/database/schema/*.ts',
out: './src/database/migrations',
driver: 'pg',
dbCredentials: {
connectionString: process.env.DATABASE_URL,
},
} satisfies Config;
```
### 6.2 Script de Migration
```typescript
// src/database/migrations/migrate.ts
import { drizzle } from 'drizzle-orm/node-postgres';
import { migrate } from 'drizzle-orm/node-postgres/migrator';
import { Pool } from 'pg';
import * as dotenv from 'dotenv';
dotenv.config();
async function main() {
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle(pool);
console.log('Running migrations...');
await migrate(db, { migrationsFolder: './src/database/migrations' });
console.log('Migrations completed successfully!');
await pool.end();
}
main().catch((err) => {
console.error('Migration failed!', err);
process.exit(1);
});
```
## 7. Optimisations
### 7.1 Indexation
Pour optimiser les performances des requêtes fréquentes, nous ajouterons des index sur les colonnes suivantes :
```typescript
// Exemple d'ajout d'index sur la table persons
import { pgTable, uuid, varchar, text, integer, timestamp, jsonb, index } from 'drizzle-orm/pg-core';
import { projects } from './projects';
export const persons = pgTable('persons', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }),
technicalLevel: integer('technical_level'),
gender: varchar('gender', { length: 50 }),
attributes: jsonb('attributes').notNull().default({}),
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
}, (table) => ({
nameIdx: index('person_name_idx').on(table.name),
projectIdIdx: index('person_project_id_idx').on(table.projectId),
technicalLevelIdx: index('person_technical_level_idx').on(table.technicalLevel),
}));
```
### 7.2 Types de Données Optimisés
Utilisation de types de données optimisés pour réduire l'espace de stockage et améliorer les performances :
- `uuid` pour les identifiants uniques
- `varchar` avec longueur limitée pour les chaînes de caractères
- `jsonb` pour les données structurées flexibles
- `timestamp` pour les dates et heures
### 7.3 Contraintes d'Intégrité
Utilisation de contraintes d'intégrité référentielle pour garantir la cohérence des données :
- Clés primaires sur toutes les tables
- Clés étrangères avec actions en cascade pour la suppression
- Contraintes d'unicité sur les colonnes appropriées
## 8. Requêtes Communes
### 8.1 Récupération d'un Projet avec ses Personnes et Groupes
```typescript
// Exemple de requête pour récupérer un projet avec ses personnes et groupes
const getProjectWithPersonsAndGroups = async (db, projectId) => {
const project = await db.query.projects.findFirst({
where: (projects, { eq }) => eq(projects.id, projectId),
with: {
persons: true,
groups: {
with: {
persons: true,
},
},
},
});
return project;
};
```
### 8.2 Récupération des Personnes avec leurs Tags
```typescript
// Exemple de requête pour récupérer les personnes avec leurs tags
const getPersonsWithTags = async (db, projectId) => {
const persons = await db.query.persons.findMany({
where: (persons, { eq }) => eq(persons.projectId, projectId),
with: {
tags: {
columns: {
name: true,
color: true,
},
},
},
});
return persons;
};
```
## 9. Conclusion
Ce schéma de base de données fournit une structure solide pour l'application de création de groupes, avec une conception qui prend en compte les performances, la flexibilité et l'intégrité des données. Les relations entre les entités sont clairement définies, et les types de données sont optimisés pour les besoins de l'application.
L'utilisation de DrizzleORM permet une intégration transparente avec NestJS et offre une expérience de développement type-safe, facilitant la maintenance et l'évolution du schéma au fil du temps.

View File

@ -0,0 +1,33 @@
# Implementation Guides
This directory contains detailed implementation guides for different aspects of the "Application de Création de Groupes" project.
## Available Guides
1. [Backend Implementation Plan](../BACKEND_IMPLEMENTATION_PLAN.md) - Comprehensive plan for implementing the backend of the application
2. [Authentication Implementation Plan](../AUTH_IMPLEMENTATION_PLAN.md) - Detailed guide for implementing OAuth 2.0 authentication with GitHub
3. [Database Schema Plan](../DATABASE_SCHEMA_PLAN.md) - Plan for implementing the database schema with DrizzleORM
4. [WebSocket Implementation Plan](../WEBSOCKET_IMPLEMENTATION_PLAN.md) - Guide for implementing real-time communication with Socket.IO
## How to Use These Guides
These implementation guides are designed to be followed in a specific order:
1. Start with the **Database Schema Plan** to set up the foundation of your data model
2. Follow the **Backend Implementation Plan** to create the basic structure of your NestJS application
3. Implement the **Authentication Implementation Plan** to add user authentication
4. Finally, add real-time capabilities using the **WebSocket Implementation Plan**
Each guide includes:
- Detailed steps for implementation
- Code examples
- Configuration instructions
- Best practices
## Recommended Development Approach
1. **Iterative Development**: Implement features incrementally, starting with the core functionality
2. **Test-Driven Development**: Write tests before implementing features
3. **Continuous Integration**: Set up CI/CD pipelines to automate testing and deployment
4. **Code Reviews**: Have team members review code changes before merging
5. **Documentation**: Keep documentation up-to-date as the implementation progresses

View File

@ -0,0 +1,900 @@
# Plan d'Implémentation des WebSockets
Ce document détaille le plan d'implémentation du système de communication en temps réel avec WebSockets pour l'application de création de groupes, basé sur les spécifications du cahier des charges.
## 1. Vue d'Ensemble
L'application utilisera Socket.IO pour la communication en temps réel entre les clients et le serveur. Cette approche permettra :
- La collaboration en temps réel entre les utilisateurs travaillant sur le même projet
- Les notifications instantanées pour les actions importantes
- La mise à jour automatique de l'interface utilisateur lorsque des changements sont effectués par d'autres utilisateurs
## 2. Architecture WebSocket
```mermaid
flowchart TB
subgraph Client["Clients"]
C1["Client 1"]
C2["Client 2"]
C3["Client 3"]
end
subgraph Server["Serveur NestJS"]
GW["WebSocket Gateway"]
AS["Authentication Service"]
PS["Project Service"]
GS["Group Service"]
NS["Notification Service"]
end
C1 <--> GW
C2 <--> GW
C3 <--> GW
GW <--> AS
GW <--> PS
GW <--> GS
GW <--> NS
```
## 3. Configuration de Socket.IO avec NestJS
### 3.1 Installation des Dépendances
```bash
pnpm add @nestjs/websockets @nestjs/platform-socket.io socket.io
pnpm add -D @types/socket.io
```
### 3.2 Module WebSockets
```typescript
// src/modules/websockets/websockets.module.ts
import { Module } from '@nestjs/common';
import { ProjectsGateway } from './gateways/projects.gateway';
import { GroupsGateway } from './gateways/groups.gateway';
import { NotificationsGateway } from './gateways/notifications.gateway';
import { WebSocketService } from './services/websocket.service';
import { AuthModule } from '../auth/auth.module';
import { ProjectsModule } from '../projects/projects.module';
import { GroupsModule } from '../groups/groups.module';
@Module({
imports: [AuthModule, ProjectsModule, GroupsModule],
providers: [
ProjectsGateway,
GroupsGateway,
NotificationsGateway,
WebSocketService,
],
exports: [WebSocketService],
})
export class WebSocketsModule {}
```
## 4. Service WebSocket
Le service WebSocket sera responsable de la gestion des connexions et des salles.
```typescript
// src/modules/websockets/services/websocket.service.ts
import { Injectable } from '@nestjs/common';
import { Server, Socket } from 'socket.io';
@Injectable()
export class WebSocketService {
private server: Server;
setServer(server: Server) {
this.server = server;
}
getServer(): Server {
return this.server;
}
joinRoom(socket: Socket, room: string) {
socket.join(room);
}
leaveRoom(socket: Socket, room: string) {
socket.leave(room);
}
emitToRoom(room: string, event: string, data: any) {
this.server.to(room).emit(event, data);
}
emitToAll(event: string, data: any) {
this.server.emit(event, data);
}
emitToUser(userId: string, event: string, data: any) {
this.emitToRoom(`user:${userId}`, event, data);
}
emitToProject(projectId: string, event: string, data: any) {
this.emitToRoom(`project:${projectId}`, event, data);
}
emitToGroup(groupId: string, event: string, data: any) {
this.emitToRoom(`group:${groupId}`, event, data);
}
}
```
## 5. Gateways WebSocket
### 5.1 Gateway de Base
```typescript
// src/modules/websockets/gateways/base.gateway.ts
import {
WebSocketGateway,
OnGatewayInit,
OnGatewayConnection,
OnGatewayDisconnect,
WebSocketServer,
} from '@nestjs/websockets';
import { Logger } from '@nestjs/common';
import { Server, Socket } from 'socket.io';
import { WebSocketService } from '../services/websocket.service';
import { AuthService } from '../../auth/services/auth.service';
@WebSocketGateway({
cors: {
origin: process.env.CORS_ORIGIN,
credentials: true,
},
})
export class BaseGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server;
protected readonly logger = new Logger(this.constructor.name);
constructor(
protected readonly webSocketService: WebSocketService,
protected readonly authService: AuthService,
) {}
afterInit(server: Server) {
this.webSocketService.setServer(server);
this.logger.log('WebSocket Gateway initialized');
}
async handleConnection(client: Socket) {
try {
const token = client.handshake.auth.token;
if (!token) {
client.disconnect();
return;
}
const user = await this.authService.validateToken(token);
if (!user) {
client.disconnect();
return;
}
client.data.user = user;
// Rejoindre la salle personnelle de l'utilisateur
this.webSocketService.joinRoom(client, `user:${user.id}`);
this.logger.log(`Client connected: ${client.id}, User: ${user.id}`);
} catch (error) {
this.logger.error(`Connection error: ${error.message}`);
client.disconnect();
}
}
handleDisconnect(client: Socket) {
this.logger.log(`Client disconnected: ${client.id}`);
}
getUserFromSocket(client: Socket) {
return client.data.user;
}
}
```
### 5.2 Gateway Projets
```typescript
// src/modules/websockets/gateways/projects.gateway.ts
import { SubscribeMessage, MessageBody, ConnectedSocket } from '@nestjs/websockets';
import { Socket } from 'socket.io';
import { BaseGateway } from './base.gateway';
import { WebSocketService } from '../services/websocket.service';
import { AuthService } from '../../auth/services/auth.service';
import { ProjectsService } from '../../projects/services/projects.service';
export class ProjectsGateway extends BaseGateway {
constructor(
protected readonly webSocketService: WebSocketService,
protected readonly authService: AuthService,
private readonly projectsService: ProjectsService,
) {
super(webSocketService, authService);
}
@SubscribeMessage('project:join')
async handleJoinProject(
@ConnectedSocket() client: Socket,
@MessageBody() data: { projectId: string },
) {
try {
const user = this.getUserFromSocket(client);
const { projectId } = data;
// Vérifier si l'utilisateur a accès au projet
const hasAccess = await this.projectsService.hasAccess(projectId, user.id);
if (!hasAccess) {
return { error: 'Access denied' };
}
// Rejoindre la salle du projet
this.webSocketService.joinRoom(client, `project:${projectId}`);
this.logger.log(`User ${user.id} joined project ${projectId}`);
return { success: true };
} catch (error) {
this.logger.error(`Error joining project: ${error.message}`);
return { error: 'Failed to join project' };
}
}
@SubscribeMessage('project:leave')
handleLeaveProject(
@ConnectedSocket() client: Socket,
@MessageBody() data: { projectId: string },
) {
try {
const user = this.getUserFromSocket(client);
const { projectId } = data;
// Quitter la salle du projet
this.webSocketService.leaveRoom(client, `project:${projectId}`);
this.logger.log(`User ${user.id} left project ${projectId}`);
return { success: true };
} catch (error) {
this.logger.error(`Error leaving project: ${error.message}`);
return { error: 'Failed to leave project' };
}
}
@SubscribeMessage('project:update')
async handleUpdateProject(
@ConnectedSocket() client: Socket,
@MessageBody() data: { projectId: string, changes: any },
) {
try {
const user = this.getUserFromSocket(client);
const { projectId, changes } = data;
// Vérifier si l'utilisateur a accès au projet
const hasAccess = await this.projectsService.hasAccess(projectId, user.id);
if (!hasAccess) {
return { error: 'Access denied' };
}
// Émettre l'événement de mise à jour à tous les clients dans la salle du projet
this.webSocketService.emitToProject(projectId, 'project:updated', {
projectId,
changes,
updatedBy: user.id,
});
return { success: true };
} catch (error) {
this.logger.error(`Error updating project: ${error.message}`);
return { error: 'Failed to update project' };
}
}
}
```
### 5.3 Gateway Groupes
```typescript
// src/modules/websockets/gateways/groups.gateway.ts
import { SubscribeMessage, MessageBody, ConnectedSocket } from '@nestjs/websockets';
import { Socket } from 'socket.io';
import { BaseGateway } from './base.gateway';
import { WebSocketService } from '../services/websocket.service';
import { AuthService } from '../../auth/services/auth.service';
import { GroupsService } from '../../groups/services/groups.service';
import { ProjectsService } from '../../projects/services/projects.service';
export class GroupsGateway extends BaseGateway {
constructor(
protected readonly webSocketService: WebSocketService,
protected readonly authService: AuthService,
private readonly groupsService: GroupsService,
private readonly projectsService: ProjectsService,
) {
super(webSocketService, authService);
}
@SubscribeMessage('group:join')
async handleJoinGroup(
@ConnectedSocket() client: Socket,
@MessageBody() data: { groupId: string },
) {
try {
const user = this.getUserFromSocket(client);
const { groupId } = data;
// Récupérer le groupe et vérifier si l'utilisateur a accès au projet associé
const group = await this.groupsService.findById(groupId);
if (!group) {
return { error: 'Group not found' };
}
const hasAccess = await this.projectsService.hasAccess(group.projectId, user.id);
if (!hasAccess) {
return { error: 'Access denied' };
}
// Rejoindre la salle du groupe
this.webSocketService.joinRoom(client, `group:${groupId}`);
this.logger.log(`User ${user.id} joined group ${groupId}`);
return { success: true };
} catch (error) {
this.logger.error(`Error joining group: ${error.message}`);
return { error: 'Failed to join group' };
}
}
@SubscribeMessage('group:leave')
handleLeaveGroup(
@ConnectedSocket() client: Socket,
@MessageBody() data: { groupId: string },
) {
try {
const user = this.getUserFromSocket(client);
const { groupId } = data;
// Quitter la salle du groupe
this.webSocketService.leaveRoom(client, `group:${groupId}`);
this.logger.log(`User ${user.id} left group ${groupId}`);
return { success: true };
} catch (error) {
this.logger.error(`Error leaving group: ${error.message}`);
return { error: 'Failed to leave group' };
}
}
@SubscribeMessage('group:update')
async handleUpdateGroup(
@ConnectedSocket() client: Socket,
@MessageBody() data: { groupId: string, changes: any },
) {
try {
const user = this.getUserFromSocket(client);
const { groupId, changes } = data;
// Récupérer le groupe et vérifier si l'utilisateur a accès au projet associé
const group = await this.groupsService.findById(groupId);
if (!group) {
return { error: 'Group not found' };
}
const hasAccess = await this.projectsService.hasAccess(group.projectId, user.id);
if (!hasAccess) {
return { error: 'Access denied' };
}
// Émettre l'événement de mise à jour à tous les clients dans la salle du groupe
this.webSocketService.emitToGroup(groupId, 'group:updated', {
groupId,
changes,
updatedBy: user.id,
});
// Émettre également l'événement au niveau du projet
this.webSocketService.emitToProject(group.projectId, 'group:updated', {
groupId,
changes,
updatedBy: user.id,
});
return { success: true };
} catch (error) {
this.logger.error(`Error updating group: ${error.message}`);
return { error: 'Failed to update group' };
}
}
@SubscribeMessage('group:addPerson')
async handleAddPersonToGroup(
@ConnectedSocket() client: Socket,
@MessageBody() data: { groupId: string, personId: string },
) {
try {
const user = this.getUserFromSocket(client);
const { groupId, personId } = data;
// Récupérer le groupe et vérifier si l'utilisateur a accès au projet associé
const group = await this.groupsService.findById(groupId);
if (!group) {
return { error: 'Group not found' };
}
const hasAccess = await this.projectsService.hasAccess(group.projectId, user.id);
if (!hasAccess) {
return { error: 'Access denied' };
}
// Émettre l'événement d'ajout de personne à tous les clients dans la salle du groupe
this.webSocketService.emitToGroup(groupId, 'group:personAdded', {
groupId,
personId,
addedBy: user.id,
});
// Émettre également l'événement au niveau du projet
this.webSocketService.emitToProject(group.projectId, 'group:personAdded', {
groupId,
personId,
addedBy: user.id,
});
return { success: true };
} catch (error) {
this.logger.error(`Error adding person to group: ${error.message}`);
return { error: 'Failed to add person to group' };
}
}
@SubscribeMessage('group:removePerson')
async handleRemovePersonFromGroup(
@ConnectedSocket() client: Socket,
@MessageBody() data: { groupId: string, personId: string },
) {
try {
const user = this.getUserFromSocket(client);
const { groupId, personId } = data;
// Récupérer le groupe et vérifier si l'utilisateur a accès au projet associé
const group = await this.groupsService.findById(groupId);
if (!group) {
return { error: 'Group not found' };
}
const hasAccess = await this.projectsService.hasAccess(group.projectId, user.id);
if (!hasAccess) {
return { error: 'Access denied' };
}
// Émettre l'événement de suppression de personne à tous les clients dans la salle du groupe
this.webSocketService.emitToGroup(groupId, 'group:personRemoved', {
groupId,
personId,
removedBy: user.id,
});
// Émettre également l'événement au niveau du projet
this.webSocketService.emitToProject(group.projectId, 'group:personRemoved', {
groupId,
personId,
removedBy: user.id,
});
return { success: true };
} catch (error) {
this.logger.error(`Error removing person from group: ${error.message}`);
return { error: 'Failed to remove person from group' };
}
}
}
```
### 5.4 Gateway Notifications
```typescript
// src/modules/websockets/gateways/notifications.gateway.ts
import { SubscribeMessage, MessageBody, ConnectedSocket } from '@nestjs/websockets';
import { Socket } from 'socket.io';
import { BaseGateway } from './base.gateway';
import { WebSocketService } from '../services/websocket.service';
import { AuthService } from '../../auth/services/auth.service';
export class NotificationsGateway extends BaseGateway {
constructor(
protected readonly webSocketService: WebSocketService,
protected readonly authService: AuthService,
) {
super(webSocketService, authService);
}
@SubscribeMessage('notification:read')
handleReadNotification(
@ConnectedSocket() client: Socket,
@MessageBody() data: { notificationId: string },
) {
try {
const user = this.getUserFromSocket(client);
const { notificationId } = data;
// Logique pour marquer la notification comme lue
this.logger.log(`User ${user.id} read notification ${notificationId}`);
return { success: true };
} catch (error) {
this.logger.error(`Error reading notification: ${error.message}`);
return { error: 'Failed to read notification' };
}
}
// Méthode pour envoyer une notification à un utilisateur spécifique
sendNotificationToUser(userId: string, notification: any) {
this.webSocketService.emitToUser(userId, 'notification:new', notification);
}
// Méthode pour envoyer une notification à tous les utilisateurs d'un projet
sendNotificationToProject(projectId: string, notification: any) {
this.webSocketService.emitToProject(projectId, 'notification:new', notification);
}
}
```
## 6. Intégration avec les Services Existants
### 6.1 Service Projets
```typescript
// src/modules/projects/services/projects.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { WebSocketService } from '../../websockets/services/websocket.service';
@Injectable()
export class ProjectsService {
constructor(
// Autres injections...
private readonly webSocketService: WebSocketService,
) {}
// Méthodes existantes...
async update(id: string, data: any, userId: string) {
// Logique de mise à jour du projet
// Notification en temps réel
this.webSocketService.emitToProject(id, 'project:updated', {
projectId: id,
changes: data,
updatedBy: userId,
});
return updatedProject;
}
async addCollaborator(projectId: string, collaboratorId: string, role: string, userId: string) {
// Logique d'ajout de collaborateur
// Notification en temps réel
this.webSocketService.emitToProject(projectId, 'project:collaboratorAdded', {
projectId,
collaboratorId,
role,
addedBy: userId,
});
// Notification personnelle au collaborateur
this.webSocketService.emitToUser(collaboratorId, 'notification:new', {
type: 'PROJECT_INVITATION',
projectId,
invitedBy: userId,
role,
});
return result;
}
}
```
### 6.2 Service Groupes
```typescript
// src/modules/groups/services/groups.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { WebSocketService } from '../../websockets/services/websocket.service';
@Injectable()
export class GroupsService {
constructor(
// Autres injections...
private readonly webSocketService: WebSocketService,
) {}
// Méthodes existantes...
async create(data: any, userId: string) {
// Logique de création de groupe
// Notification en temps réel
this.webSocketService.emitToProject(data.projectId, 'group:created', {
groupId: createdGroup.id,
group: createdGroup,
createdBy: userId,
});
return createdGroup;
}
async addPerson(groupId: string, personId: string, userId: string) {
// Logique d'ajout de personne au groupe
const group = await this.findById(groupId);
// Notification en temps réel
this.webSocketService.emitToGroup(groupId, 'group:personAdded', {
groupId,
personId,
addedBy: userId,
});
this.webSocketService.emitToProject(group.projectId, 'group:personAdded', {
groupId,
personId,
addedBy: userId,
});
return result;
}
}
```
## 7. Intégration avec le Frontend
### 7.1 Configuration du Client Socket.IO
```typescript
// frontend/lib/socket.ts
import { io, Socket } from 'socket.io-client';
import { useEffect, useState } from 'react';
let socket: Socket | null = null;
export const initializeSocket = (token: string) => {
if (socket) {
socket.disconnect();
}
socket = io(process.env.NEXT_PUBLIC_API_URL, {
auth: { token },
withCredentials: true,
});
return socket;
};
export const getSocket = () => {
return socket;
};
export const disconnectSocket = () => {
if (socket) {
socket.disconnect();
socket = null;
}
};
export const useSocket = (token: string | null) => {
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
if (!token) {
disconnectSocket();
setIsConnected(false);
return;
}
const socketInstance = initializeSocket(token);
socketInstance.on('connect', () => {
setIsConnected(true);
});
socketInstance.on('disconnect', () => {
setIsConnected(false);
});
return () => {
socketInstance.off('connect');
socketInstance.off('disconnect');
};
}, [token]);
return { socket, isConnected };
};
```
### 7.2 Hook pour les Projets
```typescript
// frontend/hooks/useProjectSocket.ts
import { useEffect, useState } from 'react';
import { getSocket } from '../lib/socket';
export const useProjectSocket = (projectId: string | null) => {
const [isJoined, setIsJoined] = useState(false);
const socket = getSocket();
useEffect(() => {
if (!socket || !projectId) {
setIsJoined(false);
return;
}
// Rejoindre la salle du projet
socket.emit('project:join', { projectId }, (response) => {
if (response.success) {
setIsJoined(true);
} else {
console.error('Failed to join project:', response.error);
}
});
// Quitter la salle du projet lors du démontage
return () => {
socket.emit('project:leave', { projectId });
setIsJoined(false);
};
}, [socket, projectId]);
const updateProject = (changes: any) => {
if (!socket || !projectId || !isJoined) {
return Promise.reject('Not connected to project');
}
return new Promise((resolve, reject) => {
socket.emit('project:update', { projectId, changes }, (response) => {
if (response.success) {
resolve(response);
} else {
reject(response.error);
}
});
});
};
return { isJoined, updateProject };
};
```
### 7.3 Hook pour les Groupes
```typescript
// frontend/hooks/useGroupSocket.ts
import { useEffect, useState } from 'react';
import { getSocket } from '../lib/socket';
export const useGroupSocket = (groupId: string | null) => {
const [isJoined, setIsJoined] = useState(false);
const socket = getSocket();
useEffect(() => {
if (!socket || !groupId) {
setIsJoined(false);
return;
}
// Rejoindre la salle du groupe
socket.emit('group:join', { groupId }, (response) => {
if (response.success) {
setIsJoined(true);
} else {
console.error('Failed to join group:', response.error);
}
});
// Quitter la salle du groupe lors du démontage
return () => {
socket.emit('group:leave', { groupId });
setIsJoined(false);
};
}, [socket, groupId]);
const updateGroup = (changes: any) => {
if (!socket || !groupId || !isJoined) {
return Promise.reject('Not connected to group');
}
return new Promise((resolve, reject) => {
socket.emit('group:update', { groupId, changes }, (response) => {
if (response.success) {
resolve(response);
} else {
reject(response.error);
}
});
});
};
const addPersonToGroup = (personId: string) => {
if (!socket || !groupId || !isJoined) {
return Promise.reject('Not connected to group');
}
return new Promise((resolve, reject) => {
socket.emit('group:addPerson', { groupId, personId }, (response) => {
if (response.success) {
resolve(response);
} else {
reject(response.error);
}
});
});
};
const removePersonFromGroup = (personId: string) => {
if (!socket || !groupId || !isJoined) {
return Promise.reject('Not connected to group');
}
return new Promise((resolve, reject) => {
socket.emit('group:removePerson', { groupId, personId }, (response) => {
if (response.success) {
resolve(response);
} else {
reject(response.error);
}
});
});
};
return { isJoined, updateGroup, addPersonToGroup, removePersonFromGroup };
};
```
### 7.4 Hook pour les Notifications
```typescript
// frontend/hooks/useNotifications.ts
import { useEffect, useState } from 'react';
import { getSocket } from '../lib/socket';
export const useNotifications = () => {
const [notifications, setNotifications] = useState([]);
const socket = getSocket();
useEffect(() => {
if (!socket) {
return;
}
// Écouter les nouvelles notifications
socket.on('notification:new', (notification) => {
setNotifications((prev) => [notification, ...prev]);
});
return () => {
socket.off('notification:new');
};
}, [socket]);
const markAsRead = (notificationId: string) => {
if (!socket) {