brief-20/IMPLEMENTATION_GUIDE.md
Avnyr ef934a8599 docs: add implementation plans for authentication and database schema
Documented comprehensive implementation plans for the authentication system and database schema, including architecture, module structure, API integration, security measures, and GDPR compliance details.
2025-05-15 13:10:00 +02:00

21 KiB

Guide d'Implémentation du Backend

Ce document présente un guide complet pour l'implémentation du backend de l'application de création de groupes, basé sur les spécifications du cahier des charges et les plans détaillés précédemment établis.

Table des Matières

  1. Vue d'Ensemble
  2. Préparation de l'Environnement
  3. Structure du Projet
  4. Configuration de Base
  5. Base de Données
  6. Authentification
  7. Modules Fonctionnels
  8. Communication en Temps Réel
  9. Sécurité et Conformité RGPD
  10. Tests et Documentation
  11. Déploiement
  12. Calendrier d'Implémentation

1. Vue d'Ensemble

L'application est une plateforme de création et de gestion de groupes qui permet aux utilisateurs de créer des groupes en prenant en compte divers paramètres et de conserver un historique des groupes précédemment créés.

1.1 Architecture Globale

L'application suit une architecture monorepo avec séparation claire entre le frontend et le backend :

  • Frontend : Application Next.js avec App Router et Server Components
  • Backend : API NestJS avec PostgreSQL et DrizzleORM
  • Communication : API REST pour les opérations CRUD et WebSockets pour les mises à jour en temps réel
  • Authentification : OAuth 2.0 avec GitHub et JWT pour la gestion des sessions

2. Préparation de l'Environnement

2.1 Installation des Dépendances

# Installation des 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

# Installation des dépendances de développement
pnpm add -D drizzle-kit
pnpm add -D @types/passport-github2 @types/socket.io @types/pg @types/uuid

2.2 Configuration de l'Environnement

Créer un fichier .env.example à la racine du projet backend :

# 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
FRONTEND_URL=http://localhost:3000

3. Structure du Projet

La structure du projet backend suivra l'organisation suivante :

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
│   │   ├── users/                   # Module de gestion des utilisateurs
│   │   ├── projects/                # Module de gestion des projets
│   │   ├── persons/                 # Module de gestion des personnes
│   │   ├── groups/                  # Module de gestion des groupes
│   │   ├── tags/                    # Module de gestion des tags
│   │   └── websockets/              # Module de gestion des 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

4. Configuration de Base

4.1 Point d'Entrée de l'Application

Mettre à jour le fichier src/main.ts :

import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const configService = app.get(ConfigService);
  
  // Configuration globale des pipes de validation
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      transform: true,
      forbidNonWhitelisted: true,
    }),
  );
  
  // Configuration CORS
  app.enableCors({
    origin: configService.get<string>('CORS_ORIGIN'),
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
    credentials: true,
  });
  
  // Préfixe global pour les routes API
  app.setGlobalPrefix(configService.get<string>('API_PREFIX', 'api'));
  
  const port = configService.get<number>('PORT', 3000);
  await app.listen(port);
  console.log(`Application is running on: http://localhost:${port}`);
}
bootstrap();

4.2 Module Principal

Mettre à jour le fichier 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 { DatabaseModule } from './database/database.module';
import { AuthModule } from './modules/auth/auth.module';
import { UsersModule } from './modules/users/users.module';
import { ProjectsModule } from './modules/projects/projects.module';
import { PersonsModule } from './modules/persons/persons.module';
import { GroupsModule } from './modules/groups/groups.module';
import { TagsModule } from './modules/tags/tags.module';
import { WebSocketsModule } from './modules/websockets/websockets.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,
    ProjectsModule,
    PersonsModule,
    GroupsModule,
    TagsModule,
    WebSocketsModule,
  ],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: JwtAuthGuard,
    },
  ],
})
export class AppModule {}

4.3 Validation des Variables d'Environnement

Créer le fichier src/config/env.validation.ts :

import { plainToClass } from 'class-transformer';
import { IsEnum, IsNumber, IsString, validateSync } from 'class-validator';

enum Environment {
  Development = 'development',
  Production = 'production',
  Test = 'test',
}

class EnvironmentVariables {
  @IsEnum(Environment)
  NODE_ENV: Environment;

  @IsNumber()
  PORT: number;

  @IsString()
  API_PREFIX: string;

  @IsString()
  DATABASE_URL: string;

  @IsString()
  GITHUB_CLIENT_ID: string;

  @IsString()
  GITHUB_CLIENT_SECRET: string;

  @IsString()
  GITHUB_CALLBACK_URL: string;

  @IsString()
  JWT_ACCESS_SECRET: string;

  @IsString()
  JWT_REFRESH_SECRET: string;

  @IsString()
  JWT_ACCESS_EXPIRATION: string;

  @IsString()
  JWT_REFRESH_EXPIRATION: string;

  @IsString()
  CORS_ORIGIN: string;

  @IsString()
  FRONTEND_URL: string;
}

