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 +1 -1
- package/setup.js +7 -2
- package/unutils.txt +77 -0
- package/utils/configs/setupCleanArchitecture.js +42 -128
- package/utils/setups/setupAuth.js +199 -82
- package/utils/setups/setupDatabase.js +1 -1
- package/utils/setups/setupLogger.js +53 -0
- package/utils/setups/setupSwagger.js +6 -5
- package/utils/utils.js +304 -34
package/package.json
CHANGED
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)
|
|
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
|
|
456
|
-
let
|
|
457
|
-
let
|
|
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
|
-
|
|
463
|
-
|
|
393
|
+
extraImports = `import { PrismaService } from 'src/prisma/prisma.service';`;
|
|
394
|
+
providersBlock.push("PrismaService");
|
|
464
395
|
} else if (dbConfig.orm === "typeorm") {
|
|
465
|
-
|
|
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
|
-
* -
|
|
424
|
+
* - Repository
|
|
476
425
|
* - Use Cases
|
|
477
426
|
* - Mapper
|
|
478
427
|
*/
|
|
479
428
|
import { Module } from '@nestjs/common';
|
|
480
|
-
|
|
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
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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
|
-
${
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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('
|
|
116
|
+
@Inject('IUserRepository')
|
|
117
|
+
private readonly userRepository: IUserRepository,
|
|
79
118
|
) {}
|
|
80
119
|
|
|
81
|
-
//
|
|
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
|
-
// 🧾
|
|
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
|
-
// 🔑
|
|
104
|
-
async login(
|
|
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
|
-
// 🔁
|
|
121
|
-
async refreshToken(
|
|
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
|
-
// 🚪
|
|
138
|
-
async logout(
|
|
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
|
-
// 📲
|
|
145
|
-
async sendOtp(
|
|
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
|
|
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
|
-
// ✅
|
|
153
|
-
async verifyOtp(
|
|
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
|
-
//
|
|
163
|
-
async forgotPassword(
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
194
|
-
async getProfile(user:
|
|
195
|
-
const found = await this.userRepository.findById(user.
|
|
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
|
-
|
|
229
|
+
const email = found.getEmail();
|
|
230
|
+
return { email: email };
|
|
198
231
|
}
|
|
199
232
|
|
|
200
|
-
//
|
|
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
|
|
214
|
-
import { JwtAuthGuard } from
|
|
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:
|
|
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:
|
|
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:
|
|
232
|
-
return this.authService.refreshToken(body
|
|
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:
|
|
237
|
-
return this.authService.logout(body
|
|
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:
|
|
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:
|
|
247
|
-
return this.authService.verifyOtp(body
|
|
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:
|
|
252
|
-
return this.authService.forgotPassword(body
|
|
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:
|
|
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 {
|
|
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"`;
|
|
163
|
+
return `"${f.name}_example"`;
|
|
165
164
|
case "number":
|
|
166
|
-
return f.name === "price" ? 199.99 : 123;
|
|
165
|
+
return f.name === "price" ? 199.99 : 123;
|
|
167
166
|
case "boolean":
|
|
168
|
-
return true;
|
|
167
|
+
return true;
|
|
169
168
|
case "Date":
|
|
170
169
|
case "date":
|
|
171
|
-
return `"2024-01-01T00:00:00Z"`;
|
|
170
|
+
return `"2024-01-01T00:00:00Z"`;
|
|
172
171
|
case "role":
|
|
173
|
-
return `"user"`;
|
|
172
|
+
return `"user"`;
|
|
174
173
|
case "discount":
|
|
175
|
-
return 10;
|
|
174
|
+
return 10;
|
|
176
175
|
case "account":
|
|
177
|
-
return `"acc_${f.name}_12345"`;
|
|
176
|
+
return `"acc_${f.name}_12345"`;
|
|
178
177
|
case "amount":
|
|
179
|
-
return 500.75;
|
|
178
|
+
return 500.75;
|
|
179
|
+
case "email":
|
|
180
|
+
return "user@example.com";
|
|
180
181
|
default:
|
|
181
|
-
return `"sample_${f.name}"`;
|
|
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
|
|
195
|
-
?
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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":
|