nestcraftx 0.1.8 → 0.1.9

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.8",
3
+ "version": "0.1.9",
4
4
  "description": "CLI to generate scalable NestJS projects with Clean Architecture and Clean Code",
5
5
  "main": "bin/nestcraft.js",
6
6
  "bin": {
@@ -148,7 +148,7 @@ export class Create${entityName}UseCase {
148
148
  this.logger.log('Début création ${entityName}');
149
149
  try {
150
150
  const result = await this.${entityNameLower}Repository.create(data);
151
- this.logger.log('Création réussie');
151
+ this.logger.log('Création réussie: ', result.getId());
152
152
  return result;
153
153
  } catch (error) {
154
154
  this.logger.error('Erreur lors de la création', error.stack);
@@ -176,7 +176,7 @@ export class GetById${entityName}UseCase {
176
176
  ) {}
177
177
 
178
178
  async execute(id: string): Promise<${entityName}Entity | null> {
179
- this.logger.log(\`Recherche de ${entityName} par id: \${id}\`);
179
+ // this.logger.log(\`Recherche de ${entityName} par id: \${id}\`);
180
180
  try {
181
181
  const result = await this.${entityNameLower}Repository.findById(id);
182
182
  this.logger.log('Recherche réussie');
@@ -207,7 +207,7 @@ export class GetAll${entityName}UseCase {
207
207
  ) {}
208
208
 
209
209
  async execute(): Promise<${entityName}Entity[]> {
210
- this.logger.log('Récupération de tous les ${entityName}s');
210
+ // this.logger.log('Récupération de tous les ${entityName}s');
211
211
  try {
212
212
  const result = await this.${entityNameLower}Repository.findAll();
213
213
  this.logger.log('Récupération réussie');
@@ -239,8 +239,15 @@ export class Update${entityName}UseCase {
239
239
  ) {}
240
240
 
241
241
  async execute(id: string, data: Update${entityName}Dto): Promise<${entityName}Entity | null> {
242
- this.logger.log(\`Mise à jour de ${entityName} id: \${id}\`);
242
+ // this.logger.log(\`Mise à jour de ${entityName} id: \${id}\`);
243
+
243
244
  try {
245
+ // Vérifier l'existence de l'élément
246
+ const existing = await this.${entityNameLower}Repository.findById(id);
247
+ if (!existing) {
248
+ this.logger.warn(\`${entityName} avec l'id \${id} non trouvé pour la mise à jour\`);
249
+ throw new Error('${entityName} non trouvé');
250
+ }
244
251
  const result = await this.${entityNameLower}Repository.update(id, data);
245
252
  this.logger.log('Mise à jour réussie');
246
253
  return result;
@@ -269,8 +276,14 @@ export class Delete${entityName}UseCase {
269
276
  ) {}
270
277
 
271
278
  async execute(id: string): Promise<void> {
272
- this.logger.log(\`Suppression de ${entityName} id: \${id}\`);
279
+ // this.logger.log(\`Suppression de ${entityName} id: \${id}\`);
273
280
  try {
281
+ // Vérifier l'existence de l'élément
282
+ const existing = await this.${entityNameLower}Repository.findById(id);
283
+ if (!existing) {
284
+ this.logger.warn(\`${entityName} avec l'id \${id} non trouvé !\`);
285
+ throw new Error('${entityName} non trouvé');
286
+ }
274
287
  await this.${entityNameLower}Repository.delete(id);
275
288
  this.logger.log('Suppression réussie');
276
289
  } catch (error) {
@@ -490,7 +503,7 @@ import { ${entityNameCapitalized}Mapper } from '${entityPath}/domain/mappers/${e
490
503
  ${providersBlock.join(",\n ")}
491
504
  ],
492
505
  exports: [
493
- ${entityNameCapitalized}Service
506
+ ${entityNameCapitalized}Service, 'I${entityNameCapitalized}Repository'
494
507
  ]
495
508
  })
496
509
  export class ${entityNameCapitalized}Module {}
@@ -499,7 +512,8 @@ export class ${entityNameCapitalized}Module {}
499
512
 
500
513
  await safeUpdateAppModule(entityNameLower);
501
514
  }
502
- await generateMiddlewares();
515
+
516
+ await generateMiddlewares(dbConfig.orm);
503
517
 
504
518
  // modification de AppModule
505
519
  const appModulePath = "src/app.module.ts";
@@ -21,7 +21,7 @@ async function createProject(inputs) {
21
21
  // installation des dependances
22
22
  logInfo("Installation des dépendances...");
23
23
  await runCommand(
24
- `${inputs.packageManager} add @nestjs/config @nestjs/typeorm typeorm pg class-validator class-transformer`,
24
+ `${inputs.packageManager} add @nestjs/config class-validator class-transformer`,
25
25
  "Échec de l'installation des dépendances"
26
26
  );
27
27
  }
@@ -12,11 +12,11 @@ async function setupAuth(inputs) {
12
12
  const useSwagger = inputs.useSwagger;
13
13
 
14
14
  await runCommand(
15
- `npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt`,
15
+ `npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt uuid`,
16
16
  "Échec de l'installation des dépendances d'authentification"
17
17
  );
18
18
  await runCommand(
19
- `npm install -D @types/passport-jwt @types/bcrypt`,
19
+ `npm install -D @types/passport-jwt @types/bcrypt @types/uuid`,
20
20
  "Échec de l'installation des dépendances de dev"
21
21
  );
22
22
 
@@ -41,12 +41,11 @@ async function setupAuth(inputs) {
41
41
  if (dbConfig.orm === "typeorm") {
42
42
  ormImports = `import { TypeOrmModule } from '@nestjs/typeorm';
43
43
  import { User } from 'src/entities/User.entity';`;
44
- ormModuleImport = `TypeOrmModule.forFeature([User]),`;
45
- prismaProvider = "PrismaService,";
44
+ ormModuleImport = `TypeOrmModule.forFeature([User])`;
46
45
  } else if (dbConfig.orm === "mongoose") {
47
46
  ormImports = `import { MongooseModule } from '@nestjs/mongoose';
48
47
  import { User, UserSchema } from 'src/user/domain/entities/user.schema';`;
49
- ormModuleImport = `MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),`;
48
+ ormModuleImport = `MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])`;
50
49
  prismaProvider = ""; // Ne pas ajouter PrismaService
51
50
  } else if (dbConfig.orm === "prisma") {
52
51
  ormImports = "";
@@ -58,9 +57,9 @@ import { User, UserSchema } from 'src/user/domain/entities/user.schema';`;
58
57
  path: `${authPath}/auth.module.ts`,
59
58
  contente: `
60
59
  import { Module } from '@nestjs/common';
61
- import { JwtModule } from '@nestjs/jwt';
60
+ import { JwtModule, JwtService } from '@nestjs/jwt';
62
61
  import { PassportModule } from '@nestjs/passport';
63
- import { UserMapper } from 'src/user/domain/mappers/user.mapper';
62
+ // import { UserMapper } from 'src/user/domain/mappers/user.mapper';
64
63
  ${ormImports}
65
64
  import { AuthService } from '${authPaths.authServicesPath}/auth.service';
66
65
  ${
@@ -71,13 +70,14 @@ ${
71
70
  import { AuthController } from '${
72
71
  authPaths.authControllersPath
73
72
  }/auth.controller';
73
+ import { UserModule } from 'src/user/user.module';
74
74
  import { JwtStrategy } from '${authPaths.authStrategyPath}/jwt.strategy';
75
75
  import { AuthGuard } from '${authPaths.authGuardsPath}/auth.guard';
76
- import { UserRepository } from 'src/user/infrastructure/repositories/user.repository';
77
76
 
78
77
  @Module({
79
78
  imports: [
80
- ${ormModuleImport}
79
+ UserModule,
80
+ ${ormModuleImport},
81
81
  PassportModule,
82
82
  JwtModule.register({ secret: 'your-secret-key', signOptions: { expiresIn: '1h' } }),
83
83
  ],
@@ -86,7 +86,8 @@ import { UserRepository } from 'src/user/infrastructure/repositories/user.reposi
86
86
  ${prismaProvider}
87
87
  AuthService,
88
88
  JwtStrategy,
89
- AuthGuard
89
+ AuthGuard,
90
+ JwtService
90
91
  ],
91
92
  exports: [AuthService],
92
93
  })
@@ -34,7 +34,7 @@ MONGO_DB=${inputs.dbConfig.MONGO_DB}
34
34
  pattern: importsPattern,
35
35
  replacement: (match) =>
36
36
  `${match}
37
- MongooseModule.forRoot(process.env.MONGO_URI, {
37
+ MongooseModule.forRoot(process.env.MONGO_URI || " ", {
38
38
  dbName: process.env.MONGO_DB,
39
39
  }),`,
40
40
  });
@@ -110,29 +110,117 @@ model ${entity.name} {
110
110
  new RegExp(`model ${from} \\{`),
111
111
  (match) => {
112
112
  if (type === "1-n") {
113
- return `${match}\n ${to}s ${to}[]`;
114
- } else if (type === "1-1") {
115
- /* return `${match}\n ${to} ${to}? @relation(fields: [${to}Id], references: [id])\n ${to}Id String?`;
116
- */
117
- return `${match}\n ${to} ${to}? @relation(fields: [${to}Id], references: [id])\n ${to}Id String? @unique`;
118
- } else if (type === "n-n") {
119
- return `${match}\n ${to}s ${to}[]`;
113
+ // Côté "one" (source) : ajoute la liste
114
+ schemaContent = schemaContent.replace(
115
+ new RegExp(`model ${from} {([\\s\\S]*?)}`),
116
+ (match) => {
117
+ const fieldLine = `${to}s ${to}[]`;
118
+ return match.includes(fieldLine)
119
+ ? match
120
+ : `${match}\n ${fieldLine}`;
121
+ }
122
+ );
123
+ // Côté "many" (cible) : ajoute la relation et la clé étrangère si absente
124
+ schemaContent = schemaContent.replace(
125
+ new RegExp(`model ${to} {([\\s\\S]*?)}`),
126
+ (match) => {
127
+ const relationLine = `${from} ${from} @relation(fields: [${from}Id], references: [id])`;
128
+ const fkLine = `${from}Id String`;
129
+ let result = match.includes(relationLine)
130
+ ? match
131
+ : `${match}\n ${relationLine}`;
132
+ result = result.includes(fkLine)
133
+ ? result
134
+ : `${result}\n ${fkLine}`;
135
+ return result;
136
+ }
137
+ );
120
138
  }
121
- return match;
122
- }
123
- );
124
139
 
125
- // Mise à jour du modèle cible
126
- schemaContent = schemaContent.replace(
127
- new RegExp(`model ${to} \\{`),
128
- (match) => {
129
- if (type === "1-n") {
130
- return `${match}\n ${from} ${from} @relation(fields: [${from}Id], references: [id])\n ${from}Id String`;
131
- } else if (type === "1-1") {
132
- return `${match}\n ${from} ${from}?`;
133
- } else if (type === "n-n") {
134
- return `${match}\n ${from}s ${from}[]`;
140
+ if (type === "n-1") {
141
+ // Côté "many" (source) : ajoute la relation et la clé étrangère si absente
142
+ schemaContent = schemaContent.replace(
143
+ new RegExp(`model ${from} {([\\s\\S]*?)}`),
144
+ (match) => {
145
+ const relationLine = `${to} ${to} @relation(fields: [${to}Id], references: [id])`;
146
+ const fkLine = `${to}Id String`;
147
+ let result = match.includes(relationLine)
148
+ ? match
149
+ : `${match}\n ${relationLine}`;
150
+ result = result.includes(fkLine)
151
+ ? result
152
+ : `${result}\n ${fkLine}`;
153
+ return result;
154
+ }
155
+ );
156
+ // Côté "one" (cible) : ajoute la liste
157
+ schemaContent = schemaContent.replace(
158
+ new RegExp(`model ${to} {([\\s\\S]*?)}`),
159
+ (match) => {
160
+ const fieldLine = `${from}s ${from}[]`;
161
+ return match.includes(fieldLine)
162
+ ? match
163
+ : `${match}\n ${fieldLine}`;
164
+ }
165
+ );
135
166
  }
167
+
168
+ if (type === "1-1") {
169
+ // Côté A
170
+ schemaContent = schemaContent.replace(
171
+ new RegExp(`model ${from} {([\\s\\S]*?)}`),
172
+ (match) => {
173
+ const relationLine = `${to} ${to} @relation(fields: [${to}Id], references: [id])`;
174
+ const fkLine = `${to}Id String @unique`;
175
+ let result = match.includes(relationLine)
176
+ ? match
177
+ : `${match}\n ${relationLine}`;
178
+ result = result.includes(fkLine)
179
+ ? result
180
+ : `${result}\n ${fkLine}`;
181
+ return result;
182
+ }
183
+ );
184
+ // Côté B
185
+ schemaContent = schemaContent.replace(
186
+ new RegExp(`model ${to} {([\\s\\S]*?)}`),
187
+ (match) => {
188
+ const relationLine = `${from} ${from}? @relation(fields: [${from}Id], references: [id])`;
189
+ const fkLine = `${from}Id String? @unique`;
190
+ let result = match.includes(relationLine)
191
+ ? match
192
+ : `${match}\n ${relationLine}`;
193
+ result = result.includes(fkLine)
194
+ ? result
195
+ : `${result}\n ${fkLine}`;
196
+ return result;
197
+ }
198
+ );
199
+ }
200
+
201
+ if (type === "n-n" || type === "m-n") {
202
+ // Pour n-n, généralement, il faut créer une table de jointure à la main.
203
+ // Ici, on ajoute juste les listes de chaque côté si absentes.
204
+ schemaContent = schemaContent.replace(
205
+ new RegExp(`model ${from} {([\\s\\S]*?)}`),
206
+ (match) => {
207
+ const fieldLine = `${to}s ${to}[]`;
208
+ return match.includes(fieldLine)
209
+ ? match
210
+ : `${match}\n ${fieldLine}`;
211
+ }
212
+ );
213
+ schemaContent = schemaContent.replace(
214
+ new RegExp(`model ${to} {([\\s\\S]*?)}`),
215
+ (match) => {
216
+ const fieldLine = `${from}s ${from}[]`;
217
+ return match.includes(fieldLine)
218
+ ? match
219
+ : `${match}\n ${fieldLine}`;
220
+ }
221
+ );
222
+ }
223
+
136
224
  return match;
137
225
  }
138
226
  );
@@ -244,6 +244,33 @@ export async function getUserInputs2() {
244
244
  type: relType,
245
245
  });
246
246
 
247
+ if (relType === "1-1") {
248
+ from.fields.push({
249
+ name: `${to.name.toLowerCase()}Id`,
250
+ type: "string",
251
+ });
252
+ to.fields.push({
253
+ name: `${from.name.toLowerCase()}Id`,
254
+ type: "string",
255
+ });
256
+ } else if (relType === "1-n") {
257
+ to.fields.push({
258
+ name: `${from.name.toLowerCase()}Id`,
259
+ type: "string",
260
+ }); // ex: video.userId
261
+ // pas de champ inverse dans from (user)
262
+ } else if (relType === "n-n") {
263
+ // pour n-n tu peux gérer ça plus tard avec une table pivot
264
+ from.fields.push({
265
+ name: `${to.name.toLowerCase()}Ids`,
266
+ type: "string[]",
267
+ });
268
+ to.fields.push({
269
+ name: `${from.name.toLowerCase()}Ids`,
270
+ type: "string[]",
271
+ });
272
+ }
273
+
247
274
  const again = readline.keyInYNStrict("Ajouter une autre relation ?");
248
275
  if (!again) break;
249
276
  }
package/utils/utils.js CHANGED
@@ -198,6 +198,9 @@ export async function generateDto(entity, useSwagger, isAuthDto = false) {
198
198
  if (fieldName.includes("discount")) {
199
199
  return 10;
200
200
  }
201
+ if (fieldName.includes("Id")) {
202
+ return "UID-exemple-4f06-bd56-f4021b541c57";
203
+ }
201
204
 
202
205
  // 🛠 Sinon fallback basique selon le type
203
206
  switch (f.type) {
@@ -249,6 +252,11 @@ ${
249
252
  ? "import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';"
250
253
  : ""
251
254
  }
255
+ ${
256
+ entityName == "User"
257
+ ? "import { Role } from 'src/user/domain/enums/role.enum';"
258
+ : ""
259
+ }
252
260
  `;
253
261
 
254
262
  // ✅ Cas AUTH DTO : une seule classe + nom déjà donné (pas de Create/Update)
@@ -266,8 +274,6 @@ ${dtoFields}
266
274
  return `
267
275
  ${commonImports}
268
276
 
269
- // Vous pouvez décommenter ce champ si vous voulez gérer le rôle de l'utilisateur.
270
- import { Role } from 'src/user/domain/enums/role.enum';
271
277
 
272
278
  export class Create${entityName}Dto {
273
279
  ${dtoFields}
@@ -382,7 +388,7 @@ export class ${entityNameCapitalized}Controller {
382
388
  `;
383
389
  }
384
390
 
385
- export async function generateMiddlewares() {
391
+ export async function generateMiddlewares(orm = "global") {
386
392
  logInfo(
387
393
  "\u2728 G\u00e9n\u00e9ration des middlewares, interceptors, guards et filters personnalis\u00e9s..."
388
394
  );
@@ -409,8 +415,13 @@ export function LoggerMiddleware(
409
415
  `,
410
416
  });
411
417
 
412
- // Error Handling Filter
418
+ // Error Handling Filter (personnalisé selon l'ORM)
413
419
  await createFile({
420
+ path: `${basePath}/filters/all-exceptions.filter.ts`,
421
+ contente: getExceptionFilterContent(orm),
422
+ });
423
+ // Error Handling Filter
424
+ /* await createFile({
414
425
  path: `${basePath}/filters/all-exceptions.filter.ts`,
415
426
  contente: `import {
416
427
  ExceptionFilter,
@@ -418,29 +429,118 @@ export function LoggerMiddleware(
418
429
  ArgumentsHost,
419
430
  HttpException,
420
431
  HttpStatus,
432
+ Logger,
421
433
  } from '@nestjs/common';
422
434
  import { Request, Response } from 'express';
423
435
 
424
436
  @Catch()
425
437
  export class AllExceptionsFilter implements ExceptionFilter {
438
+ private readonly logger = new Logger(AllExceptionsFilter.name);
439
+
426
440
  catch(exception: unknown, host: ArgumentsHost) {
427
441
  const ctx = host.switchToHttp();
428
442
  const response = ctx.getResponse<Response>();
429
443
  const request = ctx.getRequest<Request>();
430
- const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
431
444
 
432
- const message = exception instanceof HttpException ? exception.getResponse() : 'Internal server error';
433
- console.log('exception: ', exception);
445
+ let status = HttpStatus.INTERNAL_SERVER_ERROR;
446
+ let message: string | string[] = 'Internal server error';
447
+ let errorDetails: any = null;
448
+
449
+ // Handle NestJS HttpExceptions (BadRequest, NotFound, etc.)
450
+ if (exception instanceof HttpException) {
451
+ status = exception.getStatus();
452
+ const res = exception.getResponse();
453
+
454
+ if (typeof res === 'string') {
455
+ message = res;
456
+ } else if (typeof res === 'object' && res !== null) {
457
+ const resObj = res as any;
458
+ message = resObj.message || resObj.error || 'HttpException';
459
+ errorDetails = resObj;
460
+ }
461
+ }
462
+
463
+ // Handle Prisma errors dynamiquement (sans import bloquant)
464
+ else if (
465
+ typeof exception === 'object' &&
466
+ exception &&
467
+ exception.constructor &&
468
+ (
469
+ exception.constructor.name === 'PrismaClientKnownRequestError' ||
470
+ exception.constructor.name === 'PrismaClientValidationError'
471
+ )
472
+ ) {
473
+ status = HttpStatus.BAD_REQUEST;
474
+ message = (exception as any).message || 'Prisma error';
475
+ errorDetails = exception;
476
+ }
477
+
478
+ // Handle Mongoose/Mongo errors dynamiquement
479
+ else if (
480
+ typeof exception === 'object' &&
481
+ exception &&
482
+ 'name' in exception &&
483
+ (
484
+ (exception as any).name === 'MongoError' ||
485
+ (exception as any).name === 'MongooseError'
486
+ )
487
+ ) {
488
+ status = HttpStatus.BAD_REQUEST;
489
+ message = (exception as any).message || 'MongoDB error';
490
+ errorDetails = exception;
491
+ }
492
+
493
+ // Handle Sequelize errors dynamiquement
494
+ else if (
495
+ typeof exception === 'object' &&
496
+ exception &&
497
+ exception.constructor &&
498
+ (
499
+ exception.constructor.name === 'SequelizeDatabaseError' ||
500
+ exception.constructor.name === 'SequelizeValidationError'
501
+ )
502
+ ) {
503
+ status = HttpStatus.BAD_REQUEST;
504
+ message = (exception as any).message || 'Sequelize error';
505
+ errorDetails = exception;
506
+ }
507
+
508
+ // Handle unknown errors
509
+ else if (exception instanceof Error) {
510
+ message = exception.message;
511
+ errorDetails = {
512
+ name: exception.name,
513
+ stack: exception.stack,
514
+ };
515
+ } else {
516
+ message = 'Une erreur inattendue est survenue';
517
+ errorDetails = exception;
518
+ }
519
+
520
+ // Log the full exception on the server
521
+ this.logger.error(
522
+ \`Exception on \${request.method} \${request.url}\`,
523
+ JSON.stringify({
524
+ message,
525
+ status,
526
+ errorDetails,
527
+ exception,
528
+ }),
529
+ );
434
530
 
531
+ // Send clean error to client
435
532
  response.status(status).json({
436
533
  statusCode: status,
437
534
  timestamp: new Date().toISOString(),
438
535
  path: request.url,
439
- message: message,
536
+ method: request.method,
537
+ message,
538
+ error: errorDetails,
440
539
  });
441
540
  }
442
- }`,
443
- });
541
+ }
542
+ `,
543
+ }); */
444
544
 
445
545
  // Response Interceptor
446
546
  await createFile({
@@ -521,18 +621,19 @@ export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
521
621
 
522
622
  // ✅ 1. Mise à jour des imports
523
623
  const importPattern = "import { AppModule } from './app.module';";
524
- const importReplacer = `import { AppModule } from 'src/app.module';
525
- import { AllExceptionsFilter } from 'src/common/filters/all-exceptions.filter';
526
- import { ResponseInterceptor } from 'src/common/interceptors/response.interceptor';
527
- import { LoggerMiddleware } from 'src/common/middlewares/logger.middleware';
624
+ const importReplacer = `import { AppModule } from 'src/app.module'
625
+ import { AllExceptionsFilter } from 'src/common/filters/all-exceptions.filter'
626
+ import { ResponseInterceptor } from 'src/common/interceptors/response.interceptor'
627
+ import { LoggerMiddleware } from 'src/common/middlewares/logger.middleware'
528
628
  import { ValidationPipe } from '@nestjs/common';`;
529
629
 
530
630
  // ✅ 2. Injection dans le contenu de bootstrap()
531
631
  const contentPattern = `const app = await NestFactory.create(AppModule);`;
532
- const contentReplacer = `const app = await NestFactory.create(AppModule);
632
+
633
+ const contentReplacer = `
533
634
 
534
635
  // 🔒 Global filter pour gérer toutes les exceptions
535
- app.useGlobalFilters(new AllExceptionsFilter());
636
+ app.useGlobalFilters(new AllExceptionsFilter())
536
637
 
537
638
  // 🔁 Global interceptor pour structurer les réponses
538
639
  // app.useGlobalInterceptors(new ResponseInterceptor()); //deja appliquer dans le app.module.ts par convention (ne choisir que l'un des deux)
@@ -717,7 +818,7 @@ export class ${entityNameCapitalized}Repository implements I${entityNameCapitali
717
818
  contente: `import { Injectable, NotFoundException } from '@nestjs/common';
718
819
  import { InjectModel } from '@nestjs/mongoose';
719
820
  import { Model } from 'mongoose';
720
- import { UserEntity } from '${entityPath}/domain/entities/${entityNameLower}.entity';
821
+ import { ${entityNameCapitalized}Entity } from '${entityPath}/domain/entities/${entityNameLower}.entity';
721
822
  import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
722
823
  import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
723
824
  import { ${entityNameCapitalized} } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.schema';
@@ -882,3 +983,417 @@ function getFormattedType(field) {
882
983
  }
883
984
  return formatType(field.type);
884
985
  }
986
+
987
+ // Génère le contenu du filtre d'exception selon l'ORM
988
+ function getExceptionFilterContent(orm) {
989
+ if (orm === "prisma") {
990
+ return `
991
+ import {
992
+ ExceptionFilter,
993
+ Catch,
994
+ ArgumentsHost,
995
+ HttpException,
996
+ HttpStatus,
997
+ Logger,
998
+ } from '@nestjs/common';
999
+ import { Request, Response } from 'express';
1000
+
1001
+ @Catch()
1002
+ export class AllExceptionsFilter implements ExceptionFilter {
1003
+ private readonly logger = new Logger(AllExceptionsFilter.name);
1004
+
1005
+ catch(exception: unknown, host: ArgumentsHost) {
1006
+ const ctx = host.switchToHttp();
1007
+ const response = ctx.getResponse<Response>();
1008
+ const request = ctx.getRequest<Request>();
1009
+
1010
+ let status = HttpStatus.INTERNAL_SERVER_ERROR;
1011
+ let message: string | string[] = 'Internal server error';
1012
+ let errorDetails: any = null;
1013
+
1014
+ if (exception instanceof HttpException) {
1015
+ status = exception.getStatus();
1016
+ const res = exception.getResponse();
1017
+ if (typeof res === 'string') {
1018
+ message = res;
1019
+ } else if (typeof res === 'object' && res !== null) {
1020
+ const resObj = res as any;
1021
+ message = resObj.message || resObj.error || 'HttpException';
1022
+ errorDetails = resObj;
1023
+ }
1024
+ } else if (
1025
+ typeof exception === 'object' &&
1026
+ exception &&
1027
+ exception.constructor &&
1028
+ (
1029
+ exception.constructor.name === 'PrismaClientKnownRequestError' ||
1030
+ exception.constructor.name === 'PrismaClientValidationError'
1031
+ )
1032
+ ) {
1033
+ status = HttpStatus.BAD_REQUEST;
1034
+ message = (exception as any).message || 'Prisma error';
1035
+ errorDetails = exception;
1036
+ } else if (exception instanceof Error) {
1037
+ message = exception.message;
1038
+ errorDetails = {
1039
+ name: exception.name,
1040
+ stack: exception.stack,
1041
+ };
1042
+ } else {
1043
+ message = 'Une erreur inattendue est survenue';
1044
+ errorDetails = exception;
1045
+ }
1046
+
1047
+ this.logger.error(
1048
+ \`Exception on \${request.method} \${request.url}\`,
1049
+ JSON.stringify({ message, status, errorDetails, exception }),
1050
+ );
1051
+
1052
+ response.status(status).json({
1053
+ statusCode: status,
1054
+ timestamp: new Date().toISOString(),
1055
+ path: request.url,
1056
+ method: request.method,
1057
+ message,
1058
+ error: errorDetails,
1059
+ });
1060
+ }
1061
+ }
1062
+ `.trim();
1063
+ }
1064
+
1065
+ if (orm === "mongoose") {
1066
+ return `
1067
+ import {
1068
+ ExceptionFilter,
1069
+ Catch,
1070
+ ArgumentsHost,
1071
+ HttpException,
1072
+ HttpStatus,
1073
+ Logger,
1074
+ } from '@nestjs/common';
1075
+ import { Request, Response } from 'express';
1076
+
1077
+ @Catch()
1078
+ export class AllExceptionsFilter implements ExceptionFilter {
1079
+ private readonly logger = new Logger(AllExceptionsFilter.name);
1080
+
1081
+ catch(exception: unknown, host: ArgumentsHost) {
1082
+ const ctx = host.switchToHttp();
1083
+ const response = ctx.getResponse<Response>();
1084
+ const request = ctx.getRequest<Request>();
1085
+
1086
+ let status = HttpStatus.INTERNAL_SERVER_ERROR;
1087
+ let message: string | string[] = 'Internal server error';
1088
+ let errorDetails: any = null;
1089
+
1090
+ if (exception instanceof HttpException) {
1091
+ status = exception.getStatus();
1092
+ const res = exception.getResponse();
1093
+ if (typeof res === 'string') {
1094
+ message = res;
1095
+ } else if (typeof res === 'object' && res !== null) {
1096
+ const resObj = res as any;
1097
+ message = resObj.message || resObj.error || 'HttpException';
1098
+ errorDetails = resObj;
1099
+ }
1100
+ } else if (
1101
+ typeof exception === 'object' &&
1102
+ exception &&
1103
+ 'name' in exception &&
1104
+ (
1105
+ (exception as any).name === 'MongoError' ||
1106
+ (exception as any).name === 'MongooseError'
1107
+ )
1108
+ ) {
1109
+ status = HttpStatus.BAD_REQUEST;
1110
+ message = (exception as any).message || 'MongoDB error';
1111
+ errorDetails = exception;
1112
+ } else if (exception instanceof Error) {
1113
+ message = exception.message;
1114
+ errorDetails = {
1115
+ name: exception.name,
1116
+ stack: exception.stack,
1117
+ };
1118
+ } else {
1119
+ message = 'Une erreur inattendue est survenue';
1120
+ errorDetails = exception;
1121
+ }
1122
+
1123
+ this.logger.error(
1124
+ \`Exception on \${request.method} \${request.url}\`,
1125
+ JSON.stringify({ message, status, errorDetails, exception }),
1126
+ );
1127
+
1128
+ response.status(status).json({
1129
+ statusCode: status,
1130
+ timestamp: new Date().toISOString(),
1131
+ path: request.url,
1132
+ method: request.method,
1133
+ message,
1134
+ error: errorDetails,
1135
+ });
1136
+ }
1137
+ }
1138
+ `.trim();
1139
+ }
1140
+
1141
+ if (orm === "typeorm") {
1142
+ return `
1143
+ import {
1144
+ ExceptionFilter,
1145
+ Catch,
1146
+ ArgumentsHost,
1147
+ HttpException,
1148
+ HttpStatus,
1149
+ Logger,
1150
+ } from '@nestjs/common';
1151
+ import { Request, Response } from 'express';
1152
+
1153
+ @Catch()
1154
+ export class AllExceptionsFilter implements ExceptionFilter {
1155
+ private readonly logger = new Logger(AllExceptionsFilter.name);
1156
+
1157
+ catch(exception: unknown, host: ArgumentsHost) {
1158
+ const ctx = host.switchToHttp();
1159
+ const response = ctx.getResponse<Response>();
1160
+ const request = ctx.getRequest<Request>();
1161
+
1162
+ let status = HttpStatus.INTERNAL_SERVER_ERROR;
1163
+ let message: string | string[] = 'Internal server error';
1164
+ let errorDetails: any = null;
1165
+
1166
+ if (exception instanceof HttpException) {
1167
+ status = exception.getStatus();
1168
+ const res = exception.getResponse();
1169
+ if (typeof res === 'string') {
1170
+ message = res;
1171
+ } else if (typeof res === 'object' && res !== null) {
1172
+ const resObj = res as any;
1173
+ message = resObj.message || resObj.error || 'HttpException';
1174
+ errorDetails = resObj;
1175
+ }
1176
+ } else if (
1177
+ typeof exception === 'object' &&
1178
+ exception &&
1179
+ 'name' in exception &&
1180
+ (
1181
+ (exception as any).name === 'QueryFailedError' ||
1182
+ (exception as any).name === 'EntityNotFoundError' ||
1183
+ (exception as any).name === 'CannotCreateEntityIdMapError'
1184
+ )
1185
+ ) {
1186
+ status = HttpStatus.BAD_REQUEST;
1187
+ message = (exception as any).message || 'TypeORM error';
1188
+ errorDetails = exception;
1189
+ } else if (exception instanceof Error) {
1190
+ message = exception.message;
1191
+ errorDetails = {
1192
+ name: exception.name,
1193
+ stack: exception.stack,
1194
+ };
1195
+ } else {
1196
+ message = 'Une erreur inattendue est survenue';
1197
+ errorDetails = exception;
1198
+ }
1199
+
1200
+ this.logger.error(
1201
+ \`Exception on \${request.method} \${request.url}\`,
1202
+ JSON.stringify({ message, status, errorDetails, exception }),
1203
+ );
1204
+
1205
+ response.status(status).json({
1206
+ statusCode: status,
1207
+ timestamp: new Date().toISOString(),
1208
+ path: request.url,
1209
+ method: request.method,
1210
+ message,
1211
+ error: errorDetails,
1212
+ });
1213
+ }
1214
+ }
1215
+ `.trim();
1216
+ }
1217
+
1218
+ if (orm === "sequelize") {
1219
+ return `
1220
+ import {
1221
+ ExceptionFilter,
1222
+ Catch,
1223
+ ArgumentsHost,
1224
+ HttpException,
1225
+ HttpStatus,
1226
+ Logger,
1227
+ } from '@nestjs/common';
1228
+ import { Request, Response } from 'express';
1229
+
1230
+ @Catch()
1231
+ export class AllExceptionsFilter implements ExceptionFilter {
1232
+ private readonly logger = new Logger(AllExceptionsFilter.name);
1233
+
1234
+ catch(exception: unknown, host: ArgumentsHost) {
1235
+ const ctx = host.switchToHttp();
1236
+ const response = ctx.getResponse<Response>();
1237
+ const request = ctx.getRequest<Request>();
1238
+
1239
+ let status = HttpStatus.INTERNAL_SERVER_ERROR;
1240
+ let message: string | string[] = 'Internal server error';
1241
+ let errorDetails: any = null;
1242
+
1243
+ if (exception instanceof HttpException) {
1244
+ status = exception.getStatus();
1245
+ const res = exception.getResponse();
1246
+ if (typeof res === 'string') {
1247
+ message = res;
1248
+ } else if (typeof res === 'object' && res !== null) {
1249
+ const resObj = res as any;
1250
+ message = resObj.message || resObj.error || 'HttpException';
1251
+ errorDetails = resObj;
1252
+ }
1253
+ } else if (
1254
+ typeof exception === 'object' &&
1255
+ exception &&
1256
+ exception.constructor &&
1257
+ (
1258
+ exception.constructor.name === 'SequelizeDatabaseError' ||
1259
+ exception.constructor.name === 'SequelizeValidationError'
1260
+ )
1261
+ ) {
1262
+ status = HttpStatus.BAD_REQUEST;
1263
+ message = (exception as any).message || 'Sequelize error';
1264
+ errorDetails = exception;
1265
+ } else if (exception instanceof Error) {
1266
+ message = exception.message;
1267
+ errorDetails = {
1268
+ name: exception.name,
1269
+ stack: exception.stack,
1270
+ };
1271
+ } else {
1272
+ message = 'Une erreur inattendue est survenue';
1273
+ errorDetails = exception;
1274
+ }
1275
+
1276
+ this.logger.error(
1277
+ \`Exception on \${request.method} \${request.url}\`,
1278
+ JSON.stringify({ message, status, errorDetails, exception }),
1279
+ );
1280
+
1281
+ response.status(status).json({
1282
+ statusCode: status,
1283
+ timestamp: new Date().toISOString(),
1284
+ path: request.url,
1285
+ method: request.method,
1286
+ message,
1287
+ error: errorDetails,
1288
+ });
1289
+ }
1290
+ }
1291
+ `.trim();
1292
+ }
1293
+
1294
+ // Version universelle (multi-ORM)
1295
+ return `
1296
+ import {
1297
+ ExceptionFilter,
1298
+ Catch,
1299
+ ArgumentsHost,
1300
+ HttpException,
1301
+ HttpStatus,
1302
+ Logger,
1303
+ } from '@nestjs/common';
1304
+ import { Request, Response } from 'express';
1305
+
1306
+ @Catch()
1307
+ export class AllExceptionsFilter implements ExceptionFilter {
1308
+ private readonly logger = new Logger(AllExceptionsFilter.name);
1309
+
1310
+ catch(exception: unknown, host: ArgumentsHost) {
1311
+ const ctx = host.switchToHttp();
1312
+ const response = ctx.getResponse<Response>();
1313
+ const request = ctx.getRequest<Request>();
1314
+
1315
+ let status = HttpStatus.INTERNAL_SERVER_ERROR;
1316
+ let message: string | string[] = 'Internal server error';
1317
+ let errorDetails: any = null;
1318
+
1319
+ if (exception instanceof HttpException) {
1320
+ status = exception.getStatus();
1321
+ const res = exception.getResponse();
1322
+ if (typeof res === 'string') {
1323
+ message = res;
1324
+ } else if (typeof res === 'object' && res !== null) {
1325
+ const resObj = res as any;
1326
+ message = resObj.message || resObj.error || 'HttpException';
1327
+ errorDetails = resObj;
1328
+ }
1329
+ }
1330
+ // Prisma
1331
+ else if (
1332
+ typeof exception === 'object' &&
1333
+ exception &&
1334
+ exception.constructor &&
1335
+ (
1336
+ exception.constructor.name === 'PrismaClientKnownRequestError' ||
1337
+ exception.constructor.name === 'PrismaClientValidationError'
1338
+ )
1339
+ ) {
1340
+ status = HttpStatus.BAD_REQUEST;
1341
+ message = (exception as any).message || 'Prisma error';
1342
+ errorDetails = exception;
1343
+ }
1344
+ // Mongoose/Mongo
1345
+ else if (
1346
+ typeof exception === 'object' &&
1347
+ exception &&
1348
+ 'name' in exception &&
1349
+ (
1350
+ (exception as any).name === 'MongoError' ||
1351
+ (exception as any).name === 'MongooseError'
1352
+ )
1353
+ ) {
1354
+ status = HttpStatus.BAD_REQUEST;
1355
+ message = (exception as any).message || 'MongoDB error';
1356
+ errorDetails = exception;
1357
+ }
1358
+ // Sequelize
1359
+ else if (
1360
+ typeof exception === 'object' &&
1361
+ exception &&
1362
+ exception.constructor &&
1363
+ (
1364
+ exception.constructor.name === 'SequelizeDatabaseError' ||
1365
+ exception.constructor.name === 'SequelizeValidationError'
1366
+ )
1367
+ ) {
1368
+ status = HttpStatus.BAD_REQUEST;
1369
+ message = (exception as any).message || 'Sequelize error';
1370
+ errorDetails = exception;
1371
+ }
1372
+ else if (exception instanceof Error) {
1373
+ message = exception.message;
1374
+ errorDetails = {
1375
+ name: exception.name,
1376
+ stack: exception.stack,
1377
+ };
1378
+ } else {
1379
+ message = 'Une erreur inattendue est survenue';
1380
+ errorDetails = exception;
1381
+ }
1382
+
1383
+ this.logger.error(
1384
+ \`Exception on \${request.method} \${request.url}\`,
1385
+ JSON.stringify({ message, status, errorDetails, exception }),
1386
+ );
1387
+
1388
+ response.status(status).json({
1389
+ statusCode: status,
1390
+ timestamp: new Date().toISOString(),
1391
+ path: request.url,
1392
+ method: request.method,
1393
+ message,
1394
+ error: errorDetails,
1395
+ });
1396
+ }
1397
+ }
1398
+ `.trim();
1399
+ }