bv-express 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 ADDED
@@ -0,0 +1,5 @@
1
+ PORT=3000
2
+ DB_HOST=localhost
3
+ DB_USER=root
4
+ DB_PASS=
5
+ DB_NAME=moto
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # đŸïž BV Express
2
+
3
+ API Express avec l'intégration du builder bvbuilder
4
+
5
+ ---
6
+
7
+ ## 📋 Table des matiùres
8
+
9
+ - [Installation & Configuration](#-installation--configuration)
10
+ - [Lancement du Projet](#-lancement-du-projet)
11
+ - [Fonctionnalités Principales](#-fonctionnalités-principales)
12
+ - [Documentation API](#-documentation-api-swagger)
13
+ - [Tests](#-tests)
14
+ - [Structure du Projet](#-structure-du-projet)
15
+ - [Technologies Utilisées](#-technologies-utilisées)
16
+ - [Contribution](#-contribution)
17
+
18
+ ---
19
+
20
+ ## 🚀 Installation & Configuration
21
+
22
+ ### Prérequis
23
+
24
+ - Node.js (version 14 ou supérieure)
25
+ - npm
26
+ - Mysql
27
+
28
+ ### Installation des dépendances
29
+
30
+ ```bash
31
+ npm install
32
+ ```
33
+
34
+ ### Configuration des variables d'environnement
35
+
36
+ Le projet nécessite un fichier `.env` pour fonctionner correctement .
37
+
38
+ 1. Copiez le fichier d'exemple :
39
+ ```bash
40
+ cp .env.example .env
41
+ ```
42
+
43
+ 2. Éditez le fichier `.env` avec vos propres paramùtres :
44
+ ```env
45
+ # Exemple de configuration
46
+ PORT=3000
47
+ DB_HOST=localhost
48
+ DB_USER=
49
+ DB_PASS=
50
+ DB_NAME=moto
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 🏃 Lancement du Projet
56
+
57
+ ### Mode Développement
58
+ Avec rechargement automatique Ă  chaque modification :
59
+ ```bash
60
+ npm run dev
61
+ ```
62
+
63
+ ### Mode Production
64
+ ```bash
65
+ npm start
66
+ ```
67
+
68
+ L'API sera accessible par défaut sur `http://localhost:3000`
69
+
70
+ ---
71
+
72
+ ## ✹ FonctionnalitĂ©s Principales
73
+
74
+ - ** Authentification JWT** : Sécurisation des accÚs par jeton JSON Web Token
75
+ ---
76
+
77
+ ## 📖 Documentation API (Swagger)
78
+
79
+ Accédez à la documentation interactive pour tester les endpoints et consulter les schémas de données :
80
+
81
+ 🔗 **[http://localhost:3000/api-docs](http://localhost:3000/api-docs)**
82
+
83
+ La documentation Swagger vous permet de :
84
+ - Visualiser tous les endpoints disponibles
85
+ - Tester les requĂȘtes directement depuis l'interface
86
+ - Consulter les schĂ©mas de requĂȘte et de rĂ©ponse
87
+ - Voir les codes de statut et messages d'erreur
88
+
89
+ ---
90
+
91
+ ## đŸ§Ș Tests
92
+
93
+ Le projet intÚgre des tests unitaires pour garantir la fiabilité des fonctionnalités :
94
+
95
+ ```bash
96
+ npm test
97
+ ```
98
+
99
+ Pour exécuter les tests avec la couverture de code :
100
+ ```bash
101
+ npm run test:coverage
102
+ ```
103
+
104
+ ---
105
+
106
+ ## 📁 Structure du Projet
107
+
108
+ ```
109
+ bbv-express/
110
+ ├── src/
111
+ │ ├── controllers/ # Logique mĂ©tier des routes
112
+ │ ├── models/ # ModĂšles de donnĂ©es
113
+ │ ├── routes/ # DĂ©finition des routes API
114
+ │ ├── middlewares/ # Middlewares (auth, validation, etc.)
115
+ │ └── config/ # Configuration de l'application
116
+ ├── tests/ # Tests unitaires et d'intĂ©gration
117
+ ├── .env # Configuration
118
+ ├── .env.example # Exemple de configuration
119
+ ├── .gitignore
120
+ ├── package.json
121
+ └── README.md
122
+ ```
123
+
124
+ ---
125
+
126
+ ## đŸ› ïž Technologies UtilisĂ©es
127
+
128
+ - **Node.js** - Environnement d'exécution JavaScript
129
+ - **Bvbuilder** - Permet de build, model,controller, route et table via commande
130
+ - **Express.js** - Framework web minimaliste
131
+ - **MySql** - Base de données SQL
132
+ - **JWT** - Authentification par jetons
133
+ - **Swagger** - Documentation API interactive
134
+ - **Jest** - Framework de tests
135
+
136
+ ---
137
+
138
+ ## 📧 Contact
139
+
140
+ Pour toute question ou suggestion, n'hésitez pas à me contacter.
package/index.js ADDED
@@ -0,0 +1 @@
1
+ console.log('Happy developing ✹')
package/jest.config.js ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "bv-express",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "node src/server.js",
8
+ "dev": "nodemon src/server.js",
9
+ "test": "jest --watchAll --verbose"
10
+ },
11
+ "private": false,
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "ISC",
15
+ "type": "commonjs",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/Xenozn/bv-express.git"
19
+ },
20
+ "dependencies": {
21
+ "bcryptjs": "^3.0.3",
22
+ "cors": "^2.8.5",
23
+ "dotenv": "^17.2.3",
24
+ "express": "^5.2.1",
25
+ "jsonwebtoken": "^9.0.3",
26
+ "mysql2": "^3.16.0",
27
+ "swagger-jsdoc": "^6.2.8",
28
+ "swagger-ui-express": "^5.0.1"
29
+ },
30
+ "devDependencies": {
31
+ "bvbuilder": "^1.0.2",
32
+ "jest": "^30.2.0",
33
+ "nodemon": "^3.1.11",
34
+ "supertest": "^7.2.2"
35
+ }
36
+ }
package/src/app.js ADDED
@@ -0,0 +1,78 @@
1
+ const express = require('express');
2
+ require('dotenv').config();
3
+ const path = require('node:path');
4
+ const fs = require('node:fs'); // Ajouté pour lire les dossiers
5
+
6
+ const logger = require('./middlewares/logger');
7
+ const swaggerUi = require('swagger-ui-express');
8
+ const swaggerJsdoc = require('swagger-jsdoc');
9
+
10
+ const app = express();
11
+
12
+ // --- CONFIGURATION SWAGGER ---
13
+ const swaggerOptions = {
14
+ definition: {
15
+ openapi: '3.0.0',
16
+ info: {
17
+ title: 'BV Express API Documentation',
18
+ version: '1.0.0',
19
+ description: 'Documentation interactive de mon API',
20
+ },
21
+ servers: [{ url: 'http://localhost:3000' }],
22
+
23
+ components: {
24
+ securitySchemes: {
25
+ bearerAuth: {
26
+ type: 'http',
27
+ scheme: 'bearer',
28
+ bearerFormat: 'JWT',
29
+ },
30
+ },
31
+ },
32
+
33
+ security: [
34
+ {
35
+ bearerAuth: [],
36
+ },
37
+ ],
38
+ },
39
+ apis: [path.join(__dirname, './v*/routes/*.js')],
40
+ };
41
+
42
+
43
+ const swaggerDocs = swaggerJsdoc(swaggerOptions);
44
+
45
+ // --- MIDDLEWARES ---
46
+ app.use(express.json());
47
+ app.use(logger);
48
+
49
+ // --- CHARGEMENT AUTOMATIQUE DES ROUTES ---
50
+ const versions = fs.readdirSync(__dirname).filter(file => {
51
+ const filePath = path.join(__dirname, file);
52
+ return fs.statSync(filePath).isDirectory() && /^v\d+$/.test(file);
53
+ });
54
+ // Dans ton app.js, remplace la boucle par celle-ci :
55
+ versions.forEach(version => {
56
+ const routesPath = path.join(__dirname, version, 'routes');
57
+
58
+ if (fs.existsSync(routesPath)) {
59
+ fs.readdirSync(routesPath).forEach(file => {
60
+ if (!file.endsWith('.js')) return;
61
+
62
+ let routeName = file.replace('Routes.js', '').toLowerCase();
63
+ if (routeName !== 'auth' && !routeName.endsWith('s')) {
64
+ routeName += 's';
65
+ }
66
+ const route = require(`./${version}/routes/${file}`);
67
+ app.use(`/api/${version}/${routeName}`, route);
68
+
69
+ console.log(`✅ Route chargĂ©e : /api/${version}/${routeName}`);
70
+ });
71
+ }
72
+ });
73
+
74
+
75
+ // --- DOCS ---
76
+ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
77
+
78
+ module.exports = app;
@@ -0,0 +1,11 @@
1
+ const mysql = require('mysql2');
2
+ require('dotenv').config();
3
+
4
+ const pool = mysql.createPool({
5
+ host: process.env.DB_HOST,
6
+ user: process.env.DB_USER,
7
+ password: process.env.DB_PASS,
8
+ database: process.env.DB_NAME
9
+ });
10
+
11
+ module.exports = pool.promise();
@@ -0,0 +1,27 @@
1
+ const jwt = require('jsonwebtoken');
2
+ const authMiddleware = (req, res, next) => {
3
+ const token = req.headers['authorization']?.split(' ')[1];
4
+
5
+ if (!token) {
6
+ return res.status(403).json({ message: "Token requis" });
7
+ }
8
+
9
+ try {
10
+ const decoded = jwt.verify(
11
+ token,
12
+ process.env.JWT_SECRET || 'secret_par_defaut'
13
+ );
14
+
15
+ req.userId = decoded.id;
16
+ req.userRole = decoded.role;
17
+
18
+ next();
19
+ } catch (error) {
20
+ console.error('JWT error details:', error);
21
+ return res.status(401).json({
22
+ message: "Token invalide",
23
+ });
24
+ }
25
+ };
26
+
27
+ module.exports = authMiddleware;
@@ -0,0 +1,6 @@
1
+ const logger = (req, res, next) => {
2
+ console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
3
+ next();
4
+ };
5
+
6
+ module.exports = logger;
@@ -0,0 +1,9 @@
1
+ exports.isAdmin = (req, res, next) => {
2
+ // req.userRole doit ĂȘtre injectĂ© par ton authMiddleware
3
+ if (req.userRole !== 'admin') {
4
+ return res.status(403).json({
5
+ message: "AccĂšs interdit : privilĂšges administrateur requis"
6
+ });
7
+ }
8
+ next();
9
+ };
package/src/server.js ADDED
@@ -0,0 +1,8 @@
1
+ require('dotenv').config();
2
+ const app = require('./app');
3
+
4
+ const PORT = process.env.PORT || 3000;
5
+
6
+ app.listen(PORT, () => {
7
+ console.log(`🚀 Serveur dĂ©marrĂ© sur http://localhost:${PORT}`);
8
+ });
@@ -0,0 +1,64 @@
1
+ const User = require('../models/userModel'); // Import du modĂšle
2
+ const bcrypt = require('bcryptjs');
3
+ const jwt = require('jsonwebtoken');
4
+
5
+ // --- REGISTER ---
6
+ exports.register = async (req, res) => {
7
+ const { email, password, nom, prenom } = req.body;
8
+
9
+ try {
10
+ const existingUser = await User.findByEmail(email);
11
+ if (existingUser) {
12
+ return res.status(400).json({ message: "Utilisateur déjà existant" });
13
+ }
14
+
15
+ const hashedPassword = await bcrypt.hash(password, 10);
16
+ const forcedRole = 'user';
17
+
18
+ const userId = await User.create({
19
+ email,
20
+ password: hashedPassword,
21
+ nom,
22
+ prenom,
23
+ role: forcedRole
24
+ });
25
+
26
+ res.status(201).json({
27
+ message: "Utilisateur créé !",
28
+ user: { id: userId, email, nom, prenom, role: forcedRole }
29
+ });
30
+
31
+ } catch (error) {
32
+ res.status(500).json({ status: "error", message: error.message });
33
+ }
34
+ };
35
+
36
+ // --- LOGIN ---
37
+ exports.login = async (req, res) => {
38
+ const { email, password } = req.body;
39
+
40
+ try {
41
+ const user = await User.findByEmail(email);
42
+
43
+ if (!user || !(await bcrypt.compare(password, user.password))) {
44
+ return res.status(401).json({ message: "Identifiants invalides" });
45
+ }
46
+
47
+ const token = jwt.sign(
48
+ { id: user.id, role: user.role },
49
+ process.env.JWT_SECRET || 'secret_par_defaut',
50
+ { expiresIn: '1h' }
51
+ );
52
+
53
+ const { password: _, ...userWithoutPassword } = user;
54
+
55
+ res.json({
56
+ message: "Connecté !",
57
+ token,
58
+ user: userWithoutPassword
59
+ });
60
+
61
+ } catch (error) {
62
+ res.status(500).json({ status: "error", message: error.message });
63
+ }
64
+ };
@@ -0,0 +1,34 @@
1
+ const User = require('../models/userModel');
2
+
3
+ // --- RÉCUPÉRER MON PROFIL (Via Token) ---
4
+ exports.getMe = async (req, res) => {
5
+ try {
6
+ // req.userId est injecté par votre authMiddleware
7
+ const user = await User.findById(req.userId);
8
+
9
+ if (!user) {
10
+ return res.status(404).json({ message: "Utilisateur non trouvé" });
11
+ }
12
+
13
+ res.json({ status: "success", data: user });
14
+ } catch (error) {
15
+ res.status(500).json({ status: "error", message: error.message });
16
+ }
17
+ };
18
+
19
+ // --- RÉCUPÉRER UN UTILISATEUR PAR ID (Paramùtre URL) ---
20
+ exports.getUserById = async (req, res) => {
21
+ const { id } = req.params;
22
+
23
+ try {
24
+ const user = await User.findById(id);
25
+
26
+ if (!user) {
27
+ return res.status(404).json({ message: "Utilisateur non trouvé" });
28
+ }
29
+
30
+ res.json({ status: "success", data: user });
31
+ } catch (error) {
32
+ res.status(500).json({ status: "error", message: error.message });
33
+ }
34
+ };
@@ -0,0 +1,34 @@
1
+ const db = require('../../config/db');
2
+
3
+ const User = {
4
+ // Trouver un utilisateur par son email
5
+ findByEmail: async (email) => {
6
+ const [rows] = await db.query('SELECT * FROM users WHERE email = ?', [email]);
7
+ return rows[0];
8
+ },
9
+
10
+ // Créer un nouvel utilisateur
11
+ create: async (userData) => {
12
+ const { email, password, nom, prenom, role } = userData;
13
+ const [result] = await db.query(
14
+ 'INSERT INTO users (email, password, nom, prenom, role) VALUES (?, ?, ?, ?, ?)',
15
+ [email, password, nom, prenom, role]
16
+ );
17
+ return result.insertId;
18
+ },
19
+
20
+ findById: async (id) => {
21
+ const [rows] = await db.query(
22
+ 'SELECT id, email, nom, prenom, role, createdAt FROM users WHERE id = ?',
23
+ [id]
24
+ );
25
+ return rows[0]; // Retourne l'utilisateur ou undefined
26
+ },
27
+ findAll: async () => {
28
+ const [rows] = await db.query('SELECT id, email, nom, prenom, role, createdAt FROM users');
29
+ return rows;
30
+ }
31
+
32
+ };
33
+
34
+ module.exports = User;
@@ -0,0 +1,56 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const authController = require('../controllers/authController');
4
+
5
+ /**
6
+ * @swagger
7
+ * /api/v1/auth/register:
8
+ * post:
9
+ * summary: Inscrire un utilisateur
10
+ * tags: [Auth]
11
+ * requestBody:
12
+ * required: true
13
+ * content:
14
+ * application/json:
15
+ * schema:
16
+ * type: object
17
+ * properties:
18
+ * email:
19
+ * type: string
20
+ * password:
21
+ * type: string
22
+ * nom:
23
+ * type: string
24
+ * prenom:
25
+ * type: string
26
+ * responses:
27
+ * 201:
28
+ * description: SuccĂšs
29
+ */
30
+ router.post('/register', authController.register);
31
+
32
+ /**
33
+ * @swagger
34
+ * /api/v1/auth/login:
35
+ * post:
36
+ * summary: Se connecter
37
+ * tags: [Auth]
38
+ * requestBody:
39
+ * required: true
40
+ * content:
41
+ * application/json:
42
+ * schema:
43
+ * type: object
44
+ * properties:
45
+ * email:
46
+ * type: string
47
+ * password:
48
+ * type: string
49
+ * responses:
50
+ * 200:
51
+ * description: Connexion réussie
52
+ */
53
+ router.post('/login', authController.login);
54
+
55
+
56
+ module.exports = router;
@@ -0,0 +1,49 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const userController = require('../controllers/userController');
4
+ const authMiddleware = require('../../middlewares/authMiddleware');
5
+ const { isAdmin } = require('../../middlewares/roleMiddleware');
6
+
7
+ /**
8
+ * @swagger
9
+ * /api/v1/users/me:
10
+ * get:
11
+ * summary: Récupérer les informations de l'utilisateur connecté
12
+ * tags: [Users]
13
+ * security:
14
+ * - bearerAuth: []
15
+ * responses:
16
+ * 200:
17
+ * description: Informations de l'utilisateur connecté
18
+ * 401:
19
+ * description: Non authentifié
20
+ */
21
+ router.get('/me', authMiddleware, userController.getMe);
22
+
23
+ /**
24
+ * @swagger
25
+ * /api/v1/users/{id}:
26
+ * get:
27
+ * summary: Récupérer un utilisateur par ID (admin uniquement)
28
+ * tags: [Users]
29
+ * security:
30
+ * - bearerAuth: []
31
+ * parameters:
32
+ * - in: path
33
+ * name: id
34
+ * required: true
35
+ * schema:
36
+ * type: integer
37
+ * responses:
38
+ * 200:
39
+ * description: Utilisateur trouvé
40
+ * 401:
41
+ * description: Non authentifié
42
+ * 403:
43
+ * description: AccÚs refusé (admin requis)
44
+ * 404:
45
+ * description: Utilisateur non trouvé
46
+ */
47
+ router.get('/:id', authMiddleware, isAdmin, userController.getUserById);
48
+
49
+ module.exports = router;
@@ -0,0 +1,52 @@
1
+ const request = require('supertest');
2
+ const app = require('../src/app');
3
+ const db = require('../src/config/db');
4
+ const bcrypt = require('bcryptjs');
5
+
6
+ // Mock de la base de données
7
+ jest.mock('../src/config/db');
8
+
9
+ describe('Auth API - Login', () => {
10
+
11
+ it('devrait retourner un token 200 si les identifiants sont corrects', async () => {
12
+ const hashedPassword = await bcrypt.hash('password123', 10);
13
+ const mockUser = {
14
+ id: 1,
15
+ email: 'test@example.com',
16
+ password: hashedPassword,
17
+ nom: 'Test',
18
+ prenom: 'User',
19
+ role: 'user'
20
+ };
21
+
22
+ db.query.mockResolvedValue([[mockUser]]);
23
+
24
+ const res = await request(app)
25
+ .post('/api/v1/auth/login')
26
+ .send({
27
+ email: 'test@example.com',
28
+ password: 'password123'
29
+ });
30
+
31
+ // 3. Vérifications (Assertions)
32
+ expect(res.statusCode).toEqual(200);
33
+ expect(res.body).toHaveProperty('token');
34
+ expect(res.body.user.email).toBe('test@example.com');
35
+ expect(res.body.message).toBe('Connecté !');
36
+ });
37
+
38
+ it('devrait retourner 401 si le mot de passe est faux', async () => {
39
+ const hashedPassword = await bcrypt.hash('correct_password', 10);
40
+ db.query.mockResolvedValue([[{ email: 'test@example.com', password: hashedPassword }]]);
41
+
42
+ const res = await request(app)
43
+ .post('/api/v1/auth/login')
44
+ .send({
45
+ email: 'test@example.com',
46
+ password: 'wrong_password'
47
+ });
48
+
49
+ expect(res.statusCode).toEqual(401);
50
+ expect(res.body.message).toBe('Identifiants invalides');
51
+ });
52
+ });