add-nest-auth 1.0.9 → 1.1.1

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 CHANGED
@@ -36,6 +36,457 @@ var init_cjs_shims = __esm({
36
36
  }
37
37
  });
38
38
 
39
+ // src/analyzer/orm-detector.ts
40
+ async function detectORM(packageJson) {
41
+ const dependencies = {
42
+ ...packageJson.dependencies,
43
+ ...packageJson.devDependencies
44
+ };
45
+ if (dependencies["@nestjs/typeorm"] || dependencies["typeorm"]) {
46
+ return "typeorm";
47
+ }
48
+ if (dependencies["@prisma/client"] || dependencies["prisma"]) {
49
+ return "prisma";
50
+ }
51
+ if (dependencies["@nestjs/mongoose"] || dependencies["mongoose"]) {
52
+ return "mongoose";
53
+ }
54
+ return "none";
55
+ }
56
+ function detectDatabase(packageJson, orm) {
57
+ const dependencies = {
58
+ ...packageJson.dependencies,
59
+ ...packageJson.devDependencies
60
+ };
61
+ if (orm === "typeorm") {
62
+ if (dependencies["pg"]) return "postgres";
63
+ if (dependencies["mysql2"] || dependencies["mysql"]) return "mysql";
64
+ if (dependencies["sqlite3"]) return "sqlite";
65
+ if (dependencies["mongodb"]) return "mongodb";
66
+ }
67
+ if (orm === "prisma") {
68
+ return void 0;
69
+ }
70
+ if (orm === "mongoose") {
71
+ return "mongodb";
72
+ }
73
+ return void 0;
74
+ }
75
+ var init_orm_detector = __esm({
76
+ "src/analyzer/orm-detector.ts"() {
77
+ "use strict";
78
+ init_cjs_shims();
79
+ }
80
+ });
81
+
82
+ // src/analyzer/project-detector.ts
83
+ async function detectProject(cwd = process.cwd()) {
84
+ const detector = new ProjectDetector(cwd);
85
+ return detector.detectProject();
86
+ }
87
+ var path, fs, ProjectDetector;
88
+ var init_project_detector = __esm({
89
+ "src/analyzer/project-detector.ts"() {
90
+ "use strict";
91
+ init_cjs_shims();
92
+ path = __toESM(require("path"));
93
+ fs = __toESM(require("fs-extra"));
94
+ init_orm_detector();
95
+ ProjectDetector = class {
96
+ constructor(cwd) {
97
+ this.cwd = cwd;
98
+ }
99
+ /**
100
+ * Detect and validate a NestJS project
101
+ */
102
+ async detectProject() {
103
+ const errors = [];
104
+ const root = this.cwd;
105
+ const packageJsonPath = path.join(root, "package.json");
106
+ if (!await fs.pathExists(packageJsonPath)) {
107
+ errors.push("package.json not found");
108
+ return this.createInvalidProject(root, errors);
109
+ }
110
+ const packageJson = await this.readPackageJson(packageJsonPath);
111
+ if (!packageJson) {
112
+ errors.push("Failed to read package.json");
113
+ return this.createInvalidProject(root, errors);
114
+ }
115
+ const hasNestCore = packageJson.dependencies?.["@nestjs/core"];
116
+ const hasNestCommon = packageJson.dependencies?.["@nestjs/common"];
117
+ if (!hasNestCore || !hasNestCommon) {
118
+ errors.push("Not a NestJS project (missing @nestjs/core or @nestjs/common)");
119
+ return this.createInvalidProject(root, errors);
120
+ }
121
+ const nestCliConfigPath = path.join(root, "nest-cli.json");
122
+ const nestCliConfig = await this.readNestCliConfig(nestCliConfigPath);
123
+ const sourceRoot = nestCliConfig?.sourceRoot || "src";
124
+ const appModulePath = path.join(root, sourceRoot, "app.module.ts");
125
+ if (!await fs.pathExists(appModulePath)) {
126
+ errors.push(`app.module.ts not found at ${sourceRoot}/app.module.ts`);
127
+ return this.createInvalidProject(root, errors);
128
+ }
129
+ const mainTsPath = path.join(root, sourceRoot, "main.ts");
130
+ const orm = await detectORM(packageJson);
131
+ const database = detectDatabase(packageJson, orm);
132
+ const authModulePath = path.join(root, sourceRoot, "auth");
133
+ const authExists = await fs.pathExists(authModulePath);
134
+ return {
135
+ authExists,
136
+ root,
137
+ sourceRoot,
138
+ appModulePath,
139
+ mainTsPath,
140
+ packageJsonPath,
141
+ nestCliConfigPath,
142
+ orm,
143
+ database,
144
+ nestVersion: packageJson.dependencies?.["@nestjs/core"],
145
+ typescriptVersion: packageJson.devDependencies?.["typescript"],
146
+ isValid: errors.length === 0,
147
+ errors
148
+ };
149
+ }
150
+ /**
151
+ * Read and parse package.json
152
+ */
153
+ async readPackageJson(packageJsonPath) {
154
+ try {
155
+ const content = await fs.readFile(packageJsonPath, "utf-8");
156
+ return JSON.parse(content);
157
+ } catch (error) {
158
+ return null;
159
+ }
160
+ }
161
+ /**
162
+ * Read and parse nest-cli.json
163
+ */
164
+ async readNestCliConfig(nestCliConfigPath) {
165
+ try {
166
+ if (!await fs.pathExists(nestCliConfigPath)) {
167
+ return null;
168
+ }
169
+ const content = await fs.readFile(nestCliConfigPath, "utf-8");
170
+ return JSON.parse(content);
171
+ } catch (error) {
172
+ return null;
173
+ }
174
+ }
175
+ /**
176
+ * Create invalid project info object
177
+ */
178
+ createInvalidProject(root, errors) {
179
+ return {
180
+ root,
181
+ sourceRoot: "src",
182
+ appModulePath: path.join(root, "src", "app.module.ts"),
183
+ mainTsPath: path.join(root, "src", "main.ts"),
184
+ packageJsonPath: path.join(root, "package.json"),
185
+ nestCliConfigPath: path.join(root, "nest-cli.json"),
186
+ orm: "none",
187
+ isValid: false,
188
+ errors
189
+ };
190
+ }
191
+ };
192
+ }
193
+ });
194
+
195
+ // src/analyzer/index.ts
196
+ var init_analyzer = __esm({
197
+ "src/analyzer/index.ts"() {
198
+ "use strict";
199
+ init_cjs_shims();
200
+ init_project_detector();
201
+ init_orm_detector();
202
+ }
203
+ });
204
+
205
+ // src/config/utils.ts
206
+ function generateSecret(length = 32) {
207
+ return (0, import_crypto.randomBytes)(length).toString("base64");
208
+ }
209
+ var import_crypto;
210
+ var init_utils = __esm({
211
+ "src/config/utils.ts"() {
212
+ "use strict";
213
+ init_cjs_shims();
214
+ import_crypto = require("crypto");
215
+ }
216
+ });
217
+
218
+ // src/cli/prompts.ts
219
+ async function promptConfig(detectedORM, detectedDB) {
220
+ const dbLabel = detectedDB ? ` with ${detectedDB.charAt(0).toUpperCase() + detectedDB.slice(1)}` : "";
221
+ const answers = await import_inquirer.default.prompt([
222
+ {
223
+ type: "list",
224
+ name: "strategy",
225
+ message: "Choose authentication strategy:",
226
+ choices: [
227
+ { name: "JWT Authentication (Recommended)", value: "jwt" },
228
+ { name: "OAuth 2.0 (Google, GitHub) [Coming soon]", value: "oauth", disabled: true },
229
+ { name: "Session-based (Traditional) [Coming soon]", value: "session", disabled: true }
230
+ ],
231
+ default: "jwt"
232
+ },
233
+ {
234
+ type: "confirm",
235
+ name: "enableRBAC",
236
+ message: "Enable Role-Based Access Control (RBAC)?",
237
+ default: true
238
+ },
239
+ {
240
+ type: "checkbox",
241
+ name: "roles",
242
+ message: "Select default roles:",
243
+ choices: [
244
+ { name: "Admin", value: "Admin", checked: true },
245
+ { name: "User", value: "User", checked: true },
246
+ { name: "Moderator", value: "Moderator", checked: false },
247
+ { name: "Guest", value: "Guest", checked: false }
248
+ ],
249
+ when: (answers2) => answers2.enableRBAC,
250
+ validate: (input) => {
251
+ if (input.length === 0) {
252
+ return "Please select at least one role";
253
+ }
254
+ return true;
255
+ }
256
+ },
257
+ {
258
+ type: "confirm",
259
+ name: "refreshTokens",
260
+ message: "Enable Refresh Token rotation?",
261
+ default: true
262
+ },
263
+ {
264
+ type: "list",
265
+ name: "accessExpiration",
266
+ message: "JWT Access Token expiration:",
267
+ choices: [
268
+ { name: "15 minutes", value: "15m" },
269
+ { name: "30 minutes", value: "30m" },
270
+ { name: "1 hour (Recommended)", value: "1h" },
271
+ { name: "4 hours", value: "4h" },
272
+ { name: "1 day", value: "1d" }
273
+ ],
274
+ default: "1h"
275
+ },
276
+ {
277
+ type: "list",
278
+ name: "refreshExpiration",
279
+ message: "JWT Refresh Token expiration:",
280
+ choices: [
281
+ { name: "7 days (Recommended)", value: "7d" },
282
+ { name: "30 days", value: "30d" },
283
+ { name: "90 days", value: "90d" },
284
+ { name: "1 year", value: "1y" }
285
+ ],
286
+ default: "7d",
287
+ when: (answers2) => answers2.refreshTokens
288
+ },
289
+ {
290
+ type: "confirm",
291
+ name: "enableRateLimiting",
292
+ message: "Enable rate limiting on auth endpoints? (recommended)",
293
+ default: true
294
+ },
295
+ {
296
+ type: "confirm",
297
+ name: "useDetectedORM",
298
+ message: `Detected ${detectedORM.toUpperCase()}${dbLabel}. Use it?`,
299
+ default: true,
300
+ when: () => detectedORM !== "none"
301
+ },
302
+ {
303
+ type: "list",
304
+ name: "database",
305
+ message: "Select database:",
306
+ choices: [
307
+ { name: "PostgreSQL (Recommended)", value: "postgres" },
308
+ { name: "MySQL", value: "mysql" },
309
+ { name: "SQLite (for testing)", value: "sqlite" },
310
+ { name: "MongoDB", value: "mongodb" }
311
+ ],
312
+ default: "postgres",
313
+ when: (answers2) => detectedORM === "none" || !answers2.useDetectedORM
314
+ },
315
+ {
316
+ type: "confirm",
317
+ name: "autoInstall",
318
+ message: "Auto-install dependencies after generation?",
319
+ default: true
320
+ }
321
+ ]);
322
+ return answers;
323
+ }
324
+ function getDefaultAnswers(detectedORM, detectedDB) {
325
+ return {
326
+ strategy: "jwt",
327
+ enableRBAC: true,
328
+ roles: ["Admin", "User"],
329
+ refreshTokens: true,
330
+ accessExpiration: "1h",
331
+ refreshExpiration: "7d",
332
+ enableRateLimiting: true,
333
+ useDetectedORM: true,
334
+ database: detectedDB || "postgres",
335
+ autoInstall: true
336
+ };
337
+ }
338
+ function buildConfig(answers, projectName, sourceRoot, detectedORM, detectedDB) {
339
+ const config = {
340
+ projectName,
341
+ sourceRoot,
342
+ strategy: answers.strategy,
343
+ rbac: {
344
+ enabled: answers.enableRBAC,
345
+ roles: answers.roles || []
346
+ },
347
+ orm: answers.useDetectedORM !== false ? detectedORM : "typeorm",
348
+ database: answers.database || detectedDB || "postgres",
349
+ features: {
350
+ refreshTokens: answers.refreshTokens,
351
+ rateLimiting: answers.enableRateLimiting
352
+ },
353
+ jwt: {
354
+ secret: generateSecret(),
355
+ accessExpiration: answers.accessExpiration,
356
+ refreshExpiration: answers.refreshExpiration || "7d"
357
+ },
358
+ autoInstall: answers.autoInstall,
359
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
360
+ generatorVersion: "1.1.0"
361
+ };
362
+ return config;
363
+ }
364
+ var import_inquirer;
365
+ var init_prompts = __esm({
366
+ "src/cli/prompts.ts"() {
367
+ "use strict";
368
+ init_cjs_shims();
369
+ import_inquirer = __toESM(require("inquirer"));
370
+ init_utils();
371
+ }
372
+ });
373
+
374
+ // src/cli/ui.ts
375
+ function getVersion() {
376
+ try {
377
+ const packageJsonPath = (0, import_path.join)(__dirname, "../package.json");
378
+ const packageJson = JSON.parse((0, import_fs.readFileSync)(packageJsonPath, "utf-8"));
379
+ return packageJson.version;
380
+ } catch (error) {
381
+ return "1.0.0";
382
+ }
383
+ }
384
+ function showBanner() {
385
+ console.log(import_chalk.default.cyan(`
386
+ ___ _ _ __ __
387
+ / _ \\ | | | | | \\/ |
388
+ / /_\\ \\_ _ | |_| |__ | \\ / | ___
389
+ | _ | | | || __| '_ \\ | |\\/| |/ _ \\
390
+ | | | | |_| || |_| | | | | | | | __/
391
+ \\_| |_/\\__,_| \\__|_| |_| \\_| |_/\\___|
392
+ `));
393
+ console.log(import_chalk.default.bold(`\u{1F510} NestJS Authentication Module Generator v${getVersion()}`));
394
+ console.log();
395
+ }
396
+ function showProjectInfo(info) {
397
+ console.log(import_chalk.default.green("\u2713"), `Detected NestJS ${info.nestVersion || "project"}`);
398
+ if (info.orm !== "none") {
399
+ console.log(import_chalk.default.green("\u2713"), `Found ${info.orm.toUpperCase()}`);
400
+ }
401
+ console.log(import_chalk.default.green("\u2713"), `Source directory: ${info.sourceRoot}/`);
402
+ console.log(import_chalk.default.green("\u2713"), "No existing auth module found");
403
+ console.log();
404
+ }
405
+ function showError(message, errors) {
406
+ console.log();
407
+ console.log(import_chalk.default.red("\u274C Error:"), import_chalk.default.bold(message));
408
+ if (errors && errors.length > 0) {
409
+ console.log();
410
+ errors.forEach((error) => {
411
+ console.log(import_chalk.default.red(" \u2022"), error);
412
+ });
413
+ }
414
+ console.log();
415
+ }
416
+ function showNestJSHelp() {
417
+ console.log(import_chalk.default.yellow("To create a new NestJS project:"));
418
+ console.log();
419
+ console.log(import_chalk.default.cyan(" npm i -g @nestjs/cli"));
420
+ console.log(import_chalk.default.cyan(" nest new my-project"));
421
+ console.log();
422
+ }
423
+ function showSuccess(stats) {
424
+ console.log();
425
+ console.log(import_chalk.default.green.bold("\u{1F389} Success!"), "Authentication module generated.");
426
+ console.log();
427
+ console.log(import_chalk.default.bold("\u{1F4C1} Files created:"));
428
+ console.log(` \u2022 ${stats.filesCreated} new files in src/auth/ and src/users/`);
429
+ console.log(` \u2022 Updated src/app.module.ts`);
430
+ console.log(` \u2022 Updated package.json`);
431
+ console.log();
432
+ console.log(import_chalk.default.bold("\u{1F4E6} Dependencies added:"));
433
+ console.log(` \u2022 @nestjs/jwt, @nestjs/passport, @nestjs/config`);
434
+ console.log(` \u2022 passport, passport-jwt, passport-local`);
435
+ console.log(` \u2022 bcrypt, class-validator, class-transformer`);
436
+ console.log(` \u2022 ${stats.dependenciesAdded} packages total`);
437
+ console.log();
438
+ console.log(import_chalk.default.bold("\u{1F510} JWT Configuration:"));
439
+ console.log(` \u2022 Access token: ${stats.jwt.accessExpiration}`);
440
+ if (stats.jwt.refreshExpiration) {
441
+ console.log(` \u2022 Refresh token: ${stats.jwt.refreshExpiration}`);
442
+ }
443
+ console.log(` \u2022 Secret: Auto-generated (see .env)`);
444
+ console.log();
445
+ console.log(import_chalk.default.bold("\u{1F4CB} Next steps:"));
446
+ console.log(import_chalk.default.cyan(" 1. Review .env file (auto-generated with secure secret)"));
447
+ console.log(import_chalk.default.gray(" # .env.example is also provided as a git-safe reference"));
448
+ console.log();
449
+ console.log(import_chalk.default.cyan(" 2. Create database migration (if using TypeORM)"));
450
+ console.log(import_chalk.default.gray(" npm run migration:generate -- src/migrations/CreateUserTable"));
451
+ console.log(import_chalk.default.gray(" npm run migration:run"));
452
+ console.log();
453
+ console.log(import_chalk.default.cyan(" 3. Start your NestJS app"));
454
+ console.log(import_chalk.default.gray(" npm run start:dev"));
455
+ console.log();
456
+ console.log(import_chalk.default.cyan(" 4. Test authentication endpoints"));
457
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/register"));
458
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/login"));
459
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/refresh"));
460
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/logout (requires JWT)"));
461
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/logout-all (requires JWT)"));
462
+ console.log(import_chalk.default.gray(" GET http://localhost:3000/users/profile (requires JWT)"));
463
+ console.log();
464
+ console.log(import_chalk.default.bold("\u{1F4D6} Full documentation:"), "src/auth/README.md");
465
+ console.log();
466
+ console.log(import_chalk.default.bold("\u{1F4A1} Tips:"));
467
+ console.log(" \u2022 Use @Public() decorator for routes that don't require auth");
468
+ console.log(" \u2022 Use @Roles('Admin') to restrict routes by role");
469
+ console.log(" \u2022 Access current user with @CurrentUser() decorator");
470
+ console.log();
471
+ }
472
+ function createSpinner(text) {
473
+ return (0, import_ora.default)({
474
+ text,
475
+ color: "cyan"
476
+ });
477
+ }
478
+ var import_chalk, import_ora, import_fs, import_path;
479
+ var init_ui = __esm({
480
+ "src/cli/ui.ts"() {
481
+ "use strict";
482
+ init_cjs_shims();
483
+ import_chalk = __toESM(require("chalk"));
484
+ import_ora = __toESM(require("ora"));
485
+ import_fs = require("fs");
486
+ import_path = require("path");
487
+ }
488
+ });
489
+
39
490
  // src/generator/template-engine.ts
