From f72bcc69aa90aeb39ce0edbcbeae5a7d826b8e21 Mon Sep 17 00:00:00 2001 From: 5t4l1n Date: Tue, 29 Jul 2025 00:13:52 +0530 Subject: [PATCH] update error --- backend/main.py | 121 ++++--- backend/routes/auth.py | 2 +- backend/routes/dashboard.py | 2 +- frontend/app/auth/login/page.tsx | 201 ++++++------ frontend/app/dashboard/page.tsx | 453 ++++++++++++++++++++------ frontend/components/ErrorBoundary.tsx | 26 ++ frontend/context/auth-context.tsx | 206 +++++++----- 7 files changed, 670 insertions(+), 341 deletions(-) create mode 100644 frontend/components/ErrorBoundary.tsx diff --git a/backend/main.py b/backend/main.py index 8333445..18306c1 100644 --- a/backend/main.py +++ b/backend/main.py @@ -13,6 +13,9 @@ from pymongo import MongoClient from bson import ObjectId import hashlib import time +import signal +import io +from contextlib import redirect_stdout, redirect_stderr # Load environment variables load_dotenv() @@ -276,7 +279,7 @@ def get_comprehensive_stats(): # Calculate real-time statistics current_time = datetime.now() join_date = user_stats.get('join_date', current_time - timedelta(days=30)) if user_stats else current_time - timedelta(days=30) - days_since_join = (current_time - join_date).days if days_since_join > 0 else 30 + days_since_join = (current_time - join_date).days if (current_time - join_date).days > 0 else 30 # ✅ ENHANCED: Calculate coding streak with proper logic coding_streak = calculate_coding_streak(db, user_id) @@ -381,34 +384,41 @@ def get_recent_activity(): ] for collection, activity_type, default_title, default_points in activity_sources: - recent_items = collection.find( - {"user_id": user_id} - ).sort([("completed_at", -1), ("submitted_at", -1), ("earned_at", -1), ("issued_at", -1)]).limit(max_records // len(activity_sources)) - - for item in recent_items: - # Determine the completion date field - completed_at = ( - item.get('completed_at') or - item.get('submitted_at') or - item.get('earned_at') or - item.get('issued_at') or - datetime.now() - ) + try: + recent_items = collection.find( + {"user_id": user_id} + ).sort([("completed_at", -1), ("submitted_at", -1), ("earned_at", -1), ("issued_at", -1)]).limit(max_records // len(activity_sources)) - if isinstance(completed_at, str): - completed_at = datetime.fromisoformat(completed_at) - - activities.append({ - "id": str(item.get('_id', uuid.uuid4())), - "type": activity_type, - "title": item.get('title', item.get('name', default_title)), - "description": format_activity_description(item, activity_type), - "completed_at": completed_at.isoformat(), - "points_earned": item.get('points', item.get('points_earned', default_points)), - "success_rate": item.get('score', item.get('completion_percentage', 100)), - "difficulty": item.get('difficulty', 'Intermediate'), - "blockchain_verified": item.get('blockchain_verified', False) - }) + for item in recent_items: + # Determine the completion date field + completed_at = ( + item.get('completed_at') or + item.get('submitted_at') or + item.get('earned_at') or + item.get('issued_at') or + datetime.now() + ) + + if isinstance(completed_at, str): + try: + completed_at = datetime.fromisoformat(completed_at) + except: + completed_at = datetime.now() + + activities.append({ + "id": str(item.get('_id', uuid.uuid4())), + "type": activity_type, + "title": item.get('title', item.get('name', default_title)), + "description": format_activity_description(item, activity_type), + "completed_at": completed_at.isoformat(), + "points_earned": item.get('points', item.get('points_earned', default_points)), + "success_rate": item.get('score', item.get('completion_percentage', 100)), + "difficulty": item.get('difficulty', 'Intermediate'), + "blockchain_verified": item.get('blockchain_verified', False) + }) + except Exception as e: + logger.warning(f"⚠️ Failed to fetch {activity_type} activities: {e}") + continue # Sort all activities by completion date activities.sort(key=lambda x: x['completed_at'], reverse=True) @@ -559,7 +569,10 @@ def calculate_coding_streak(db, user_id): for submission in submissions: submission_date = submission.get('submitted_at') if isinstance(submission_date, str): - submission_date = datetime.fromisoformat(submission_date).date() + try: + submission_date = datetime.fromisoformat(submission_date).date() + except: + continue elif isinstance(submission_date, datetime): submission_date = submission_date.date() else: @@ -860,15 +873,11 @@ def format_activity_description(item, activity_type): return "Activity completed" # =================================================================== -# ✅ ENHANCED DYNAMIC SCORING SYSTEM - WITH YOUR UPDATES +# ✅ ENHANCED DYNAMIC SCORING SYSTEM # =================================================================== def calculate_dynamic_score(code, language, problem): """Enhanced dynamic scoring with better error handling and feedback""" - import io - from contextlib import redirect_stdout, redirect_stderr - import time - import signal # Handle both old and new problem formats test_cases = problem.get('test_cases', []) @@ -906,11 +915,24 @@ def calculate_dynamic_score(code, language, problem): # ✅ ENHANCED: Safer execution environment exec_globals = { "__builtins__": { - **__builtins__, - '__import__': None, # Disable imports for security - 'open': None, # Disable file operations - 'eval': None, # Disable eval - 'exec': None, # Disable nested exec + 'print': print, + 'len': len, + 'str': str, + 'int': int, + 'float': float, + 'list': list, + 'dict': dict, + 'tuple': tuple, + 'set': set, + 'range': range, + 'enumerate': enumerate, + 'zip': zip, + 'sum': sum, + 'max': max, + 'min': min, + 'sorted': sorted, + 'abs': abs, + 'round': round, }, "__name__": "__main__" } @@ -923,18 +945,25 @@ def calculate_dynamic_score(code, language, problem): else: exec_globals['input'] = lambda prompt='': '' - # ✅ ADDED: Timeout protection - def timeout_handler(signum, frame): - raise TimeoutError("Code execution timed out") - - signal.signal(signal.SIGALRM, timeout_handler) - signal.alarm(5) # 5 second timeout + # ✅ ADDED: Timeout protection (Unix-like systems only) + try: + def timeout_handler(signum, frame): + raise TimeoutError("Code execution timed out") + + signal.signal(signal.SIGALRM, timeout_handler) + signal.alarm(5) # 5 second timeout + except: + # Skip timeout on Windows + pass try: with redirect_stdout(stdout_buffer), redirect_stderr(stderr_buffer): exec(code, exec_globals) finally: - signal.alarm(0) # Cancel timeout + try: + signal.alarm(0) # Cancel timeout + except: + pass actual_output = stdout_buffer.getvalue().strip() stderr_content = stderr_buffer.getvalue().strip() diff --git a/backend/routes/auth.py b/backend/routes/auth.py index a59be19..bd7d18d 100644 --- a/backend/routes/auth.py +++ b/backend/routes/auth.py @@ -157,4 +157,4 @@ def verify_signature(): return jsonify({ "success": False, "error": str(e) - }), 500 + }), 500 \ No newline at end of file diff --git a/backend/routes/dashboard.py b/backend/routes/dashboard.py index 79fe2dd..28456de 100644 --- a/backend/routes/dashboard.py +++ b/backend/routes/dashboard.py @@ -824,4 +824,4 @@ def dashboard_root(): "/api/dashboard/update-profile" ], "authentication": "JWT Token in Authorization header OR Wallet address in X-Wallet-Address header" - }) + }) \ No newline at end of file diff --git a/frontend/app/auth/login/page.tsx b/frontend/app/auth/login/page.tsx index 77a05b7..bf60b89 100644 --- a/frontend/app/auth/login/page.tsx +++ b/frontend/app/auth/login/page.tsx @@ -1,81 +1,101 @@ "use client" -import { useState, useEffect, useRef } from "react" +import { useEffect, useRef, useState } from "react" import { useRouter } from "next/navigation" import { useAuth } from "@/context/auth-context" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Separator } from "@/components/ui/separator" -import { Alert, AlertDescription } from "@/components/ui/alert" -import { Wallet, Mail, Lock, Loader2, Shield, CheckCircle2, AlertCircle } from "lucide-react" +import { Wallet, Mail, Lock, Loader2, CheckCircle2 } from "lucide-react" import { toast } from "react-hot-toast" export default function LoginPage() { const { - connectWallet, - loginWithEmail, - isLoadingAuth, + user, + firebaseUser, walletConnected, walletAddress, - firebaseUser, - authMethod + isLoadingAuth, + authMethod, + connectWallet, + loginWithEmail } = useAuth() const router = useRouter() + const hasRedirected = useRef(false) const [email, setEmail] = useState("") const [password, setPassword] = useState("") const [isEmailLogin, setIsEmailLogin] = useState(false) - const [isConnectingWallet, setIsConnectingWallet] = useState(false) const [isSubmittingEmail, setIsSubmittingEmail] = useState(false) - const hasRedirected = useRef(false) - // ✅ Check for existing authentication + // ✅ FIXED: More comprehensive redirect logic with debug logging useEffect(() => { - if (hasRedirected.current || isLoadingAuth) return + console.log("🔍 Login page - checking auth state:", { + isLoadingAuth, + hasRedirected: hasRedirected.current, + user: !!user, + firebaseUser: !!firebaseUser, + walletConnected, + walletAddress, + authMethod + }) - const checkAuth = setTimeout(() => { - if (isLoadingAuth) return + // Don't redirect if still loading or already redirected + if (isLoadingAuth || hasRedirected.current) { + console.log("⏳ Skipping redirect - loading or already redirected") + return + } - const isAuthenticated = (walletConnected && walletAddress) || firebaseUser + // Check for successful authentication + const isMetaMaskAuth = walletConnected && walletAddress && user && authMethod === "metamask" + const isFirebaseAuth = firebaseUser && authMethod === "firebase" + const isAuthenticated = isMetaMaskAuth || isFirebaseAuth - if (isAuthenticated && !hasRedirected.current) { - console.log('✅ User already authenticated, redirecting to dashboard...') - hasRedirected.current = true + console.log("🔍 Authentication check:", { + isMetaMaskAuth, + isFirebaseAuth, + isAuthenticated + }) + + if (isAuthenticated && !hasRedirected.current) { + console.log("✅ User authenticated - redirecting to dashboard...") + hasRedirected.current = true + + // Add a small delay to ensure state is fully updated + setTimeout(() => { router.replace("/dashboard") - } - }, 500) + }, 100) + } + }, [ + user, + firebaseUser, + walletConnected, + walletAddress, + authMethod, + isLoadingAuth, + router + ]) // ✅ FIXED: Include all necessary dependencies - return () => clearTimeout(checkAuth) - }, [isLoadingAuth, walletConnected, walletAddress, firebaseUser, router]) - - // ✅ Handle MetaMask connection - const handleWalletConnect = async () => { - if (isConnectingWallet || isLoadingAuth) return - - setIsConnectingWallet(true) - + // ✅ Handle MetaMask connection with immediate redirect check + const handleMetaMaskLogin = async () => { try { - console.log('🦊 Starting MetaMask connection...') + console.log("🦊 Starting MetaMask login...") + await connectWallet() + console.log("🦊 MetaMask login completed, checking for redirect...") - // Check if MetaMask is installed - if (typeof window !== 'undefined' && !window.ethereum) { - toast.error("MetaMask not detected. Please install MetaMask extension.") - window.open('https://metamask.io/download/', '_blank') - return - } - - const success = await connectWallet() - - if (success) { - console.log('✅ MetaMask connection successful') - // Redirect will be handled by useEffect - } - } catch (error: any) { - console.error('❌ Wallet connection error:', error) - } finally { - setIsConnectingWallet(false) + // Force a redirect check after a short delay + setTimeout(() => { + const isAuth = walletConnected && walletAddress && user && authMethod === "metamask" + if (isAuth && !hasRedirected.current) { + console.log("🔄 Force redirecting after MetaMask success...") + hasRedirected.current = true + router.replace("/dashboard") + } + }, 500) + } catch (error) { + console.error("❌ MetaMask login failed:", error) } } @@ -83,8 +103,6 @@ export default function LoginPage() { const handleEmailLogin = async (e: React.FormEvent) => { e.preventDefault() - if (isSubmittingEmail || isLoadingAuth) return - if (!email.trim() || !password.trim()) { toast.error("Please enter both email and password") return @@ -96,34 +114,39 @@ export default function LoginPage() { await loginWithEmail(email, password) // Redirect will be handled by useEffect } catch (error: any) { - console.error('❌ Email login failed:', error) - toast.error(error.message || "Login failed. Please check your credentials.") + console.error("❌ Email login failed:", error) + toast.error(error.message || "Login failed") } finally { setIsSubmittingEmail(false) } } - // Show connected state - if ((walletConnected && walletAddress) || firebaseUser) { + // ✅ Show success state when authenticated but not yet redirected + const isAuthenticated = (walletConnected && walletAddress && user) || firebaseUser + + if (isAuthenticated && !hasRedirected.current) { return (
- {walletConnected ? "MetaMask Connected! 🦊" : "Email Login Successful! 📧"} + Login Successful! ✅ - - - {walletConnected - ? `🦊 ${walletAddress?.slice(0, 6)}...${walletAddress?.slice(-4)}` - : `📧 ${firebaseUser?.email}` - } - - +

