outlet-orm 4.2.1 → 5.5.1

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/README.md CHANGED
@@ -30,71 +30,171 @@ Si aucun driver n'est installé, un message d'erreur explicite vous indiquera le
30
30
 
31
31
  ## 📁 Structure de Projet Recommandée
32
32
 
33
- Organisez votre projet utilisant Outlet ORM comme suit :
33
+ Organisez votre projet utilisant Outlet ORM avec une **architecture en couches** (recommandée pour la production) :
34
34
 
35
35
  > 🔐 **Sécurité** : Voir le [Guide de Sécurité](./docs/SECURITY.md) pour les bonnes pratiques.
36
36
 
37
37
  ```
38
38
  mon-projet/
39
- ├── .env # ⚠️ JAMAIS commité (dans .gitignore)
40
- ├── .env.example # Template sans secrets
41
- ├── .gitignore # Exclure .env, node_modules, logs
39
+ ├── .env # ⚠️ JAMAIS commité (dans .gitignore)
40
+ ├── .env.example # Template sans secrets
41
+ ├── .gitignore
42
42
  ├── package.json
43
- ├── config/ # 🔒 Configuration centralisée
44
- ├── app.js # Config générale
45
- │ ├── database.js # Config DB (lit .env)
46
- └── security.js # Rate limit, helmet, CORS...
43
+
44
+ ├── src/ # 📦 Code source centralisé
45
+ │ ├── index.js # Point d'entrée de l'application
46
+
47
+ │ ├── config/ # ⚙️ Configuration
48
+ │ │ ├── app.js # Config générale (port, env)
49
+ │ │ ├── database.js # Config DB (lit .env)
50
+ │ │ └── security.js # CORS, helmet, rate limit
51
+ │ │
52
+ │ ├── models/ # 📊 Couche Data (Entities)
53
+ │ │ ├── index.js # Export centralisé des models
54
+ │ │ ├── User.js
55
+ │ │ ├── Post.js
56
+ │ │ └── Comment.js
57
+ │ │
58
+ │ ├── repositories/ # 🗄️ Couche Accès Données
59
+ │ │ ├── BaseRepository.js # Méthodes CRUD génériques
60
+ │ │ ├── UserRepository.js # Requêtes spécifiques User
61
+ │ │ └── PostRepository.js
62
+ │ │
63
+ │ ├── services/ # 💼 Couche Métier (Business Logic)
64
+ │ │ ├── AuthService.js # Logique d'authentification
65
+ │ │ ├── UserService.js # Logique métier utilisateur
66
+ │ │ ├── PostService.js
67
+ │ │ └── EmailService.js # Service externe (emails)
68
+ │ │
69
+ │ ├── controllers/ # 🎮 Couche Présentation (HTTP)
70
+ │ │ ├── AuthController.js
71
+ │ │ ├── UserController.js
72
+ │ │ └── PostController.js
73
+ │ │
74
+ │ ├── routes/ # 🛤️ Définition des routes
75
+ │ │ ├── index.js # Agrégateur de routes
76
+ │ │ ├── auth.routes.js
77
+ │ │ ├── user.routes.js
78
+ │ │ └── post.routes.js
79
+ │ │
80
+ │ ├── middlewares/ # 🔒 Middlewares
81
+ │ │ ├── auth.js # JWT verification
82
+ │ │ ├── authorize.js # RBAC / permissions
83
+ │ │ ├── rateLimiter.js # Protection DDoS
84
+ │ │ ├── validator.js # Validation request body
85
+ │ │ └── errorHandler.js # Gestion centralisée erreurs
86
+ │ │
87
+ │ ├── validators/ # ✅ Schémas de validation
88
+ │ │ ├── authValidator.js
89
+ │ │ └── userValidator.js
90
+ │ │
91
+ │ └── utils/ # 🔧 Utilitaires
92
+ │ ├── hash.js # bcrypt wrapper
93
+ │ ├── token.js # JWT helpers
94
+ │ ├── logger.js # Winston/Pino config
95
+ │ └── response.js # Formatage réponses API
96
+
47
97
  ├── database/
48
- │ ├── config.js # Config migrations (généré par outlet-init)
49
- └── migrations/ # Vos fichiers de migration
50
- ├── models/ # Vos classes Model
51
- ├── User.js
52
- ├── Post.js
53
- │ └── Comment.js
54
- ├── controllers/ # Vos contrôleurs (logique métier)
55
- │ ├── UserController.js
56
- │ └── PostController.js
57
- ├── routes/ # Vos fichiers de routes
58
- │ ├── index.js
59
- │ └── userRoutes.js
60
- ├── middlewares/ # 🔒 Middlewares de sécurité
61
- │ ├── auth.js # Authentification JWT
62
- │ ├── authorization.js # Contrôle des permissions (RBAC)
63
- │ ├── rateLimiter.js # Protection anti-DDoS
64
- │ ├── validator.js # Validation des entrées
65
- │ └── errorHandler.js # Gestion centralisée des erreurs
66
- ├── services/ # Vos services (logique réutilisable)
67
- │ ├── AuthService.js
68
- │ └── EmailService.js
69
- ├── utils/ # 🔒 Utilitaires sécurité
70
- │ ├── hash.js # Hachage mots de passe (bcrypt)
71
- │ └── token.js # Génération tokens sécurisés
72
- ├── validators/ # 🔒 Schémas de validation
73
- │ └── userValidator.js
74
- ├── public/ # ✅ Seul dossier accessible publiquement
98
+ │ ├── config.js # Config migrations (outlet-init)
99
+ ├── migrations/ # Fichiers de migration
100
+ │ └── seeds/ # Données de test/démo
101
+ └── UserSeeder.js
102
+
103
+ ├── public/ # ✅ Fichiers statiques publics
75
104
  │ ├── images/
76
105
  │ ├── css/
77
106
  │ └── js/
78
- ├── uploads/ # ⚠️ Fichiers uploadés (validés)
79
- ├── logs/ # 📋 Journaux (non versionnés)
80
- ├── src/ # Votre code applicatif
81
- │ └── index.js
82
- └── tests/ # Vos tests
83
- └── models.test.js
107
+
108
+ ├── uploads/ # ⚠️ Fichiers uploadés
109
+
110
+ ├── logs/ # 📋 Journaux (non versionnés)
111
+
112
+ └── tests/ # 🧪 Tests
113
+ ├── unit/ # Tests unitaires
114
+ │ ├── services/
115
+ │ └── models/
116
+ ├── integration/ # Tests d'intégration
117
+ │ └── api/
118
+ └── fixtures/ # Données de test
119
+ └── users.json
84
120
  ```
