diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4ebee68 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,52 @@ +# Git +.git +.gitignore +README.md + +# Node modules +node_modules +frontend/node_modules +frontend/.next + +# Python +__pycache__ +*.pyc +*.pyo +*.pyd +venv +.pytest_cache + +# Environment files +.env* +!.env.example + +# IDE +.vscode +.idea +*.swp +*.swo + +# Logs +*.log +logs +backend/openlearnx.log + +# OS +.DS_Store +Thumbs.db + +# Docker +Dockerfile* +docker-compose*.yml +.dockerignore + +# Temporary +tmp/ +temp/ +cache/ +backend/cache/ + +# Build artifacts +dist/ +build/ +out/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3a27ad3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,160 @@ +# Multi-stage Dockerfile for OpenLearnX Platform +FROM node:18-alpine AS frontend-builder + +# Build Frontend +WORKDIR /app/frontend +COPY frontend/package.json frontend/pnpm-lock.yaml* ./ +RUN corepack enable pnpm && pnpm install --frozen-lockfile + +COPY frontend/ ./ +ENV NEXT_TELEMETRY_DISABLED=1 +RUN pnpm run build + +# Main Python image with everything +FROM python:3.10-slim + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV TF_CPP_MIN_LOG_LEVEL=2 +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +# Install system dependencies (Python + Node.js) +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + gcc \ + g++ \ + libpq-dev \ + curl \ + wget \ + ca-certificates \ + gnupg \ + nginx \ + supervisor \ + && curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ + && apt-get install -y nodejs \ + && npm install -g pnpm \ + && rm -rf /var/lib/apt/lists/* + +# Set work directory +WORKDIR /app + +# Copy and install Python dependencies +COPY backend/requirements.txt ./backend/ +RUN pip install --no-cache-dir --upgrade pip \ + && pip install --no-cache-dir -r backend/requirements.txt + +# Copy backend code +COPY backend/ ./backend/ + +# Copy built frontend from previous stage +COPY --from=frontend-builder /app/frontend/.next/standalone ./frontend/ +COPY --from=frontend-builder /app/frontend/.next/static ./frontend/.next/static +COPY --from=frontend-builder /app/frontend/public ./frontend/public + +# Create necessary directories +RUN mkdir -p /var/log/supervisor \ + && mkdir -p /app/logs \ + && mkdir -p /app/uploads + +# Configure Nginx +COPY </dev/null 2>&1 || { print_error "Docker not found. Please install Docker."; exit 1; } +command -v docker-compose >/dev/null 2>&1 || { print_error "docker-compose not found. Please install docker-compose."; exit 1; } + +# Check if Docker is running +if ! docker info >/dev/null 2>&1; then + print_error "Docker is not running. Please start Docker first." + exit 1 +fi + +print_success "Prerequisites check passed!" + +# Create necessary directories +print_status "Creating necessary directories..." +mkdir -p backend/uploads backend/logs + +# Stop existing containers +print_status "Stopping existing containers..." +docker-compose down -v 2>/dev/null || true + +# Clean up old images +print_status "Cleaning up old images..." +docker system prune -f + +# Build the single container +print_status "Building OpenLearnX single container..." +docker-compose build --no-cache openlearnx + +# Start all services +print_status "Starting all services..." +docker-compose up -d + +# Wait for services to be ready +print_status "Waiting for services to start..." +sleep 60 + +# Health checks +print_status "Performing health checks..." + +check_service() { + local service=$1 + local url=$2 + local max_attempts=30 + local attempt=1 + + while [ $attempt -le $max_attempts ]; do + if curl -f "$url" >/dev/null 2>&1; then + print_success "$service is healthy!" + return 0 + fi + print_status "Waiting for $service... (attempt $attempt/$max_attempts)" + sleep 5 + ((attempt++)) + done + + print_warning "$service health check failed after $max_attempts attempts" + return 1 +} + +# Check all services +check_service "Main Application" "http://localhost/health" +check_service "Frontend" "http://localhost/" +check_service "Backend API" "http://localhost/api/health" +check_service "Database" "http://localhost:5432" || print_warning "Database connection check skipped" + +# Display service status +print_status "Service Status:" +docker-compose ps + +# Display logs for debugging if needed +print_status "Recent logs:" +docker-compose logs --tail=10 openlearnx + +# Display URLs and information +echo "" +echo "🎉 OpenLearnX Platform Deployed Successfully!" +echo "=============================================" +echo "🌐 Main Application: http://localhost" +echo "🔧 Backend API: http://localhost/api/" +echo "📱 Frontend: http://localhost/" +echo "🗄️ Database: postgresql://postgres:openlearnx123@localhost:5432/openlearnx" +echo "📊 Redis: redis://localhost:6379" +echo "" +echo "📋 Useful Commands:" +echo " - View logs: docker-compose logs -f openlearnx" +echo " - View all logs: docker-compose logs -f" +echo " - Stop services: docker-compose down" +echo " - Restart: docker-compose restart openlearnx" +echo " - Enter container: docker-compose exec openlearnx bash" +echo " - View processes: docker-compose exec openlearnx supervisorctl status" +echo "" +echo "🔍 Monitoring:" +echo " - Application logs: docker-compose exec openlearnx tail -f /var/log/supervisor/backend.out.log" +echo " - Frontend logs: docker-compose exec openlearnx tail -f /var/log/supervisor/frontend.out.log" +echo " - Nginx logs: docker-compose exec openlearnx tail -f /var/log/supervisor/nginx.out.log" +echo "" + +# Optional: Open browser +if command -v xdg-open &> /dev/null; then + print_status "Opening application in browser..." + xdg-open http://localhost +elif command -v open &> /dev/null; then + print_status "Opening application in browser..." + open http://localhost +fi + +print_success "Single container deployment completed! 🐳🚀" +print_status "Your OpenLearnX platform is running in a single Docker container with all services!" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..37887c0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,69 @@ +version: '3.8' + +services: + # Main OpenLearnX Application + openlearnx: + build: + context: . + dockerfile: Dockerfile + ports: + - "80:80" + environment: + - DATABASE_URL=postgresql://postgres:openlearnx123@postgres:5432/openlearnx + - REDIS_URL=redis://redis:6379 + - FLASK_ENV=production + - NODE_ENV=production + - TF_CPP_MIN_LOG_LEVEL=2 + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_started + restart: unless-stopped + networks: + - openlearnx-network + volumes: + - app_uploads:/app/uploads + - app_logs:/app/logs + + # PostgreSQL Database + postgres: + image: postgres:15-alpine + environment: + POSTGRES_DB: openlearnx + POSTGRES_USER: postgres + POSTGRES_PASSWORD: openlearnx123 + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + networks: + - openlearnx-network + + # Redis Cache + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + command: redis-server --appendonly yes + restart: unless-stopped + networks: + - openlearnx-network + +networks: + openlearnx-network: + driver: bridge + +volumes: + postgres_data: + redis_data: + app_uploads: + app_logs: diff --git a/frontend/app/coding/page.tsx b/frontend/app/coding/page.tsx index 078cfb7..9f5d73a 100644 --- a/frontend/app/coding/page.tsx +++ b/frontend/app/coding/page.tsx @@ -1,7 +1,7 @@ 'use client' import React, { useState, useEffect } from 'react' import { useRouter } from 'next/navigation' -import { Play, Lock, Shield, AlertTriangle, Users, Trophy, Clock } from 'lucide-react' +import { Play, Lock, Shield, AlertTriangle, Users, Trophy, Clock, Code, Zap, Sparkles, Star, Brain, CheckCircle, XCircle } from 'lucide-react' type UserRole = 'selector' | 'host' | 'participant' type ExamStatus = 'waiting' | 'active' | 'completed' @@ -139,7 +139,6 @@ export default function CodingExamPlatform() { exam_details: data.exam_details || {} })) - // ✅ ENHANCED SUCCESS MESSAGE WITH REDIRECT INFO alert(`✅ Exam Created Successfully! 📝 Exam Code: ${participantCode} @@ -251,6 +250,7 @@ Redirecting to exam interface...`) } const submitSolution = async () => { + setIsExecuting(true) try { const response = await fetch('http://127.0.0.1:5000/api/exam/submit-solution', { method: 'POST', @@ -265,6 +265,8 @@ Redirecting to exam interface...`) } } catch (error) { alert('Failed to submit solution') + } finally { + setIsExecuting(false) } } @@ -284,108 +286,315 @@ Redirecting to exam interface...`) return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` } - // Role Selection Screen + // Role Selection Screen with Enhanced Animations if (userRole === 'selector') { return ( -
-
-

