student-help 2.0.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/cli.mjs ADDED
@@ -0,0 +1,1415 @@
1
+ import { Command } from 'commander';
2
+ import { execSync } from 'child_process';
3
+ import fs from 'fs-extra';
4
+ import path from 'path';
5
+ import chalk from 'chalk';
6
+ import ora from 'ora';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ // ============================================
10
+ // Print beautiful Student Help banner
11
+ // ============================================
12
+ const printBanner = () => {
13
+ console.clear();
14
+ console.log(chalk.hex('#00D9FF').bold('\n'));
15
+ console.log(chalk.hex('#00D9FF').bold(' ███████╗████████╗██╗ ██╗██████╗ ███████╗███╗ ██╗████████╗'));
16
+ console.log(chalk.hex('#0099FF').bold(' ██╔════╝╚══██╔══╝██║ ██║██╔══██╗██╔════╝████╗ ██║╚══██╔══╝'));
17
+ console.log(chalk.hex('#0052FF').bold(' ███████╗ ██║ ██║ ██║██║ ██║█████╗ ██╔██╗ ██║ ██║'));
18
+ console.log(chalk.hex('#6600FF').bold(' ╚════██║ ██║ ██║ ██║██║ ██║██╔══╝ ██║╚██╗██║ ██║'));
19
+ console.log(chalk.hex('#FF00FF').bold(' ███████║ ██║ ╚██████╔╝██████╔╝███████╗██║ ╚████║ ██║'));
20
+ console.log(chalk.hex('#FF0066').bold(' ╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝'));
21
+ console.log(chalk.hex('#FF00FF').bold('\n 🚀 HELP FRAMEWORK - Create Full-Stack Apps in Minutes\n'));
22
+ console.log(chalk.hex('#00D9FF').bold(' ' + '═'.repeat(58) + '\n'));
23
+ };
24
+
25
+ // ============================================
26
+ // Command: new <projectName>
27
+ // ============================================
28
+ const newCommand = async (projectName) => {
29
+ printBanner();
30
+
31
+ const projectPath = path.join(process.cwd(), projectName);
32
+ fs.ensureDirSync(projectPath);
33
+
34
+ console.log(chalk.cyan.bold('🎯 Creating your full-stack project...\n'));
35
+
36
+ // Backend
37
+ let spinner = ora({
38
+ text: 'Setting up NestJS backend',
39
+ spinner: 'dots3',
40
+ color: 'cyan'
41
+ }).start();
42
+
43
+ try {
44
+ const tempBackendName = path.basename(projectPath) + '_backend_temp';
45
+ execSync(`npx @nestjs/cli new ${tempBackendName} --package-manager npm --skip-git 2>&1 > /dev/null`, {
46
+ stdio: 'ignore',
47
+ cwd: '/tmp'
48
+ });
49
+
50
+ const tempBackendPath = path.join('/tmp', tempBackendName);
51
+ const backendDir = path.join(projectPath, 'backend');
52
+
53
+ if (!fs.existsSync(tempBackendPath)) {
54
+ throw new Error('Backend temporary directory was not created');
55
+ }
56
+
57
+ fs.moveSync(tempBackendPath, backendDir);
58
+ spinner.succeed(chalk.green('Backend NestJS configured'));
59
+ } catch (error) {
60
+ spinner.fail(chalk.red('Error creating backend'));
61
+ throw error;
62
+ }
63
+
64
+ // Frontend
65
+ spinner = ora({
66
+ text: 'Setting up Next.js frontend',
67
+ spinner: 'dots3',
68
+ color: 'magenta'
69
+ }).start();
70
+
71
+ try {
72
+ execSync(`npx create-next-app@latest ${projectPath}/frontend --typescript --use-npm --eslint --app --no-src-dir --import-alias '@/*' --skip-install 2>&1 > /dev/null`, {
73
+ stdio: 'ignore',
74
+ cwd: process.cwd(),
75
+ env: {
76
+ ...process.env,
77
+ CI: 'true'
78
+ }
79
+ });
80
+ spinner.succeed(chalk.green('Frontend structure created'));
81
+
82
+ spinner = ora({
83
+ text: 'Installing frontend dependencies',
84
+ spinner: 'arc',
85
+ color: 'blue'
86
+ }).start();
87
+
88
+ execSync(`npm install`, { stdio: 'ignore', cwd: path.join(projectPath, 'frontend') });
89
+ spinner.succeed(chalk.green('Frontend dependencies installed'));
90
+
91
+ spinner = ora({
92
+ text: 'Adding modern packages (Tailwind, ESLint, Prettier)',
93
+ spinner: 'bouncingBar',
94
+ color: 'yellow'
95
+ }).start();
96
+
97
+ const frontendPath = path.join(projectPath, 'frontend');
98
+ execSync(`npm install -D tailwindcss postcss autoprefixer prettier @typescript-eslint/eslint-plugin eslint-config-prettier eslint-plugin-prettier axios zustand 2>&1 > /dev/null`, {
99
+ stdio: 'ignore',
100
+ cwd: frontendPath
101
+ });
102
+
103
+ spinner.succeed(chalk.green('Frontend configured with modern packages'));
104
+ } catch (error) {
105
+ spinner.fail(chalk.red('Error creating frontend'));
106
+ throw error;
107
+ }
108
+
109
+ // Docker configuration
110
+ spinner = ora({
111
+ text: 'Setting up Docker & environment',
112
+ spinner: 'line',
113
+ color: 'cyan'
114
+ }).start();
115
+
116
+ try {
117
+ const backendDir = path.join(projectPath, 'backend');
118
+ const frontendDir = path.join(projectPath, 'frontend');
119
+
120
+ fs.ensureDirSync(backendDir);
121
+ fs.ensureDirSync(frontendDir);
122
+
123
+ // Backend Dockerfile
124
+ const backendDockerfile = [
125
+ 'FROM node:18-alpine',
126
+ '',
127
+ 'WORKDIR /app',
128
+ '',
129
+ 'COPY package*.json ./',
130
+ '',
131
+ 'RUN npm ci',
132
+ '',
133
+ 'COPY . .',
134
+ '',
135
+ 'RUN npm run build',
136
+ '',
137
+ 'EXPOSE 3000',
138
+ '',
139
+ 'CMD ["node", "dist/main.js"]',
140
+ ].join('\n');
141
+
142
+ fs.writeFileSync(path.join(backendDir, 'Dockerfile'), backendDockerfile);
143
+
144
+ // Frontend Dockerfile
145
+ const frontendDockerfile = [
146
+ 'FROM node:18-alpine',
147
+ '',
148
+ 'WORKDIR /app',
149
+ '',
150
+ 'COPY package*.json ./',
151
+ '',
152
+ 'RUN npm ci',
153
+ '',
154
+ 'COPY . .',
155
+ '',
156
+ 'RUN npm run build',
157
+ '',
158
+ 'EXPOSE 3000',
159
+ '',
160
+ 'CMD ["npm", "start"]',
161
+ ].join('\n');
162
+
163
+ fs.writeFileSync(path.join(frontendDir, 'Dockerfile'), frontendDockerfile);
164
+
165
+ // Docker Compose
166
+ const dockerCompose = `version: '3.8'
167
+
168
+ services:
169
+ backend:
170
+ build: ./backend
171
+ container_name: ${projectName}_backend
172
+ ports:
173
+ - "3000:3000"
174
+ environment:
175
+ NODE_ENV: development
176
+ DB_HOST: postgres
177
+ DB_PORT: 5432
178
+ DB_USER: student
179
+ DB_PASSWORD: student123
180
+ DB_NAME: ${projectName}
181
+ DATABASE_URL: postgresql://student:student123@postgres:5432/${projectName}
182
+ volumes:
183
+ - ./backend:/app
184
+ - /app/node_modules
185
+ depends_on:
186
+ postgres:
187
+ condition: service_healthy
188
+ networks:
189
+ - app-network
190
+ restart: unless-stopped
191
+
192
+ frontend:
193
+ build: ./frontend
194
+ container_name: ${projectName}_frontend
195
+ ports:
196
+ - "3001:3000"
197
+ environment:
198
+ NODE_ENV: development
199
+ NEXT_PUBLIC_API_URL: http://localhost:3000/api
200
+ volumes:
201
+ - ./frontend:/app
202
+ - /app/node_modules
203
+ depends_on:
204
+ - backend
205
+ networks:
206
+ - app-network
207
+ restart: unless-stopped
208
+
209
+ postgres:
210
+ image: postgres:16-alpine
211
+ container_name: ${projectName}_postgres
212
+ environment:
213
+ POSTGRES_USER: student
214
+ POSTGRES_PASSWORD: student123
215
+ POSTGRES_DB: ${projectName}
216
+ ports:
217
+ - "5432:5432"
218
+ volumes:
219
+ - postgres_data:/var/lib/postgresql/data
220
+ networks:
221
+ - app-network
222
+ healthcheck:
223
+ test: ["CMD-SHELL", "pg_isready -U student -d ${projectName}"]
224
+ interval: 10s
225
+ timeout: 5s
226
+ retries: 5
227
+ restart: unless-stopped
228
+
229
+ volumes:
230
+ postgres_data:
231
+ driver: local
232
+
233
+ networks:
234
+ app-network:
235
+ driver: bridge
236
+ `;
237
+
238
+ fs.writeFileSync(path.join(projectPath, 'docker-compose.yml'), dockerCompose);
239
+
240
+ // .env.example
241
+ const envExample = `# Backend Configuration
242
+ BACKEND_PORT=3000
243
+ NODE_ENV=development
244
+
245
+ # Frontend Configuration
246
+ FRONTEND_PORT=3001
247
+ NEXT_PUBLIC_API_URL=http://localhost:3000/api
248
+
249
+ # Database Configuration
250
+ DB_HOST=postgres
251
+ DB_PORT=5432
252
+ DB_USER=student
253
+ DB_PASSWORD=student123
254
+ DB_NAME=${projectName}
255
+ DATABASE_URL=postgresql://student:student123@postgres:5432/${projectName}
256
+
257
+ # Security
258
+ JWT_SECRET=your_super_secret_key_change_this_in_production
259
+ JWT_EXPIRATION=7d
260
+
261
+ # API
262
+ API_PREFIX=api/v1
263
+ `;
264
+
265
+ fs.writeFileSync(path.join(projectPath, '.env.example'), envExample);
266
+
267
+ // .gitignore
268
+ const gitignore = [
269
+ '# Dependencies',
270
+ 'node_modules/',
271
+ '/.pnp',
272
+ '.pnp.js',
273
+ '',
274
+ '# Testing',
275
+ '/coverage',
276
+ '',
277
+ '# Production',
278
+ '/build',
279
+ '/dist',
280
+ '/out',
281
+ '',
282
+ '# Misc',
283
+ '.DS_Store',
284
+ '*.pem',
285
+ '',
286
+ '# Logs',
287
+ 'npm-debug.log*',
288
+ 'yarn-debug.log*',
289
+ 'yarn-error.log*',
290
+ 'pnpm-debug.log*',
291
+ 'lerna-debug.log*',
292
+ '',
293
+ '# Environment',
294
+ '.env',
295
+ '.env.local',
296
+ '.env.development.local',
297
+ '.env.test.local',
298
+ '.env.production.local',
299
+ '',
300
+ '# IDE',
301
+ '.vscode',
302
+ '.idea',
303
+ '*.swp',
304
+ '*.swo',
305
+ '*~',
306
+ '.iml',
307
+ '',
308
+ '# OS',
309
+ '.AppleDouble',
310
+ '.LSOverride',
311
+ 'Thumbs.db',
312
+ '',
313
+ '# Next.js',
314
+ '.next',
315
+ 'out',
316
+ '',
317
+ '# Docker',
318
+ 'docker-compose.override.yml',
319
+ ].join('\n');
320
+
321
+ fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore);
322
+
323
+ // .prettierrc.json
324
+ const prettierConfig = {
325
+ semi: true,
326
+ trailingComma: 'es5',
327
+ singleQuote: true,
328
+ printWidth: 100,
329
+ tabWidth: 2,
330
+ useTabs: false,
331
+ arrowParens: 'always',
332
+ endOfLine: 'lf'
333
+ };
334
+
335
+ fs.writeFileSync(path.join(projectPath, '.prettierrc.json'), JSON.stringify(prettierConfig, null, 2));
336
+
337
+ spinner.succeed(chalk.green('Docker & environment configured'));
338
+ } catch (error) {
339
+ spinner.fail(chalk.red('Error configuring Docker'));
340
+ throw error;
341
+ }
342
+
343
+ // README
344
+ spinner = ora({
345
+ text: 'Generating documentation',
346
+ spinner: 'pong',
347
+ color: 'green'
348
+ }).start();
349
+
350
+ const readmeContent = `# ${projectName}
351
+
352
+ A modern full-stack application created with **Student Help Framework**.
353
+
354
+ ## 📚 What is Student Help Framework?
355
+
356
+ Student Help Framework is a mini-framework that simplifies the creation of modern full-stack applications. It automates the complete setup of:
357
+
358
+ - **Backend**: NestJS (a robust and scalable Node.js framework)
359
+ - **Frontend**: Next.js (React framework with SSR and optimizations)
360
+ - **Database**: PostgreSQL (reliable relational database)
361
+ - **Docker**: Complete containerization for environment isolation
362
+ - **DevOps**: Docker Compose for local orchestration
363
+
364
+ ### Why use Student Help Framework?
365
+
366
+ ✨ **Zero Configuration** - Everything is pre-configured and ready to go
367
+ ⚡ **Production Ready** - Scalable structure from day one
368
+ 🐳 **Docker Included** - Consistent environment across machines
369
+ 🔄 **Code Generation** - Create CRUD resources with a single command
370
+ 📦 **Modern Stack** - Latest technologies with best practices
371
+ 🎨 **Professional UI** - Tailwind CSS pre-configured
372
+
373
+ ## 🚀 Getting Started
374
+
375
+ ### 1. Start the Development Environment
376
+
377
+ \`\`\`bash
378
+ docker-compose up -d
379
+ \`\`\`
380
+
381
+ This starts:
382
+ - **Backend**: Port 3000
383
+ - **Frontend**: Port 3001
384
+ - **PostgreSQL**: Port 5432
385
+
386
+ ### 2. Access the Application
387
+
388
+ - **Frontend**: http://localhost:3001
389
+ - **Backend API**: http://localhost:3000
390
+ - **Database**: localhost:5432 (user: student / password: student123)
391
+
392
+ ## 🛠️ Development Without Docker
393
+
394
+ ### Backend
395
+ \`\`\`bash
396
+ cd backend
397
+ npm install
398
+ npm run start:dev
399
+ \`\`\`
400
+
401
+ Server runs at: http://localhost:3000
402
+
403
+ ### Frontend
404
+ \`\`\`bash
405
+ cd frontend
406
+ npm install
407
+ npm run dev
408
+ \`\`\`
409
+
410
+ App runs at: http://localhost:3001
411
+
412
+ ## 📦 Generate Resources Automatically
413
+
414
+ ### Create Authentication Module
415
+ \`\`\`bash
416
+ student-help generate:auth
417
+ \`\`\`
418
+
419
+ ### Create a CRUD Resource
420
+ \`\`\`bash
421
+ student-help generate:resource Post
422
+ \`\`\`
423
+
424
+ ## 📁 Project Structure
425
+
426
+ \`\`\`
427
+ ${projectName}/
428
+ ├── backend/ # NestJS API
429
+ ├── frontend/ # Next.js App
430
+ ├── docker-compose.yml # Orchestration
431
+ ├── .env.example # Environment template
432
+ └── .gitignore
433
+ \`\`\`
434
+
435
+ ## 🗄️ Database
436
+
437
+ PostgreSQL is pre-configured:
438
+ - **User**: student
439
+ - **Password**: student123
440
+ - **Database**: ${projectName}
441
+
442
+ ---
443
+
444
+ **Built with ❤️ using Student Help Framework**
445
+ `;
446
+
447
+ fs.writeFileSync(path.join(projectPath, 'README.md'), readmeContent);
448
+
449
+ spinner.succeed(chalk.green('Documentation generated'));
450
+
451
+ // Git initialization
452
+ spinner = ora({
453
+ text: 'Initializing Git repository',
454
+ spinner: 'dots2',
455
+ color: 'blue'
456
+ }).start();
457
+
458
+ try {
459
+ execSync(`cd ${projectPath} && git init && git add . && git commit -m "Initial commit: Full-stack project setup with Student Help Framework" 2>&1 > /dev/null`, {
460
+ stdio: 'ignore',
461
+ cwd: projectPath
462
+ });
463
+ spinner.succeed(chalk.green('Git repository created'));
464
+ } catch (error) {
465
+ spinner.succeed(chalk.yellow('Git initialized'));
466
+ }
467
+
468
+ // Add backend modern packages
469
+ spinner = ora({
470
+ text: 'Adding backend modern packages',
471
+ spinner: 'flip',
472
+ color: 'magenta'
473
+ }).start();
474
+
475
+ try {
476
+ const backendPath = path.join(projectPath, 'backend');
477
+ execSync(`npm install @nestjs/config @nestjs/typeorm typeorm pg joi class-validator class-transformer helmet cors 2>&1 > /dev/null`, {
478
+ stdio: 'ignore',
479
+ cwd: backendPath
480
+ });
481
+ execSync(`npm install -D prettier @typescript-eslint/eslint-plugin eslint-config-prettier eslint-plugin-prettier 2>&1 > /dev/null`, {
482
+ stdio: 'ignore',
483
+ cwd: backendPath
484
+ });
485
+ spinner.succeed(chalk.green('Backend dependencies updated'));
486
+ } catch (error) {
487
+ spinner.succeed(chalk.yellow('Backend packages updated'));
488
+ }
489
+
490
+ // Create .prettierrc for backend
491
+ const prettierConfig = {
492
+ semi: true,
493
+ trailingComma: 'es5',
494
+ singleQuote: true,
495
+ printWidth: 100,
496
+ tabWidth: 2,
497
+ useTabs: false,
498
+ arrowParens: 'always',
499
+ endOfLine: 'lf'
500
+ };
501
+
502
+ fs.writeFileSync(path.join(projectPath, 'backend', '.prettierrc.json'), JSON.stringify(prettierConfig, null, 2));
503
+
504
+ // Setup StudentHelp Module
505
+ spinner = ora({
506
+ text: 'Setting up StudentHelp utility module',
507
+ spinner: 'dots3',
508
+ color: 'cyan'
509
+ }).start();
510
+
511
+ try {
512
+ const backendPath = path.join(projectPath, 'backend');
513
+ const srcPath = path.join(backendPath, 'src');
514
+ const studentHelpDir = path.join(srcPath, 'student-help');
515
+
516
+ fs.ensureDirSync(studentHelpDir);
517
+
518
+ // Get template files path - resolve from current script location
519
+ const __filename = fileURLToPath(import.meta.url);
520
+ const __dirname = path.dirname(__filename);
521
+ const templatesPath = path.join(__dirname, 'templates', 'backend');
522
+
523
+ // Read StudentHelp templates from disk
524
+ const serviceTemplatePath = path.join(templatesPath, 'student-help.service.ts');
525
+ const controllerTemplatePath = path.join(templatesPath, 'student-help.controller.ts');
526
+ const studentHelpModuleTemplate = `import { Global, Module } from '@nestjs/common';
527
+ import { StudentHelpService } from './student-help.service';
528
+ import { StudentHelpController } from './student-help.controller';
529
+
530
+ @Global()
531
+ @Module({
532
+ controllers: [StudentHelpController],
533
+ providers: [StudentHelpService],
534
+ exports: [StudentHelpService],
535
+ })
536
+ export class StudentHelpModule {}
537
+ `;
538
+
539
+ try {
540
+ // Check if template files exist
541
+ if (!fs.existsSync(serviceTemplatePath)) {
542
+ throw new Error(`Service template not found at: ${serviceTemplatePath}`);
543
+ }
544
+ if (!fs.existsSync(controllerTemplatePath)) {
545
+ throw new Error(`Controller template not found at: ${controllerTemplatePath}`);
546
+ }
547
+
548
+ const studentHelpServiceTemplate = fs.readFileSync(serviceTemplatePath, 'utf-8');
549
+ const studentHelpControllerTemplate = fs.readFileSync(controllerTemplatePath, 'utf-8');
550
+
551
+ fs.writeFileSync(path.join(studentHelpDir, 'student-help.module.ts'), studentHelpModuleTemplate);
552
+ fs.writeFileSync(path.join(studentHelpDir, 'student-help.service.ts'), studentHelpServiceTemplate);
553
+ fs.writeFileSync(path.join(studentHelpDir, 'student-help.controller.ts'), studentHelpControllerTemplate);
554
+ } catch (templateError) {
555
+ // Fallback to basic version if templates don't exist
556
+ const fallbackService = `import { Injectable } from '@nestjs/common';
557
+ import * as bcrypt from 'bcrypt';
558
+
559
+ export interface StudentUser {
560
+ id?: string;
561
+ email: string;
562
+ name: string;
563
+ password?: string;
564
+ }
565
+
566
+ export interface AuthResponse {
567
+ success: boolean;
568
+ message: string;
569
+ token?: string;
570
+ user?: StudentUser;
571
+ }
572
+
573
+ @Injectable()
574
+ export class StudentHelpService {
575
+ private users: Map<string, StudentUser> = new Map();
576
+ private sessions: Map<string, AuthResponse> = new Map();
577
+
578
+ async register(email: string, name: string, password: string): Promise<AuthResponse> {
579
+ try {
580
+ if (this._getUserByEmail(email)) {
581
+ return { success: false, message: 'User already exists' };
582
+ }
583
+ const hashedPassword = await bcrypt.hash(password, 10);
584
+ const userId = \`user_\${Date.now()}\`;
585
+ const user: StudentUser = { id: userId, email, name, password: hashedPassword };
586
+ this.users.set(userId, user);
587
+ return { success: true, message: 'User registered successfully', user: { id: userId, email, name } };
588
+ } catch (error) {
589
+ return { success: false, message: \`Registration failed: \${error.message}\` };
590
+ }
591
+ }
592
+
593
+ async login(email: string, password: string): Promise<AuthResponse> {
594
+ try {
595
+ const user = this._getUserByEmail(email);
596
+ if (!user || !user.password) {
597
+ return { success: false, message: 'Invalid credentials' };
598
+ }
599
+ const isPasswordValid = await bcrypt.compare(password, user.password);
600
+ if (!isPasswordValid) {
601
+ return { success: false, message: 'Invalid credentials' };
602
+ }
603
+ const response: AuthResponse = { success: true, message: 'Login successful', user: { id: user.id, email: user.email, name: user.name } };
604
+ this.sessions.set(user.id || '', response);
605
+ return response;
606
+ } catch (error) {
607
+ return { success: false, message: \`Login failed: \${error.message}\` };
608
+ }
609
+ }
610
+
611
+ async logout(userId: string): Promise<AuthResponse> {
612
+ try {
613
+ this.sessions.delete(userId);
614
+ return { success: true, message: 'Logout successful' };
615
+ } catch (error) {
616
+ return { success: false, message: \`Logout failed: \${error.message}\` };
617
+ }
618
+ }
619
+
620
+ getUser(userId: string): StudentUser | null {
621
+ const user = this.users.get(userId);
622
+ if (user) {
623
+ const { password, ...userWithoutPassword } = user;
624
+ return userWithoutPassword as StudentUser;
625
+ }
626
+ return null;
627
+ }
628
+
629
+ getAllUsers(): StudentUser[] {
630
+ const users: StudentUser[] = [];
631
+ this.users.forEach((user) => {
632
+ const { password, ...userWithoutPassword } = user;
633
+ users.push(userWithoutPassword as StudentUser);
634
+ });
635
+ return users;
636
+ }
637
+
638
+ async updateUser(userId: string, updateData: Partial<StudentUser>): Promise<AuthResponse> {
639
+ try {
640
+ const user = this.users.get(userId);
641
+ if (!user) {
642
+ return { success: false, message: 'User not found' };
643
+ }
644
+ if (updateData.name) user.name = updateData.name;
645
+ if (updateData.email) user.email = updateData.email;
646
+ return { success: true, message: 'User updated successfully', user: { id: user.id, email: user.email, name: user.name } };
647
+ } catch (error) {
648
+ return { success: false, message: \`Update failed: \${error.message}\` };
649
+ }
650
+ }
651
+
652
+ async deleteUser(userId: string): Promise<AuthResponse> {
653
+ try {
654
+ const user = this.users.get(userId);
655
+ if (!user) {
656
+ return { success: false, message: 'User not found' };
657
+ }
658
+ this.users.delete(userId);
659
+ this.sessions.delete(userId);
660
+ return { success: true, message: 'User deleted successfully' };
661
+ } catch (error) {
662
+ return { success: false, message: \`Delete failed: \${error.message}\` };
663
+ }
664
+ }
665
+
666
+ isAuthenticated(userId: string): boolean {
667
+ return this.sessions.has(userId);
668
+ }
669
+
670
+ private _getUserByEmail(email: string): StudentUser | null {
671
+ for (const user of this.users.values()) {
672
+ if (user.email === email) {
673
+ return user;
674
+ }
675
+ }
676
+ return null;
677
+ }
678
+
679
+ getInfo() {
680
+ return { name: 'StudentHelp', version: '2.0.0', description: 'Global utility service for student applications' };
681
+ }
682
+ }
683
+ `;
684
+ const fallbackController = `import { Controller, Post, Get, Body, Param } from '@nestjs/common';
685
+ import { StudentHelpService, AuthResponse } from './student-help.service';
686
+
687
+ @Controller('student-help')
688
+ export class StudentHelpController {
689
+ constructor(private studentHelpService: StudentHelpService) {}
690
+
691
+ @Post('register')
692
+ async register(@Body() body: { email: string; name: string; password: string }): Promise<AuthResponse> {
693
+ return this.studentHelpService.register(body.email, body.name, body.password);
694
+ }
695
+
696
+ @Post('login')
697
+ async login(@Body() body: { email: string; password: string }): Promise<AuthResponse> {
698
+ return this.studentHelpService.login(body.email, body.password);
699
+ }
700
+
701
+ @Post('logout/:userId')
702
+ async logout(@Param('userId') userId: string): Promise<AuthResponse> {
703
+ return this.studentHelpService.logout(userId);
704
+ }
705
+
706
+ @Get('user/:userId')
707
+ getUser(@Param('userId') userId: string) {
708
+ return this.studentHelpService.getUser(userId);
709
+ }
710
+
711
+ @Get('users')
712
+ getAllUsers() {
713
+ return this.studentHelpService.getAllUsers();
714
+ }
715
+
716
+ @Get('info')
717
+ getInfo() {
718
+ return this.studentHelpService.getInfo();
719
+ }
720
+ }
721
+ `;
722
+ fs.writeFileSync(path.join(studentHelpDir, 'student-help.module.ts'), studentHelpModuleTemplate);
723
+ fs.writeFileSync(path.join(studentHelpDir, 'student-help.service.ts'), fallbackService);
724
+ fs.writeFileSync(path.join(studentHelpDir, 'student-help.controller.ts'), fallbackController);
725
+ }
726
+
727
+ // Read and modify app.module.ts
728
+ const appModulePath = path.join(srcPath, 'app.module.ts');
729
+ let appModuleContent = fs.readFileSync(appModulePath, 'utf-8');
730
+
731
+ // Add StudentHelpModule import if not already present
732
+ if (!appModuleContent.includes('StudentHelpModule')) {
733
+ // Add import statement at the beginning
734
+ appModuleContent = appModuleContent.replace(
735
+ "import { Module } from '@nestjs/common';",
736
+ "import { Module } from '@nestjs/common';\nimport { StudentHelpModule } from './student-help/student-help.module';"
737
+ );
738
+
739
+ // Add to imports array
740
+ appModuleContent = appModuleContent.replace(
741
+ 'imports: [',
742
+ 'imports: [StudentHelpModule,'
743
+ );
744
+
745
+ fs.writeFileSync(appModulePath, appModuleContent);
746
+ }
747
+
748
+ // Install bcrypt if not already installed
749
+ try {
750
+ execSync('npm list bcrypt 2>&1 | grep bcrypt', { stdio: 'ignore', cwd: backendPath });
751
+ } catch {
752
+ execSync('npm install bcrypt 2>&1 > /dev/null', { stdio: 'ignore', cwd: backendPath });
753
+ }
754
+
755
+ spinner.succeed(chalk.green('StudentHelp module setup complete'));
756
+ } catch (error) {
757
+ spinner.fail(chalk.red('Error setting up StudentHelp module'));
758
+ console.error(error.message);
759
+ }
760
+
761
+ // Success banner
762
+ console.log(chalk.hex('#00FF00').bold('\n' + '═'.repeat(60)));
763
+ console.log(chalk.hex('#00FF00').bold('║') + chalk.hex('#39FF14').bold(' ✨ Project Created Successfully Fasttt! ✨').padEnd(58) + chalk.hex('#00FF00').bold('║'));
764
+ console.log(chalk.hex('#00FF00').bold('═'.repeat(60)));
765
+
766
+ console.log(chalk.cyan.bold('\n📂 Next Steps:\n'));
767
+ console.log(chalk.white(` ${chalk.hex('#00D9FF')('$')} cd ${projectName}`));
768
+ console.log(chalk.white(` ${chalk.hex('#00D9FF')('$')} docker-compose up -d\n`));
769
+
770
+ console.log(chalk.hex('#FFD700').bold('🌐 Access Points:\n'));
771
+ console.log(chalk.white(` Frontend: ${chalk.hex('#FF00FF').bold('http://localhost:3001')}`));
772
+ console.log(chalk.white(` Backend: ${chalk.hex('#00FFFF').bold('http://localhost:3000')}`));
773
+ console.log(chalk.white(` Database: ${chalk.hex('#FFD700').bold('localhost:5432')}\n`));
774
+
775
+ console.log(chalk.hex('#39FF14').bold('💡 Useful Commands:\n'));
776
+ console.log(chalk.white(` student-help generate:auth`));
777
+ console.log(chalk.white(` student-help generate:resource User\n`));
778
+
779
+ console.log(chalk.hex('#00FF00').bold('═'.repeat(60) + '\n'));
780
+ };
781
+
782
+ // ============================================
783
+ // Command: dev
784
+ // ============================================
785
+ const devCommand = async () => {
786
+ printBanner();
787
+
788
+ const spinner = ora({
789
+ text: 'Starting Docker containers',
790
+ spinner: 'dots3',
791
+ color: 'cyan'
792
+ }).start();
793
+
794
+ try {
795
+ execSync('docker-compose up -d', { stdio: 'ignore' });
796
+ spinner.succeed(chalk.green('Docker containers running'));
797
+
798
+ console.log(chalk.blue.bold('\n⏳ Waiting for services to initialize...\n'));
799
+ await new Promise(resolve => setTimeout(resolve, 3000));
800
+
801
+ console.log(chalk.green.bold('✓ Development Environment Ready!\n'));
802
+ console.log(chalk.cyan.bold('Access:\n'));
803
+ console.log(chalk.white(` Frontend: ${chalk.yellow.bold('http://localhost:3001')}`));
804
+ console.log(chalk.white(` Backend: ${chalk.yellow.bold('http://localhost:3000')}`));
805
+ console.log(chalk.white(` Database: ${chalk.yellow.bold('localhost:5432')}\n`));
806
+
807
+ console.log(chalk.yellow('To stop: docker-compose down\n'));
808
+ } catch (error) {
809
+ spinner.fail(chalk.red('Error starting environment'));
810
+ throw error;
811
+ }
812
+ };
813
+
814
+ // ============================================
815
+ // Command: db <database>
816
+ // ============================================
817
+ const dbCommand = async (database) => {
818
+ console.log(chalk.blue(`\n📦 Database Information: ${database}\n`));
819
+
820
+ switch (database.toLowerCase()) {
821
+ case 'postgres':
822
+ case 'postgresql':
823
+ console.log(chalk.green('✅ PostgreSQL is included!\n'));
824
+ console.log(chalk.white('Credentials:'));
825
+ console.log(chalk.cyan(' Host: postgres (Docker) / localhost (local)'));
826
+ console.log(chalk.cyan(' User: student'));
827
+ console.log(chalk.cyan(' Password: student123\n'));
828
+ break;
829
+
830
+ case 'mongodb':
831
+ case 'mongo':
832
+ console.log(chalk.yellow('To add MongoDB, edit docker-compose.yml\n'));
833
+ break;
834
+
835
+ case 'redis':
836
+ console.log(chalk.yellow('To add Redis, edit docker-compose.yml\n'));
837
+ break;
838
+
839
+ default:
840
+ console.log(chalk.red(`Database '${database}' not recognized`));
841
+ console.log(chalk.cyan('Available: postgres, mongodb, redis\n'));
842
+ }
843
+ };
844
+
845
+ // ============================================
846
+ // Command: generate:auth
847
+ // ============================================
848
+ const generateAuthCommand = async () => {
849
+ const spinner = ora({
850
+ text: 'Generating Auth module',
851
+ spinner: 'dots3',
852
+ color: 'magenta'
853
+ }).start();
854
+
855
+ try {
856
+ execSync('cd backend && nest g module auth 2>&1 > /dev/null', { stdio: 'ignore' });
857
+ execSync('cd backend && nest g controller auth 2>&1 > /dev/null', { stdio: 'ignore' });
858
+ execSync('cd backend && nest g service auth 2>&1 > /dev/null', { stdio: 'ignore' });
859
+
860
+ spinner.succeed(chalk.green('Module created'));
861
+
862
+ const depsSpinner = ora({
863
+ text: 'Installing dependencies',
864
+ spinner: 'arc',
865
+ color: 'blue'
866
+ }).start();
867
+
868
+ execSync('cd backend && npm install @nestjs/jwt @nestjs/passport passport passport-jwt @types/passport-jwt bcrypt 2>&1 > /dev/null', { stdio: 'ignore' });
869
+ execSync('cd backend && npm install -D @types/bcrypt 2>&1 > /dev/null', { stdio: 'ignore' });
870
+
871
+ depsSpinner.succeed(chalk.green('Dependencies installed'));
872
+
873
+ console.log(chalk.green.bold('\n✓ Auth Module Created!\n'));
874
+ console.log(chalk.yellow('Next steps:\n'));
875
+ console.log(chalk.white(' 1. Configure JWT_SECRET in .env'));
876
+ console.log(chalk.white(' 2. Implement logic in backend/src/auth/'));
877
+ console.log(chalk.white(' 3. Register module in app.module.ts\n'));
878
+ } catch (error) {
879
+ spinner.fail(chalk.red('Error generating auth'));
880
+ throw error;
881
+ }
882
+ };
883
+
884
+ // ============================================
885
+ // Command: generate:resource <name>
886
+ // ============================================
887
+ const generateResourceCommand = async (resourceName) => {
888
+ const resourceLower = resourceName.toLowerCase();
889
+ const resourcePascal = resourceName.charAt(0).toUpperCase() + resourceName.slice(1);
890
+
891
+ const spinner = ora({
892
+ text: `Generating ${resourcePascal} CRUD`,
893
+ spinner: 'bouncingBar',
894
+ color: 'yellow'
895
+ }).start();
896
+
897
+ try {
898
+ const backendPath = path.join(process.cwd(), 'backend/src', resourceLower);
899
+ fs.ensureDirSync(backendPath);
900
+
901
+ // Module
902
+ const moduleContent = [
903
+ 'import { Module } from \'@nestjs/common\';',
904
+ `import { ${resourcePascal}Service } from './${resourceLower}.service';`,
905
+ `import { ${resourcePascal}Controller } from './${resourceLower}.controller';`,
906
+ '',
907
+ `@Module({`,
908
+ ` controllers: [${resourcePascal}Controller],`,
909
+ ` providers: [${resourcePascal}Service],`,
910
+ `})`,
911
+ `export class ${resourcePascal}Module {}`,
912
+ ].join('\n');
913
+
914
+ fs.writeFileSync(path.join(backendPath, `${resourceLower}.module.ts`), moduleContent);
915
+
916
+ // Service
917
+ const serviceContent = [
918
+ 'import { Injectable } from \'@nestjs/common\';',
919
+ `import { Create${resourcePascal}Dto } from './dto/create-${resourceLower}.dto';`,
920
+ `import { Update${resourcePascal}Dto } from './dto/update-${resourceLower}.dto';`,
921
+ '',
922
+ '@Injectable()',
923
+ `export class ${resourcePascal}Service {`,
924
+ ` create(create${resourcePascal}Dto: Create${resourcePascal}Dto) {`,
925
+ ` return 'Create endpoint';`,
926
+ ` }`,
927
+ ` findAll() {`,
928
+ ` return 'Find all endpoint';`,
929
+ ` }`,
930
+ ` findOne(id: number) {`,
931
+ ` return { id };`,
932
+ ` }`,
933
+ ` update(id: number, update${resourcePascal}Dto: Update${resourcePascal}Dto) {`,
934
+ ` return { id, data: update${resourcePascal}Dto };`,
935
+ ` }`,
936
+ ` remove(id: number) {`,
937
+ ` return { id };`,
938
+ ` }`,
939
+ `}`,
940
+ ].join('\n');
941
+
942
+ fs.writeFileSync(path.join(backendPath, `${resourceLower}.service.ts`), serviceContent);
943
+
944
+ // Controller
945
+ const controllerContent = [
946
+ 'import {',
947
+ ' Controller,',
948
+ ' Get,',
949
+ ' Post,',
950
+ ' Body,',
951
+ ' Patch,',
952
+ ' Param,',
953
+ ' Delete,',
954
+ '} from \'@nestjs/common\';',
955
+ `import { ${resourcePascal}Service } from './${resourceLower}.service';`,
956
+ `import { Create${resourcePascal}Dto } from './dto/create-${resourceLower}.dto';`,
957
+ `import { Update${resourcePascal}Dto } from './dto/update-${resourceLower}.dto';`,
958
+ '',
959
+ `@Controller('${resourceLower}')`,
960
+ `export class ${resourcePascal}Controller {`,
961
+ ` constructor(private readonly ${resourceLower}Service: ${resourcePascal}Service) {}`,
962
+ '',
963
+ ` @Post()`,
964
+ ` create(@Body() create${resourcePascal}Dto: Create${resourcePascal}Dto) {`,
965
+ ` return this.${resourceLower}Service.create(create${resourcePascal}Dto);`,
966
+ ` }`,
967
+ ` @Get()`,
968
+ ` findAll() {`,
969
+ ` return this.${resourceLower}Service.findAll();`,
970
+ ` }`,
971
+ ` @Get(':id')`,
972
+ ` findOne(@Param('id') id: string) {`,
973
+ ` return this.${resourceLower}Service.findOne(+id);`,
974
+ ` }`,
975
+ ` @Patch(':id')`,
976
+ ` update(`,
977
+ ` @Param('id') id: string,`,
978
+ ` @Body() update${resourcePascal}Dto: Update${resourcePascal}Dto,`,
979
+ ` ) {`,
980
+ ` return this.${resourceLower}Service.update(+id, update${resourcePascal}Dto);`,
981
+ ` }`,
982
+ ` @Delete(':id')`,
983
+ ` remove(@Param('id') id: string) {`,
984
+ ` return this.${resourceLower}Service.remove(+id);`,
985
+ ` }`,
986
+ `}`,
987
+ ].join('\n');
988
+
989
+ fs.writeFileSync(path.join(backendPath, `${resourceLower}.controller.ts`), controllerContent);
990
+
991
+ // DTOs
992
+ const dtoDir = path.join(backendPath, 'dto');
993
+ fs.ensureDirSync(dtoDir);
994
+
995
+ const createDtoContent = `export class Create${resourcePascal}Dto {
996
+ name: string;
997
+ description?: string;
998
+ email?: string;
999
+ }`;
1000
+
1001
+ const updateDtoContent = `export class Update${resourcePascal}Dto {
1002
+ name?: string;
1003
+ description?: string;
1004
+ email?: string;
1005
+ }`;
1006
+
1007
+ fs.writeFileSync(path.join(dtoDir, `create-${resourceLower}.dto.ts`), createDtoContent);
1008
+ fs.writeFileSync(path.join(dtoDir, `update-${resourceLower}.dto.ts`), updateDtoContent);
1009
+
1010
+ // Frontend
1011
+ const frontendPath = path.join(process.cwd(), 'frontend/app', resourceLower);
1012
+ fs.ensureDirSync(frontendPath);
1013
+
1014
+ const pageContent = `'use client';
1015
+
1016
+ import React, { useState } from 'react';
1017
+
1018
+ interface ${resourcePascal} {
1019
+ id?: number;
1020
+ name: string;
1021
+ description?: string;
1022
+ email?: string;
1023
+ }
1024
+
1025
+ export default function ${resourcePascal}Page() {
1026
+ const [items, setItems] = useState<${resourcePascal}[]>([]);
1027
+ const [form, setForm] = useState<${resourcePascal}>({
1028
+ name: '',
1029
+ description: '',
1030
+ email: '',
1031
+ });
1032
+
1033
+ const handleSubmit = (e: React.FormEvent) => {
1034
+ e.preventDefault();
1035
+ if (form.name) {
1036
+ setItems([...items, { ...form, id: Date.now() }]);
1037
+ setForm({ name: '', description: '', email: '' });
1038
+ }
1039
+ };
1040
+
1041
+ return (
1042
+ <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 py-12 px-4">
1043
+ <div className="max-w-4xl mx-auto">
1044
+ <div className="bg-white rounded-lg shadow-xl p-8">
1045
+ <h1 className="text-4xl font-bold text-gray-900 mb-2">${resourcePascal} Management</h1>
1046
+ <p className="text-gray-600 mb-8">Create and manage ${resourceLower} records</p>
1047
+
1048
+ <form onSubmit={handleSubmit} className="mb-8">
1049
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
1050
+ <input
1051
+ type="text"
1052
+ placeholder="Name"
1053
+ value={form.name}
1054
+ onChange={(e) => setForm({ ...form, name: e.target.value })}
1055
+ className="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
1056
+ />
1057
+ <input
1058
+ type="email"
1059
+ placeholder="Email"
1060
+ value={form.email || ''}
1061
+ onChange={(e) => setForm({ ...form, email: e.target.value })}
1062
+ className="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
1063
+ />
1064
+ <input
1065
+ type="text"
1066
+ placeholder="Description"
1067
+ value={form.description || ''}
1068
+ onChange={(e) => setForm({ ...form, description: e.target.value })}
1069
+ className="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
1070
+ />
1071
+ </div>
1072
+ <button
1073
+ type="submit"
1074
+ className="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition"
1075
+ >
1076
+ Add ${resourcePascal}
1077
+ </button>
1078
+ </form>
1079
+
1080
+ <div className="grid gap-4">
1081
+ {items.length === 0 ? (
1082
+ <p className="text-center text-gray-400 py-8">No ${resourceLower}s created yet</p>
1083
+ ) : (
1084
+ items.map((item) => (
1085
+ <div key={item.id} className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition">
1086
+ <h3 className="font-semibold text-lg text-gray-900">{item.name}</h3>
1087
+ {item.email && <p className="text-sm text-gray-600">{item.email}</p>}
1088
+ {item.description && <p className="text-sm text-gray-500">{item.description}</p>}
1089
+ </div>
1090
+ ))
1091
+ )}
1092
+ </div>
1093
+ </div>
1094
+ </div>
1095
+ </div>
1096
+ );
1097
+ }`;
1098
+
1099
+ fs.writeFileSync(path.join(frontendPath, 'page.tsx'), pageContent);
1100
+
1101
+ spinner.succeed(chalk.green(`${resourcePascal} resource generated`));
1102
+
1103
+ console.log(chalk.green.bold(`\n✓ ${resourcePascal} Created!\n`));
1104
+ console.log(chalk.yellow('Next steps:\n'));
1105
+ console.log(chalk.white(` 1. Register module in backend/src/app.module.ts`));
1106
+ console.log(chalk.white(` 2. Implement logic in backend/src/${resourceLower}/`));
1107
+ console.log(chalk.white(` 3. Customize component in frontend/app/${resourceLower}/page.tsx\n`));
1108
+ } catch (error) {
1109
+ spinner.fail(chalk.red('Error generating resource'));
1110
+ throw error;
1111
+ }
1112
+ };
1113
+
1114
+ // ============================================
1115
+ // CLI Setup
1116
+ // ============================================
1117
+ const program = new Command();
1118
+
1119
+ program
1120
+ .version('2.0.0')
1121
+ .description(chalk.hex('#FF00FF').bold('✨ Student Help Framework') + ' - Create full-stack apps in minutes');
1122
+
1123
+ program
1124
+ .command('new <projectName>')
1125
+ .description('✨ Create a new full-stack project with NestJS + Next.js + PostgreSQL')
1126
+ .action(async (projectName) => {
1127
+ try {
1128
+ await newCommand(projectName);
1129
+ } catch (error) {
1130
+ console.error(chalk.red('\n❌ Error:'), error.message);
1131
+ process.exit(1);
1132
+ }
1133
+ });
1134
+
1135
+ program
1136
+ .command('dev')
1137
+ .description('🚀 Start development environment with Docker')
1138
+ .action(async () => {
1139
+ try {
1140
+ await devCommand();
1141
+ } catch (error) {
1142
+ console.error(chalk.red('\n❌ Error:'), error.message);
1143
+ process.exit(1);
1144
+ }
1145
+ });
1146
+
1147
+ program
1148
+ .command('db <database>')
1149
+ .description('📦 Database information and setup guide')
1150
+ .action(async (database) => {
1151
+ try {
1152
+ await dbCommand(database);
1153
+ } catch (error) {
1154
+ console.error(chalk.red('\n❌ Error:'), error.message);
1155
+ process.exit(1);
1156
+ }
1157
+ });
1158
+
1159
+ program
1160
+ .command('generate:auth')
1161
+ .description('🔐 Generate authentication module with JWT support')
1162
+ .action(async () => {
1163
+ try {
1164
+ await generateAuthCommand();
1165
+ } catch (error) {
1166
+ console.error(chalk.red('\n❌ Error:'), error.message);
1167
+ process.exit(1);
1168
+ }
1169
+ });
1170
+
1171
+ program
1172
+ .command('generate:resource <resourceName>')
1173
+ .description('🛠️ Generate CRUD resource (backend + frontend)')
1174
+ .action(async (resourceName) => {
1175
+ try {
1176
+ await generateResourceCommand(resourceName);
1177
+ } catch (error) {
1178
+ console.error(chalk.red('\n❌ Error:'), error.message);
1179
+ process.exit(1);
1180
+ }
1181
+ });
1182
+
1183
+ // ============================================
1184
+ // Command: commands
1185
+ // ============================================
1186
+ const commandsCommand = () => {
1187
+ printBanner();
1188
+
1189
+ console.log(chalk.hex('#FFD700').bold('\n📚 AVAILABLE COMMANDS\n'));
1190
+ console.log(chalk.hex('#00D9FF').bold('═'.repeat(60) + '\n'));
1191
+
1192
+ const commands = [
1193
+ {
1194
+ name: 'new <projectName>',
1195
+ emoji: '✨',
1196
+ description: 'Create a new full-stack project',
1197
+ details: 'Sets up NestJS backend, Next.js frontend, PostgreSQL, Docker Compose,\nand all modern dependencies in one command.',
1198
+ example: 'student-help new my-awesome-app'
1199
+ },
1200
+ {
1201
+ name: 'dev',
1202
+ emoji: '🚀',
1203
+ description: 'Start development environment',
1204
+ details: 'Launches Docker Compose with all services:\nBackend (port 3000), Frontend (port 3001), Database (port 5432)',
1205
+ example: 'student-help dev'
1206
+ },
1207
+ {
1208
+ name: 'db <database>',
1209
+ emoji: '📦',
1210
+ description: 'Database information and setup',
1211
+ details: 'Shows credentials and setup instructions for supported databases.\nSupported: postgres, mongodb, redis',
1212
+ example: 'student-help db postgres'
1213
+ },
1214
+ {
1215
+ name: 'generate:auth',
1216
+ emoji: '🔐',
1217
+ description: 'Generate authentication module',
1218
+ details: 'Creates Auth Module with JWT support, Passport.js integration,\nand bcrypt password hashing setup.',
1219
+ example: 'student-help generate:auth'
1220
+ },
1221
+ {
1222
+ name: 'generate:resource <name>',
1223
+ emoji: '🛠️',
1224
+ description: 'Generate CRUD resource',
1225
+ details: 'Auto-generates complete CRUD operations for backend (NestJS) and\nfrontend (React component) with database DTOs.',
1226
+ example: 'student-help generate:resource Post'
1227
+ },
1228
+ {
1229
+ name: 'commands',
1230
+ emoji: '📚',
1231
+ description: 'List all available commands',
1232
+ details: 'Shows this help menu with detailed information about each command.',
1233
+ example: 'student-help commands'
1234
+ },
1235
+ {
1236
+ name: 'features',
1237
+ emoji: '⚡',
1238
+ description: 'Show advanced features',
1239
+ details: 'Displays how to use advanced features from the features/ directory\nincluding custom modules and utilities.',
1240
+ example: 'student-help features'
1241
+ }
1242
+ ];
1243
+
1244
+ commands.forEach((cmd, index) => {
1245
+ console.log(chalk.hex('#FF00FF').bold(`${cmd.emoji} ${cmd.name.padEnd(30)} ${cmd.description}`));
1246
+ console.log(chalk.gray(` ${cmd.details}`));
1247
+ console.log(chalk.hex('#00D9FF')(` $ ${cmd.example}`));
1248
+ if (index < commands.length - 1) {
1249
+ console.log('');
1250
+ }
1251
+ });
1252
+
1253
+ console.log(chalk.hex('#00D9FF').bold('\n' + '═'.repeat(60)));
1254
+ console.log(chalk.hex('#39FF14').bold('\n💡 Quick Start:'));
1255
+ console.log(chalk.white(' 1. student-help new my-app'));
1256
+ console.log(chalk.white(' 2. cd my-app'));
1257
+ console.log(chalk.white(' 3. docker-compose up -d'));
1258
+ console.log(chalk.white(' 4. Visit http://localhost:3001'));
1259
+ console.log(chalk.hex('#00D9FF').bold('\n' + '═'.repeat(60) + '\n'));
1260
+ };
1261
+
1262
+ // ============================================
1263
+ // Command: features
1264
+ // ============================================
1265
+ const featuresCommand = () => {
1266
+ printBanner();
1267
+
1268
+ console.log(chalk.hex('#FFD700').bold('\n⚡ ADVANCED FEATURES & UTILITIES\n'));
1269
+ console.log(chalk.hex('#00D9FF').bold('═'.repeat(60) + '\n'));
1270
+
1271
+ const features = [
1272
+ {
1273
+ category: '📁 Features Directory',
1274
+ items: [
1275
+ {
1276
+ name: 'Custom Middleware',
1277
+ description: 'Add authentication, logging, and error handling middleware'
1278
+ },
1279
+ {
1280
+ name: 'Database Utilities',
1281
+ description: 'Pre-configured database connection pools and migrations'
1282
+ },
1283
+ {
1284
+ name: 'API Response Formatters',
1285
+ description: 'Standardized response formats for your API'
1286
+ },
1287
+ {
1288
+ name: 'Error Handlers',
1289
+ description: 'Global error handling with custom error messages'
1290
+ }
1291
+ ]
1292
+ },
1293
+ {
1294
+ category: '🔧 Configuration Options',
1295
+ items: [
1296
+ {
1297
+ name: 'Environment Variables',
1298
+ description: 'Use .env.example as template for your configuration'
1299
+ },
1300
+ {
1301
+ name: 'Docker Compose Override',
1302
+ description: 'Create docker-compose.override.yml for local development changes'
1303
+ },
1304
+ {
1305
+ name: 'Database Migrations',
1306
+ description: 'Use TypeORM CLI: npm run migration:generate'
1307
+ }
1308
+ ]
1309
+ },
1310
+ {
1311
+ category: '📚 Additional Commands',
1312
+ items: [
1313
+ {
1314
+ name: 'Backend Scripts',
1315
+ description: 'npm run start:dev (watch mode)\n npm run build (production build)\n npm run test (run tests)'
1316
+ },
1317
+ {
1318
+ name: 'Frontend Scripts',
1319
+ description: 'npm run dev (dev server)\n npm run build (production build)\n npm run lint (check code quality)'
1320
+ }
1321
+ ]
1322
+ },
1323
+ {
1324
+ category: '🔐 Security Features',
1325
+ items: [
1326
+ {
1327
+ name: 'JWT Authentication',
1328
+ description: 'Pre-configured with Passport.js and bcrypt'
1329
+ },
1330
+ {
1331
+ name: 'CORS Configuration',
1332
+ description: 'Cross-Origin Resource Sharing setup in backend'
1333
+ },
1334
+ {
1335
+ name: 'Environment Security',
1336
+ description: 'Sensitive data protected via .env files'
1337
+ }
1338
+ ]
1339
+ },
1340
+ {
1341
+ category: '🚀 Performance Features',
1342
+ items: [
1343
+ {
1344
+ name: 'Code Splitting',
1345
+ description: 'Next.js automatic code splitting on frontend'
1346
+ },
1347
+ {
1348
+ name: 'Caching',
1349
+ description: 'Built-in caching strategies with Redis support'
1350
+ },
1351
+ {
1352
+ name: 'Database Indexing',
1353
+ description: 'Recommendations for optimal query performance'
1354
+ }
1355
+ ]
1356
+ }
1357
+ ];
1358
+
1359
+ features.forEach((section) => {
1360
+ console.log(chalk.hex('#FF00FF').bold(section.category));
1361
+ console.log('');
1362
+
1363
+ section.items.forEach((item) => {
1364
+ console.log(chalk.hex('#00D9FF')(` ✓ ${item.name}`));
1365
+ item.description.split('\n').forEach(line => {
1366
+ console.log(chalk.gray(` ${line}`));
1367
+ });
1368
+ });
1369
+
1370
+ console.log('');
1371
+ });
1372
+
1373
+ console.log(chalk.hex('#00D9FF').bold('═'.repeat(60)));
1374
+ console.log(chalk.hex('#39FF14').bold('\n📖 Documentation:'));
1375
+ console.log(chalk.white(' - Backend: /backend/README.md'));
1376
+ console.log(chalk.white(' - Frontend: /frontend/README.md'));
1377
+ console.log(chalk.white(' - Project: README.md'));
1378
+
1379
+ console.log(chalk.hex('#FFD700').bold('\n🔗 Useful Links:'));
1380
+ console.log(chalk.white(' - NestJS: https://docs.nestjs.com'));
1381
+ console.log(chalk.white(' - Next.js: https://nextjs.org/docs'));
1382
+ console.log(chalk.white(' - PostgreSQL: https://www.postgresql.org/docs'));
1383
+ console.log(chalk.white(' - Tailwind CSS: https://tailwindcss.com/docs'));
1384
+ console.log(chalk.white(' - Docker: https://docs.docker.com'));
1385
+ console.log(chalk.hex('#00D9FF').bold('\n' + '═'.repeat(60) + '\n'));
1386
+ };
1387
+
1388
+ // ============================================
1389
+ // Register commands
1390
+ // ============================================
1391
+ program
1392
+ .command('commands')
1393
+ .description('📚 List all available commands with descriptions')
1394
+ .action(() => {
1395
+ try {
1396
+ commandsCommand();
1397
+ } catch (error) {
1398
+ console.error(chalk.red('\n❌ Error:'), error.message);
1399
+ process.exit(1);
1400
+ }
1401
+ });
1402
+
1403
+ program
1404
+ .command('features')
1405
+ .description('⚡ Show advanced features and utilities available')
1406
+ .action(() => {
1407
+ try {
1408
+ featuresCommand();
1409
+ } catch (error) {
1410
+ console.error(chalk.red('\n❌ Error:'), error.message);
1411
+ process.exit(1);
1412
+ }
1413
+ });
1414
+
1415
+ program.parse(process.argv);