85
121
 
86
- | Dossier | Rôle | Sécurité |
87
- |---------|------|----------|
88
- | `config/` | Configuration centralisée | 🔒 Lit les secrets depuis .env |
89
- | `database/` | Migrations et config DB | `outlet-init` |
90
- | `models/` | Classes Model avec `hidden` et `fillable` | 🔒 Mass assignment protection |
91
- | `controllers/` | Logique métier | Valider les entrées |
92
- | `routes/` | Définition des routes API/Web | 🔒 Appliquer middlewares auth |
93
- | `middlewares/` | Auth, validation, rate limiting | 🔒 **Critique pour la sécurité** |
94
- | `services/` | Services réutilisables | Isoler la logique sensible |
95
- | `utils/` | Hash, tokens, encryption | 🔒 Ne jamais exposer |
96
- | `public/` | Seul dossier accessible | ✅ Fichiers statiques sûrs |
97
- | `logs/` | Journaux d'accès/erreurs | 📋 Dans .gitignore |
122
+ ### 🏗️ Architecture en Couches
123
+
124
+ ```
125
+ ┌─────────────────────────────────────────────────────────────┐
126
+ │ HTTP Request │
127
+ └─────────────────────────────────────────────────────────────┘
128
+
129
+
130
+ ┌─────────────────────────────────────────────────────────────┐
131
+ │ MIDDLEWARES: auth validate rateLimiter errorHandler
132
+ └─────────────────────────────────────────────────────────────┘
133
+
134
+
135
+ ┌─────────────────────────────────────────────────────────────┐
136
+ │ ROUTES → CONTROLLERS (Couche Présentation) │
137
+ │ Reçoit la requête, appelle le service, retourne réponse │
138
+ └─────────────────────────────────────────────────────────────┘
139
+
140
+
141
+ ┌─────────────────────────────────────────────────────────────┐
142
+ │ SERVICES (Couche Métier / Business Logic) │
143
+ │ Logique métier, orchestration, règles business │
144
+ └─────────────────────────────────────────────────────────────┘
145
+
146
+
147
+ ┌─────────────────────────────────────────────────────────────┐
148
+ │ REPOSITORIES (Couche Accès Données) │
149
+ │ Abstraction des requêtes DB, utilise les Models │
150
+ └─────────────────────────────────────────────────────────────┘
151
+
152
+
153
+ ┌─────────────────────────────────────────────────────────────┐
154
+ │ MODELS (Outlet ORM) → DATABASE │
155
+ └─────────────────────────────────────────────────────────────┘
156
+ ```
157
+
158
+ ### 📋 Rôle de chaque couche
159
+
160
+ | Couche | Dossier | Responsabilité | Dépend de |
161
+ |--------|---------|----------------|-----------|
162
+ | **Présentation** | `controllers/` | Traiter HTTP, valider entrées, formater réponses | Services |
163
+ | **Métier** | `services/` | Logique business, orchestration, règles | Repositories |
164
+ | **Données** | `repositories/` | Requêtes DB complexes, abstraction | Models |
165
+ | **Entités** | `models/` | Définition des entités, relations, validations | Outlet ORM |
166
+
167
+ ### ✅ Avantages de cette architecture
168
+
169
+ - **Testabilité** : Chaque couche peut être testée indépendamment
170
+ - **Maintenabilité** : Séparation claire des responsabilités
171
+ - **Scalabilité** : Facile d'ajouter de nouvelles fonctionnalités
172
+ - **Réutilisabilité** : Services utilisables depuis CLI, workers, etc.
173
+
174
+ ### 📝 Exemple de flux
175
+
176
+ ```javascript
177
+ // routes/user.routes.js
178
+ router.get('/users/:id', auth, UserController.show);
179
+
180
+ // controllers/UserController.js
181
+ async show(req, res) {
182
+ const user = await userService.findById(req.params.id);
183
+ res.json({ data: user });
184
+ }
185
+
186
+ // services/UserService.js
187
+ async findById(id) {
188
+ const user = await userRepository.findWithPosts(id);
189
+ if (!user) throw new NotFoundError('User not found');
190
+ return user;
191
+ }
192
+
193
+ // repositories/UserRepository.js
194
+ async findWithPosts(id) {
195
+ return User.with('posts').find(id);
196
+ }
197
+ ```
98
198
 
