docs: ajout documentation technique complète (configuration, documents légaux, tickets, décisions, backlog Phase 2)
This commit is contained in:
parent
a5dae7a017
commit
d0827a119e
712
docs/21_CONFIGURATION-SYSTEME.md
Normal file
712
docs/21_CONFIGURATION-SYSTEME.md
Normal file
@ -0,0 +1,712 @@
|
|||||||
|
# 🔧 Documentation Technique - Configuration Système On-Premise
|
||||||
|
|
||||||
|
**Version** : 1.0
|
||||||
|
**Date** : 25 Novembre 2025
|
||||||
|
**Auteur** : Équipe PtitsPas
|
||||||
|
**Référence** : Architecture On-Premise
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Table des matières
|
||||||
|
|
||||||
|
1. [Vue d'ensemble](#vue-densemble)
|
||||||
|
2. [Architecture de configuration](#architecture-de-configuration)
|
||||||
|
3. [Table configuration](#table-configuration)
|
||||||
|
4. [Service Configuration](#service-configuration)
|
||||||
|
5. [Workflow Setup Initial](#workflow-setup-initial)
|
||||||
|
6. [APIs Configuration](#apis-configuration)
|
||||||
|
7. [Interface Admin](#interface-admin)
|
||||||
|
8. [Exemples de configuration](#exemples-de-configuration)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Vue d'ensemble
|
||||||
|
|
||||||
|
### Problématique
|
||||||
|
|
||||||
|
L'application P'titsPas est déployée **on-premise** chez différentes collectivités. Chaque collectivité a :
|
||||||
|
- Son propre serveur SMTP
|
||||||
|
- Ses propres ports et configurations réseau
|
||||||
|
- Son propre nom de domaine
|
||||||
|
- Sa propre charte graphique
|
||||||
|
|
||||||
|
**Solution** : Configuration dynamique stockée en base de données, modifiable via interface web.
|
||||||
|
|
||||||
|
### Principes
|
||||||
|
|
||||||
|
1. ✅ **Pas de hardcoding** : Aucune valeur en dur dans le code
|
||||||
|
2. ✅ **Pas de redéploiement** : Modification sans rebuild Docker
|
||||||
|
3. ✅ **Sécurité** : Mots de passe chiffrés en AES-256
|
||||||
|
4. ✅ **Traçabilité** : Qui a modifié quoi et quand
|
||||||
|
5. ✅ **Setup wizard** : Configuration guidée à la première connexion
|
||||||
|
6. ✅ **Validation** : Test SMTP avant sauvegarde
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ Architecture de configuration
|
||||||
|
|
||||||
|
### Flux de données
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ Application │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────┐ ┌──────────────┐ │
|
||||||
|
│ │ Frontend │─────▶│ Backend │ │
|
||||||
|
│ │ (Admin) │ │ ConfigAPI │ │
|
||||||
|
│ └──────────────┘ └──────┬───────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌──────────────┐ │
|
||||||
|
│ │ ConfigService│ │
|
||||||
|
│ │ (Cache) │ │
|
||||||
|
│ └──────┬───────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌──────────────┐ │
|
||||||
|
│ │ PostgreSQL │ │
|
||||||
|
│ │ configuration│ │
|
||||||
|
│ └──────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Composants
|
||||||
|
|
||||||
|
1. **Table `configuration`** : Stockage clé/valeur en BDD
|
||||||
|
2. **ConfigService** : Cache en mémoire + chiffrement
|
||||||
|
3. **ConfigAPI** : Endpoints REST pour CRUD
|
||||||
|
4. **Guard Setup** : Redirection forcée si config incomplète
|
||||||
|
5. **Interface Admin** : Formulaire de configuration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Table configuration
|
||||||
|
|
||||||
|
### Schéma SQL
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Table de configuration système (clé/valeur)
|
||||||
|
CREATE TABLE configuration (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
cle VARCHAR(100) UNIQUE NOT NULL, -- Clé unique (ex: 'smtp_host')
|
||||||
|
valeur TEXT, -- Valeur (peut être NULL)
|
||||||
|
type VARCHAR(50) NOT NULL, -- Type: 'string', 'number', 'boolean', 'json', 'encrypted'
|
||||||
|
categorie VARCHAR(50), -- Catégorie: 'email', 'app', 'security'
|
||||||
|
description TEXT, -- Description pour l'interface admin
|
||||||
|
modifie_le TIMESTAMPTZ DEFAULT now(), -- Date dernière modification
|
||||||
|
modifie_par UUID REFERENCES utilisateurs(id) -- Qui a modifié (traçabilité)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Index pour performance
|
||||||
|
CREATE INDEX idx_configuration_cle ON configuration(cle);
|
||||||
|
CREATE INDEX idx_configuration_categorie ON configuration(categorie);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Seed initial
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO configuration (cle, valeur, type, categorie, description) VALUES
|
||||||
|
-- === Configuration Email (SMTP) ===
|
||||||
|
('smtp_host', 'localhost', 'string', 'email', 'Serveur SMTP (ex: mail.mairie-bezons.fr, smtp.gmail.com)'),
|
||||||
|
('smtp_port', '25', 'number', 'email', 'Port SMTP (25, 465, 587)'),
|
||||||
|
('smtp_secure', 'false', 'boolean', 'email', 'Utiliser SSL/TLS (true pour port 465)'),
|
||||||
|
('smtp_auth_required', 'false', 'boolean', 'email', 'Authentification SMTP requise'),
|
||||||
|
('smtp_user', '', 'string', 'email', 'Utilisateur SMTP (si authentification requise)'),
|
||||||
|
('smtp_password', '', 'encrypted', 'email', 'Mot de passe SMTP (chiffré en AES-256)'),
|
||||||
|
('email_from_name', 'P''titsPas', 'string', 'email', 'Nom de l''expéditeur affiché dans les emails'),
|
||||||
|
('email_from_address', 'no-reply@ptits-pas.fr', 'string', 'email', 'Adresse email de l''expéditeur'),
|
||||||
|
|
||||||
|
-- === Configuration Application ===
|
||||||
|
('app_name', 'P''titsPas', 'string', 'app', 'Nom de l''application (affiché dans l''interface)'),
|
||||||
|
('app_url', 'https://app.ptits-pas.fr', 'string', 'app', 'URL publique de l''application (pour les liens dans emails)'),
|
||||||
|
('app_logo_url', '/assets/logo.png', 'string', 'app', 'URL du logo de l''application'),
|
||||||
|
('setup_completed', 'false', 'boolean', 'app', 'Configuration initiale terminée'),
|
||||||
|
|
||||||
|
-- === Configuration Sécurité ===
|
||||||
|
('password_reset_token_expiry_days', '7', 'number', 'security', 'Durée de validité des tokens de création/réinitialisation de mot de passe (en jours)'),
|
||||||
|
('jwt_expiry_hours', '24', 'number', 'security', 'Durée de validité des sessions JWT (en heures)'),
|
||||||
|
('max_upload_size_mb', '5', 'number', 'security', 'Taille maximale des fichiers uploadés (en MB)'),
|
||||||
|
('bcrypt_rounds', '12', 'number', 'security', 'Nombre de rounds bcrypt pour le hachage des mots de passe');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Types de données
|
||||||
|
|
||||||
|
| Type | Description | Exemple |
|
||||||
|
|------|-------------|---------|
|
||||||
|
| `string` | Chaîne de caractères | `"mail.example.com"` |
|
||||||
|
| `number` | Nombre entier ou décimal | `587` |
|
||||||
|
| `boolean` | Booléen | `true` / `false` |
|
||||||
|
| `json` | Objet JSON | `{"key": "value"}` |
|
||||||
|
| `encrypted` | Chaîne chiffrée AES-256 | `"a3f8b2..."` (hash) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Service Configuration
|
||||||
|
|
||||||
|
### Responsabilités
|
||||||
|
|
||||||
|
1. **Cache en mémoire** : Chargement au démarrage
|
||||||
|
2. **Lecture** : `get(key, defaultValue)`
|
||||||
|
3. **Écriture** : `set(key, value, userId)`
|
||||||
|
4. **Chiffrement** : AES-256 pour type `encrypted`
|
||||||
|
5. **Conversion de types** : string → number/boolean/json
|
||||||
|
6. **Test SMTP** : Validation connexion
|
||||||
|
|
||||||
|
### Implémentation (TypeScript)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// backend/src/config/config.service.ts
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { Configuration } from './entities/configuration.entity';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ConfigService {
|
||||||
|
private cache: Map<string, any> = new Map();
|
||||||
|
private readonly ENCRYPTION_KEY = process.env.CONFIG_ENCRYPTION_KEY;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Configuration)
|
||||||
|
private configRepo: Repository<Configuration>,
|
||||||
|
) {
|
||||||
|
this.loadCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chargement du cache au démarrage
|
||||||
|
async loadCache() {
|
||||||
|
const configs = await this.configRepo.find();
|
||||||
|
configs.forEach(config => {
|
||||||
|
let value = config.valeur;
|
||||||
|
|
||||||
|
// Déchiffrement si nécessaire
|
||||||
|
if (config.type === 'encrypted' && value) {
|
||||||
|
value = this.decrypt(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversion de type
|
||||||
|
value = this.convertType(value, config.type);
|
||||||
|
|
||||||
|
this.cache.set(config.cle, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupération d'une valeur
|
||||||
|
get(key: string, defaultValue?: any): any {
|
||||||
|
return this.cache.has(key) ? this.cache.get(key) : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mise à jour d'une valeur
|
||||||
|
async set(key: string, value: any, userId: string): Promise<void> {
|
||||||
|
const config = await this.configRepo.findOne({ where: { cle: key } });
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
throw new Error(`Configuration key '${key}' not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let valueToStore = String(value);
|
||||||
|
|
||||||
|
// Chiffrement si nécessaire
|
||||||
|
if (config.type === 'encrypted') {
|
||||||
|
valueToStore = this.encrypt(valueToStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.valeur = valueToStore;
|
||||||
|
config.modifie_par = userId;
|
||||||
|
config.modifie_le = new Date();
|
||||||
|
|
||||||
|
await this.configRepo.save(config);
|
||||||
|
|
||||||
|
// Mise à jour du cache
|
||||||
|
this.cache.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupération de toutes les configs par catégorie
|
||||||
|
async getByCategory(category: string): Promise<any> {
|
||||||
|
const configs = await this.configRepo.find({ where: { categorie: category } });
|
||||||
|
|
||||||
|
return configs.reduce((acc, config) => {
|
||||||
|
let value = config.valeur;
|
||||||
|
|
||||||
|
if (config.type === 'encrypted') {
|
||||||
|
value = '***********'; // Masquer les mots de passe
|
||||||
|
} else {
|
||||||
|
value = this.convertType(value, config.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[config.cle] = {
|
||||||
|
value,
|
||||||
|
description: config.description,
|
||||||
|
type: config.type,
|
||||||
|
};
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test de connexion SMTP
|
||||||
|
async testSmtpConnection(): Promise<boolean> {
|
||||||
|
const nodemailer = require('nodemailer');
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: this.get('smtp_host'),
|
||||||
|
port: this.get('smtp_port'),
|
||||||
|
secure: this.get('smtp_secure'),
|
||||||
|
auth: this.get('smtp_auth_required') ? {
|
||||||
|
user: this.get('smtp_user'),
|
||||||
|
pass: this.get('smtp_password'),
|
||||||
|
} : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await transporter.verify();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('SMTP test failed:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utilitaires
|
||||||
|
private convertType(value: string, type: string): any {
|
||||||
|
switch (type) {
|
||||||
|
case 'number': return Number(value);
|
||||||
|
case 'boolean': return value === 'true';
|
||||||
|
case 'json': return JSON.parse(value);
|
||||||
|
default: return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private encrypt(text: string): string {
|
||||||
|
const cipher = crypto.createCipher('aes-256-cbc', this.ENCRYPTION_KEY);
|
||||||
|
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||||
|
encrypted += cipher.final('hex');
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private decrypt(text: string): string {
|
||||||
|
const decipher = crypto.createDecipher('aes-256-cbc', this.ENCRYPTION_KEY);
|
||||||
|
let decrypted = decipher.update(text, 'hex', 'utf8');
|
||||||
|
decrypted += decipher.final('utf8');
|
||||||
|
return decrypted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Workflow Setup Initial
|
||||||
|
|
||||||
|
### Diagramme de séquence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant SA as Super Admin
|
||||||
|
participant App as Application
|
||||||
|
participant Guard as SetupGuard
|
||||||
|
participant API as ConfigAPI
|
||||||
|
participant DB as PostgreSQL
|
||||||
|
participant SMTP as Serveur SMTP
|
||||||
|
|
||||||
|
SA->>App: Première connexion
|
||||||
|
App->>Guard: Vérifier setup_completed
|
||||||
|
Guard->>DB: SELECT valeur FROM configuration<br/>WHERE cle='setup_completed'
|
||||||
|
DB-->>Guard: 'false'
|
||||||
|
|
||||||
|
Guard-->>App: Redirection forcée vers<br/>/admin/setup
|
||||||
|
|
||||||
|
SA->>SA: Remplit formulaire config<br/>(SMTP, app, sécurité)
|
||||||
|
|
||||||
|
SA->>App: Clic "Tester la connexion SMTP"
|
||||||
|
App->>API: POST /api/v1/configuration/test-smtp
|
||||||
|
API->>SMTP: Test connexion
|
||||||
|
|
||||||
|
alt Test SMTP OK
|
||||||
|
SMTP-->>API: ✅ Connexion réussie
|
||||||
|
API->>SA: Envoi email de test
|
||||||
|
API-->>App: ✅ Test réussi
|
||||||
|
App-->>SA: Message: "Email de test envoyé"
|
||||||
|
else Test SMTP KO
|
||||||
|
SMTP-->>API: ❌ Erreur connexion
|
||||||
|
API-->>App: ❌ Erreur détaillée
|
||||||
|
App-->>SA: Message: "Erreur: vérifiez les paramètres"
|
||||||
|
end
|
||||||
|
|
||||||
|
SA->>App: Clic "Sauvegarder"
|
||||||
|
App->>API: PATCH /api/v1/configuration/bulk<br/>{smtp_host, smtp_port, ...}
|
||||||
|
|
||||||
|
API->>DB: BEGIN TRANSACTION
|
||||||
|
API->>DB: UPDATE configuration SET valeur=...<br/>FOR EACH key
|
||||||
|
API->>DB: UPDATE configuration<br/>SET valeur='true'<br/>WHERE cle='setup_completed'
|
||||||
|
API->>DB: COMMIT
|
||||||
|
|
||||||
|
API->>API: Recharger cache ConfigService
|
||||||
|
|
||||||
|
API-->>App: ✅ Configuration sauvegardée
|
||||||
|
App-->>SA: Redirection vers /admin/dashboard
|
||||||
|
|
||||||
|
SA->>App: Accès complet à l'application
|
||||||
|
```
|
||||||
|
|
||||||
|
### Étapes détaillées
|
||||||
|
|
||||||
|
#### 1. Détection configuration incomplète
|
||||||
|
|
||||||
|
**Guard** : `SetupGuard` (NestJS)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@Injectable()
|
||||||
|
export class SetupGuard implements CanActivate {
|
||||||
|
constructor(private configService: ConfigService) {}
|
||||||
|
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const setupCompleted = this.configService.get('setup_completed', false);
|
||||||
|
|
||||||
|
// Exemptions
|
||||||
|
const exemptedRoutes = ['/auth/login', '/admin/setup', '/api/v1/configuration'];
|
||||||
|
if (exemptedRoutes.some(route => request.url.includes(route))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si setup non complété, bloquer
|
||||||
|
if (!setupCompleted) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Configuration initiale requise',
|
||||||
|
HttpStatus.TEMPORARY_REDIRECT,
|
||||||
|
{ location: '/admin/setup' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Formulaire Setup (Frontend)
|
||||||
|
|
||||||
|
**3 onglets** :
|
||||||
|
|
||||||
|
##### Onglet 1 : Configuration Email 📧
|
||||||
|
|
||||||
|
| Champ | Type | Valeur par défaut | Obligatoire |
|
||||||
|
|-------|------|-------------------|-------------|
|
||||||
|
| Serveur SMTP | Text | `localhost` | ✅ |
|
||||||
|
| Port SMTP | Number | `25` | ✅ |
|
||||||
|
| Sécurité | Select | `Aucune` / `STARTTLS` / `SSL/TLS` | ✅ |
|
||||||
|
| Authentification requise | Checkbox | `false` | - |
|
||||||
|
| Utilisateur SMTP | Text | - | Si auth |
|
||||||
|
| Mot de passe SMTP | Password | - | Si auth |
|
||||||
|
| Nom expéditeur | Text | `P'titsPas` | ✅ |
|
||||||
|
| Email expéditeur | Email | `no-reply@ptits-pas.fr` | ✅ |
|
||||||
|
|
||||||
|
**Bouton** : "🧪 Tester la connexion SMTP"
|
||||||
|
|
||||||
|
##### Onglet 2 : Personnalisation 🎨
|
||||||
|
|
||||||
|
| Champ | Type | Valeur par défaut | Obligatoire |
|
||||||
|
|-------|------|-------------------|-------------|
|
||||||
|
| Nom de l'application | Text | `P'titsPas` | ✅ |
|
||||||
|
| URL de l'application | URL | `https://app.ptits-pas.fr` | ✅ |
|
||||||
|
| Logo | File (PNG/JPG) | Logo par défaut | ❌ |
|
||||||
|
|
||||||
|
##### Onglet 3 : Paramètres avancés ⚙️
|
||||||
|
|
||||||
|
| Champ | Type | Valeur par défaut | Obligatoire |
|
||||||
|
|-------|------|-------------------|-------------|
|
||||||
|
| Durée validité token MDP (jours) | Number | `7` | ✅ |
|
||||||
|
| Durée session JWT (heures) | Number | `24` | ✅ |
|
||||||
|
| Taille max upload (MB) | Number | `5` | ✅ |
|
||||||
|
|
||||||
|
**Bouton** : "💾 Sauvegarder et terminer la configuration"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 APIs Configuration
|
||||||
|
|
||||||
|
### Endpoint 1 : Récupérer config par catégorie
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/v1/configuration/:category
|
||||||
|
Authorization: Bearer <super_admin_token>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Paramètres** :
|
||||||
|
- `category` : `email` | `app` | `security`
|
||||||
|
|
||||||
|
**Réponse 200** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"smtp_host": {
|
||||||
|
"value": "localhost",
|
||||||
|
"description": "Serveur SMTP",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"smtp_port": {
|
||||||
|
"value": 25,
|
||||||
|
"description": "Port SMTP",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"smtp_password": {
|
||||||
|
"value": "***********",
|
||||||
|
"description": "Mot de passe SMTP",
|
||||||
|
"type": "encrypted"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Endpoint 2 : Mise à jour multiple
|
||||||
|
|
||||||
|
```http
|
||||||
|
PATCH /api/v1/configuration/bulk
|
||||||
|
Authorization: Bearer <super_admin_token>
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"smtp_host": "mail.mairie-bezons.fr",
|
||||||
|
"smtp_port": 587,
|
||||||
|
"smtp_secure": false,
|
||||||
|
"smtp_auth_required": true,
|
||||||
|
"smtp_user": "noreply@mairie-bezons.fr",
|
||||||
|
"smtp_password": "SecretPassword123",
|
||||||
|
"email_from_name": "P'titsPas - Mairie de Bezons",
|
||||||
|
"email_from_address": "noreply@mairie-bezons.fr",
|
||||||
|
"app_name": "P'titsPas Bezons",
|
||||||
|
"app_url": "https://ptitspas.mairie-bezons.fr"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse 200** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "Configuration mise à jour avec succès",
|
||||||
|
"updated": 10
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Endpoint 3 : Test connexion SMTP
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /api/v1/configuration/test-smtp
|
||||||
|
Authorization: Bearer <super_admin_token>
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"smtp_host": "mail.mairie-bezons.fr",
|
||||||
|
"smtp_port": 587,
|
||||||
|
"smtp_secure": false,
|
||||||
|
"smtp_auth_required": true,
|
||||||
|
"smtp_user": "noreply@mairie-bezons.fr",
|
||||||
|
"smtp_password": "SecretPassword123",
|
||||||
|
"test_email": "admin@mairie-bezons.fr"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse 200** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Connexion SMTP réussie. Email de test envoyé à admin@mairie-bezons.fr"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse 400** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "Erreur de connexion SMTP",
|
||||||
|
"error": "ECONNREFUSED: Connection refused"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💻 Interface Admin
|
||||||
|
|
||||||
|
### Écran Setup Initial
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ 🚀 Configuration Initiale - P'titsPas │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Bienvenue ! Configurez votre installation P'titsPas │
|
||||||
|
│ │
|
||||||
|
│ [ 📧 Email ] [ 🎨 Personnalisation ] [ ⚙️ Avancé ] │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 📧 Configuration Email (SMTP) │
|
||||||
|
│ │
|
||||||
|
│ Serveur SMTP * │
|
||||||
|
│ [_____________________________________________] │
|
||||||
|
│ Ex: mail.mairie-bezons.fr, smtp.gmail.com │
|
||||||
|
│ │
|
||||||
|
│ Port SMTP * │
|
||||||
|
│ [_____] 25 (standard), 465 (SSL), 587 (STARTTLS) │
|
||||||
|
│ │
|
||||||
|
│ Sécurité * │
|
||||||
|
│ [ ▼ Aucune ] STARTTLS SSL/TLS │
|
||||||
|
│ │
|
||||||
|
│ ☐ Authentification requise │
|
||||||
|
│ │
|
||||||
|
│ Utilisateur SMTP │
|
||||||
|
│ [_____________________________________________] │
|
||||||
|
│ │
|
||||||
|
│ Mot de passe SMTP │
|
||||||
|
│ [_____________________________________________] │
|
||||||
|
│ │
|
||||||
|
│ Nom de l'expéditeur * │
|
||||||
|
│ [_____________________________________________] │
|
||||||
|
│ Ex: P'titsPas - Mairie de Bezons │
|
||||||
|
│ │
|
||||||
|
│ Email expéditeur * │
|
||||||
|
│ [_____________________________________________] │
|
||||||
|
│ Ex: noreply@mairie-bezons.fr │
|
||||||
|
│ │
|
||||||
|
│ [ 🧪 Tester la connexion SMTP ] │
|
||||||
|
│ │
|
||||||
|
│ ───────────────────────────────────────────────── │
|
||||||
|
│ │
|
||||||
|
│ [ ← Précédent ] [ Suivant → ] │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Écran Paramètres (accès permanent)
|
||||||
|
|
||||||
|
Identique au Setup Initial, mais accessible depuis le menu admin :
|
||||||
|
- Menu Admin → Paramètres → Configuration Système
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Exemples de configuration
|
||||||
|
|
||||||
|
### Configuration 1 : Mairie (serveur local)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"smtp_host": "mail.mairie-bezons.fr",
|
||||||
|
"smtp_port": 25,
|
||||||
|
"smtp_secure": false,
|
||||||
|
"smtp_auth_required": false,
|
||||||
|
"email_from_name": "P'titsPas - Mairie de Bezons",
|
||||||
|
"email_from_address": "noreply@mairie-bezons.fr",
|
||||||
|
"app_name": "P'titsPas Bezons",
|
||||||
|
"app_url": "https://ptitspas.mairie-bezons.fr"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration 2 : Gmail (pour tests)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"smtp_host": "smtp.gmail.com",
|
||||||
|
"smtp_port": 587,
|
||||||
|
"smtp_secure": false,
|
||||||
|
"smtp_auth_required": true,
|
||||||
|
"smtp_user": "contact@ptits-pas.fr",
|
||||||
|
"smtp_password": "abcd efgh ijkl mnop",
|
||||||
|
"email_from_name": "P'titsPas",
|
||||||
|
"email_from_address": "contact@ptits-pas.fr",
|
||||||
|
"app_name": "P'titsPas",
|
||||||
|
"app_url": "https://app.ptits-pas.fr"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note** : Pour Gmail, utiliser un "Mot de passe d'application" (App Password)
|
||||||
|
|
||||||
|
### Configuration 3 : Office 365
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"smtp_host": "smtp.office365.com",
|
||||||
|
"smtp_port": 587,
|
||||||
|
"smtp_secure": false,
|
||||||
|
"smtp_auth_required": true,
|
||||||
|
"smtp_user": "noreply@collectivite.fr",
|
||||||
|
"smtp_password": "MotDePasseSecurise123",
|
||||||
|
"email_from_name": "P'titsPas - Collectivité",
|
||||||
|
"email_from_address": "noreply@collectivite.fr",
|
||||||
|
"app_name": "P'titsPas",
|
||||||
|
"app_url": "https://ptitspas.collectivite.fr"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Sécurité
|
||||||
|
|
||||||
|
### Chiffrement des mots de passe
|
||||||
|
|
||||||
|
**Algorithme** : AES-256-CBC
|
||||||
|
**Clé** : Variable d'environnement `CONFIG_ENCRYPTION_KEY` (32 caractères)
|
||||||
|
|
||||||
|
**Génération de la clé** :
|
||||||
|
```bash
|
||||||
|
# Linux/Mac
|
||||||
|
openssl rand -hex 32
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fichier `.env`** :
|
||||||
|
```env
|
||||||
|
CONFIG_ENCRYPTION_KEY=a3f8b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables d'environnement critiques
|
||||||
|
|
||||||
|
**Fichier `.env`** (à créer lors de l'installation) :
|
||||||
|
|
||||||
|
```env
|
||||||
|
# Base de données
|
||||||
|
DATABASE_URL=postgresql://app_user:password@ptitspas-postgres:5432/ptitpas_db
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
JWT_SECRET=VotreSecretJWTTresLongEtAleatoire123456789
|
||||||
|
JWT_EXPIRY=24h
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
CONFIG_ENCRYPTION_KEY=VotreCleDeChiffrementAES256TresLongue32Caracteres
|
||||||
|
|
||||||
|
# Application
|
||||||
|
NODE_ENV=production
|
||||||
|
PORT=3000
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Références
|
||||||
|
|
||||||
|
### Documentation interne
|
||||||
|
- [01_CAHIER-DES-CHARGES.md](./01_CAHIER-DES-CHARGES.md)
|
||||||
|
- [02_ARCHITECTURE.md](./02_ARCHITECTURE.md)
|
||||||
|
- [03_DEPLOYMENT.md](./03_DEPLOYMENT.md)
|
||||||
|
- [10_DATABASE.md](./10_DATABASE.md)
|
||||||
|
- [11_API.md](./11_API.md)
|
||||||
|
|
||||||
|
### Documentation externe
|
||||||
|
- [NestJS Configuration](https://docs.nestjs.com/techniques/configuration)
|
||||||
|
- [Nodemailer SMTP](https://nodemailer.com/smtp/)
|
||||||
|
- [Node.js Crypto](https://nodejs.org/api/crypto.html)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Dernière mise à jour** : 25 Novembre 2025
|
||||||
|
**Version** : 1.0
|
||||||
|
**Statut** : ✅ Document validé
|
||||||
|
|
||||||
697
docs/22_DOCUMENTS-LEGAUX.md
Normal file
697
docs/22_DOCUMENTS-LEGAUX.md
Normal file
@ -0,0 +1,697 @@
|
|||||||
|
# 📄 Documentation Technique - Gestion Documents Légaux (CGU/Privacy)
|
||||||
|
|
||||||
|
**Version** : 1.0
|
||||||
|
**Date** : 25 Novembre 2025
|
||||||
|
**Auteur** : Équipe PtitsPas
|
||||||
|
**Référence** : RGPD & Conformité juridique
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Table des matières
|
||||||
|
|
||||||
|
1. [Vue d'ensemble](#vue-densemble)
|
||||||
|
2. [Architecture](#architecture)
|
||||||
|
3. [Tables BDD](#tables-bdd)
|
||||||
|
4. [Service Documents Légaux](#service-documents-légaux)
|
||||||
|
5. [Workflow Upload & Activation](#workflow-upload--activation)
|
||||||
|
6. [Workflow Acceptation Utilisateur](#workflow-acceptation-utilisateur)
|
||||||
|
7. [APIs](#apis)
|
||||||
|
8. [Interface Admin](#interface-admin)
|
||||||
|
9. [Conformité RGPD](#conformité-rgpd)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Vue d'ensemble
|
||||||
|
|
||||||
|
### Problématique
|
||||||
|
|
||||||
|
Chaque collectivité déployant P'titsPas on-premise doit pouvoir :
|
||||||
|
1. ✅ **Personnaliser** les CGU et la Politique de confidentialité
|
||||||
|
2. ✅ **Versionner** les documents (traçabilité juridique)
|
||||||
|
3. ✅ **Tracer** qui a accepté quelle version (RGPD)
|
||||||
|
4. ✅ **Prouver** l'acceptation (IP, User-Agent, horodatage)
|
||||||
|
5. ✅ **Empêcher** le retour en arrière (sécurité juridique)
|
||||||
|
|
||||||
|
### Solution
|
||||||
|
|
||||||
|
- **Documents génériques v1** fournis par défaut (rédigés avec juriste)
|
||||||
|
- **Upload de nouvelles versions** par l'admin (PDF uniquement)
|
||||||
|
- **Versioning automatique** (incrémentation sans retour arrière)
|
||||||
|
- **Activation manuelle** (prévisualisation avant mise en prod)
|
||||||
|
- **Traçabilité complète** (hash SHA-256, IP, User-Agent)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ Architecture
|
||||||
|
|
||||||
|
### Flux de données
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ Workflow Documents │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 1. UPLOAD (Admin) │
|
||||||
|
│ Admin ──▶ API ──▶ File System ──▶ BDD │
|
||||||
|
│ /documents/legaux/ │
|
||||||
|
│ cgu_v4_<timestamp>.pdf │
|
||||||
|
│ │
|
||||||
|
│ 2. ACTIVATION (Admin) │
|
||||||
|
│ Admin ──▶ API ──▶ BDD (actif=true) │
|
||||||
|
│ │
|
||||||
|
│ 3. ACCEPTATION (Utilisateur) │
|
||||||
|
│ User ──▶ Frontend ──▶ API ──▶ BDD │
|
||||||
|
│ (inscription) (trace IP/UA) │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Composants
|
||||||
|
|
||||||
|
1. **Table `documents_legaux`** : Stockage versions + métadonnées
|
||||||
|
2. **Table `acceptations_documents`** : Traçabilité acceptations
|
||||||
|
3. **Service `DocumentsLegauxService`** : Upload, versioning, activation
|
||||||
|
4. **API REST** : CRUD documents
|
||||||
|
5. **Interface Admin** : Upload + activation
|
||||||
|
6. **Interface Inscription** : Affichage + acceptation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Tables BDD
|
||||||
|
|
||||||
|
### Table 1 : `documents_legaux`
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Table pour gérer les versions des documents légaux
|
||||||
|
CREATE TABLE documents_legaux (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
type VARCHAR(50) NOT NULL, -- 'cgu' ou 'privacy'
|
||||||
|
version INTEGER NOT NULL, -- Numéro de version (auto-incrémenté)
|
||||||
|
fichier_nom VARCHAR(255) NOT NULL, -- Nom original du fichier
|
||||||
|
fichier_path VARCHAR(500) NOT NULL, -- Chemin de stockage
|
||||||
|
fichier_hash VARCHAR(64) NOT NULL, -- Hash SHA-256 pour intégrité
|
||||||
|
actif BOOLEAN DEFAULT false, -- Version actuellement active
|
||||||
|
televerse_par UUID REFERENCES utilisateurs(id), -- Qui a uploadé
|
||||||
|
televerse_le TIMESTAMPTZ DEFAULT now(), -- Date d'upload
|
||||||
|
active_le TIMESTAMPTZ, -- Date d'activation
|
||||||
|
UNIQUE(type, version) -- Pas de doublon version
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Index pour performance
|
||||||
|
CREATE INDEX idx_documents_legaux_type_actif ON documents_legaux(type, actif);
|
||||||
|
CREATE INDEX idx_documents_legaux_version ON documents_legaux(type, version DESC);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Contraintes** :
|
||||||
|
- ✅ Un seul document `actif=true` par type à la fois
|
||||||
|
- ✅ Versioning auto-incrémenté (pas de gaps)
|
||||||
|
- ✅ Hash SHA-256 pour vérifier l'intégrité du fichier
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Table 2 : `acceptations_documents`
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Table de traçabilité des acceptations (RGPD)
|
||||||
|
CREATE TABLE acceptations_documents (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
id_utilisateur UUID REFERENCES utilisateurs(id) ON DELETE CASCADE,
|
||||||
|
id_document UUID REFERENCES documents_legaux(id),
|
||||||
|
type_document VARCHAR(50) NOT NULL, -- 'cgu' ou 'privacy'
|
||||||
|
version_document INTEGER NOT NULL, -- Version acceptée
|
||||||
|
accepte_le TIMESTAMPTZ DEFAULT now(), -- Date d'acceptation
|
||||||
|
ip_address INET, -- IP de l'utilisateur (RGPD)
|
||||||
|
user_agent TEXT -- Navigateur (preuve)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_acceptations_utilisateur ON acceptations_documents(id_utilisateur);
|
||||||
|
CREATE INDEX idx_acceptations_document ON acceptations_documents(id_document);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Données capturées** :
|
||||||
|
- ✅ **Qui** : `id_utilisateur`
|
||||||
|
- ✅ **Quoi** : `type_document`, `version_document`
|
||||||
|
- ✅ **Quand** : `accepte_le`
|
||||||
|
- ✅ **Où** : `ip_address`
|
||||||
|
- ✅ **Comment** : `user_agent`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Modification table `utilisateurs`
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Ajouter colonnes pour référence rapide (optionnel)
|
||||||
|
ALTER TABLE utilisateurs
|
||||||
|
ADD COLUMN cgu_version_acceptee INTEGER,
|
||||||
|
ADD COLUMN cgu_acceptee_le TIMESTAMPTZ,
|
||||||
|
ADD COLUMN privacy_version_acceptee INTEGER,
|
||||||
|
ADD COLUMN privacy_acceptee_le TIMESTAMPTZ;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note** : Ces colonnes sont **redondantes** avec `acceptations_documents`, mais permettent un accès rapide sans JOIN.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Seed initial
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Documents génériques v1 (fournis par défaut)
|
||||||
|
INSERT INTO documents_legaux (type, version, fichier_nom, fichier_path, fichier_hash, actif, televerse_le, active_le) VALUES
|
||||||
|
('cgu', 1, 'cgu_v1_default.pdf', '/documents/legaux/cgu_v1_default.pdf', 'a3f8b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2', true, now(), now()),
|
||||||
|
('privacy', 1, 'privacy_v1_default.pdf', '/documents/legaux/privacy_v1_default.pdf', 'b4f9c3d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4', true, now(), now());
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fichiers à fournir** :
|
||||||
|
- `/documents/legaux/cgu_v1_default.pdf` (rédigé avec juriste)
|
||||||
|
- `/documents/legaux/privacy_v1_default.pdf` (conforme RGPD)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Service Documents Légaux
|
||||||
|
|
||||||
|
### Responsabilités
|
||||||
|
|
||||||
|
1. **Récupérer documents actifs** : `getDocumentsActifs()`
|
||||||
|
2. **Uploader nouvelle version** : `uploadNouvelleVersion(type, file, userId)`
|
||||||
|
3. **Activer une version** : `activerVersion(documentId)`
|
||||||
|
4. **Lister versions** : `listerVersions(type)`
|
||||||
|
5. **Télécharger document** : `telechargerDocument(documentId)`
|
||||||
|
|
||||||
|
### Implémentation (TypeScript)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// backend/src/documents-legaux/documents-legaux.service.ts
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { DocumentLegal } from './entities/document-legal.entity';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import * as fs from 'fs/promises';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DocumentsLegauxService {
|
||||||
|
private readonly UPLOAD_DIR = '/app/documents/legaux';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(DocumentLegal)
|
||||||
|
private docRepo: Repository<DocumentLegal>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// Récupérer les documents actifs
|
||||||
|
async getDocumentsActifs(): Promise<{ cgu: DocumentLegal; privacy: DocumentLegal }> {
|
||||||
|
const cgu = await this.docRepo.findOne({
|
||||||
|
where: { type: 'cgu', actif: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const privacy = await this.docRepo.findOne({
|
||||||
|
where: { type: 'privacy', actif: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!cgu || !privacy) {
|
||||||
|
throw new Error('Documents légaux manquants');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { cgu, privacy };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uploader une nouvelle version
|
||||||
|
async uploadNouvelleVersion(
|
||||||
|
type: 'cgu' | 'privacy',
|
||||||
|
file: Express.Multer.File,
|
||||||
|
userId: string,
|
||||||
|
): Promise<DocumentLegal> {
|
||||||
|
// 1. Calculer la prochaine version
|
||||||
|
const lastDoc = await this.docRepo.findOne({
|
||||||
|
where: { type },
|
||||||
|
order: { version: 'DESC' },
|
||||||
|
});
|
||||||
|
const nouvelleVersion = (lastDoc?.version || 0) + 1;
|
||||||
|
|
||||||
|
// 2. Calculer le hash du fichier
|
||||||
|
const fileBuffer = file.buffer;
|
||||||
|
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
||||||
|
|
||||||
|
// 3. Générer le nom de fichier unique
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const fileName = `${type}_v${nouvelleVersion}_${timestamp}.pdf`;
|
||||||
|
const filePath = path.join(this.UPLOAD_DIR, fileName);
|
||||||
|
|
||||||
|
// 4. Sauvegarder le fichier
|
||||||
|
await fs.mkdir(this.UPLOAD_DIR, { recursive: true });
|
||||||
|
await fs.writeFile(filePath, fileBuffer);
|
||||||
|
|
||||||
|
// 5. Créer l'entrée en BDD
|
||||||
|
const document = this.docRepo.create({
|
||||||
|
type,
|
||||||
|
version: nouvelleVersion,
|
||||||
|
fichier_nom: file.originalname,
|
||||||
|
fichier_path: filePath,
|
||||||
|
fichier_hash: hash,
|
||||||
|
actif: false, // Pas actif par défaut
|
||||||
|
televerse_par: userId,
|
||||||
|
televerse_le: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return await this.docRepo.save(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activer une version
|
||||||
|
async activerVersion(documentId: string): Promise<void> {
|
||||||
|
const document = await this.docRepo.findOne({ where: { id: documentId } });
|
||||||
|
|
||||||
|
if (!document) {
|
||||||
|
throw new Error('Document non trouvé');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction : désactiver l'ancienne version, activer la nouvelle
|
||||||
|
await this.docRepo.manager.transaction(async (manager) => {
|
||||||
|
// Désactiver toutes les versions de ce type
|
||||||
|
await manager.update(
|
||||||
|
DocumentLegal,
|
||||||
|
{ type: document.type, actif: true },
|
||||||
|
{ actif: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Activer la nouvelle version
|
||||||
|
await manager.update(
|
||||||
|
DocumentLegal,
|
||||||
|
{ id: documentId },
|
||||||
|
{ actif: true, active_le: new Date() },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lister toutes les versions (pour l'admin)
|
||||||
|
async listerVersions(type: 'cgu' | 'privacy'): Promise<DocumentLegal[]> {
|
||||||
|
return await this.docRepo.find({
|
||||||
|
where: { type },
|
||||||
|
order: { version: 'DESC' },
|
||||||
|
relations: ['televerse_par'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Télécharger un document (stream)
|
||||||
|
async telechargerDocument(documentId: string): Promise<{ stream: Buffer; filename: string }> {
|
||||||
|
const document = await this.docRepo.findOne({ where: { id: documentId } });
|
||||||
|
|
||||||
|
if (!document) {
|
||||||
|
throw new Error('Document non trouvé');
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileBuffer = await fs.readFile(document.fichier_path);
|
||||||
|
|
||||||
|
return {
|
||||||
|
stream: fileBuffer,
|
||||||
|
filename: document.fichier_nom,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier l'intégrité d'un document
|
||||||
|
async verifierIntegrite(documentId: string): Promise<boolean> {
|
||||||
|
const document = await this.docRepo.findOne({ where: { id: documentId } });
|
||||||
|
|
||||||
|
if (!document) {
|
||||||
|
throw new Error('Document non trouvé');
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileBuffer = await fs.readFile(document.fichier_path);
|
||||||
|
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
||||||
|
|
||||||
|
return hash === document.fichier_hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Workflow Upload & Activation
|
||||||
|
|
||||||
|
### Diagramme de séquence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant A as Admin
|
||||||
|
participant API as Backend API
|
||||||
|
participant FS as File System
|
||||||
|
participant DB as PostgreSQL
|
||||||
|
|
||||||
|
A->>API: POST /api/v1/documents-legaux<br/>{type: 'cgu', file: PDF}
|
||||||
|
|
||||||
|
API->>API: Validation fichier<br/>(PDF, max 10MB)
|
||||||
|
API->>API: Calcul hash SHA-256
|
||||||
|
|
||||||
|
API->>DB: SELECT MAX(version)<br/>WHERE type='cgu'
|
||||||
|
DB-->>API: version = 3
|
||||||
|
|
||||||
|
API->>API: Nouvelle version = 4
|
||||||
|
|
||||||
|
API->>FS: Enregistrer fichier<br/>/documents/legaux/cgu_v4_<timestamp>.pdf
|
||||||
|
FS-->>API: ✅ Fichier sauvegardé
|
||||||
|
|
||||||
|
API->>DB: INSERT INTO documents_legaux<br/>(type, version=4, actif=false)
|
||||||
|
DB-->>API: ✅ Document créé
|
||||||
|
|
||||||
|
API-->>A: 201 Created<br/>{id, version: 4, actif: false}
|
||||||
|
|
||||||
|
A->>A: Prévisualisation PDF
|
||||||
|
A->>API: PATCH /api/v1/documents-legaux/{id}/activer
|
||||||
|
|
||||||
|
API->>DB: BEGIN TRANSACTION
|
||||||
|
API->>DB: UPDATE documents_legaux<br/>SET actif=false WHERE type='cgu'
|
||||||
|
API->>DB: UPDATE documents_legaux<br/>SET actif=true, active_le=now()<br/>WHERE id={id}
|
||||||
|
API->>DB: COMMIT
|
||||||
|
|
||||||
|
API-->>A: ✅ CGU v4 activées
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📥 Workflow Acceptation Utilisateur
|
||||||
|
|
||||||
|
### Diagramme de séquence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant U as Utilisateur
|
||||||
|
participant App as Frontend
|
||||||
|
participant API as Backend
|
||||||
|
participant DB as PostgreSQL
|
||||||
|
|
||||||
|
U->>App: Inscription (étape CGU)
|
||||||
|
|
||||||
|
App->>API: GET /api/v1/documents-legaux/actifs
|
||||||
|
API->>DB: SELECT * FROM documents_legaux<br/>WHERE actif=true
|
||||||
|
DB-->>API: {cgu: v4, privacy: v2}
|
||||||
|
API-->>App: {cgu: {version: 4, url: '...'}, privacy: {...}}
|
||||||
|
|
||||||
|
App->>App: Afficher liens PDF<br/>"CGU v4" et "Privacy v2"
|
||||||
|
|
||||||
|
U->>U: Lit les documents
|
||||||
|
U->>U: Coche "J'accepte"
|
||||||
|
|
||||||
|
App->>API: POST /api/v1/auth/register<br/>{..., cgu_version: 4, privacy_version: 2, ip, user_agent}
|
||||||
|
|
||||||
|
API->>DB: BEGIN TRANSACTION
|
||||||
|
|
||||||
|
API->>DB: INSERT INTO utilisateurs<br/>(..., cgu_version_acceptee=4, privacy_version_acceptee=2)
|
||||||
|
DB-->>API: id_utilisateur
|
||||||
|
|
||||||
|
API->>DB: INSERT INTO acceptations_documents<br/>(id_utilisateur, type='cgu', version=4, ip, user_agent)
|
||||||
|
API->>DB: INSERT INTO acceptations_documents<br/>(id_utilisateur, type='privacy', version=2, ip, user_agent)
|
||||||
|
|
||||||
|
API->>DB: COMMIT
|
||||||
|
|
||||||
|
API-->>App: ✅ Inscription réussie
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 APIs
|
||||||
|
|
||||||
|
### API 1 : Récupérer documents actifs (Public)
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/v1/documents-legaux/actifs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse 200** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cgu": {
|
||||||
|
"id": "uuid-cgu-v4",
|
||||||
|
"type": "cgu",
|
||||||
|
"version": 4,
|
||||||
|
"url": "/api/v1/documents-legaux/uuid-cgu-v4/download",
|
||||||
|
"active_le": "2025-11-20T14:30:00Z"
|
||||||
|
},
|
||||||
|
"privacy": {
|
||||||
|
"id": "uuid-privacy-v2",
|
||||||
|
"type": "privacy",
|
||||||
|
"version": 2,
|
||||||
|
"url": "/api/v1/documents-legaux/uuid-privacy-v2/download",
|
||||||
|
"active_le": "2025-10-15T09:15:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### API 2 : Lister versions (Admin)
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/v1/documents-legaux/:type/versions
|
||||||
|
Authorization: Bearer <super_admin_token>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Paramètres** :
|
||||||
|
- `type` : `cgu` | `privacy`
|
||||||
|
|
||||||
|
**Réponse 200** :
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "uuid-cgu-v4",
|
||||||
|
"version": 4,
|
||||||
|
"fichier_nom": "CGU_Mairie_Bezons_2025.pdf",
|
||||||
|
"actif": true,
|
||||||
|
"televerse_par": {
|
||||||
|
"id": "uuid-admin",
|
||||||
|
"prenom": "Lucas",
|
||||||
|
"nom": "MOREAU"
|
||||||
|
},
|
||||||
|
"televerse_le": "2025-11-20T14:00:00Z",
|
||||||
|
"active_le": "2025-11-20T14:30:00Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "uuid-cgu-v3",
|
||||||
|
"version": 3,
|
||||||
|
"fichier_nom": "CGU_v3.pdf",
|
||||||
|
"actif": false,
|
||||||
|
"televerse_par": {
|
||||||
|
"id": "uuid-admin",
|
||||||
|
"prenom": "Admin",
|
||||||
|
"nom": "Système"
|
||||||
|
},
|
||||||
|
"televerse_le": "2025-10-15T09:00:00Z",
|
||||||
|
"active_le": "2025-10-15T09:15:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### API 3 : Upload nouvelle version (Admin)
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /api/v1/documents-legaux
|
||||||
|
Authorization: Bearer <super_admin_token>
|
||||||
|
Content-Type: multipart/form-data
|
||||||
|
```
|
||||||
|
|
||||||
|
**Body** :
|
||||||
|
```
|
||||||
|
type: cgu
|
||||||
|
file: <fichier PDF>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse 201** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "uuid-nouveau-doc",
|
||||||
|
"type": "cgu",
|
||||||
|
"version": 5,
|
||||||
|
"fichier_nom": "CGU_Mairie_Bezons_2025_v2.pdf",
|
||||||
|
"actif": false,
|
||||||
|
"televerse_le": "2025-11-25T10:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Erreurs** :
|
||||||
|
- `400 Bad Request` : Fichier non PDF ou trop volumineux (>10MB)
|
||||||
|
- `401 Unauthorized` : Token manquant ou invalide
|
||||||
|
- `403 Forbidden` : Rôle insuffisant (pas super_admin)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### API 4 : Activer une version (Admin)
|
||||||
|
|
||||||
|
```http
|
||||||
|
PATCH /api/v1/documents-legaux/:id/activer
|
||||||
|
Authorization: Bearer <super_admin_token>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse 200** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "Document activé avec succès",
|
||||||
|
"documentId": "uuid-nouveau-doc",
|
||||||
|
"type": "cgu",
|
||||||
|
"version": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### API 5 : Télécharger document (Public)
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/v1/documents-legaux/:id/download
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse 200** :
|
||||||
|
```
|
||||||
|
Content-Type: application/pdf
|
||||||
|
Content-Disposition: attachment; filename="CGU_v5.pdf"
|
||||||
|
|
||||||
|
<binary PDF data>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### API 6 : Historique acceptations utilisateur (Admin)
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/v1/users/:userId/acceptations
|
||||||
|
Authorization: Bearer <super_admin_token>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse 200** :
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type_document": "cgu",
|
||||||
|
"version_document": 4,
|
||||||
|
"accepte_le": "2025-11-20T15:30:00Z",
|
||||||
|
"ip_address": "192.168.1.100",
|
||||||
|
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_document": "privacy",
|
||||||
|
"version_document": 2,
|
||||||
|
"accepte_le": "2025-11-20T15:30:00Z",
|
||||||
|
"ip_address": "192.168.1.100",
|
||||||
|
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💻 Interface Admin
|
||||||
|
|
||||||
|
### Écran Gestion Documents Légaux
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ 📄 Gestion des Documents Légaux │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ [ CGU ] [ Politique de confidentialité ] │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 📋 Conditions Générales d'Utilisation (CGU) │
|
||||||
|
│ │
|
||||||
|
│ Version active : v4 │
|
||||||
|
│ Activée le : 20/11/2025 14:30 │
|
||||||
|
│ Téléversée par : Lucas MOREAU │
|
||||||
|
│ │
|
||||||
|
│ [ 📥 Télécharger ] [ 👁️ Prévisualiser ] │
|
||||||
|
│ │
|
||||||
|
│ ───────────────────────────────────────────────── │
|
||||||
|
│ │
|
||||||
|
│ 📤 Uploader une nouvelle version │
|
||||||
|
│ │
|
||||||
|
│ ⚠️ Attention : L'upload d'une nouvelle version │
|
||||||
|
│ créera la version v5. Vous pourrez la prévisualiser│
|
||||||
|
│ avant de l'activer. │
|
||||||
|
│ │
|
||||||
|
│ [ Choisir un fichier PDF ] (max 10MB) │
|
||||||
|
│ │
|
||||||
|
│ [ 📤 Uploader ] │
|
||||||
|
│ │
|
||||||
|
│ ───────────────────────────────────────────────── │
|
||||||
|
│ │
|
||||||
|
│ 📜 Historique des versions │
|
||||||
|
│ │
|
||||||
|
│ ┌───────────────────────────────────────────────┐ │
|
||||||
|
│ │ ✅ v4 (Active) │ │
|
||||||
|
│ │ Activée le : 20/11/2025 14:30 │ │
|
||||||
|
│ │ Par : Lucas MOREAU │ │
|
||||||
|
│ │ Hash : a3f8b2c4...e0f1a2 ✓ │ │
|
||||||
|
│ │ [ 📥 Télécharger ] [ 👁️ Voir ] │ │
|
||||||
|
│ └───────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌───────────────────────────────────────────────┐ │
|
||||||
|
│ │ v3 (Inactive) │ │
|
||||||
|
│ │ Activée le : 15/10/2025 09:15 │ │
|
||||||
|
│ │ Par : Admin Système │ │
|
||||||
|
│ │ Hash : b4f9c3d6...f2a3b4 ✓ │ │
|
||||||
|
│ │ [ 📥 Télécharger ] [ 👁️ Voir ] [🔄 Réactiver]│ │
|
||||||
|
│ └───────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Conformité RGPD
|
||||||
|
|
||||||
|
### Données capturées
|
||||||
|
|
||||||
|
| Donnée | Justification RGPD | Durée conservation |
|
||||||
|
|--------|-------------------|-------------------|
|
||||||
|
| `id_utilisateur` | Traçabilité acceptation | Durée du compte |
|
||||||
|
| `version_document` | Preuve version acceptée | Durée du compte |
|
||||||
|
| `accepte_le` | Horodatage légal | Durée du compte |
|
||||||
|
| `ip_address` | Preuve origine acceptation | 1 an (recommandé) |
|
||||||
|
| `user_agent` | Preuve navigateur/appareil | 1 an (recommandé) |
|
||||||
|
|
||||||
|
### Droits utilisateur
|
||||||
|
|
||||||
|
#### Droit d'accès (Article 15)
|
||||||
|
L'utilisateur peut demander :
|
||||||
|
- Quelles versions il a acceptées
|
||||||
|
- Quand il les a acceptées
|
||||||
|
- Depuis quelle IP
|
||||||
|
|
||||||
|
**API** : `GET /api/v1/users/me/acceptations`
|
||||||
|
|
||||||
|
#### Droit à l'oubli (Article 17)
|
||||||
|
Lors de la suppression du compte :
|
||||||
|
- Suppression des données personnelles
|
||||||
|
- Conservation des acceptations anonymisées (obligation légale)
|
||||||
|
|
||||||
|
**Implémentation** :
|
||||||
|
```sql
|
||||||
|
-- Anonymisation (pas suppression totale)
|
||||||
|
UPDATE acceptations_documents
|
||||||
|
SET ip_address = NULL,
|
||||||
|
user_agent = NULL
|
||||||
|
WHERE id_utilisateur = '<uuid>';
|
||||||
|
|
||||||
|
-- Puis suppression utilisateur
|
||||||
|
DELETE FROM utilisateurs WHERE id = '<uuid>';
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Références
|
||||||
|
|
||||||
|
### Documentation interne
|
||||||
|
- [01_CAHIER-DES-CHARGES.md](./01_CAHIER-DES-CHARGES.md)
|
||||||
|
- [10_DATABASE.md](./10_DATABASE.md)
|
||||||
|
- [11_API.md](./11_API.md)
|
||||||
|
- [21_CONFIGURATION-SYSTEME.md](./21_CONFIGURATION-SYSTEME.md)
|
||||||
|
|
||||||
|
### Documentation externe
|
||||||
|
- [RGPD - Article 7 (Consentement)](https://www.cnil.fr/fr/reglement-europeen-protection-donnees/chapitre2#Article7)
|
||||||
|
- [RGPD - Article 15 (Droit d'accès)](https://www.cnil.fr/fr/reglement-europeen-protection-donnees/chapitre3#Article15)
|
||||||
|
- [RGPD - Article 17 (Droit à l'oubli)](https://www.cnil.fr/fr/reglement-europeen-protection-donnees/chapitre3#Article17)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Dernière mise à jour** : 25 Novembre 2025
|
||||||
|
**Version** : 1.0
|
||||||
|
**Statut** : ✅ Document validé
|
||||||
|
|
||||||
|
|
||||||
1108
docs/23_LISTE-TICKETS.md
Normal file
1108
docs/23_LISTE-TICKETS.md
Normal file
File diff suppressed because it is too large
Load Diff
568
docs/24_DECISIONS-PROJET.md
Normal file
568
docs/24_DECISIONS-PROJET.md
Normal file
@ -0,0 +1,568 @@
|
|||||||
|
# 📋 Décisions Projet - P'titsPas
|
||||||
|
|
||||||
|
**Version** : 1.0
|
||||||
|
**Date** : 25 Novembre 2025
|
||||||
|
**Auteur** : Équipe PtitsPas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Objectif de ce document
|
||||||
|
|
||||||
|
Ce document trace toutes les **décisions importantes** prises lors de la conception du projet P'titsPas. Il sert de référence pour comprendre les choix techniques et fonctionnels.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Décisions Architecture
|
||||||
|
|
||||||
|
### 1. Mono-repo vs Multi-repo
|
||||||
|
|
||||||
|
**Décision** : ✅ **Mono-repo**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Code cohérent dans un seul dépôt Git
|
||||||
|
- Commits atomiques (frontend + backend + BDD en même temps)
|
||||||
|
- CI/CD simplifié (un seul webhook)
|
||||||
|
- Facilite le travail collaboratif
|
||||||
|
|
||||||
|
**Structure** :
|
||||||
|
```
|
||||||
|
ptitspas-app/
|
||||||
|
├── frontend/ # Application Flutter
|
||||||
|
├── backend/ # API NestJS
|
||||||
|
├── database/ # Migrations SQL/Prisma
|
||||||
|
├── api-contracts/ # Contrats OpenAPI + Prisma
|
||||||
|
├── docs/ # Documentation
|
||||||
|
└── docker-compose.yml # Orchestration
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Déploiement On-Premise
|
||||||
|
|
||||||
|
**Décision** : ✅ **Application on-premise avec configuration dynamique**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Chaque collectivité a son infrastructure (SMTP, domaines, ports)
|
||||||
|
- Autonomie totale (pas de dépendance à notre infra)
|
||||||
|
- Conformité aux politiques IT locales
|
||||||
|
- Sécurité (données restent dans le SI de la collectivité)
|
||||||
|
|
||||||
|
**Solution technique** :
|
||||||
|
- Table `configuration` en BDD (clé/valeur)
|
||||||
|
- Setup Wizard à la première connexion
|
||||||
|
- Configuration SMTP dynamique
|
||||||
|
- Personnalisation (nom app, logo, URL)
|
||||||
|
|
||||||
|
**Référence** : [21_CONFIGURATION-SYSTEME.md](./21_CONFIGURATION-SYSTEME.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Gestion des fichiers uploadés
|
||||||
|
|
||||||
|
**Décision** : ✅ **Système de fichiers local avec volume Docker**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Simplicité (pas de S3/MinIO pour on-premise)
|
||||||
|
- Performance (accès direct au disque)
|
||||||
|
- Coût (pas de service externe)
|
||||||
|
|
||||||
|
**Solution technique** :
|
||||||
|
- Stockage dans `/uploads/photos/`
|
||||||
|
- Volume Docker persistant
|
||||||
|
- Service Upload avec Multer
|
||||||
|
- Validation taille (depuis config)
|
||||||
|
|
||||||
|
**Alternatives rejetées** :
|
||||||
|
- ❌ Base de données (BYTEA) : Mauvaise performance
|
||||||
|
- ❌ S3/MinIO : Overkill pour on-premise
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Documents légaux (CGU/Privacy)
|
||||||
|
|
||||||
|
**Décision** : ✅ **Versioning automatique avec traçabilité RGPD**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Conformité juridique (preuve d'acceptation)
|
||||||
|
- Flexibilité (chaque collectivité adapte ses CGU)
|
||||||
|
- Traçabilité (hash SHA-256, IP, User-Agent)
|
||||||
|
- Sécurité juridique (pas de retour en arrière)
|
||||||
|
|
||||||
|
**Solution technique** :
|
||||||
|
- Table `documents_legaux` (versioning auto-incrémenté)
|
||||||
|
- Table `acceptations_documents` (traçabilité RGPD)
|
||||||
|
- Upload PDF par l'admin
|
||||||
|
- Activation manuelle après prévisualisation
|
||||||
|
- Documents génériques v1 fournis par défaut
|
||||||
|
|
||||||
|
**Référence** : [22_DOCUMENTS-LEGAUX.md](./22_DOCUMENTS-LEGAUX.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Décisions Fonctionnelles
|
||||||
|
|
||||||
|
### 5. Workflow inscription sans mot de passe
|
||||||
|
|
||||||
|
**Décision** : ✅ **Pas de mot de passe lors de l'inscription, lien email après validation**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Simplifie l'inscription (moins de friction)
|
||||||
|
- Adapté aux parents séparés/divorcés (communication difficile)
|
||||||
|
- Sécurité (token UUID avec expiration)
|
||||||
|
- Validation gestionnaire avant activation
|
||||||
|
|
||||||
|
**Workflow** :
|
||||||
|
1. Parent/AM s'inscrit (sans MDP)
|
||||||
|
2. Gestionnaire valide
|
||||||
|
3. Email envoyé avec lien création MDP (valable 7 jours)
|
||||||
|
4. Utilisateur crée son MDP
|
||||||
|
5. Compte activé
|
||||||
|
|
||||||
|
**Référence** : [20_WORKFLOW-CREATION-COMPTE.md](./20_WORKFLOW-CREATION-COMPTE.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Genre enfant obligatoire (H/F)
|
||||||
|
|
||||||
|
**Décision** : ✅ **Genre obligatoire (H/F uniquement)**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Contexte métier : Enfants à garder (pas de genre neutre)
|
||||||
|
- Conformité CDC
|
||||||
|
- Simplicité
|
||||||
|
|
||||||
|
**Implémentation** :
|
||||||
|
- Champ `genre` ENUM('H', 'F') NOT NULL dans table `enfants`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. Suppression champs téléphone
|
||||||
|
|
||||||
|
**Décision** : ✅ **Un seul champ "Téléphone" (mobile privilégié)**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Simplification (plus besoin de "Mobile" et "Téléphone fixe")
|
||||||
|
- Réalité terrain : Tout le monde a un mobile
|
||||||
|
- Note dans l'interface : "(mobile privilégié)"
|
||||||
|
|
||||||
|
**Implémentation** :
|
||||||
|
- Supprimer `mobile` et `telephone_fixe`
|
||||||
|
- Garder uniquement `telephone`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. NIR obligatoire pour Assistantes Maternelles
|
||||||
|
|
||||||
|
**Décision** : ✅ **NIR (Numéro de Sécurité sociale) obligatoire**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Conformité CDC
|
||||||
|
- Nécessaire pour génération automatique du contrat
|
||||||
|
- Stockage sécurisé (chiffrement recommandé)
|
||||||
|
|
||||||
|
**Implémentation** :
|
||||||
|
- Champ `nir_chiffre` VARCHAR(15) NOT NULL dans `assistantes_maternelles`
|
||||||
|
- Validation : 15 chiffres exactement
|
||||||
|
- Affichage masqué dans l'interface (XXX XX XX XX XXX 123)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. Suppression notifications SMS
|
||||||
|
|
||||||
|
**Décision** : ✅ **Supprimer les notifications SMS, garder uniquement Email**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Pas de plus-value identifiée
|
||||||
|
- Coût supplémentaire (service SMS)
|
||||||
|
- Complexité (intégration Twilio/OVH)
|
||||||
|
- Email suffit largement
|
||||||
|
|
||||||
|
**Action** :
|
||||||
|
- Amender le CDC v1.4
|
||||||
|
- Remplacer "Email OU SMS" par "Email"
|
||||||
|
- Supprimer toute mention de SMS
|
||||||
|
|
||||||
|
**Référence** : Ticket #57
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚫 Décisions Phase 2 (reportées)
|
||||||
|
|
||||||
|
### 10. Gestion erreurs SMTP (renvoyer email)
|
||||||
|
|
||||||
|
**Décision** : ⏸️ **Phase 2 ou cas par cas**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Pas prioritaire pour MVP
|
||||||
|
- Downtime SMTP rare (< 24h)
|
||||||
|
- Recréation dossier acceptable en phase de test
|
||||||
|
- Peut être ajouté plus tard si besoin terrain
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 11. Sauvegarde & Restauration automatique
|
||||||
|
|
||||||
|
**Décision** : ⏸️ **Phase finale (quand tout fonctionne)**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Pas bloquant pour développement
|
||||||
|
- Nécessite application stable
|
||||||
|
- Script `pg_dump` simple à ajouter
|
||||||
|
- Documentation pour admins sys
|
||||||
|
|
||||||
|
**À faire** :
|
||||||
|
- Script `backup.sh` avec cron
|
||||||
|
- Guide sauvegarde/restauration
|
||||||
|
- Test restauration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 12. RGPD avancé (droit à l'oubli, export données)
|
||||||
|
|
||||||
|
**Décision** : ⏸️ **Phase 2**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Conformité de base assurée (consentement, traçabilité)
|
||||||
|
- Droit à l'oubli : Peu de demandes en phase test
|
||||||
|
- Export données : Peut être fait manuellement en phase test
|
||||||
|
|
||||||
|
**À faire** :
|
||||||
|
- API suppression compte (soft delete)
|
||||||
|
- API export données personnelles (JSON)
|
||||||
|
- Anonymisation comptes inactifs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 13. Statistiques dashboard
|
||||||
|
|
||||||
|
**Décision** : ⏸️ **Phase 2 (besoin d'utilisateurs d'abord)**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Pas de données = pas de statistiques
|
||||||
|
- Fonctionnalité importante pour vente
|
||||||
|
- Nécessite application en production
|
||||||
|
|
||||||
|
**À faire** :
|
||||||
|
- API statistiques (comptes, enfants, AM, validations)
|
||||||
|
- Widgets dashboard (graphiques)
|
||||||
|
- Export rapports
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 14. Migration de données
|
||||||
|
|
||||||
|
**Décision** : ❌ **Pas de migration prévue**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Pas de système existant à migrer
|
||||||
|
- Application nouvelle
|
||||||
|
- Import CSV peut être ajouté cas par cas si besoin
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 15. Documentation utilisateur
|
||||||
|
|
||||||
|
**Décision** : ⏸️ **Phase 2 (formation présentiel prioritaire)**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Premiers utilisateurs : Formation directe par l'équipe
|
||||||
|
- Documentation nécessaire pour scalabilité
|
||||||
|
- Vidéos tutoriels utiles mais pas bloquant
|
||||||
|
|
||||||
|
**À faire** :
|
||||||
|
- Guide utilisateur gestionnaire
|
||||||
|
- Guide utilisateur parent/AM
|
||||||
|
- FAQ
|
||||||
|
- Vidéos tutoriels
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Décisions Sécurité
|
||||||
|
|
||||||
|
### 16. Chiffrement mots de passe configuration
|
||||||
|
|
||||||
|
**Décision** : ✅ **AES-256-CBC pour mots de passe SMTP**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Sécurité (mots de passe SMTP en clair = risque)
|
||||||
|
- Conformité sécurité
|
||||||
|
- Déchiffrement uniquement en mémoire
|
||||||
|
|
||||||
|
**Implémentation** :
|
||||||
|
- Type `encrypted` dans table `configuration`
|
||||||
|
- Clé de chiffrement dans `.env` (32 caractères)
|
||||||
|
- Service ConfigService gère chiffrement/déchiffrement
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 17. Hash mots de passe utilisateurs
|
||||||
|
|
||||||
|
**Décision** : ✅ **Bcrypt avec 12 rounds**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Standard industrie
|
||||||
|
- Résistant aux attaques brute-force
|
||||||
|
- Configurable (rounds dans config)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 18. Tokens création mot de passe
|
||||||
|
|
||||||
|
**Décision** : ✅ **UUID avec expiration 7 jours**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Sécurité (UUID impossible à deviner)
|
||||||
|
- Expiration (limite fenêtre d'attaque)
|
||||||
|
- Durée configurable (dans table `configuration`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 19. JWT sessions
|
||||||
|
|
||||||
|
**Décision** : ✅ **JWT avec expiration 24h**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Stateless (pas de session en BDD)
|
||||||
|
- Performance (pas de lookup BDD à chaque requête)
|
||||||
|
- Durée configurable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Décisions Base de Données
|
||||||
|
|
||||||
|
### 20. PostgreSQL vs autres SGBD
|
||||||
|
|
||||||
|
**Décision** : ✅ **PostgreSQL 17**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Open-source
|
||||||
|
- Robuste et performant
|
||||||
|
- Support JSON (flexibilité)
|
||||||
|
- Support UUID natif
|
||||||
|
- Communauté active
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 21. ORM : Prisma vs TypeORM
|
||||||
|
|
||||||
|
**Décision** : ✅ **Prisma** (mais TypeORM déjà en place)
|
||||||
|
|
||||||
|
**Justification Prisma** :
|
||||||
|
- Type-safety
|
||||||
|
- Migrations claires
|
||||||
|
- Génération client automatique
|
||||||
|
|
||||||
|
**Note** : Le projet YNOV utilise TypeORM. À évaluer si migration vers Prisma nécessaire.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 22. Migrations versionnées
|
||||||
|
|
||||||
|
**Décision** : ✅ **Migrations SQL versionnées**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Traçabilité des changements schéma
|
||||||
|
- Rollback possible
|
||||||
|
- Déploiement automatisé
|
||||||
|
|
||||||
|
**Implémentation** :
|
||||||
|
- Fichiers `XX_nom_migration.sql`
|
||||||
|
- Exécution via `prisma migrate deploy`
|
||||||
|
- User `app_admin` pour DDL
|
||||||
|
- User `app_user` pour DML (runtime)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Décisions Tests
|
||||||
|
|
||||||
|
### 23. Stratégie de tests
|
||||||
|
|
||||||
|
**Décision** : ✅ **Tests unitaires + intégration + E2E**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Qualité code
|
||||||
|
- Confiance déploiement
|
||||||
|
- Détection régression
|
||||||
|
|
||||||
|
**Couverture cible** :
|
||||||
|
- Tests unitaires : > 80%
|
||||||
|
- Tests intégration : Workflows complets
|
||||||
|
- Tests E2E : Parcours utilisateurs critiques
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Décisions Documentation
|
||||||
|
|
||||||
|
### 24. Structure documentation
|
||||||
|
|
||||||
|
**Décision** : ✅ **Documentation centralisée dans `/docs/` avec numérotation**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Lisibilité (ordre logique)
|
||||||
|
- Maintenance (tout au même endroit)
|
||||||
|
- Versioning (Git)
|
||||||
|
|
||||||
|
**Structure** :
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── 00_INDEX.md
|
||||||
|
├── 01_CAHIER-DES-CHARGES.md
|
||||||
|
├── 02_ARCHITECTURE.md
|
||||||
|
├── 03_DEPLOYMENT.md
|
||||||
|
├── 10_DATABASE.md
|
||||||
|
├── 11_API.md
|
||||||
|
├── 20_WORKFLOW-CREATION-COMPTE.md
|
||||||
|
├── 21_CONFIGURATION-SYSTEME.md
|
||||||
|
├── 22_DOCUMENTS-LEGAUX.md
|
||||||
|
├── 23_LISTE-TICKETS.md
|
||||||
|
├── 24_DECISIONS-PROJET.md (ce document)
|
||||||
|
├── 90_AUDIT.md
|
||||||
|
└── test-data/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Décisions Workflow
|
||||||
|
|
||||||
|
### 25. Gitea + Webhook + Déploiement automatique
|
||||||
|
|
||||||
|
**Décision** : ✅ **Déploiement automatique sur push master**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Productivité (pas de déploiement manuel)
|
||||||
|
- Fiabilité (script testé)
|
||||||
|
- Rapidité (feedback immédiat)
|
||||||
|
|
||||||
|
**Workflow** :
|
||||||
|
1. Push sur `master`
|
||||||
|
2. Webhook Gitea déclenché
|
||||||
|
3. Script `deploy-ptitspas.sh` exécuté
|
||||||
|
4. Git pull + Migrations + Docker rebuild + Restart
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 26. Branches Git
|
||||||
|
|
||||||
|
**Décision** : ✅ **Stratégie simple : master + feature branches**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Simplicité (petite équipe)
|
||||||
|
- Flexibilité
|
||||||
|
|
||||||
|
**Branches** :
|
||||||
|
- `master` : Production
|
||||||
|
- `archive/*` : Archives (ex: maquette initiale)
|
||||||
|
- `migration/*` : Migrations (ex: intégration YNOV)
|
||||||
|
- `feature/*` : Nouvelles fonctionnalités
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎫 Décisions Ticketing
|
||||||
|
|
||||||
|
### 27. Taille des tickets
|
||||||
|
|
||||||
|
**Décision** : ✅ **Tickets relativement petits (2-6h)**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Granularité (suivi précis)
|
||||||
|
- Motivation (tickets terminables rapidement)
|
||||||
|
- Revue de code facilitée
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 28. Organisation tickets
|
||||||
|
|
||||||
|
**Décision** : ✅ **1 ticket = 1 fonctionnalité complète (Front + Back + BDD si nécessaire)**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Cohérence (toute la feature développée ensemble)
|
||||||
|
- Testable (end-to-end immédiatement)
|
||||||
|
- Atomique (livraison de valeur fonctionnelle)
|
||||||
|
|
||||||
|
**Alternative rejetée** :
|
||||||
|
- ❌ 1 ticket Front + 1 ticket Back + 1 ticket BDD = Dépendances complexes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Décisions Déploiement
|
||||||
|
|
||||||
|
### 29. Docker Compose vs Kubernetes
|
||||||
|
|
||||||
|
**Décision** : ✅ **Docker Compose**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Simplicité (on-premise petite/moyenne collectivité)
|
||||||
|
- Pas de besoin de scalabilité horizontale
|
||||||
|
- Maintenance facile
|
||||||
|
|
||||||
|
**Alternative rejetée** :
|
||||||
|
- ❌ Kubernetes : Overkill pour on-premise mono-instance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 30. Traefik comme reverse proxy
|
||||||
|
|
||||||
|
**Décision** : ✅ **Traefik**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Configuration automatique (labels Docker)
|
||||||
|
- SSL Let's Encrypt automatique
|
||||||
|
- Dashboard intégré
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Logs & Monitoring
|
||||||
|
|
||||||
|
### 31. Système de logs
|
||||||
|
|
||||||
|
**Décision** : ✅ **Winston avec rotation quotidienne**
|
||||||
|
|
||||||
|
**Justification** :
|
||||||
|
- Standard NestJS
|
||||||
|
- Rotation automatique (pas de disque plein)
|
||||||
|
- Niveaux de log (info, warn, error)
|
||||||
|
- Transports multiples (console + fichier)
|
||||||
|
|
||||||
|
**Logs à capturer** :
|
||||||
|
- Logs applicatifs (info, warn, error)
|
||||||
|
- Logs emails (succès/échec SMTP)
|
||||||
|
- Logs connexions (qui se connecte quand)
|
||||||
|
- Logs validations (qui valide quoi)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Résumé des décisions critiques
|
||||||
|
|
||||||
|
| # | Décision | Statut | Impact |
|
||||||
|
|---|----------|--------|--------|
|
||||||
|
| 1 | Mono-repo | ✅ Phase 1 | Architecture |
|
||||||
|
| 2 | On-premise avec config dynamique | ✅ Phase 1 | Déploiement |
|
||||||
|
| 3 | Système de fichiers pour uploads | ✅ Phase 1 | Stockage |
|
||||||
|
| 4 | Versioning documents légaux | ✅ Phase 1 | RGPD |
|
||||||
|
| 5 | Inscription sans MDP | ✅ Phase 1 | UX |
|
||||||
|
| 6 | Genre enfant obligatoire (H/F) | ✅ Phase 1 | Métier |
|
||||||
|
| 7 | Un seul champ téléphone | ✅ Phase 1 | Simplification |
|
||||||
|
| 8 | NIR obligatoire AM | ✅ Phase 1 | Conformité |
|
||||||
|
| 9 | Suppression SMS | ✅ Phase 1 | Simplification |
|
||||||
|
| 10 | Renvoyer email | ⏸️ Phase 2 | Nice-to-have |
|
||||||
|
| 11 | Sauvegarde auto | ⏸️ Phase finale | Ops |
|
||||||
|
| 12 | RGPD avancé | ⏸️ Phase 2 | Conformité |
|
||||||
|
| 13 | Statistiques | ⏸️ Phase 2 | Business |
|
||||||
|
| 14 | Migration données | ❌ Rejeté | N/A |
|
||||||
|
| 15 | Doc utilisateur | ⏸️ Phase 2 | Formation |
|
||||||
|
| 31 | Logs Winston | ✅ Phase 1 | Monitoring |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Historique des modifications
|
||||||
|
|
||||||
|
| Date | Version | Modifications |
|
||||||
|
|------|---------|---------------|
|
||||||
|
| 25/11/2025 | 1.0 | Création du document - Toutes les décisions initiales |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Dernière mise à jour** : 25 Novembre 2025
|
||||||
|
**Version** : 1.0
|
||||||
|
**Statut** : ✅ Document validé
|
||||||
|
|
||||||
433
docs/25_PHASE-2-BACKLOG.md
Normal file
433
docs/25_PHASE-2-BACKLOG.md
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
# 📋 Backlog Phase 2 - P'titsPas
|
||||||
|
|
||||||
|
**Version** : 1.0
|
||||||
|
**Date** : 25 Novembre 2025
|
||||||
|
**Auteur** : Équipe PtitsPas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Objectif de ce document
|
||||||
|
|
||||||
|
Ce document liste toutes les **fonctionnalités reportées en Phase 2**. Ces fonctionnalités ne sont pas bloquantes pour le MVP (Phase 1), mais apportent de la valeur ajoutée pour la production intensive.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Vue d'ensemble
|
||||||
|
|
||||||
|
| Catégorie | Nombre de tickets | Estimation |
|
||||||
|
|-----------|-------------------|------------|
|
||||||
|
| **RGPD avancé** | 3 tickets | ~8h |
|
||||||
|
| **Monitoring avancé** | 2 tickets | ~6h |
|
||||||
|
| **Statistiques & Reporting** | 4 tickets | ~16h |
|
||||||
|
| **Sauvegarde & Restauration** | 2 tickets | ~6h |
|
||||||
|
| **Documentation utilisateur** | 3 tickets | ~12h |
|
||||||
|
| **Améliorations UX** | 3 tickets | ~10h |
|
||||||
|
| **TOTAL** | **17 tickets** | **~58h** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 RGPD avancé
|
||||||
|
|
||||||
|
### Ticket P2-01 : [Backend] API Suppression compte (soft delete)
|
||||||
|
**Estimation** : 3h
|
||||||
|
**Labels** : `backend`, `phase-2`, `rgpd`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Implémenter le droit à l'oubli (RGPD Article 17) avec suppression logique des comptes.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Endpoint `DELETE /api/v1/users/:id` (soft delete)
|
||||||
|
- [ ] Ajout champ `supprime_le` TIMESTAMPTZ dans `utilisateurs`
|
||||||
|
- [ ] Anonymisation données personnelles (email, téléphone, adresse)
|
||||||
|
- [ ] Conservation données légales (acceptations CGU)
|
||||||
|
- [ ] Guards (super_admin only)
|
||||||
|
- [ ] Tests unitaires
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Peu de demandes en phase test
|
||||||
|
- Suppression manuelle possible en attendant
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-02 : [Backend] API Export données personnelles (RGPD)
|
||||||
|
**Estimation** : 3h
|
||||||
|
**Labels** : `backend`, `phase-2`, `rgpd`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Implémenter le droit à la portabilité (RGPD Article 20) avec export JSON des données personnelles.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Endpoint `GET /api/v1/users/:id/export` (JSON)
|
||||||
|
- [ ] Export données utilisateur + enfants + acceptations
|
||||||
|
- [ ] Format JSON structuré
|
||||||
|
- [ ] Guards (utilisateur lui-même ou admin)
|
||||||
|
- [ ] Tests unitaires
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Export manuel SQL possible en phase test
|
||||||
|
- Peu de demandes attendues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-03 : [Backend] Cron anonymisation comptes inactifs
|
||||||
|
**Estimation** : 2h
|
||||||
|
**Labels** : `backend`, `phase-2`, `rgpd`, `cron`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Anonymiser automatiquement les comptes inactifs après X mois (configurable).
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Cron job quotidien
|
||||||
|
- [ ] Détection comptes inactifs (dernière connexion > X mois)
|
||||||
|
- [ ] Anonymisation automatique
|
||||||
|
- [ ] Notification admin
|
||||||
|
- [ ] Configuration durée inactivité (table `configuration`)
|
||||||
|
- [ ] Tests
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Pas de comptes inactifs en phase test
|
||||||
|
- Peut être fait manuellement
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Monitoring avancé
|
||||||
|
|
||||||
|
### Ticket P2-04 : [Backend] API Métriques système
|
||||||
|
**Estimation** : 3h
|
||||||
|
**Labels** : `backend`, `phase-2`, `monitoring`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Exposer des métriques système (CPU, RAM, disque, BDD) pour monitoring.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Endpoint `GET /api/v1/metrics` (super_admin only)
|
||||||
|
- [ ] Métriques système (CPU, RAM, disque)
|
||||||
|
- [ ] Métriques BDD (connexions, taille, requêtes lentes)
|
||||||
|
- [ ] Métriques application (requêtes/s, temps réponse)
|
||||||
|
- [ ] Format Prometheus (optionnel)
|
||||||
|
- [ ] Tests
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Pas critique pour MVP
|
||||||
|
- Logs suffisent pour debugging initial
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-05 : [Frontend] Dashboard Monitoring
|
||||||
|
**Estimation** : 3h
|
||||||
|
**Labels** : `frontend`, `phase-2`, `monitoring`, `admin`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Créer un dashboard de monitoring pour le super admin.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Page `/admin/monitoring` (super_admin only)
|
||||||
|
- [ ] Graphiques temps réel (CPU, RAM, disque)
|
||||||
|
- [ ] Alertes (seuils configurables)
|
||||||
|
- [ ] Historique métriques (7 jours)
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Pas critique pour MVP
|
||||||
|
- Monitoring serveur possible via outils système
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 Statistiques & Reporting
|
||||||
|
|
||||||
|
### Ticket P2-06 : [Backend] API Statistiques dashboard
|
||||||
|
**Estimation** : 4h
|
||||||
|
**Labels** : `backend`, `phase-2`, `statistiques`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Créer les endpoints pour récupérer les statistiques de l'application.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Endpoint `GET /api/v1/stats/overview`
|
||||||
|
- [ ] Statistiques globales (comptes, enfants, AM, validations)
|
||||||
|
- [ ] Statistiques temporelles (inscriptions par mois, validations par semaine)
|
||||||
|
- [ ] Statistiques géographiques (par ville)
|
||||||
|
- [ ] Cache (rafraîchissement toutes les heures)
|
||||||
|
- [ ] Tests
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Pas de données = pas de statistiques
|
||||||
|
- Nécessite application en production
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-07 : [Frontend] Widgets statistiques dashboard
|
||||||
|
**Estimation** : 4h
|
||||||
|
**Labels** : `frontend`, `phase-2`, `statistiques`, `gestionnaire`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Afficher les statistiques dans le dashboard gestionnaire.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Widget "Comptes en attente" (nombre)
|
||||||
|
- [ ] Widget "Comptes validés ce mois" (graphique)
|
||||||
|
- [ ] Widget "Enfants inscrits" (nombre)
|
||||||
|
- [ ] Widget "AM disponibles" (nombre)
|
||||||
|
- [ ] Graphique évolution inscriptions (6 derniers mois)
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Besoin d'utilisateurs réels pour avoir des données
|
||||||
|
- Fonctionnalité importante pour vente, mais pas bloquante pour MVP
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-08 : [Backend] API Export rapports (CSV/PDF)
|
||||||
|
**Estimation** : 4h
|
||||||
|
**Labels** : `backend`, `phase-2`, `reporting`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Permettre l'export de rapports pour les gestionnaires.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Endpoint `GET /api/v1/reports/users` (CSV)
|
||||||
|
- [ ] Endpoint `GET /api/v1/reports/validations` (CSV)
|
||||||
|
- [ ] Endpoint `GET /api/v1/reports/summary` (PDF)
|
||||||
|
- [ ] Génération PDF (librairie PDFKit)
|
||||||
|
- [ ] Filtres (date, statut, rôle)
|
||||||
|
- [ ] Tests
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Export manuel SQL possible en phase test
|
||||||
|
- Nécessite données réelles
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-09 : [Frontend] Écran Rapports
|
||||||
|
**Estimation** : 4h
|
||||||
|
**Labels** : `frontend`, `phase-2`, `reporting`, `gestionnaire`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Créer un écran de génération de rapports pour les gestionnaires.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Page `/admin/rapports` (gestionnaire/admin)
|
||||||
|
- [ ] Sélection type de rapport (utilisateurs, validations, synthèse)
|
||||||
|
- [ ] Filtres (date, statut, rôle)
|
||||||
|
- [ ] Prévisualisation
|
||||||
|
- [ ] Téléchargement (CSV/PDF)
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Pas prioritaire pour MVP
|
||||||
|
- Nécessite données réelles
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💾 Sauvegarde & Restauration
|
||||||
|
|
||||||
|
### Ticket P2-10 : [Infra] Script backup PostgreSQL automatique
|
||||||
|
**Estimation** : 3h
|
||||||
|
**Labels** : `infra`, `phase-2`, `backup`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Créer un script de sauvegarde automatique de la base de données.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Script `backup.sh` avec `pg_dump`
|
||||||
|
- [ ] Compression (gzip)
|
||||||
|
- [ ] Rotation (garder 30 derniers jours)
|
||||||
|
- [ ] Cron job quotidien (3h du matin)
|
||||||
|
- [ ] Notification email en cas d'échec
|
||||||
|
- [ ] Tests restauration
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Pas bloquant pour développement
|
||||||
|
- Backup manuel possible en phase test
|
||||||
|
- À faire avant mise en production
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-11 : [Doc] Guide sauvegarde & restauration
|
||||||
|
**Estimation** : 3h
|
||||||
|
**Labels** : `documentation`, `phase-2`, `backup`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Documenter les procédures de sauvegarde et restauration pour les admins sys.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Procédure sauvegarde manuelle
|
||||||
|
- [ ] Procédure restauration
|
||||||
|
- [ ] Configuration cron
|
||||||
|
- [ ] Troubleshooting
|
||||||
|
- [ ] Exemples de commandes
|
||||||
|
- [ ] Checklist pré-restauration
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Documentation technique, pas bloquante pour MVP
|
||||||
|
- À faire avant mise en production
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Documentation utilisateur
|
||||||
|
|
||||||
|
### Ticket P2-12 : [Doc] Guide utilisateur Gestionnaire
|
||||||
|
**Estimation** : 4h
|
||||||
|
**Labels** : `documentation`, `phase-2`, `formation`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Rédiger le guide utilisateur pour les gestionnaires.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Connexion et première utilisation
|
||||||
|
- [ ] Consultation des demandes
|
||||||
|
- [ ] Validation/Refus de comptes
|
||||||
|
- [ ] Gestion des paramètres
|
||||||
|
- [ ] FAQ gestionnaire
|
||||||
|
- [ ] Captures d'écran
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Formation présentiel prioritaire pour premiers utilisateurs
|
||||||
|
- Documentation nécessaire pour scalabilité
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-13 : [Doc] Guide utilisateur Parent/AM
|
||||||
|
**Estimation** : 4h
|
||||||
|
**Labels** : `documentation`, `phase-2`, `formation`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Rédiger le guide utilisateur pour les parents et assistantes maternelles.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Inscription étape par étape
|
||||||
|
- [ ] Création du mot de passe
|
||||||
|
- [ ] Connexion
|
||||||
|
- [ ] Utilisation de l'application
|
||||||
|
- [ ] FAQ parent/AM
|
||||||
|
- [ ] Captures d'écran
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Formation présentiel prioritaire
|
||||||
|
- Documentation nécessaire pour scalabilité
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-14 : [Doc] Vidéos tutoriels
|
||||||
|
**Estimation** : 4h
|
||||||
|
**Labels** : `documentation`, `phase-2`, `formation`, `video`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Créer des vidéos tutoriels pour les utilisateurs.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Vidéo "Inscription parent" (3-5 min)
|
||||||
|
- [ ] Vidéo "Inscription AM" (3-5 min)
|
||||||
|
- [ ] Vidéo "Validation comptes gestionnaire" (3-5 min)
|
||||||
|
- [ ] Vidéo "Configuration initiale admin" (5-7 min)
|
||||||
|
- [ ] Hébergement (YouTube privé ou serveur)
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Pas bloquant pour MVP
|
||||||
|
- Utile pour scalabilité et autonomie utilisateurs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 Améliorations UX
|
||||||
|
|
||||||
|
### Ticket P2-15 : [Frontend] Mode sombre
|
||||||
|
**Estimation** : 3h
|
||||||
|
**Labels** : `frontend`, `phase-2`, `ux`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Ajouter un mode sombre à l'application.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Thème sombre (couleurs, contrastes)
|
||||||
|
- [ ] Toggle mode clair/sombre
|
||||||
|
- [ ] Sauvegarde préférence utilisateur
|
||||||
|
- [ ] Adaptation tous les écrans
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Nice-to-have, pas bloquant
|
||||||
|
- Améliore confort utilisateur
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-16 : [Frontend] Notifications push (optionnel)
|
||||||
|
**Estimation** : 4h
|
||||||
|
**Labels** : `frontend`, `phase-2`, `ux`, `notifications`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Ajouter des notifications push pour les événements importants.
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Service Worker (PWA)
|
||||||
|
- [ ] Notification "Compte validé"
|
||||||
|
- [ ] Notification "Nouveau message" (si messagerie)
|
||||||
|
- [ ] Gestion permissions
|
||||||
|
- [ ] Paramètres notifications
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Nice-to-have, pas bloquant
|
||||||
|
- Nécessite PWA ou app mobile
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ticket P2-17 : [Frontend] Accessibilité (WCAG 2.1)
|
||||||
|
**Estimation** : 3h
|
||||||
|
**Labels** : `frontend`, `phase-2`, `ux`, `a11y`
|
||||||
|
|
||||||
|
**Description** :
|
||||||
|
Améliorer l'accessibilité de l'application (conformité WCAG 2.1).
|
||||||
|
|
||||||
|
**Tâches** :
|
||||||
|
- [ ] Audit accessibilité (Lighthouse, axe)
|
||||||
|
- [ ] Correction contrastes
|
||||||
|
- [ ] Attributs ARIA
|
||||||
|
- [ ] Navigation clavier
|
||||||
|
- [ ] Lecteur d'écran (test)
|
||||||
|
|
||||||
|
**Justification report Phase 2** :
|
||||||
|
- Amélioration continue
|
||||||
|
- Pas bloquant pour MVP
|
||||||
|
- Important pour collectivités (obligation légale)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Priorisation Phase 2
|
||||||
|
|
||||||
|
### Priorité Haute (à faire en premier)
|
||||||
|
1. **Sauvegarde & Restauration** (Tickets P2-10, P2-11) → Avant mise en production
|
||||||
|
2. **Statistiques** (Tickets P2-06, P2-07) → Argument de vente
|
||||||
|
3. **Documentation utilisateur** (Tickets P2-12, P2-13) → Scalabilité
|
||||||
|
|
||||||
|
### Priorité Moyenne
|
||||||
|
4. **RGPD avancé** (Tickets P2-01, P2-02, P2-03) → Conformité
|
||||||
|
5. **Reporting** (Tickets P2-08, P2-09) → Utile pour gestionnaires
|
||||||
|
|
||||||
|
### Priorité Basse
|
||||||
|
6. **Monitoring avancé** (Tickets P2-04, P2-05) → Nice-to-have
|
||||||
|
7. **Améliorations UX** (Tickets P2-15, P2-16, P2-17) → Confort
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Critères de passage en Phase 2
|
||||||
|
|
||||||
|
La Phase 2 peut commencer quand :
|
||||||
|
- ✅ Phase 1 terminée (61 tickets)
|
||||||
|
- ✅ Application déployée en production (au moins 1 collectivité)
|
||||||
|
- ✅ Utilisateurs réels (au moins 10 comptes validés)
|
||||||
|
- ✅ Feedback terrain collecté
|
||||||
|
- ✅ Bugs critiques corrigés
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Estimation globale Phase 2
|
||||||
|
|
||||||
|
**Total** : 17 tickets
|
||||||
|
**Estimation** : ~58h de développement
|
||||||
|
|
||||||
|
**Planning suggéré** :
|
||||||
|
- Sprint 1 (2 semaines) : Sauvegarde + Statistiques (26h)
|
||||||
|
- Sprint 2 (2 semaines) : Documentation + RGPD (24h)
|
||||||
|
- Sprint 3 (1 semaine) : Reporting + Améliorations UX (8h)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Dernière mise à jour** : 25 Novembre 2025
|
||||||
|
**Version** : 1.0
|
||||||
|
**Statut** : ✅ Backlog Phase 2 défini
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user