40
491
  var import_handlebars, path2, fs2, TemplateEngine;
41
492
  var init_template_engine = __esm({
@@ -321,6 +772,7 @@ var init_generator = __esm({
321
772
  }
322
773
  plan.push(
323
774
  { template: "shared/env.template.hbs", output: ".env.example" },
775
+ { template: "shared/env.hbs", output: ".env" },
324
776
  { template: "shared/README.auth.md.hbs", output: `${config.sourceRoot}/auth/README.md` },
325
777
  { template: "shared/main.ts.snippet.hbs", output: "main.ts.example" }
326
778
  );
@@ -784,6 +1236,9 @@ var init_package_updater = __esm({
784
1236
  break;
785
1237
  }
786
1238
  }
1239
+ if (config.features.rateLimiting) {
1240
+ dependencies["@nestjs/throttler"] = "^6.0.0";
1241
+ }
787
1242
  return { dependencies, devDependencies };
788
1243
  }
789
1244
  /**
@@ -815,477 +1270,80 @@ var init_package_updater = __esm({
815
1270
  * Clean up backup
816
1271
  */
817
1272
  async cleanupBackup() {
818
- if (this.backupPath && await fs6.pathExists(this.backupPath)) {
819
- await fs6.remove(this.backupPath);
820
- }
821
- }
822
- };
823
- }
824
- });
825
-
826
- // src/installer/dependency-installer.ts
827
- var import_execa, import_detect_package_manager, DependencyInstaller;
828
- var init_dependency_installer = __esm({
829
- "src/installer/dependency-installer.ts"() {
830
- "use strict";
831
- init_cjs_shims();
832
- import_execa = require("execa");
833
- import_detect_package_manager = require("detect-package-manager");
834
- DependencyInstaller = class {
835
- /**
836
- * Install dependencies using the detected package manager
837
- */
838
- async install(cwd) {
839
- const packageManager = await this.detectPackageManager(cwd);
840
- console.log(`\u{1F4E6} Installing dependencies with ${packageManager}...`);
841
- try {
842
- await (0, import_execa.execa)(packageManager, ["install"], {
843
- cwd,
844
- stdio: "inherit"
845
- });
846
- console.log("\u2705 Dependencies installed successfully");
847
- } catch (error) {
848
- throw new Error(
849
- `Failed to install dependencies with ${packageManager}: ${error instanceof Error ? error.message : "Unknown error"}`
850
- );
851
- }
852
- }
853
- /**
854
- * Detect which package manager is being used
855
- */
856
- async detectPackageManager(cwd) {
857
- try {
858
- return await (0, import_detect_package_manager.detect)({ cwd });
859
- } catch (error) {
860
- return "npm";
861
- }
862
- }
863
- };
864
- }
865
- });
866
-
867
- // src/installer/index.ts
868
- var installer_exports = {};
869
- __export(installer_exports, {
870
- AppModuleUpdater: () => AppModuleUpdater,
871
- DependencyInstaller: () => DependencyInstaller,
872
- MainTsUpdater: () => MainTsUpdater,
873
- PackageUpdater: () => PackageUpdater
874
- });
875
- var init_installer = __esm({
876
- "src/installer/index.ts"() {
877
- "use strict";
878
- init_cjs_shims();
879
- init_ast_updater();
880
- init_main_ts_updater();
881
- init_package_updater();
882
- init_dependency_installer();
883
- }
884
- });
885
-
886
- // src/cli.ts
887
- init_cjs_shims();
888
-
889
- // src/index.ts
890
- init_cjs_shims();
891
-
892
- // src/analyzer/index.ts
893
- init_cjs_shims();
894
-
895
- // src/analyzer/project-detector.ts
896
- init_cjs_shims();
897
- var path = __toESM(require("path"));
898
- var fs = __toESM(require("fs-extra"));
899
-
900
- // src/analyzer/orm-detector.ts
901
- init_cjs_shims();
902
- async function detectORM(packageJson) {
903
- const dependencies = {
904
- ...packageJson.dependencies,
905
- ...packageJson.devDependencies
906
- };
907
- if (dependencies["@nestjs/typeorm"] || dependencies["typeorm"]) {
908
- return "typeorm";
909
- }
910
- if (dependencies["@prisma/client"] || dependencies["prisma"]) {
911
- return "prisma";
912
- }
913
- if (dependencies["@nestjs/mongoose"] || dependencies["mongoose"]) {
914
- return "mongoose";
915
- }
916
- return "none";
917
- }
918
- function detectDatabase(packageJson, orm) {
919
- const dependencies = {
920
- ...packageJson.dependencies,
921
- ...packageJson.devDependencies
922
- };
923
- if (orm === "typeorm") {
924
- if (dependencies["pg"]) return "postgres";
925
- if (dependencies["mysql2"] || dependencies["mysql"]) return "mysql";
926
- if (dependencies["sqlite3"]) return "sqlite";
927
- if (dependencies["mongodb"]) return "mongodb";
928
- }
929
- if (orm === "prisma") {
930
- return void 0;
931
- }
932
- if (orm === "mongoose") {
933
- return "mongodb";
934
- }
935
- return void 0;
936
- }
937
-
938
- // src/analyzer/project-detector.ts
939
- var ProjectDetector = class {
940
- constructor(cwd) {
941
- this.cwd = cwd;
942
- }
943
- /**
944
- * Detect and validate a NestJS project
945
- */
946
- async detectProject() {
947
- const errors = [];
948
- const root = this.cwd;
949
- const packageJsonPath = path.join(root, "package.json");
950
- if (!await fs.pathExists(packageJsonPath)) {
951
- errors.push("package.json not found");
952
- return this.createInvalidProject(root, errors);
953
- }
954
- const packageJson = await this.readPackageJson(packageJsonPath);
955
- if (!packageJson) {
956
- errors.push("Failed to read package.json");
957
- return this.createInvalidProject(root, errors);
958
- }
959
- const hasNestCore = packageJson.dependencies?.["@nestjs/core"];
960
- const hasNestCommon = packageJson.dependencies?.["@nestjs/common"];
961
- if (!hasNestCore || !hasNestCommon) {
962
- errors.push("Not a NestJS project (missing @nestjs/core or @nestjs/common)");
963
- return this.createInvalidProject(root, errors);
964
- }
965
- const nestCliConfigPath = path.join(root, "nest-cli.json");
966
- const nestCliConfig = await this.readNestCliConfig(nestCliConfigPath);
967
- const sourceRoot = nestCliConfig?.sourceRoot || "src";
968
- const appModulePath = path.join(root, sourceRoot, "app.module.ts");
969
- if (!await fs.pathExists(appModulePath)) {
970
- errors.push(`app.module.ts not found at ${sourceRoot}/app.module.ts`);
971
- return this.createInvalidProject(root, errors);
972
- }
973
- const mainTsPath = path.join(root, sourceRoot, "main.ts");
974
- const orm = await detectORM(packageJson);
975
- const database = detectDatabase(packageJson, orm);
976
- const authModulePath = path.join(root, sourceRoot, "auth");
977
- const authExists = await fs.pathExists(authModulePath);
978
- return {
979
- authExists,
980
- root,
981
- sourceRoot,
982
- appModulePath,
983
- mainTsPath,
984
- packageJsonPath,
985
- nestCliConfigPath,
986
- orm,
987
- database,
988
- nestVersion: packageJson.dependencies?.["@nestjs/core"],
989
- typescriptVersion: packageJson.devDependencies?.["typescript"],
990
- isValid: errors.length === 0,
991
- errors
992
- };
993
- }
994
- /**
995
- * Read and parse package.json
996
- */
997
- async readPackageJson(packageJsonPath) {
998
- try {
999
- const content = await fs.readFile(packageJsonPath, "utf-8");
1000
- return JSON.parse(content);
1001
- } catch (error) {
1002
- return null;
1003
- }
1004
- }
1005
- /**
1006
- * Read and parse nest-cli.json
1007
- */
1008
- async readNestCliConfig(nestCliConfigPath) {
1009
- try {
1010
- if (!await fs.pathExists(nestCliConfigPath)) {
1011
- return null;
1012
- }
1013
- const content = await fs.readFile(nestCliConfigPath, "utf-8");
1014
- return JSON.parse(content);
1015
- } catch (error) {
1016
- return null;
1017
- }
1018
- }
1019
- /**
1020
- * Create invalid project info object
1021
- */
1022
- createInvalidProject(root, errors) {
1023
- return {
1024
- root,
1025
- sourceRoot: "src",
1026
- appModulePath: path.join(root, "src", "app.module.ts"),
1027
- mainTsPath: path.join(root, "src", "main.ts"),
1028
- packageJsonPath: path.join(root, "package.json"),
1029
- nestCliConfigPath: path.join(root, "nest-cli.json"),
1030
- orm: "none",
1031
- isValid: false,
1032
- errors
1033
- };
1034
- }
1035
- };
1036
- async function detectProject(cwd = process.cwd()) {
1037
- const detector = new ProjectDetector(cwd);
1038
- return detector.detectProject();
1039
- }
1040
-
1041
- // src/cli/prompts.ts
1042
- init_cjs_shims();
1043
- var import_inquirer = __toESM(require("inquirer"));
1044
-
1045
- // src/config/utils.ts
1046
- init_cjs_shims();
1047
- var import_crypto = require("crypto");
1048
- function generateSecret(length = 32) {
1049
- return (0, import_crypto.randomBytes)(length).toString("base64");
1050
- }
1051
-
1052
- // src/cli/prompts.ts
1053
- async function promptConfig(detectedORM, detectedDB) {
1054
- const dbLabel = detectedDB ? ` with ${detectedDB.charAt(0).toUpperCase() + detectedDB.slice(1)}` : "";
1055
- const answers = await import_inquirer.default.prompt([
1056
- {
1057
- type: "list",
1058
- name: "strategy",
1059
- message: "Choose authentication strategy:",
1060
- choices: [
1061
- { name: "JWT Authentication (Recommended)", value: "jwt" },
1062
- { name: "OAuth 2.0 (Google, GitHub) [v1.1]", value: "oauth", disabled: true },
1063
- { name: "Session-based (Traditional) [v1.2]", value: "session", disabled: true }
1064
- ],
1065
- default: "jwt"
1066
- },
1067
- {
1068
- type: "confirm",
1069
- name: "enableRBAC",
1070
- message: "Enable Role-Based Access Control (RBAC)?",
1071
- default: true
1072
- },
1073
- {
1074
- type: "checkbox",
1075
- name: "roles",
1076
- message: "Select default roles:",
1077
- choices: [
1078
- { name: "Admin", value: "Admin", checked: true },
1079
- { name: "User", value: "User", checked: true },
1080
- { name: "Moderator", value: "Moderator", checked: false },
1081
- { name: "Guest", value: "Guest", checked: false }
1082
- ],
1083
- when: (answers2) => answers2.enableRBAC,
1084
- validate: (input) => {
1085
- if (input.length === 0) {
1086
- return "Please select at least one role";
1273
+ if (this.backupPath && await fs6.pathExists(this.backupPath)) {
1274
+ await fs6.remove(this.backupPath);
1087
1275
  }
1088
- return true;
1089
1276
  }
1090
- },
1091
- {
1092
- type: "confirm",
1093
- name: "refreshTokens",
1094
- message: "Enable Refresh Token rotation?",
1095
- default: true
1096
- },
1097
- {
1098
- type: "list",
1099
- name: "accessExpiration",
1100
- message: "JWT Access Token expiration:",
1101
- choices: [
1102
- { name: "15 minutes", value: "15m" },
1103
- { name: "30 minutes", value: "30m" },
1104
- { name: "1 hour (Recommended)", value: "1h" },
1105
- { name: "4 hours", value: "4h" },
1106
- { name: "1 day", value: "1d" }
1107
- ],
1108
- default: "1h"
1109
- },
1110
- {
1111
- type: "list",
1112
- name: "refreshExpiration",
1113
- message: "JWT Refresh Token expiration:",
1114
- choices: [
1115
- { name: "7 days (Recommended)", value: "7d" },
1116
- { name: "30 days", value: "30d" },
1117
- { name: "90 days", value: "90d" },
1118
- { name: "1 year", value: "1y" }
1119
- ],
1120
- default: "7d",
1121
- when: (answers2) => answers2.refreshTokens
1122
- },
1123
- {
1124
- type: "confirm",
1125
- name: "useDetectedORM",
1126
- message: `Detected ${detectedORM.toUpperCase()}${dbLabel}. Use it?`,
1127
- default: true,
1128
- when: () => detectedORM !== "none"
1129
- },
1130
- {
1131
- type: "list",
1132
- name: "database",
1133
- message: "Select database:",
1134
- choices: [
1135
- { name: "PostgreSQL (Recommended)", value: "postgres" },
1136
- { name: "MySQL", value: "mysql" },
1137
- { name: "SQLite (for testing)", value: "sqlite" },
1138
- { name: "MongoDB", value: "mongodb" }
1139
- ],
1140
- default: "postgres",
1141
- when: (answers2) => detectedORM === "none" || !answers2.useDetectedORM
1142
- },
1143
- {
1144
- type: "confirm",
1145
- name: "autoInstall",
1146
- message: "Auto-install dependencies after generation?",
1147
- default: true
1148
- }
1149
- ]);
1150
- return answers;
1151
- }
1152
- function buildConfig(answers, projectName, sourceRoot, detectedORM, detectedDB) {
1153
- const config = {
1154
- projectName,
1155
- sourceRoot,
1156
- strategy: answers.strategy,
1157
- rbac: {
1158
- enabled: answers.enableRBAC,
1159
- roles: answers.roles || []
1160
- },
1161
- orm: answers.useDetectedORM !== false ? detectedORM : "typeorm",
1162
- database: answers.database || detectedDB || "postgres",
1163
- features: {
1164
- refreshTokens: answers.refreshTokens
1165
- },
1166
- jwt: {
1167
- secret: generateSecret(),
1168
- accessExpiration: answers.accessExpiration,
1169
- refreshExpiration: answers.refreshExpiration || "7d"
1170
- },
1171
- autoInstall: answers.autoInstall,
1172
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1173
- generatorVersion: "1.0.0"
1174
- };
1175
- return config;
1176
- }
1177
-
1178
- // src/cli/ui.ts
1179
- init_cjs_shims();
1180
- var import_chalk = __toESM(require("chalk"));
1181
- var import_ora = __toESM(require("ora"));
1182
- var import_fs = require("fs");
1183
- var import_path = require("path");
1184
- function getVersion() {
1185
- try {
1186
- const packageJsonPath = (0, import_path.join)(__dirname, "../package.json");
1187
- const packageJson = JSON.parse((0, import_fs.readFileSync)(packageJsonPath, "utf-8"));
1188
- return packageJson.version;
1189
- } catch (error) {
1190
- return "1.0.0";
1191
- }
1192
- }
1193
- function showBanner() {
1194
- console.log(import_chalk.default.cyan(`
1195
- ___ _ _ __ __
1196
- / _ \\ | | | | | \\/ |
1197
- / /_\\ \\_ _ | |_| |__ | \\ / | ___
1198
- | _ | | | || __| '_ \\ | |\\/| |/ _ \\
1199
- | | | | |_| || |_| | | | | | | | __/
1200
- \\_| |_/\\__,_| \\__|_| |_| \\_| |_/\\___|
1201
- `));
1202
- console.log(import_chalk.default.bold(`\u{1F510} NestJS Authentication Module Generator v${getVersion()}`));
1203
- console.log();
1204
- }
1205
- function showProjectInfo(info) {
1206
- console.log(import_chalk.default.green("\u2713"), `Detected NestJS ${info.nestVersion || "project"}`);
1207
- if (info.orm !== "none") {
1208
- console.log(import_chalk.default.green("\u2713"), `Found ${info.orm.toUpperCase()}`);
1277
+ };
1209
1278
  }
1210
- console.log(import_chalk.default.green("\u2713"), `Source directory: ${info.sourceRoot}/`);
1211
- console.log(import_chalk.default.green("\u2713"), "No existing auth module found");
1212
- console.log();
1213
- }
1214
- function showError(message, errors) {
1215
- console.log();
1216
- console.log(import_chalk.default.red("\u274C Error:"), import_chalk.default.bold(message));
1217
- if (errors && errors.length > 0) {
1218
- console.log();
1219
- errors.forEach((error) => {
1220
- console.log(import_chalk.default.red(" \u2022"), error);
1221
- });
1279
+ });
1280
+
1281
+ // src/installer/dependency-installer.ts
1282
+ var import_execa, import_detect_package_manager, DependencyInstaller;
1283
+ var init_dependency_installer = __esm({
1284
+ "src/installer/dependency-installer.ts"() {
1285
+ "use strict";
1286
+ init_cjs_shims();
1287
+ import_execa = require("execa");
1288
+ import_detect_package_manager = require("detect-package-manager");
1289
+ DependencyInstaller = class {
1290
+ /**
1291
+ * Install dependencies using the detected package manager
1292
+ */
1293
+ async install(cwd) {
1294
+ const packageManager = await this.detectPackageManager(cwd);
1295
+ console.log(`\u{1F4E6} Installing dependencies with ${packageManager}...`);
1296
+ try {
1297
+ await (0, import_execa.execa)(packageManager, ["install"], {
1298
+ cwd,
1299
+ stdio: "inherit"
1300
+ });
1301
+ console.log("\u2705 Dependencies installed successfully");
1302
+ } catch (error) {
1303
+ throw new Error(
1304
+ `Failed to install dependencies with ${packageManager}: ${error instanceof Error ? error.message : "Unknown error"}`
1305
+ );
1306
+ }
1307
+ }
1308
+ /**
1309
+ * Detect which package manager is being used
1310
+ */
1311
+ async detectPackageManager(cwd) {
1312
+ try {
1313
+ return await (0, import_detect_package_manager.detect)({ cwd });
1314
+ } catch (error) {
1315
+ return "npm";
1316
+ }
1317
+ }
1318
+ };
1222
1319
  }
1223
- console.log();
1224
- }
1225
- function showNestJSHelp() {
1226
- console.log(import_chalk.default.yellow("To create a new NestJS project:"));
1227
- console.log();
1228
- console.log(import_chalk.default.cyan(" npm i -g @nestjs/cli"));
1229
- console.log(import_chalk.default.cyan(" nest new my-project"));
1230
- console.log();
1231
- }
1232
- function showSuccess(stats) {
1233
- console.log();
1234
- console.log(import_chalk.default.green.bold("\u{1F389} Success!"), "Authentication module generated.");
1235
- console.log();
1236
- console.log(import_chalk.default.bold("\u{1F4C1} Files created:"));
1237
- console.log(` \u2022 ${stats.filesCreated} new files in src/auth/ and src/users/`);
1238
- console.log(` \u2022 Updated src/app.module.ts`);
1239
- console.log(` \u2022 Updated package.json`);
1240
- console.log();
1241
- console.log(import_chalk.default.bold("\u{1F4E6} Dependencies added:"));
1242
- console.log(` \u2022 @nestjs/jwt, @nestjs/passport, @nestjs/config`);
1243
- console.log(` \u2022 passport, passport-jwt, passport-local`);
1244
- console.log(` \u2022 bcrypt, class-validator, class-transformer`);
1245
- console.log(` \u2022 ${stats.dependenciesAdded} packages total`);
1246
- console.log();
1247
- console.log(import_chalk.default.bold("\u{1F510} JWT Configuration:"));
1248
- console.log(` \u2022 Access token: ${stats.jwt.accessExpiration}`);
1249
- if (stats.jwt.refreshExpiration) {
1250
- console.log(` \u2022 Refresh token: ${stats.jwt.refreshExpiration}`);
1320
+ });
1321
+
1322
+ // src/installer/index.ts
1323
+ var installer_exports = {};
1324
+ __export(installer_exports, {
1325
+ AppModuleUpdater: () => AppModuleUpdater,
1326
+ DependencyInstaller: () => DependencyInstaller,
1327
+ MainTsUpdater: () => MainTsUpdater,
1328
+ PackageUpdater: () => PackageUpdater
1329
+ });
1330
+ var init_installer = __esm({
1331
+ "src/installer/index.ts"() {
1332
+ "use strict";
1333
+ init_cjs_shims();
1334
+ init_ast_updater();
1335
+ init_main_ts_updater();
1336
+ init_package_updater();
1337
+ init_dependency_installer();
1251
1338
  }
1252
- console.log(` \u2022 Secret: Auto-generated (see .env.example)`);
1253
- console.log();
1254
- console.log(import_chalk.default.bold("\u{1F4CB} Next steps:"));
1255
- console.log(import_chalk.default.cyan(" 1. Copy .env.example to .env"));
1256
- console.log(import_chalk.default.gray(" cp .env.example .env"));
1257
- console.log();
1258
- console.log(import_chalk.default.cyan(" 2. Update JWT_SECRET in .env (or keep auto-generated)"));
1259
- console.log();
1260
- console.log(import_chalk.default.cyan(" 3. Create database migration (if using TypeORM)"));
1261
- console.log(import_chalk.default.gray(" npm run migration:generate -- src/migrations/CreateUserTable"));
1262
- console.log(import_chalk.default.gray(" npm run migration:run"));
1263
- console.log();
1264
- console.log(import_chalk.default.cyan(" 4. Start your NestJS app"));
1265
- console.log(import_chalk.default.gray(" npm run start:dev"));
1266
- console.log();
1267
- console.log(import_chalk.default.cyan(" 5. Test authentication endpoints"));
1268
- console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/register"));
1269
- console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/login"));
1270
- console.log(import_chalk.default.gray(" GET http://localhost:3000/users/profile (requires JWT)"));
1271
- console.log();
1272
- console.log(import_chalk.default.bold("\u{1F4D6} Full documentation:"), "src/auth/README.md");
1273
- console.log();
1274
- console.log(import_chalk.default.bold("\u{1F4A1} Tips:"));
1275
- console.log(" \u2022 Use @Public() decorator for routes that don't require auth");
1276
- console.log(" \u2022 Use @Roles('Admin') to restrict routes by role");
1277
- console.log(" \u2022 Access current user with @CurrentUser() decorator");
1278
- console.log();
1279
- }
1280
- function createSpinner(text) {
1281
- return (0, import_ora.default)({
1282
- text,
1283
- color: "cyan"
1284
- });
1285
- }
1339
+ });
1286
1340
 