+ {authMethod === "metamask" + ? `🦊 MetaMask connected: ${walletAddress?.slice(0, 6)}...${walletAddress?.slice(-4)}` + : `📧 Email: ${firebaseUser?.email}` + } +

+
+ + Redirecting to dashboard... +
+ {/* Manual redirect button as backup */}
@@ -141,26 +164,11 @@ export default function LoginPage() { ) } - // Show loading while initializing - if (isLoadingAuth) { - return ( -
-
- -

Initializing...

-
-
- ) - } - + // ✅ Show login form return (
-
- -
- Welcome to OpenLearnX! 🎓 @@ -170,11 +178,11 @@ export default function LoginPage() { {/* MetaMask Login */}
@@ -231,14 +239,14 @@ export default function LoginPage() { value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Enter your password" - disabled={isSubmittingEmail || isConnectingWallet} + disabled={isSubmittingEmail || isLoadingAuth} required />
- - {/* MetaMask Installation Help */} - {typeof window !== 'undefined' && !window.ethereum && ( - - - - MetaMask not detected. - - - - )} diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx index 3289175..5007923 100644 --- a/frontend/app/dashboard/page.tsx +++ b/frontend/app/dashboard/page.tsx @@ -1,121 +1,374 @@ "use client" +import { useAuth } from "@/context/auth-context" import { useEffect, useState } from "react" import { useRouter } from "next/navigation" -import { useAuth } from "@/context/auth-context" -import { DashboardStatsOverview } from "@/components/dashboard-stats" -import { Loader2, AlertCircle } from "lucide-react" -import { Button } from "@/components/ui/button" +import { + User, + LogOut, + Settings, + Trophy, + BookOpen, + Target, + TrendingUp, + Wallet, + Mail, + Calendar, + Award, + BarChart3, + Activity, + Edit3, + Save, + X +} from "lucide-react" export default function DashboardPage() { - const { isLoadingAuth, walletConnected, walletAddress, firebaseUser, authMethod } = useAuth() + const { user, firebaseUser, walletConnected, logout, authMethod } = useAuth() const router = useRouter() - const [showDashboard, setShowDashboard] = useState(false) - const [debugInfo, setDebugInfo] = useState(null) + const [isEditingProfile, setIsEditingProfile] = useState(false) + const [profileData, setProfileData] = useState({ + name: user?.name || '', + bio: user?.bio || '', + avatar: user?.avatar || '' + }) + const [stats, setStats] = useState({ + coursesCompleted: 12, + totalXP: 2450, + currentStreak: 7, + rank: 156, + certificatesEarned: 3, + hoursLearned: 45 + }) + useEffect(() => { - // Debug authentication state - const authState = { - isLoadingAuth, - walletConnected, - walletAddress: !!walletAddress, - firebaseUser: !!firebaseUser, - authMethod, - localStorage: { - token: !!localStorage.getItem('openlearnx_jwt_token'), - wallet: !!localStorage.getItem('openlearnx_wallet'), - user: !!localStorage.getItem('openlearnx_user') - } + if (!user && !firebaseUser) { + router.replace("/auth/login") } - - setDebugInfo(authState) - console.log('📊 Dashboard auth state:', authState) + }, [user, firebaseUser, router]) - // Give auth some time to initialize - const timer = setTimeout(() => { - const isAuthenticated = (walletConnected && walletAddress) || firebaseUser - - if (isAuthenticated) { - console.log('✅ User authenticated, showing dashboard') - setShowDashboard(true) - } else if (!isLoadingAuth) { - console.log('❌ User not authenticated, redirecting to login') - router.replace("/auth/login") - } - }, 2000) // Wait 2 seconds for auth to stabilize + const handleProfileUpdate = async () => { + try { + // Here you would call your API to update profile + // await updateProfile(profileData) + setIsEditingProfile(false) + console.log("Profile updated:", profileData) + } catch (error) { + console.error("Failed to update profile:", error) + } + } - return () => clearTimeout(timer) - }, [isLoadingAuth, walletConnected, walletAddress, firebaseUser, authMethod, router]) - - // Show loading state - if (isLoadingAuth || !showDashboard) { + if (!user && !firebaseUser) { return ( -
-
- -
-

- Loading Dashboard... -

-

- {walletConnected ? `Connected to ${walletAddress?.slice(0, 6)}...${walletAddress?.slice(-4)}` : - firebaseUser ? `Logged in as ${firebaseUser.email}` : - 'Verifying authentication...'} -

-
- - {/* Debug info in development */} - {process.env.NODE_ENV === 'development' && debugInfo && ( -
- Debug Info -
{JSON.stringify(debugInfo, null, 2)}
-
- )} -
+
+
) } - // Show error state if no auth after loading - if (!walletConnected && !firebaseUser && !isLoadingAuth) { - return ( -
-
- -

- Authentication Required -

-

- Please log in to access your dashboard. -

-
- - + return ( +
+ {/* Professional Header */} +
+
+
+
+
+
+ +
+
+

+ OpenLearnX +

+

Learn • Earn • Grow

+
+
+
+ +
+ + +
- - {/* Debug info */} - {process.env.NODE_ENV === 'development' && ( -
- Debug Info -
{JSON.stringify(debugInfo, null, 2)}
-
- )}
-
- ) - } + - // Show dashboard if authenticated - return + {/* Main Dashboard Content */} +
+ {/* Welcome Section */} +
+
+
+
+

+ Welcome back! 👋 +

+

+ Ready to continue your learning journey? +

+ {authMethod === "metamask" && user ? ( +
+ + + Connected: {user.wallet_address.slice(0, 6)}...{user.wallet_address.slice(-4)} + +
+ ) : firebaseUser && ( +
+ + + {firebaseUser.email} + +
+ )} +
+
+
+ +
+
+
+
+
+ + {/* Stats Grid */} +
+
+
+
+

Total XP

+

{stats.totalXP.toLocaleString()}

+
+
+ +
+
+
+ + +12% from last week +
+
+ +
+
+
+

Courses

+

{stats.coursesCompleted}

+
+
+ +
+
+
+ + 3 in progress +
+
+ +
+
+
+

Streak

+

{stats.currentStreak} days

+
+
+ +
+
+
+ 🔥 Keep it up! +
+
+ +
+
+
+

Global Rank

+

#{stats.rank}

+
+
+ +
+
+
+ + Top 5% learner +
+
+
+ + {/* Main Content Grid */} +
+ {/* Profile Card with Edit Functionality */} +
+
+
+

Profile

+ +
+ +
+
+ +
+ {isEditingProfile ? ( +
+ setProfileData({...profileData, name: e.target.value})} + placeholder="Your name" + className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-center" + /> +