speedrun-cli 2.6.12 → 2.7.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +1 -1
  3. package/src/generator.js +1 -16
  4. package/src/moduleGenerator.js +597 -121
  5. package/src/postSetup.js +423 -402
  6. package/src/prompts.js +25 -3
  7. package/templates/orm/drizzle/package.json +2 -1
  8. package/templates/orm/mongoose/package.json +2 -1
  9. package/templates/orm/prisma/package.json +1 -0
  10. package/templates/base-crud/src/modules/products/dto/create-product.dto.ts +0 -34
  11. package/templates/base-crud/src/modules/products/dto/product.dto.ts +0 -36
  12. package/templates/base-crud/src/modules/products/dto/update-product.dto.ts +0 -4
  13. package/templates/base-crud/src/modules/products/products.controller.ts +0 -78
  14. package/templates/base-crud-drizzle/src/modules/products/dto/create-product.dto.ts +0 -34
  15. package/templates/base-crud-drizzle/src/modules/products/dto/product.dto.ts +0 -36
  16. package/templates/base-crud-drizzle/src/modules/products/dto/update-product.dto.ts +0 -4
  17. package/templates/base-crud-drizzle/src/modules/products/products.controller.ts +0 -78
  18. package/templates/base-crud-drizzle/src/modules/products/products.module.ts +0 -24
  19. package/templates/base-crud-drizzle/src/modules/products/products.service.ts +0 -140
  20. package/templates/base-crud-drizzle/src/modules/products/schema/products.schema.ts +0 -39
  21. package/templates/base-crud-mongoose/src/modules/products/dto/create-product.dto.ts +0 -34
  22. package/templates/base-crud-mongoose/src/modules/products/dto/product.dto.ts +0 -36
  23. package/templates/base-crud-mongoose/src/modules/products/dto/update-product.dto.ts +0 -4
  24. package/templates/base-crud-mongoose/src/modules/products/products.controller.ts +0 -78
  25. package/templates/base-crud-mongoose/src/modules/products/products.module.ts +0 -26
  26. package/templates/base-crud-mongoose/src/modules/products/products.service.ts +0 -133
  27. package/templates/base-crud-mongoose/src/modules/products/schemas/product.schema.ts +0 -67
  28. package/templates/base-crud-prisma/src/modules/products/dto/create-product.dto.ts +0 -34
  29. package/templates/base-crud-prisma/src/modules/products/dto/product.dto.ts +0 -36
  30. package/templates/base-crud-prisma/src/modules/products/dto/update-product.dto.ts +0 -4
  31. package/templates/base-crud-prisma/src/modules/products/products.controller.ts +0 -78
  32. package/templates/base-crud-prisma/src/modules/products/products.module.ts +0 -12
  33. package/templates/base-crud-prisma/src/modules/products/products.service.ts +0 -100
  34. package/templates/base-crud-typeorm/src/modules/products/dto/create-product.dto.ts +0 -34
  35. package/templates/base-crud-typeorm/src/modules/products/dto/product.dto.ts +0 -36
  36. package/templates/base-crud-typeorm/src/modules/products/dto/update-product.dto.ts +0 -4
  37. package/templates/base-crud-typeorm/src/modules/products/entities/product.entity.ts +0 -58
  38. package/templates/base-crud-typeorm/src/modules/products/products.controller.ts +0 -78
  39. package/templates/base-crud-typeorm/src/modules/products/products.module.ts +0 -25
  40. package/templates/base-crud-typeorm/src/modules/products/products.service.ts +0 -102