OpenLearnX Coding Exam

+
+ {/* Animated Background Elements */} +
+
+
+
-
- + {/* Floating particles */} +
+
+
+
+ + {/* Floating sparkles */} +
+ +
+
+ +
+ +
+ {/* Card shine effect */} +
+ +
+
+
+ +
+

+ OpenLearnX Coding Exam +

+

+ Choose your role to get started +

+
- +
+ + + +
+ + {/* Animated footer */} +
+

+ Secure • Real-time • Professional +

+
) } - // Host Setup Screen + // Host Setup Screen with Enhanced UI if (userRole === 'host' && !examId) { return ( -
-
-

Host Coding Exam

+
+ {/* Enhanced background animations */} +
+
+
+
+
+ + {/* Floating icons */} +
+ +
+
+ +
+ +
+ {/* Enhanced shine effect */} +
-
- setParticipantName(e.target.value)} - className="w-full p-3 border border-gray-300 rounded-lg" - /> +
+
+
+
+ +
+ {/* Floating particles around icon */} +
+
+
+

+ Host Coding Exam +

+

+ Create a secure coding environment for your participants +

+
- -
- - {/* Debug Info */} -
-

Will create with host_name: "{participantName}"

-

✅ Will display exam_code (6 chars), not exam_id

-

🔄 After creation → redirect to /coding/host/[examCode]

