kapi-mvc-blank 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +11 -0
- package/README.md +1147 -0
- package/compare-versions.js +52 -0
- package/package.json +44 -0
- package/prisma/migrations/20260116084200_init/migration.sql +23 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +25 -0
- package/prisma.config.ts +14 -0
- package/src/__tests__/user.controller.test.js +16 -0
- package/src/app.js +121 -0
- package/src/config/swagger.js +30 -0
- package/src/jest.config.cjs +10 -0
- package/src/server.js +10 -0
- package/src/services/jwt.service.js +7 -0
- package/src/services/prisma.service.js +26 -0
- package/src/utils/logger.js +21 -0
- package/src/v1/controllers/auth.controller.js +19 -0
- package/src/v1/controllers/upload.controller.js +15 -0
- package/src/v1/controllers/user.controller.js +16 -0
- package/src/v1/middlewares/auth.middleware.js +19 -0
- package/src/v1/middlewares/role.middleware.js +8 -0
- package/src/v1/models/user.model.js +6 -0
- package/src/v1/routes/auth.routes.js +9 -0
- package/src/v1/routes/upload.routes.js +80 -0
- package/src/v1/routes/user.routes.js +49 -0
- package/src/v1/services/jwt.service.js +7 -0
- package/test-api/index.php +47 -0
- package/test-endpoints.js +51 -0
- package/test-setup.js +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,1147 @@
|
|
|
1
|
+
# API MVC Node - Documentation Développeurs
|
|
2
|
+
|
|
3
|
+
Bienvenue dans le projet **API MVC Node**. Cette documentation est destinée aux développeurs pour comprendre l'architecture, les conventions et les procédures de développement.
|
|
4
|
+
|
|
5
|
+
## 📋 Table des matières
|
|
6
|
+
|
|
7
|
+
- [Vue d'ensemble](#vue-densemble)
|
|
8
|
+
- [Architecture](#architecture)
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Configuration](#configuration)
|
|
11
|
+
- [Démarrage](#démarrage)
|
|
12
|
+
- [Structure du projet](#structure-du-projet)
|
|
13
|
+
- [API Endpoints](#api-endpoints)
|
|
14
|
+
- [Versioning](#versioning)
|
|
15
|
+
- [Authentification](#authentification)
|
|
16
|
+
- [Téléchargement de fichiers](#téléchargement-de-fichiers)
|
|
17
|
+
- [Logging](#logging)
|
|
18
|
+
- [Tests](#tests)
|
|
19
|
+
- [Documentation Swagger](#documentation-swagger)
|
|
20
|
+
- [Conventions de code](#conventions-de-code)
|
|
21
|
+
- [Dépannage](#dépannage)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🎯 Vue d'ensemble
|
|
26
|
+
|
|
27
|
+
Ce projet est une API REST construite avec **Express.js** en utilisant le pattern **MVC** (Model-View-Controller). Elle supporte le **versioning d'API** avec des routes séparées pour chaque version (v1, v2, etc.).
|
|
28
|
+
|
|
29
|
+
**Stack technologique :**
|
|
30
|
+
- Node.js (ES modules)
|
|
31
|
+
- Express 5.x
|
|
32
|
+
- JWT pour l'authentification
|
|
33
|
+
- Multer pour les uploads de fichiers
|
|
34
|
+
- Pino pour le logging
|
|
35
|
+
- Jest pour les tests
|
|
36
|
+
- Swagger/OpenAPI pour la documentation
|
|
37
|
+
- Rate limiting pour la protection
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🏗️ Architecture
|
|
42
|
+
|
|
43
|
+
### Pattern MVC
|
|
44
|
+
|
|
45
|
+
Le projet suit une architecture MVC structurée par version d'API :
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
src/
|
|
49
|
+
├── v1/ # API Version 1
|
|
50
|
+
│ ├── controllers/ # Logique métier
|
|
51
|
+
│ ├── routes/ # Définition des routes
|
|
52
|
+
│ ├── middlewares/ # Middlewares personnalisés
|
|
53
|
+
│ ├── models/ # Schémas de données
|
|
54
|
+
│ └── services/ # Services réutilisables
|
|
55
|
+
├── v2/ # API Version 2
|
|
56
|
+
│ ├── controllers/ # Logique métier
|
|
57
|
+
│ ├── routes/ # Définition des routes
|
|
58
|
+
│ ├── middlewares/ # Middlewares personnalisés
|
|
59
|
+
│ ├── models/ # Schémas de données
|
|
60
|
+
│ └── services/ # Services réutilisables
|
|
61
|
+
├── config/ # Configuration (Swagger, etc.)
|
|
62
|
+
├── services/ # Services partagés (JWT, etc.)
|
|
63
|
+
├── utils/ # Utilitaires (Logger, etc.)
|
|
64
|
+
├── app.js # Configuration Express
|
|
65
|
+
└── server.js # Entrée du serveur
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Détails des répertoires
|
|
69
|
+
|
|
70
|
+
| Répertoire | Rôle |
|
|
71
|
+
|-----------|------|
|
|
72
|
+
| `controllers/` | Traitement des requêtes, appel de services, envoi de réponses |
|
|
73
|
+
| `routes/` | Définition des endpoints, validation, middleware d'authentification |
|
|
74
|
+
| `middlewares/` | Authentification, autorisation, validation |
|
|
75
|
+
| `models/` | Schémas et validations de données |
|
|
76
|
+
| `services/` | Logique métier réutilisable, requêtes BD, calculs |
|
|
77
|
+
| `config/` | Configuration globale (Swagger, BD, etc.) |
|
|
78
|
+
|
|
79
|
+
### Principes architecturaux
|
|
80
|
+
|
|
81
|
+
- **Séparation des préoccupations** : Chaque couche a une responsabilité unique
|
|
82
|
+
- **Réutilisabilité** : Services partagés entre versions
|
|
83
|
+
- **Versioning** : Maintenance parallèle de plusieurs versions
|
|
84
|
+
- **Maintenabilité** : Code organisé et facile à naviguer
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 💻 Installation
|
|
89
|
+
|
|
90
|
+
### Prérequis
|
|
91
|
+
|
|
92
|
+
- **Node.js** >= 16.x
|
|
93
|
+
- **npm** ou **yarn**
|
|
94
|
+
- Un gestionnaire d'environnement (`.env`)
|
|
95
|
+
|
|
96
|
+
### Étapes
|
|
97
|
+
|
|
98
|
+
1. **Cloner le repository**
|
|
99
|
+
```bash
|
|
100
|
+
git clone <repository-url>
|
|
101
|
+
cd api-mvc-node
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
2. **Installer les dépendances**
|
|
105
|
+
```bash
|
|
106
|
+
npm install
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
3. **Configurer l'environnement**
|
|
110
|
+
```bash
|
|
111
|
+
cp .env.example .env
|
|
112
|
+
# Éditer .env avec vos configurations
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
4. **Configurer Prisma**
|
|
116
|
+
```bash
|
|
117
|
+
npx prisma migrate dev --name init
|
|
118
|
+
npx prisma generate
|
|
119
|
+
# if is their some trouble please read prisma doc
|
|
120
|
+
# you probable nead model in schema.prisma or complete tour DSN in tour .env
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
5. **Vérifier l'installation**
|
|
124
|
+
```bash
|
|
125
|
+
npm run dev
|
|
126
|
+
```
|
|
127
|
+
Le serveur devrait démarrer sur `http://localhost:3000`
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## ⚙️ Configuration
|
|
132
|
+
|
|
133
|
+
### Variables d'environnement (`.env`)
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Serveur
|
|
137
|
+
PORT=3000
|
|
138
|
+
NODE_ENV=development
|
|
139
|
+
|
|
140
|
+
# JWT
|
|
141
|
+
JWT_SECRET=your_super_secret_key_change_in_production
|
|
142
|
+
|
|
143
|
+
# Téléchargements
|
|
144
|
+
UPLOAD_DIR=uploads
|
|
145
|
+
UPLOAD_MAX_SIZE=5242880 # 5MB en bytes
|
|
146
|
+
|
|
147
|
+
# Logging
|
|
148
|
+
LOG_DIR=logs
|
|
149
|
+
LOG_FILE=app.log
|
|
150
|
+
LOG_LEVEL=info # debug, info, warn, error
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### .env.example
|
|
154
|
+
|
|
155
|
+
Créez un fichier `.env.example` à la racine avec les variables modèles :
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# Server Configuration
|
|
159
|
+
PORT=3000
|
|
160
|
+
NODE_ENV=development
|
|
161
|
+
|
|
162
|
+
# Security
|
|
163
|
+
JWT_SECRET=change_me_in_production
|
|
164
|
+
|
|
165
|
+
# File Upload
|
|
166
|
+
UPLOAD_DIR=uploads
|
|
167
|
+
UPLOAD_MAX_SIZE=5242880
|
|
168
|
+
|
|
169
|
+
# Logging
|
|
170
|
+
LOG_DIR=logs
|
|
171
|
+
LOG_FILE=app.log
|
|
172
|
+
LOG_LEVEL=info
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Variables importantes
|
|
176
|
+
|
|
177
|
+
| Variable | Description | Défaut |
|
|
178
|
+
|----------|-------------|--------|
|
|
179
|
+
| `PORT` | Port du serveur | `3000` |
|
|
180
|
+
| `JWT_SECRET` | Clé secrète JWT | (obligatoire) |
|
|
181
|
+
| `UPLOAD_DIR` | Répertoire des uploads | `uploads` |
|
|
182
|
+
| `UPLOAD_MAX_SIZE` | Taille max des fichiers (bytes) | `5242880` (5MB) |
|
|
183
|
+
| `LOG_LEVEL` | Niveau de log | `info` |
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 🚀 Démarrage
|
|
188
|
+
|
|
189
|
+
### Mode développement (avec auto-reload)
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npm run dev
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
- Utilise **Nodemon** pour redémarrer le serveur automatiquement
|
|
196
|
+
- Parfait pour le développement local
|
|
197
|
+
- Les modifications sont détectées en temps réel
|
|
198
|
+
|
|
199
|
+
### Mode production
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npm start
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
- Lance directement le serveur sans surveillance
|
|
206
|
+
- Port par défaut : `3000` (configurable via `PORT`)
|
|
207
|
+
- Optimisé pour les performances
|
|
208
|
+
|
|
209
|
+
### Vérifier le démarrage
|
|
210
|
+
|
|
211
|
+
Une fois démarré, vous verrez dans la console :
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
🚀 Server running on port 3000
|
|
215
|
+
✓ Route registered: /api/v1/auth
|
|
216
|
+
✓ Route registered: /api/v1/users
|
|
217
|
+
✓ Route registered: /api/v1/uploads
|
|
218
|
+
✓ Route registered: /api/v2/auth
|
|
219
|
+
✓ Route registered: /api/v2/users
|
|
220
|
+
✓ Route registered: /api/v2/uploads
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 📁 Structure du projet
|
|
226
|
+
|
|
227
|
+
### Fichiers racine
|
|
228
|
+
|
|
229
|
+
| Fichier | Objectif |
|
|
230
|
+
|---------|----------|
|
|
231
|
+
| `package.json` | Dépendances et scripts npm |
|
|
232
|
+
| `.env` | Variables d'environnement (à créer) |
|
|
233
|
+
| `.env.example` | Modèle de configuration |
|
|
234
|
+
| `.gitignore` | Fichiers à ignorer dans Git |
|
|
235
|
+
| `jest.config.cjs` | Configuration des tests |
|
|
236
|
+
|
|
237
|
+
### Répertoires principaux
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
src/
|
|
241
|
+
├── app.js # Instance Express + initialisation routes
|
|
242
|
+
├── server.js # Point d'entrée (PORT + démarrage)
|
|
243
|
+
├── config/
|
|
244
|
+
│ └── swagger.js # Configuration OpenAPI/Swagger
|
|
245
|
+
├── services/
|
|
246
|
+
│ └── jwt.service.js # Gestion des JWT
|
|
247
|
+
├── utils/
|
|
248
|
+
│ └── logger.js # Configuration Pino logger
|
|
249
|
+
├── v1/ et v2/ # Versions de l'API
|
|
250
|
+
└── __tests__/
|
|
251
|
+
└── user.controller.test.js # Tests unitaires
|
|
252
|
+
|
|
253
|
+
logs/ # Fichiers journaux (généré à runtime)
|
|
254
|
+
uploads/ # Fichiers uploadés (généré à runtime)
|
|
255
|
+
test-api/ # Scripts de test API
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Fichiers d'exécution
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
compare-versions.js # Script pour comparer v1 vs v2
|
|
262
|
+
test-endpoints.js # Script pour tester les endpoints
|
|
263
|
+
test-setup.js # Configuration des tests
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 🔌 API Endpoints
|
|
269
|
+
|
|
270
|
+
### Endpoints v1
|
|
271
|
+
|
|
272
|
+
| Méthode | Endpoint | Description | Auth |
|
|
273
|
+
|---------|----------|-------------|------|
|
|
274
|
+
| `GET` | `/api/v1/users/info` | Info basique de l'API | ❌ |
|
|
275
|
+
| `GET` | `/api/v1/users/profile` | Profil utilisateur | ✅ JWT |
|
|
276
|
+
| `POST` | `/api/v1/uploads/:name` | Upload une image | ❌ |
|
|
277
|
+
|
|
278
|
+
### Endpoints v2
|
|
279
|
+
|
|
280
|
+
| Méthode | Endpoint | Description | Différences |
|
|
281
|
+
|---------|----------|-------------|-------------|
|
|
282
|
+
| `GET` | `/api/v2/users/info` | Info enrichie (OS, hostname) | Inclut api_version, os, arch, hostname |
|
|
283
|
+
| `GET` | `/api/v2/users/profile` | Profil utilisateur | Identique à v1 |
|
|
284
|
+
| `POST` | `/api/v2/uploads/:name` | Upload une image | Identique à v1 |
|
|
285
|
+
|
|
286
|
+
### Exemples de requêtes
|
|
287
|
+
|
|
288
|
+
**v1 - Info basique :**
|
|
289
|
+
```bash
|
|
290
|
+
curl http://localhost:3000/api/v1/users/info
|
|
291
|
+
```
|
|
292
|
+
Réponse (200) :
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"msg": "ok"
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**v2 - Info enrichie :**
|
|
300
|
+
```bash
|
|
301
|
+
curl http://localhost:3000/api/v2/users/info
|
|
302
|
+
```
|
|
303
|
+
Réponse (200) :
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"msg": "ok",
|
|
307
|
+
"api_version": "v2",
|
|
308
|
+
"os": "linux",
|
|
309
|
+
"arch": "x64",
|
|
310
|
+
"hostname": "server-01"
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Profil utilisateur (avec JWT) :**
|
|
315
|
+
```bash
|
|
316
|
+
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
|
|
317
|
+
http://localhost:3000/api/v1/users/profile
|
|
318
|
+
```
|
|
319
|
+
Réponse (200) :
|
|
320
|
+
```json
|
|
321
|
+
{
|
|
322
|
+
"id": "user123",
|
|
323
|
+
"role": "admin"
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Upload d'image :**
|
|
328
|
+
```bash
|
|
329
|
+
curl -X POST -F "file=@image.jpg" \
|
|
330
|
+
http://localhost:3000/api/v1/uploads/my-image
|
|
331
|
+
```
|
|
332
|
+
Réponse (201) :
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"url": "uploads/my-image.jpg",
|
|
336
|
+
"size": 15234,
|
|
337
|
+
"mimetype": "image/jpeg"
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## RAD-api
|
|
344
|
+
|
|
345
|
+
Pour générer les routes, controller, models vous pouvez utiliser notre rad
|
|
346
|
+
|
|
347
|
+
### Route
|
|
348
|
+
Générez un fichier de route complet pour une table spécifique :
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
npx kapi generate route <tableName>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
#### Exemple :
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
npx kapi generate route citations
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Controller
|
|
361
|
+
Générez un fichier de controller complet pour une table spécifique :
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
npx kapi generate controller <tableName>
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
#### Exemple :
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
npx kapi generate controller citations
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Model
|
|
374
|
+
Générez un fichier de model complet pour une table spécifique :
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
npx kapi generate model <tableName>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
#### Exemple :
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
npx kapi generate model citations
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 🔄 Versioning
|
|
389
|
+
|
|
390
|
+
### Stratégie de versioning
|
|
391
|
+
|
|
392
|
+
- **Compatibilité descendante** : Les clients utilisant v1 ne sont pas affectés par les changements en v2
|
|
393
|
+
- **Évolution progressive** : Déployer des améliorations sans briser l'API existante
|
|
394
|
+
- **Maintenance parallèle** : Pouvoir supporter plusieurs versions pendant la transition
|
|
395
|
+
|
|
396
|
+
### Structure d'une version
|
|
397
|
+
|
|
398
|
+
Chaque version possède sa propre structure indépendante :
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
src/v1/
|
|
402
|
+
├── controllers/ # Logique spécifique à v1
|
|
403
|
+
├── routes/ # Routes v1
|
|
404
|
+
├── middlewares/ # Middlewares v1
|
|
405
|
+
├── models/ # Models v1
|
|
406
|
+
└── services/ # Services v1
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Ajouter une nouvelle version (v3)
|
|
410
|
+
|
|
411
|
+
1. **Créer la structure**
|
|
412
|
+
```bash
|
|
413
|
+
mkdir -p src/v3/{controllers,routes,middlewares,models,services}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
2. **Créer les fichiers** (copier depuis v2 et adapter)
|
|
417
|
+
```
|
|
418
|
+
src/v3/
|
|
419
|
+
├── controllers/
|
|
420
|
+
├── routes/
|
|
421
|
+
├── middlewares/
|
|
422
|
+
├── models/
|
|
423
|
+
└── services/
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
3. **Le système découvre automatiquement** les nouvelles routes grâce au code dans [app.js](src/app.js) :
|
|
427
|
+
```javascript
|
|
428
|
+
const versionDirs = fs.readdirSync(versionsDir)
|
|
429
|
+
.filter(file => /^v\d+$/.test(file));
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
4. **Les endpoints seront disponibles** sur `/api/v3/...`
|
|
433
|
+
|
|
434
|
+
### Différences entre versions
|
|
435
|
+
|
|
436
|
+
Utilisez `compare-versions.js` pour comparer les réponses entre versions :
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
node compare-versions.js
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## 🔐 Authentification
|
|
445
|
+
|
|
446
|
+
### JWT (JSON Web Tokens)
|
|
447
|
+
|
|
448
|
+
L'authentification utilise **JWT** avec les éléments suivants :
|
|
449
|
+
|
|
450
|
+
**Génération d'un token :**
|
|
451
|
+
```javascript
|
|
452
|
+
import jwt from 'jsonwebtoken';
|
|
453
|
+
|
|
454
|
+
const token = jwt.sign(
|
|
455
|
+
{ id: userId, role: userRole },
|
|
456
|
+
process.env.JWT_SECRET,
|
|
457
|
+
{ expiresIn: '24h' }
|
|
458
|
+
);
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**Vérification via middleware :**
|
|
462
|
+
|
|
463
|
+
Utilisez le middleware `authMiddleware` sur les routes protégées :
|
|
464
|
+
|
|
465
|
+
```javascript
|
|
466
|
+
import { Router } from 'express';
|
|
467
|
+
import { profile } from '../controllers/user.controller.js';
|
|
468
|
+
import { authMiddleware } from '../middlewares/auth.middleware.js';
|
|
469
|
+
|
|
470
|
+
const router = Router();
|
|
471
|
+
router.get('/profile', authMiddleware, profile);
|
|
472
|
+
|
|
473
|
+
export default router;
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
**Header requis pour les endpoints protégés :**
|
|
477
|
+
```
|
|
478
|
+
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Flux d'authentification
|
|
482
|
+
|
|
483
|
+
1. **Utilisateur envoie ses identifiants** au endpoint de login
|
|
484
|
+
2. **Serveur valide et génère un JWT**
|
|
485
|
+
3. **Client stocke le token** (localStorage, sessionStorage, cookie)
|
|
486
|
+
4. **Client envoie le token** dans chaque requête protégée
|
|
487
|
+
5. **Middleware valide le token** avant d'exécuter la route
|
|
488
|
+
|
|
489
|
+
### Erreurs d'authentification
|
|
490
|
+
|
|
491
|
+
| Code | Message | Cause |
|
|
492
|
+
|------|---------|-------|
|
|
493
|
+
| `401` | `"Token missing"` | Header Authorization absent |
|
|
494
|
+
| `401` | `"Invalid token"` | Token malformé, expiré ou clé incorrecte |
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## 📤 Téléchargement de fichiers
|
|
499
|
+
|
|
500
|
+
### Configuration
|
|
501
|
+
|
|
502
|
+
- **Répertoire** : `uploads/` (configurable via `UPLOAD_DIR`)
|
|
503
|
+
- **Taille max** : 5 MB (configurable via `UPLOAD_MAX_SIZE`)
|
|
504
|
+
- **Types autorisés** : JPEG, PNG, GIF, WebP
|
|
505
|
+
- **Sécurité** : Noms de fichiers sanitisés
|
|
506
|
+
|
|
507
|
+
### Utilisation
|
|
508
|
+
|
|
509
|
+
**Endpoint :**
|
|
510
|
+
```
|
|
511
|
+
POST /api/v1/uploads/:name
|
|
512
|
+
POST /api/v2/uploads/:name
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Exemple avec curl :**
|
|
516
|
+
```bash
|
|
517
|
+
curl -X POST -F "file=@photo.png" \
|
|
518
|
+
http://localhost:3000/api/v1/uploads/my-photo
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**Exemple avec JavaScript/Fetch :**
|
|
522
|
+
```javascript
|
|
523
|
+
const formData = new FormData();
|
|
524
|
+
formData.append('file', fileInput.files[0]);
|
|
525
|
+
|
|
526
|
+
const response = await fetch('/api/v1/uploads/my-photo', {
|
|
527
|
+
method: 'POST',
|
|
528
|
+
body: formData
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
const data = await response.json();
|
|
532
|
+
console.log(data.url); // uploads/my-photo.png
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Réponse réussie (201) :**
|
|
536
|
+
```json
|
|
537
|
+
{
|
|
538
|
+
"url": "uploads/my-photo.png",
|
|
539
|
+
"size": 15234,
|
|
540
|
+
"mimetype": "image/png"
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**Erreurs possibles :**
|
|
545
|
+
|
|
546
|
+
| Code | Cause | Solution |
|
|
547
|
+
|------|-------|----------|
|
|
548
|
+
| `400` | Fichier manquant ou type non autorisé | Vérifier le type MIME (JPEG, PNG, GIF, WebP) |
|
|
549
|
+
| `413` | Fichier trop volumineux | Réduire la taille ou augmenter `UPLOAD_MAX_SIZE` |
|
|
550
|
+
|
|
551
|
+
### Accéder aux fichiers uploadés
|
|
552
|
+
|
|
553
|
+
Une fois uploadés, les fichiers sont accessibles via :
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
http://localhost:3000/uploads/my-photo.png
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
Le serveur expose le répertoire `uploads/` en tant que ressource statique.
|
|
560
|
+
|
|
561
|
+
### Configuration de la taille
|
|
562
|
+
|
|
563
|
+
Pour augmenter la limite de taille d'upload, modifiez `.env` :
|
|
564
|
+
|
|
565
|
+
```bash
|
|
566
|
+
# 10 MB
|
|
567
|
+
UPLOAD_MAX_SIZE=10485760
|
|
568
|
+
|
|
569
|
+
# 50 MB
|
|
570
|
+
UPLOAD_MAX_SIZE=52428800
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
## 📊 Logging
|
|
576
|
+
|
|
577
|
+
### Configuration
|
|
578
|
+
|
|
579
|
+
Le projet utilise **Pino** pour un logging performant et structuré. Les logs sont enregistrés dans des fichiers et affichés dans la console.
|
|
580
|
+
|
|
581
|
+
**Variables d'environnement :**
|
|
582
|
+
```bash
|
|
583
|
+
LOG_DIR=logs # Répertoire des logs
|
|
584
|
+
LOG_FILE=app.log # Nom du fichier
|
|
585
|
+
LOG_LEVEL=info # Niveaux: debug, info, warn, error
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Niveaux de log
|
|
589
|
+
|
|
590
|
+
| Niveau | Utilisation | Exemple |
|
|
591
|
+
|--------|------------|---------|
|
|
592
|
+
| `debug` | Informations détaillées pour débogage | Données temporaires, état intermédiaire |
|
|
593
|
+
| `info` | Événements normaux | Requêtes reçues, opérations complétées |
|
|
594
|
+
| `warn` | Avertissements | Erreurs résolubles, fichiers manquants |
|
|
595
|
+
| `error` | Erreurs graves | Exceptions non gérées, BD inaccessible |
|
|
596
|
+
|
|
597
|
+
### Accéder au logger dans le code
|
|
598
|
+
|
|
599
|
+
**Dans les contrôleurs (via middleware Pino) :**
|
|
600
|
+
```javascript
|
|
601
|
+
export const profile = (req, res) => {
|
|
602
|
+
req.log?.info({ userId: req.user?.id }, 'profile requested');
|
|
603
|
+
res.json({ id: req.user.id, role: req.user.role });
|
|
604
|
+
};
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
**Dans les services :**
|
|
608
|
+
```javascript
|
|
609
|
+
import { logger } from '../utils/logger.js';
|
|
610
|
+
|
|
611
|
+
export const userService = {
|
|
612
|
+
getUser(id) {
|
|
613
|
+
logger.debug({ userId: id }, 'Fetching user');
|
|
614
|
+
// logique métier
|
|
615
|
+
logger.info({ userId: id }, 'User fetched');
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### Changer le niveau de log
|
|
621
|
+
|
|
622
|
+
**Temporairement (en ligne de commande) :**
|
|
623
|
+
```bash
|
|
624
|
+
LOG_LEVEL=debug npm run dev
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
**Permanemment (dans .env) :**
|
|
628
|
+
```bash
|
|
629
|
+
LOG_LEVEL=debug
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### Fichiers générés
|
|
633
|
+
|
|
634
|
+
Les logs sont écrits dans `logs/app.log` et affichés dans la console. Format :
|
|
635
|
+
|
|
636
|
+
```json
|
|
637
|
+
{"level":30,"time":"2026-01-15T10:30:45.123Z","pid":1234,"hostname":"server-01","msg":"profile requested","userId":"user123"}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## 🧪 Tests
|
|
643
|
+
|
|
644
|
+
### Exécuter les tests
|
|
645
|
+
|
|
646
|
+
```bash
|
|
647
|
+
# Mode once (une seule exécution)
|
|
648
|
+
npm test
|
|
649
|
+
|
|
650
|
+
# Mode watch (détecte les changements)
|
|
651
|
+
npm run test:watch
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Framework et outils
|
|
655
|
+
|
|
656
|
+
- **Jest** : Framework de test avec assertions
|
|
657
|
+
- **Supertest** : Testing des routes HTTP sans serveur externe
|
|
658
|
+
|
|
659
|
+
### Écrire un test
|
|
660
|
+
|
|
661
|
+
Exemple d'un test d'endpoint :
|
|
662
|
+
|
|
663
|
+
```javascript
|
|
664
|
+
import request from 'supertest';
|
|
665
|
+
import app, { initializeRoutes } from '../app.js';
|
|
666
|
+
|
|
667
|
+
describe('User Controller', () => {
|
|
668
|
+
beforeAll(async () => {
|
|
669
|
+
// Initialiser les routes avant les tests
|
|
670
|
+
await initializeRoutes();
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
test('GET /api/v1/users/info returns 200 and msg', async () => {
|
|
674
|
+
const res = await request(app)
|
|
675
|
+
.get('/api/v1/users/info')
|
|
676
|
+
.expect(200);
|
|
677
|
+
|
|
678
|
+
expect(res.body).toEqual({ msg: 'ok' });
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
test('GET /api/v2/users/info returns enhanced info', async () => {
|
|
682
|
+
const res = await request(app)
|
|
683
|
+
.get('/api/v2/users/info')
|
|
684
|
+
.expect(200);
|
|
685
|
+
|
|
686
|
+
expect(res.body).toHaveProperty('api_version');
|
|
687
|
+
expect(res.body).toHaveProperty('os');
|
|
688
|
+
expect(res.body).toHaveProperty('arch');
|
|
689
|
+
expect(res.body).toHaveProperty('hostname');
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### Test d'authentification
|
|
695
|
+
|
|
696
|
+
```javascript
|
|
697
|
+
test('GET /api/v1/users/profile without token returns 401', async () => {
|
|
698
|
+
const res = await request(app)
|
|
699
|
+
.get('/api/v1/users/profile')
|
|
700
|
+
.expect(401);
|
|
701
|
+
|
|
702
|
+
expect(res.body.error).toBe('Token missing');
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
test('GET /api/v1/users/profile with token returns 200', async () => {
|
|
706
|
+
const token = jwt.sign({ id: 'user1', role: 'admin' }, process.env.JWT_SECRET);
|
|
707
|
+
|
|
708
|
+
const res = await request(app)
|
|
709
|
+
.get('/api/v1/users/profile')
|
|
710
|
+
.set('Authorization', `Bearer ${token}`)
|
|
711
|
+
.expect(200);
|
|
712
|
+
|
|
713
|
+
expect(res.body.id).toBe('user1');
|
|
714
|
+
});
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
### Localisation des tests
|
|
718
|
+
|
|
719
|
+
Les tests sont organisés dans `src/__tests__/` :
|
|
720
|
+
- `user.controller.test.js` : Tests du contrôleur utilisateur
|
|
721
|
+
- `auth.controller.test.js` : Tests d'authentification (à ajouter)
|
|
722
|
+
- `upload.controller.test.js` : Tests d'upload (à ajouter)
|
|
723
|
+
|
|
724
|
+
### Configuration Jest
|
|
725
|
+
|
|
726
|
+
Le fichier `src/jest.config.cjs` configure Jest pour ES modules. Vérifiez qu'il contient :
|
|
727
|
+
|
|
728
|
+
```javascript
|
|
729
|
+
export default {
|
|
730
|
+
testEnvironment: 'node',
|
|
731
|
+
transform: {},
|
|
732
|
+
extensionsToTreatAsEsm: ['.js'],
|
|
733
|
+
};
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
---
|
|
737
|
+
|
|
738
|
+
## 📚 Documentation Swagger
|
|
739
|
+
|
|
740
|
+
### Accès
|
|
741
|
+
|
|
742
|
+
L'API est documentée avec **Swagger/OpenAPI** et accessible à :
|
|
743
|
+
|
|
744
|
+
```
|
|
745
|
+
http://localhost:3000/docs
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
**Format brut JSON :**
|
|
749
|
+
```
|
|
750
|
+
http://localhost:3000/docs.json
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
### Ajouter une documentation pour une route
|
|
754
|
+
|
|
755
|
+
Utilisez les commentaires **JSDoc OpenAPI** au-dessus de vos routes :
|
|
756
|
+
|
|
757
|
+
```javascript
|
|
758
|
+
/**
|
|
759
|
+
* @openapi
|
|
760
|
+
* /users/profile:
|
|
761
|
+
* get:
|
|
762
|
+
* summary: Get current user profile
|
|
763
|
+
* description: Retrieve the authenticated user's profile information
|
|
764
|
+
* security:
|
|
765
|
+
* - bearerAuth: []
|
|
766
|
+
* responses:
|
|
767
|
+
* 200:
|
|
768
|
+
* description: User profile retrieved successfully
|
|
769
|
+
* content:
|
|
770
|
+
* application/json:
|
|
771
|
+
* schema:
|
|
772
|
+
* type: object
|
|
773
|
+
* properties:
|
|
774
|
+
* id:
|
|
775
|
+
* type: string
|
|
776
|
+
* example: user123
|
|
777
|
+
* role:
|
|
778
|
+
* type: string
|
|
779
|
+
* example: admin
|
|
780
|
+
* 401:
|
|
781
|
+
* description: Unauthorized - Token missing or invalid
|
|
782
|
+
*/
|
|
783
|
+
router.get('/profile', authMiddleware, profile);
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
### Exemple complet avec upload
|
|
787
|
+
|
|
788
|
+
```javascript
|
|
789
|
+
/**
|
|
790
|
+
* @openapi
|
|
791
|
+
* /uploads:
|
|
792
|
+
* post:
|
|
793
|
+
* summary: Upload an image file
|
|
794
|
+
* description: Upload an image (JPEG, PNG, GIF, WebP). Max size 5MB.
|
|
795
|
+
* requestBody:
|
|
796
|
+
* required: true
|
|
797
|
+
* content:
|
|
798
|
+
* multipart/form-data:
|
|
799
|
+
* schema:
|
|
800
|
+
* type: object
|
|
801
|
+
* properties:
|
|
802
|
+
* file:
|
|
803
|
+
* type: string
|
|
804
|
+
* format: binary
|
|
805
|
+
* responses:
|
|
806
|
+
* 201:
|
|
807
|
+
* description: File uploaded successfully
|
|
808
|
+
* content:
|
|
809
|
+
* application/json:
|
|
810
|
+
* schema:
|
|
811
|
+
* type: object
|
|
812
|
+
* properties:
|
|
813
|
+
* url:
|
|
814
|
+
* type: string
|
|
815
|
+
* example: uploads/image-123.jpg
|
|
816
|
+
* 400:
|
|
817
|
+
* description: Invalid file type or missing file
|
|
818
|
+
* 413:
|
|
819
|
+
* description: File too large
|
|
820
|
+
*/
|
|
821
|
+
router.post('/:name', uploadHandler);
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
### Configuration Swagger
|
|
825
|
+
|
|
826
|
+
La configuration se trouve dans [src/config/swagger.js](src/config/swagger.js). Vous pouvez y ajouter :
|
|
827
|
+
|
|
828
|
+
```javascript
|
|
829
|
+
const swaggerSpec = {
|
|
830
|
+
definition: {
|
|
831
|
+
openapi: '3.0.0',
|
|
832
|
+
info: {
|
|
833
|
+
title: 'API MVC Node',
|
|
834
|
+
version: '1.0.0',
|
|
835
|
+
description: 'REST API with MVC pattern and versioning'
|
|
836
|
+
},
|
|
837
|
+
servers: [
|
|
838
|
+
{ url: 'http://localhost:3000', description: 'Development server' }
|
|
839
|
+
]
|
|
840
|
+
},
|
|
841
|
+
apis: ['./src/**/routes/*.js']
|
|
842
|
+
};
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
## 📝 Conventions de code
|
|
848
|
+
|
|
849
|
+
### Nommage des fichiers
|
|
850
|
+
|
|
851
|
+
- **Routes** : `{resource}.routes.js` (ex: `user.routes.js`)
|
|
852
|
+
- **Contrôleurs** : `{resource}.controller.js` (ex: `user.controller.js`)
|
|
853
|
+
- **Middlewares** : `{type}.middleware.js` (ex: `auth.middleware.js`)
|
|
854
|
+
- **Models** : `{resource}.model.js` (ex: `user.model.js`)
|
|
855
|
+
- **Services** : `{resource}.service.js` (ex: `jwt.service.js`)
|
|
856
|
+
- **Tests** : `{resource}.test.js` (ex: `user.controller.test.js`)
|
|
857
|
+
|
|
858
|
+
### Style de code
|
|
859
|
+
|
|
860
|
+
- **Syntax** : ES Modules (`import`/`export`)
|
|
861
|
+
- **Indentation** : 2 espaces
|
|
862
|
+
- **Quotes** : Guillemets simples (`'`) sauf pour JSDoc
|
|
863
|
+
- **Semicolons** : Oui
|
|
864
|
+
- **Async/await** : Préféré aux callbacks ou `.then()`
|
|
865
|
+
|
|
866
|
+
### Structure d'une route
|
|
867
|
+
|
|
868
|
+
```javascript
|
|
869
|
+
import { Router } from 'express';
|
|
870
|
+
import { profile, info } from '../controllers/user.controller.js';
|
|
871
|
+
import { authMiddleware } from '../middlewares/auth.middleware.js';
|
|
872
|
+
|
|
873
|
+
const router = Router();
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* @openapi
|
|
877
|
+
* /users/profile:
|
|
878
|
+
* get:
|
|
879
|
+
* summary: Description
|
|
880
|
+
*/
|
|
881
|
+
router.get('/profile', authMiddleware, profile);
|
|
882
|
+
|
|
883
|
+
router.get('/info', info);
|
|
884
|
+
|
|
885
|
+
export default router;
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
### Structure d'un contrôleur
|
|
889
|
+
|
|
890
|
+
```javascript
|
|
891
|
+
export const profile = (req, res, next) => {
|
|
892
|
+
try {
|
|
893
|
+
req.log?.info({ userId: req.user?.id }, 'profile requested');
|
|
894
|
+
res.status(200).json({
|
|
895
|
+
id: req.user.id,
|
|
896
|
+
role: req.user.role
|
|
897
|
+
});
|
|
898
|
+
} catch (err) {
|
|
899
|
+
next(err); // Passer les erreurs au middleware d'erreur
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
export const info = (req, res, next) => {
|
|
904
|
+
try {
|
|
905
|
+
res.status(200).json({ msg: 'ok' });
|
|
906
|
+
} catch (err) {
|
|
907
|
+
next(err);
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
### Structure d'un service
|
|
913
|
+
|
|
914
|
+
```javascript
|
|
915
|
+
export const userService = {
|
|
916
|
+
/**
|
|
917
|
+
* Récupère un utilisateur par ID
|
|
918
|
+
* @param {string} id - ID utilisateur
|
|
919
|
+
* @returns {Promise<Object>} Données utilisateur
|
|
920
|
+
*/
|
|
921
|
+
async getUser(id) {
|
|
922
|
+
req.log?.debug({ userId: id }, 'Fetching user');
|
|
923
|
+
// logique métier
|
|
924
|
+
return user;
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
### Gestion des erreurs
|
|
930
|
+
|
|
931
|
+
```javascript
|
|
932
|
+
export const createUser = (req, res, next) => {
|
|
933
|
+
try {
|
|
934
|
+
// Validation
|
|
935
|
+
if (!req.body.email) {
|
|
936
|
+
const error = new Error('Email is required');
|
|
937
|
+
error.status = 400;
|
|
938
|
+
throw error;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Logique métier
|
|
942
|
+
const user = userService.create(req.body);
|
|
943
|
+
|
|
944
|
+
res.status(201).json(user);
|
|
945
|
+
} catch (err) {
|
|
946
|
+
// Laisser le middleware d'erreur global gérer
|
|
947
|
+
next(err);
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
---
|
|
953
|
+
|
|
954
|
+
## 🐛 Dépannage
|
|
955
|
+
|
|
956
|
+
### Le serveur ne démarre pas
|
|
957
|
+
|
|
958
|
+
**Erreur** : `EADDRINUSE: address already in use :::3000`
|
|
959
|
+
|
|
960
|
+
**Cause** : Le port 3000 est déjà utilisé
|
|
961
|
+
|
|
962
|
+
**Solutions** :
|
|
963
|
+
|
|
964
|
+
```bash
|
|
965
|
+
# Windows : Trouver et tuer le processus
|
|
966
|
+
netstat -ano | findstr :3000
|
|
967
|
+
taskkill /PID <PID> /F
|
|
968
|
+
|
|
969
|
+
# macOS/Linux :
|
|
970
|
+
lsof -i :3000
|
|
971
|
+
kill -9 <PID>
|
|
972
|
+
|
|
973
|
+
# Ou utiliser un port différent
|
|
974
|
+
PORT=3001 npm run dev
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
### "Cannot find module" errors
|
|
978
|
+
|
|
979
|
+
**Erreur** : `Cannot find module 'express'`
|
|
980
|
+
|
|
981
|
+
**Cause** : Dépendances non installées
|
|
982
|
+
|
|
983
|
+
**Solution** :
|
|
984
|
+
```bash
|
|
985
|
+
npm install
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
### Erreurs d'import ES modules
|
|
989
|
+
|
|
990
|
+
**Erreur** : `ERR_MODULE_NOT_FOUND`
|
|
991
|
+
|
|
992
|
+
**Cause** : Chemin d'import incorrect
|
|
993
|
+
|
|
994
|
+
**Vérifier** :
|
|
995
|
+
- L'extension `.js` est incluse dans l'import
|
|
996
|
+
- Le chemin relatif est correct
|
|
997
|
+
- `"type": "module"` est présent dans `package.json`
|
|
998
|
+
|
|
999
|
+
```javascript
|
|
1000
|
+
// ✅ Correct
|
|
1001
|
+
import app from './app.js';
|
|
1002
|
+
|
|
1003
|
+
// ❌ Incorrect
|
|
1004
|
+
import app from './app';
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
### Erreurs de JWT
|
|
1008
|
+
|
|
1009
|
+
**Erreur** : `"Invalid token"`
|
|
1010
|
+
|
|
1011
|
+
**Cause** : Token expiré ou clé secrète incorrecte
|
|
1012
|
+
|
|
1013
|
+
**Solution** :
|
|
1014
|
+
```bash
|
|
1015
|
+
# Vérifier la clé secrète
|
|
1016
|
+
echo $JWT_SECRET # macOS/Linux
|
|
1017
|
+
echo %JWT_SECRET% # Windows
|
|
1018
|
+
|
|
1019
|
+
# Modifier .env
|
|
1020
|
+
JWT_SECRET=new_secret_key
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
**Erreur** : `"Token missing"`
|
|
1024
|
+
|
|
1025
|
+
**Cause** : Header Authorization absent
|
|
1026
|
+
|
|
1027
|
+
**Solution** : Ajouter le header :
|
|
1028
|
+
```bash
|
|
1029
|
+
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
|
1030
|
+
http://localhost:3000/api/v1/users/profile
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
### Les logs ne s'affichent pas
|
|
1034
|
+
|
|
1035
|
+
**Problème** : `LOG_LEVEL` trop élevé ou fichier non créé
|
|
1036
|
+
|
|
1037
|
+
**Solution** :
|
|
1038
|
+
```bash
|
|
1039
|
+
# Réduire le seuil
|
|
1040
|
+
LOG_LEVEL=debug npm run dev
|
|
1041
|
+
|
|
1042
|
+
# Ou modifier .env
|
|
1043
|
+
LOG_LEVEL=debug
|
|
1044
|
+
|
|
1045
|
+
# Vérifier que le répertoire existe
|
|
1046
|
+
mkdir -p logs
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
### Upload échoue
|
|
1050
|
+
|
|
1051
|
+
**Erreur 413** : `Payload Too Large`
|
|
1052
|
+
|
|
1053
|
+
**Cause** : Fichier dépasse la taille limite
|
|
1054
|
+
|
|
1055
|
+
**Solution** :
|
|
1056
|
+
```bash
|
|
1057
|
+
# Augmenter la limite dans .env
|
|
1058
|
+
UPLOAD_MAX_SIZE=10485760 # 10MB
|
|
1059
|
+
|
|
1060
|
+
# Ou réduire le fichier
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
**Erreur 400** : `Only image files are allowed`
|
|
1064
|
+
|
|
1065
|
+
**Cause** : Type de fichier non autorisé
|
|
1066
|
+
|
|
1067
|
+
**Types acceptés** : JPEG, PNG, GIF, WebP
|
|
1068
|
+
|
|
1069
|
+
**Vérifier** :
|
|
1070
|
+
```bash
|
|
1071
|
+
# Utiliser un fichier valide
|
|
1072
|
+
file image.jpg # Doit afficher : image data
|
|
1073
|
+
|
|
1074
|
+
# Ou utiliser imagemagick
|
|
1075
|
+
identify image.jpg
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
### Tests ne passent pas
|
|
1079
|
+
|
|
1080
|
+
**Erreur** : `Test suites: 1 failed`
|
|
1081
|
+
|
|
1082
|
+
**Solution** :
|
|
1083
|
+
```bash
|
|
1084
|
+
# Vérifier que le serveur n'est pas en cours d'exécution
|
|
1085
|
+
npm test
|
|
1086
|
+
|
|
1087
|
+
# Mode verbose pour plus de détails
|
|
1088
|
+
npm test -- --verbose
|
|
1089
|
+
|
|
1090
|
+
# Exécuter un test spécifique
|
|
1091
|
+
npm test -- user.controller.test.js
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
### Base de données introuvable
|
|
1095
|
+
|
|
1096
|
+
**Erreur** : `ECONNREFUSED`
|
|
1097
|
+
|
|
1098
|
+
**Cause** : Service BD non accessible
|
|
1099
|
+
|
|
1100
|
+
**Vérifier** :
|
|
1101
|
+
```bash
|
|
1102
|
+
# Vérifier la connexion
|
|
1103
|
+
ping database_host
|
|
1104
|
+
|
|
1105
|
+
# Vérifier les ports
|
|
1106
|
+
netstat -an | grep 5432 # PostgreSQL
|
|
1107
|
+
netstat -an | grep 3306 # MySQL
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
---
|
|
1111
|
+
|
|
1112
|
+
## 📞 Support & Contribution
|
|
1113
|
+
|
|
1114
|
+
Pour toute question ou contribution :
|
|
1115
|
+
|
|
1116
|
+
1. **Consulter la documentation** : [Swagger UI](/docs)
|
|
1117
|
+
2. **Vérifier les logs** : `logs/app.log`
|
|
1118
|
+
3. **Exécuter les tests** : `npm test`
|
|
1119
|
+
4. **Testez manuellement** : `node compare-versions.js` ou `node test-endpoints.js`
|
|
1120
|
+
5. **Ouvrir une issue** sur le repository
|
|
1121
|
+
|
|
1122
|
+
---
|
|
1123
|
+
|
|
1124
|
+
## Scripts utiles
|
|
1125
|
+
|
|
1126
|
+
### Scripts npm
|
|
1127
|
+
|
|
1128
|
+
```bash
|
|
1129
|
+
# Démarrage
|
|
1130
|
+
npm start # Mode production
|
|
1131
|
+
npm run dev # Mode développement avec auto-reload
|
|
1132
|
+
|
|
1133
|
+
# Tests
|
|
1134
|
+
npm test # Exécuter les tests une seule fois
|
|
1135
|
+
npm run test:watch # Mode watch (détecte les changements)
|
|
1136
|
+
|
|
1137
|
+
# Utilitaires
|
|
1138
|
+
node compare-versions.js # Comparer v1 vs v2
|
|
1139
|
+
node test-endpoints.js # Tester les endpoints
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
---
|
|
1143
|
+
|
|
1144
|
+
**Dernière mise à jour** : Janvier 2026
|
|
1145
|
+
**Version du projet** : 1.0.0
|
|
1146
|
+
**Node.js requis** : >= 16.x
|
|
1147
|
+
**License** : ISC
|