export function validate(config: Record<string, unknown>) {
  const validatedConfig = plainToClass(
    EnvironmentVariables,
    {
      ...config,
      PORT: config.PORT ? parseInt(config.PORT as string, 10) : 3000,
    },
    { enableImplicitConversion: true },
  );

  const errors = validateSync(validatedConfig, {
    skipMissingProperties: false,
  });

  if (errors.length > 0) {
    throw new Error(errors.toString());
  }
  return validatedConfig;
}

5. Base de Données

5.1 Configuration de DrizzleORM

Créer le fichier drizzle.config.ts à la racine du projet backend :

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 || 'postgres://postgres:postgres@localhost:5432/groupmaker',
  },
  verbose: true,
  strict: true,
} satisfies Config;

5.2 Module de Base de Données

Créer le fichier src/database/database.module.ts :

import { Module, Global } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { Pool } from 'pg';
import { drizzle } from 'drizzle-orm/node-postgres';
import * as schema from './schema';

export const DATABASE_POOL = 'DATABASE_POOL';
export const DRIZZLE = 'DRIZZLE';

@Global()
@Module({
  imports: [ConfigModule],
  providers: [
    {
      provide: DATABASE_POOL,
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => {
        const pool = new Pool({
          connectionString: configService.get<string>('DATABASE_URL'),
        });
        
        // Test the connection
        const client = await pool.connect();
        try {
          await client.query('SELECT NOW()');
          console.log('Database connection established successfully');
        } finally {
          client.release();
        }
        
        return pool;
      },
    },
    {
      provide: DRIZZLE,
      inject: [DATABASE_POOL],
      useFactory: (pool: Pool) => {
        return drizzle(pool, { schema });
      },
    },
  ],
  exports: [DATABASE_POOL, DRIZZLE],
})
export class DatabaseModule {}

5.3 Script de Migration

Créer le fichier src/database/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();

const main = async () => {
  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');
  console.error(err);
  process.exit(1);
});

5.4 Schéma de Base de Données

Créer les fichiers de schéma dans le dossier src/database/schema/ selon le plan détaillé dans le document DATABASE_SCHEMA_PLAN.md.

5.5 Scripts pour les Migrations

Ajouter les scripts suivants au package.json du backend :

{
  "scripts": {
    "db:generate": "drizzle-kit generate:pg",
    "db:migrate": "ts-node src/database/migrate.ts",
    "db:studio": "drizzle-kit studio"
  }
}

6. Authentification

6.1 Module d'Authentification

Créer le fichier src/modules/auth/auth.module.ts selon le plan détaillé dans le document AUTH_IMPLEMENTATION_PLAN.md.

6.2 Stratégies d'Authentification

Implémenter les stratégies d'authentification (GitHub, JWT, JWT Refresh) selon le plan détaillé dans le document AUTH_IMPLEMENTATION_PLAN.md.

6.3 Service d'Authentification

Implémenter le service d'authentification selon le plan détaillé dans le document AUTH_IMPLEMENTATION_PLAN.md.

6.4 Contrôleur d'Authentification

Implémenter le contrôleur d'authentification selon le plan détaillé dans le document AUTH_IMPLEMENTATION_PLAN.md.

6.5 Guards et Décorateurs

Implémenter les guards et décorateurs d'authentification selon le plan détaillé dans le document AUTH_IMPLEMENTATION_PLAN.md.

7. Modules Fonctionnels

7.1 Module Utilisateurs

7.1.1 Service Utilisateurs

// src/modules/users/services/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { DRIZZLE } from '../../../database/database.module';
import { Inject } from '@nestjs/common';
import { eq } from 'drizzle-orm';
import * as schema from '../../../database/schema';
import { CreateUserDto } from '../dto/create-user.dto';
import { UpdateUserDto } from '../dto/update-user.dto';

@Injectable()
export class UsersService {
  constructor(@Inject(DRIZZLE) private readonly db: any) {}

  async create(createUserDto: CreateUserDto) {
    const [user] = await this.db
      .insert(schema.users)
      .values(createUserDto)
      .returning();
    return user;
  }

  async findAll() {
    return this.db.select().from(schema.users);
  }

  async findById(id: string) {
    const [user] = await this.db
      .select()
      .from(schema.users)
      .where(eq(schema.users.id, id));
    
    if (!user) {
      throw new NotFoundException(`User with ID ${id} not found`);
    }
    
    return user;
  }

  async findByGithubId(githubId: string) {
    const [user] = await this.db
      .select()
      .from(schema.users)
      .where(eq(schema.users.githubId, githubId));
    
    return user;
  }

  async update(id: string, updateUserDto: UpdateUserDto) {
    const [user] = await this.db
      .update(schema.users)
      .set({
        ...updateUserDto,
        updatedAt: new Date(),
      })
      .where(eq(schema.users.id, id))
      .returning();
    
    if (!user) {
      throw new NotFoundException(`User with ID ${id} not found`);
    }
    
    return user;
  }

  async remove(id: string) {
    const [user] = await this.db
      .delete(schema.users)
      .where(eq(schema.users.id, id))
      .returning();
    
    if (!user) {
      throw new NotFoundException(`User with ID ${id} not found`);
    }
    
    return user;
  }

