nestcraftx 0.1.5 → 0.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestcraftx",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "CLI to generate scalable NestJS projects with Clean Architecture and Clean Code",
5
5
  "main": "bin/nestcraft.js",
6
6
  "bin": {
package/setup.js CHANGED
@@ -10,6 +10,7 @@ const { createProject } = require("./utils/setups/projectSetup");
10
10
  const { setupAuth } = require("./utils/setups/setupAuth");
11
11
  const { setupSwagger } = require("./utils/setups/setupSwagger");
12
12
  const { setupDatabase } = require("./utils/setups/setupDatabase");
13
+ const { setupBootstrapLogger } = require("./utils/setups/setupLogger");
13
14
 
14
15
  async function main() {
15
16
  const inputs = await getUserInputs2();
@@ -17,8 +18,12 @@ async function main() {
17
18
  await createProject(inputs);
18
19
  await setupCleanArchitecture(inputs);
19
20
 
20
- if (inputs.useAuth) await setupAuth();
21
- if (inputs.useSwagger) await setupSwagger(inputs.swaggerInputs);
21
+ if (inputs.useAuth) await setupAuth(inputs);
22
+ if (inputs.useSwagger) {
23
+ await setupSwagger(inputs.swaggerInputs);
24
+ } else {
25
+ setupBootstrapLogger();
26
+ }
22
27
  if (inputs.useDocker) await configureDocker(inputs);
23
28
 
24
29
  // await setupPrisma(inputs);
package/unutils.txt CHANGED
@@ -171,3 +171,80 @@ export class ${entityNameCapitalized}Repository implements I${entityNameCapitali
171
171
  }
172
172
  }
173
173
  `
174
+
175
+
176
+ `
177
+ /**
178
+ * ${entityNameCapitalized}Controller gère les endpoints de l'API pour l'entité ${entityNameCapitalized}.
179
+ * Il utilise les cas d'utilisation (Use Cases) pour orchestrer les différentes actions métiers liées à l'entité.
180
+ * Ce contrôleur est responsable des actions HTTP telles que la création, la mise à jour, la récupération, et la suppression de ${entityNameCapitalized}.
181
+ */
182
+
183
+ import { Controller, Get, Post, Body, Param, Put, Delete, Injectable } from "@nestjs/common";
184
+ import { ApiTags, ApiOperation } from '@nestjs/swagger';
185
+ // Importation des cas d'utilisation (Use Cases) spécifiques à ${entityNameCapitalized}
186
+ import { Create${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/create-${entityNameLower}.use-case";
187
+ import { Update${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/update-${entityNameLower}.use-case";
188
+ import { GetById${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/getById-${entityNameLower}.use-case";
189
+ import { GetAll${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/getAll-${entityNameLower}.use-case";
190
+ import { Delete${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/delete-${entityNameLower}.use-case";
191
+ // Importation des DTOs pour la validation et la transformation des données entrantes
192
+ import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
193
+
194
+ /**
195
+ * Le contrôleur est annoté avec @ApiTags pour la documentation Swagger.
196
+ * Il regroupe les opérations HTTP relatives à l'entité ${entityNameCapitalized}.
197
+ */
198
+ @Injectable()
199
+ @ApiTags('${entityNameCapitalized}')
200
+ @Controller('${entityNameLower}')
201
+ export class ${entityNameCapitalized}Controller {
202
+ constructor(
203
+ private readonly createUseCase: Create${entityNameCapitalized}UseCase,
204
+ private readonly updateUseCase: Update${entityNameCapitalized}UseCase,
205
+ private readonly getByIdUseCase: GetById${entityNameCapitalized}UseCase,
206
+ private readonly getAllUseCase: GetAll${entityNameCapitalized}UseCase,
207
+ private readonly deleteUseCase: Delete${entityNameCapitalized}UseCase,
208
+ ) {}
209
+
210
+ // 📌 Créer un ${entityNameLower}
211
+ @Post()
212
+ @ApiOperation({ summary: 'Create a new ${entityNameLower}' })
213
+ async create${entityNameCapitalized}(
214
+ @Body() create${entityNameCapitalized}Dto: Create${entityNameCapitalized}Dto,
215
+ ) {
216
+ return this.createUseCase.execute(create${entityNameCapitalized}Dto);
217
+ }
218
+
219
+ // 📌 Mettre à jour un ${entityNameLower}
220
+ @Put(':id')
221
+ @ApiOperation({ summary: 'Update a ${entityNameLower}' })
222
+ async update${entityNameCapitalized}(
223
+ @Param('id') id: string,
224
+ @Body() update${entityNameCapitalized}Dto: Update${entityNameCapitalized}Dto,
225
+ ) {
226
+ return this.updateUseCase.execute(id, update${entityNameCapitalized}Dto);
227
+ }
228
+
229
+ // 📌 Récupérer un ${entityNameLower} par ID
230
+ @Get(':id')
231
+ @ApiOperation({ summary: 'Get a ${entityNameLower} by ID' })
232
+ async getById${entityNameCapitalized}(@Param('id') id: string) {
233
+ return this.getByIdUseCase.execute(id);
234
+ }
235
+
236
+ // 📌 Récupérer tous les ${entityNameLower}s
237
+ @Get()
238
+ @ApiOperation({ summary: 'Get all ${entityNameLower}s' })
239
+ async getAll${entityNameCapitalized}() {
240
+ return this.getAllUseCase.execute();
241
+ }
242
+
243
+ // 📌 Supprimer un ${entityNameLower}
244
+ @Delete(':id')
245
+ @ApiOperation({ summary: 'Delete a ${entityNameLower} by ID' })
246
+ async delete${entityNameCapitalized}(@Param('id') id: string) {
247
+ return this.deleteUseCase.execute(id);
248
+ }
249
+ }
250
+ `
@@ -13,6 +13,7 @@ const {
13
13
  generateDto,
14
14
  generateMiddlewares,
15
15
  generateRepository,
16
+ generateController,
16
17
  } = require("../utils");
17
18
 
18
19
  async function setupCleanArchitecture(inputs) {
@@ -21,6 +22,7 @@ async function setupCleanArchitecture(inputs) {
21
22
 
22
23
  const entitiesData = inputs.entitiesData;
23
24
  const dbConfig = inputs.dbConfig;
25
+ const useSwagger = inputs.useSwagger;
24
26
 
25
27
  const srcPath = "src";
26
28
  const baseFolders = [
@@ -247,7 +249,7 @@ export class ${useCase}${capitalize(entity.name)}UseCase {
247
249
  });
248
250
 
249
251
  // 📌 5. DTOs
250
- const DtoFileContent = await generateDto(entity);
252
+ const DtoFileContent = await generateDto(entity, useSwagger);
251
253
  await createFile({
252
254
  path: `${entityPath}/application/dtos/${entity.name}.dto.ts`,
253
255
  contente: DtoFileContent,
@@ -372,99 +374,46 @@ export class ${entityNameCapitalized}Adapter {
372
374
  });
373
375
 
374
376
  // 📌 10. Controller
377
+ const controllerContente = await generateController(
378
+ entity.name,
379
+ entityPath,
380
+ useSwagger
381
+ );
375
382
  await createFile({
376
383
  path: `${entityPath}/presentation/controllers/${entityNameLower}.controller.ts`,
377
- contente: `
378
- /**
379
- * ${entityNameCapitalized}Controller gère les endpoints de l'API pour l'entité ${entityNameCapitalized}.
380
- * Il utilise les cas d'utilisation (Use Cases) pour orchestrer les différentes actions métiers liées à l'entité.
381
- * Ce contrôleur est responsable des actions HTTP telles que la création, la mise à jour, la récupération, et la suppression de ${entityNameCapitalized}.
382
- */
383
-
384
- import { Controller, Get, Post, Body, Param, Put, Delete, Injectable } from "@nestjs/common";
385
- import { ApiTags, ApiOperation } from '@nestjs/swagger';
386
- // Importation des cas d'utilisation (Use Cases) spécifiques à ${entityNameCapitalized}
387
- import { Create${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/create-${entityNameLower}.use-case";
388
- import { Update${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/update-${entityNameLower}.use-case";
389
- import { GetById${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/getById-${entityNameLower}.use-case";
390
- import { GetAll${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/getAll-${entityNameLower}.use-case";
391
- import { Delete${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/delete-${entityNameLower}.use-case";
392
- // Importation des DTOs pour la validation et la transformation des données entrantes
393
- import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
394
-
395
- /**
396
- * Le contrôleur est annoté avec @ApiTags pour la documentation Swagger.
397
- * Il regroupe les opérations HTTP relatives à l'entité ${entityNameCapitalized}.
398
- */
399
- @Injectable()
400
- @ApiTags('${entityNameCapitalized}')
401
- @Controller('${entityNameLower}')
402
- export class ${entityNameCapitalized}Controller {
403
- constructor(
404
- private readonly createUseCase: Create${entityNameCapitalized}UseCase,
405
- private readonly updateUseCase: Update${entityNameCapitalized}UseCase,
406
- private readonly getByIdUseCase: GetById${entityNameCapitalized}UseCase,
407
- private readonly getAllUseCase: GetAll${entityNameCapitalized}UseCase,
408
- private readonly deleteUseCase: Delete${entityNameCapitalized}UseCase,
409
- ) {}
410
-
411
- // 📌 Créer un ${entityNameLower}
412
- @Post()
413
- @ApiOperation({ summary: 'Create a new ${entityNameLower}' })
414
- async create${entityNameCapitalized}(
415
- @Body() create${entityNameCapitalized}Dto: Create${entityNameCapitalized}Dto,
416
- ) {
417
- return this.createUseCase.execute(create${entityNameCapitalized}Dto);
418
- }
419
-
420
- // 📌 Mettre à jour un ${entityNameLower}
421
- @Put(':id')
422
- @ApiOperation({ summary: 'Update a ${entityNameLower}' })
423
- async update${entityNameCapitalized}(
424
- @Param('id') id: string,
425
- @Body() update${entityNameCapitalized}Dto: Update${entityNameCapitalized}Dto,
426
- ) {
427
- return this.updateUseCase.execute(id, update${entityNameCapitalized}Dto);
428
- }
429
-
430
- // 📌 Récupérer un ${entityNameLower} par ID
431
- @Get(':id')
432
- @ApiOperation({ summary: 'Get a ${entityNameLower} by ID' })
433
- async getById${entityNameCapitalized}(@Param('id') id: string) {
434
- return this.getByIdUseCase.execute(id);
435
- }
436
-
437
- // 📌 Récupérer tous les ${entityNameLower}s
438
- @Get()
439
- @ApiOperation({ summary: 'Get all ${entityNameLower}s' })
440
- async getAll${entityNameCapitalized}() {
441
- return this.getAllUseCase.execute();
442
- }
443
-
444
- // 📌 Supprimer un ${entityNameLower}
445
- @Delete(':id')
446
- @ApiOperation({ summary: 'Delete a ${entityNameLower} by ID' })
447
- async delete${entityNameCapitalized}(@Param('id') id: string) {
448
- return this.deleteUseCase.execute(id);
449
- }
450
- }
451
- `,
384
+ contente: controllerContente,
452
385
  });
453
386
 
454
387
  // 📌 11. Module
455
- let Import = "";
456
- let prismaProvider = "";
457
- let importTormM = `imports: [
458
- TypeOrmModule.forFeature([${entityNameCapitalized}]), // Injection de l'entité
459
- ],`;
388
+ let importsBlock = [];
389
+ let providersBlock = [];
390
+ let extraImports = "";
460
391
 
461
392
  if (dbConfig.orm === "prisma") {
462
- prismaImport = `import { PrismaService } from 'src/prisma/prisma.service';`;
463
- prismaProvider = ` PrismaService,`;
393
+ extraImports = `import { PrismaService } from 'src/prisma/prisma.service';`;
394
+ providersBlock.push("PrismaService");
464
395
  } else if (dbConfig.orm === "typeorm") {
465
- Import = `import { ${entityNameCapitalized} } from 'src/entities/${entityNameCapitalized}.entity';`;
396
+ extraImports = `import { ${entityNameCapitalized} } from 'src/entities/${entityNameCapitalized}.entity';\nimport { TypeOrmModule } from '@nestjs/typeorm';`;
397
+ importsBlock.push(
398
+ `TypeOrmModule.forFeature([${entityNameCapitalized}])`
399
+ );
466
400
  }
467
401
 
402
+ // Always necessary providers
403
+ providersBlock.push(
404
+ `{
405
+ provide: 'I${entityNameCapitalized}Repository',
406
+ useClass: ${entityNameCapitalized}Repository,
407
+ }`,
408
+ `${entityNameCapitalized}Repository`,
409
+ `Create${entityNameCapitalized}UseCase`,
410
+ `Update${entityNameCapitalized}UseCase`,
411
+ `GetById${entityNameCapitalized}UseCase`,
412
+ `GetAll${entityNameCapitalized}UseCase`,
413
+ `Delete${entityNameCapitalized}UseCase`,
414
+ `${entityNameCapitalized}Mapper`
415
+ );
416
+
468
417
  await createFile({
469
418
  path: `${entityPath}/${entityNameLower}.module.ts`,
470
419
  contente: `
@@ -472,12 +421,12 @@ export class ${entityNameCapitalized}Controller {
472
421
  * ${entityNameCapitalized}Module est le module principal qui gère l'entité ${entityNameCapitalized}.
473
422
  * Il regroupe tous les composants nécessaires pour traiter cette entité :
474
423
  * - Contrôleur
475
- * - Répository
424
+ * - Repository
476
425
  * - Use Cases
477
426
  * - Mapper
478
427
  */
479
428
  import { Module } from '@nestjs/common';
480
- import { TypeOrmModule } from '@nestjs/typeorm';
429
+ ${extraImports}
481
430
  import { ${entityNameCapitalized}Controller } from '${entityPath}/presentation/controllers/${entityNameLower}.controller';
482
431
  import { ${entityNameCapitalized}Repository } from '${entityPath}/infrastructure/repositories/${entityNameLower}.repository';
483
432
  import { Create${entityNameCapitalized}UseCase } from '${entityPath}/application/use-cases/create-${entityNameLower}.use-case';
@@ -486,51 +435,18 @@ import { GetById${entityNameCapitalized}UseCase } from '${entityPath}/applicatio
486
435
  import { GetAll${entityNameCapitalized}UseCase } from '${entityPath}/application/use-cases/getAll-${entityNameLower}.use-case';
487
436
  import { Delete${entityNameCapitalized}UseCase } from '${entityPath}/application/use-cases/delete-${entityNameLower}.use-case';
488
437
  import { ${entityNameCapitalized}Mapper } from '${entityPath}/domain/mappers/${entityNameLower}.mapper';
489
- ${Import}
490
438
 
491
439
  @Module({
492
-
493
- ${importTormM}
494
-
495
- /**
496
- * Déclare le contrôleur qui gère les requêtes HTTP relatives à ${entityNameCapitalized}.
497
- * Ce contrôleur contient les actions d'API pour manipuler l'entité ${entityNameCapitalized}.
498
- */
499
- controllers: [${entityNameCapitalized}Controller],
500
-
501
- /**
502
- * Liste des providers nécessaires à la gestion de ${entityNameCapitalized}.
503
- * Cela inclut :
504
- * - Repository : Fournisseur pour accéder aux données de ${entityNameCapitalized}.
505
- * - Use Cases : Logique métier pour la gestion de ${entityNameCapitalized}.
506
- * - Mapper : Permet la transformation entre les DTOs et les entités.
507
- */
440
+ imports: [
441
+ ${importsBlock.join(",\n ")}
442
+ ],
443
+ controllers: [
444
+ ${entityNameCapitalized}Controller
445
+ ],
508
446
  providers: [
509
- ${prismaProvider}
510
-
511
- // Repository : Permet d'interagir avec la base de données
512
- {
513
- provide: 'I${entityNameCapitalized}Repository', // Interface du repository
514
- useClass: ${entityNameCapitalized}Repository, // Classe qui implémente l'interface
515
- },
516
- ${entityNameCapitalized}Repository, // Fournisseur pour l'accès aux données
517
-
518
- // Use Cases : Logique métier pour la gestion de ${entityNameCapitalized}
519
- Create${entityNameCapitalized}UseCase, // Use Case pour créer un ${entityNameLower}
520
- Update${entityNameCapitalized}UseCase, // Use Case pour mettre à jour un ${entityNameLower}
521
- GetById${entityNameCapitalized}UseCase, // Use Case pour récupérer un ${entityNameLower} par son ID
522
- GetAll${entityNameCapitalized}UseCase, // Use Case pour récupérer tous les ${entityNameLower}s
523
- Delete${entityNameCapitalized}UseCase, // Use Case pour supprimer un ${entityNameLower}
524
-
525
- // Mapper : Convertit entre les entités et les DTOs
526
- ${entityNameCapitalized}Mapper, // Mapper pour la transformation des données
447
+ ${providersBlock.join(",\n ")}
527
448
  ],
528
449
  })
529
- /**
530
- * Le module ${entityNameCapitalized} est une unité logique regroupant toutes les dépendances nécessaires
531
- * pour le bon fonctionnement de l'entité ${entityNameCapitalized}.
532
- * Il gère l'injection des services, les actions métier, ainsi que la transformation des données.
533
- */
534
450
  export class ${entityNameCapitalized}Module {}
535
451
  `.trim(),
536
452
  });
@@ -561,8 +477,6 @@ import { APP_INTERCEPTOR } from '@nestjs/core';`,
561
477
  useClass: ResponseInterceptor,
562
478
  },`,
563
479
  });
564
-
565
- logSuccess("structure Clean Architecture générée avec succès !");
566
480
  } catch (error) {
567
481
  logError(`process currency have error: ${error}`);
568
482
  }
@@ -3,9 +3,14 @@ const { logInfo } = require("../loggers/logInfo");
3
3
  const { runCommand } = require("../shell");
4
4
  const { createDirectory, createFile, updateFile } = require("../userInput");
5
5
  const { logSuccess } = require("../loggers/logSuccess");
6
+ const { generateDto } = require("../utils");
6
7
 
7
- async function setupAuth() {
8
+ async function setupAuth(inputs) {
8
9
  logInfo("Ajout de l'authentification avec JWT et Passport...");
10
+
11
+ const dbConfig = inputs.dbConfig;
12
+ const useSwagger = inputs.useSwagger;
13
+
9
14
  await runCommand(
10
15
  `npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt`,
11
16
  "Échec de l'installation des dépendances d'authentification"
@@ -29,26 +34,53 @@ async function setupAuth() {
29
34
  await createDirectory(path);
30
35
  });
31
36
 
32
- // 📌 Auth Module
37
+ const importsArray = [
38
+ dbConfig.orm === "typeorm" ? `TypeOrmModule.forFeature([User])` : null,
39
+ `PassportModule`,
40
+ `JwtModule.register({ secret: 'your-secret-key', signOptions: { expiresIn: '1h' } })`,
41
+ ]
42
+ .filter(Boolean)
43
+ .join(",\n ");
44
+
45
+ const typeormImports =
46
+ dbConfig.orm === "typeorm"
47
+ ? `import { TypeOrmModule } from '@nestjs/typeorm';
48
+ import { User } from 'src/entities/User.entity';`
49
+ : "";
50
+
33
51
  await createFile({
34
52
  path: `${authPath}/auth.module.ts`,
35
53
  contente: `import { Module } from '@nestjs/common';
36
- import { JwtModule } from '@nestjs/jwt';
37
- import { PassportModule } from '@nestjs/passport';
38
- import { AuthService } from '${authPaths.authServicesPath}/auth.service';
39
- import { AuthController } from '${authPaths.authControllersPath}/auth.controller';
40
- import { JwtStrategy } from '${authPaths.authStrategyPath}/jwt.strategy';
41
- import { AuthGuard } from '${authPaths.authGuardsPath}/auth.guard';
42
- @Module({
43
- imports: [
44
- PassportModule,
45
- JwtModule.register({ secret: 'your-secret-key', signOptions: { expiresIn: '1h' } }),
46
- ],
47
- controllers: [AuthController],
48
- providers: [AuthService, JwtStrategy, AuthGuard],
49
- exports: [AuthService],
50
- })
51
- export class AuthModule {}`,
54
+ import { JwtModule } from '@nestjs/jwt';
55
+ import { PassportModule } from '@nestjs/passport';
56
+ import { UserMapper } from 'src/user/domain/mappers/user.mapper';
57
+ ${typeormImports}
58
+ import { AuthService } from '${authPaths.authServicesPath}/auth.service';
59
+ import { PrismaService } from 'src/prisma/prisma.service';
60
+ import { AuthController } from '${authPaths.authControllersPath}/auth.controller';
61
+ import { JwtStrategy } from '${authPaths.authStrategyPath}/jwt.strategy';
62
+ import { AuthGuard } from '${authPaths.authGuardsPath}/auth.guard';
63
+ import { UserRepository } from 'src/user/infrastructure/repositories/user.repository';
64
+
65
+ @Module({
66
+ imports: [
67
+ ${importsArray}
68
+ ],
69
+ controllers: [AuthController],
70
+ providers: [
71
+ PrismaService,
72
+ UserMapper,
73
+ {
74
+ provide: 'IUserRepository',
75
+ useClass: UserRepository,
76
+ },
77
+ AuthService,
78
+ JwtStrategy,
79
+ AuthGuard
80
+ ],
81
+ exports: [AuthService],
82
+ })
83
+ export class AuthModule {}`,
52
84
  });
53
85
 
54
86
  // 📌 Auth Service
@@ -63,11 +95,17 @@ async function setupAuth() {
63
95
  } from '@nestjs/common';
64
96
  import { JwtService } from '@nestjs/jwt';
65
97
  import * as bcrypt from 'bcrypt';
66
- import { CreateUserDto } from 'src/user/application/dtos/user.dto';
67
- import { IUserRepository } from 'src/user/application/interfaces/user.repository.interface';
68
- import { UserEntity } from 'src/user/domain/entities/user.entity';
69
98
  import { v4 as uuidv4 } from 'uuid';
70
99
 
100
+ import { IUserRepository } from 'src/user/application/interfaces/user.repository.interface';
101
+ import { CreateUserDto } from 'src/user/application/dtos/user.dto';
102
+ import { LoginCredentialDto } from 'src/user/application/dtos/loginCredential.dto';
103
+ import { RefreshTokenDto } from 'src/user/application/dtos/refreshToken.dto';
104
+ import { SendOtpDto } from 'src/user/application/dtos/sendOtp.dto';
105
+ import { VerifyOtpDto } from 'src/user/application/dtos/verifyOtp.dto';
106
+ import { ForgotPasswordDto } from 'src/user/application/dtos/forgotPassword.dto';
107
+ import { ResetPasswordDto } from 'src/user/application/dtos/resetPassword.dto';
108
+
71
109
  @Injectable()
72
110
  export class AuthService {
73
111
  private refreshTokens = new Map<string, string>();
@@ -75,19 +113,21 @@ export class AuthService {
75
113
 
76
114
  constructor(
77
115
  private readonly jwtService: JwtService,
78
- @Inject('UserRepository') private readonly userRepository: IUserRepository,
116
+ @Inject('IUserRepository')
117
+ private readonly userRepository: IUserRepository,
79
118
  ) {}
80
119
 
81
- // Hash & verify
120
+ // 🔒 Hasher le mot de passe utilisateur
82
121
  async hashPassword(password: string): Promise<string> {
83
122
  return bcrypt.hash(password, 10);
84
123
  }
85
124
 
125
+ // 🧪 Comparer un mot de passe en clair avec un hash
86
126
  async comparePassword(password: string, hash: string): Promise<boolean> {
87
127
  return bcrypt.compare(password, hash);
88
128
  }
89
129
 
90
- // 🧾 Register
130
+ // 🧾 Inscription (register)
91
131
  async register(dto: CreateUserDto): Promise<{ message: string }> {
92
132
  const existing = await this.userRepository.findAll();
93
133
  if (existing.find((user) => user.getEmail() === dto.email)) {
@@ -100,11 +140,11 @@ export class AuthService {
100
140
  return { message: 'Registration successful' };
101
141
  }
102
142
 
103
- // 🔑 Login
104
- async login({ email, password }: { email: string; password: string }) {
143
+ // 🔑 Connexion (login)
144
+ async login(dto: LoginCredentialDto) {
105
145
  const users = await this.userRepository.findAll();
106
- const user = users.find((u) => u.getEmail() === email);
107
- if (!user || !(await this.comparePassword(password, user.getPassword()))) {
146
+ const user = users.find((u) => u.getEmail() === dto.email);
147
+ if (!user || !(await this.comparePassword(dto.password, user.getPassword()))) {
108
148
  throw new UnauthorizedException('Invalid credentials');
109
149
  }
110
150
 
@@ -117,12 +157,12 @@ export class AuthService {
117
157
  return { accessToken, refreshToken };
118
158
  }
119
159
 
120
- // 🔁 Refresh token
121
- async refreshToken(refreshToken: string) {
160
+ // 🔁 Rafraîchir un token d'accès
161
+ async refreshToken(dto: RefreshTokenDto) {
122
162
  try {
123
- const payload = this.jwtService.verify(refreshToken);
163
+ const payload = this.jwtService.verify(dto.refreshToken);
124
164
  const stored = this.refreshTokens.get(payload.sub);
125
- if (stored !== refreshToken) throw new UnauthorizedException();
165
+ if (stored !== dto.refreshToken) throw new UnauthorizedException();
126
166
 
127
167
  const accessToken = this.jwtService.sign(
128
168
  { sub: payload.sub, email: payload.email },
@@ -134,135 +174,146 @@ export class AuthService {
134
174
  }
135
175
  }
136
176
 
137
- // 🚪 Logout
138
- async logout(refreshToken: string) {
139
- const payload = this.jwtService.verify(refreshToken);
177
+ // 🚪 Déconnexion
178
+ async logout(dto: RefreshTokenDto) {
179
+ const payload = this.jwtService.verify(dto.refreshToken);
140
180
  this.refreshTokens.delete(payload.sub);
141
181
  return { message: 'Logged out successfully' };
142
182
  }
143
183
 
144
- // 📲 Send OTP
145
- async sendOtp(email: string) {
184
+ // 📲 Envoyer un OTP
185
+ async sendOtp(dto: SendOtpDto) {
146
186
  const otp = Math.floor(100000 + Math.random() * 900000).toString();
147
- this.otps.set(email, otp);
148
- console.log(\`[OTP] for ${email} is ${otp}\`);
187
+ this.otps.set(dto.email, otp);
188
+ console.log(\`[OTP] for \${dto.email} is \${otp}\`);
149
189
  return { message: 'OTP sent' };
150
190
  }
151
191
 
152
- // ✅ Verify OTP
153
- async verifyOtp(email: string, otp: string) {
154
- const valid = this.otps.get(email);
155
- if (valid === otp) {
156
- this.otps.delete(email);
192
+ // ✅ Vérifier un OTP
193
+ async verifyOtp(dto: VerifyOtpDto) {
194
+ const valid = this.otps.get(dto.email);
195
+ if (valid === dto.otp) {
196
+ this.otps.delete(dto.email);
157
197
  return { message: 'OTP verified' };
158
198
  }
159
199
  throw new UnauthorizedException('Invalid OTP');
160
200
  }
161
201
 
162
- // 🔐 Forgot password
163
- async forgotPassword(email: string) {
202
+ // 📬 Mot de passe oublié
203
+ async forgotPassword(dto: ForgotPasswordDto) {
164
204
  const users = await this.userRepository.findAll();
165
- const user = users.find((u) => u.getEmail() === email);
205
+ const user = users.find((u) => u.getEmail() === dto.email);
166
206
  if (!user) throw new NotFoundException('User not found');
167
207
 
168
208
  const token = uuidv4();
169
- // await this.userRepository.update(user.getId(), {});
170
- console.log(\`[ResetToken] for ${email} is ${token}\`);
171
-
209
+ console.log(\`[ResetToken] for \${dto.email} is \${token}\`);
172
210
  return { message: 'Reset token sent' };
173
211
  }
174
212
 
175
- // 🔐 Reset password
176
- async resetPassword({
177
- email,
178
- newPassword,
179
- }: {
180
- email: string;
181
- newPassword: string;
182
- }) {
213
+ // 🔄 Réinitialiser le mot de passe
214
+ async resetPassword(dto: ResetPasswordDto) {
183
215
  const users = await this.userRepository.findAll();
184
- const user = users.find((u) => u.getEmail() === email);
216
+ const user = users.find((u) => u.getEmail() === dto.email);
185
217
  if (!user) throw new UnauthorizedException('Invalid reset token');
186
218
 
187
- const password = await this.hashPassword(newPassword);
219
+ const password = await this.hashPassword(dto.newPassword);
188
220
  await this.userRepository.update(user.getId(), { password });
189
221
 
190
222
  return { message: 'Password reset successful' };
191
223
  }
192
224
 
193
- // 👤 Profile
194
- async getProfile(user: UserEntity) {
195
- const found = await this.userRepository.findById(user.getId());
225
+ // 👤 Obtenir le profil
226
+ async getProfile(user: any) {
227
+ const found = await this.userRepository.findById(user.userId);
196
228
  if (!found) throw new NotFoundException('User not found');
197
- return found;
229
+ const email = found.getEmail();
230
+ return { email: email };
198
231
  }
199
232
 
200
- // Token utils
233
+ // 🔧 Générer un token manuellement
201
234
  generateToken(payload: any) {
202
235
  return this.jwtService.sign(payload);
203
236
  }
204
237
  }
205
238
  `,
206
239
  });
207
-
208
240
  // 📌 Auth Controller
209
241
  await createFile({
210
242
  path: `${authPaths.authControllersPath}/auth.controller.ts`,
211
243
  contente: `import { Controller, Post, Body, UseGuards, Get, Req } from '@nestjs/common';
212
244
  import { Request } from 'express';
213
- import { AuthService } from '${authPaths.authServicesPath}/auth.service';
214
- import { JwtAuthGuard } from '${authPaths.authGuardsPath}/jwt-auth.guard';
245
+ import { AuthService } from "${authPaths.authServicesPath}/auth.service";
246
+ import { JwtAuthGuard } from "${authPaths.authGuardsPath}/jwt-auth.guard";
247
+ import { CreateUserDto } from 'src/user/application/dtos/user.dto';
248
+ import { LoginCredentialDto } from 'src/user/application/dtos/loginCredential.dto';
249
+ import { RefreshTokenDto } from 'src/user/application/dtos/refreshToken.dto';
250
+ import { SendOtpDto } from 'src/user/application/dtos/sendOtp.dto';
251
+ import { VerifyOtpDto } from 'src/user/application/dtos/verifyOtp.dto';
252
+ import { ForgotPasswordDto } from 'src/user/application/dtos/forgotPassword.dto';
253
+ import { ResetPasswordDto } from 'src/user/application/dtos/resetPassword.dto';
254
+ ${useSwagger ? "import { ApiBearerAuth } from '@nestjs/swagger';" : ""}
215
255
 
216
256
  @Controller('auth')
217
257
  export class AuthController {
218
258
  constructor(private readonly authService: AuthService) {}
219
259
 
260
+ // 📝 Créer un compte utilisateur (👤)
220
261
  @Post('register')
221
- async register(@Body() body: any) {
262
+ async register(@Body() body: CreateUserDto) {
222
263
  return this.authService.register(body);
223
264
  }
224
265
 
266
+ // 🔐 Connexion utilisateur (🔑)
225
267
  @Post('login')
226
- async login(@Body() body: any) {
268
+ async login(@Body() body: LoginCredentialDto) {
227
269
  return this.authService.login(body);
228
270
  }
229
271
 
272
+ // ♻️ Rafraîchir le token d'accès (🔁)
230
273
  @Post('refresh')
231
- async refreshToken(@Body() body: any) {
232
- return this.authService.refreshToken(body.refreshToken);
274
+ async refreshToken(@Body() body: RefreshTokenDto) {
275
+ return this.authService.refreshToken(body);
233
276
  }
234
277
 
278
+ // 🚪 Déconnexion utilisateur (🚫)
235
279
  @Post('logout')
236
- async logout(@Body() body: any) {
237
- return this.authService.logout(body.refreshToken);
280
+ async logout(@Body() body: RefreshTokenDto) {
281
+ return this.authService.logout(body);
238
282
  }
239
283
 
284
+ // 📤 Envoyer un OTP au mail (📧)
240
285
  @Post('send-otp')
241
- async sendOtp(@Body() body: any) {
286
+ async sendOtp(@Body() body: SendOtpDto) {
242
287
  return this.authService.sendOtp(body);
243
288
  }
244
289
 
290
+ // ✅ Vérifier l'OTP envoyé (✔️)
245
291
  @Post('verify-otp')
246
- async verifyOtp(@Body() body: { email: string; otp: string }) {
247
- return this.authService.verifyOtp(body.email, body.otp);
292
+ async verifyOtp(@Body() body: VerifyOtpDto) {
293
+ return this.authService.verifyOtp(body);
248
294
  }
249
295
 
296
+ // 🔁 Mot de passe oublié (📨)
250
297
  @Post('forgot-password')
251
- async forgotPassword(@Body() body: any) {
252
- return this.authService.forgotPassword(body.email);
298
+ async forgotPassword(@Body() body: ForgotPasswordDto) {
299
+ return this.authService.forgotPassword(body);
253
300
  }
254
301
 
302
+ // 🔄 Réinitialiser le mot de passe (🔓)
255
303
  @Post('reset-password')
256
- async resetPassword(@Body() body: any) {
304
+ async resetPassword(@Body() body: ResetPasswordDto) {
257
305
  return this.authService.resetPassword(body);
258
306
  }
259
307
 
308
+ // 👤 Obtenir le profil utilisateur connecté (🧑‍💼)
309
+ ${useSwagger ? "@ApiBearerAuth()" : ""}
260
310
  @UseGuards(JwtAuthGuard)
261
311
  @Get('me')
262
312
  async getProfile(@Req() req: Request) {
263
313
  if (req.user) return this.authService.getProfile(req.user);
264
314
  }
265
315
  }
316
+
266
317
  `,
267
318
  });
268
319
 
@@ -282,8 +333,10 @@ export class AuthController {
282
333
  });
283
334
  }
284
335
  async validate(payload: any) {
285
- return { userId: payload.sub, username: payload.username };
286
- }
336
+ return {
337
+ userId: payload.sub,
338
+ email: payload.email,
339
+ }; }
287
340
  }`,
288
341
  });
289
342
 
@@ -400,6 +453,70 @@ export class JwtAuthGuard extends AuthGuard('jwt') {
400
453
  `,
401
454
  });
402
455
 
456
+ // 📌 auth DTOs in user entity
457
+ const dtos = [
458
+ {
459
+ name: "loginCredential",
460
+ fields: [
461
+ { name: "email", type: "string", swaggerExample: "user@example.com" },
462
+ {
463
+ name: "password",
464
+ type: "string",
465
+ swaggerExample: "StrongPassword123!",
466
+ },
467
+ ],
468
+ },
469
+ {
470
+ name: "refreshToken",
471
+ fields: [
472
+ {
473
+ name: "refreshToken",
474
+ type: "string",
475
+ swaggerExample: "eyJhbGciOiJIUzI1NiIsInR5cCI6...",
476
+ },
477
+ ],
478
+ },
479
+ {
480
+ name: "sendOtp",
481
+ fields: [
482
+ { name: "email", type: "string", swaggerExample: "user@example.com" },
483
+ ],
484
+ },
485
+ {
486
+ name: "verifyOtp",
487
+ fields: [
488
+ { name: "email", type: "string", swaggerExample: "user@example.com" },
489
+ { name: "otp", type: "string", swaggerExample: "123456" },
490
+ ],
491
+ },
492
+ {
493
+ name: "forgotPassword",
494
+ fields: [
495
+ { name: "email", type: "string", swaggerExample: "user@example.com" },
496
+ ],
497
+ },
498
+ {
499
+ name: "resetPassword",
500
+ fields: [
501
+ { name: "email", type: "string", swaggerExample: "user@example.com" },
502
+ {
503
+ name: "newPassword",
504
+ type: "string",
505
+ swaggerExample: "NewStrongPass123!",
506
+ },
507
+ ],
508
+ },
509
+ ];
510
+
511
+ // ✅ Génération de chaque DTO
512
+ for (const dto of dtos) {
513
+ const DtoFileContent = await generateDto(dto, useSwagger, true); // tu dois adapter ta fonction generateDto pour recevoir un dto avec `name` et `fields`
514
+ await createFile({
515
+ path: `src/user/application/dtos/${dto.name}.dto.ts`,
516
+ contente: DtoFileContent,
517
+ });
518
+ }
519
+
403
520
  // modification de AppModule
404
521
  const appModulePath = "src/app.module.ts";
405
522
 
@@ -37,7 +37,7 @@ async function setupDatabase(inputs) {
37
37
  async function setupMongoDB(inputs) {
38
38
  logInfo("Configuration de MongoDB...");
39
39
  // Appelle un script spécifique à MongoDB
40
- await setupMongoDBConfig(inputs);
40
+ // await setupMongoDBConfig(inputs);
41
41
  }
42
42
  async function setupSQLite(inputs) {
43
43
  logInfo("Configuration de SQLite...");
@@ -0,0 +1,53 @@
1
+ const fs = require("fs");
2
+
3
+ async function setupBootstrapLogger() {
4
+ // Modification de main.ts pour intégrer Swagger
5
+ const mainTsPath = "src/main.ts";
6
+ let mainTs = fs.readFileSync(mainTsPath, "utf8");
7
+
8
+ if (!mainTs.includes("await app.listen")) return mainTs;
9
+
10
+ // Injecte les imports si non présents
11
+ if (!mainTs.includes("Logger")) {
12
+ mainTs = mainTs.replace(
13
+ "import { NestFactory",
14
+ "import { Logger } from '@nestjs/common';\nimport { NestFactory"
15
+ );
16
+ }
17
+ if (!mainTs.includes("ConfigService")) {
18
+ mainTs = mainTs.replace(
19
+ "import { NestFactory",
20
+ "import { ConfigService } from '@nestjs/config';\nimport { NestFactory"
21
+ );
22
+ }
23
+
24
+ // Injecte la récupération des variables
25
+ if (!mainTs.includes("const host = configService.get<string>('HOST'")) {
26
+ mainTs = mainTs.replace(
27
+ "const app = await NestFactory.create(AppModule);",
28
+ `const app = await NestFactory.create(AppModule);
29
+
30
+ const configService = app.get(ConfigService);
31
+ const port = configService.get<number>('PORT', 3000);
32
+ const host = configService.get<string>('HOST', '0.0.0.0');`
33
+ );
34
+ }
35
+
36
+ // Remplace le démarrage de l'app par le bloc stylisé
37
+ return mainTs.replace(
38
+ "await app.listen(process.env.PORT ?? 3000);",
39
+ `try {
40
+ await app.listen(port, host);
41
+
42
+ const logger = new Logger('Bootstrap');
43
+ logger.log(\`🚀 Application running on: \${await app.getUrl()}\`);
44
+ logger.log(\`🌐 Environment: \${process.env.NODE_ENV || 'development'}\`);
45
+ logger.log(\`📡 Listening on \${host}:\${port}\`);
46
+ } catch (error) {
47
+ const logger = new Logger('Bootstrap');
48
+ logger.error('❌ Failed to start the server', error);
49
+ process.exit(1);
50
+ }`
51
+ );
52
+ }
53
+ module.exports = { setupBootstrapLogger };
@@ -45,14 +45,15 @@ async function setupSwagger(inputs) {
45
45
  .setTitle('${inputs.title}')
46
46
  .setDescription('${inputs.description}')
47
47
  .setVersion('${inputs.version}')
48
- .addBearerAuth(
49
- { type: 'http', scheme: 'bearer', bearerFormat: 'JWT', in: 'header' },
50
- 'access-token'
51
- )
48
+ .addBearerAuth()
52
49
  .build();
53
50
 
54
51
  const document = SwaggerModule.createDocument(app, config);
55
- SwaggerModule.setup('${inputs.endpoint}', app, document);
52
+ SwaggerModule.setup('${inputs.endpoint}', app, document,{
53
+ swaggerOptions: {
54
+ persistAuthorization: true,
55
+ },
56
+ });
56
57
 
57
58
  const configService = app.get(ConfigService);
58
59
  const port = configService.get<number>('PORT', 3000);
package/utils/utils.js CHANGED
@@ -154,31 +154,32 @@ export class ${entityName}Mapper {
154
154
  `;
155
155
  }
156
156
 
157
- export async function generateDto(entity) {
157
+ /* export async function generateDto(entity, useSwagger) {
158
158
  const entityName = capitalize(entity.name);
159
159
 
160
160
  const getExampleForField = (f) => {
161
- // Dynamique et plus réaliste, basé sur le type de champ et des exemples courants
162
161
  switch (f.type) {
163
162
  case "string":
164
- return `"${f.name}_example"`; // Exemple générique basé sur le nom du champ
163
+ return `"${f.name}_example"`;
165
164
  case "number":
166
- return f.name === "price" ? 199.99 : 123; // Exemple spécifique pour 'price'
165
+ return f.name === "price" ? 199.99 : 123;
167
166
  case "boolean":
168
- return true; // Exemple générique pour les booléens
167
+ return true;
169
168
  case "Date":
170
169
  case "date":
171
- return `"2024-01-01T00:00:00Z"`; // Exemple générique pour les dates
170
+ return `"2024-01-01T00:00:00Z"`;
172
171
  case "role":
173
- return `"user"`; // Exemple pour les rôles, par défaut 'user'
172
+ return `"user"`;
174
173
  case "discount":
175
- return 10; // Exemple pour une valeur de type discount
174
+ return 10;
176
175
  case "account":
177
- return `"acc_${f.name}_12345"`; // Exemple pour un compte
176
+ return `"acc_${f.name}_12345"`;
178
177
  case "amount":
179
- return 500.75; // Exemple pour un montant
178
+ return 500.75;
179
+ case "email":
180
+ return "user@example.com";
180
181
  default:
181
- return `"sample_${f.name}"`; // Fallback générique pour les autres types
182
+ return `"sample_${f.name}"`;
182
183
  }
183
184
  };
184
185
 
@@ -191,17 +192,19 @@ export async function generateDto(entity) {
191
192
  date: "IsDate",
192
193
  }[f.type] || "IsString";
193
194
 
194
- const apiPropertyDecorator = f.optional
195
- ? `@ApiPropertyOptional({ example: ${getExampleForField(f)}, type: '${
196
- f.type
197
- }' })`
198
- : `@ApiProperty({ example: ${getExampleForField(f)}, type: '${
199
- f.type
200
- }' })`;
201
-
202
- return `${apiPropertyDecorator}
203
- @${typeDecorator}()
204
- ${f.name}${f.optional ? "?" : ""}: ${f.type};`;
195
+ const swaggerDecorator = useSwagger
196
+ ? f.optional
197
+ ? `@ApiPropertyOptional({ example: ${getExampleForField(f)}, type: '${
198
+ f.type
199
+ }' })\n`
200
+ : `@ApiProperty({ example: ${getExampleForField(f)}, type: '${
201
+ f.type
202
+ }' })\n`
203
+ : "";
204
+
205
+ return `${swaggerDecorator} @${typeDecorator}()\n ${f.name}${
206
+ f.optional ? "?" : ""
207
+ }: ${f.type};`;
205
208
  };
206
209
 
207
210
  const dtoFields = entity.fields
@@ -213,8 +216,12 @@ export async function generateDto(entity) {
213
216
  .join("\n\n");
214
217
 
215
218
  return `
216
- import { IsOptional, IsString, IsInt, IsBoolean, IsDate } from 'class-validator';
217
- import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
219
+ import { IsOptional, IsString, IsEnum, IsInt, IsBoolean, IsDate, MinLength } from 'class-validator';
220
+ ${
221
+ useSwagger
222
+ ? "import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';"
223
+ : ""
224
+ }
218
225
 
219
226
  // Vous pouvez décommenter ce champ si vous voulez gérer le rôle de l'utilisateur.
220
227
  // import { Role } from 'src/modules/user/domain/enums/role.enum';
@@ -222,19 +229,284 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
222
229
  export class Create${entityName}Dto {
223
230
  ${dtoFields}
224
231
 
225
- // Décommentez et ajustez le rôle si nécessaire.
226
- // @ApiProperty({ example: 'admin', type: 'string' })
227
- // @IsString()
228
- // role: Role;
232
+ // Ajustez le rôle si nécessaire.
233
+ ${
234
+ useSwagger && entityName == "User"
235
+ ? "@ApiPropertyOptional({ example: 'admin', type: 'string' })\n "
236
+ : ""
237
+ }${
238
+ useSwagger && entityName == "User"
239
+ ? `@IsEnum(Role)
240
+ role?: Role.ADMIN;
241
+ `
242
+ : ""
243
+ }
229
244
  }
230
245
 
231
246
  export class Update${entityName}Dto {
232
247
  ${updateDtoFields}
233
248
 
234
- // Décommentez et ajustez le rôle si nécessaire.
235
- // @ApiPropertyOptional({ example: 'admin', type: 'string' })
236
- // @IsString()
237
- // role?: Role;
249
+ ${
250
+ useSwagger && entityName == "User"
251
+ ? "@ApiPropertyOptional({ example: 'admin', type: 'string' })\n "
252
+ : ""
253
+ }${
254
+ useSwagger && entityName == "User"
255
+ ? `@IsEnum(Role)
256
+ role?: Role.ADMIN;
257
+ `
258
+ : ""
259
+ }
260
+ }
261
+ `;
262
+ } */
263
+
264
+ export async function generateDto(entity, useSwagger, isAuthDto = false) {
265
+ const entityName = capitalize(entity.name);
266
+
267
+ const getExampleForField = (f) => {
268
+ const fieldName = f.name.toLowerCase();
269
+
270
+ // 🚀 Cas spécifiques pour AUTH DTO (exemples réalistes)
271
+ if (isAuthDto) {
272
+ if (fieldName.includes("newpassword")) {
273
+ return `"NewStrongPass123!"`;
274
+ }
275
+ if (fieldName.includes("otp")) {
276
+ return `"123456"`;
277
+ }
278
+ if (fieldName.includes("token")) {
279
+ return `"eyJhbGciOiJIUzI1NiIsInR5cCI6..."`;
280
+ }
281
+ }
282
+
283
+ if (fieldName.includes("email")) {
284
+ return `"user@example.com"`;
285
+ }
286
+ if (fieldName.includes("password")) {
287
+ return `"StrongPassword123!"`;
288
+ }
289
+ // 🌐 Cas génériques applicables à tout
290
+ if (fieldName.includes("price")) {
291
+ return 199.99;
292
+ }
293
+ if (fieldName.includes("amount")) {
294
+ return 500.75;
295
+ }
296
+ if (fieldName.includes("role")) {
297
+ return `"user"`;
298
+ }
299
+ if (fieldName.includes("date")) {
300
+ return `"2024-01-01T00:00:00Z"`;
301
+ }
302
+ if (fieldName.includes("account")) {
303
+ return `"acc_${f.name}_12345"`;
304
+ }
305
+ if (fieldName.includes("discount")) {
306
+ return 10;
307
+ }
308
+
309
+ // 🛠 Sinon fallback basique selon le type
310
+ switch (f.type) {
311
+ case "string":
312
+ return `"${f.name}_example"`;
313
+ case "number":
314
+ return 123;
315
+ case "boolean":
316
+ return true;
317
+ case "Date":
318
+ case "date":
319
+ return `"2024-01-01T00:00:00Z"`;
320
+ default:
321
+ return `"sample_${f.name}"`;
322
+ }
323
+ };
324
+
325
+ const generateFieldLine = (f) => {
326
+ const typeDecorator =
327
+ {
328
+ string: "IsString",
329
+ number: "IsInt",
330
+ boolean: "IsBoolean",
331
+ date: "IsDate",
332
+ }[f.type] || "IsString";
333
+
334
+ const swaggerDecorator = useSwagger
335
+ ? f.optional
336
+ ? `@ApiPropertyOptional({ example: ${getExampleForField(f)}, type: '${
337
+ f.type
338
+ }' })\n`
339
+ : `@ApiProperty({ example: ${getExampleForField(f)}, type: '${
340
+ f.type
341
+ }' })\n`
342
+ : "";
343
+
344
+ return `${swaggerDecorator} @${typeDecorator}()\n ${f.name}${
345
+ f.optional ? "?" : ""
346
+ }: ${f.type};`;
347
+ };
348
+
349
+ const dtoFields = entity.fields
350
+ .map((f) => generateFieldLine({ ...f, optional: false }))
351
+ .join("\n\n");
352
+
353
+ const updateDtoFields = entity.fields
354
+ .map((f) => generateFieldLine({ ...f, optional: true }))
355
+ .join("\n\n");
356
+
357
+ const commonImports = `import { IsOptional, IsString, IsEnum, IsInt, IsBoolean, IsDate, MinLength } from 'class-validator';
358
+ ${
359
+ useSwagger
360
+ ? "import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';"
361
+ : ""
362
+ }
363
+ `;
364
+
365
+ // ✅ Cas AUTH DTO : une seule classe + nom déjà donné (pas de Create/Update)
366
+ if (isAuthDto) {
367
+ return `
368
+ ${commonImports}
369
+
370
+ export class ${entityName}Dto {
371
+ ${dtoFields}
372
+ }
373
+ `;
374
+ }
375
+
376
+ // ✅ Cas général : Create + Update
377
+ return `
378
+ ${commonImports}
379
+
380
+ // Vous pouvez décommenter ce champ si vous voulez gérer le rôle de l'utilisateur.
381
+ import { Role } from 'src/user/domain/enums/role.enum';
382
+
383
+ export class Create${entityName}Dto {
384
+ ${dtoFields}
385
+
386
+ // Ajustez le rôle si nécessaire.
387
+ ${
388
+ useSwagger && entityName == "User"
389
+ ? "@ApiPropertyOptional({ example: 'admin', type: 'string' })\n "
390
+ : ""
391
+ }${
392
+ useSwagger && entityName == "User"
393
+ ? `@IsEnum(Role)
394
+ role?: Role.ADMIN;
395
+ `
396
+ : ""
397
+ }
398
+ }
399
+
400
+ export class Update${entityName}Dto {
401
+ ${updateDtoFields}
402
+
403
+ ${
404
+ useSwagger && entityName == "User"
405
+ ? "@ApiPropertyOptional({ example: 'admin', type: 'string' })\n "
406
+ : ""
407
+ }${
408
+ useSwagger && entityName == "User"
409
+ ? `@IsEnum(Role)
410
+ role?: Role.ADMIN;
411
+ `
412
+ : ""
413
+ }
414
+ }
415
+ `;
416
+ }
417
+
418
+ export async function generateController(entityName, entityPath, useSwagger) {
419
+ const entityNameLower = decapitalize(entityName);
420
+ const entityNameCapitalized = capitalize(entityName);
421
+
422
+ const swaggerImports = useSwagger
423
+ ? `import { ApiTags, ApiOperation } from '@nestjs/swagger';`
424
+ : "";
425
+
426
+ const swaggerClassDecorator = useSwagger
427
+ ? `@ApiTags('${entityNameCapitalized}')`
428
+ : "";
429
+
430
+ const swaggerMethodDecorator = (summary) =>
431
+ useSwagger ? `@ApiOperation({ summary: '${summary}' })\n ` : "";
432
+
433
+ return `
434
+ /**
435
+ * ${entityNameCapitalized}Controller gère les endpoints de l'API pour l'entité ${entityNameCapitalized}.
436
+ * Il utilise les cas d'utilisation (Use Cases) pour orchestrer les différentes actions métiers liées à l'entité.
437
+ */
438
+
439
+ import { Controller, Get, Post, Body, Param, Put, Delete, Injectable } from "@nestjs/common";
440
+ ${swaggerImports}
441
+ // Importation des cas d'utilisation
442
+ import { Create${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/create-${entityNameLower}.use-case";
443
+ import { Update${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/update-${entityNameLower}.use-case";
444
+ import { GetById${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/getById-${entityNameLower}.use-case";
445
+ import { GetAll${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/getAll-${entityNameLower}.use-case";
446
+ import { Delete${entityNameCapitalized}UseCase } from "${entityPath}/application/use-cases/delete-${entityNameLower}.use-case";
447
+ // DTOs
448
+ import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
449
+
450
+ @Injectable()
451
+ ${swaggerClassDecorator}
452
+ @Controller('${entityNameLower}')
453
+ export class ${entityNameCapitalized}Controller {
454
+ constructor(
455
+ private readonly createUseCase: Create${entityNameCapitalized}UseCase,
456
+ private readonly updateUseCase: Update${entityNameCapitalized}UseCase,
457
+ private readonly getByIdUseCase: GetById${entityNameCapitalized}UseCase,
458
+ private readonly getAllUseCase: GetAll${entityNameCapitalized}UseCase,
459
+ private readonly deleteUseCase: Delete${entityNameCapitalized}UseCase,
460
+ ) {}
461
+
462
+ ${
463
+ entityNameLower != "user"
464
+ ? `// 📌 Créer un ${entityNameLower}
465
+ @Post()
466
+ ${swaggerMethodDecorator(
467
+ `Create a new ${entityNameLower}`
468
+ )}async create${entityNameCapitalized}(
469
+ @Body() create${entityNameCapitalized}Dto: Create${entityNameCapitalized}Dto,
470
+ ) {
471
+ return this.createUseCase.execute(create${entityNameCapitalized}Dto);
472
+ }`
473
+ : ""
474
+ }
475
+
476
+ // 📌 Mettre à jour un ${entityNameLower}
477
+ @Put(':id')
478
+ ${swaggerMethodDecorator(
479
+ `Update a ${entityNameLower}`
480
+ )}async update${entityNameCapitalized}(
481
+ @Param('id') id: string,
482
+ @Body() update${entityNameCapitalized}Dto: Update${entityNameCapitalized}Dto,
483
+ ) {
484
+ return this.updateUseCase.execute(id, update${entityNameCapitalized}Dto);
485
+ }
486
+
487
+ // 📌 Récupérer un ${entityNameLower} par ID
488
+ @Get(':id')
489
+ ${swaggerMethodDecorator(
490
+ `Get a ${entityNameLower} by ID`
491
+ )}async getById${entityNameCapitalized}(@Param('id') id: string) {
492
+ return this.getByIdUseCase.execute(id);
493
+ }
494
+
495
+ // 📌 Récupérer tous les ${entityNameLower}s
496
+ @Get()
497
+ ${swaggerMethodDecorator(
498
+ `Get all ${entityNameLower}s`
499
+ )}async getAll${entityNameCapitalized}() {
500
+ return this.getAllUseCase.execute();
501
+ }
502
+
503
+ // 📌 Supprimer un ${entityNameLower}
504
+ @Delete(':id')
505
+ ${swaggerMethodDecorator(
506
+ `Delete a ${entityNameLower} by ID`
507
+ )}async delete${entityNameCapitalized}(@Param('id') id: string) {
508
+ return this.deleteUseCase.execute(id);
509
+ }
238
510
  }
239
511
  `;
240
512
  }
@@ -436,8 +708,6 @@ export async function generateRepository(entityName, orm) {
436
708
  const entityNameLower = entityName.toLowerCase();
437
709
  const entityPath = `src/${entityNameLower}`;
438
710
 
439
- console.log(`entity name: ${entityNameLower} \n omr selected: ${orm}`);
440
-
441
711
  // Gère le switch en fonction de l'ORM choisi
442
712
  switch (orm) {
443
713
  case "typeorm":