add-nest-auth 1.0.6 → 1.0.8
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/README.md +2 -2
- package/dist/cli.js +139 -33
- package/dist/cli.js.map +1 -1
- package/dist/generator/templates/dto/register.dto.ts.hbs +1 -7
- package/dist/generator/templates/jwt/auth.service.ts.hbs +1 -1
- package/dist/generator/templates/shared/README.auth.md.hbs +1 -1
- package/dist/index.js +139 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -359,9 +359,9 @@ If this tool helped you, please consider:
|
|
|
359
359
|
|
|
360
360
|
## 🔗 Links
|
|
361
361
|
|
|
362
|
-
- **GitHub**: https://github.com/
|
|
362
|
+
- **GitHub**: https://github.com/Islamawad132/add-nest-auth
|
|
363
363
|
- **npm**: https://www.npmjs.com/package/add-nest-auth
|
|
364
|
-
- **Issues**: https://github.com/
|
|
364
|
+
- **Issues**: https://github.com/Islamawad132/add-nest-auth/issues
|
|
365
365
|
|
|
366
366
|
---
|
|
367
367
|
|
package/dist/cli.js
CHANGED
|
@@ -127,6 +127,7 @@ var init_file_writer = __esm({
|
|
|
127
127
|
fs3 = __toESM(require("fs-extra"));
|
|
128
128
|
FileWriter = class {
|
|
129
129
|
writtenFiles = [];
|
|
130
|
+
skippedFiles = [];
|
|
130
131
|
backups = /* @__PURE__ */ new Map();
|
|
131
132
|
/**
|
|
132
133
|
* Write a file to disk
|
|
@@ -135,7 +136,8 @@ var init_file_writer = __esm({
|
|
|
135
136
|
const { overwrite = false, backup = true } = options;
|
|
136
137
|
const exists = await fs3.pathExists(filePath);
|
|
137
138
|
if (exists && !overwrite) {
|
|
138
|
-
|
|
139
|
+
this.skippedFiles.push(filePath);
|
|
140
|
+
return;
|
|
139
141
|
}
|
|
140
142
|
if (exists && backup) {
|
|
141
143
|
await this.createBackup(filePath);
|
|
@@ -187,6 +189,12 @@ var init_file_writer = __esm({
|
|
|
187
189
|
getWrittenFiles() {
|
|
188
190
|
return [...this.writtenFiles];
|
|
189
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Get list of skipped files (already existed)
|
|
194
|
+
*/
|
|
195
|
+
getSkippedFiles() {
|
|
196
|
+
return [...this.skippedFiles];
|
|
197
|
+
}
|
|
190
198
|
};
|
|
191
199
|
}
|
|
192
200
|
});
|
|
@@ -225,7 +233,7 @@ var init_generator = __esm({
|
|
|
225
233
|
/**
|
|
226
234
|
* Generate all authentication files
|
|
227
235
|
*/
|
|
228
|
-
async generate(config, projectInfo) {
|
|
236
|
+
async generate(config, projectInfo, overwrite = false) {
|
|
229
237
|
try {
|
|
230
238
|
const context = buildTemplateContext(config);
|
|
231
239
|
const plan = this.buildGenerationPlan(config);
|
|
@@ -239,19 +247,22 @@ var init_generator = __esm({
|
|
|
239
247
|
);
|
|
240
248
|
const outputPath = path4.join(projectInfo.root, fileSpec.output);
|
|
241
249
|
await this.fileWriter.writeFile(outputPath, content, {
|
|
242
|
-
overwrite
|
|
250
|
+
overwrite
|
|
243
251
|
});
|
|
244
252
|
}
|
|
245
253
|
const filesCreated = this.fileWriter.getWrittenFiles();
|
|
254
|
+
const filesSkipped = this.fileWriter.getSkippedFiles();
|
|
246
255
|
await this.fileWriter.cleanupBackups();
|
|
247
256
|
return {
|
|
248
257
|
filesCreated,
|
|
258
|
+
filesSkipped,
|
|
249
259
|
success: true
|
|
250
260
|
};
|
|
251
261
|
} catch (error) {
|
|
252
262
|
await this.fileWriter.rollback();
|
|
253
263
|
return {
|
|
254
264
|
filesCreated: [],
|
|
265
|
+
filesSkipped: [],
|
|
255
266
|
success: false,
|
|
256
267
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
257
268
|
};
|
|
@@ -348,7 +359,10 @@ var init_ast_updater = __esm({
|
|
|
348
359
|
constructor(appModulePath) {
|
|
349
360
|
this.appModulePath = appModulePath;
|
|
350
361
|
this.project = new import_ts_morph.Project({
|
|
351
|
-
skipAddingFilesFromTsConfig: true
|
|
362
|
+
skipAddingFilesFromTsConfig: true,
|
|
363
|
+
manipulationSettings: {
|
|
364
|
+
indentationText: import_ts_morph.IndentationText.TwoSpaces
|
|
365
|
+
}
|
|
352
366
|
});
|
|
353
367
|
}
|
|
354
368
|
project;
|
|
@@ -357,12 +371,13 @@ var init_ast_updater = __esm({
|
|
|
357
371
|
/**
|
|
358
372
|
* Update app.module.ts with auth modules
|
|
359
373
|
*/
|
|
360
|
-
async update() {
|
|
374
|
+
async update(config) {
|
|
361
375
|
await this.createBackup();
|
|
362
376
|
try {
|
|
363
377
|
this.sourceFile = this.project.addSourceFileAtPath(this.appModulePath);
|
|
364
|
-
this.addImports();
|
|
365
|
-
this.addModulesToDecorator();
|
|
378
|
+
this.addImports(config);
|
|
379
|
+
this.addModulesToDecorator(config);
|
|
380
|
+
this.sourceFile.formatText();
|
|
366
381
|
await this.sourceFile.save();
|
|
367
382
|
} catch (error) {
|
|
368
383
|
await this.restoreBackup();
|
|
@@ -372,11 +387,18 @@ var init_ast_updater = __esm({
|
|
|
372
387
|
/**
|
|
373
388
|
* Add necessary imports
|
|
374
389
|
*/
|
|
375
|
-
addImports() {
|
|
390
|
+
addImports(config) {
|
|
376
391
|
if (!this.sourceFile) {
|
|
377
392
|
throw new Error("Source file not loaded");
|
|
378
393
|
}
|
|
379
394
|
this.addImport("@nestjs/config", ["ConfigModule"]);
|
|
395
|
+
if (config && config.orm === "typeorm") {
|
|
396
|
+
this.addImport("@nestjs/typeorm", ["TypeOrmModule"]);
|
|
397
|
+
this.addImport("./users/entities/user.entity", ["User"]);
|
|
398
|
+
if (config.features.refreshTokens) {
|
|
399
|
+
this.addImport("./users/entities/refresh-token.entity", ["RefreshToken"]);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
380
402
|
this.addImport("./auth/auth.module", ["AuthModule"]);
|
|
381
403
|
this.addImport("./users/users.module", ["UsersModule"]);
|
|
382
404
|
}
|
|
@@ -404,7 +426,7 @@ var init_ast_updater = __esm({
|
|
|
404
426
|
/**
|
|
405
427
|
* Add modules to @Module decorator imports array
|
|
406
428
|
*/
|
|
407
|
-
addModulesToDecorator() {
|
|
429
|
+
addModulesToDecorator(config) {
|
|
408
430
|
if (!this.sourceFile) return;
|
|
409
431
|
const appModuleClass = this.sourceFile.getClass("AppModule");
|
|
410
432
|
if (!appModuleClass) {
|
|
@@ -437,6 +459,11 @@ var init_ast_updater = __esm({
|
|
|
437
459
|
if (!existingModules.has("ConfigModule")) {
|
|
438
460
|
importsArray.addElement("ConfigModule.forRoot({ isGlobal: true })");
|
|
439
461
|
}
|
|
462
|
+
if (config && config.orm === "typeorm" && !existingModules.has("TypeOrmModule")) {
|
|
463
|
+
const entities = config.features.refreshTokens ? "[User, RefreshToken]" : "[User]";
|
|
464
|
+
const typeOrmConfig = this.buildTypeOrmConfig(config.database, entities);
|
|
465
|
+
importsArray.addElement(typeOrmConfig);
|
|
466
|
+
}
|
|
440
467
|
if (!existingModules.has("AuthModule")) {
|
|
441
468
|
importsArray.addElement("AuthModule");
|
|
442
469
|
}
|
|
@@ -444,6 +471,43 @@ var init_ast_updater = __esm({
|
|
|
444
471
|
importsArray.addElement("UsersModule");
|
|
445
472
|
}
|
|
446
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* Build TypeORM.forRoot() configuration string based on database type
|
|
476
|
+
*/
|
|
477
|
+
buildTypeOrmConfig(database, entities) {
|
|
478
|
+
switch (database) {
|
|
479
|
+
case "sqlite":
|
|
480
|
+
return `TypeOrmModule.forRoot({
|
|
481
|
+
type: 'sqlite',
|
|
482
|
+
database: 'database.sqlite',
|
|
483
|
+
entities: ${entities},
|
|
484
|
+
synchronize: true, // WARNING: disable in production!
|
|
485
|
+
})`;
|
|
486
|
+
case "mysql":
|
|
487
|
+
return `TypeOrmModule.forRoot({
|
|
488
|
+
type: 'mysql',
|
|
489
|
+
host: process.env.DATABASE_HOST || 'localhost',
|
|
490
|
+
port: parseInt(process.env.DATABASE_PORT || '3306'),
|
|
491
|
+
username: process.env.DATABASE_USER || 'root',
|
|
492
|
+
password: process.env.DATABASE_PASSWORD || '',
|
|
493
|
+
database: process.env.DATABASE_NAME || 'auth_db',
|
|
494
|
+
entities: ${entities},
|
|
495
|
+
synchronize: true, // WARNING: disable in production!
|
|
496
|
+
})`;
|
|
497
|
+
case "postgres":
|
|
498
|
+
default:
|
|
499
|
+
return `TypeOrmModule.forRoot({
|
|
500
|
+
type: 'postgres',
|
|
501
|
+
host: process.env.DATABASE_HOST || 'localhost',
|
|
502
|
+
port: parseInt(process.env.DATABASE_PORT || '5432'),
|
|
503
|
+
username: process.env.DATABASE_USER || 'postgres',
|
|
504
|
+
password: process.env.DATABASE_PASSWORD || 'postgres',
|
|
505
|
+
database: process.env.DATABASE_NAME || 'auth_db',
|
|
506
|
+
entities: ${entities},
|
|
507
|
+
synchronize: true, // WARNING: disable in production!
|
|
508
|
+
})`;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
447
511
|
/**
|
|
448
512
|
* Get existing module names from imports array
|
|
449
513
|
*/
|
|
@@ -501,7 +565,10 @@ var init_main_ts_updater = __esm({
|
|
|
501
565
|
constructor(mainTsPath) {
|
|
502
566
|
this.mainTsPath = mainTsPath;
|
|
503
567
|
this.project = new import_ts_morph2.Project({
|
|
504
|
-
skipAddingFilesFromTsConfig: true
|
|
568
|
+
skipAddingFilesFromTsConfig: true,
|
|
569
|
+
manipulationSettings: {
|
|
570
|
+
indentationText: import_ts_morph2.IndentationText.TwoSpaces
|
|
571
|
+
}
|
|
505
572
|
});
|
|
506
573
|
}
|
|
507
574
|
project;
|
|
@@ -519,6 +586,7 @@ var init_main_ts_updater = __esm({
|
|
|
519
586
|
this.sourceFile = this.project.addSourceFileAtPath(this.mainTsPath);
|
|
520
587
|
this.addImports();
|
|
521
588
|
this.addGlobalGuardsAndPipes();
|
|
589
|
+
this.sourceFile.formatText();
|
|
522
590
|
await this.sourceFile.save();
|
|
523
591
|
} catch (error) {
|
|
524
592
|
await this.restoreBackup();
|
|
@@ -588,19 +656,19 @@ var init_main_ts_updater = __esm({
|
|
|
588
656
|
}
|
|
589
657
|
const codeToInsert = [
|
|
590
658
|
"",
|
|
591
|
-
"
|
|
592
|
-
"
|
|
593
|
-
"
|
|
594
|
-
"
|
|
595
|
-
"
|
|
596
|
-
"
|
|
597
|
-
"
|
|
598
|
-
"
|
|
659
|
+
"// Enable global validation pipe",
|
|
660
|
+
"app.useGlobalPipes(",
|
|
661
|
+
" new ValidationPipe({",
|
|
662
|
+
" whitelist: true,",
|
|
663
|
+
" forbidNonWhitelisted: true,",
|
|
664
|
+
" transform: true,",
|
|
665
|
+
" }),",
|
|
666
|
+
");",
|
|
599
667
|
"",
|
|
600
|
-
"
|
|
601
|
-
"
|
|
602
|
-
"
|
|
603
|
-
"
|
|
668
|
+
"// Enable global JWT guard (all routes protected by default)",
|
|
669
|
+
"// Use @Public() decorator on routes that should be accessible without auth",
|
|
670
|
+
"const reflector = app.get(Reflector);",
|
|
671
|
+
"app.useGlobalGuards(new JwtAuthGuard(reflector));",
|
|
604
672
|
""
|
|
605
673
|
].join("\n");
|
|
606
674
|
body.insertStatements(listenIndex, codeToInsert);
|
|
@@ -840,6 +908,25 @@ async function detectORM(packageJson) {
|
|
|
840
908
|
}
|
|
841
909
|
return "none";
|
|
842
910
|
}
|
|
911
|
+
function detectDatabase(packageJson, orm) {
|
|
912
|
+
const dependencies = {
|
|
913
|
+
...packageJson.dependencies,
|
|
914
|
+
...packageJson.devDependencies
|
|
915
|
+
};
|
|
916
|
+
if (orm === "typeorm") {
|
|
917
|
+
if (dependencies["pg"]) return "postgres";
|
|
918
|
+
if (dependencies["mysql2"] || dependencies["mysql"]) return "mysql";
|
|
919
|
+
if (dependencies["sqlite3"]) return "sqlite";
|
|
920
|
+
if (dependencies["mongodb"]) return "mongodb";
|
|
921
|
+
}
|
|
922
|
+
if (orm === "prisma") {
|
|
923
|
+
return void 0;
|
|
924
|
+
}
|
|
925
|
+
if (orm === "mongoose") {
|
|
926
|
+
return "mongodb";
|
|
927
|
+
}
|
|
928
|
+
return void 0;
|
|
929
|
+
}
|
|
843
930
|
|
|
844
931
|
// src/analyzer/project-detector.ts
|
|
845
932
|
var ProjectDetector = class {
|
|
@@ -878,11 +965,11 @@ var ProjectDetector = class {
|
|
|
878
965
|
}
|
|
879
966
|
const mainTsPath = path.join(root, sourceRoot, "main.ts");
|
|
880
967
|
const orm = await detectORM(packageJson);
|
|
968
|
+
const database = detectDatabase(packageJson, orm);
|
|
881
969
|
const authModulePath = path.join(root, sourceRoot, "auth");
|
|
882
|
-
|
|
883
|
-
errors.push("auth/ directory already exists (use --force to overwrite)");
|
|
884
|
-
}
|
|
970
|
+
const authExists = await fs.pathExists(authModulePath);
|
|
885
971
|
return {
|
|
972
|
+
authExists,
|
|
886
973
|
root,
|
|
887
974
|
sourceRoot,
|
|
888
975
|
appModulePath,
|
|
@@ -890,6 +977,7 @@ var ProjectDetector = class {
|
|
|
890
977
|
packageJsonPath,
|
|
891
978
|
nestCliConfigPath,
|
|
892
979
|
orm,
|
|
980
|
+
database,
|
|
893
981
|
nestVersion: packageJson.dependencies?.["@nestjs/core"],
|
|
894
982
|
typescriptVersion: packageJson.devDependencies?.["typescript"],
|
|
895
983
|
isValid: errors.length === 0,
|
|
@@ -955,7 +1043,8 @@ function generateSecret(length = 32) {
|
|
|
955
1043
|
}
|
|
956
1044
|
|
|
957
1045
|
// src/cli/prompts.ts
|
|
958
|
-
async function promptConfig(detectedORM) {
|
|
1046
|
+
async function promptConfig(detectedORM, detectedDB) {
|
|
1047
|
+
const dbLabel = detectedDB ? ` with ${detectedDB.charAt(0).toUpperCase() + detectedDB.slice(1)}` : "";
|
|
959
1048
|
const answers = await import_inquirer.default.prompt([
|
|
960
1049
|
{
|
|
961
1050
|
type: "list",
|
|
@@ -1027,7 +1116,7 @@ async function promptConfig(detectedORM) {
|
|
|
1027
1116
|
{
|
|
1028
1117
|
type: "confirm",
|
|
1029
1118
|
name: "useDetectedORM",
|
|
1030
|
-
message: `Detected ${detectedORM.toUpperCase()}${
|
|
1119
|
+
message: `Detected ${detectedORM.toUpperCase()}${dbLabel}. Use it?`,
|
|
1031
1120
|
default: true,
|
|
1032
1121
|
when: () => detectedORM !== "none"
|
|
1033
1122
|
},
|
|
@@ -1053,7 +1142,7 @@ async function promptConfig(detectedORM) {
|
|
|
1053
1142
|
]);
|
|
1054
1143
|
return answers;
|
|
1055
1144
|
}
|
|
1056
|
-
function buildConfig(answers, projectName, sourceRoot, detectedORM) {
|
|
1145
|
+
function buildConfig(answers, projectName, sourceRoot, detectedORM, detectedDB) {
|
|
1057
1146
|
const config = {
|
|
1058
1147
|
projectName,
|
|
1059
1148
|
sourceRoot,
|
|
@@ -1063,7 +1152,7 @@ function buildConfig(answers, projectName, sourceRoot, detectedORM) {
|
|
|
1063
1152
|
roles: answers.roles || []
|
|
1064
1153
|
},
|
|
1065
1154
|
orm: answers.useDetectedORM !== false ? detectedORM : "typeorm",
|
|
1066
|
-
database: answers.database || "postgres",
|
|
1155
|
+
database: answers.database || detectedDB || "postgres",
|
|
1067
1156
|
features: {
|
|
1068
1157
|
refreshTokens: answers.refreshTokens
|
|
1069
1158
|
},
|
|
@@ -1205,12 +1294,26 @@ async function run(cwd = process.cwd()) {
|
|
|
1205
1294
|
orm: projectInfo.orm,
|
|
1206
1295
|
sourceRoot: projectInfo.sourceRoot
|
|
1207
1296
|
});
|
|
1208
|
-
|
|
1297
|
+
if (projectInfo.authExists) {
|
|
1298
|
+
const inquirer2 = (await import("inquirer")).default;
|
|
1299
|
+
const { overwrite } = await inquirer2.prompt([{
|
|
1300
|
+
type: "confirm",
|
|
1301
|
+
name: "overwrite",
|
|
1302
|
+
message: "auth/ directory already exists. Overwrite existing files?",
|
|
1303
|
+
default: false
|
|
1304
|
+
}]);
|
|
1305
|
+
if (!overwrite) {
|
|
1306
|
+
console.log("\n\u23ED\uFE0F Cancelled. Existing auth module unchanged.\n");
|
|
1307
|
+
process.exit(0);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
const answers = await promptConfig(projectInfo.orm, projectInfo.database);
|
|
1209
1311
|
const config = buildConfig(
|
|
1210
1312
|
answers,
|
|
1211
1313
|
projectInfo.root.split(/[/\\]/).pop() || "project",
|
|
1212
1314
|
projectInfo.sourceRoot,
|
|
1213
|
-
projectInfo.orm
|
|
1315
|
+
projectInfo.orm,
|
|
1316
|
+
projectInfo.database
|
|
1214
1317
|
);
|
|
1215
1318
|
console.log();
|
|
1216
1319
|
console.log("\u2699\uFE0F Generating authentication module...");
|
|
@@ -1218,18 +1321,21 @@ async function run(cwd = process.cwd()) {
|
|
|
1218
1321
|
const { Generator: Generator2 } = await Promise.resolve().then(() => (init_generator2(), generator_exports));
|
|
1219
1322
|
const generator = new Generator2();
|
|
1220
1323
|
const genSpinner = createSpinner("Generating files from templates...").start();
|
|
1221
|
-
const result = await generator.generate(config, projectInfo);
|
|
1324
|
+
const result = await generator.generate(config, projectInfo, !!projectInfo.authExists);
|
|
1222
1325
|
if (!result.success) {
|
|
1223
1326
|
genSpinner.fail("Generation failed");
|
|
1224
1327
|
showError("Failed to generate files", [result.error || "Unknown error"]);
|
|
1225
1328
|
process.exit(1);
|
|
1226
1329
|
}
|
|
1227
1330
|
genSpinner.succeed(`Generated ${result.filesCreated.length} files`);
|
|
1331
|
+
if (result.filesSkipped.length > 0) {
|
|
1332
|
+
console.log(` \u26A0\uFE0F Skipped ${result.filesSkipped.length} existing file(s)`);
|
|
1333
|
+
}
|
|
1228
1334
|
const astSpinner = createSpinner("Updating app.module.ts...").start();
|
|
1229
1335
|
try {
|
|
1230
1336
|
const { AppModuleUpdater: AppModuleUpdater2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
|
|
1231
1337
|
const astUpdater = new AppModuleUpdater2(projectInfo.appModulePath);
|
|
1232
|
-
await astUpdater.update();
|
|
1338
|
+
await astUpdater.update(config);
|
|
1233
1339
|
await astUpdater.cleanupBackup();
|
|
1234
1340
|
astSpinner.succeed("Updated app.module.ts");
|
|
1235
1341
|
} catch (error) {
|