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 +1 -1
- package/utils/configs/setupCleanArchitecture.js +21 -7
- package/utils/setups/projectSetup.js +1 -1
- package/utils/setups/setupAuth.js +11 -10
- package/utils/setups/setupMongoose.js +1 -1
- package/utils/setups/setupPrisma.js +108 -20
- package/utils/userInput.js +27 -0
- package/utils/utils.js +532 -17
package/package.json
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
);
|
package/utils/userInput.js
CHANGED
|
@@ -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
|
-
|
|
433
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
+
}
|