package/src/postSetup.js CHANGED
@@ -1,402 +1,423 @@
1
- /**
2
- * Post-Setup Interactive Handlers
3
- * @module postSetup
4
- */
5
-
6
- const fs = require('fs-extra');
7
- const path = require('path');
8
- const { execSync } = require('child_process');
9
- const chalk = require('chalk');
10
- const inquirer = require('inquirer');
11
- const { ORM_OPTIONS, DATABASE_OPTIONS } = require('./constants');
12
- const { generateJWTSecret, getRunPrefix } = require('./utils');
13
- // Lazy-loaded to avoid circular deps: moduleGenerator requires constants/utils
14
- let _generateModule;
15
- function getGenerateModule() {
16
- if (!_generateModule) _generateModule = require('./moduleGenerator').generateModule;
17
- return _generateModule;
18
- }
19
-
20
- /**
21
- * Handles interactive post-setup configuration
22
- * @param {string} targetDir - Project directory
23
- * @param {string} appName - Application name
24
- * @param {object} options - Configuration options
25
- * @returns {Promise<boolean>} Whether interactive setup completed
26
- */
27
- async function handlePostSetup(targetDir, appName, options) {
28
- const { packageManager, orm, database, swagger, baseCrud, yes: isYesMode, skipInstall } = options;
29
-
30
- if (isYesMode || skipInstall) {
31
- return false;
32
- }
33
-
34
- printSuccessHeader(appName, orm, database, swagger, baseCrud);
35
-
36
- const { continueSetup } = await inquirer.prompt([{
37
- type: 'confirm',
38
- name: 'continueSetup',
39
- message: 'Would you like to complete the setup now? (JWT secrets, database, CRUD)',
40
- default: true,
41
- }]);
42
-
43
- if (!continueSetup) {
44
- return false;
45
- }
46
-
47
- // Step 1: Configure JWT secrets and database URL
48
- await configureEnvironment(targetDir, database);
49
-
50
- // Step 2: ORM-specific database setup (Schema/Migration → Seed)
51
- await setupDatabase(targetDir, orm, packageManager);
52
-
53
- // Step 3: CRUD module generation (always offered when setup is accepted)
54
- await promptCrudGeneration(targetDir, orm);
55
-
56
- // Step 4: Optionally start dev server
57
- await promptDevServer(targetDir, packageManager);
58
-
59
- return true;
60
- }
61
-
62
- /**
63
- * Prints the success header
64
- */
65
- function printSuccessHeader(appName, orm, database, swagger, baseCrud) {
66
- console.log(chalk.green('\n✅ Success! Created ' + chalk.bold(appName)));
67
- console.log(chalk.gray(` ORM: ${ORM_OPTIONS[orm]?.name || orm}`));
68
- console.log(chalk.gray(` Database: ${DATABASE_OPTIONS[database]?.name || database}`));
69
- if (swagger) {
70
- console.log(chalk.gray(` Swagger: ${chalk.green('Enabled')}`));
71
- }
72
- if (baseCrud) {
73
- console.log(chalk.gray(` Base CRUD Architecture: ${chalk.green('Enabled')} src/common/base/`));
74
- }
75
- console.log(chalk.white('\n🎉 Your project is ready!\n'));
76
- }
77
-
78
- /**
79
- * Configures environment variables
80
- */
81
- async function configureEnvironment(targetDir, database) {
82
- console.log(chalk.yellow('\n🔑 Generating JWT secrets...\n'));
83
- const accessSecret = generateJWTSecret();
84
- const refreshSecret = generateJWTSecret();
85
-
86
- console.log(chalk.gray(' Generated JWT_ACCESS_SECRET'));
87
- console.log(chalk.gray(' Generated JWT_REFRESH_SECRET\n'));
88
-
89
- const dbInfo = DATABASE_OPTIONS[database];
90
- const { databaseUrl } = await inquirer.prompt([{
91
- type: 'input',
92
- name: 'databaseUrl',
93
- message: `Enter your ${dbInfo.name} database URL:`,
94
- default: dbInfo.urlTemplate,
95
- validate: (input) => {
96
- if (!input || input.trim() === '') {
97
- return 'Database URL is required';
98
- }
99
- const validPrefix = dbInfo.urlPrefix.some((prefix) => input.startsWith(prefix));
100
- if (!validPrefix) {
101
- return `Database URL must start with ${dbInfo.urlPrefix.join(' or ')}`;
102
- }
103
- return true;
104
- },
105
- }]);
106
-
107
- // Update .env file
108
- console.log(chalk.gray('\n Updating .env file...'));
109
- const envPath = path.join(targetDir, '.env');
110
-
111
- try {
112
- let envContent = await fs.readFile(envPath, 'utf8');
113
- envContent = envContent.replace(/DATABASE_URL=.*/, `DATABASE_URL="${databaseUrl}"`);
114
- envContent = envContent.replace(/JWT_ACCESS_SECRET=.*/, `JWT_ACCESS_SECRET="${accessSecret}"`);
115
- envContent = envContent.replace(/JWT_REFRESH_SECRET=.*/, `JWT_REFRESH_SECRET="${refreshSecret}"`);
116
- await fs.writeFile(envPath, envContent);
117
- console.log(chalk.green(' ✓ Environment variables configured\n'));
118
- } catch {
119
- console.error(chalk.red(' ✗ Failed to update .env file'));
120
- }
121
- }
122
-
123
- /**
124
- * Sets up the database based on selected ORM
125
- */
126
- async function setupDatabase(targetDir, orm, packageManager) {
127
- const setupHandlers = {
128
- prisma: setupPrisma,
129
- typeorm: setupTypeOrm,
130
- mongoose: setupMongoose,
131
- drizzle: setupDrizzle,
132
- };
133
-
134
- const handler = setupHandlers[orm];
135
- if (handler) {
136
- await handler(targetDir, packageManager);
137
- }
138
- }
139
-
140
- async function setupPrisma(targetDir, packageManager) {
141
- const { setupDatabase } = await inquirer.prompt([{
142
- type: 'confirm',
143
- name: 'setupDatabase',
144
- message: 'Set up the database now? (generate Prisma client, run migrations, seed)',
145
- default: true,
146
- }]);
147
-
148
- if (!setupDatabase) return;
149
-
150
- // Prompt for migration name before running prisma commands
151
- const { migrationName } = await inquirer.prompt([{
152
- type: 'input',
153
- name: 'migrationName',
154
- message: 'Enter migration name:',
155
- default: 'init',
156
- validate: (input) => {
157
- if (!input || input.trim() === '') {
158
- return 'Migration name is required';
159
- }
160
- if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
161
- return 'Migration name can only contain letters, numbers, underscores, and hyphens';
162
- }
163
- return true;
164
- },
165
- }]);
166
-
167
- console.log(chalk.yellow('\n📦 Setting up database...\n'));
168
- const pmPrefix = getRunPrefix(packageManager);
169
-
170
- try {
171
- console.log(chalk.gray(' Generating Prisma client...'));
172
- execSync(`${pmPrefix} prisma:generate`, { cwd: targetDir, stdio: 'inherit' });
173
-
174
- console.log(chalk.gray('\n Running database migrations...'));
175
- // Run prisma directly with --name to avoid double prompt
176
- execSync(`npx prisma migrate dev --name ${migrationName}`, { cwd: targetDir, stdio: 'inherit' });
177
-
178
- console.log(chalk.gray('\n Seeding database...'));
179
- execSync(`${pmPrefix} prisma:seed`, { cwd: targetDir, stdio: 'inherit' });
180
-
181
- printCredentials();
182
- } catch {
183
- console.error(chalk.red('\n ✗ Database setup failed'));
184
- console.error(chalk.yellow(' Run these commands manually:'));
185
- console.error(chalk.gray(` ${pmPrefix} prisma:generate`));
186
- console.error(chalk.gray(` ${pmPrefix} prisma:migrate`));
187
- console.error(chalk.gray(` ${pmPrefix} prisma:seed\n`));
188
- }
189
- }
190
-
191
- async function setupTypeOrm(targetDir, packageManager) {
192
- const { setupDatabase } = await inquirer.prompt([{
193
- type: 'confirm',
194
- name: 'setupDatabase',
195
- message: 'Set up the database now? (sync schema, seed)',
196
- default: true,
197
- }]);
198
-
199
- if (!setupDatabase) return;
200
-
201
- console.log(chalk.yellow('\n📦 Setting up database...\n'));
202
- const pmPrefix = getRunPrefix(packageManager);
203
-
204
- try {
205
- console.log(chalk.gray(' Synchronizing database schema...'));
206
- execSync(`${pmPrefix} schema:sync`, { cwd: targetDir, stdio: 'inherit' });
207
-
208
- console.log(chalk.gray('\n Seeding database...'));
209
- execSync(`${pmPrefix} seed`, { cwd: targetDir, stdio: 'inherit' });
210
-
211
- printCredentials();
212
- } catch {
213
- console.error(chalk.red('\n ✗ Database setup failed'));
214
- console.error(chalk.yellow(' Run these commands manually:'));
215
- console.error(chalk.gray(` ${pmPrefix} schema:sync`));
216
- console.error(chalk.gray(` ${pmPrefix} seed\n`));
217
- }
218
- }
219
-
220
- async function setupMongoose(targetDir, packageManager) {
221
- const { setupDatabase } = await inquirer.prompt([{
222
- type: 'confirm',
223
- name: 'setupDatabase',
224
- message: 'Seed the database now? (create default admin user)',
225
- default: true,
226
- }]);
227
-
228
- if (!setupDatabase) return;
229
-
230
- console.log(chalk.yellow('\n📦 Setting up database...\n'));
231
- const pmPrefix = getRunPrefix(packageManager);
232
-
233
- try {
234
- console.log(chalk.gray(' Seeding database...'));
235
- execSync(`${pmPrefix} db:seed`, { cwd: targetDir, stdio: 'inherit' });
236
-
237
- printCredentials();
238
- } catch {
239
- console.error(chalk.red('\n ✗ Database setup failed'));
240
- console.error(chalk.yellow(' Run this command manually:'));
241
- console.error(chalk.gray(` ${pmPrefix} db:seed\n`));
242
- }
243
- }
244
-
245
- async function setupDrizzle(targetDir, packageManager) {
246
- const { setupDatabase } = await inquirer.prompt([{
247
- type: 'confirm',
248
- name: 'setupDatabase',
249
- message: 'Set up the database now? (push schema, seed)',
250
- default: true,
251
- }]);
252
-
253
- if (!setupDatabase) return;
254
-
255
- console.log(chalk.yellow('\n📦 Setting up database...\n'));
256
- const pmPrefix = getRunPrefix(packageManager);
257
-
258
- try {
259
- console.log(chalk.gray(' Pushing schema to database...'));
260
- execSync(`${pmPrefix} db:push`, { cwd: targetDir, stdio: 'inherit' });
261
-
262
- console.log(chalk.gray('\n Seeding database...'));
263
- execSync(`${pmPrefix} db:seed`, { cwd: targetDir, stdio: 'inherit' });
264
-
265
- printCredentials();
266
- } catch {
267
- console.error(chalk.red('\n ✗ Database setup failed'));
268
- console.error(chalk.yellow(' Run these commands manually:'));
269
- console.error(chalk.gray(` ${pmPrefix} db:push`));
270
- console.error(chalk.gray(` ${pmPrefix} db:seed\n`));
271
- }
272
- }
273
-
274
- /**
275
- * Prints default admin credentials
276
- */
277
- function printCredentials() {
278
- console.log(chalk.green('\n ✓ Database setup complete!\n'));
279
- console.log(chalk.cyan(' 📝 Default admin credentials:'));
280
- console.log(chalk.white(' Email: admin@example.com'));
281
- console.log(chalk.white(' Password: Admin@123\n'));
282
- }
283
-
284
- /**
285
- * Prompts user to generate their first CRUD module
286
- */
287
- async function promptCrudGeneration(targetDir, orm) {
288
- const { generateNow } = await inquirer.prompt([{
289
- type: 'confirm',
290
- name: 'generateNow',
291
- message: 'Do you want to generate your first CRUD module now?',
292
- default: true,
293
- }]);
294
-
295
- if (!generateNow) {
296
- console.log(chalk.gray('\n Skipping CRUD generation. Run `speedrun-cli generate <name>` anytime.\n'));
297
- return;
298
- }
299
-
300
- try {
301
- await getGenerateModule()(undefined, targetDir, orm);
302
- } catch (err) {
303
- console.warn(chalk.yellow(`\n ⚠️ CRUD generation failed: ${err.message}`));
304
- console.warn(chalk.gray(' Run `speedrun-cli generate <name>` manually inside your project.\n'));
305
- }
306
- }
307
-
308
- /**
309
- * Prompts user to start dev server
310
- */
311
- async function promptDevServer(targetDir, packageManager) {
312
- const { startServer } = await inquirer.prompt([{
313
- type: 'confirm',
314
- name: 'startServer',
315
- message: 'Start the development server now?',
316
- default: false,
317
- }]);
318
-
319
- if (!startServer) return;
320
-
321
- console.log(chalk.yellow('\n🚀 Starting development server...\n'));
322
- console.log(chalk.gray(` Your API will be available at: ${chalk.cyan('http://localhost:8080/api/v1')}`));
323
- console.log(chalk.gray(` Press ${chalk.bold('Ctrl+C')} to stop the server\n`));
324
-
325
- const pmPrefix = getRunPrefix(packageManager);
326
-
327
- try {
328
- execSync(`${pmPrefix} start:dev`, { cwd: targetDir, stdio: 'inherit' });
329
- } catch {
330
- console.log(chalk.yellow('\n Server stopped.'));
331
- }
332
- }
333
-
334
- /**
335
- * Prints manual setup instructions when interactive setup is skipped
336
- */
337
- function printManualInstructions(appName, options) {
338
- const { orm, database, packageManager, installDependencies, swagger, baseCrud } = options;
339
-
340
- console.log(chalk.green('\n✅ Success! Created ' + chalk.bold(appName)));
341
- console.log(chalk.gray(` ORM: ${ORM_OPTIONS[orm]?.name || orm}`));
342
- console.log(chalk.gray(` Database: ${DATABASE_OPTIONS[database]?.name || database}`));
343
- if (swagger) {
344
- console.log(chalk.gray(` Swagger: ${chalk.green('Enabled')}`));
345
- }
346
- if (baseCrud) {
347
- console.log(chalk.gray(` Base CRUD Architecture: ${chalk.green('Enabled')} see CRUD_README.md`));
348
- }
349
- console.log(chalk.white('\n📚 Next steps:\n'));
350
- console.log(chalk.cyan(` cd ${appName}`));
351
-
352
- if (!installDependencies) {
353
- const installCmd = require('./utils').getInstallCommand(packageManager);
354
- console.log(chalk.cyan(` ${installCmd}`));
355
- }
356
-
357
- console.log(chalk.cyan('\n # Generate secure JWT secrets (save these!):'));
358
- console.log(chalk.gray(' openssl rand -base64 32 # For JWT_ACCESS_SECRET'));
359
- console.log(chalk.gray(' openssl rand -base64 32 # For JWT_REFRESH_SECRET'));
360
- console.log(chalk.cyan('\n # Edit .env with your database URL and JWT secrets'));
361
-
362
- const ormCommands = {
363
- prisma: ['prisma:generate', 'prisma:migrate', 'prisma:seed'],
364
- typeorm: ['schema:sync', 'seed'],
365
- mongoose: ['db:seed'],
366
- drizzle: ['db:push', 'db:seed'],
367
- };
368
-
369
- const commands = ormCommands[orm];
370
- if (commands) {
371
- console.log(chalk.cyan('\n # Then setup the database:'));
372
- commands.forEach((cmd) => console.log(chalk.gray(` npm run ${cmd}`)));
373
- }
374
-
375
- console.log(chalk.cyan('\n # Generate your first CRUD module:'));
376
- console.log(chalk.gray(' speedrun-cli generate <module-name>'));
377
- console.log(chalk.gray(' # e.g. speedrun-cli generate orders'));
378
-
379
- console.log(chalk.cyan('\n # Start development server:'));
380
- console.log(chalk.gray(' npm run start:dev'));
381
-
382
- if (swagger) {
383
- console.log(chalk.cyan('\n # Swagger API documentation:'));
384
- console.log(chalk.gray(' http://localhost:8080/api/docs'));
385
- }
386
-
387
- if (baseCrud) {
388
- console.log(chalk.cyan('\n # Base CRUD Architecture:'));
389
- console.log(chalk.gray(' src/common/base/ — BaseService & BaseController'));
390
- console.log(chalk.gray(' src/modules/products/ Concrete example (ProductModule)'));
391
- console.log(chalk.gray(' CRUD_README.md — Full guide & cheatsheet'));
392
- }
393
-
394
- console.log(chalk.white('\n📖 Documentation: https://github.com/masabinhok/create-nestjs-auth'));
395
- console.log(chalk.white('🐛 Issues: https://github.com/masabinhok/create-nestjs-auth/issues\n'));
396
- console.log(chalk.magenta('Happy coding! 🎉\n'));
397
- }
398
-
399
- module.exports = {
400
- handlePostSetup,
401
- printManualInstructions,
402
- };
1
+ /**
2
+ * Post-Setup Interactive Handlers
3
+ * @module postSetup
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+ const { execSync } = require('child_process');
9
+ const chalk = require('chalk');
10
+ const inquirer = require('inquirer');
11
+ const { ORM_OPTIONS, DATABASE_OPTIONS } = require('./constants');
12
+ const { generateJWTSecret, getRunPrefix } = require('./utils');
13
+
14
+ // Lazy-loaded to avoid circular deps: moduleGenerator requires constants/utils
15
+ let _generateModule;
16
+ function getGenerateModule() {
17
+ if (!_generateModule) _generateModule = require('./moduleGenerator').generateModule;
18
+ return _generateModule;
19
+ }
20
+
21
+ /**
22
+ * Handles interactive post-setup configuration
23
+ * @param {string} targetDir - Project directory
24
+ * @param {string} appName - Application name
25
+ * @param {object} options - Configuration options
26
+ * @returns {Promise<boolean>} Whether interactive setup completed
27
+ */
28
+ async function handlePostSetup(targetDir, appName, options) {
29
+ const { packageManager, orm, database, swagger, baseCrud, generateFirstCrud, firstModuleName, yes: isYesMode, skipInstall } = options;
30
+
31
+ if (isYesMode || skipInstall) {
32
+ return false;
33
+ }
34
+
35
+ printSuccessHeader(appName, orm, database, swagger, baseCrud);
36
+
37
+ const { continueSetup } = await inquirer.prompt([{
38
+ type: 'confirm',
39
+ name: 'continueSetup',
40
+ message: 'Would you like to complete the setup now? (JWT secrets, database, CRUD)',
41
+ default: true,
42
+ }]);
43
+
44
+ if (!continueSetup) {
45
+ return false;
46
+ }
47
+
48
+ // Step 1: Configure JWT secrets and database URL
49
+ await configureEnvironment(targetDir, database);
50
+
51
+ // Step 2: ORM-specific database setup (Schema/Migration Seed)
52
+ await setupDatabase(targetDir, orm, packageManager);
53
+
54
+ // Step 3: CRUD module generation
55
+ if (generateFirstCrud !== false) {
56
+ await promptCrudGeneration(targetDir, orm, firstModuleName);
57
+ }
58
+
59
+ // Step 4: Display Post-Setup Summary Log with Next Steps
60
+ printNextStepsSummary(orm);
61
+
62
+ // Step 5: Optionally start dev server
63
+ await promptDevServer(targetDir, packageManager);
64
+
65
+ return true;
66
+ }
67
+
68
+ /**
69
+ * Prints the success header
70
+ */
71
+ function printSuccessHeader(appName, orm, database, swagger, baseCrud) {
72
+ console.log(chalk.green('\n✅ Success! Created ' + chalk.bold(appName)));
73
+ console.log(chalk.gray(` ORM: ${ORM_OPTIONS[orm]?.name || orm}`));
74
+ console.log(chalk.gray(` Database: ${DATABASE_OPTIONS[database]?.name || database}`));
75
+ if (swagger) {
76
+ console.log(chalk.gray(` Swagger: ${chalk.green('Enabled')}`));
77
+ }
78
+ if (baseCrud) {
79
+ console.log(chalk.gray(` Base CRUD Architecture: ${chalk.green('Enabled')} — src/common/base/`));
80
+ }
81
+ console.log(chalk.white('\n🎉 Your project is ready!\n'));
82
+ }
83
+
84
+ /**
85
+ * Configures environment variables
86
+ */
87
+ async function configureEnvironment(targetDir, database) {
88
+ console.log(chalk.yellow('\n🔑 Generating JWT secrets...\n'));
89
+ const accessSecret = generateJWTSecret();
90
+ const refreshSecret = generateJWTSecret();
91
+
92
+ console.log(chalk.gray(' Generated JWT_ACCESS_SECRET'));
93
+ console.log(chalk.gray(' Generated JWT_REFRESH_SECRET\n'));
94
+
95
+ const dbInfo = DATABASE_OPTIONS[database];
96
+ const { databaseUrl } = await inquirer.prompt([{
97
+ type: 'input',
98
+ name: 'databaseUrl',
99
+ message: `Enter your ${dbInfo.name} database URL:`,
100
+ default: dbInfo.urlTemplate,
101
+ validate: (input) => {
102
+ if (!input || input.trim() === '') {
103
+ return 'Database URL is required';
104
+ }
105
+ const validPrefix = dbInfo.urlPrefix.some((prefix) => input.startsWith(prefix));
106
+ if (!validPrefix) {
107
+ return `Database URL must start with ${dbInfo.urlPrefix.join(' or ')}`;
108
+ }
109
+ return true;
110
+ },
111
+ }]);
112
+
113
+ // Update .env file
114
+ console.log(chalk.gray('\n Updating .env file...'));
115
+ const envPath = path.join(targetDir, '.env');
116
+
117
+ try {
118
+ let envContent = await fs.readFile(envPath, 'utf8');
119
+ envContent = envContent.replace(/DATABASE_URL=.*/, `DATABASE_URL="${databaseUrl}"`);
120
+ envContent = envContent.replace(/JWT_ACCESS_SECRET=.*/, `JWT_ACCESS_SECRET="${accessSecret}"`);
121
+ envContent = envContent.replace(/JWT_REFRESH_SECRET=.*/, `JWT_REFRESH_SECRET="${refreshSecret}"`);
122
+ await fs.writeFile(envPath, envContent);
123
+ console.log(chalk.green(' ✓ Environment variables configured\n'));
124
+ } catch {
125
+ console.error(chalk.red(' ✗ Failed to update .env file'));
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Sets up the database based on selected ORM
131
+ */
132
+ async function setupDatabase(targetDir, orm, packageManager) {
133
+ const setupHandlers = {
134
+ prisma: setupPrisma,
135
+ typeorm: setupTypeOrm,
136
+ mongoose: setupMongoose,
137
+ drizzle: setupDrizzle,
138
+ };
139
+
140
+ const handler = setupHandlers[orm];
141
+ if (handler) {
142
+ await handler(targetDir, packageManager);
143
+ }
144
+ }
145
+
146
+ async function setupPrisma(targetDir, packageManager) {
147
+ const { setupDatabase } = await inquirer.prompt([{
148
+ type: 'confirm',
149
+ name: 'setupDatabase',
150
+ message: 'Set up the database now? (generate Prisma client, run migrations, seed)',
151
+ default: true,
152
+ }]);
153
+
154
+ if (!setupDatabase) return;
155
+
156
+ // Prompt for migration name before running prisma commands
157
+ const { migrationName } = await inquirer.prompt([{
158
+ type: 'input',
159
+ name: 'migrationName',
160
+ message: 'Enter migration name:',
161
+ default: 'init',
162
+ validate: (input) => {
163
+ if (!input || input.trim() === '') {
164
+ return 'Migration name is required';
165
+ }
166
+ if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
167
+ return 'Migration name can only contain letters, numbers, underscores, and hyphens';
168
+ }
169
+ return true;
170
+ },
171
+ }]);
172
+
173
+ console.log(chalk.yellow('\n📦 Setting up database...\n'));
174
+ const pmPrefix = getRunPrefix(packageManager);
175
+
176
+ try {
177
+ console.log(chalk.gray(' Generating Prisma client...'));
178
+ execSync(`${pmPrefix} prisma:generate`, { cwd: targetDir, stdio: 'inherit' });
179
+
180
+ console.log(chalk.gray('\n Running database migrations...'));
181
+ // Run prisma directly with --name to avoid double prompt
182
+ execSync(`npx prisma migrate dev --name ${migrationName}`, { cwd: targetDir, stdio: 'inherit' });
183
+
184
+ console.log(chalk.gray('\n Seeding database...'));
185
+ execSync(`${pmPrefix} prisma:seed`, { cwd: targetDir, stdio: 'inherit' });
186
+
187
+ printCredentials();
188
+ } catch {
189
+ console.error(chalk.red('\n ✗ Database setup failed'));
190
+ console.error(chalk.yellow(' Run these commands manually:'));
191
+ console.error(chalk.gray(` ${pmPrefix} prisma:generate`));
192
+ console.error(chalk.gray(` ${pmPrefix} prisma:migrate`));
193
+ console.error(chalk.gray(` ${pmPrefix} prisma:seed\n`));
194
+ }
195
+ }
196
+
197
+ async function setupTypeOrm(targetDir, packageManager) {
198
+ const { setupDatabase } = await inquirer.prompt([{
199
+ type: 'confirm',
200
+ name: 'setupDatabase',
201
+ message: 'Set up the database now? (sync schema, seed)',
202
+ default: true,
203
+ }]);
204
+
205
+ if (!setupDatabase) return;
206
+
207
+ console.log(chalk.yellow('\n📦 Setting up database...\n'));
208
+ const pmPrefix = getRunPrefix(packageManager);
209
+
210
+ try {
211
+ console.log(chalk.gray(' Synchronizing database schema...'));
212
+ execSync(`${pmPrefix} schema:sync`, { cwd: targetDir, stdio: 'inherit' });
213
+
214
+ console.log(chalk.gray('\n Seeding database...'));
215
+ execSync(`${pmPrefix} seed`, { cwd: targetDir, stdio: 'inherit' });
216
+
217
+ printCredentials();
218
+ } catch {
219
+ console.error(chalk.red('\n ✗ Database setup failed'));
220
+ console.error(chalk.yellow(' Run these commands manually:'));
221
+ console.error(chalk.gray(` ${pmPrefix} schema:sync`));
222
+ console.error(chalk.gray(` ${pmPrefix} seed\n`));
223
+ }
224
+ }
225
+
226
+ async function setupMongoose(targetDir, packageManager) {
227
+ const { setupDatabase } = await inquirer.prompt([{
228
+ type: 'confirm',
229
+ name: 'setupDatabase',
230
+ message: 'Seed the database now? (create default admin user)',
231
+ default: true,
232
+ }]);
233
+
234
+ if (!setupDatabase) return;
235
+
236
+ console.log(chalk.yellow('\n📦 Setting up database...\n'));
237
+ const pmPrefix = getRunPrefix(packageManager);
238
+
239
+ try {
240
+ console.log(chalk.gray(' Seeding database...'));
241
+ execSync(`${pmPrefix} db:seed`, { cwd: targetDir, stdio: 'inherit' });
242
+
243
+ printCredentials();
244
+ } catch {
245
+ console.error(chalk.red('\n ✗ Database setup failed'));
246
+ console.error(chalk.yellow(' Run this command manually:'));
247
+ console.error(chalk.gray(` ${pmPrefix} db:seed\n`));
248
+ }
249
+ }
250
+
251
+ async function setupDrizzle(targetDir, packageManager) {
252
+ const { setupDatabase } = await inquirer.prompt([{
253
+ type: 'confirm',
254
+ name: 'setupDatabase',
255
+ message: 'Set up the database now? (push schema, seed)',
256
+ default: true,
257
+ }]);
258
+
259
+ if (!setupDatabase) return;
260
+
261
+ console.log(chalk.yellow('\n📦 Setting up database...\n'));
262
+ const pmPrefix = getRunPrefix(packageManager);
263
+
264
+ try {
265
+ console.log(chalk.gray(' Pushing schema to database...'));
266
+ execSync(`${pmPrefix} db:push`, { cwd: targetDir, stdio: 'inherit' });
267
+
268
+ console.log(chalk.gray('\n Seeding database...'));
269
+ execSync(`${pmPrefix} db:seed`, { cwd: targetDir, stdio: 'inherit' });
270
+
271
+ printCredentials();
272
+ } catch {
273
+ console.error(chalk.red('\n ✗ Database setup failed'));
274
+ console.error(chalk.yellow(' Run these commands manually:'));
275
+ console.error(chalk.gray(` ${pmPrefix} db:push`));
276
+ console.error(chalk.gray(` ${pmPrefix} db:seed\n`));
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Prints default admin credentials
282
+ */
283
+ function printCredentials() {
284
+ console.log(chalk.green('\n ✓ Database setup complete!\n'));
285
+ console.log(chalk.cyan(' 📝 Default admin credentials:'));
286
+ console.log(chalk.white(' Email: admin@example.com'));
287
+ console.log(chalk.white(' Password: Admin@123\n'));
288
+ }
289
+
290
+ /**
291
+ * Prompts user to generate their first CRUD module
292
+ */
293
+ async function promptCrudGeneration(targetDir, orm, providedModuleName) {
294
+ let moduleName = providedModuleName;
295
+
296
+ if (!moduleName) {
297
+ const { generateNow } = await inquirer.prompt([{
298
+ type: 'confirm',
299
+ name: 'generateNow',
300
+ message: 'Do you want to generate your first CRUD module now?',
301
+ default: true,
302
+ }]);
303
+
304
+ if (!generateNow) {
305
+ console.log(chalk.gray('\n Skipping CRUD generation. Run `speedrun-cli generate <name>` anytime.\n'));
306
+ return;
307
+ }
308
+ }
309
+
310
+ try {
311
+ await getGenerateModule()(moduleName, targetDir, orm);
312
+ } catch (err) {
313
+ console.warn(chalk.yellow(`\n ⚠️ CRUD generation failed: ${err.message}`));
314
+ console.warn(chalk.gray(' Run `speedrun-cli generate <name>` manually inside your project.\n'));
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Display post-install summary with instructions to run database migrations and seeds dynamically based on selected ORM
320
+ */
321
+ function printNextStepsSummary(orm) {
322
+ console.log(chalk.cyan.bold('\n💡 Next Steps:\n'));
323
+
324
+ if (orm === 'prisma') {
325
+ console.log(chalk.white('1. Run Database Migration: ') + chalk.yellow('npx prisma db push'));
326
+ console.log(chalk.white('2. Run Database Seed: ') + chalk.yellow('npm run seed\n'));
327
+ } else if (orm === 'typeorm') {
328
+ console.log(chalk.white('1. Synchronize Database Schema: ') + chalk.yellow('npm run schema:sync'));
329
+ console.log(chalk.white('2. Run Database Seed: ') + chalk.yellow('npm run seed\n'));
330
+ } else if (orm === 'mongoose') {
331
+ console.log(chalk.white('1. Run Database Seed: ') + chalk.yellow('npm run db:seed\n'));
332
+ } else if (orm === 'drizzle') {
333
+ console.log(chalk.white('1. Push Schema to Database: ') + chalk.yellow('npm run db:push'));
334
+ console.log(chalk.white('2. Run Database Seed: ') + chalk.yellow('npm run db:seed\n'));
335
+ } else {
336
+ console.log(chalk.white('1. Run Database Migration & Seed commands for your ORM\n'));
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Prompts user to start dev server
342
+ */
343
+ async function promptDevServer(targetDir, packageManager) {
344
+ const { startServer } = await inquirer.prompt([{
345
+ type: 'confirm',
346
+ name: 'startServer',
347
+ message: 'Start the development server now?',
348
+ default: false,
349
+ }]);
350
+
351
+ if (!startServer) return;
352
+
353
+ console.log(chalk.yellow('\n🚀 Starting development server...\n'));
354
+ console.log(chalk.gray(` Your API will be available at: ${chalk.cyan('http://localhost:8080/api/v1')}`));
355
+ console.log(chalk.gray(` Press ${chalk.bold('Ctrl+C')} to stop the server\n`));
356
+
357
+ const pmPrefix = getRunPrefix(packageManager);
358
+
359
+ try {
360
+ execSync(`${pmPrefix} start:dev`, { cwd: targetDir, stdio: 'inherit' });
361
+ } catch {
362
+ console.log(chalk.yellow('\n Server stopped.'));
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Prints manual setup instructions when interactive setup is skipped
368
+ */
369
+ function printManualInstructions(appName, options) {
370
+ const { orm, database, packageManager, installDependencies, swagger, baseCrud } = options;
371
+
372
+ console.log(chalk.green('\n✅ Success! Created ' + chalk.bold(appName)));
373
+ console.log(chalk.gray(` ORM: ${ORM_OPTIONS[orm]?.name || orm}`));
374
+ console.log(chalk.gray(` Database: ${DATABASE_OPTIONS[database]?.name || database}`));
375
+ if (swagger) {
376
+ console.log(chalk.gray(` Swagger: ${chalk.green('Enabled')}`));
377
+ }
378
+ if (baseCrud) {
379
+ console.log(chalk.gray(` Base CRUD Architecture: ${chalk.green('Enabled')} — see CRUD_README.md`));
380
+ }
381
+ console.log(chalk.white('\n📚 Next steps:\n'));
382
+ console.log(chalk.cyan(` cd ${appName}`));
383
+
384
+ if (!installDependencies) {
385
+ const installCmd = require('./utils').getInstallCommand(packageManager);
386
+ console.log(chalk.cyan(` ${installCmd}`));
387
+ }
388
+
389
+ console.log(chalk.cyan('\n # Generate secure JWT secrets (save these!):'));
390
+ console.log(chalk.gray(' openssl rand -base64 32 # For JWT_ACCESS_SECRET'));
391
+ console.log(chalk.gray(' openssl rand -base64 32 # For JWT_REFRESH_SECRET'));
392
+ console.log(chalk.cyan('\n # Edit .env with your database URL and JWT secrets'));
393
+
394
+ printNextStepsSummary(orm);
395
+
396
+ console.log(chalk.cyan(' # Generate your first CRUD module:'));
397
+ console.log(chalk.gray(' speedrun-cli generate <module-name>'));
398
+ console.log(chalk.gray(' # e.g. speedrun-cli generate orders'));
399
+
400
+ console.log(chalk.cyan('\n # Start development server:'));
401
+ console.log(chalk.gray(' npm run start:dev'));
402
+
403
+ if (swagger) {
404
+ console.log(chalk.cyan('\n # Swagger API documentation:'));
405
+ console.log(chalk.gray(' http://localhost:8080/api/docs'));
406
+ }
407
+
408
+ if (baseCrud) {
409
+ console.log(chalk.cyan('\n # Base CRUD Architecture:'));
410
+ console.log(chalk.gray(' src/common/base/ — BaseService & BaseController'));
411
+ console.log(chalk.gray(' CRUD_README.md — Full guide & cheatsheet'));
412
+ }
413
+
414
+ console.log(chalk.white('\n📖 Documentation: https://github.com/masabinhok/create-nestjs-auth'));
415
+ console.log(chalk.white('🐛 Issues: https://github.com/masabinhok/create-nestjs-auth/issues\n'));
416
+ console.log(chalk.magenta('Happy coding! 🎉\n'));
417
+ }
418
+
419
+ module.exports = {
420
+ handlePostSetup,
421
+ printManualInstructions,
422
+ printNextStepsSummary,
423
+ };