1287
1341
  // src/index.ts
1288
- async function run(cwd = process.cwd()) {
1342
+ var index_exports = {};
1343
+ __export(index_exports, {
1344
+ run: () => run
1345
+ });
1346
+ async function run(cwd = process.cwd(), options = {}) {
1289
1347
  showBanner();
1290
1348
  const spinner = createSpinner("Analyzing project...").start();
1291
1349
  const projectInfo = await detectProject(cwd);
@@ -1302,6 +1360,10 @@ async function run(cwd = process.cwd()) {
1302
1360
  sourceRoot: projectInfo.sourceRoot
1303
1361
  });
1304
1362
  if (projectInfo.authExists) {
1363
+ if (options.yes) {
1364
+ console.log("\n auth/ directory already exists. Use interactive mode to overwrite.\n");
1365
+ process.exit(0);
1366
+ }
1305
1367
  const inquirer2 = (await import("inquirer")).default;
1306
1368
  const { overwrite } = await inquirer2.prompt([{
1307
1369
  type: "confirm",
@@ -1314,7 +1376,7 @@ async function run(cwd = process.cwd()) {
1314
1376
  process.exit(0);
1315
1377
  }
1316
1378
  }
1317
- const answers = await promptConfig(projectInfo.orm, projectInfo.database);
1379
+ const answers = options.yes ? getDefaultAnswers(projectInfo.orm, projectInfo.database) : await promptConfig(projectInfo.orm, projectInfo.database);
1318
1380
  const config = buildConfig(
1319
1381
  answers,
1320
1382
  projectInfo.root.split(/[/\\]/).pop() || "project",
@@ -1404,14 +1466,32 @@ async function run(cwd = process.cwd()) {
1404
1466
  console.log("\u2B50 Like it? https://github.com/Islamawad132/add-nest-auth");
1405
1467
  console.log();
1406
1468
  }
1469
+ var init_index = __esm({
1470
+ "src/index.ts"() {
1471
+ "use strict";
1472
+ init_cjs_shims();
1473
+ init_analyzer();
1474
+ init_prompts();
1475
+ init_ui();
1476
+ }
1477
+ });
1407
1478
 
1408
1479
  // src/cli.ts
1480
+ init_cjs_shims();
1481
+ var import_commander = require("commander");
1409
1482
  process.on("unhandledRejection", (error) => {
1410
1483
  console.error("Unhandled rejection:", error);
1411
1484
  process.exit(1);
1412
1485
  });
1413
- run().catch((error) => {
1414
- console.error("Fatal error:", error);
1415
- process.exit(1);
1486
+ var program = new import_commander.Command();
1487
+ program.name("add-nest-auth").description("Add production-ready authentication to any NestJS project").version("1.1.0").option("-y, --yes", "Skip all prompts and use sensible defaults").action(async (options) => {
1488
+ try {
1489
+ const { run: run2 } = await Promise.resolve().then(() => (init_index(), index_exports));
1490
+ await run2(process.cwd(), { yes: options.yes || false });
1491
+ } catch (error) {
1492
+ console.error("Fatal error:", error);
1493
+ process.exit(1);
1494
+ }
1416
1495
  });
1496
+ program.parse();
1417
1497
  //# sourceMappingURL=cli.js.map