Spaces:
Sleeping
Sleeping
| console.log('\n[STARTUP] Process started. Loading modules...'); | |
| const globalStartTime = Date.now(); | |
| // Immediate heartbeat to reassure the user during heavy module loading | |
| const globalHeartbeat = setInterval(() => { | |
| const elapsed = Math.floor((Date.now() - globalStartTime) / 1000); | |
| console.log(`[STARTUP] Still loading modules... (${elapsed}s elapsed)`); | |
| }, 10000); | |
| import { NestFactory, Reflector } from '@nestjs/core'; | |
| import { | |
| ValidationPipe, | |
| ClassSerializerInterceptor, | |
| Logger, | |
| } from '@nestjs/common'; | |
| import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; | |
| import { AppModule } from './app.module'; | |
| const logger = new Logger('Bootstrap'); | |
| async function bootstrap() { | |
| clearInterval(globalHeartbeat); // Switch to Nest Logger heartbeat | |
| const bootstrapStartTime = Date.now(); | |
| const heartbeat = setInterval(() => { | |
| const elapsed = Math.floor((Date.now() - globalStartTime) / 1000); | |
| logger.log(`Startup in progress... (${elapsed}s elapsed)`); | |
| }, 10000); | |
| logger.log('Starting Nest application...'); | |
| const app = await NestFactory.create(AppModule, { | |
| logger: ['error', 'warn', 'fatal'], | |
| }); | |
| logger.log( | |
| `Nest application initialized (+${Date.now() - bootstrapStartTime}ms)`, | |
| ); | |
| // Enable CORS | |
| app.enableCors({ | |
| origin: (origin, callback) => { | |
| const normalizeOrigin = (value: string): string => { | |
| const trimmed = value.trim(); | |
| if (!trimmed) return ''; | |
| try { | |
| return new URL(trimmed).origin.toLowerCase(); | |
| } catch { | |
| return trimmed.replace(/\/+$/, '').toLowerCase(); | |
| } | |
| }; | |
| const normalizeEnvOrigins = (value?: string): string[] => | |
| (value || '') | |
| .split(',') | |
| .map((entry) => normalizeOrigin(entry)) | |
| .filter(Boolean); | |
| // Allow: no origin (file://, curl, Postman), localhost on any port, 127.0.0.1 on any port | |
| if (!origin) return callback(null, true); | |
| if (/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(origin)) | |
| return callback(null, true); | |
| const normalizedOrigin = normalizeOrigin(origin); | |
| const staticAllowedOrigins = [ | |
| normalizeOrigin('https://eduverse-pi.vercel.app'), | |
| ]; | |
| const allowedOrigins = [ | |
| ...normalizeEnvOrigins(process.env.CORS_ORIGIN), | |
| ...normalizeEnvOrigins(process.env.FRONTEND_URL), | |
| ...staticAllowedOrigins, | |
| ]; | |
| if (allowedOrigins.includes(normalizedOrigin)) return callback(null, true); | |
| callback(new Error(`CORS: origin ${origin} not allowed`)); | |
| }, | |
| credentials: true, | |
| }); | |
| // Global validation pipe | |
| app.useGlobalPipes( | |
| new ValidationPipe({ | |
| whitelist: true, | |
| forbidNonWhitelisted: true, | |
| transform: true, | |
| transformOptions: { | |
| enableImplicitConversion: true, | |
| }, | |
| }), | |
| ); | |
| // Global class serializer interceptor (for @Exclude() decorator support) | |
| app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); | |
| // Swagger: on in non-production by default; in production (e.g. Hugging Face) set ENABLE_SWAGGER=true | |
| const nodeEnv = (process.env.NODE_ENV || '').toLowerCase(); | |
| const isProd = nodeEnv === 'production' || nodeEnv === 'prod'; | |
| const enableSwagger = | |
| process.env.ENABLE_SWAGGER === 'true' || !isProd; | |
| if (enableSwagger) { | |
| const config = new DocumentBuilder() | |
| .setTitle('EduVerse API') | |
| .setDescription( | |
| ` | |
| ## EduVerse Backend API Documentation | |
| ### Overview | |
| EduVerse is a comprehensive educational platform API that provides endpoints for managing: | |
| - **Authentication & Authorization** - User registration, login, JWT tokens, and role management | |
| - **Campus Management** - Campuses, departments, programs, and semesters | |
| - **Course Management** - Courses, sections, and schedules | |
| - **Enrollment System** - Student course registration and waitlist management | |
| - **File Management** - File uploads, folders, and permissions | |
| - **YouTube Integration** - Video uploads and management | |
| ### Authentication | |
| Most endpoints require JWT Bearer token authentication. To authenticate: | |
| 1. Use the \`/api/auth/login\` endpoint to obtain an access token | |
| 2. Click the **Authorize** button (π) at the top of this page | |
| 3. Enter your token in the format: \`Bearer <your-access-token>\` | |
| 4. Click **Authorize** to apply the token to all requests | |
| ### Role-Based Access Control (RBAC) | |
| The API implements role-based access control with the following roles: | |
| | Role | Description | | |
| |------|-------------| | |
| | **STUDENT** | Regular student users - can enroll in courses, view materials | | |
| | **INSTRUCTOR** | Faculty members - can manage course content, view enrollments | | |
| | **TA** | Teaching assistants - limited instructor capabilities | | |
| | **ADMIN** | Administrative staff - can manage users, courses, and campus data | | |
| | **IT_ADMIN** | IT administrators - full system access | | |
| ### Response Codes | |
| | Code | Description | | |
| |------|-------------| | |
| | 200 | Success | | |
| | 201 | Created | | |
| | 204 | No Content (successful deletion) | | |
| | 400 | Bad Request - Invalid input data | | |
| | 401 | Unauthorized - Missing or invalid token | | |
| | 403 | Forbidden - Insufficient role permissions | | |
| | 404 | Not Found - Resource doesn't exist | | |
| | 409 | Conflict - Resource already exists | | |
| | 500 | Internal Server Error | | |
| `, | |
| ) | |
| .setVersion('1.0.0') | |
| .setContact('EduVerse Team', 'https://eduverse.com', 'support@eduverse.com') | |
| .setLicense('MIT', 'https://opensource.org/licenses/MIT') | |
| .addBearerAuth( | |
| { | |
| type: 'http', | |
| scheme: 'bearer', | |
| bearerFormat: 'JWT', | |
| name: 'JWT', | |
| description: | |
| 'Enter your JWT access token obtained from /api/auth/login', | |
| in: 'header', | |
| }, | |
| 'JWT-auth', | |
| ) | |
| .addTag( | |
| 'π Authentication', | |
| 'User authentication, registration, and session management', | |
| ) | |
| .addTag( | |
| 'π₯ User Management', | |
| 'Admin endpoints for managing users, roles, and permissions', | |
| ) | |
| .addTag('ποΈ Campus', 'Campus management endpoints') | |
| .addTag('π’ Departments', 'Department management within campuses') | |
| .addTag('π Programs', 'Academic program management') | |
| .addTag('π Semesters', 'Semester and academic period management') | |
| .addTag('π Courses', 'Course catalog and management') | |
| .addTag('π Course Sections', 'Course section and schedule management') | |
| .addTag( | |
| 'π Course Schedules', | |
| 'Course schedule (day/time) management per section', | |
| ) | |
| .addTag('β Enrollments', 'Student course enrollment and registration') | |
| .addTag('π Assignments', 'Assignment creation, submission, and grading') | |
| .addTag('π Grades', 'Grade management, GPA calculation, and transcripts') | |
| .addTag('π Rubrics', 'Grading rubric management') | |
| .addTag('π Files', 'File upload, download, and management') | |
| .addTag('π Folders', 'Folder organization and hierarchy') | |
| .addTag('π¬ YouTube', 'YouTube video integration and uploads') | |
| .addTag('π Attendance', 'Attendance session and record management') | |
| .addTag('π Quizzes', 'Quiz creation, questions, attempts, and grading') | |
| .addTag( | |
| 'Labs', | |
| 'Lab assignments, submissions, instructions, and attendance', | |
| ) | |
| .addTag( | |
| 'Notifications', | |
| 'User notifications, preferences, and admin broadcast', | |
| ) | |
| .addTag( | |
| 'π¬ Messaging', | |
| 'WhatsApp-like real-time messaging with conversations, read receipts, and file sharing', | |
| ) | |
| .addTag( | |
| 'π Discussions', | |
| 'Course discussion forums with threads, replies, pinning, and answer marking', | |
| ) | |
| .addTag( | |
| 'π’ Announcements', | |
| 'Course, department, and system-wide announcements with scheduling and targeting', | |
| ) | |
| .addTag( | |
| 'π Community Posts', | |
| 'Community forum posts with discussions, questions, and resource sharing', | |
| ) | |
| .addTag('π¬ Community Comments', 'Comments and replies on community posts') | |
| .addTag( | |
| 'π Forum Categories', | |
| 'Forum category management for organizing community discussions', | |
| ) | |
| .addTag( | |
| 'π Schedule', | |
| 'Personal schedule views with daily/weekly aggregation of classes, exams, and events', | |
| ) | |
| .addTag( | |
| 'π Exam Schedules', | |
| 'Exam scheduling management with conflict detection', | |
| ) | |
| .addTag( | |
| 'π Calendar Events', | |
| 'Personal and course calendar events management', | |
| ) | |
| .addTag( | |
| 'π Calendar Integrations', | |
| 'External calendar sync (Google Calendar, Outlook, iCal)', | |
| ) | |
| .addTag( | |
| 'π Course Materials', | |
| 'Course material upload, management, and YouTube video integration', | |
| ) | |
| .addTag( | |
| 'ποΈ Course Structure', | |
| 'Course content organization by lectures, sections, labs, and weeks', | |
| ) | |
| .build(); | |
| const swaggerStartTime = Date.now(); | |
| const document = SwaggerModule.createDocument(app, config); | |
| logger.log( | |
| `Swagger documentation generated (+${Date.now() - swaggerStartTime}ms)`, | |
| ); | |
| SwaggerModule.setup('api-docs', app, document, { | |
| customSiteTitle: 'EduVerse API Documentation', | |
| customfavIcon: 'https://nestjs.com/img/logo-small.svg', | |
| customCss: ` | |
| .swagger-ui .topbar { display: none } | |
| .swagger-ui .info .title { color: #3b82f6; } | |
| .swagger-ui .scheme-container { background: #f8fafc; padding: 15px; border-radius: 8px; } | |
| `, | |
| swaggerOptions: { | |
| persistAuthorization: true, | |
| docExpansion: 'none', | |
| filter: true, | |
| showRequestDuration: true, | |
| syntaxHighlight: { | |
| activate: true, | |
| theme: 'monokai', | |
| }, | |
| }, | |
| }); | |
| } else { | |
| logger.log('Swagger disabled in development (set ENABLE_SWAGGER=true to override)'); | |
| } | |
| const port = process.env.PORT || 8081; | |
| await app.listen(port, '0.0.0.0'); | |
| clearInterval(heartbeat); | |
| const totalTime = ((Date.now() - globalStartTime) / 1000).toFixed(2); | |
| logger.log( | |
| `π Application is running on: http://localhost:${port} (Total startup time: ${totalTime}s)`, | |
| ); | |
| logger.log(`π Swagger API Documentation: http://localhost:${port}/api-docs`); | |
| } | |
| bootstrap(); | |