outlet-orm 4.2.0 → 4.2.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 +39 -26
- package/bin/init.js +166 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,56 +32,69 @@ Si aucun driver n'est installé, un message d'erreur explicite vous indiquera le
|
|
|
32
32
|
|
|
33
33
|
Organisez votre projet utilisant Outlet ORM comme suit :
|
|
34
34
|
|
|
35
|
+
> 🔐 **Sécurité** : Voir le [Guide de Sécurité](./docs/SECURITY.md) pour les bonnes pratiques.
|
|
36
|
+
|
|
35
37
|
```
|
|
36
38
|
mon-projet/
|
|
37
|
-
├── .env #
|
|
39
|
+
├── .env # ⚠️ JAMAIS commité (dans .gitignore)
|
|
40
|
+
├── .env.example # Template sans secrets
|
|
41
|
+
├── .gitignore # Exclure .env, node_modules, logs
|
|
38
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...
|
|
39
47
|
├── database/
|
|
40
48
|
│ ├── config.js # Config migrations (généré par outlet-init)
|
|
41
49
|
│ └── migrations/ # Vos fichiers de migration
|
|
42
|
-
│ ├── 20240101_create_users_table.js
|
|
43
|
-
│ └── 20240102_create_posts_table.js
|
|
44
50
|
├── models/ # Vos classes Model
|
|
45
51
|
│ ├── User.js
|
|
46
52
|
│ ├── Post.js
|
|
47
53
|
│ └── Comment.js
|
|
48
54
|
├── controllers/ # Vos contrôleurs (logique métier)
|
|
49
55
|
│ ├── UserController.js
|
|
50
|
-
│
|
|
51
|
-
│ └── CommentController.js
|
|
56
|
+
│ └── PostController.js
|
|
52
57
|
├── routes/ # Vos fichiers de routes
|
|
53
58
|
│ ├── index.js
|
|
54
|
-
│
|
|
55
|
-
|
|
56
|
-
├──
|
|
57
|
-
│ ├──
|
|
58
|
-
│
|
|
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
|
|
59
66
|
├── services/ # Vos services (logique réutilisable)
|
|
60
67
|
│ ├── AuthService.js
|
|
61
68
|
│ └── EmailService.js
|
|
62
|
-
├──
|
|
63
|
-
│ ├──
|
|
64
|
-
│
|
|
65
|
-
|
|
66
|
-
│
|
|
67
|
-
|
|
68
|
-
│
|
|
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
|
|
75
|
+
│ ├── images/
|
|
76
|
+
│ ├── css/
|
|
77
|
+
│ └── js/
|
|
78
|
+
├── uploads/ # ⚠️ Fichiers uploadés (validés)
|
|
79
|
+
├── logs/ # 📋 Journaux (non versionnés)
|
|
69
80
|
├── src/ # Votre code applicatif
|
|
70
81
|
│ └── index.js
|
|
71
82
|
└── tests/ # Vos tests
|
|
72
83
|
└── models.test.js
|
|
73
84
|
```
|
|
74
85
|
|
|
75
|
-
| Dossier | Rôle |
|
|
86
|
+
| Dossier | Rôle | Sécurité |
|
|
76
87
|
|---------|------|----------|
|
|
77
|
-
| `
|
|
78
|
-
| `database
|
|
79
|
-
| `models/` |
|
|
80
|
-
| `controllers/` |
|
|
81
|
-
| `routes/` | Définition des routes API/Web |
|
|
82
|
-
| `middlewares/` |
|
|
83
|
-
| `services/` | Services réutilisables
|
|
84
|
-
| `
|
|
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 |
|
|
85
98
|
|
|
86
99
|
## ✨ Fonctionnalités clés
|
|
87
100
|
|
package/bin/init.js
CHANGED
|
@@ -95,6 +95,7 @@ module.exports = db;
|
|
|
95
95
|
|
|
96
96
|
// Create project structure directories
|
|
97
97
|
const directories = [
|
|
98
|
+
'config',
|
|
98
99
|
'database',
|
|
99
100
|
'database/migrations',
|
|
100
101
|
'models',
|
|
@@ -102,13 +103,14 @@ module.exports = db;
|
|
|
102
103
|
'routes',
|
|
103
104
|
'middlewares',
|
|
104
105
|
'services',
|
|
105
|
-
'
|
|
106
|
-
'
|
|
107
|
-
'
|
|
108
|
-
'
|
|
109
|
-
'
|
|
110
|
-
'
|
|
111
|
-
'
|
|
106
|
+
'utils',
|
|
107
|
+
'validators',
|
|
108
|
+
'public',
|
|
109
|
+
'public/images',
|
|
110
|
+
'public/css',
|
|
111
|
+
'public/js',
|
|
112
|
+
'uploads',
|
|
113
|
+
'logs',
|
|
112
114
|
'src',
|
|
113
115
|
'tests'
|
|
114
116
|
];
|
|
@@ -124,6 +126,157 @@ module.exports = db;
|
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
// Generate .gitignore
|
|
130
|
+
const gitignoreContent = `# Secrets
|
|
131
|
+
.env
|
|
132
|
+
.env.local
|
|
133
|
+
.env.production
|
|
134
|
+
|
|
135
|
+
# Logs
|
|
136
|
+
logs/
|
|
137
|
+
*.log
|
|
138
|
+
|
|
139
|
+
# Uploads
|
|
140
|
+
uploads/
|
|
141
|
+
|
|
142
|
+
# Dependencies
|
|
143
|
+
node_modules/
|
|
144
|
+
|
|
145
|
+
# Build
|
|
146
|
+
dist/
|
|
147
|
+
build/
|
|
148
|
+
|
|
149
|
+
# IDE
|
|
150
|
+
.vscode/
|
|
151
|
+
.idea/
|
|
152
|
+
`;
|
|
153
|
+
|
|
154
|
+
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
155
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
156
|
+
fs.writeFileSync(gitignorePath, gitignoreContent);
|
|
157
|
+
console.log(`\n✅ .gitignore créé`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Generate .env.example
|
|
161
|
+
const envExampleContent = `# Base de données
|
|
162
|
+
DB_DRIVER=mysql
|
|
163
|
+
DB_HOST=localhost
|
|
164
|
+
DB_PORT=3306
|
|
165
|
+
DB_DATABASE=myapp
|
|
166
|
+
DB_USER=your_user
|
|
167
|
+
DB_PASSWORD=your_password
|
|
168
|
+
|
|
169
|
+
# Sécurité
|
|
170
|
+
JWT_SECRET=your_jwt_secret_here
|
|
171
|
+
JWT_EXPIRES_IN=15m
|
|
172
|
+
|
|
173
|
+
# Application
|
|
174
|
+
NODE_ENV=development
|
|
175
|
+
PORT=3000
|
|
176
|
+
|
|
177
|
+
# CORS
|
|
178
|
+
CORS_ORIGIN=http://localhost:3000
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
const envExamplePath = path.join(process.cwd(), '.env.example');
|
|
182
|
+
if (!fs.existsSync(envExamplePath)) {
|
|
183
|
+
fs.writeFileSync(envExamplePath, envExampleContent);
|
|
184
|
+
console.log(`✅ .env.example créé`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Generate config/security.js
|
|
188
|
+
const securityConfigContent = `/**
|
|
189
|
+
* Configuration de sécurité
|
|
190
|
+
* npm install helmet express-rate-limit xss-clean hpp
|
|
191
|
+
*/
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
// Rate limiting (100 requêtes/15min par IP)
|
|
195
|
+
rateLimit: {
|
|
196
|
+
windowMs: 15 * 60 * 1000,
|
|
197
|
+
max: 100,
|
|
198
|
+
message: { error: 'Trop de requêtes, réessayez plus tard' }
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Rate limiting strict pour auth (5 tentatives/15min)
|
|
202
|
+
authRateLimit: {
|
|
203
|
+
windowMs: 15 * 60 * 1000,
|
|
204
|
+
max: 5,
|
|
205
|
+
message: { error: 'Trop de tentatives de connexion' }
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// CORS
|
|
209
|
+
cors: {
|
|
210
|
+
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
|
211
|
+
credentials: true,
|
|
212
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
|
213
|
+
allowedHeaders: ['Content-Type', 'Authorization']
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
// JWT
|
|
217
|
+
jwt: {
|
|
218
|
+
secret: process.env.JWT_SECRET,
|
|
219
|
+
expiresIn: process.env.JWT_EXPIRES_IN || '15m'
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
`;
|
|
223
|
+
|
|
224
|
+
const securityConfigPath = path.join(process.cwd(), 'config', 'security.js');
|
|
225
|
+
if (!fs.existsSync(securityConfigPath)) {
|
|
226
|
+
fs.writeFileSync(securityConfigPath, securityConfigContent);
|
|
227
|
+
console.log(`✅ config/security.js créé`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Generate middlewares/errorHandler.js
|
|
231
|
+
const errorHandlerContent = `/**
|
|
232
|
+
* Gestionnaire d'erreurs centralisé
|
|
233
|
+
*/
|
|
234
|
+
const errorHandler = (err, req, res, next) => {
|
|
235
|
+
console.error(\`[\${new Date().toISOString()}] Error:\`, err);
|
|
236
|
+
|
|
237
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
238
|
+
|
|
239
|
+
res.status(err.status || 500).json({
|
|
240
|
+
error: isDev ? err.message : 'Erreur serveur',
|
|
241
|
+
stack: isDev ? err.stack : undefined
|
|
242
|
+
});
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
module.exports = errorHandler;
|
|
246
|
+
`;
|
|
247
|
+
|
|
248
|
+
const errorHandlerPath = path.join(process.cwd(), 'middlewares', 'errorHandler.js');
|
|
249
|
+
if (!fs.existsSync(errorHandlerPath)) {
|
|
250
|
+
fs.writeFileSync(errorHandlerPath, errorHandlerContent);
|
|
251
|
+
console.log(`✅ middlewares/errorHandler.js créé`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Generate utils/hash.js
|
|
255
|
+
const hashUtilContent = `/**
|
|
256
|
+
* Utilitaires de hachage
|
|
257
|
+
* npm install bcrypt
|
|
258
|
+
*/
|
|
259
|
+
const bcrypt = require('bcrypt');
|
|
260
|
+
|
|
261
|
+
const SALT_ROUNDS = 12;
|
|
262
|
+
|
|
263
|
+
const hashPassword = async (password) => {
|
|
264
|
+
return bcrypt.hash(password, SALT_ROUNDS);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const verifyPassword = async (password, hash) => {
|
|
268
|
+
return bcrypt.compare(password, hash);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
module.exports = { hashPassword, verifyPassword };
|
|
272
|
+
`;
|
|
273
|
+
|
|
274
|
+
const hashUtilPath = path.join(process.cwd(), 'utils', 'hash.js');
|
|
275
|
+
if (!fs.existsSync(hashUtilPath)) {
|
|
276
|
+
fs.writeFileSync(hashUtilPath, hashUtilContent);
|
|
277
|
+
console.log(`✅ utils/hash.js créé`);
|
|
278
|
+
}
|
|
279
|
+
|
|
127
280
|
// Generate example model
|
|
128
281
|
const modelContent = `const { Model } = require('outlet-orm');
|
|
129
282
|
const db = require('./database');
|
|
@@ -131,11 +284,16 @@ const db = require('./database');
|
|
|
131
284
|
class User extends Model {
|
|
132
285
|
static table = 'users';
|
|
133
286
|
static fillable = ['name', 'email', 'password'];
|
|
134
|
-
static hidden = ['password'];
|
|
287
|
+
static hidden = ['password', 'refresh_token']; // 🔒 Ne jamais exposer
|
|
135
288
|
static casts = {
|
|
136
289
|
id: 'int',
|
|
137
290
|
email_verified: 'boolean'
|
|
138
291
|
};
|
|
292
|
+
static rules = {
|
|
293
|
+
name: 'required|string|min:2|max:100',
|
|
294
|
+
email: 'required|email',
|
|
295
|
+
password: 'required|min:8'
|
|
296
|
+
};
|
|
139
297
|
static connection = db;
|
|
140
298
|
|
|
141
299
|
// Définissez vos relations ici
|