Mark-Lasfar
		
	commited on
		
		
					Commit 
							
							·
						
						5da2025
	
1
								Parent(s):
							
							7f8c5ab
								
Update backend and server frontend for OAuth JSON response, client-side navigation, and add .gitignore
Browse files- api/auth.py +44 -7
- api/endpoints.py +10 -4
- requirements.txt +2 -1
- static/js/chat.js +34 -15
- templates/login.html +37 -37
    	
        api/auth.py
    CHANGED
    
    | @@ -16,6 +16,8 @@ import os | |
| 16 | 
             
            import logging
         | 
| 17 | 
             
            import secrets
         | 
| 18 | 
             
            import httpx
         | 
|  | |
|  | |
| 19 |  | 
| 20 | 
             
            from api.database import User, OAuthAccount, CustomSQLAlchemyUserDatabase, get_user_db
         | 
| 21 | 
             
            from api.models import UserRead, UserCreate, UserUpdate
         | 
| @@ -58,7 +60,7 @@ github_oauth_client = GitHubOAuth2(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET) | |
| 58 | 
             
            github_oauth_client._access_token_url = "https://github.com/login/oauth/access_token"
         | 
| 59 | 
             
            github_oauth_client._access_token_params = {"headers": {"Accept": "application/json"}}
         | 
| 60 |  | 
| 61 | 
            -
            # مدير المستخدمين | 
| 62 | 
             
            class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
         | 
| 63 | 
             
                reset_password_token_secret = SECRET
         | 
| 64 | 
             
                verification_token_secret = SECRET
         | 
