add-nest-auth 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +142 -19
- package/dist/cli.js.map +1 -1
- package/dist/generator/templates/dto/auth-response.dto.ts.hbs +29 -0
- package/dist/generator/templates/dto/change-password.dto.ts.hbs +22 -0
- package/dist/generator/templates/dto/create-user.dto.ts.hbs +22 -1
- package/dist/generator/templates/dto/forgot-password.dto.ts.hbs +13 -0
- package/dist/generator/templates/dto/login.dto.ts.hbs +9 -0
- package/dist/generator/templates/dto/register.dto.ts.hbs +20 -0
- package/dist/generator/templates/dto/reset-password.dto.ts.hbs +22 -0
- package/dist/generator/templates/entities/user.entity.typeorm.hbs +21 -0
- package/dist/generator/templates/jwt/auth.controller.ts.hbs +123 -2
- package/dist/generator/templates/jwt/auth.module.ts.hbs +8 -0
- package/dist/generator/templates/jwt/auth.service.ts.hbs +177 -4
- package/dist/generator/templates/prisma/prisma.module.ts.hbs +9 -0
- package/dist/generator/templates/prisma/prisma.service.ts.hbs +9 -0
- package/dist/generator/templates/prisma/schema.prisma.additions.hbs +40 -0
- package/dist/generator/templates/shared/env.hbs +4 -0
- package/dist/generator/templates/shared/env.template.hbs +4 -0
- package/dist/generator/templates/shared/main.ts.snippet.hbs +18 -0
- package/dist/generator/templates/tests/auth.controller.spec.ts.hbs +189 -0
- package/dist/generator/templates/tests/auth.service.spec.ts.hbs +330 -0
- package/dist/generator/templates/users/users.controller.ts.hbs +24 -0
- package/dist/generator/templates/users/users.module.ts.hbs +4 -0
- package/dist/generator/templates/users/users.service.ts.hbs +138 -39
- package/dist/index.js +142 -19
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/cli.js
CHANGED
|
@@ -292,6 +292,36 @@ async function promptConfig(detectedORM, detectedDB) {
|
|
|
292
292
|
message: "Enable rate limiting on auth endpoints? (recommended)",
|
|
293
293
|
default: true
|
|
294
294
|
},
|
|
295
|
+
{
|
|
296
|
+
type: "confirm",
|
|
297
|
+
name: "enableSwagger",
|
|
298
|
+
message: "Enable Swagger API documentation? (recommended)",
|
|
299
|
+
default: true
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
type: "confirm",
|
|
303
|
+
name: "generateTests",
|
|
304
|
+
message: "Generate unit tests? (recommended)",
|
|
305
|
+
default: true
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
type: "confirm",
|
|
309
|
+
name: "useUsername",
|
|
310
|
+
message: "Add username field to user?",
|
|
311
|
+
default: false
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
type: "confirm",
|
|
315
|
+
name: "enableEmailVerification",
|
|
316
|
+
message: "Enable email verification?",
|
|
317
|
+
default: false
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
type: "confirm",
|
|
321
|
+
name: "enableResetPassword",
|
|
322
|
+
message: "Enable forgot/reset password?",
|
|
323
|
+
default: true
|
|
324
|
+
},
|
|
295
325
|
{
|
|
296
326
|
type: "confirm",
|
|
297
327
|
name: "useDetectedORM",
|
|
@@ -330,6 +360,11 @@ function getDefaultAnswers(detectedORM, detectedDB) {
|
|
|
330
360
|
accessExpiration: "1h",
|
|
331
361
|
refreshExpiration: "7d",
|
|
332
362
|
enableRateLimiting: true,
|
|
363
|
+
enableSwagger: true,
|
|
364
|
+
generateTests: true,
|
|
365
|
+
useUsername: false,
|
|
366
|
+
enableEmailVerification: false,
|
|
367
|
+
enableResetPassword: true,
|
|
333
368
|
useDetectedORM: true,
|
|
334
369
|
database: detectedDB || "postgres",
|
|
335
370
|
autoInstall: true
|
|
@@ -344,11 +379,16 @@ function buildConfig(answers, projectName, sourceRoot, detectedORM, detectedDB)
|
|
|
344
379
|
enabled: answers.enableRBAC,
|
|
345
380
|
roles: answers.roles || []
|
|
346
381
|
},
|
|
347
|
-
orm: answers.useDetectedORM !== false ? detectedORM : "
|
|
382
|
+
orm: answers.useDetectedORM !== false ? detectedORM : "none",
|
|
348
383
|
database: answers.database || detectedDB || "postgres",
|
|
349
384
|
features: {
|
|
350
385
|
refreshTokens: answers.refreshTokens,
|
|
351
|
-
rateLimiting: answers.enableRateLimiting
|
|
386
|
+
rateLimiting: answers.enableRateLimiting,
|
|
387
|
+
swagger: answers.enableSwagger,
|
|
388
|
+
unitTests: answers.generateTests,
|
|
389
|
+
useUsername: answers.useUsername,
|
|
390
|
+
emailVerification: answers.enableEmailVerification,
|
|
391
|
+
resetPassword: answers.enableResetPassword
|
|
352
392
|
},
|
|
353
393
|
jwt: {
|
|
354
394
|
secret: generateSecret(),
|
|
@@ -357,7 +397,7 @@ function buildConfig(answers, projectName, sourceRoot, detectedORM, detectedDB)
|
|
|
357
397
|
},
|
|
358
398
|
autoInstall: answers.autoInstall,
|
|
359
399
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
360
|
-
generatorVersion: "1.
|
|
400
|
+
generatorVersion: "1.3.0"
|
|
361
401
|
};
|
|
362
402
|
return config;
|
|
363
403
|
}
|
|
@@ -433,7 +473,12 @@ function showSuccess(stats) {
|
|
|
433
473
|
console.log(` \u2022 @nestjs/jwt, @nestjs/passport, @nestjs/config`);
|
|
434
474
|
console.log(` \u2022 passport, passport-jwt, passport-local`);
|
|
435
475
|
console.log(` \u2022 bcrypt, class-validator, class-transformer`);
|
|
436
|
-
|
|
476
|
+
if (stats.orm === "prisma") {
|
|
477
|
+
console.log(` \u2022 @prisma/client, prisma`);
|
|
478
|
+
}
|
|
479
|
+
if (stats.swagger) {
|
|
480
|
+
console.log(` \u2022 @nestjs/swagger`);
|
|
481
|
+
}
|
|
437
482
|
console.log();
|
|
438
483
|
console.log(import_chalk.default.bold("\u{1F510} JWT Configuration:"));
|
|
439
484
|
console.log(` \u2022 Access token: ${stats.jwt.accessExpiration}`);
|
|
@@ -446,9 +491,16 @@ function showSuccess(stats) {
|
|
|
446
491
|
console.log(import_chalk.default.cyan(" 1. Review .env file (auto-generated with secure secret)"));
|
|
447
492
|
console.log(import_chalk.default.gray(" # .env.example is also provided as a git-safe reference"));
|
|
448
493
|
console.log();
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
494
|
+
if (stats.orm === "prisma") {
|
|
495
|
+
console.log(import_chalk.default.cyan(" 2. Add Prisma schema models (see prisma-schema-additions.prisma)"));
|
|
496
|
+
console.log(import_chalk.default.gray(" # Copy the models into your prisma/schema.prisma"));
|
|
497
|
+
console.log(import_chalk.default.gray(" npx prisma migrate dev --name add-auth-models"));
|
|
498
|
+
console.log(import_chalk.default.gray(" npx prisma generate"));
|
|
499
|
+
} else {
|
|
500
|
+
console.log(import_chalk.default.cyan(" 2. Create database migration (if using TypeORM)"));
|
|
501
|
+
console.log(import_chalk.default.gray(" npm run migration:generate -- src/migrations/CreateUserTable"));
|
|
502
|
+
console.log(import_chalk.default.gray(" npm run migration:run"));
|
|
503
|
+
}
|
|
452
504
|
console.log();
|
|
453
505
|
console.log(import_chalk.default.cyan(" 3. Start your NestJS app"));
|
|
454
506
|
console.log(import_chalk.default.gray(" npm run start:dev"));
|
|
@@ -456,10 +508,24 @@ function showSuccess(stats) {
|
|
|
456
508
|
console.log(import_chalk.default.cyan(" 4. Test authentication endpoints"));
|
|
457
509
|
console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/register"));
|
|
458
510
|
console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/login"));
|
|
511
|
+
console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/change-password (requires JWT)"));
|
|
512
|
+
if (stats.emailVerification) {
|
|
513
|
+
console.log(import_chalk.default.gray(" GET http://localhost:3000/auth/verify-email?token=..."));
|
|
514
|
+
console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/resend-verification"));
|
|
515
|
+
}
|
|
516
|
+
if (stats.resetPassword) {
|
|
517
|
+
console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/forgot-password"));
|
|
518
|
+
console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/reset-password"));
|
|
519
|
+
}
|
|
459
520
|
console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/refresh"));
|
|
460
521
|
console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/logout (requires JWT)"));
|
|
461
522
|
console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/logout-all (requires JWT)"));
|
|
462
523
|
console.log(import_chalk.default.gray(" GET http://localhost:3000/users/profile (requires JWT)"));
|
|
524
|
+
if (stats.swagger) {
|
|
525
|
+
console.log();
|
|
526
|
+
console.log(import_chalk.default.cyan(" 5. View Swagger API documentation"));
|
|
527
|
+
console.log(import_chalk.default.gray(" http://localhost:3000/api"));
|
|
528
|
+
}
|
|
463
529
|
console.log();
|
|
464
530
|
console.log(import_chalk.default.bold("\u{1F4D6} Full documentation:"), "src/auth/README.md");
|
|
465
531
|
console.log();
|
|
@@ -467,6 +533,9 @@ function showSuccess(stats) {
|
|
|
467
533
|
console.log(" \u2022 Use @Public() decorator for routes that don't require auth");
|
|
468
534
|
console.log(" \u2022 Use @Roles('Admin') to restrict routes by role");
|
|
469
535
|
console.log(" \u2022 Access current user with @CurrentUser() decorator");
|
|
536
|
+
if (stats.swagger) {
|
|
537
|
+
console.log(" \u2022 Visit /api for interactive Swagger documentation");
|
|
538
|
+
}
|
|
470
539
|
console.log();
|
|
471
540
|
}
|
|
472
541
|
function createSpinner(text) {
|
|
@@ -512,6 +581,8 @@ var init_template_engine = __esm({
|
|
|
512
581
|
registerHelpers() {
|
|
513
582
|
this.handlebars.registerHelper("eq", (a, b) => a === b);
|
|
514
583
|
this.handlebars.registerHelper("ne", (a, b) => a !== b);
|
|
584
|
+
this.handlebars.registerHelper("or", (a, b) => a || b);
|
|
585
|
+
this.handlebars.registerHelper("and", (a, b) => a && b);
|
|
515
586
|
this.handlebars.registerHelper("includes", (arr, item) => arr?.includes(item));
|
|
516
587
|
this.handlebars.registerHelper("capitalize", (str) => {
|
|
517
588
|
if (!str) return "";
|
|
@@ -751,9 +822,16 @@ var init_generator = __esm({
|
|
|
751
822
|
plan.push(
|
|
752
823
|
{ template: "dto/login.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/login.dto.ts` },
|
|
753
824
|
{ template: "dto/register.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/register.dto.ts` },
|
|
825
|
+
{ template: "dto/change-password.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/change-password.dto.ts` },
|
|
754
826
|
{ template: "dto/auth-response.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/auth-response.dto.ts` },
|
|
755
827
|
{ template: "dto/create-user.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/create-user.dto.ts` }
|
|
756
828
|
);
|
|
829
|
+
if (config.features.resetPassword) {
|
|
830
|
+
plan.push(
|
|
831
|
+
{ template: "dto/forgot-password.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/forgot-password.dto.ts` },
|
|
832
|
+
{ template: "dto/reset-password.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/reset-password.dto.ts` }
|
|
833
|
+
);
|
|
834
|
+
}
|
|
757
835
|
plan.push(
|
|
758
836
|
{ template: "users/users.module.ts.hbs", output: `${config.sourceRoot}/users/users.module.ts` },
|
|
759
837
|
{ template: "users/users.service.ts.hbs", output: `${config.sourceRoot}/users/users.service.ts` },
|
|
@@ -769,6 +847,18 @@ var init_generator = __esm({
|
|
|
769
847
|
output: `${config.sourceRoot}/users/entities/refresh-token.entity.ts`
|
|
770
848
|
});
|
|
771
849
|
}
|
|
850
|
+
} else if (config.orm === "prisma") {
|
|
851
|
+
plan.push(
|
|
852
|
+
{ template: "prisma/prisma.service.ts.hbs", output: `${config.sourceRoot}/prisma/prisma.service.ts` },
|
|
853
|
+
{ template: "prisma/prisma.module.ts.hbs", output: `${config.sourceRoot}/prisma/prisma.module.ts` },
|
|
854
|
+
{ template: "prisma/schema.prisma.additions.hbs", output: "prisma-schema-additions.prisma" }
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
if (config.features.unitTests) {
|
|
858
|
+
plan.push(
|
|
859
|
+
{ template: "tests/auth.service.spec.ts.hbs", output: `${config.sourceRoot}/auth/auth.service.spec.ts` },
|
|
860
|
+
{ template: "tests/auth.controller.spec.ts.hbs", output: `${config.sourceRoot}/auth/auth.controller.spec.ts` }
|
|
861
|
+
);
|
|
772
862
|
}
|
|
773
863
|
plan.push(
|
|
774
864
|
{ template: "shared/env.template.hbs", output: ".env.example" },
|
|
@@ -850,6 +940,8 @@ var init_ast_updater = __esm({
|
|
|
850
940
|
if (config.features.refreshTokens) {
|
|
851
941
|
this.addImport("./users/entities/refresh-token.entity", ["RefreshToken"]);
|
|
852
942
|
}
|
|
943
|
+
} else if (config && config.orm === "prisma") {
|
|
944
|
+
this.addImport("./prisma/prisma.module", ["PrismaModule"]);
|
|
853
945
|
}
|
|
854
946
|
this.addImport("./auth/auth.module", ["AuthModule"]);
|
|
855
947
|
this.addImport("./users/users.module", ["UsersModule"]);
|
|
@@ -916,6 +1008,8 @@ var init_ast_updater = __esm({
|
|
|
916
1008
|
if (config && config.orm === "typeorm" && !existingModules.has("TypeOrmModule")) {
|
|
917
1009
|
const entities = config.features.refreshTokens ? "[User, RefreshToken]" : "[User]";
|
|
918
1010
|
allElements.push(this.buildTypeOrmConfig(config.database, entities));
|
|
1011
|
+
} else if (config && config.orm === "prisma" && !existingModules.has("PrismaModule")) {
|
|
1012
|
+
allElements.push("PrismaModule");
|
|
919
1013
|
}
|
|
920
1014
|
if (!existingModules.has("AuthModule")) {
|
|
921
1015
|
allElements.push("AuthModule");
|
|
@@ -1034,17 +1128,17 @@ var init_main_ts_updater = __esm({
|
|
|
1034
1128
|
sourceFile;
|
|
1035
1129
|
backupPath = null;
|
|
1036
1130
|
/**
|
|
1037
|
-
* Update main.ts with global guards and
|
|
1131
|
+
* Update main.ts with global guards, validation pipe, and optionally Swagger
|
|
1038
1132
|
*/
|
|
1039
|
-
async update() {
|
|
1133
|
+
async update(config) {
|
|
1040
1134
|
if (!await fs5.pathExists(this.mainTsPath)) {
|
|
1041
1135
|
throw new Error(`main.ts not found at ${this.mainTsPath}`);
|
|
1042
1136
|
}
|
|
1043
1137
|
await this.createBackup();
|
|
1044
1138
|
try {
|
|
1045
1139
|
this.sourceFile = this.project.addSourceFileAtPath(this.mainTsPath);
|
|
1046
|
-
this.addImports();
|
|
1047
|
-
this.addGlobalGuardsAndPipes();
|
|
1140
|
+
this.addImports(config);
|
|
1141
|
+
this.addGlobalGuardsAndPipes(config);
|
|
1048
1142
|
this.sourceFile.formatText();
|
|
1049
1143
|
await this.sourceFile.save();
|
|
1050
1144
|
} catch (error) {
|
|
@@ -1055,13 +1149,16 @@ var init_main_ts_updater = __esm({
|
|
|
1055
1149
|
/**
|
|
1056
1150
|
* Add necessary imports
|
|
1057
1151
|
*/
|
|
1058
|
-
addImports() {
|
|
1152
|
+
addImports(config) {
|
|
1059
1153
|
if (!this.sourceFile) {
|
|
1060
1154
|
throw new Error("Source file not loaded");
|
|
1061
1155
|
}
|
|
1062
1156
|
this.addImport("@nestjs/core", ["Reflector"]);
|
|
1063
1157
|
this.addImport("@nestjs/common", ["ValidationPipe"]);
|
|
1064
1158
|
this.addImport("./auth/guards/jwt-auth.guard", ["JwtAuthGuard"]);
|
|
1159
|
+
if (config?.features?.swagger) {
|
|
1160
|
+
this.addImport("@nestjs/swagger", ["SwaggerModule", "DocumentBuilder"]);
|
|
1161
|
+
}
|
|
1065
1162
|
}
|
|
1066
1163
|
/**
|
|
1067
1164
|
* Add an import statement if it doesn't exist
|
|
@@ -1087,7 +1184,7 @@ var init_main_ts_updater = __esm({
|
|
|
1087
1184
|
/**
|
|
1088
1185
|
* Add global guards and validation pipe to bootstrap function
|
|
1089
1186
|
*/
|
|
1090
|
-
addGlobalGuardsAndPipes() {
|
|
1187
|
+
addGlobalGuardsAndPipes(config) {
|
|
1091
1188
|
if (!this.sourceFile) return;
|
|
1092
1189
|
const bootstrapFunc = this.sourceFile.getFunction("bootstrap");
|
|
1093
1190
|
if (!bootstrapFunc) {
|
|
@@ -1113,7 +1210,7 @@ var init_main_ts_updater = __esm({
|
|
|
1113
1210
|
if (listenIndex === -1) {
|
|
1114
1211
|
listenIndex = statements.length;
|
|
1115
1212
|
}
|
|
1116
|
-
const
|
|
1213
|
+
const codeLines = [
|
|
1117
1214
|
"",
|
|
1118
1215
|
"// Enable global validation pipe",
|
|
1119
1216
|
"app.useGlobalPipes(",
|
|
@@ -1127,9 +1224,24 @@ var init_main_ts_updater = __esm({
|
|
|
1127
1224
|
"// Enable global JWT guard (all routes protected by default)",
|
|
1128
1225
|
"// Use @Public() decorator on routes that should be accessible without auth",
|
|
1129
1226
|
"const reflector = app.get(Reflector);",
|
|
1130
|
-
"app.useGlobalGuards(new JwtAuthGuard(reflector));"
|
|
1131
|
-
|
|
1132
|
-
|
|
1227
|
+
"app.useGlobalGuards(new JwtAuthGuard(reflector));"
|
|
1228
|
+
];
|
|
1229
|
+
if (config?.features?.swagger) {
|
|
1230
|
+
codeLines.push(
|
|
1231
|
+
"",
|
|
1232
|
+
"// Swagger API documentation",
|
|
1233
|
+
"const swaggerConfig = new DocumentBuilder()",
|
|
1234
|
+
" .setTitle('API Documentation')",
|
|
1235
|
+
" .setDescription('JWT Authentication API')",
|
|
1236
|
+
" .setVersion('1.0')",
|
|
1237
|
+
" .addBearerAuth()",
|
|
1238
|
+
" .build();",
|
|
1239
|
+
"const document = SwaggerModule.createDocument(app, swaggerConfig);",
|
|
1240
|
+
"SwaggerModule.setup('api', app, document);"
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
codeLines.push("");
|
|
1244
|
+
const codeToInsert = codeLines.join("\n");
|
|
1133
1245
|
body.insertStatements(listenIndex, codeToInsert);
|
|
1134
1246
|
}
|
|
1135
1247
|
/**
|
|
@@ -1236,9 +1348,16 @@ var init_package_updater = __esm({
|
|
|
1236
1348
|
break;
|
|
1237
1349
|
}
|
|
1238
1350
|
}
|
|
1351
|
+
if (config.orm === "prisma") {
|
|
1352
|
+
dependencies["@prisma/client"] = "^6.0.0";
|
|
1353
|
+
devDependencies["prisma"] = "^6.0.0";
|
|
1354
|
+
}
|
|
1239
1355
|
if (config.features.rateLimiting) {
|
|
1240
1356
|
dependencies["@nestjs/throttler"] = "^6.0.0";
|
|
1241
1357
|
}
|
|
1358
|
+
if (config.features.swagger) {
|
|
1359
|
+
dependencies["@nestjs/swagger"] = "^11.0.0";
|
|
1360
|
+
}
|
|
1242
1361
|
return { dependencies, devDependencies };
|
|
1243
1362
|
}
|
|
1244
1363
|
/**
|
|
@@ -1419,7 +1538,7 @@ async function run(cwd = process.cwd(), options = {}) {
|
|
|
1419
1538
|
try {
|
|
1420
1539
|
const { MainTsUpdater: MainTsUpdater2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
|
|
1421
1540
|
const mainUpdater = new MainTsUpdater2(projectInfo.mainTsPath);
|
|
1422
|
-
await mainUpdater.update();
|
|
1541
|
+
await mainUpdater.update(config);
|
|
1423
1542
|
await mainUpdater.cleanupBackup();
|
|
1424
1543
|
mainSpinner.succeed("Updated main.ts with global JWT guard");
|
|
1425
1544
|
} catch (error) {
|
|
@@ -1460,7 +1579,11 @@ async function run(cwd = process.cwd(), options = {}) {
|
|
|
1460
1579
|
jwt: {
|
|
1461
1580
|
accessExpiration: config.jwt.accessExpiration,
|
|
1462
1581
|
refreshExpiration: config.features.refreshTokens ? config.jwt.refreshExpiration : void 0
|
|
1463
|
-
}
|
|
1582
|
+
},
|
|
1583
|
+
orm: config.orm,
|
|
1584
|
+
swagger: config.features.swagger,
|
|
1585
|
+
emailVerification: config.features.emailVerification,
|
|
1586
|
+
resetPassword: config.features.resetPassword
|
|
1464
1587
|
});
|
|
1465
1588
|
console.log("\u{1F41B} Issues? https://github.com/Islamawad132/add-nest-auth/issues");
|
|
1466
1589
|
console.log("\u2B50 Like it? https://github.com/Islamawad132/add-nest-auth");
|