+
+
+ setParticipantName(e.target.value)} + className="w-full p-4 border-2 border-gray-200 rounded-xl text-lg transition-all duration-300 focus:ring-4 focus:ring-blue-200 focus:border-blue-500 hover:border-blue-300 bg-gray-50 hover:bg-white focus:bg-white group" + /> + {/* Input decoration */} +
+ +
+
+ + +
+ + {/* Enhanced Debug Info */} +
+
+
+ System Status +
+
+

+ + Will create with host_name: "{participantName}" +

+

+ + Will display exam_code (6 chars), not exam_id +

+

+ + After creation → redirect to /coding/host/[examCode] +

+
+
) } - // Join Exam Screen + // Join Exam Screen with Enhanced Animations if (userRole === 'participant' && !examInfo) { return ( -
-
-

Join Coding Exam

+
+ {/* Enhanced background effects */} +
+
+
+
+
+ + {/* Animated particles */} +
+
+
+
+
+ +
+ {/* Enhanced card effects */} +
-
- setExamId(e.target.value.toUpperCase())} - className="w-full p-3 border border-gray-300 rounded-lg text-center font-mono text-lg tracking-widest uppercase" - maxLength={6} - /> +
+
+
+
+ +
+ {/* Animated ring around icon */} +
+
+
+

+ Join Coding Exam +

+

+ Enter the exam code to participate in the coding challenge +

+
- setParticipantName(e.target.value)} - className="w-full p-3 border border-gray-300 rounded-lg" - /> - - - - {/* Debug Info */} -
-

Will send: exam_code="{examId}" student_name="{participantName}"

-

✅ After join → redirect to /coding/exam

+
+
+ setExamId(e.target.value.toUpperCase())} + className="w-full p-4 border-2 border-gray-200 rounded-xl text-center font-mono text-2xl tracking-widest uppercase transition-all duration-300 focus:ring-4 focus:ring-green-200 focus:border-green-500 hover:border-green-300 bg-gray-50 hover:bg-white focus:bg-white relative group" + maxLength={6} + /> + {/* Input decorations */} +
+ {examId.length === 6 && ( +
+ +
+ )} +
+ +
+ setParticipantName(e.target.value)} + className="w-full p-4 border-2 border-gray-200 rounded-xl text-lg transition-all duration-300 focus:ring-4 focus:ring-green-200 focus:border-green-500 hover:border-green-300 bg-gray-50 hover:bg-white focus:bg-white group" + /> + {/* Name validation indicator */} + {participantName.length > 2 && ( +
+ +
+ )} +
+ + + + {/* Enhanced Debug Info */} +
+
+
+ Connection Status +
+
+

+ + Will send: exam_code="{examId}" student_name="{participantName}" +

+

+ + After join → redirect to /coding/exam +

+
+
@@ -393,139 +602,446 @@ Redirecting to exam interface...`) ) } - // System Requirements Check + // Enhanced System Requirements Check if (!systemChecked) { return ( -
-
-

System Requirements Check

+
+ {/* Animated warning elements */} +
+
+
+
+
+ + {/* Floating warning icons */} +
+ +
+
+ +
+ +
+ {/* Security-themed background */} +
-
-
- - Fullscreen mode support +
+
+
+
+ +
+ {/* Security rings */} +
+
+
+

+ System Requirements Check +

+

+ Preparing secure exam environment +

-
- - Copy/paste will be disabled + +
+
+ +
+ Fullscreen mode support +

Required for secure examination

+
+ +
+ +
+ +
+ Copy/paste will be disabled +

Prevents unauthorized assistance

+
+ +
+ +
+ +
+ Virtual environments will be detected +

Ensures exam integrity

+
+ +
-
- - Virtual environments will be detected + + + + {/* Security notice */} +
+
+ + Security Notice +
+

+ This exam uses advanced security measures. Browser restrictions will be enforced during the examination period. +

- -
) } - // Main Exam Interface + // Enhanced Main Exam Interface return ( -
- {/* Security Status Bar */} -
-
- - SECURE MODE ACTIVE - - Copy/Paste Disabled +
+ {/* Animated background elements */} +
+
+
+
+
+ + {/* Enhanced Security Status Bar */} +
+ {/* Security bar background animation */} +
+ +
+
+ + SECURE MODE ACTIVE +
+ +
+ + Copy/Paste Disabled +
+ +
+ + VM Detection Active +
-
+
{timeRemaining > 0 && ( -
- - {formatTime(timeRemaining)} +
+ + {formatTime(timeRemaining)} + {timeRemaining < 300 && ( // Last 5 minutes warning +
+ )}
)} - Exam: {examId} + +
+ Exam: {examId} +
- {/* Main Coding Area */} -
- {/* Problem Description */} -
-

Problem: String Capitalizer

-

Write a function that converts a string to uppercase.

-
- + {/* Enhanced Main Coding Area */} +
+ {/* Problem Description with Enhanced Styling */} +
+ {/* Card background animation */} +
+ +
+
+
+ +
+

Problem: String Capitalizer

+
+
+ +

+ Write a function that converts a string to uppercase. +

+ +
+
 {`def capitalize_string(text):
     # Your code here
     pass
 
 # Test: capitalize_string("hello") should return "HELLO"`}
-              
+                
+
-
- - {/* Code Editor */} -
-

Code Editor

-