| @@ -153,6 +155,15 @@ fastapi_users = FastAPIUsers[User, int]( | |
| 153 |  | 
| 154 | 
             
            current_active_user = fastapi_users.current_user(active=True, optional=True)
         | 
| 155 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 156 | 
             
            # --- إضافة custom authorize endpoints (يرجع JSON مع authorization_url) ---
         | 
| 157 |  | 
| 158 | 
             
            async def custom_google_authorize(
         | 
| @@ -187,13 +198,20 @@ async def custom_github_authorize( | |
| 187 |  | 
| 188 | 
             
            async def custom_oauth_callback(
         | 
| 189 | 
             
                code: str,
         | 
|  | |
| 190 | 
             
                user_manager: UserManager = Depends(get_user_manager),
         | 
| 191 | 
             
                oauth_client=Depends(lambda: google_oauth_client),
         | 
| 192 | 
             
                redirect_url: str = GOOGLE_REDIRECT_URL,
         | 
| 193 | 
             
                response: Response = None,
         | 
| 194 | 
             
            ):
         | 
| 195 | 
            -
                logger.debug(f"Processing Google callback with code: {code}")
         | 
| 196 | 
             
                try:
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 197 | 
             
                    # Get access token
         | 
| 198 | 
             
                    token_data = await oauth_client.get_access_token(code, redirect_url)
         | 
| 199 | 
             
                    access_token = token_data["access_token"]
         | 
| @@ -219,9 +237,10 @@ async def custom_oauth_callback( | |
| 219 | 
             
                        is_verified_by_default=True,
         | 
| 220 | 
             
                    )
         | 
| 221 |  | 
| 222 | 
            -
                     | 
| 223 | 
            -
                    token = await  | 
| 224 |  | 
|  | |
| 225 | 
             
                    cookie_transport.set_cookie(response, token)
         | 
| 226 |  | 
| 227 | 
             
                    return JSONResponse(content={
         | 
| @@ -230,17 +249,29 @@ async def custom_oauth_callback( | |
| 230 | 
             
                    }, status_code=200)
         | 
| 231 | 
             
                except Exception as e:
         | 
| 232 | 
             
                    logger.error(f"Error in Google OAuth callback: {str(e)}")
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 233 | 
             
                    return JSONResponse(content={"detail": str(e)}, status_code=400)
         | 
| 234 |  | 
| 235 | 
             
            async def custom_github_oauth_callback(
         | 
| 236 | 
             
                code: str,
         | 
|  | |
| 237 | 
             
                user_manager: UserManager = Depends(get_user_manager),
         | 
| 238 | 
             
                oauth_client=Depends(lambda: github_oauth_client),
         | 
| 239 | 
             
                redirect_url: str = GITHUB_REDIRECT_URL,
         | 
| 240 | 
             
                response: Response = None,
         | 
| 241 | 
             
            ):
         | 
| 242 | 
            -
                logger.debug(f"Processing GitHub callback with code: {code}")
         | 
| 243 | 
             
                try:
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 244 | 
             
                    # Get access token
         | 
| 245 | 
             
                    token_data = await oauth_client.get_access_token(code, redirect_url)
         | 
| 246 | 
             
                    access_token = token_data["access_token"]
         | 
| @@ -284,9 +315,10 @@ async def custom_github_oauth_callback( | |
| 284 | 
             
                        is_verified_by_default=True,
         | 
| 285 | 
             
                    )
         | 
| 286 |  | 
| 287 | 
            -
                     | 
| 288 | 
            -
                    token = await  | 
| 289 |  | 
|  | |
| 290 | 
             
                    cookie_transport.set_cookie(response, token)
         | 
| 291 |  | 
| 292 | 
             
                    return JSONResponse(content={
         | 
| @@ -295,6 +327,11 @@ async def custom_github_oauth_callback( | |
| 295 | 
             
                    }, status_code=200)
         | 
| 296 | 
             
                except Exception as e:
         | 
| 297 | 
             
                    logger.error(f"Error in GitHub OAuth callback: {str(e)}")
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 298 | 
             
                    return JSONResponse(content={"detail": str(e)}, status_code=400)
         | 
| 299 |  | 
| 300 | 
             
            # تضمين الراوترات داخل التطبيق
         | 
|  | |
| 16 | 
             
            import logging
         | 
| 17 | 
             
            import secrets
         | 
| 18 | 
             
            import httpx
         | 
| 19 | 
            +
            from datetime import datetime
         | 
| 20 | 
            +
            import jwt  # إضافة مكتبة pyjwt لتوليد الـ token يدويًا
         | 
| 21 |  | 
| 22 | 
             
            from api.database import User, OAuthAccount, CustomSQLAlchemyUserDatabase, get_user_db
         | 
| 23 | 
             
            from api.models import UserRead, UserCreate, UserUpdate
         | 
|  | |
| 60 | 
             
            github_oauth_client._access_token_url = "https://github.com/login/oauth/access_token"
         | 
| 61 | 
             
            github_oauth_client._access_token_params = {"headers": {"Accept": "application/json"}}
         | 
| 62 |  | 
| 63 | 
            +
            # مدير المستخدمين
         | 
| 64 | 
             
            class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
         | 
| 65 | 
             
                reset_password_token_secret = SECRET
         | 
| 66 | 
             
                verification_token_secret = SECRET
         | 
|  | |
| 155 |  | 
| 156 | 
             
            current_active_user = fastapi_users.current_user(active=True, optional=True)
         | 
| 157 |  | 
| 158 | 
            +
            # دالة مساعدة لتوليد JWT token يدويًا
         | 
| 159 | 
            +
            async def generate_jwt_token(user: User, secret: str, lifetime_seconds: int) -> str:
         | 
| 160 | 
            +
                payload = {
         | 
| 161 | 
            +
                    "sub": str(user.id),
         | 
| 162 | 
            +
                    "aud": "fastapi-users:auth",
         | 
| 163 | 
            +
                    "exp": int(datetime.utcnow().timestamp()) + lifetime_seconds,
         | 
| 164 | 
            +
                }
         | 
| 165 | 
            +
                return jwt.encode(payload, secret, algorithm="HS256")
         | 
| 166 | 
            +
             | 
| 167 | 
             
            # --- إضافة custom authorize endpoints (يرجع JSON مع authorization_url) ---
         | 
| 168 |  | 
| 169 | 
             
            async def custom_google_authorize(
         | 
|  | |
| 198 |  | 
| 199 | 
             
            async def custom_oauth_callback(
         | 
| 200 | 
             
                code: str,
         | 
| 201 | 
            +
                state: Optional[str] = None,
         | 
| 202 | 
             
                user_manager: UserManager = Depends(get_user_manager),
         | 
| 203 | 
             
                oauth_client=Depends(lambda: google_oauth_client),
         | 
| 204 | 
             
                redirect_url: str = GOOGLE_REDIRECT_URL,
         | 
| 205 | 
             
                response: Response = None,
         | 
| 206 | 
             
            ):
         | 
| 207 | 
            +
                logger.debug(f"Processing Google callback with code: {code}, state: {state}")
         | 
| 208 | 
             
                try:
         | 
| 209 | 
            +
                    # تحقق من الـ state لو موجود (اختياري لـ CSRF protection)
         | 
| 210 | 
            +
                    if state:
         | 
| 211 | 
            +
                        logger.debug(f"Received state: {state}")
         | 
| 212 | 
            +
                        # إضافة تحقق من الـ state لو بتستخدمه لمنع CSRF
         | 
| 213 | 
            +
                        # مثال: if state != stored_state: raise ValueError("Invalid state parameter")
         | 
| 214 | 
            +
             | 
| 215 | 
             
                    # Get access token
         | 
| 216 | 
             
                    token_data = await oauth_client.get_access_token(code, redirect_url)
         | 
| 217 | 
             
                    access_token = token_data["access_token"]
         | 
|  | |
| 237 | 
             
                        is_verified_by_default=True,
         | 
| 238 | 
             
                    )
         | 
| 239 |  | 
| 240 | 
            +
                    # توليد الـ JWT token يدويًا
         | 
| 241 | 
            +
                    token = await generate_jwt_token(user, SECRET, 3600)
         | 
| 242 |  | 
| 243 | 
            +
                    # ضبط الـ cookie
         | 
| 244 | 
             
                    cookie_transport.set_cookie(response, token)
         | 
| 245 |  | 
| 246 | 
             
                    return JSONResponse(content={
         | 
|  | |
| 249 | 
             
                    }, status_code=200)
         | 
| 250 | 
             
                except Exception as e:
         | 
| 251 | 
             
                    logger.error(f"Error in Google OAuth callback: {str(e)}")
         | 
| 252 | 
            +
                    if "400" in str(e):
         | 
| 253 | 
            +
                        return JSONResponse(
         | 
| 254 | 
            +
                            content={"detail": "Invalid authorization code. Please try logging in again."},
         | 
| 255 | 
            +
                            status_code=400
         | 
| 256 | 
            +
                        )
         | 
| 257 | 
             
                    return JSONResponse(content={"detail": str(e)}, status_code=400)
         | 
| 258 |  | 
| 259 | 
             
            async def custom_github_oauth_callback(
         | 
| 260 | 
             
                code: str,
         | 
| 261 | 
            +
                state: Optional[str] = None,
         | 
| 262 | 
             
                user_manager: UserManager = Depends(get_user_manager),
         | 
| 263 | 
             
                oauth_client=Depends(lambda: github_oauth_client),
         | 
| 264 | 
             
                redirect_url: str = GITHUB_REDIRECT_URL,
         | 
| 265 | 
             
                response: Response = None,
         | 
| 266 | 
             
            ):
         | 
| 267 | 
            +
                logger.debug(f"Processing GitHub callback with code: {code}, state: {state}")
         | 
| 268 | 
             
                try:
         | 
| 269 | 
            +
                    # تحقق من الـ state لو موجود (اختياري لـ CSRF protection)
         | 
| 270 | 
            +
                    if state:
         | 
| 271 | 
            +
                        logger.debug(f"Received state: {state}")
         | 
| 272 | 
            +
                        # إضافة تحقق من الـ state لو بتستخدمه لمنع CSRF
         | 
| 273 | 
            +
                        # مثال: if state != stored_state: raise ValueError("Invalid state parameter")
         | 
| 274 | 
            +
             | 
| 275 | 
             
                    # Get access token
         | 
| 276 | 
             
                    token_data = await oauth_client.get_access_token(code, redirect_url)
         | 
| 277 | 
             
                    access_token = token_data["access_token"]
         | 
|  | |
| 315 | 
             
                        is_verified_by_default=True,
         | 
| 316 | 
             
                    )
         | 
| 317 |  | 
| 318 | 
            +
                    # توليد الـ JWT token يدويًا
         | 
| 319 | 
            +
                    token = await generate_jwt_token(user, SECRET, 3600)
         | 
| 320 |  | 
| 321 | 
            +
                    # ضبط الـ cookie
         | 
| 322 | 
             
                    cookie_transport.set_cookie(response, token)
         | 
| 323 |  | 
| 324 | 
             
                    return JSONResponse(content={
         | 
|  | |
| 327 | 
             
                    }, status_code=200)
         | 
| 328 | 
             
                except Exception as e:
         | 
| 329 | 
             
                    logger.error(f"Error in GitHub OAuth callback: {str(e)}")
         | 
| 330 | 
            +
                    if "400" in str(e):
         | 
| 331 | 
            +
                        return JSONResponse(
         | 
| 332 | 
            +
                            content={"detail": "Invalid authorization code. Please try logging in again."},
         | 
| 333 | 
            +
                            status_code=400
         | 
| 334 | 
            +
                        )
         | 
| 335 | 
             
                    return JSONResponse(content={"detail": str(e)}, status_code=400)
         | 
| 336 |  | 
| 337 | 
             
            # تضمين الراوترات داخل التطبيق
         | 
    	
        api/endpoints.py
    CHANGED
    
    | @@ -4,7 +4,7 @@ | |
| 4 |  | 
| 5 | 
             
            import os
         | 
| 6 | 
             
            import uuid
         | 
| 7 | 
            -
            from fastapi import APIRouter, Depends, HTTPException, Request, status, UploadFile, File
         | 
| 8 | 
             
            from fastapi.responses import StreamingResponse
         | 
| 9 | 
             
            from api.database import User, Conversation, Message
         | 
| 10 | 
             
            from api.models import QueryRequest, ConversationOut, ConversationCreate, UserUpdate
         | 
| @@ -24,7 +24,6 @@ import logging | |
| 24 | 
             
            from typing import List, Optional
         | 
| 25 | 
             
            # from utils.constants import MODEL_ALIASES, MODEL_NAME, SECONDARY_MODEL_NAME, TERTIARY_MODEL_NAME, CLIP_BASE_MODEL, CLIP_LARGE_MODEL, ASR_MODEL, TTS_MODEL, IMAGE_GEN_MODEL, SECONDARY_IMAGE_GEN_MODEL
         | 
| 26 | 
             
            from utils.constants import MODEL_ALIASES, MODEL_NAME, SECONDARY_MODEL_NAME, TERTIARY_MODEL_NAME, CLIP_BASE_MODEL, CLIP_LARGE_MODEL, ASR_MODEL, TTS_MODEL, IMAGE_GEN_MODEL, SECONDARY_IMAGE_GEN_MODEL, IMAGE_INFERENCE_API
         | 
| 27 | 
            -
            from fastapi import Body
         | 
| 28 | 
             
            import psutil
         | 
| 29 | 
             
            import time
         | 
| 30 | 
             
            router = APIRouter()
         | 
| @@ -899,12 +898,19 @@ async def get_user_settings(user: User = Depends(current_active_user)): | |
| 899 | 
             
                }
         | 
| 900 |  | 
| 901 |  | 
|  | |
| 902 | 
             
            @router.get("/api/verify-token")
         | 
| 903 | 
             
            async def verify_token(user: User = Depends(current_active_user)):
         | 
| 904 | 
             
                if not user:
         | 
| 905 | 
             
                    raise HTTPException(status_code=401, detail="Invalid or expired token")
         | 
| 906 | 
            -
                return { | 
| 907 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 908 |  | 
| 909 | 
             
            @router.put("/users/me")
         | 
| 910 | 
             
            async def update_user_settings(
         | 
|  | |
| 4 |  | 
| 5 | 
             
            import os
         | 
| 6 | 
             
            import uuid
         | 
| 7 | 
            +
            from fastapi import APIRouter, Depends, HTTPException, Request, status, UploadFile, File , Body
         | 
| 8 | 
             
            from fastapi.responses import StreamingResponse
         | 
| 9 | 
             
            from api.database import User, Conversation, Message
         | 
| 10 | 
             
            from api.models import QueryRequest, ConversationOut, ConversationCreate, UserUpdate
         | 
|  | |
| 24 | 
             
            from typing import List, Optional
         | 
| 25 | 
             
            # from utils.constants import MODEL_ALIASES, MODEL_NAME, SECONDARY_MODEL_NAME, TERTIARY_MODEL_NAME, CLIP_BASE_MODEL, CLIP_LARGE_MODEL, ASR_MODEL, TTS_MODEL, IMAGE_GEN_MODEL, SECONDARY_IMAGE_GEN_MODEL
         | 
| 26 | 
             
            from utils.constants import MODEL_ALIASES, MODEL_NAME, SECONDARY_MODEL_NAME, TERTIARY_MODEL_NAME, CLIP_BASE_MODEL, CLIP_LARGE_MODEL, ASR_MODEL, TTS_MODEL, IMAGE_GEN_MODEL, SECONDARY_IMAGE_GEN_MODEL, IMAGE_INFERENCE_API
         | 
|  | |
| 27 | 
             
            import psutil
         | 
| 28 | 
             
            import time
         | 
| 29 | 
             
            router = APIRouter()
         | 
|  | |
| 898 | 
             
                }
         | 
| 899 |  | 
| 900 |  | 
| 901 | 
            +
             | 
| 902 | 
             
            @router.get("/api/verify-token")
         | 
| 903 | 
             
            async def verify_token(user: User = Depends(current_active_user)):
         | 
| 904 | 
             
                if not user:
         | 
| 905 | 
             
                    raise HTTPException(status_code=401, detail="Invalid or expired token")
         | 
| 906 | 
            +
                return {
         | 
| 907 | 
            +
                    "status": "valid",
         | 
| 908 | 
            +
                    "user": {
         | 
| 909 | 
            +
                        "id": user.id,
         | 
| 910 | 
            +
                        "email": user.email,
         | 
| 911 | 
            +
                        "is_active": user.is_active
         | 
| 912 | 
            +
                    }
         | 
| 913 | 
            +
                }
         | 
| 914 |  | 
| 915 | 
             
            @router.put("/users/me")
         | 
| 916 | 
             
            async def update_user_settings(
         | 
    	
        requirements.txt
    CHANGED
    
    | @@ -51,4 +51,5 @@ diffusers>=0.30.0 | |
| 51 | 
             
            psutil>=5.9.0
         | 
| 52 | 
             
            xformers>=0.0.27
         | 
| 53 | 
             
            anyio==4.6.0
         | 
| 54 | 
            -
             | 
|  | 
|  | |
| 51 | 
             
            psutil>=5.9.0
         | 
| 52 | 
             
            xformers>=0.0.27
         | 
| 53 | 
             
            anyio==4.6.0
         | 
| 54 | 
            +
            pyjwt>=2.4.0
         | 
| 55 | 
            +
            duckduckgo-search
         | 
    	
        static/js/chat.js
    CHANGED
    
    | @@ -61,17 +61,32 @@ document.addEventListener('DOMContentLoaded', async () => { | |
| 61 | 
             
              // Force chat view to be visible immediately
         | 
| 62 | 
             
              enterChatView(true);
         | 
| 63 |  | 
| 64 | 
            -
               | 
| 65 | 
            -
             | 
|  | |
| 66 | 
             
                await loadConversation(currentConversationId);
         | 
| 67 | 
            -
             | 
| 68 | 
            -
                 | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 69 | 
             
                conversationHistory.forEach(msg => {
         | 
| 70 | 
             
                  console.log('Adding message from history:', msg);
         | 
| 71 | 
             
                  addMsg(msg.role, msg.content);
         | 
| 72 | 
             
                });
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 73 | 
             
              } else {
         | 
| 74 | 
             
                console.log('No conversation history or ID, starting fresh');
         | 
|  | |
|  | |
|  | |
|  | |
| 75 | 
             
              }
         | 
| 76 |  | 
| 77 | 
             
              autoResizeTextarea();
         | 
| @@ -88,28 +103,32 @@ document.addEventListener('DOMContentLoaded', async () => { | |
| 88 | 
             
            async function checkAuth() {
         | 
| 89 | 
             
              const token = localStorage.getItem('token');
         | 
| 90 | 
             
              if (!token) {
         | 
| 91 | 
            -
                console.log('No auth token found');
         | 
| 92 | 
            -
                return false;
         | 
| 93 | 
             
              }
         | 
| 94 | 
             
              try {
         | 
| 95 | 
             
                const response = await fetch('/api/verify-token', {
         | 
| 96 | 
            -
                   | 
|  | |
|  | |
|  | |
|  | |
| 97 | 
             
                });
         | 
| 98 | 
            -
                 | 
| 99 | 
            -
             | 
| 100 | 
            -
                   | 
|  | |
| 101 | 
             
                } else {
         | 
| 102 | 
            -
                  console.log('Token verification failed:', response.status);
         | 
| 103 | 
             
                  localStorage.removeItem('token');
         | 
| 104 | 
            -
                  return false;
         | 
| 105 | 
             
                }
         | 
| 106 | 
             
              } catch (error) {
         | 
| 107 | 
             
                console.error('Error verifying token:', error);
         | 
| 108 | 
             
                localStorage.removeItem('token');
         | 
| 109 | 
            -
                return false;
         | 
| 110 | 
             
              }
         | 
| 111 | 
             
            }
         | 
| 112 | 
            -
             | 
| 113 | 
             
            // Handle session for non-logged-in users
         | 
| 114 | 
             
            async function handleSession() {
         | 
| 115 | 
             
              const sessionId = sessionStorage.getItem('session_id');
         | 
| @@ -1260,4 +1279,4 @@ function autoResizeTextarea() { | |
| 1260 | 
             
            // Function to check if text contains Arabic characters
         | 
| 1261 | 
             
            function isArabicText(text) {
         | 
| 1262 | 
             
              return /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/.test(text);
         | 
| 1263 | 
            -
            }
         | 
|  | |
| 61 | 
             
              // Force chat view to be visible immediately
         | 
| 62 | 
             
              enterChatView(true);
         | 
| 63 |  | 
| 64 | 
            +
              const authResult = await checkAuth();
         | 
| 65 | 
            +
              if (authResult.authenticated && currentConversationId) {
         | 
| 66 | 
            +
                console.log('Authenticated user, loading conversation with ID:', currentConversationId);
         | 
| 67 | 
             
                await loadConversation(currentConversationId);
         | 
| 68 | 
            +
                // عرض بيانات المستخدم في الواجهة
         | 
| 69 | 
            +
                const userInfoElement = document.getElementById('user-info');
         | 
| 70 | 
            +
                if (userInfoElement && authResult.user) {
         | 
| 71 | 
            +
                  userInfoElement.textContent = `Welcome, ${authResult.user.email}`;
         | 
| 72 | 
            +
                }
         | 
| 73 | 
            +
              } else if (!authResult.authenticated && conversationHistory.length > 0) {
         | 
| 74 | 
            +
                console.log('Unauthenticated user, restoring conversation history from sessionStorage:', conversationHistory);
         | 
| 75 | 
             
                conversationHistory.forEach(msg => {
         | 
| 76 | 
             
                  console.log('Adding message from history:', msg);
         | 
| 77 | 
             
                  addMsg(msg.role, msg.content);
         | 
| 78 | 
             
                });
         | 
| 79 | 
            +
                // عرض Anonymous لو مفيش مستخدم موثّق
         | 
| 80 | 
            +
                const userInfoElement = document.getElementById('user-info');
         | 
| 81 | 
            +
                if (userInfoElement) {
         | 
| 82 | 
            +
                  userInfoElement.textContent = 'Anonymous';
         | 
| 83 | 
            +
                }
         | 
| 84 | 
             
              } else {
         | 
| 85 | 
             
                console.log('No conversation history or ID, starting fresh');
         | 
| 86 | 
            +
                const userInfoElement = document.getElementById('user-info');
         | 
| 87 | 
            +
                if (userInfoElement) {
         | 
| 88 | 
            +
                  userInfoElement.textContent = 'Anonymous';
         | 
| 89 | 
            +
                }
         | 
| 90 | 
             
              }
         | 
| 91 |  | 
| 92 | 
             
              autoResizeTextarea();
         | 
|  | |
| 103 | 
             
            async function checkAuth() {
         | 
| 104 | 
             
              const token = localStorage.getItem('token');
         | 
| 105 | 
             
              if (!token) {
         | 
| 106 | 
            +
                console.log('No auth token found in localStorage');
         | 
| 107 | 
            +
                return { authenticated: false, user: null };
         | 
| 108 | 
             
              }
         | 
| 109 | 
             
              try {
         | 
| 110 | 
             
                const response = await fetch('/api/verify-token', {
         | 
| 111 | 
            +
                  method: 'GET',
         | 
| 112 | 
            +
                  headers: { 
         | 
| 113 | 
            +
                    'Authorization': `Bearer ${token}`,
         | 
| 114 | 
            +
                    'Accept': 'application/json'
         | 
| 115 | 
            +
                  }
         | 
| 116 | 
             
                });
         | 
| 117 | 
            +
                const data = await response.json();
         | 
| 118 | 
            +
                if (response.ok && data.status === 'valid') {
         | 
| 119 | 
            +
                  console.log('Auth token verified, user:', data.user);
         | 
| 120 | 
            +
                  return { authenticated: true, user: data.user };
         | 
| 121 | 
             
                } else {
         | 
| 122 | 
            +
                  console.log('Token verification failed:', data.detail || response.status);
         | 
| 123 | 
             
                  localStorage.removeItem('token');
         | 
| 124 | 
            +
                  return { authenticated: false, user: null };
         | 
| 125 | 
             
                }
         | 
| 126 | 
             
              } catch (error) {
         | 
| 127 | 
             
                console.error('Error verifying token:', error);
         | 
| 128 | 
             
                localStorage.removeItem('token');
         | 
| 129 | 
            +
                return { authenticated: false, user: null };
         | 
| 130 | 
             
              }
         | 
| 131 | 
             
            }
         | 
|  | |
| 132 | 
             
            // Handle session for non-logged-in users
         | 
| 133 | 
             
            async function handleSession() {
         | 
| 134 | 
             
              const sessionId = sessionStorage.getItem('session_id');
         | 
|  | |
| 1279 | 
             
            // Function to check if text contains Arabic characters
         | 
| 1280 | 
             
            function isArabicText(text) {
         | 
| 1281 | 
             
              return /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/.test(text);
         | 
| 1282 | 
            +
            }
         | 
    	
        templates/login.html
    CHANGED
    
    | @@ -427,14 +427,14 @@ | |
| 427 | 
             
                    localStorage.setItem('theme', 'light');
         | 
| 428 | 
             
                }
         | 
| 429 |  | 
| 430 | 
            -
                // Check authentication status | 
| 431 | 
             
                async function checkAuthStatus() {
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 432 | 
             
                    try {
         | 
| 433 | 
            -
                        const token = localStorage.getItem('token');
         | 
| 434 | 
            -
                        if (!token) {
         | 
| 435 | 
            -
                            console.log('No token found in localStorage');
         | 
| 436 | 
            -
                            return false;
         | 
| 437 | 
            -
                        }
         | 
| 438 | 
             
                        const response = await fetch('/api/verify-token', {
         | 
| 439 | 
             
                            method: 'GET',
         | 
| 440 | 
             
                            headers: {
         | 
| @@ -442,17 +442,17 @@ | |
| 442 | 
             
                                'Accept': 'application/json'
         | 
| 443 | 
             
                            }
         | 
| 444 | 
             
                        });
         | 
| 445 | 
            -
                         | 
| 446 | 
            -
             | 
| 447 | 
            -
                             | 
| 448 | 
             
                            return true;
         | 
| 449 | 
             
                        } else {
         | 
| 450 | 
            -
                            console.log('Token verification failed,  | 
| 451 | 
             
                            localStorage.removeItem('token');
         | 
| 452 | 
             
                            return false;
         | 
| 453 | 
             
                        }
         | 
| 454 | 
             
                    } catch (error) {
         | 
| 455 | 
            -
                        console.error('Error  | 
| 456 | 
             
                        localStorage.removeItem('token');
         | 
| 457 | 
             
                        return false;
         | 
| 458 | 
             
                    }
         | 
| @@ -566,7 +566,7 @@ | |
| 566 | 
             
                    });
         | 
| 567 | 
             
                }
         | 
| 568 |  | 
| 569 | 
            -
                // Check for OAuth callback on load | 
| 570 | 
             
                window.addEventListener('load', async () => {
         | 
| 571 | 
             
                    console.log('Page loaded, checking for auth status and OAuth callback');
         | 
| 572 | 
             
                    if (!navigator.onLine) {
         | 
| @@ -582,38 +582,38 @@ | |
| 582 | 
             
                        errorMsg.classList.remove('hidden');
         | 
| 583 | 
             
                        console.error('OAuth error from URL:', error);
         | 
| 584 | 
             
                    } else if (code) {
         | 
| 585 | 
            -
                        console.log('OAuth code detected in URL,  | 
| 586 | 
            -
                         | 
| 587 | 
            -
                         | 
| 588 | 
            -
             | 
| 589 | 
            -
             | 
| 590 | 
            -
             | 
| 591 | 
            -
                             | 
| 592 | 
            -
                             | 
| 593 | 
            -
                                const  | 
| 594 | 
            -
             | 
| 595 | 
            -
                                     | 
| 596 | 
            -
             | 
| 597 | 
            -
             | 
| 598 | 
            -
                                     | 
| 599 | 
            -
             | 
| 600 | 
            -
                                        localStorage.setItem('token', data.access_token);
         | 
| 601 | 
            -
                                        console.log(`${provider} login successful, token saved`);
         | 
| 602 | 
             
                                        window.location.href = '/chat';
         | 
| 603 | 
             
                                    } else {
         | 
| 604 | 
            -
                                        throw new Error(' | 
| 605 | 
             
                                    }
         | 
| 606 | 
             
                                } else {
         | 
| 607 | 
            -
                                     | 
| 608 | 
            -
                                    errorMsg.textContent = errorData.detail || `Failed to complete ${provider} login`;
         | 
| 609 | 
            -
                                    errorMsg.classList.remove('hidden');
         | 
| 610 | 
            -
                                    console.error(`Failed to complete ${provider} login:`, errorData);
         | 
| 611 | 
             
                                }
         | 
| 612 | 
            -
                            }  | 
| 613 | 
            -
                                 | 
|  | |
| 614 | 
             
                                errorMsg.classList.remove('hidden');
         | 
| 615 | 
            -
                                console.error(` | 
| 616 | 
             
                            }
         | 
|  | |
|  | |
|  | |
|  | |
| 617 | 
             
                        }
         | 
| 618 | 
             
                    } else {
         | 
| 619 | 
             
                        await checkAuthStatus();
         | 
|  | |
| 427 | 
             
                    localStorage.setItem('theme', 'light');
         | 
| 428 | 
             
                }
         | 
| 429 |  | 
| 430 | 
            +
                // Check authentication status
         | 
| 431 | 
             
                async function checkAuthStatus() {
         | 
| 432 | 
            +
                    const token = localStorage.getItem('token');
         | 
| 433 | 
            +
                    if (!token) {
         | 
| 434 | 
            +
                        console.log('No token found in localStorage');
         | 
| 435 | 
            +
                        return false;
         | 
| 436 | 
            +
                    }
         | 
| 437 | 
             
                    try {
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 438 | 
             
                        const response = await fetch('/api/verify-token', {
         | 
| 439 | 
             
                            method: 'GET',
         | 
| 440 | 
             
                            headers: {
         | 
|  | |
| 442 | 
             
                                'Accept': 'application/json'
         | 
| 443 | 
             
                            }
         | 
| 444 | 
             
                        });
         | 
| 445 | 
            +
                        const data = await response.json();
         | 
| 446 | 
            +
                        if (response.ok && data.status === 'valid') {
         | 
| 447 | 
            +
                            console.log('Token is valid, user:', data.user);
         | 
| 448 | 
             
                            return true;
         | 
| 449 | 
             
                        } else {
         | 
| 450 | 
            +
                            console.log('Token verification failed:', data.detail || 'Invalid token');
         | 
| 451 | 
             
                            localStorage.removeItem('token');
         | 
| 452 | 
             
                            return false;
         | 
| 453 | 
             
                        }
         | 
| 454 | 
             
                    } catch (error) {
         | 
| 455 | 
            +
                        console.error('Error verifying token:', error);
         | 
| 456 | 
             
                        localStorage.removeItem('token');
         | 
| 457 | 
             
                        return false;
         | 
| 458 | 
             
                    }
         | 
|  | |
| 566 | 
             
                    });
         | 
| 567 | 
             
                }
         | 
| 568 |  | 
| 569 | 
            +
                // Check for OAuth callback on load
         | 
| 570 | 
             
                window.addEventListener('load', async () => {
         | 
| 571 | 
             
                    console.log('Page loaded, checking for auth status and OAuth callback');
         | 
| 572 | 
             
                    if (!navigator.onLine) {
         | 
|  | |
| 582 | 
             
                        errorMsg.classList.remove('hidden');
         | 
| 583 | 
             
                        console.error('OAuth error from URL:', error);
         | 
| 584 | 
             
                    } else if (code) {
         | 
| 585 | 
            +
                        console.log('OAuth code detected in URL, processing callback');
         | 
| 586 | 
            +
                        const provider = window.location.pathname.includes('google') ? 'google' : 'github';
         | 
| 587 | 
            +
                        try {
         | 
| 588 | 
            +
                            const response = await fetch(window.location.href, {
         | 
| 589 | 
            +
                                method: 'GET',
         | 
| 590 | 
            +
                                headers: { 'Accept': 'application/json' }
         | 
| 591 | 
            +
                            });
         | 
| 592 | 
            +
                            if (response.ok) {
         | 
| 593 | 
            +
                                const data = await response.json();
         | 
| 594 | 
            +
                                if (data.access_token) {
         | 
| 595 | 
            +
                                    localStorage.setItem('token', data.access_token);
         | 
| 596 | 
            +
                                    console.log(`${provider} login successful, token saved`);
         | 
| 597 | 
            +
                                    const isAuthenticated = await checkAuthStatus();
         | 
| 598 | 
            +
                                    if (isAuthenticated) {
         | 
| 599 | 
            +
                                        console.log('Redirecting to /chat');
         | 
|  | |
|  | |
| 600 | 
             
                                        window.location.href = '/chat';
         | 
| 601 | 
             
                                    } else {
         | 
| 602 | 
            +
                                        throw new Error('Authentication failed after receiving token');
         | 
| 603 | 
             
                                    }
         | 
| 604 | 
             
                                } else {
         | 
| 605 | 
            +
                                    throw new Error('No access token received');
         | 
|  | |
|  | |
|  | |
| 606 | 
             
                                }
         | 
| 607 | 
            +
                            } else {
         | 
| 608 | 
            +
                                const errorData = await response.json();
         | 
| 609 | 
            +
                                errorMsg.textContent = errorData.detail || `Failed to complete ${provider} login`;
         | 
| 610 | 
             
                                errorMsg.classList.remove('hidden');
         | 
| 611 | 
            +
                                console.error(`Failed to complete ${provider} login:`, errorData);
         | 
| 612 | 
             
                            }
         | 
| 613 | 
            +
                        } catch (error) {
         | 
| 614 | 
            +
                            errorMsg.textContent = `Failed to process ${provider} login. Please try again.`;
         | 
| 615 | 
            +
                            errorMsg.classList.remove('hidden');
         | 
| 616 | 
            +
                            console.error(`Error processing ${provider} callback:`, error);
         | 
| 617 | 
             
                        }
         | 
| 618 | 
             
                    } else {
         | 
| 619 | 
             
                        await checkAuthStatus();
         | 