99
199
  ## ✨ Fonctionnalités clés
100
200
 
@@ -141,6 +241,19 @@ outlet-migrate make create_users_table
141
241
  outlet-migrate migrate
142
242
  ```
143
243
 
244
+ ### 🌱 Seeding rapide
245
+
246
+ ```bash
247
+ # Créer un seeder
248
+ outlet-migrate make:seed UserSeeder
249
+
250
+ # Exécuter les seeds (DatabaseSeeder prioritaire)
251
+ outlet-migrate seed
252
+
253
+ # Exécuter un seeder spécifique
254
+ outlet-migrate seed --class UserSeeder
255
+ ```
256
+
144
257
  ## 📖 Utilisation
145
258
 
146
259
  ### Configuration de la connexion
package/bin/init.js CHANGED
@@ -98,6 +98,7 @@ module.exports = db;
98
98
  'config',
99
99
  'database',
100
100
  'database/migrations',
101
+ 'database/seeds',
101
102
  'models',
102
103
  'controllers',
103
104
  'routes',
@@ -126,6 +127,23 @@ module.exports = db;
126
127
  }
127
128
  }
128
129
 
130
+ const databaseSeederPath = path.join(process.cwd(), 'database', 'seeds', 'DatabaseSeeder.js');
131
+ if (!fs.existsSync(databaseSeederPath)) {
132
+ const databaseSeederContent = `const { Seeder } = require('outlet-orm');
133
+
134
+ class DatabaseSeeder extends Seeder {
135
+ async run() {
136
+ // Example:
137
+ // await this.call('UserSeeder');
138
+ }
139
+ }
140
+
141
+ module.exports = DatabaseSeeder;
142
+ `;
143
+ fs.writeFileSync(databaseSeederPath, databaseSeederContent);
144
+ console.log('✅ database/seeds/DatabaseSeeder.js créé');
145
+ }
146
+
129
147
  // Generate .gitignore
130
148
  const gitignoreContent = `# Secrets
