alexis-cli 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.
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$" />
5
+ <orderEntry type="inheritedJdk" />
6
+ <orderEntry type="sourceFolder" forTests="false" />
7
+ </component>
8
+ </module>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/libalexis.iml" filepath="$PROJECT_DIR$/.idea/libalexis.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/php.xml ADDED
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="MessDetectorOptionsConfiguration">
4
+ <option name="transferred" value="true" />
5
+ </component>
6
+ <component name="PHPCSFixerOptionsConfiguration">
7
+ <option name="transferred" value="true" />
8
+ </component>
9
+ <component name="PHPCodeSnifferOptionsConfiguration">
10
+ <option name="highlightLevel" value="WARNING" />
11
+ <option name="transferred" value="true" />
12
+ </component>
13
+ <component name="PhpStanOptionsConfiguration">
14
+ <option name="transferred" value="true" />
15
+ </component>
16
+ <component name="PsalmOptionsConfiguration">
17
+ <option name="transferred" value="true" />
18
+ </component>
19
+ </project>
package/bin/alexis.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+
3
+ const makeEntity = require('../commands/make-entity');
4
+ const makeMigration = require('../commands/make-migration');
5
+ const migrate = require('../commands/migrate');
6
+ const makeModule = require('../commands/make-module');
7
+ const makeAuth = require('../commands/make-auth');
8
+ const makeCrud = require('../commands/make-crud');
9
+ const makeSwagger = require('../commands/make-swagger');
10
+
11
+ const [, , command, subcommand, name] = process.argv;
12
+
13
+ (async () => {
14
+ switch (`${command}:${subcommand}`) {
15
+ case 'make:entity':
16
+ await makeEntity(name);
17
+ break;
18
+
19
+ case 'make:migration':
20
+ makeMigration(name);
21
+ break;
22
+
23
+ case 'migrate:undefined':
24
+ migrate.run();
25
+ break;
26
+
27
+ case 'migrate:rollback':
28
+ migrate.rollback();
29
+ break;
30
+
31
+ case 'make:module':
32
+ makeModule(name);
33
+ break;
34
+ case 'make:auth':
35
+ makeAuth();
36
+ break;
37
+ case 'make:crud':
38
+ makeCrud(name);
39
+ break;
40
+ case 'make:swagger':
41
+ makeSwagger();
42
+ break;
43
+ default:
44
+ console.log(`
45
+ ❓ Available commands:
46
+
47
+ alexis make entity <Name>
48
+ alexis make migration <name>
49
+ alexis migrate
50
+ alexis migrate rollback
51
+ alexis make module <name>
52
+ alexis make swagger
53
+ alexis make crud <name>
54
+ `);
55
+ }
56
+ })();
@@ -0,0 +1,308 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ module.exports = () => {
5
+ console.log('🔐 Generating Auth system...');
6
+
7
+ /* =========================
8
+ 1. USER ENTITY
9
+ ========================= */
10
+
11
+ const entityDir = path.join(__dirname, '../../src/entities');
12
+ const userEntityPath = path.join(entityDir, 'User.entity.js');
13
+
14
+ if (!fs.existsSync(entityDir)) {
15
+ fs.mkdirSync(entityDir, { recursive: true });
16
+ }
17
+
18
+ if (!fs.existsSync(userEntityPath)) {
19
+ const userEntity = `
20
+ const { EntitySchema } = require('typeorm');
21
+
22
+ module.exports = new EntitySchema({
23
+ name: 'User',
24
+ tableName: 'users',
25
+ columns: {
26
+ id: { type: Number, primary: true, generated: true },
27
+ email: { type: String, unique: true },
28
+ password: { type: String },
29
+ createdAt: { type: 'timestamp', createDate: true }
30
+ }
31
+ });
32
+ `.trim();
33
+
34
+ fs.writeFileSync(userEntityPath, userEntity + '\n');
35
+ console.log('✅ User entity created');
36
+ } else {
37
+ console.log('ℹ️ User entity already exists');
38
+ }
39
+
40
+ /* =========================
41
+ 2. AUTH MODULE
42
+ ========================= */
43
+
44
+ const authDir = path.join(__dirname, '../../src/modules/v1/auth');
45
+
46
+ if (!fs.existsSync(authDir)) {
47
+ fs.mkdirSync(authDir, { recursive: true });
48
+ }
49
+
50
+ // Controller
51
+ fs.writeFileSync(
52
+ path.join(authDir, 'auth.controller.js'),
53
+ `
54
+ const authService = require('./auth.service');
55
+
56
+ exports.register = async (req, res, next) => {
57
+ try {
58
+ const user = await authService.register(req.body);
59
+ res.status(201).json(user);
60
+ } catch (err) {
61
+ next(err);
62
+ }
63
+ };
64
+
65
+ exports.login = async (req, res, next) => {
66
+ try {
67
+ const token = await authService.login(req.body);
68
+ res.json({ token });
69
+ } catch (err) {
70
+ next(err);
71
+ }
72
+ };
73
+
74
+ exports.profile = (req, res) => {
75
+ res.json(req.user);
76
+ };
77
+ `.trim() + '\n'
78
+ );
79
+
80
+ // Service
81
+ fs.writeFileSync(
82
+ path.join(authDir, 'auth.service.js'),
83
+ `
84
+ const bcrypt = require('bcrypt');
85
+ const jwt = require('jsonwebtoken');
86
+ const AppDataSource = require('../../../config/orm');
87
+ const User = require('../../../entities/User.entity');
88
+ const { JWT_SECRET } = require('../../../config/jwt');
89
+
90
+ exports.register = async ({ email, password }) => {
91
+ const repo = AppDataSource.getRepository(User);
92
+
93
+ if (await repo.findOneBy({ email })) {
94
+ throw new Error('User exists');
95
+ }
96
+
97
+ const hash = await bcrypt.hash(password, 10);
98
+ const user = repo.create({ email, password: hash });
99
+ await repo.save(user);
100
+
101
+ return { id: user.id, email: user.email };
102
+ };
103
+
104
+ exports.login = async ({ email, password }) => {
105
+ const repo = AppDataSource.getRepository(User);
106
+ const user = await repo.findOneBy({ email });
107
+
108
+ if (!user || !(await bcrypt.compare(password, user.password))) {
109
+ throw new Error('Invalid credentials');
110
+ }
111
+
112
+ return jwt.sign(
113
+ { id: user.id, email: user.email },
114
+ JWT_SECRET,
115
+ { expiresIn: '1h' }
116
+ );
117
+ };
118
+ `.trim() + '\n'
119
+ );
120
+
121
+ // Routes (Swagger inclus)
122
+ fs.writeFileSync(
123
+ path.join(authDir, 'auth.routes.js'),
124
+ `
125
+ const express = require('express');
126
+ const router = express.Router();
127
+ const controller = require('./auth.controller');
128
+ const authMiddleware = require('../../../middlewares/auth.middleware');
129
+ const rateLimit = require('express-rate-limit');
130
+
131
+ const loginLimiter = rateLimit({
132
+ windowMs: 15 * 60 * 1000, // 15 min
133
+ max: 5,
134
+ message: { message: "Trop de tentatives de login. Réessayez dans 15 minutes." },
135
+ standardHeaders: true,
136
+ legacyHeaders: false
137
+ });
138
+
139
+
140
+ /**
141
+ * @swagger
142
+ * /auth/register:
143
+ * post:
144
+ * summary: Crée un nouvel utilisateur
145
+ * tags: [Auth]
146
+ * requestBody:
147
+ * required: true
148
+ * content:
149
+ * application/json:
150
+ * schema:
151
+ * type: object
152
+ * required:
153
+ * - email
154
+ * - password
155
+ * properties:
156
+ * email:
157
+ * type: string
158
+ * example: test@test.com
159
+ * password:
160
+ * type: string
161
+ * example: 123456
162
+ * responses:
163
+ * 201:
164
+ * description: Utilisateur créé
165
+ */
166
+ router.post('/register', controller.register);
167
+
168
+ /**
169
+ * @swagger
170
+ * /auth/login:
171
+ * post:
172
+ * summary: Connexion utilisateur
173
+ * tags: [Auth]
174
+ * requestBody:
175
+ * required: true
176
+ * content:
177
+ * application/json:
178
+ * schema:
179
+ * type: object
180
+ * required:
181
+ * - email
182
+ * - password
183
+ * properties:
184
+ * email:
185
+ * type: string
186
+ * example: test@test.com
187
+ * password:
188
+ * type: string
189
+ * example: 123456
190
+ * responses:
191
+ * 200:
192
+ * description: Token JWT
193
+ */
194
+ router.post('/login', controller.login);
195
+
196
+ /**
197
+ * @swagger
198
+ * /auth/profile:
199
+ * get:
200
+ * summary: Récupère le profil de l'utilisateur connecté
201
+ * tags: [Auth]
202
+ * security:
203
+ * - bearerAuth: []
204
+ * responses:
205
+ * 200:
206
+ * description: Profil utilisateur
207
+ */
208
+ router.get('/profile', authMiddleware, controller.profile);
209
+
210
+
211
+ module.exports = router;
212
+ `.trim() + '\n'
213
+ );
214
+
215
+ /* =========================
216
+ 3. AUTH MIDDLEWARE
217
+ ========================= */
218
+
219
+ const middlewareDir = path.join(__dirname, '../../src/middlewares');
220
+ const middlewarePath = path.join(middlewareDir, 'auth.middleware.js');
221
+
222
+ if (!fs.existsSync(middlewareDir)) {
223
+ fs.mkdirSync(middlewareDir, { recursive: true });
224
+ }
225
+
226
+ if (!fs.existsSync(middlewarePath)) {
227
+ fs.writeFileSync(
228
+ middlewarePath,
229
+ `
230
+ const jwt = require('jsonwebtoken');
231
+ const { JWT_SECRET } = require('../config/jwt');
232
+
233
+ module.exports = (req, res, next) => {
234
+ const token = req.headers.authorization?.split(' ')[1];
235
+ if (!token) return res.status(401).json({ message: 'No token' });
236
+
237
+ try {
238
+ req.user = jwt.verify(token, JWT_SECRET);
239
+ next();
240
+ } catch {
241
+ res.status(401).json({ message: 'Invalid token' });
242
+ }
243
+ };
244
+ `.trim() + '\n'
245
+ );
246
+ console.log('✅ Auth middleware created');
247
+ }
248
+
249
+ /* =========================
250
+ 4. JWT CONFIG
251
+ ========================= */
252
+
253
+ const jwtConfigPath = path.join(__dirname, '../../src/config/jwt.js');
254
+
255
+ if (!fs.existsSync(jwtConfigPath)) {
256
+ fs.writeFileSync(
257
+ jwtConfigPath,
258
+ `module.exports = {
259
+ JWT_SECRET: process.env.JWT_SECRET || 'change-me'
260
+ };
261
+ `.trim() + '\n'
262
+ );
263
+ console.log('✅ JWT config created');
264
+ }
265
+
266
+ console.log('\n🎉 Auth system ready');
267
+ console.log('➡️ Next steps:');
268
+ console.log(' alexis make migration create-users');
269
+ console.log(' alexis migrate');
270
+ };
271
+
272
+ /* =========================
273
+ 5. AUTO-REGISTER AUTH ROUTE
274
+ ========================= */
275
+
276
+ const routesIndexPath = path.join(
277
+ __dirname,
278
+ '../../src/routes/v1/index.js'
279
+ );
280
+
281
+ if (!fs.existsSync(routesIndexPath)) {
282
+ console.warn('⚠️ routes/v1/index.js not found, skipping auth route registration');
283
+ } else {
284
+ let indexContent = fs.readFileSync(routesIndexPath, 'utf8');
285
+
286
+ const importLine =
287
+ `const authRoutes = require('../../../src/modules/v1/auth/auth.routes');`;
288
+ const routeLine =
289
+ `router.use('/auth', authRoutes);`;
290
+
291
+ if (!indexContent.includes(importLine)) {
292
+ indexContent = indexContent.replace(
293
+ '// AUTO-IMPORTS (do not remove)',
294
+ `// AUTO-IMPORTS (do not remove)\n${importLine}`
295
+ );
296
+ }
297
+
298
+ if (!indexContent.includes(routeLine)) {
299
+ indexContent = indexContent.replace(
300
+ '// AUTO-ROUTES (do not remove)',
301
+ `// AUTO-ROUTES (do not remove)\n${routeLine}`
302
+ );
303
+ }
304
+
305
+ fs.writeFileSync(routesIndexPath, indexContent);
306
+ console.log('🔗 Auth routes registered in routes/v1/index.js');
307
+ }
308
+
@@ -0,0 +1,276 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ module.exports = (entityName) => {
5
+ if (!entityName) {
6
+ console.error('❌ Usage: alexis make crud <Entity>');
7
+ process.exit(1);
8
+ }
9
+
10
+ const moduleName = entityName.toLowerCase();
11
+ const EntityName =
12
+ entityName.charAt(0).toUpperCase() + entityName.slice(1);
13
+
14
+ // 🔎 Vérifier l'Entity
15
+ const entityPath = path.join(
16
+ __dirname,
17
+ '../../src/entities',
18
+ `${EntityName}.entity.js`
19
+ );
20
+
21
+ if (!fs.existsSync(entityPath)) {
22
+ console.error(`❌ Entity "${EntityName}" not found`);
23
+ process.exit(1);
24
+ }
25
+
26
+ // 📁 Module
27
+ const moduleDir = path.join(
28
+ __dirname,
29
+ '../../src/modules/v1',
30
+ moduleName
31
+ );
32
+
33
+ if (fs.existsSync(moduleDir)) {
34
+ console.error(`❌ Module "${moduleName}" already exists`);
35
+ process.exit(1);
36
+ }
37
+
38
+ fs.mkdirSync(moduleDir, { recursive: true });
39
+
40
+ /* ======================
41
+ SERVICE
42
+ ====================== */
43
+ fs.writeFileSync(
44
+ path.join(moduleDir, `${moduleName}.service.js`),
45
+ `
46
+ const AppDataSource = require('../../../config/orm');
47
+ const ${EntityName} = require('../../../entities/${EntityName}.entity');
48
+
49
+ const repo = () => AppDataSource.getRepository(${EntityName});
50
+
51
+ exports.findAll = () => repo().find();
52
+
53
+ exports.findOne = (id) =>
54
+ repo().findOneBy({ id: Number(id) });
55
+
56
+ exports.create = (data) => repo().save(repo().create(data));
57
+
58
+ exports.update = async (id, data) => {
59
+ await repo().update(id, data);
60
+ return exports.findOne(id);
61
+ };
62
+
63
+ exports.remove = (id) =>
64
+ repo().delete(id);
65
+ `.trim() + '\n'
66
+ );
67
+
68
+ /* ======================
69
+ CONTROLLER
70
+ ====================== */
71
+ fs.writeFileSync(
72
+ path.join(moduleDir, `${moduleName}.controller.js`),
73
+ `
74
+ const service = require('./${moduleName}.service');
75
+
76
+ exports.findAll = async (req, res, next) => {
77
+ try {
78
+ res.json(await service.findAll());
79
+ } catch (e) {
80
+ next(e);
81
+ }
82
+ };
83
+
84
+ exports.findOne = async (req, res, next) => {
85
+ try {
86
+ res.json(await service.findOne(req.params.id));
87
+ } catch (e) {
88
+ next(e);
89
+ }
90
+ };
91
+
92
+ exports.create = async (req, res, next) => {
93
+ try {
94
+ res.status(201).json(await service.create(req.body));
95
+ } catch (e) {
96
+ next(e);
97
+ }
98
+ };
99
+
100
+ exports.update = async (req, res, next) => {
101
+ try {
102
+ res.json(await service.update(req.params.id, req.body));
103
+ } catch (e) {
104
+ next(e);
105
+ }
106
+ };
107
+
108
+ exports.remove = async (req, res, next) => {
109
+ try {
110
+ await service.remove(req.params.id);
111
+ res.status(204).end();
112
+ } catch (e) {
113
+ next(e);
114
+ }
115
+ };
116
+ `.trim() + '\n'
117
+ );
118
+
119
+ /* ======================
120
+ ROUTES + SWAGGER
121
+ ====================== */
122
+ fs.writeFileSync(
123
+ path.join(moduleDir, `${moduleName}.routes.js`),
124
+ `
125
+ /**
126
+ * @swagger
127
+ * tags:
128
+ * name: ${EntityName}
129
+ * description: CRUD ${EntityName}
130
+ */
131
+
132
+ const express = require('express');
133
+ const router = express.Router();
134
+ const controller = require('./${moduleName}.controller');
135
+
136
+ /**
137
+ * @swagger
138
+ * /${moduleName}:
139
+ * get:
140
+ * summary: Get all ${EntityName}
141
+ * tags: [${EntityName}]
142
+ * security:
143
+ * - bearerAuth: []
144
+ * responses:
145
+ * 200:
146
+ * description: List of ${EntityName}
147
+ */
148
+ router.get('/', controller.findAll);
149
+
150
+ /**
151
+ * @swagger
152
+ * /${moduleName}/{id}:
153
+ * get:
154
+ * summary: Get a ${EntityName} by id
155
+ * tags: [${EntityName}]
156
+ * security:
157
+ * - bearerAuth: []
158
+ * parameters:
159
+ * - in: path
160
+ * name: id
161
+ * required: true
162
+ * schema:
163
+ * type: integer
164
+ * responses:
165
+ * 200:
166
+ * description: ${EntityName} found
167
+ * 404:
168
+ * description: ${EntityName} not found
169
+ */
170
+ router.get('/:id', controller.findOne);
171
+
172
+ /**
173
+ * @swagger
174
+ * /${moduleName}:
175
+ * post:
176
+ * summary: Create a ${EntityName}
177
+ * tags: [${EntityName}]
178
+ * security:
179
+ * - bearerAuth: []
180
+ * requestBody:
181
+ * required: true
182
+ * content:
183
+ * application/json:
184
+ * schema:
185
+ * type: object
186
+ * responses:
187
+ * 201:
188
+ * description: ${EntityName} created
189
+ */
190
+ router.post('/', controller.create);
191
+
192
+ /**
193
+ * @swagger
194
+ * /${moduleName}/{id}:
195
+ * put:
196
+ * summary: Update a ${EntityName}
197
+ * tags: [${EntityName}]
198
+ * security:
199
+ * - bearerAuth: []
200
+ * parameters:
201
+ * - in: path
202
+ * name: id
203
+ * required: true
204
+ * schema:
205
+ * type: integer
206
+ * requestBody:
207
+ * required: true
208
+ * content:
209
+ * application/json:
210
+ * schema:
211
+ * type: object
212
+ * responses:
213
+ * 200:
214
+ * description: ${EntityName} updated
215
+ */
216
+ router.put('/:id', controller.update);
217
+
218
+ /**
219
+ * @swagger
220
+ * /${moduleName}/{id}:
221
+ * delete:
222
+ * summary: Delete a ${EntityName}
223
+ * tags: [${EntityName}]
224
+ * security:
225
+ * - bearerAuth: []
226
+ * parameters:
227
+ * - in: path
228
+ * name: id
229
+ * required: true
230
+ * schema:
231
+ * type: integer
232
+ * responses:
233
+ * 204:
234
+ * description: ${EntityName} deleted
235
+ */
236
+ router.delete('/:id', controller.remove);
237
+
238
+ module.exports = router;
239
+ `.trim() + '\n'
240
+ );
241
+
242
+ console.log(`✅ CRUD module "${moduleName}" created`);
243
+
244
+ /* ======================
245
+ AUTO-REGISTER ROUTE
246
+ ====================== */
247
+ const routesIndexPath = path.join(
248
+ __dirname,
249
+ '../../src/routes/v1/index.js'
250
+ );
251
+
252
+ let index = fs.readFileSync(routesIndexPath, 'utf8');
253
+
254
+ const importLine =
255
+ `const ${moduleName}Routes = require('../../../src/modules/v1/${moduleName}/${moduleName}.routes');`;
256
+ const routeLine =
257
+ `router.use('/${moduleName}', ${moduleName}Routes);`;
258
+
259
+ if (!index.includes(importLine)) {
260
+ index = index.replace(
261
+ '// AUTO-IMPORTS (do not remove)',
262
+ `// AUTO-IMPORTS (do not remove)\n${importLine}`
263
+ );
264
+ }
265
+
266
+ if (!index.includes(routeLine)) {
267
+ index = index.replace(
268
+ '// AUTO-ROUTES (do not remove)',
269
+ `// AUTO-ROUTES (do not remove)\n${routeLine}`
270
+ );
271
+ }
272
+
273
+ fs.writeFileSync(routesIndexPath, index);
274
+
275
+ console.log(`🔗 Route "/${moduleName}" registered`);
276
+ };