  async updateGdprConsent(id: string) {
    return this.update(id, { gdprTimestamp: new Date() });
  }

  async exportUserData(id: string) {
    const user = await this.findById(id);
    const projects = await this.db
      .select()
      .from(schema.projects)
      .where(eq(schema.projects.ownerId, id));
    
    return {
      user,
      projects,
    };
  }
}

7.1.2 Contrôleur Utilisateurs

// src/modules/users/controllers/users.controller.ts
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  UseGuards,
} from '@nestjs/common';
import { UsersService } from '../services/users.service';
import { CreateUserDto } from '../dto/create-user.dto';
import { UpdateUserDto } from '../dto/update-user.dto';
import { GetUser } from '../../auth/decorators/get-user.decorator';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../../auth/guards/roles.guard';
import { Roles } from '../../auth/decorators/roles.decorator';
import { Role } from '../../auth/enums/role.enum';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(Role.ADMIN)
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get()
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(Role.ADMIN)
  findAll() {
    return this.usersService.findAll();
  }

  @Get('profile')
  @UseGuards(JwtAuthGuard)
  getProfile(@GetUser() user) {
    return user;
  }

  @Get(':id')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(Role.ADMIN)
  findOne(@Param('id') id: string) {
    return this.usersService.findById(id);
  }

  @Patch(':id')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(Role.ADMIN)
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(id, updateUserDto);
  }

  @Delete(':id')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(Role.ADMIN)
  remove(@Param('id') id: string) {
    return this.usersService.remove(id);
  }

  @Post('gdpr-consent')
  @UseGuards(JwtAuthGuard)
  updateGdprConsent(@GetUser('id') userId: string) {
    return this.usersService.updateGdprConsent(userId);
  }

  @Get('export-data')
  @UseGuards(JwtAuthGuard)
  exportUserData(@GetUser('id') userId: string) {
    return this.usersService.exportUserData(userId);
  }
}

7.2 Module Projets

Implémenter le module de gestion des projets avec les opérations CRUD et les relations avec les utilisateurs, les personnes et les groupes.

7.3 Module Personnes

Implémenter le module de gestion des personnes avec les opérations CRUD et les attributs spécifiés (niveau technique, genre, âge, etc.).

7.4 Module Groupes

Implémenter le module de gestion des groupes avec les opérations CRUD et les algorithmes de création automatique de groupes équilibrés.

7.5 Module Tags

Implémenter le module de gestion des tags avec les opérations CRUD et la gestion des types de tags (PROJECT, PERSON).

8. Communication en Temps Réel

8.1 Module WebSockets

Implémenter le module WebSockets selon le plan détaillé dans le document WEBSOCKET_IMPLEMENTATION_PLAN.md.

8.2 Gateways WebSocket

Implémenter les gateways WebSocket (Projets, Groupes, Notifications) selon le plan détaillé dans le document WEBSOCKET_IMPLEMENTATION_PLAN.md.

8.3 Service WebSocket

Implémenter le service WebSocket selon le plan détaillé dans le document WEBSOCKET_IMPLEMENTATION_PLAN.md.

9. Sécurité et Conformité RGPD

9.1 Sécurité

9.1.1 Protection contre les Attaques Courantes

  • Implémenter la protection CSRF pour les opérations sensibles
  • Configurer les en-têtes de sécurité (Content-Security-Policy, X-XSS-Protection, etc.)
  • Utiliser des paramètres préparés avec DrizzleORM pour prévenir les injections SQL
  • Mettre en place le rate limiting pour prévenir les attaques par force brute

9.1.2 Gestion des Tokens

  • Implémenter la révocation des tokens JWT
  • Configurer la rotation des clés de signature
  • Mettre en place la validation complète des tokens (signature, expiration, émetteur)

9.2 Conformité RGPD

9.2.1 Gestion du Consentement

  • Implémenter l'enregistrement du timestamp d'acceptation RGPD
  • Mettre en place le renouvellement du consentement tous les 13 mois

9.2.2 Droits des Utilisateurs

  • Implémenter l'export des données personnelles
  • Mettre en place la suppression de compte avec option de conservation ou suppression des projets

10. Tests et Documentation

10.1 Tests

10.1.1 Tests Unitaires

Écrire des tests unitaires pour les services et les contrôleurs en utilisant Jest.

10.1.2 Tests E2E

Développer des tests end-to-end pour les API en utilisant Supertest.

10.2 Documentation

10.2.1 Documentation API

Générer la documentation API avec Swagger en utilisant les décorateurs NestJS.

10.2.2 Documentation Technique

Documenter l'architecture, les modèles de données et les flux d'interaction.

11. Déploiement

11.1 Conteneurisation

Créer un Dockerfile pour le backend :

FROM node:20-alpine AS builder

WORKDIR /app

COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install

COPY . .
RUN pnpm build

FROM node:20-alpine

WORKDIR /app

COPY --from=builder /app/package.json /app/pnpm-lock.yaml ./
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules

EXPOSE 3000

CMD ["node", "dist/main"]

11.2 CI/CD

Configurer un workflow CI/CD avec GitHub Actions pour l'intégration et le déploiement continus.

12. 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