131
149
  .env
package/bin/migrate.js CHANGED
@@ -31,8 +31,14 @@ async function main() {
31
31
  return;
32
32
  }
33
33
 
34
+ if (command === 'make:seed' || command === 'seed:make') {
35
+ await makeSeeder();
36
+ rl.close();
37
+ return;
38
+ }
39
+
34
40
  // Support non-interactive commands for automation and CI
35
- const nonInteractive = new Set(['migrate', 'up', 'rollback', 'reset', 'refresh', 'fresh', 'status']);
41
+ const nonInteractive = new Set(['migrate', 'up', 'rollback', 'reset', 'refresh', 'fresh', 'status', 'seed', 'db:seed']);
36
42
  if (nonInteractive.has(command)) {
37
43
  const flags = parseFlags(process.argv.slice(3));
38
44
  await runNonInteractive(command, flags);
@@ -94,6 +100,40 @@ async function makeMigration() {
94
100
  console.log(` Location: ${filePath}`);
95
101
  }
96
102
 
103
+ /**
104
+ * Create a new seeder file
105
+ */
106
+ async function makeSeeder() {
107
+ const seederName = process.argv[3];
108
+
109
+ if (!seederName) {
110
+ console.error('✗ Error: Seeder name is required');
111
+ console.log('Usage: outlet-migrate make:seed <seeder_name>');
112
+ console.log('Example: outlet-migrate make:seed UserSeeder');
113
+ return;
114
+ }
115
+
116
+ const seedsDir = path.join(process.cwd(), 'database', 'seeds');
117
+
118
+ try {
119
+ await fs.mkdir(seedsDir, { recursive: true });
120
+ } catch (error) {
121
+ if (error.code !== 'EEXIST') {
122
+ throw error;
123
+ }
124
+ }
125
+
126
+ const className = toSeederClassName(seederName);
127
+ const fileName = `${className}.js`;
128
+ const filePath = path.join(seedsDir, fileName);
129
+
130
+ const template = getSeederTemplate(className);
131
+ await fs.writeFile(filePath, template);
132
+
133
+ console.log(`✓ Seeder created: ${fileName}`);
134
+ console.log(` Location: ${filePath}`);
135
+ }
136
+
97
137
  /**
98
138
  * Extract table name from migration name
99
139
  */
@@ -128,7 +168,7 @@ function getCreateMigrationTemplate(tableName) {
128
168
  * Migration: Create ${tableName} table
129
169
  */
130
170
 
131
- const Migration = require('../../lib/Migrations/Migration');
171
+ const { Migration } = require('outlet-orm');
132
172
 
133
173
  class Create${capitalize(tableName)}Table extends Migration {
134
174
  /**
@@ -165,7 +205,7 @@ function getAlterMigrationTemplate(tableName) {
165
205
  * Migration: Alter ${tableName} table
166
206
  */
167
207
 
168
- const Migration = require('../../lib/Migrations/Migration');
208
+ const { Migration } = require('outlet-orm');
169
209
 
170
210
  class Alter${capitalize(tableName)}Table extends Migration {
171
211
  /**
@@ -204,6 +244,47 @@ function capitalize(str) {
204
244
  return str.charAt(0).toUpperCase() + str.slice(1);
205
245
  }
206
246
 
247
+ function toSeederClassName(name) {
248
+ const cleaned = String(name)
249
+ .replace(/\.js$/i, '')
250
+ .replace(/[^a-zA-Z0-9]+/g, ' ')
251
+ .trim();
252
+
253
+ const pascal = cleaned
254
+ .split(/\s+/)
255
+ .filter(Boolean)
256
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
257
+ .join('');
258
+
259
+ if (!pascal) {
260
+ throw new Error('Invalid seeder name');
261
+ }
262
+
263
+ return pascal.endsWith('Seeder') ? pascal : `${pascal}Seeder`;
264
+ }
265
+
266
+ function getSeederTemplate(className) {
267
+ return `/**
268
+ * Seeder: ${className}
269
+ */
270
+
271
+ const { Seeder } = require('outlet-orm');
272
+
273
+ class ${className} extends Seeder {
274
+ /**
275
+ * Run the seeder
276
+ */
277
+ async run() {
278
+ await this.insert('table_name', [
279
+ // { name: 'Example' }
280
+ ]);
281
+ }
282
+ }
283
+
284
+ module.exports = ${className};
285
+ `;
286
+ }
287
+
207
288
  /**
208
289
  * Simple flag parser for CLI args
209
290
  * Supports formats:
@@ -219,6 +300,10 @@ function parseFlags(argv) {
219
300
  const stepsRe = /(?:--steps(?:=|\s+)|-s\s+)(\S+)/;
220
301
  const stepsMatch = stepsRe.exec(text);
221
302
  if (stepsMatch) flags.steps = coerce(stepsMatch[1]);
303
+ // Seeder target: --class Name, --class=Name, -c Name
304
+ const classRe = /(?:--class(?:=|\s+)|-c\s+)(\S+)/;
305
+ const classMatch = classRe.exec(text);
306
+ if (classMatch) flags.class = classMatch[1];
222
307
  return flags;
223
308
  }
224
309
 
@@ -261,8 +346,7 @@ async function runNonInteractive(cmd, flags) {
261
346
  }
262
347
  }
263
348
 
264
- const { DatabaseConnection } = require('../lib/Database/DatabaseConnection');
265
- const MigrationManager = require('../lib/Migrations/MigrationManager');
349
+ const { DatabaseConnection, MigrationManager, SeederManager } = require('../src');
266
350
 
267
351
  const connection = new DatabaseConnection(dbConfig);
268
352
  await connection.connect();
@@ -313,6 +397,13 @@ async function runNonInteractive(cmd, flags) {
313
397
  await manager.status();
314
398
  break;
315
399
 
400
+ case 'seed':
401
+ case 'db:seed': {
402
+ const seederManager = new SeederManager(connection);
403
+ await seederManager.run(flags.class || null);
404
+ break;
405
+ }
406
+
316
407
  default:
317
408
  console.error(`✗ Unknown command: ${cmd}`);
318
409
  }
@@ -335,6 +426,8 @@ async function runMigrationCommands() {
335
426
  console.log('4. refresh - Reset and re-run all migrations');
336
427
  console.log('5. fresh - Drop all tables and re-run migrations');
337
428
  console.log('6. status - Show migration status');
429
+ console.log('7. seed - Run seeders from database/seeds');
430
+ console.log('8. make:seed - Create a new seeder file');
338
431
  console.log('0. Exit\n');
339
432
 
340
433
  const choice = await question('Enter your choice: ');
@@ -370,8 +463,7 @@ async function runMigrationCommands() {
370
463
  }
371
464
  }
372
465
 
373
- const { DatabaseConnection } = require('../lib/Database/DatabaseConnection');
374
- const MigrationManager = require('../lib/Migrations/MigrationManager');
466
+ const { DatabaseConnection, MigrationManager, SeederManager } = require('../src');
375
467
 
376
468
  const connection = new DatabaseConnection(dbConfig);
377
469
  await connection.connect();
@@ -424,6 +516,16 @@ async function runMigrationCommands() {
424
516
  await manager.status();
425
517
  break;
426
518
 
519
+ case '7': {
520
+ const seederManager = new SeederManager(connection);
521
+ await seederManager.run();
522
+ break;
523
+ }
524
+
525
+ case '8':
526
+ await makeSeeder();
527
+ break;
528
+
427
529
  default:
428
530
  console.log('Invalid choice');
429
531
  }